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 purposespull/3684/head
parent
368f471199
commit
d851e52643
|
@ -94,6 +94,7 @@ class ExpressionNode extends PureComponent<Props, State> {
|
||||||
key={i}
|
key={i}
|
||||||
index={i}
|
index={i}
|
||||||
func={func}
|
func={func}
|
||||||
|
funcs={funcs}
|
||||||
bodyID={bodyID}
|
bodyID={bodyID}
|
||||||
service={service}
|
service={service}
|
||||||
onChangeArg={onChangeArg}
|
onChangeArg={onChangeArg}
|
||||||
|
@ -139,6 +140,7 @@ class ExpressionNode extends PureComponent<Props, State> {
|
||||||
key={i}
|
key={i}
|
||||||
index={i}
|
index={i}
|
||||||
func={func}
|
func={func}
|
||||||
|
funcs={funcs}
|
||||||
bodyID={bodyID}
|
bodyID={bodyID}
|
||||||
service={service}
|
service={service}
|
||||||
onChangeArg={onChangeArg}
|
onChangeArg={onChangeArg}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import React, {PureComponent, MouseEvent} from 'react'
|
import React, {PureComponent, MouseEvent} from 'react'
|
||||||
import classnames from 'classnames'
|
import classnames from 'classnames'
|
||||||
|
import _ from 'lodash'
|
||||||
|
|
||||||
import FuncArgs from 'src/flux/components/FuncArgs'
|
import FuncArgs from 'src/flux/components/FuncArgs'
|
||||||
import FuncArgsPreview from 'src/flux/components/FuncArgsPreview'
|
import FuncArgsPreview from 'src/flux/components/FuncArgsPreview'
|
||||||
|
@ -14,6 +15,7 @@ import {Service} from 'src/types'
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
func: Func
|
func: Func
|
||||||
|
funcs: Func[]
|
||||||
service: Service
|
service: Service
|
||||||
bodyID: string
|
bodyID: string
|
||||||
index: number
|
index: number
|
||||||
|
@ -29,7 +31,7 @@ interface Props {
|
||||||
}
|
}
|
||||||
|
|
||||||
interface State {
|
interface State {
|
||||||
isExpanded: boolean
|
editing: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
@ErrorHandling
|
@ErrorHandling
|
||||||
|
@ -42,53 +44,107 @@ export default class FuncNode extends PureComponent<Props, State> {
|
||||||
super(props)
|
super(props)
|
||||||
|
|
||||||
this.state = {
|
this.state = {
|
||||||
isExpanded: false,
|
editing: this.isLast,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public render() {
|
public render() {
|
||||||
|
const {func} = this.props
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<div
|
||||||
|
className={this.nodeClassName}
|
||||||
|
onClick={this.handleToggleEdit}
|
||||||
|
title="Edit function arguments"
|
||||||
|
>
|
||||||
|
<div className="func-node--connector" />
|
||||||
|
<div className="func-node--name">{func.name}</div>
|
||||||
|
<FuncArgsPreview func={func} />
|
||||||
|
{this.funcMenu}
|
||||||
|
</div>
|
||||||
|
{this.funcArgs}
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private get funcArgs(): JSX.Element {
|
||||||
const {
|
const {
|
||||||
func,
|
func,
|
||||||
bodyID,
|
bodyID,
|
||||||
service,
|
service,
|
||||||
|
isYielding,
|
||||||
onChangeArg,
|
onChangeArg,
|
||||||
declarationID,
|
declarationID,
|
||||||
onGenerateScript,
|
onGenerateScript,
|
||||||
declarationsFromBody,
|
declarationsFromBody,
|
||||||
} = this.props
|
} = this.props
|
||||||
const {isExpanded} = this.state
|
const {editing} = this.state
|
||||||
|
|
||||||
|
if (!editing || isYielding) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<FuncArgs
|
||||||
className={this.nodeClassName}
|
func={func}
|
||||||
onMouseEnter={this.handleMouseEnter}
|
bodyID={bodyID}
|
||||||
onMouseLeave={this.handleMouseLeave}
|
service={service}
|
||||||
onClick={this.handleClick}
|
onChangeArg={onChangeArg}
|
||||||
>
|
declarationID={declarationID}
|
||||||
<div className="func-node--connector" />
|
onGenerateScript={onGenerateScript}
|
||||||
<div className="func-node--name">{func.name}</div>
|
declarationsFromBody={declarationsFromBody}
|
||||||
<FuncArgsPreview func={func} />
|
onStopPropagation={this.handleClickArgs}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
{isExpanded && (
|
private get funcMenu(): JSX.Element {
|
||||||
<FuncArgs
|
return (
|
||||||
func={func}
|
<div className="func-node--menu">
|
||||||
bodyID={bodyID}
|
{this.yieldToggleButton}
|
||||||
service={service}
|
<button
|
||||||
onChangeArg={onChangeArg}
|
className="btn btn-sm btn-square btn-danger"
|
||||||
declarationID={declarationID}
|
onClick={this.handleDelete}
|
||||||
onGenerateScript={onGenerateScript}
|
title="Delete this Function"
|
||||||
onDeleteFunc={this.handleDelete}
|
>
|
||||||
declarationsFromBody={declarationsFromBody}
|
<span className="icon trash" />
|
||||||
onStopPropagation={this.handleClickArgs}
|
</button>
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private get yieldToggleButton(): JSX.Element {
|
||||||
|
const {isYielding} = this.props
|
||||||
|
|
||||||
|
if (isYielding) {
|
||||||
|
return (
|
||||||
|
<button
|
||||||
|
className="btn btn-sm btn-square btn-warning"
|
||||||
|
onClick={this.handleToggleYield}
|
||||||
|
title="Hide Data Table"
|
||||||
|
>
|
||||||
|
<span className="icon eye-closed" />
|
||||||
|
</button>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<button
|
||||||
|
className="btn btn-sm btn-square btn-warning"
|
||||||
|
onClick={this.handleToggleYield}
|
||||||
|
title="See Data Table returned by this Function"
|
||||||
|
>
|
||||||
|
<span className="icon eye-open" />
|
||||||
|
</button>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
private get nodeClassName(): string {
|
private get nodeClassName(): string {
|
||||||
const {isYielding} = this.props
|
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<HTMLElement>): void => {
|
private handleDelete = (e: MouseEvent<HTMLElement>): void => {
|
||||||
|
@ -98,19 +154,11 @@ export default class FuncNode extends PureComponent<Props, State> {
|
||||||
this.props.onDelete({funcID: func.id, bodyID, declarationID})
|
this.props.onDelete({funcID: func.id, bodyID, declarationID})
|
||||||
}
|
}
|
||||||
|
|
||||||
private handleMouseEnter = (e: MouseEvent<HTMLElement>): void => {
|
private handleToggleEdit = (): void => {
|
||||||
e.stopPropagation()
|
this.setState({editing: !this.state.editing})
|
||||||
|
|
||||||
this.setState({isExpanded: true})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private handleMouseLeave = (e: MouseEvent<HTMLElement>): void => {
|
private handleToggleYield = (e: MouseEvent<HTMLElement>): void => {
|
||||||
e.stopPropagation()
|
|
||||||
|
|
||||||
this.setState({isExpanded: false})
|
|
||||||
}
|
|
||||||
|
|
||||||
private handleClick = (e: MouseEvent<HTMLElement>): void => {
|
|
||||||
e.stopPropagation()
|
e.stopPropagation()
|
||||||
|
|
||||||
const {
|
const {
|
||||||
|
@ -128,7 +176,16 @@ export default class FuncNode extends PureComponent<Props, State> {
|
||||||
onToggleYieldWithLast(index)
|
onToggleYieldWithLast(index)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private handleClickArgs = (e: MouseEvent<HTMLElement>): void => {
|
private handleClickArgs = (e: MouseEvent<HTMLElement>): void => {
|
||||||
e.stopPropagation()
|
e.stopPropagation()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private get isLast(): boolean {
|
||||||
|
const {funcs, func} = this.props
|
||||||
|
|
||||||
|
const lastFunc = _.last(funcs)
|
||||||
|
|
||||||
|
return lastFunc.id === func.id
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
background-color: $g3-castle;
|
||||||
border-radius: $radius;
|
border-radius: $radius;
|
||||||
padding: 10px;
|
padding: 6px;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: stretch;
|
align-items: stretch;
|
||||||
position: absolute;
|
}
|
||||||
top: 0;
|
|
||||||
left: calc(100% + #{$flux-node-tooltip-gap});
|
.func-node--editor .func-node--connector {
|
||||||
z-index: 9999;
|
// Vertical Line
|
||||||
box-shadow: 0 0 10px 2px $g2-kevlar; // Caret
|
|
||||||
&:before {
|
&:before {
|
||||||
content: '';
|
height: calc(100% + #{($flux-node-tooltip-gap / 2) + $flux-node-gap});
|
||||||
border-width: 9px;
|
}
|
||||||
border-style: solid;
|
// Horizontal Line
|
||||||
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
|
|
||||||
&:after {
|
&:after {
|
||||||
content: '';
|
content: none;
|
||||||
height: 50%;
|
|
||||||
width: $flux-node-tooltip-gap * 3;
|
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
left: -$flux-node-tooltip-gap * 3;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -284,7 +296,6 @@ $flux-invalid-hover: $c-dreamsicle;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
margin-left: 8px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.func-node--build {
|
.func-node--build {
|
||||||
|
@ -313,7 +324,7 @@ $flux-invalid-hover: $c-dreamsicle;
|
||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
color: $g10-wolf;
|
color: $g10-wolf;
|
||||||
padding-right: 8px;
|
padding: 0 8px;
|
||||||
@include no-user-select();
|
@include no-user-select();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue