diff --git a/ui/src/ifql/components/FuncArgs.tsx b/ui/src/ifql/components/FuncArgs.tsx index 10c75d4cf..6f68dea86 100644 --- a/ui/src/ifql/components/FuncArgs.tsx +++ b/ui/src/ifql/components/FuncArgs.tsx @@ -10,6 +10,7 @@ interface Props { onChangeArg: OnChangeArg declarationID: string onGenerateScript: () => void + onDeleteFunc: () => void } @ErrorHandling @@ -19,12 +20,13 @@ export default class FuncArgs extends PureComponent { func, bodyID, onChangeArg, + onDeleteFunc, declarationID, onGenerateScript, } = this.props return ( -
+
{func.args.map(({key, value, type}) => { return ( { /> ) })} +
+ Delete +
) } diff --git a/ui/src/ifql/components/FuncNode.tsx b/ui/src/ifql/components/FuncNode.tsx index 2f1d679e1..747d630d4 100644 --- a/ui/src/ifql/components/FuncNode.tsx +++ b/ui/src/ifql/components/FuncNode.tsx @@ -13,7 +13,7 @@ interface Props { } interface State { - isOpen: boolean + isExpanded: boolean } @ErrorHandling @@ -25,7 +25,7 @@ export default class FuncNode extends PureComponent { constructor(props) { super(props) this.state = { - isOpen: true, + isExpanded: false, } } @@ -37,37 +37,65 @@ export default class FuncNode extends PureComponent { declarationID, onGenerateScript, } = this.props - const {isOpen} = this.state + const {isExpanded} = this.state return ( -
-
-
{func.name}
-
- {isOpen && ( +
+
{func.name}
+
{this.stringifyArgs}
+ {isExpanded && ( )} -
) } + private get stringifyArgs(): string { + const { + func: {args}, + } = this.props + + if (!args) { + return + } + + return args.reduce((acc, arg, i) => { + if (!arg.value) { + return acc + } + + const separator = i === 0 ? '' : ', ' + + return `${acc}${separator}${arg.key}: ${arg.value}` + }, '') + } + private handleDelete = (): void => { const {func, bodyID, declarationID} = this.props this.props.onDelete({funcID: func.id, bodyID, declarationID}) } - private handleClick = (e: MouseEvent): void => { + private handleMouseEnter = (e: MouseEvent): void => { e.stopPropagation() - const {isOpen} = this.state - this.setState({isOpen: !isOpen}) + this.setState({isExpanded: true}) + } + + private handleMouseLeave = (e: MouseEvent): void => { + e.stopPropagation() + + this.setState({isExpanded: false}) } } diff --git a/ui/src/ifql/components/FuncSelector.tsx b/ui/src/ifql/components/FuncSelector.tsx index 6e2fdae10..88ab442cd 100644 --- a/ui/src/ifql/components/FuncSelector.tsx +++ b/ui/src/ifql/components/FuncSelector.tsx @@ -1,5 +1,6 @@ import React, {PureComponent, ChangeEvent, KeyboardEvent} from 'react' import _ from 'lodash' +import classnames from 'classnames' import {ClickOutside} from 'src/shared/components/ClickOutside' import FuncList from 'src/ifql/components/FuncList' @@ -36,7 +37,8 @@ export class FuncSelector extends PureComponent { return ( -
+
+
{isOpen ? ( { ) } + private get className(): string { + const {isOpen} = this.state + + return classnames('ifql-func--selector', {open: isOpen}) + } + private handleCloseList = () => { this.setState({isOpen: false, selectedFunc: ''}) } diff --git a/ui/src/style/components/func-node.scss b/ui/src/style/components/func-node.scss index a51d83c00..a256ceed1 100644 --- a/ui/src/style/components/func-node.scss +++ b/ui/src/style/components/func-node.scss @@ -1,13 +1,36 @@ +$ifql-node-height: 30px; +$ifql-node-tooltip-gap: $ifql-node-height + 4px; +$ifql-node-gap: 5px; +$ifql-node-padding: 10px; +$ifql-arg-min-width: 120px; + +/* + Shared Node styles + ------------------ +*/ +%ifql-node { + height: $ifql-node-height; + border-radius: $radius; + padding: 0 $ifql-node-padding; + font-size: 13px; + font-weight: 600; + position: relative; +} + .body-builder { - padding: 12px; + padding: 12px 30px; min-width: 440px; overflow: hidden; - background-color: $g2-kevlar; + height: 100%; + width: 100%; + background-color: $g1-raven; } .declaration { width: 100%; - margin-bottom: 12px; + margin-bottom: 24px; + display: flex; + flex-wrap: wrap; &:last-of-type { margin-bottom: 0; @@ -15,84 +38,114 @@ } .variable-name { - font-size: 13px; - font-family: $code-font; - color: $c-honeydew; - padding: 5px 10px; + @extend %ifql-node; + // font-family: $code-font; + color: $c-laser; + line-height: $ifql-node-height; + white-space: nowrap; background-color: $g3-castle; - border-radius: $radius; - margin-bottom: 2px; - width: 100%; @include no-user-select(); } - .expression-node { - width: 100%; display: flex; - flex-direction: column; } .func-node { - width: 100%; + @extend %ifql-node; display: flex; - align-items: stretch; - position: relative; - margin-bottom: 2px; + align-items: center; + background-color: $g4-onyx; + margin-left: $ifql-node-gap; + transition: background-color 0.25s ease; + + // Connection Line + &:after { + content: ''; + height: 4px; + width: $ifql-node-gap; + background-color: $g4-onyx; + position: absolute; + top: 50%; + left: 0; + transform: translate(-100%, -50%); + } + + &:hover { + background-color: $g6-smoke; + } +} +.func-node--name, +.func-node--preview { + font-size: 13px; + @include no-user-select(); + white-space: nowrap; + transition: color 0.25s ease; + font-weight: 600; } .func-node--name { - background-color: $g4-onyx; - padding: 10px; - color: $ix-text-default; - font-size: 13px; - border-radius: $radius 0 0 $radius; - font-weight: 600; - width: 130px; - @include no-user-select(); - display: flex; - overflow: hidden; - white-space: nowrap; - text-overflow: ellipsis; - align-items: center; - align-content: center; + color: $c-comet; + + .func-node:hover & { + color: $c-potassium; + } } -.func-args { +.func-node--preview { + color: $g13-mist; + margin-left: 4px; + + .func-node:hover & { + color: $g17-whisper; + } +} + + +.func-node--tooltip { background-color: $g3-castle; - border-radius: 0 $radius $radius 0; + border-radius: $radius; padding: 10px; - flex: 1 0 0; display: flex; align-items: stretch; flex-direction: column; - color: $ix-text-default; - font-family: $ix-text-font; - font-weight: 500; + position: absolute; + top: $ifql-node-tooltip-gap; + left: 0; + z-index: 9999; + 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; + position: absolute; + top: 0; + left: $ifql-node-padding + 3px; + transform: translate(-50%, -100%); + } + + // Invisible block to continue hovering + &:after { + content: ''; + width: 80%; + height: 7px; + position: absolute; + top: -7px; + left: 0; + } } .func-node--delete { - position: absolute; - width: 22px; - height: 22px; - top: 0; - right: -26px; - border-radius: 50%; - background-color: $c-curacao; - opacity: 0; - transition: background-color 0.25s ease, opacity 0.25s ease; - - &:hover { - background-color: $c-dreamsicle; - cursor: pointer; - } - - .func-node:hover & { - opacity: 1; - } + margin-top: 12px; + width: 60px; } .func-arg { + min-width: $ifql-arg-min-width; display: flex; flex-wrap: nowrap; align-items: center; @@ -104,10 +157,10 @@ } .func-arg--label { white-space: nowrap; - font-size: 12px; + font-size: 13px; font-weight: 600; color: $g10-wolf; - min-width: 40px; + padding-right: 8px; @include no-user-select(); } .func-arg--value { diff --git a/ui/src/style/components/funcs-button.scss b/ui/src/style/components/funcs-button.scss index 6862571ec..ec200d9b3 100644 --- a/ui/src/style/components/funcs-button.scss +++ b/ui/src/style/components/funcs-button.scss @@ -3,11 +3,37 @@ ---------------------------------------------------------------------------- */ +$ifql-func-selector--gap: 10px; +$ifql-func-selector--height: 30px; + .ifql-func--selector { + display: flex; + align-items: center; position: relative; + + &.open { + z-index: 9999; + } +} + +.func-selector--connector { + width: $ifql-func-selector--gap; + height: $ifql-func-selector--height; + position: relative; + + &:after { + content: ''; + position: absolute; + top: 50%; + width: 100%; + height: 4px; + transform: translateY(-50%); + @include gradient-h($g4-onyx, $c-pool); + } } .ifql-func--button { + float: left; &:focus { box-shadow: 0 0 8px 3px $c-amethyst; } @@ -16,17 +42,18 @@ .ifql-func--autocomplete, .ifql-func--list { position: absolute; - left: 0; width: 166px; } .ifql-func--autocomplete { + left: $ifql-func-selector--gap; top: 0; } .ifql-func--list { - border-radius: 4px; - top: 30px; + left: 0; + border-radius: $radius; + top: $ifql-func-selector--height; padding: 0; margin: 0; @extend %no-user-select;