feat: add a All Static Inputs module to the flow editor
This commit is contained in:
@@ -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 <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 →</Button
|
||||
>
|
||||
</div>
|
||||
{#if isStaticTemplate(inputCat)}
|
||||
<ToggleButton light position="left" value="static" size="xs">
|
||||
{'${} '}Templatable <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 →</Button
|
||||
>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<div class="max-w-xs" />
|
||||
|
||||
<div
|
||||
@@ -224,15 +232,17 @@
|
||||
Connect input →
|
||||
</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
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -423,7 +423,7 @@
|
||||
lineNumbers: 'off',
|
||||
fontSize,
|
||||
suggestOnTriggerCharacters: true,
|
||||
lineDecorationsWidth: 20
|
||||
lineDecorationsWidth: 24
|
||||
})
|
||||
|
||||
const stdLib = { content: libStdContent, filePath: 'es5.d.ts' }
|
||||
|
||||
@@ -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>
|
||||
@@ -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}
|
||||
|
||||
@@ -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'}"
|
||||
|
||||
@@ -27,7 +27,8 @@
|
||||
'settings-graph',
|
||||
'inputs',
|
||||
'schedules',
|
||||
'failure'
|
||||
'failure',
|
||||
'constants'
|
||||
].includes($selectedId) ||
|
||||
$selectedId?.includes('branch')
|
||||
|
||||
|
||||
@@ -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>
|
||||
@@ -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
|
||||
|
||||
@@ -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 />
|
||||
|
||||
@@ -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',
|
||||
|
||||
Reference in New Issue
Block a user