Make IFQL builder stack horizontally
parent
5d2089e658
commit
c4731dfbc4
|
@ -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>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -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})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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: ''})
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in New Issue