From d851e526431601c0b07e935134e12f8fa14d2499 Mon Sep 17 00:00:00 2001 From: Alex P Date: Thu, 14 Jun 2018 15:30:28 -0700 Subject: [PATCH] Refactor func node UI & UX - Nothing happens when hovering a func node - Delete & toggle yield buttons appear on hover, stay visible when editing or yielding - Clicking a func enters/exits edit mode - Last func in each body is in edit more for educational purposes --- ui/src/flux/components/ExpressionNode.tsx | 2 + ui/src/flux/components/FuncNode.tsx | 131 +++++++++++++----- .../components/time-machine/flux-builder.scss | 61 ++++---- 3 files changed, 132 insertions(+), 62 deletions(-) diff --git a/ui/src/flux/components/ExpressionNode.tsx b/ui/src/flux/components/ExpressionNode.tsx index 45d0bb195..b70d4f4aa 100644 --- a/ui/src/flux/components/ExpressionNode.tsx +++ b/ui/src/flux/components/ExpressionNode.tsx @@ -94,6 +94,7 @@ class ExpressionNode extends PureComponent { key={i} index={i} func={func} + funcs={funcs} bodyID={bodyID} service={service} onChangeArg={onChangeArg} @@ -139,6 +140,7 @@ class ExpressionNode extends PureComponent { key={i} index={i} func={func} + funcs={funcs} bodyID={bodyID} service={service} onChangeArg={onChangeArg} diff --git a/ui/src/flux/components/FuncNode.tsx b/ui/src/flux/components/FuncNode.tsx index b2e18b3d7..84594a233 100644 --- a/ui/src/flux/components/FuncNode.tsx +++ b/ui/src/flux/components/FuncNode.tsx @@ -1,5 +1,6 @@ import React, {PureComponent, MouseEvent} from 'react' import classnames from 'classnames' +import _ from 'lodash' import FuncArgs from 'src/flux/components/FuncArgs' import FuncArgsPreview from 'src/flux/components/FuncArgsPreview' @@ -14,6 +15,7 @@ import {Service} from 'src/types' interface Props { func: Func + funcs: Func[] service: Service bodyID: string index: number @@ -29,7 +31,7 @@ interface Props { } interface State { - isExpanded: boolean + editing: boolean } @ErrorHandling @@ -42,53 +44,107 @@ export default class FuncNode extends PureComponent { super(props) this.state = { - isExpanded: false, + editing: this.isLast, } } public render() { + const {func} = this.props + + return ( + <> +
+
+
{func.name}
+ + {this.funcMenu} +
+ {this.funcArgs} + + ) + } + + private get funcArgs(): JSX.Element { const { func, bodyID, service, + isYielding, onChangeArg, declarationID, onGenerateScript, declarationsFromBody, } = this.props - const {isExpanded} = this.state + const {editing} = this.state + + if (!editing || isYielding) { + return + } return ( -
-
-
{func.name}
- + + ) + } - {isExpanded && ( - - )} + private get funcMenu(): JSX.Element { + return ( +
+ {this.yieldToggleButton} +
) } + private get yieldToggleButton(): JSX.Element { + const {isYielding} = this.props + + if (isYielding) { + return ( + + ) + } + + return ( + + ) + } + private get nodeClassName(): string { const {isYielding} = this.props - return classnames('func-node', {active: isYielding}) + const {editing} = this.state + + return classnames('func-node', {active: isYielding || editing}) } private handleDelete = (e: MouseEvent): void => { @@ -98,19 +154,11 @@ export default class FuncNode extends PureComponent { this.props.onDelete({funcID: func.id, bodyID, declarationID}) } - private handleMouseEnter = (e: MouseEvent): void => { - e.stopPropagation() - - this.setState({isExpanded: true}) + private handleToggleEdit = (): void => { + this.setState({editing: !this.state.editing}) } - private handleMouseLeave = (e: MouseEvent): void => { - e.stopPropagation() - - this.setState({isExpanded: false}) - } - - private handleClick = (e: MouseEvent): void => { + private handleToggleYield = (e: MouseEvent): void => { e.stopPropagation() const { @@ -128,7 +176,16 @@ export default class FuncNode extends PureComponent { onToggleYieldWithLast(index) } } + private handleClickArgs = (e: MouseEvent): void => { e.stopPropagation() } + + private get isLast(): boolean { + const {funcs, func} = this.props + + const lastFunc = _.last(funcs) + + return lastFunc.id === func.id + } } diff --git a/ui/src/style/components/time-machine/flux-builder.scss b/ui/src/style/components/time-machine/flux-builder.scss index dde81b096..6e7e2925c 100644 --- a/ui/src/style/components/time-machine/flux-builder.scss +++ b/ui/src/style/components/time-machine/flux-builder.scss @@ -248,35 +248,47 @@ $flux-invalid-hover: $c-dreamsicle; } } -.func-node--tooltip { +.func-node--menu { + display: flex; + align-items: center; + position: absolute; + top: 50%; + right: 0; + transform: translate(100%, -50%); + opacity: 0; + transition: opacity 0.25s ease; + + > button.btn { + margin-left: 4px; + } +} + +.func-node:hover .func-node--menu, +.func-node.editing .func-node--menu, +.func-node.active .func-node--menu { + opacity: 1; +} + +.func-node--editor { + position: relative; + margin-left: $flux-node-gap; + margin-bottom: $flux-node-gap; + margin-top: $flux-node-tooltip-gap / 2; background-color: $g3-castle; border-radius: $radius; - padding: 10px; + padding: 6px; display: flex; align-items: stretch; - position: absolute; - top: 0; - left: calc(100% + #{$flux-node-tooltip-gap}); - z-index: 9999; - box-shadow: 0 0 10px 2px $g2-kevlar; // Caret +} + +.func-node--editor .func-node--connector { + // Vertical Line &:before { - content: ''; - border-width: 9px; - border-style: solid; - border-color: transparent; - border-right-color: $g3-castle; - position: absolute; - top: $flux-node-height / 2; - left: 0; - transform: translate(-100%, -50%); - } // Invisible block to continue hovering + height: calc(100% + #{($flux-node-tooltip-gap / 2) + $flux-node-gap}); + } + // Horizontal Line &:after { - content: ''; - height: 50%; - width: $flux-node-tooltip-gap * 3; - position: absolute; - top: 0; - left: -$flux-node-tooltip-gap * 3; + content: none; } } @@ -284,7 +296,6 @@ $flux-invalid-hover: $c-dreamsicle; display: flex; flex-direction: column; justify-content: center; - margin-left: 8px; } .func-node--build { @@ -313,7 +324,7 @@ $flux-invalid-hover: $c-dreamsicle; font-size: 13px; font-weight: 600; color: $g10-wolf; - padding-right: 8px; + padding: 0 8px; @include no-user-select(); }