feat: add a All Static Inputs module to the flow editor

This commit is contained in:
Ruben Fiszel
2022-12-30 05:31:32 +01:00
parent 7ca81bbcff
commit a20d3fae6f
12 changed files with 247 additions and 131 deletions

View File

@@ -27,7 +27,7 @@
export let pickForField: string | undefined = undefined
export let variableEditor: VariableEditor | undefined = undefined
export let itemPicker: ItemPicker | undefined = undefined
export let noVariablePicker = false
export let noDynamicToggle = false
let monaco: SimpleEditor | undefined = undefined
let monacoTemplate: TemplateEditor | undefined = undefined
@@ -113,7 +113,7 @@
const { focusProp, propPickerConfig } = getContext<PropPickerWrapperContext>('PropPickerWrapper')
$: isStaticTemplate(inputCat) && propertyType == 'static' && setPropertyType(arg.value)
$: isStaticTemplate(inputCat) && propertyType == 'static' && setPropertyType(arg?.value)
const openBracket = '${'
const closeBracket = '}'
</script>
@@ -141,75 +141,83 @@
</span>
{/if}
</div>
<div class="flex flex-row gap-x-4 gap-y-1 flex-wrap">
<ToggleButtonGroup
bind:selected={propertyType}
on:selected={(e) => {
const staticTemplate = isStaticTemplate(inputCat)
if (e.detail === 'javascript') {
if (arg.expr == undefined) {
arg.expr = getDefaultExpr(
argName,
previousModuleId,
staticTemplate
? `\`${arg.value ?? ''}\``
: arg.value
? JSON.stringify(arg.value, null, 4)
: ''
)
}
arg.value = undefined
propertyType = 'javascript'
arg.type = 'javascript'
} else {
if (staticTemplate) {
arg.value = codeToStaticTemplate(arg.expr)
setPropertyType(arg.value)
{#if !noDynamicToggle}
<div class="flex flex-row gap-x-4 gap-y-1 flex-wrap">
<ToggleButtonGroup
bind:selected={propertyType}
on:selected={(e) => {
const staticTemplate = isStaticTemplate(inputCat)
if (e.detail === 'javascript') {
if (arg.expr == undefined) {
arg.expr = getDefaultExpr(
argName,
previousModuleId,
staticTemplate
? `\`${arg?.value ?? ''}\``
: arg.value
? JSON.stringify(arg?.value, null, 4)
: ''
)
}
if (arg) {
arg.value = undefined
}
propertyType = 'javascript'
arg.type = 'javascript'
} else {
arg.type = 'static'
arg.value = undefined
arg.expr = undefined
if (staticTemplate) {
if (arg) {
arg.value = codeToStaticTemplate(arg.expr)
}
setPropertyType(arg?.value)
} else {
if (arg) {
arg.type = 'static'
arg.value = undefined
arg.expr = undefined
}
}
propertyType = 'static'
}
propertyType = 'static'
}
}}
>
{#if isStaticTemplate(inputCat)}
<ToggleButton light position="left" value="static" size="xs">
{'${} '}Templatable &nbsp; <Tooltip
>Write javascript expressions between "{openBracket}" and "{closeBracket}". You may
refer to contextual objects like 'flow_input', or 'result' or functions like
'resource' and 'variable'
</Tooltip></ToggleButton
>
{:else}
<ToggleButton light position="left" value="static" size="xs">Static</ToggleButton>
{/if}
<ToggleButton
light
position="right"
value="javascript"
startIcon={{ icon: faCode }}
size="xs"
}}
>
Dynamic (JS)
</ToggleButton>
</ToggleButtonGroup>
<Button
variant="contained"
color="blue"
size="xs"
on:click={() => {
focusProp(argName, 'connect', (path) => {
connectProperty(path)
return true
})
}}>Connect &rightarrow;</Button
>
</div>
{#if isStaticTemplate(inputCat)}
<ToggleButton light position="left" value="static" size="xs">
{'${} '}Templatable &nbsp; <Tooltip
>Write javascript expressions between "{openBracket}" and "{closeBracket}". You may
refer to contextual objects like 'flow_input', or 'result' or functions like
'resource' and 'variable'
</Tooltip></ToggleButton
>
{:else}
<ToggleButton light position="left" value="static" size="xs">Static</ToggleButton>
{/if}
<ToggleButton
light
position="right"
value="javascript"
startIcon={{ icon: faCode }}
size="xs"
>
Dynamic (JS)
</ToggleButton>
</ToggleButtonGroup>
<Button
variant="contained"
color="blue"
size="xs"
on:click={() => {
focusProp(argName, 'connect', (path) => {
connectProperty(path)
return true
})
}}>Connect &rightarrow;</Button
>
</div>
{/if}
</div>
<div class="max-w-xs" />
<div
@@ -224,15 +232,17 @@
Connect input &rightarrow;
</span>
{/if}
{#if isStaticTemplate(inputCat) && propertyType == 'static'}
<div class="py-1 rounded border border-1 border-gray-500">
<TemplateEditor
bind:this={monacoTemplate}
{extraLib}
on:focus={onFocus}
bind:code={arg.value}
fontSize={12}
/>
{#if isStaticTemplate(inputCat) && propertyType == 'static' && !noDynamicToggle}
<div class="py-1 mt-2 min-h-[28px] rounded border border-1 border-gray-500">
{#if arg}
<TemplateEditor
bind:this={monacoTemplate}
{extraLib}
on:focus={onFocus}
bind:code={arg.value}
fontSize={14}
/>
{/if}
</div>
{:else if propertyType === undefined || propertyType == 'static'}
<ArgInput

View File

@@ -55,7 +55,7 @@
use:popperContent={extraOpts}
on:mouseenter={open}
on:mouseleave={close}
class="z-50 text-sm font-normal text-gray-300 bg-gray-800 py-2 px-3 rounded-md
class="z-50 py-2 px-3 rounded-md text-sm font-normal !text-gray-300 bg-gray-800
whitespace-normal text-left {$$props.class}"
>
<div class="max-w-sm">

View File

@@ -26,6 +26,8 @@
export let compact = false
export let password: string | undefined = undefined
export let noVariablePicker = false
export let filter: string[] | undefined = undefined
export let noDynamicToggle = false
let clazz: string = ''
export { clazz as class }
@@ -56,48 +58,50 @@
<div class="w-full {clazz}">
{#if Object.keys(schema?.properties ?? {}).length > 0}
{#each Object.keys(schema?.properties ?? {}) as argName, i (argName)}
<div transition:slide|local>
{#if inputTransform}
<InputTransformForm
{previousModuleId}
bind:arg={args[argName]}
bind:schema
bind:argName
bind:inputCheck={inputCheck[argName]}
bind:extraLib
{variableEditor}
{itemPicker}
{noVariablePicker}
bind:pickForField
/>
{:else if typeof args == 'object'}
<ArgInput
autofocus={i == 0 && autofocus}
label={argName}
bind:description={schema.properties[argName].description}
bind:value={args[argName]}
type={schema.properties[argName].type}
required={schema.required.includes(argName)}
bind:pattern={schema.properties[argName].pattern}
bind:valid={inputCheck[argName]}
defaultValue={schema.properties[argName].default}
bind:enum_={schema.properties[argName].enum}
bind:format={schema.properties[argName].format}
contentEncoding={schema.properties[argName].contentEncoding}
properties={schema.properties[argName].properties}
bind:itemsType={schema.properties[argName].items}
disabled={disabledArgs.includes(argName) || disabled}
{editableSchema}
{compact}
password={argName == password}
{variableEditor}
{itemPicker}
bind:pickForField
/>
{:else}
Expected argument to be an object, got {JSON.stringify(args)} instead
{/if}
</div>
{#if !filter || filter.includes(argName)}
<div transition:slide|local>
{#if inputTransform}
<InputTransformForm
{previousModuleId}
bind:arg={args[argName]}
bind:schema
bind:argName
bind:inputCheck={inputCheck[argName]}
bind:extraLib
{variableEditor}
{itemPicker}
bind:pickForField
{noDynamicToggle}
/>
{:else if typeof args == 'object'}
<ArgInput
autofocus={i == 0 && autofocus}
label={argName}
bind:description={schema.properties[argName].description}
bind:value={args[argName]}
type={schema.properties[argName].type}
required={schema.required.includes(argName)}
bind:pattern={schema.properties[argName].pattern}
bind:valid={inputCheck[argName]}
defaultValue={schema.properties[argName].default}
bind:enum_={schema.properties[argName].enum}
bind:format={schema.properties[argName].format}
contentEncoding={schema.properties[argName].contentEncoding}
properties={schema.properties[argName].properties}
bind:itemsType={schema.properties[argName].items}
disabled={disabledArgs.includes(argName) || disabled}
{editableSchema}
{compact}
password={argName == password}
{variableEditor}
{itemPicker}
bind:pickForField
/>
{:else}
Expected argument to be an object, got {JSON.stringify(args)} instead
{/if}
</div>
{/if}
{/each}
{:else if !shouldHideNoInputs}
<div class="text-gray-500 text-sm">No inputs</div>

View File

@@ -423,7 +423,7 @@
lineNumbers: 'off',
fontSize,
suggestOnTriggerCharacters: true,
lineDecorationsWidth: 20
lineDecorationsWidth: 24
})
const stdLib = { content: libStdContent, filePath: 'es5.d.ts' }

View File

@@ -0,0 +1,68 @@
<script lang="ts">
import { flowStore } from '$lib/components/flows/flowStore'
import FlowCard from '../common/FlowCard.svelte'
import { Alert, Badge } from '$lib/components/common'
import type { FlowModule, FlowModuleValue, InputTransform, PathScript, RawScript } from '$lib/gen'
import SchemaForm from '$lib/components/SchemaForm.svelte'
import { flowStateStore } from '../flowState'
import { setContext } from 'svelte'
import type { PropPickerWrapperContext } from '../propPicker/PropPickerWrapper.svelte'
import { writable } from 'svelte/store'
$: steps = (
$flowStore.value.modules
.map((x) => [x.value, x] as [FlowModuleValue, FlowModule])
.filter((x) => x[0].type == 'script' || x[0].type == 'rawscript') as [
PathScript | RawScript,
FlowModule
][]
).map(
([v, m]) =>
[
v.input_transforms,
Object.entries(v.input_transforms)
.filter((x) => x[1].type == 'static')
.map((x) => x[0]),
m
] as [Record<string, InputTransform>, string[], FlowModule]
)
setContext<PropPickerWrapperContext>('PropPickerWrapper', {
focusProp: () => {},
propPickerConfig: writable(undefined),
clearFocus: () => {}
})
</script>
<div class="h-full overflow-hidden">
<FlowCard title="All Static Inputs">
<div class="h-full flex-1">
<Alert type="info" title="Static Inputs"
>This page centralizes the static inputs of every steps. It is akin to a file containing all
constants. Modifying a value here modify it in the step input directly. It is especially
useful when forking a flow to get an overview of all the variables to parametrize that are
not exposed directly as flow inputs.</Alert
>
{#each steps as [args, filter, m] (m.id)}
{#if filter.length > 0}
<div class="box">
<h2 class="pb-4 inline-flex items-center"
><span class="mr-4">{m.summary || m.value['path'] || 'Inline script'}</span>
<Badge large color="indigo">{m.id}</Badge>
</h2>
<SchemaForm
noDynamicToggle
inputTransform
{filter}
schema={$flowStateStore[m.id]?.schema ?? {}}
bind:args
/>
</div>
{/if}
{/each}
</div>
</FlowCard>
</div>

View File

@@ -7,6 +7,7 @@
import FlowInput from './FlowInput.svelte'
import FlowFailureModule from './FlowFailureModule.svelte'
import { flowStore } from '../flowStore'
import FlowConstants from './FlowConstants.svelte'
export let initialPath: string
@@ -17,6 +18,8 @@
<FlowSettings {initialPath} />
{:else if $selectedId === 'inputs'}
<FlowInput />
{:else if $selectedId === 'constants'}
<FlowConstants />
{:else if $selectedId === 'failure'}
<FlowFailureModule />
{:else}

View File

@@ -20,7 +20,7 @@
<div class="flex flex-row space-x-2" bind:clientWidth={width}>
{#if module.value.type === 'script' || module.value.type === 'rawscript'}
<Popover
class="center-center rounded border p-2 duration-200
class="center-center rounded border p-2
{moduleRetry
? 'bg-blue-100 text-blue-800 border-blue-300 hover:bg-blue-200'
: 'bg-white text-gray-800 border-gray-300 hover:bg-gray-100'}"
@@ -30,7 +30,7 @@
<svelte:fragment slot="text">Retries</svelte:fragment>
</Popover>
<Popover
class="center-center rounded border p-2 duration-200
class="center-center rounded border p-2
{module.stop_after_if
? 'bg-blue-100 text-blue-800 border-blue-300 hover:bg-blue-200'
: 'bg-white text-gray-800 border-gray-300 hover:bg-gray-100'}"
@@ -40,7 +40,7 @@
<svelte:fragment slot="text">Early stop/break</svelte:fragment>
</Popover>
<Popover
class="center-center rounded border p-2 duration-200
class="center-center rounded border p-2
{module.suspend
? 'bg-blue-100 text-blue-800 border-blue-300 hover:bg-blue-200'
: 'bg-white text-gray-800 border-gray-300 hover:bg-gray-100'}"
@@ -50,7 +50,7 @@
<svelte:fragment slot="text">Suspend</svelte:fragment>
</Popover>
<Popover
class="center-center rounded border p-2 duration-200
class="center-center rounded border p-2
{module.sleep
? 'bg-blue-100 text-blue-800 border-blue-300 hover:bg-blue-200'
: 'bg-white text-gray-800 border-gray-300 hover:bg-gray-100'}"

View File

@@ -27,7 +27,8 @@
'settings-graph',
'inputs',
'schedules',
'failure'
'failure',
'constants'
].includes($selectedId) ||
$selectedId?.includes('branch')

View File

@@ -0,0 +1,22 @@
<script lang="ts">
import type { FlowEditorContext } from '../types'
import { getContext } from 'svelte'
import Icon from 'svelte-awesome'
import { faDollarSign } from '@fortawesome/free-solid-svg-icons'
import { classNames } from '$lib/utils'
const { select, selectedId } = getContext<FlowEditorContext>('FlowEditorContext')
$: settingsClass = classNames(
'border w-full rounded-sm p-2 bg-white border-gray-400 text-sm cursor-pointer flex items-center',
$selectedId == 'constants' ? 'outline outline-offset-1 outline-2 outline-slate-900' : ''
)
</script>
<!-- svelte-ignore a11y-click-events-have-key-events -->
<div on:click={() => select('constants')} class={settingsClass}>
<Icon data={faDollarSign} class="mr-2" />
<span class="text-xs flex flex-row justify-between w-full gap-2 items-center truncate">
All Static Inputs
</span>
</div>

View File

@@ -3,14 +3,13 @@
import { getContext } from 'svelte'
import FlowModuleSchemaItem from './FlowModuleSchemaItem.svelte'
import Icon from 'svelte-awesome'
import { faFlagCheckered, faPen } from '@fortawesome/free-solid-svg-icons'
import { faFlagCheckered } from '@fortawesome/free-solid-svg-icons'
const { select, selectedId } = getContext<FlowEditorContext>('FlowEditorContext')
</script>
<FlowModuleSchemaItem
on:click={() => select('inputs')}
isFirst
hasLine
selected={$selectedId === 'inputs'}
bold

View File

@@ -19,6 +19,10 @@
import FlowInputsItem from './FlowInputsItem.svelte'
import InsertModuleButton from './InsertModuleButton.svelte'
import { slide } from 'svelte/transition'
import FlowModuleSchemaItem from './FlowModuleSchemaItem.svelte'
import { Icon } from 'svelte-awesome'
import { faDollarSign } from '@fortawesome/free-solid-svg-icons'
import FlowConstantsItem from './FlowConstantsItem.svelte'
export let root: boolean = false
export let modules: FlowModule[] | undefined
@@ -102,12 +106,17 @@
<div class="flex flex-col h-full relative">
{#if root}
<div
class="z-10 sticky top-0 bg-gray-50 flex-initial inline-flex px-3 py-2 items-center h-full max-h-12 border-b border-gray-300"
class="z-10 sticky inline-flex top-0 bg-gray-50 flex-initial px-3 py-2 items-center h-full max-h-12 border-b border-gray-300"
>
<FlowSettingsItem />
</div>
<div
class="z-10 sticky inline-flex top-0 bg-gray-50 flex-initial px-3 py-2 items-center h-full max-h-12 border-b border-gray-300"
>
<FlowConstantsItem />
</div>
{/if}
<ul class="w-full flex-auto {root ? 'px-2 pb-2 pt-3' : ''} py-1">
<ul class="w-full flex-auto {root ? 'px-2 pb-2 pt-3' : ''} py-1">
{#if root}
<li>
<FlowInputsItem />

View File

@@ -2,12 +2,12 @@
import type { FlowEditorContext } from '../types'
import { getContext } from 'svelte'
import Icon from 'svelte-awesome'
import { faCalendarAlt, faSliders } from '@fortawesome/free-solid-svg-icons'
import { faSliders } from '@fortawesome/free-solid-svg-icons'
import { classNames } from '$lib/utils'
import { Badge } from '$lib/components/common'
import { flowStore } from '../flowStore'
const { select, selectedId, schedule } = getContext<FlowEditorContext>('FlowEditorContext')
const { select, selectedId } = getContext<FlowEditorContext>('FlowEditorContext')
$: settingsClass = classNames(
'border w-full rounded-sm p-2 bg-white border-gray-400 text-sm cursor-pointer flex items-center',