Make IFQL builder stack horizontally

pull/3374/head
Alex P 2018-05-02 18:03:32 -07:00 committed by Andrew Watkins
parent 5d2089e658
commit c4731dfbc4
5 changed files with 198 additions and 74 deletions

View File

@ -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<Props> {
func,
bodyID,
onChangeArg,
onDeleteFunc,
declarationID,
onGenerateScript,
} = this.props
return (
<div className="func-args">
<div className="func-node--tooltip">
{func.args.map(({key, value, type}) => {
return (
<FuncArg
@ -41,6 +43,12 @@ export default class FuncArgs extends PureComponent<Props> {
/>
)
})}
<div
className="btn btn-sm btn-danger func-node--delete"
onClick={onDeleteFunc}
>
Delete
</div>
</div>
)
}

View File

@ -13,7 +13,7 @@ interface Props {
}
interface State {
isOpen: boolean
isExpanded: boolean
}
@ErrorHandling
@ -25,7 +25,7 @@ export default class FuncNode extends PureComponent<Props, State> {
constructor(props) {
super(props)
this.state = {
isOpen: true,
isExpanded: false,
}
}
@ -37,37 +37,65 @@ export default class FuncNode extends PureComponent<Props, State> {
declarationID,
onGenerateScript,
} = this.props
const {isOpen} = this.state
const {isExpanded} = this.state
return (
<div className="func-node">
<div className="func-node--name" onClick={this.handleClick}>
<div>{func.name}</div>
</div>
{isOpen && (
<div
className="func-node"
onMouseEnter={this.handleMouseEnter}
onMouseLeave={this.handleMouseLeave}
>
<div className="func-node--name">{func.name}</div>
<div className="func-node--preview">{this.stringifyArgs}</div>
{isExpanded && (
<FuncArgs
func={func}
bodyID={bodyID}
onChangeArg={onChangeArg}
declarationID={declarationID}
onGenerateScript={onGenerateScript}
onDeleteFunc={this.handleDelete}
/>
)}
<div className="func-node--delete" onClick={this.handleDelete} />
</div>
)
}
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<HTMLElement>): void => {
private handleMouseEnter = (e: MouseEvent<HTMLElement>): void => {
e.stopPropagation()
const {isOpen} = this.state
this.setState({isOpen: !isOpen})
this.setState({isExpanded: true})
}
private handleMouseLeave = (e: MouseEvent<HTMLElement>): void => {
e.stopPropagation()
this.setState({isExpanded: false})
}
}

View File

@ -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<Props, State> {
return (
<ClickOutside onClickOutside={this.handleClickOutside}>
<div className="ifql-func--selector">
<div className={this.className}>
<div className="func-selector--connector" />
{isOpen ? (
<FuncList
inputText={inputText}
@ -61,6 +63,12 @@ export class FuncSelector extends PureComponent<Props, State> {
)
}
private get className(): string {
const {isOpen} = this.state
return classnames('ifql-func--selector', {open: isOpen})
}
private handleCloseList = () => {
this.setState({isOpen: false, selectedFunc: ''})
}

View File

@ -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 {

View File

@ -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;