Merge pull request #3598 from influxdata/flux/vertical-builder
Make Flux Builder Stack Verticallypull/10616/head
commit
f8c8f97bec
|
@ -1,6 +1,7 @@
|
|||
import React, {PureComponent} from 'react'
|
||||
import _ from 'lodash'
|
||||
|
||||
import FancyScrollbar from 'src/shared/components/FancyScrollbar'
|
||||
import ExpressionNode from 'src/flux/components/ExpressionNode'
|
||||
import VariableName from 'src/flux/components/VariableName'
|
||||
import FuncSelector from 'src/flux/components/FuncSelector'
|
||||
|
@ -29,7 +30,7 @@ class BodyBuilder extends PureComponent<Props> {
|
|||
if (d.funcs) {
|
||||
return (
|
||||
<div className="declaration" key={i}>
|
||||
<VariableName name={d.name} />
|
||||
<VariableName name={d.name} assignedToQuery={true} />
|
||||
<ExpressionNode
|
||||
bodyID={b.id}
|
||||
declarationID={d.id}
|
||||
|
@ -43,7 +44,7 @@ class BodyBuilder extends PureComponent<Props> {
|
|||
|
||||
return (
|
||||
<div className="declaration" key={i}>
|
||||
<VariableName name={b.source} />
|
||||
<VariableName name={b.source} assignedToQuery={false} />
|
||||
</div>
|
||||
)
|
||||
})
|
||||
|
@ -62,6 +63,7 @@ class BodyBuilder extends PureComponent<Props> {
|
|||
})
|
||||
|
||||
return (
|
||||
<FancyScrollbar className="body-builder--container" autoHide={true}>
|
||||
<div className="body-builder">
|
||||
{_.flatten(bodybuilder)}
|
||||
<div className="declaration">
|
||||
|
@ -74,6 +76,7 @@ class BodyBuilder extends PureComponent<Props> {
|
|||
/>
|
||||
</div>
|
||||
</div>
|
||||
</FancyScrollbar>
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -21,30 +21,47 @@ interface Props {
|
|||
@ErrorHandling
|
||||
export default class FuncArgs extends PureComponent<Props> {
|
||||
public render() {
|
||||
const {onDeleteFunc} = this.props
|
||||
|
||||
return (
|
||||
<div className="func-node--tooltip">
|
||||
<div className="func-args">{this.renderJoinOrArgs}</div>
|
||||
<div className="func-arg--buttons">
|
||||
<div
|
||||
className="btn btn-sm btn-danger btn-square"
|
||||
onClick={onDeleteFunc}
|
||||
>
|
||||
<span className="icon trash" />
|
||||
</div>
|
||||
{this.build}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
get renderJoinOrArgs(): JSX.Element | JSX.Element[] {
|
||||
const {func} = this.props
|
||||
const {name: funcName} = func
|
||||
|
||||
if (funcName === funcNames.JOIN) {
|
||||
return this.renderJoin
|
||||
}
|
||||
|
||||
return this.renderArguments
|
||||
}
|
||||
|
||||
get renderArguments(): JSX.Element | JSX.Element[] {
|
||||
const {
|
||||
func,
|
||||
bodyID,
|
||||
service,
|
||||
onChangeArg,
|
||||
onDeleteFunc,
|
||||
declarationID,
|
||||
onGenerateScript,
|
||||
declarationsFromBody,
|
||||
} = this.props
|
||||
const {name: funcName, id: funcID} = func
|
||||
return (
|
||||
<div className="func-node--tooltip">
|
||||
{funcName === funcNames.JOIN ? (
|
||||
<Join
|
||||
func={func}
|
||||
bodyID={bodyID}
|
||||
declarationID={declarationID}
|
||||
onChangeArg={onChangeArg}
|
||||
declarationsFromBody={declarationsFromBody}
|
||||
onGenerateScript={onGenerateScript}
|
||||
/>
|
||||
) : (
|
||||
func.args.map(({key, value, type}) => (
|
||||
|
||||
return func.args.map(({key, value, type}) => (
|
||||
<FuncArg
|
||||
key={key}
|
||||
type={type}
|
||||
|
@ -59,17 +76,27 @@ export default class FuncArgs extends PureComponent<Props> {
|
|||
onGenerateScript={onGenerateScript}
|
||||
/>
|
||||
))
|
||||
)}
|
||||
<div className="func-node--buttons">
|
||||
<div
|
||||
className="btn btn-sm btn-danger func-node--delete"
|
||||
onClick={onDeleteFunc}
|
||||
>
|
||||
Delete
|
||||
</div>
|
||||
{this.build}
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
||||
get renderJoin(): JSX.Element {
|
||||
const {
|
||||
func,
|
||||
bodyID,
|
||||
onChangeArg,
|
||||
declarationID,
|
||||
onGenerateScript,
|
||||
declarationsFromBody,
|
||||
} = this.props
|
||||
|
||||
return (
|
||||
<Join
|
||||
func={func}
|
||||
bodyID={bodyID}
|
||||
declarationID={declarationID}
|
||||
onChangeArg={onChangeArg}
|
||||
declarationsFromBody={declarationsFromBody}
|
||||
onGenerateScript={onGenerateScript}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -76,19 +76,19 @@ export default class FuncArgsPreview extends PureComponent<Props> {
|
|||
case 'period':
|
||||
case 'duration':
|
||||
case 'array': {
|
||||
return <span className="variable-value--number">{argument}</span>
|
||||
return <span className="func-arg--number">{argument}</span>
|
||||
}
|
||||
case 'bool': {
|
||||
return <span className="variable-value--boolean">{argument}</span>
|
||||
return <span className="func-arg--boolean">{argument}</span>
|
||||
}
|
||||
case 'string': {
|
||||
return <span className="variable-value--string">"{argument}"</span>
|
||||
return <span className="func-arg--string">"{argument}"</span>
|
||||
}
|
||||
case 'object': {
|
||||
return <span className="variable-value--object">{argument}</span>
|
||||
return <span className="func-arg--object">{argument}</span>
|
||||
}
|
||||
case 'invalid': {
|
||||
return <span className="variable-value--invalid">{argument}</span>
|
||||
return <span className="func-arg--invalid">{argument}</span>
|
||||
}
|
||||
default: {
|
||||
return <span>{argument}</span>
|
||||
|
|
|
@ -52,6 +52,7 @@ export default class FuncNode extends PureComponent<Props, State> {
|
|||
onMouseEnter={this.handleMouseEnter}
|
||||
onMouseLeave={this.handleMouseLeave}
|
||||
>
|
||||
<div className="func-node--connector" />
|
||||
<div className="func-node--name">{func.name}</div>
|
||||
<FuncArgsPreview func={func} />
|
||||
{isExpanded && (
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
import React, {PureComponent} from 'react'
|
||||
|
||||
interface Props {
|
||||
name?: string
|
||||
name: string
|
||||
assignedToQuery: boolean
|
||||
}
|
||||
|
||||
interface State {
|
||||
|
@ -10,7 +11,7 @@ interface State {
|
|||
|
||||
export default class VariableName extends PureComponent<Props, State> {
|
||||
public static defaultProps: Partial<Props> = {
|
||||
name: '',
|
||||
assignedToQuery: false,
|
||||
}
|
||||
|
||||
constructor(props) {
|
||||
|
@ -22,7 +23,14 @@ export default class VariableName extends PureComponent<Props, State> {
|
|||
}
|
||||
|
||||
public render() {
|
||||
return <div className="variable-string">{this.nameElement}</div>
|
||||
const {assignedToQuery} = this.props
|
||||
|
||||
return (
|
||||
<div className="variable-node">
|
||||
{assignedToQuery && <div className="variable-node--connector" />}
|
||||
{this.nameElement}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
private get nameElement(): JSX.Element {
|
||||
|
@ -32,7 +40,7 @@ export default class VariableName extends PureComponent<Props, State> {
|
|||
return this.colorizeSyntax
|
||||
}
|
||||
|
||||
return <span className="variable-name">{name}</span>
|
||||
return <span className="variable-node--name">{name}</span>
|
||||
}
|
||||
|
||||
private get colorizeSyntax(): JSX.Element {
|
||||
|
@ -42,14 +50,13 @@ export default class VariableName extends PureComponent<Props, State> {
|
|||
const varValue = this.props.name.replace(/^[^=]+=/, '')
|
||||
|
||||
const valueIsString = varValue.endsWith('"')
|
||||
|
||||
return (
|
||||
<>
|
||||
<span className="variable-name">{varName}</span>
|
||||
<span className="variable-node--name">{varName}</span>
|
||||
{' = '}
|
||||
<span
|
||||
className={
|
||||
valueIsString ? 'variable-value--string' : 'variable-value--number'
|
||||
valueIsString ? 'variable-node--string' : 'variable-node--number'
|
||||
}
|
||||
>
|
||||
{varValue}
|
||||
|
|
|
@ -10,25 +10,28 @@ $flux-func-selector--height: 30px;
|
|||
display: flex;
|
||||
align-items: center;
|
||||
position: relative;
|
||||
flex-direction: column;
|
||||
|
||||
&.open {
|
||||
z-index: 9999;
|
||||
height: $flux-func-selector--height + $flux-func-selector--gap;
|
||||
}
|
||||
}
|
||||
|
||||
.func-selector--connector {
|
||||
width: $flux-func-selector--gap;
|
||||
height: $flux-func-selector--height;
|
||||
width: $flux-node-gap;
|
||||
height: $flux-func-selector--gap;
|
||||
position: relative;
|
||||
|
||||
&:after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
width: 100%;
|
||||
height: 4px;
|
||||
transform: translateY(-50%);
|
||||
@include gradient-h($g4-onyx, $c-pool);
|
||||
top: -130%;
|
||||
width: $flux-connector-line;
|
||||
left: 50%;
|
||||
height: 230%;
|
||||
transform: translateX(-50%);
|
||||
@include gradient-v($g4-onyx, $c-pool);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -51,7 +54,7 @@ $flux-func-selector--height: 30px;
|
|||
top: 0;
|
||||
|
||||
.func-selector--connector + & {
|
||||
left: $flux-func-selector--gap;
|
||||
top: $flux-func-selector--gap;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,18 +1,23 @@
|
|||
/*
|
||||
Flux Builder Styles
|
||||
------------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
$flux-builder-min-width: 440px;
|
||||
$flux-node-height: 30px;
|
||||
$flux-node-tooltip-gap: 4px;
|
||||
$flux-node-gap: 5px;
|
||||
$flux-connector-line: 2px;
|
||||
$flux-node-gap: 30px;
|
||||
$flux-node-padding: 10px;
|
||||
$flux-arg-min-width: 120px;
|
||||
|
||||
$flux-number-color: $c-neutrino;
|
||||
$flux-object-color: $c-viridian;
|
||||
$flux-string-color: $c-honeydew;
|
||||
$flux-boolean-color: $c-viridian;
|
||||
$flux-invalid-color: $c-viridian;
|
||||
/*
|
||||
Shared Node styles
|
||||
------------------
|
||||
*/
|
||||
|
||||
// Shared Node styles
|
||||
%flux-node {
|
||||
min-height: $flux-node-height;
|
||||
border-radius: $radius;
|
||||
|
@ -22,66 +27,108 @@ $flux-invalid-color: $c-viridian;
|
|||
position: relative;
|
||||
background-color: $g4-onyx;
|
||||
transition: background-color 0.25s ease;
|
||||
margin-bottom: $flux-node-tooltip-gap / 2;
|
||||
margin-top: $flux-node-tooltip-gap / 2;
|
||||
|
||||
&:hover {
|
||||
cursor: pointer;
|
||||
background-color: $g6-smoke;
|
||||
}
|
||||
}
|
||||
|
||||
.body-builder {
|
||||
padding: 30px;
|
||||
min-width: 440px;
|
||||
overflow: hidden;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
.body-builder--container {
|
||||
background-color: $g1-raven;
|
||||
}
|
||||
.body-builder {
|
||||
padding: $flux-node-height;
|
||||
padding-bottom: 0;
|
||||
min-width: $flux-builder-min-width;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.declaration {
|
||||
width: 100%;
|
||||
margin-bottom: 24px;
|
||||
margin-bottom: $flux-node-gap;
|
||||
padding-bottom: $flux-node-gap;
|
||||
border-bottom: 2px solid $g2-kevlar;
|
||||
display: flex;
|
||||
flex-wrap: nowrap;
|
||||
align-items: center;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
|
||||
&:last-of-type {
|
||||
margin-bottom: 0;
|
||||
border: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.variable-string {
|
||||
.variable-node {
|
||||
@extend %flux-node;
|
||||
color: $g11-sidewalk;
|
||||
line-height: $flux-node-height;
|
||||
white-space: nowrap;
|
||||
@include no-user-select();
|
||||
margin-top: 0;
|
||||
|
||||
&:hover {
|
||||
background-color: $g4-onyx;
|
||||
cursor: default;
|
||||
}
|
||||
}
|
||||
|
||||
.variable-blank {
|
||||
font-style: italic;
|
||||
.variable-node--connector {
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
border-radius: 50%;
|
||||
background-color: $c-pool;
|
||||
position: absolute;
|
||||
z-index: 2;
|
||||
top: 50%;
|
||||
left: $flux-node-gap / 2;
|
||||
transform: translate(-50%, -50%);
|
||||
|
||||
&:before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 100%;
|
||||
left: 50%;
|
||||
width: $flux-connector-line;
|
||||
height: $flux-node-gap;
|
||||
@include gradient-v($c-pool, $g4-onyx);
|
||||
transform: translateX(-50%);
|
||||
}
|
||||
}
|
||||
|
||||
.variable-name {
|
||||
.variable-node--name {
|
||||
color: $c-pool;
|
||||
|
||||
.variable-node--connector + & {
|
||||
margin-left: $flux-node-gap - $flux-node-padding;
|
||||
}
|
||||
}
|
||||
|
||||
.variable-value--string {
|
||||
.variable-node--string,
|
||||
.func-arg--string {
|
||||
color: $flux-string-color;
|
||||
}
|
||||
|
||||
.variable-value--boolean {
|
||||
.variable-node--boolean,
|
||||
.func-arg--boolean {
|
||||
color: $flux-boolean-color;
|
||||
}
|
||||
|
||||
.variable-value--number {
|
||||
.variable-node--number,
|
||||
.func-arg--number {
|
||||
color: $flux-number-color;
|
||||
}
|
||||
|
||||
.variable-value--object {
|
||||
.variable-node--object,
|
||||
.func-arg--object {
|
||||
color: $flux-object-color;
|
||||
}
|
||||
|
||||
.variable-value--invalid {
|
||||
.variable-node--invalid,
|
||||
.func-arg--invalid {
|
||||
color: $flux-invalid-color;
|
||||
}
|
||||
|
||||
|
@ -89,24 +136,66 @@ $flux-invalid-color: $c-viridian;
|
|||
@extend %flux-node;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
margin-left: $flux-node-gap;
|
||||
}
|
||||
|
||||
// Connection Line
|
||||
.func-node--connector {
|
||||
width: $flux-node-gap;
|
||||
height: 100%;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
transform: translateX(-100%);
|
||||
z-index: 0;
|
||||
|
||||
// Connection Lines
|
||||
&:before,
|
||||
&:after {
|
||||
content: '';
|
||||
height: 4px;
|
||||
width: $flux-node-gap;
|
||||
background-color: $g4-onyx;
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 0;
|
||||
transform: translate(-100%, -50%);
|
||||
}
|
||||
|
||||
&:first-child:after {
|
||||
content: none;
|
||||
// Vertical Line
|
||||
&:before {
|
||||
width: $flux-connector-line;
|
||||
height: calc(100% + #{$flux-node-tooltip-gap});
|
||||
top: -$flux-node-tooltip-gap / 2;
|
||||
left: $flux-node-gap / 2;
|
||||
transform: translateX(-50%);
|
||||
}
|
||||
// Horizontal Line
|
||||
&:after {
|
||||
height: $flux-connector-line;
|
||||
width: $flux-node-gap / 2;
|
||||
top: 50%;
|
||||
left: $flux-node-gap / 2;
|
||||
transform: translateY(-50%);
|
||||
}
|
||||
}
|
||||
// When a query exists unassigned to a variable
|
||||
.func-node:first-child {
|
||||
margin-left: 0;
|
||||
padding-left: $flux-node-gap;
|
||||
|
||||
.func-node--connector {
|
||||
transform: translateX(0);
|
||||
z-index: 2;
|
||||
|
||||
// Vertical Line
|
||||
&:before {
|
||||
height: $flux-node-gap;
|
||||
top: $flux-node-gap / 2;
|
||||
@include gradient-v($c-comet, $g4-onyx);
|
||||
}
|
||||
// Dot
|
||||
&:after {
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
border-radius: 50%;
|
||||
background-color: $c-comet;
|
||||
top: $flux-node-gap / 2;
|
||||
transform: translate(-50%, -50%);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -139,56 +228,65 @@ $flux-invalid-color: $c-viridian;
|
|||
}
|
||||
}
|
||||
|
||||
.func-node--tooltip,
|
||||
.variable-name--tooltip {
|
||||
.func-node--tooltip {
|
||||
background-color: $g3-castle;
|
||||
border-radius: $radius;
|
||||
padding: 10px;
|
||||
display: flex;
|
||||
align-items: stretch;
|
||||
flex-direction: column;
|
||||
position: absolute;
|
||||
top: calc(100% + #{$flux-node-tooltip-gap});
|
||||
left: 0;
|
||||
top: 0;
|
||||
left: calc(100% + #{$flux-node-tooltip-gap});
|
||||
z-index: 9999;
|
||||
box-shadow: 0 0 10px 2px $g2-kevlar; // Caret
|
||||
box-shadow: 0 0 10px 2px $g2-kevlar;
|
||||
|
||||
// Caret
|
||||
&:before {
|
||||
content: '';
|
||||
border-width: 9px;
|
||||
border-style: solid;
|
||||
border-color: transparent;
|
||||
border-bottom-color: $g3-castle;
|
||||
border-right-color: $g3-castle;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: $flux-node-padding + 3px;
|
||||
transform: translate(-50%, -100%);
|
||||
} // Invisible block to continue hovering
|
||||
top: $flux-node-height / 2;
|
||||
left: 0;
|
||||
transform: translate(-100%, -50%);
|
||||
}
|
||||
// Invisible block to continue hovering
|
||||
&:after {
|
||||
content: '';
|
||||
width: 80%;
|
||||
height: 7px;
|
||||
height: 50%;
|
||||
width: $flux-node-tooltip-gap * 3;
|
||||
position: absolute;
|
||||
top: -7px;
|
||||
left: 0;
|
||||
top: 0;
|
||||
left: -$flux-node-tooltip-gap * 3;
|
||||
}
|
||||
}
|
||||
|
||||
.func-node--buttons {
|
||||
.func-arg--buttons {
|
||||
display: flex;
|
||||
margin-top: 12px;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
margin-left: 8px;
|
||||
}
|
||||
|
||||
.func-node--delete,
|
||||
.func-node--build {
|
||||
width: 60px;
|
||||
margin-top: 4px;
|
||||
}
|
||||
|
||||
.func-node--sub .func-arg {
|
||||
.func-args {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.func-arg {
|
||||
min-width: $flux-arg-min-width;
|
||||
display: flex;
|
||||
flex-wrap: nowrap;
|
||||
align-items: center;
|
||||
margin-bottom: 4px;
|
||||
|
||||
&:last-of-type {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
@ -212,26 +310,6 @@ $flux-invalid-color: $c-viridian;
|
|||
width: 300px;
|
||||
}
|
||||
|
||||
.variable-name--tooltip {
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
flex-wrap: nowrap;
|
||||
}
|
||||
|
||||
.variable-name--input {
|
||||
width: 140px;
|
||||
}
|
||||
|
||||
.variable-name--operator {
|
||||
width: 20px;
|
||||
height: 30px;
|
||||
text-align: center;
|
||||
line-height: 30px;
|
||||
font-weight: 600;
|
||||
@include no-user-select();
|
||||
}
|
||||
|
||||
/*
|
||||
Filter Preview Styles
|
||||
------------------------------------------------------------------------------
|
||||
|
|
Loading…
Reference in New Issue