mirror of
https://github.com/windmill-labs/windmill.git
synced 2026-08-20 08:01:35 +00:00
fix: schema editor reactivity improvements (#6496)
* all * all * all * all * all * nit * all * all * all * all
This commit is contained in:
Generated
+2
-1
@@ -25,7 +25,7 @@
|
||||
"@scalar/openapi-parser": "^0.15.0",
|
||||
"@tanstack/svelte-table": "npm:tanstack-table-8-svelte-5@^0.1",
|
||||
"@tutorlatin/svelte-tiny-virtual-list": "^3.0.2",
|
||||
"@windmill-labs/svelte-dnd-action": "^0.9.48",
|
||||
"@windmill-labs/svelte-dnd-action": "^0.9.44",
|
||||
"@xterm/addon-fit": "^0.10.0",
|
||||
"@xyflow/svelte": "^1.0.0",
|
||||
"ag-charts-community": "^9.0.1",
|
||||
@@ -3898,6 +3898,7 @@
|
||||
"version": "0.9.48",
|
||||
"resolved": "https://registry.npmjs.org/@windmill-labs/svelte-dnd-action/-/svelte-dnd-action-0.9.48.tgz",
|
||||
"integrity": "sha512-A6pWayH3nOi79DZohTscGj5t2PuNtHlKM5WIAj9WlVDx5pS1A+MsyfuNZi8oCpj2590wFmQl+58G4y4xZqDa1Q==",
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"svelte": ">=3.23.0 || ^5.0.0-next.0"
|
||||
}
|
||||
|
||||
@@ -92,7 +92,7 @@
|
||||
"@scalar/openapi-parser": "^0.15.0",
|
||||
"@tanstack/svelte-table": "npm:tanstack-table-8-svelte-5@^0.1",
|
||||
"@tutorlatin/svelte-tiny-virtual-list": "^3.0.2",
|
||||
"@windmill-labs/svelte-dnd-action": "^0.9.48",
|
||||
"@windmill-labs/svelte-dnd-action": "^0.9.44",
|
||||
"@xterm/addon-fit": "^0.10.0",
|
||||
"@xyflow/svelte": "^1.0.0",
|
||||
"ag-charts-community": "^9.0.1",
|
||||
|
||||
Vendored
+2
-2
@@ -1,7 +1,7 @@
|
||||
/// <reference types="@sveltejs/kit" />
|
||||
|
||||
declare type Item = import('svelte-dnd-action').Item
|
||||
declare type DndEvent<ItemType = Item> = import('svelte-dnd-action').DndEvent<ItemType>
|
||||
declare type Item = import('@windmill-labs/svelte-dnd-action').Item
|
||||
declare type DndEvent<ItemType = Item> = import('@windmill-labs/svelte-dnd-action').DndEvent<ItemType>
|
||||
declare namespace svelte.JSX {
|
||||
interface HTMLAttributes<T> {
|
||||
onconsider?: (event: CustomEvent<DndEvent<ItemType>> & { target: EventTarget & T }) => void
|
||||
|
||||
@@ -992,8 +992,11 @@
|
||||
}
|
||||
}
|
||||
bind:args={value}
|
||||
dndType={`nested-${title}`}
|
||||
hiddenArgs={['label', 'kind']}
|
||||
hiddenArgs={[
|
||||
oneOf?.find((o) => Object.keys(o.properties ?? {}).includes('kind'))
|
||||
? 'kind'
|
||||
: 'label'
|
||||
]}
|
||||
on:reorder={(e) => {
|
||||
if (oneOf && oneOf[objIdx]) {
|
||||
const keys = e.detail
|
||||
@@ -1086,20 +1089,14 @@
|
||||
{disablePortal}
|
||||
{disabled}
|
||||
{prettifyHeader}
|
||||
bind:schema={
|
||||
() => ({
|
||||
properties,
|
||||
$schema: '',
|
||||
required: nestedRequired ?? [],
|
||||
type: 'object',
|
||||
order
|
||||
}),
|
||||
(newSchema) => {
|
||||
dispatch('nestedChange')
|
||||
}
|
||||
}
|
||||
schema={{
|
||||
properties,
|
||||
$schema: '',
|
||||
required: nestedRequired ?? [],
|
||||
type: 'object',
|
||||
order
|
||||
}}
|
||||
bind:args={value}
|
||||
dndType={`nested-${title}`}
|
||||
on:reorder={(e) => {
|
||||
const keys = e.detail
|
||||
order = keys
|
||||
|
||||
@@ -14,13 +14,18 @@
|
||||
import FlowPropertyEditor from './schema/FlowPropertyEditor.svelte'
|
||||
import PropertyEditor from './schema/PropertyEditor.svelte'
|
||||
import SimpleEditor from './SimpleEditor.svelte'
|
||||
import { createEventDispatcher, tick, untrack } from 'svelte'
|
||||
import { createEventDispatcher, untrack } from 'svelte'
|
||||
import ToggleButton from './common/toggleButton-v2/ToggleButton.svelte'
|
||||
import ToggleButtonGroup from './common/toggleButton-v2/ToggleButtonGroup.svelte'
|
||||
import Label from './Label.svelte'
|
||||
import { sendUserToast } from '$lib/toast'
|
||||
import Toggle from './Toggle.svelte'
|
||||
import { DynamicSelect, emptyString } from '$lib/utils'
|
||||
import {
|
||||
DynamicSelect,
|
||||
emptyString,
|
||||
generateRandomString,
|
||||
readFieldsRecursively
|
||||
} from '$lib/utils'
|
||||
import Popover from './meltComponents/Popover.svelte'
|
||||
import SchemaFormDnd from './schema/SchemaFormDND.svelte'
|
||||
import { deepEqual } from 'fast-equals'
|
||||
@@ -48,7 +53,6 @@
|
||||
isAppInput?: boolean
|
||||
displayWebhookWarning?: boolean
|
||||
onlyMaskPassword?: boolean
|
||||
dndType?: string | undefined
|
||||
editTab:
|
||||
| 'inputEditor'
|
||||
| 'history'
|
||||
@@ -75,6 +79,8 @@
|
||||
addProperty?: import('svelte').Snippet
|
||||
runButton?: import('svelte').Snippet
|
||||
extraTab?: import('svelte').Snippet
|
||||
schemaFormClassName?: string
|
||||
onChange?: (args: Record<string, any>) => void
|
||||
}
|
||||
|
||||
let {
|
||||
@@ -91,7 +97,6 @@
|
||||
isAppInput = false,
|
||||
displayWebhookWarning = false,
|
||||
onlyMaskPassword = false,
|
||||
dndType = undefined,
|
||||
editTab,
|
||||
previewSchema = undefined,
|
||||
editPanelInitialSize = undefined,
|
||||
@@ -110,7 +115,9 @@
|
||||
openEditTab,
|
||||
addProperty,
|
||||
runButton,
|
||||
extraTab
|
||||
extraTab,
|
||||
schemaFormClassName = undefined,
|
||||
onChange = undefined
|
||||
}: Props = $props()
|
||||
|
||||
$effect.pre(() => {
|
||||
@@ -125,6 +132,13 @@
|
||||
}
|
||||
})
|
||||
|
||||
$effect(() => {
|
||||
if (onChange) {
|
||||
readFieldsRecursively(args)
|
||||
onChange(args ?? {})
|
||||
}
|
||||
})
|
||||
|
||||
$effect(() => {
|
||||
if (schema && dynSelectCode !== undefined && dynSelectLang !== undefined) {
|
||||
if (dynSelectCode && dynSelectCode.trim()) {
|
||||
@@ -154,9 +168,10 @@
|
||||
let variableEditor: VariableEditor | undefined = $state(undefined)
|
||||
|
||||
let keys: string[] = $state(
|
||||
Array.isArray(schema?.order)
|
||||
(Array.isArray(schema?.order)
|
||||
? [...schema.order]
|
||||
: (Object.keys(schema?.properties ?? {}) ?? Object.keys(schema?.properties ?? {}))
|
||||
).filter((x) => !hiddenArgs?.includes(x))
|
||||
)
|
||||
|
||||
function alignOrderWithProperties(schema: {
|
||||
@@ -187,22 +202,18 @@
|
||||
return hasChanged
|
||||
}
|
||||
function onSchemaChange() {
|
||||
let editSchema = false
|
||||
if (alignOrderWithProperties(schema)) {
|
||||
console.log('alignOrderWithProperties', JSON.stringify(schema, null, 2))
|
||||
editSchema = true
|
||||
// console.log('alignOrderWithProperties', JSON.stringify(schema, null, 2))
|
||||
}
|
||||
let lkeys = schema?.order ?? Object.keys(schema?.properties ?? {})
|
||||
let lkeys = (schema?.order ?? Object.keys(schema?.properties ?? {})).filter(
|
||||
(x) => !hiddenArgs?.includes(x)
|
||||
)
|
||||
if (schema?.properties && !deepEqual(lkeys, keys)) {
|
||||
keys = [...lkeys]
|
||||
editSchema = true
|
||||
if (opened == undefined) {
|
||||
opened = keys[0]
|
||||
}
|
||||
}
|
||||
if (editSchema) {
|
||||
schema = schema
|
||||
}
|
||||
}
|
||||
|
||||
let opened: string | undefined = $state(untrack(() => keys[0]))
|
||||
@@ -248,30 +259,31 @@
|
||||
// clear the input
|
||||
el.value = oldName
|
||||
} else {
|
||||
let newSchema = $state.snapshot(schema)
|
||||
if (args) {
|
||||
args[newName] = args[oldName]
|
||||
delete args[oldName]
|
||||
}
|
||||
|
||||
schema.properties[newName] = schema.properties[oldName]
|
||||
delete schema.properties[oldName]
|
||||
newSchema.properties[newName] = newSchema.properties[oldName]
|
||||
delete newSchema.properties[oldName]
|
||||
|
||||
if (schema.required?.includes(oldName)) {
|
||||
schema.required = schema.required?.map((x) => (x === oldName ? newName : x))
|
||||
if (newSchema.required?.includes(oldName)) {
|
||||
newSchema.required = newSchema.required?.map((x) => (x === oldName ? newName : x))
|
||||
}
|
||||
|
||||
// Replace the old name with the new name in the order array
|
||||
if (schema.order) {
|
||||
const index = schema.order.indexOf(oldName)
|
||||
if (newSchema.order) {
|
||||
const index = newSchema.order.indexOf(oldName)
|
||||
if (index !== -1) {
|
||||
schema.order[index] = newName
|
||||
newSchema.order[index] = newName
|
||||
}
|
||||
}
|
||||
|
||||
opened = newName
|
||||
|
||||
schema = $state.snapshot(schema)
|
||||
dispatch('change', schema)
|
||||
schema = newSchema
|
||||
|
||||
sendUserToast('Argument renamed')
|
||||
}
|
||||
}
|
||||
@@ -368,6 +380,8 @@
|
||||
const code = generateFn(functionName)
|
||||
dynSelectCode = dynSelectCode ? dynSelectCode.concat(code) : code
|
||||
}
|
||||
|
||||
let dndType = $state(generateRandomString())
|
||||
</script>
|
||||
|
||||
<div class="w-full h-full">
|
||||
@@ -404,15 +418,15 @@
|
||||
class="min-h-0 overflow-y-auto grow rounded-md {runButton ? 'flex flex-col gap-2' : ''}"
|
||||
>
|
||||
<SchemaFormDnd
|
||||
nestedClasses={'flex flex-col gap-1'}
|
||||
{dndType}
|
||||
nestedClasses={'flex flex-col gap-1 ' + (schemaFormClassName ?? '')}
|
||||
bind:schema={
|
||||
() => (previewSchema ? previewSchema : schema),
|
||||
(newSchema) => {
|
||||
schema = newSchema
|
||||
tick().then(() => dispatch('change', schema))
|
||||
}
|
||||
}
|
||||
{dndType}
|
||||
{hiddenArgs}
|
||||
{disableDnd}
|
||||
{onlyMaskPassword}
|
||||
bind:args
|
||||
@@ -420,11 +434,16 @@
|
||||
opened = e.detail
|
||||
}}
|
||||
on:reorder={(e) => {
|
||||
let order = e.detail
|
||||
let newProperties = {}
|
||||
for (let key of order) {
|
||||
newProperties[key] = schema.properties[key]
|
||||
}
|
||||
schema = {
|
||||
...schema,
|
||||
properties: newProperties,
|
||||
order: e.detail
|
||||
}
|
||||
tick().then(() => dispatch('change', schema))
|
||||
}}
|
||||
helperScript={{
|
||||
type: 'inline',
|
||||
@@ -436,9 +455,6 @@
|
||||
{diff}
|
||||
on:acceptChange
|
||||
on:rejectChange
|
||||
on:nestedChange={() => {
|
||||
dispatch('change', schema)
|
||||
}}
|
||||
{shouldDispatchChanges}
|
||||
bind:isValid
|
||||
noVariablePicker={noVariablePicker || customUi?.disableVariablePicker === true}
|
||||
@@ -446,8 +462,8 @@
|
||||
|
||||
{@render runButton?.()}
|
||||
|
||||
<div class="h-full">
|
||||
{#if dynSelectFunctions.length > 0}
|
||||
{#if dynSelectFunctions.length > 0}
|
||||
<div class="grow">
|
||||
<Section
|
||||
label="Dynamic select functions"
|
||||
collapsable={true}
|
||||
@@ -494,8 +510,8 @@
|
||||
{/key}
|
||||
</div>
|
||||
</Section>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
</Pane>
|
||||
@@ -514,7 +530,7 @@
|
||||
{#if jsonEnabled && customUi?.jsonOnly != true}
|
||||
<div class="w-full p-3 flex gap-4 justify-end items-center">
|
||||
{#if addPropertyInEditorTab}
|
||||
<AddPropertyV2 bind:schema on:change>
|
||||
<AddPropertyV2 bind:schema>
|
||||
{#snippet trigger()}
|
||||
<Button color="light" size="xs" iconOnly startIcon={{ icon: Plus }} />
|
||||
{/snippet}
|
||||
@@ -629,7 +645,7 @@
|
||||
</div>
|
||||
{#if opened === argName}
|
||||
<div class="p-4 border-t">
|
||||
{#if !hiddenArgs.includes(argName) && Object.keys(schema?.properties ?? {}).includes(argName)}
|
||||
{#if Object.keys(schema?.properties ?? {}).includes(argName)}
|
||||
{#if typeof args == 'object' && schema?.properties[argName]}
|
||||
<PropertyEditor
|
||||
bind:description={schema.properties[argName].description}
|
||||
@@ -650,10 +666,6 @@
|
||||
bind:order={schema.properties[argName].order}
|
||||
{isFlowInput}
|
||||
{isAppInput}
|
||||
on:change={() => {
|
||||
schema = $state.snapshot(schema)
|
||||
dispatch('change', schema)
|
||||
}}
|
||||
>
|
||||
{#snippet typeeditor()}
|
||||
{#if isFlowInput || isAppInput}
|
||||
@@ -742,13 +754,9 @@
|
||||
type: v
|
||||
}
|
||||
}
|
||||
schema.properties = schema.properties
|
||||
}
|
||||
}
|
||||
on:selected={(e) => {
|
||||
schema = schema
|
||||
dispatch('change', schema)
|
||||
dispatch('schemaChange')
|
||||
}}
|
||||
>
|
||||
{#snippet children({ item })}
|
||||
{#each typeOptions as x}
|
||||
@@ -762,6 +770,9 @@
|
||||
|
||||
{#if isFlowInput || isAppInput}
|
||||
<FlowPropertyEditor
|
||||
onDrawerClose={() => {
|
||||
dndType = generateRandomString()
|
||||
}}
|
||||
bind:defaultValue={schema.properties[argName].default}
|
||||
{variableEditor}
|
||||
{itemPicker}
|
||||
@@ -792,12 +803,6 @@
|
||||
(x) => x !== argName
|
||||
)
|
||||
}
|
||||
dispatch('change', schema)
|
||||
}}
|
||||
on:schemaChange={(e) => {
|
||||
schema = $state.snapshot(schema)
|
||||
dispatch('change', schema)
|
||||
dispatch('schemaChange')
|
||||
}}
|
||||
/>
|
||||
{/if}
|
||||
@@ -822,7 +827,6 @@
|
||||
on:change={() => {
|
||||
try {
|
||||
schema = JSON.parse(schemaString)
|
||||
dispatch('change', schema)
|
||||
error = ''
|
||||
} catch (err) {
|
||||
error = err.message
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<script context="module">
|
||||
<script module>
|
||||
import { tick } from 'svelte'
|
||||
|
||||
export function portal(el, options) {
|
||||
@@ -50,13 +50,17 @@
|
||||
|
||||
<script>
|
||||
/**
|
||||
* DOM Element or CSS Selector
|
||||
* @type { HTMLElement|string}
|
||||
* @typedef {Object} Props
|
||||
* @property { HTMLElement|string} [target] - DOM Element or CSS Selector
|
||||
* @property {any} [name]
|
||||
* @property {string|undefined} [class]
|
||||
* @property {import('svelte').Snippet} [children]
|
||||
*/
|
||||
export let target = 'body'
|
||||
export let name = undefined
|
||||
|
||||
/** @type {Props} */
|
||||
let { target = 'body', name = undefined, class: clazz = undefined, children } = $props()
|
||||
</script>
|
||||
|
||||
<div use:portal={{ target, name }} hidden class={$$props.class}>
|
||||
<slot />
|
||||
<div use:portal={{ target, name }} hidden class={clazz}>
|
||||
{@render children?.()}
|
||||
</div>
|
||||
|
||||
@@ -108,7 +108,7 @@
|
||||
className = '',
|
||||
computeS3ForceViewerPolicies = undefined,
|
||||
workspace = undefined,
|
||||
actions
|
||||
actions: actions_render = undefined
|
||||
}: Props = $props()
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
@@ -249,8 +249,6 @@
|
||||
$effect.pre(() => {
|
||||
isValid = allTrue(inputCheck ?? {})
|
||||
})
|
||||
|
||||
const actions_render = $derived(actions)
|
||||
</script>
|
||||
|
||||
{#if showReset}
|
||||
|
||||
@@ -173,11 +173,11 @@
|
||||
<Disposable
|
||||
{id}
|
||||
bind:this={disposable}
|
||||
on:open={() => {
|
||||
onOpen={() => {
|
||||
outputs?.open.set(true)
|
||||
onOpenRecomputeIds?.forEach((id) => $runnableComponents?.[id]?.cb?.map((cb) => cb?.()))
|
||||
}}
|
||||
on:close={() => {
|
||||
onClose={() => {
|
||||
outputs?.open.set(false)
|
||||
onCloseRecomputeIds?.forEach((id) => $runnableComponents?.[id]?.cb?.map((cb) => cb?.()))
|
||||
}}
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
import { AppService, DraftService, SettingService, type Policy } from '$lib/gen'
|
||||
import { redo, undo } from '$lib/history.svelte'
|
||||
import { enterpriseLicense, userStore, workspaceStore } from '$lib/stores'
|
||||
import type { Item } from '$lib/utils'
|
||||
import {
|
||||
AlignHorizontalSpaceAround,
|
||||
BellOff,
|
||||
|
||||
@@ -12,9 +12,8 @@
|
||||
} from './componentCallbacks.svelte'
|
||||
import type { AppEditorContext, AppViewerContext } from '../../types'
|
||||
|
||||
const { history, movingcomponents, jobsDrawerOpen } = getContext<AppEditorContext>(
|
||||
'AppEditorContext'
|
||||
) as AppEditorContext
|
||||
const { history, movingcomponents, jobsDrawerOpen, runnableJobEditorPanel } =
|
||||
getContext<AppEditorContext>('AppEditorContext') as AppEditorContext
|
||||
const { app, selectedComponent, focusedGrid, componentControl } = getContext<AppViewerContext>(
|
||||
'AppViewerContext'
|
||||
) as AppViewerContext
|
||||
@@ -33,7 +32,8 @@
|
||||
let classes = event.target?.['className']
|
||||
if (
|
||||
(typeof classes === 'string' && classes.includes('inputarea')) ||
|
||||
['INPUT', 'TEXTAREA'].includes(document.activeElement?.tagName!)
|
||||
['INPUT', 'TEXTAREA'].includes(document.activeElement?.tagName!) ||
|
||||
$runnableJobEditorPanel.focused
|
||||
) {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -308,7 +308,7 @@ export async function handlePaste(
|
||||
} else {
|
||||
copiedGridItems = undefined
|
||||
}
|
||||
} catch {}
|
||||
} catch { }
|
||||
}
|
||||
|
||||
if (tempGridItems != undefined) {
|
||||
|
||||
@@ -237,6 +237,8 @@
|
||||
$effect(() => {
|
||||
items != undefined && handleItemsChange()
|
||||
})
|
||||
|
||||
const rnd = generateRandomString()
|
||||
</script>
|
||||
|
||||
<div class="flex gap-2 flex-col mt-2 w-full">
|
||||
@@ -286,7 +288,8 @@
|
||||
use:dragHandleZone={{
|
||||
items,
|
||||
flipDurationMs,
|
||||
dropTargetStyle: {}
|
||||
dropTargetStyle: {},
|
||||
type: rnd
|
||||
}}
|
||||
onconsider={handleConsider}
|
||||
onfinalize={handleFinalize}
|
||||
|
||||
@@ -126,6 +126,8 @@
|
||||
})
|
||||
component.numberOfSubgrids = items.length + 1
|
||||
}
|
||||
|
||||
const rnd = generateRandomString()
|
||||
</script>
|
||||
|
||||
<PanelSection title={'Conditions'}>
|
||||
@@ -141,7 +143,8 @@
|
||||
use:dragHandleZone={{
|
||||
items: items,
|
||||
flipDurationMs: 200,
|
||||
dropTargetStyle: {}
|
||||
dropTargetStyle: {},
|
||||
type: rnd
|
||||
}}
|
||||
onconsider={handleConsider}
|
||||
onfinalize={handleFinalize}
|
||||
|
||||
@@ -121,6 +121,8 @@
|
||||
|
||||
let resolvedPaths: string[] = $state([])
|
||||
let resolvedLabels: string[] = $state([])
|
||||
|
||||
const rnd = generateRandomString()
|
||||
</script>
|
||||
|
||||
<PanelSection
|
||||
@@ -134,7 +136,8 @@
|
||||
use:dragHandleZone={{
|
||||
items,
|
||||
flipDurationMs: 200,
|
||||
dropTargetStyle: {}
|
||||
dropTargetStyle: {},
|
||||
type: rnd
|
||||
}}
|
||||
onconsider={handleConsider}
|
||||
onfinalize={handleFinalize}
|
||||
|
||||
@@ -158,6 +158,8 @@
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
const rnd = generateRandomString()
|
||||
</script>
|
||||
|
||||
<PanelSection title={`${word}s ${tabs && tabs.length > 0 ? `(${tabs.length})` : ''}`}>
|
||||
@@ -169,7 +171,8 @@
|
||||
use:dragHandleZone={{
|
||||
items,
|
||||
flipDurationMs: 200,
|
||||
dropTargetStyle: {}
|
||||
dropTargetStyle: {},
|
||||
type: rnd
|
||||
}}
|
||||
onconsider={handleConsider}
|
||||
onfinalize={handleFinalize}
|
||||
|
||||
@@ -103,6 +103,8 @@
|
||||
|
||||
items = newItems
|
||||
}
|
||||
|
||||
const rnd = generateRandomString()
|
||||
</script>
|
||||
|
||||
{#if components}
|
||||
@@ -132,7 +134,8 @@
|
||||
use:dragHandleZone={{
|
||||
items,
|
||||
flipDurationMs: 200,
|
||||
dropTargetStyle: {}
|
||||
dropTargetStyle: {},
|
||||
type: rnd
|
||||
}}
|
||||
onconsider={handleConsider}
|
||||
onfinalize={handleFinalize}
|
||||
|
||||
@@ -1,25 +1,33 @@
|
||||
<script lang="ts" context="module">
|
||||
export let openedDrawers: string[] = []
|
||||
<script lang="ts" module>
|
||||
export let openedDrawers: { val: string[] } = $state({ val: [] })
|
||||
</script>
|
||||
|
||||
<script lang="ts">
|
||||
import { createDispatcherIfMounted } from '$lib/createDispatcherIfMounted'
|
||||
|
||||
import { zIndexes } from '$lib/zIndexes'
|
||||
import { untrack } from 'svelte'
|
||||
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
|
||||
export let open = false
|
||||
export let id = (Math.random() + 1).toString(36).substring(10)
|
||||
export let preventEscape = false
|
||||
|
||||
if (open) {
|
||||
openedDrawers.push(id)
|
||||
interface Props {
|
||||
open?: boolean
|
||||
id?: any
|
||||
preventEscape?: boolean
|
||||
initialOffset?: number
|
||||
children?: import('svelte').Snippet<[any]>
|
||||
onOpen?: () => void
|
||||
onClose?: () => void
|
||||
}
|
||||
|
||||
export let initialOffset = 0
|
||||
let {
|
||||
open = $bindable(false),
|
||||
id = (Math.random() + 1).toString(36).substring(10),
|
||||
preventEscape = false,
|
||||
initialOffset = 0,
|
||||
children,
|
||||
onOpen,
|
||||
onClose
|
||||
}: Props = $props()
|
||||
|
||||
let offset = initialOffset
|
||||
let offset = $state(initialOffset)
|
||||
let zIndex = $derived(zIndexes.disposables + offset)
|
||||
|
||||
export function toggleDrawer() {
|
||||
if (!open) {
|
||||
@@ -30,32 +38,31 @@
|
||||
}
|
||||
|
||||
export function openDrawer() {
|
||||
open = true
|
||||
offset = openedDrawers.length - 1
|
||||
|
||||
if (openedDrawers.includes(id)) {
|
||||
return
|
||||
if (!open) {
|
||||
open = true
|
||||
if (openedDrawers.val.includes(id)) {
|
||||
return
|
||||
}
|
||||
offset = openedDrawers.val.length - 1
|
||||
openedDrawers.val.push(id)
|
||||
}
|
||||
|
||||
openedDrawers.push(id)
|
||||
}
|
||||
|
||||
export function closeDrawer() {
|
||||
open = false
|
||||
offset = initialOffset
|
||||
// remove the last opened drawer
|
||||
openedDrawers = openedDrawers.filter((drawer) => drawer !== id)
|
||||
if (open) {
|
||||
open = false
|
||||
offset = initialOffset
|
||||
// remove the last opened drawer
|
||||
openedDrawers.val = openedDrawers.val.filter((drawer) => drawer !== id)
|
||||
}
|
||||
}
|
||||
|
||||
export function isOpen() {
|
||||
return open
|
||||
}
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
const dispatchIfMounted = createDispatcherIfMounted(dispatch)
|
||||
|
||||
function handleClickAway(e) {
|
||||
const last = openedDrawers[openedDrawers.length - 1]
|
||||
const last = openedDrawers.val[openedDrawers.val.length - 1]
|
||||
|
||||
if (last === id) {
|
||||
e.stopPropagation()
|
||||
@@ -68,10 +75,11 @@
|
||||
switch (event.key) {
|
||||
case 'Escape':
|
||||
if (
|
||||
(id == openedDrawers[openedDrawers.length - 1] || openedDrawers.length == 0) &&
|
||||
(id == openedDrawers.val[openedDrawers.val.length - 1] ||
|
||||
openedDrawers.val.length == 0) &&
|
||||
!preventEscape
|
||||
) {
|
||||
openedDrawers.pop()
|
||||
openedDrawers.val.pop()
|
||||
event.preventDefault()
|
||||
event.stopPropagation()
|
||||
event.stopImmediatePropagation()
|
||||
@@ -82,11 +90,34 @@
|
||||
}
|
||||
}
|
||||
|
||||
$: zIndex = zIndexes.disposables + offset
|
||||
if (open) {
|
||||
openedDrawers.val.push(id)
|
||||
}
|
||||
|
||||
$: dispatchIfMounted(open ? 'open' : 'close')
|
||||
let wasEverOpen = false
|
||||
let lastOpen = open
|
||||
$effect.pre(() => {
|
||||
if (open === untrack(() => lastOpen)) {
|
||||
return
|
||||
}
|
||||
lastOpen = open
|
||||
if (open) {
|
||||
// console.log('open', id, wasEverOpen)
|
||||
wasEverOpen = true
|
||||
onOpen?.()
|
||||
} else if (untrack(() => wasEverOpen)) {
|
||||
// console.log('close', id)
|
||||
onClose?.()
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<svelte:window on:keydown={onKeyDown} />
|
||||
<svelte:window onkeydown={onKeyDown} />
|
||||
|
||||
<slot {handleClickAway} {zIndex} {closeDrawer} {open} />
|
||||
{@render children?.({
|
||||
handleClickAway,
|
||||
zIndex,
|
||||
closeDrawer,
|
||||
open,
|
||||
isTop: openedDrawers.val[openedDrawers.val.length - 1] == id
|
||||
})}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<script lang="ts">
|
||||
import { onMount, createEventDispatcher } from 'svelte'
|
||||
import { onMount, createEventDispatcher, untrack } from 'svelte'
|
||||
import { BROWSER } from 'esm-env'
|
||||
import Disposable from './Disposable.svelte'
|
||||
import ConditionalPortal from './ConditionalPortal.svelte'
|
||||
@@ -32,7 +32,7 @@
|
||||
disableChatOffset = false,
|
||||
class: clazz = '',
|
||||
positionClass = undefined,
|
||||
children
|
||||
children: children_render
|
||||
}: Props = $props()
|
||||
|
||||
if (open === undefined) {
|
||||
@@ -52,11 +52,12 @@
|
||||
}
|
||||
|
||||
export function closeDrawer() {
|
||||
if (open) {
|
||||
setTimeout(() => {
|
||||
dispatch('afterClose')
|
||||
}, durationMs)
|
||||
}
|
||||
disposable?.closeDrawer()
|
||||
|
||||
setTimeout(() => {
|
||||
dispatch('afterClose')
|
||||
}, durationMs)
|
||||
}
|
||||
|
||||
export function isOpen() {
|
||||
@@ -83,7 +84,10 @@
|
||||
})
|
||||
|
||||
$effect(() => {
|
||||
open ? openDrawer() : closeDrawer()
|
||||
open
|
||||
untrack(() => {
|
||||
open ? openDrawer() : closeDrawer()
|
||||
})
|
||||
})
|
||||
|
||||
let timeout = $state(true)
|
||||
@@ -94,7 +98,6 @@
|
||||
mounted = true
|
||||
})
|
||||
|
||||
const children_render = $derived(children)
|
||||
const aiChatOpen = $derived(chatState.size > 0)
|
||||
</script>
|
||||
|
||||
@@ -103,11 +106,11 @@
|
||||
initialOffset={offset}
|
||||
bind:open
|
||||
bind:this={disposable}
|
||||
on:open
|
||||
on:close
|
||||
onOpen={() => dispatch('open')}
|
||||
onClose={() => dispatch('close')}
|
||||
{preventEscape}
|
||||
>
|
||||
{#snippet children({ handleClickAway, zIndex })}
|
||||
{#snippet children({ handleClickAway, zIndex, isTop })}
|
||||
<aside
|
||||
class="drawer windmill-app windmill-drawer {clazz ?? ''} {positionClass ?? ''} {aiChatOpen
|
||||
? 'respect-global-chat'
|
||||
@@ -122,7 +125,7 @@
|
||||
<div class="overlay {positionClass ?? ''}" onclick={handleClickAway}></div>
|
||||
<div class="panel {placement} {positionClass}" class:size>
|
||||
{#if open || !timeout || alwaysOpen}
|
||||
{@render children_render?.({ open })}
|
||||
{@render children_render?.({ open, isTop })}
|
||||
{/if}
|
||||
</div>
|
||||
</aside>
|
||||
|
||||
@@ -40,9 +40,10 @@
|
||||
} from '$lib/components/schema/schemaUtils.svelte'
|
||||
import SideBarTab from '$lib/components/meltComponents/SideBarTab.svelte'
|
||||
import CaptureTable from '$lib/components/triggers/CaptureTable.svelte'
|
||||
import { isObjectTooBig } from '$lib/utils'
|
||||
import { isObjectTooBig, readFieldsRecursively } from '$lib/utils'
|
||||
import { refreshStateStore } from '$lib/svelte5Utils.svelte'
|
||||
import type { ScriptLang } from '$lib/gen'
|
||||
import { deepEqual } from 'fast-equals'
|
||||
|
||||
interface Props {
|
||||
noEditor: boolean
|
||||
@@ -333,11 +334,19 @@
|
||||
|
||||
function resetArgs() {
|
||||
if (!previewSchema) {
|
||||
// previewArguments = undefined
|
||||
savedPreviewArgs = undefined
|
||||
}
|
||||
}
|
||||
|
||||
$effect(() => {
|
||||
if (!previewArgs && savedPreviewArgs != undefined) {
|
||||
readFieldsRecursively(flowStore.val.schema)
|
||||
untrack(() => {
|
||||
resetArgs()
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
let historicInputs: HistoricInputs | undefined = $state(undefined)
|
||||
let captureTable: CaptureTable | undefined = $state(undefined)
|
||||
let savedInputsPicker: SavedInputsPicker | undefined = $state(undefined)
|
||||
@@ -363,9 +372,6 @@
|
||||
bind:this={editableSchemaForm}
|
||||
bind:schema={flowStore.val.schema}
|
||||
isFlowInput
|
||||
on:edit={(e) => {
|
||||
addPropertyV2?.openDrawer(e.detail)
|
||||
}}
|
||||
on:delete={(e) => {
|
||||
addPropertyV2?.handleDeleteArgument([e.detail])
|
||||
}}
|
||||
@@ -399,14 +405,13 @@
|
||||
})
|
||||
}}
|
||||
shouldDispatchChanges={true}
|
||||
on:change={() => {
|
||||
onChange={() => {
|
||||
if (!previewSchema) {
|
||||
savedPreviewArgs = structuredClone($state.snapshot(previewArgs.val))
|
||||
let args = $state.snapshot(previewArgs.val)
|
||||
if (!deepEqual(args, savedPreviewArgs)) {
|
||||
savedPreviewArgs = args
|
||||
}
|
||||
}
|
||||
refreshStateStore(flowStore)
|
||||
}}
|
||||
on:schemaChange={() => {
|
||||
resetArgs()
|
||||
}}
|
||||
bind:isValid
|
||||
bind:dynSelectCode
|
||||
@@ -475,15 +480,9 @@
|
||||
<AddPropertyV2
|
||||
bind:schema={flowStore.val.schema}
|
||||
bind:this={addPropertyV2}
|
||||
on:change={() => {
|
||||
refreshStateStore(flowStore)
|
||||
if (editableSchemaForm) {
|
||||
editableSchemaForm.updateJson()
|
||||
}
|
||||
}}
|
||||
on:addNew={(e) => {
|
||||
onAddNew={(argName) => {
|
||||
handleEditSchema('inputEditor')
|
||||
editableSchemaForm?.openField(e.detail)
|
||||
editableSchemaForm?.openField(argName)
|
||||
refreshStateStore(flowStore)
|
||||
}}
|
||||
>
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
<script lang="ts">
|
||||
import { run } from 'svelte/legacy'
|
||||
|
||||
import Toggle from '$lib/components/Toggle.svelte'
|
||||
import Tooltip from '$lib/components/Tooltip.svelte'
|
||||
import InputTransformForm from '$lib/components/InputTransformForm.svelte'
|
||||
@@ -49,7 +47,7 @@
|
||||
}
|
||||
}
|
||||
|
||||
run(() => {
|
||||
$effect(() => {
|
||||
if ($workspaceStore && allUserGroups.length === 0) {
|
||||
untrack(() => {
|
||||
loadGroups()
|
||||
@@ -57,9 +55,22 @@
|
||||
}
|
||||
})
|
||||
|
||||
$effect(() => {
|
||||
// If the schema is empty, remove the form
|
||||
if (Object.keys(flowModule?.suspend?.resume_form?.schema?.properties ?? {}).length === 0) {
|
||||
untrack(() => {
|
||||
tick().then(() => {
|
||||
if (!flowModule.suspend) return
|
||||
flowModule.suspend.resume_form = undefined
|
||||
})
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
let jsonView: boolean = $state(false)
|
||||
</script>
|
||||
|
||||
A{JSON.stringify(flowModule.suspend?.resume_form)}B
|
||||
<Section label="Suspend/Approval/Prompt" class="w-full">
|
||||
{#snippet action()}
|
||||
<SuspendDrawer text="Approval/Prompt helpers" />
|
||||
@@ -234,21 +245,7 @@
|
||||
<div class="grid grid-cols-4 mt-4 gap-8">
|
||||
<div class="col-span-2">
|
||||
{#if flowModule?.suspend?.resume_form}
|
||||
<EditableSchemaDrawer
|
||||
bind:schema={flowModule.suspend.resume_form.schema}
|
||||
on:change={(e) => {
|
||||
const schema = e.detail
|
||||
|
||||
// If the schema is empty, remove the form
|
||||
if (Object.keys(schema?.properties ?? {}).length === 0) {
|
||||
tick().then(() => {
|
||||
if (!flowModule.suspend) return
|
||||
flowModule.suspend.resume_form = undefined
|
||||
})
|
||||
}
|
||||
}}
|
||||
{jsonView}
|
||||
/>
|
||||
<EditableSchemaDrawer bind:schema={flowModule.suspend.resume_form.schema} {jsonView} />
|
||||
{:else if emptyString($enterpriseLicense)}
|
||||
<Alert type="warning" title="Adding a form to the approval page is an EE feature" />
|
||||
{:else}
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
|
||||
export async function openDrawer(hash: string, cb: () => void): Promise<void> {
|
||||
script = undefined
|
||||
closeAnyway = false
|
||||
scriptEditorDrawer?.openDrawer?.()
|
||||
script = await ScriptService.getScriptByHash({
|
||||
workspace: $workspaceStore!,
|
||||
@@ -114,7 +115,6 @@
|
||||
async function checkForUnsavedChanges() {
|
||||
if (closeAnyway) {
|
||||
scriptEditorDrawer?.closeDrawer()
|
||||
closeAnyway = false
|
||||
return
|
||||
}
|
||||
if (savedScript && script) {
|
||||
@@ -122,12 +122,15 @@
|
||||
const current = cleanValueProperties(script)
|
||||
if (orderedJsonStringify(saved) !== orderedJsonStringify(current)) {
|
||||
unsavedModalOpen = true
|
||||
scriptEditorDrawer?.openDrawer()
|
||||
} else {
|
||||
scriptEditorDrawer?.closeDrawer()
|
||||
}
|
||||
}
|
||||
}
|
||||
let args = $state({})
|
||||
|
||||
let displayEditor = $state(true)
|
||||
</script>
|
||||
|
||||
<ConfirmationModal
|
||||
@@ -138,8 +141,9 @@
|
||||
unsavedModalOpen = false
|
||||
}}
|
||||
on:confirmed={() => {
|
||||
unsavedModalOpen = false
|
||||
console.log('confirmed')
|
||||
closeAnyway = true
|
||||
unsavedModalOpen = false
|
||||
scriptEditorDrawer?.closeDrawer()
|
||||
}}
|
||||
>
|
||||
@@ -156,7 +160,7 @@
|
||||
}
|
||||
unsavedModalOpen = false
|
||||
closeAnyway = true
|
||||
scriptEditorDrawer?.closeDrawer()
|
||||
displayEditor = false
|
||||
diffDrawer?.openDrawer()
|
||||
diffDrawer?.setDiff({
|
||||
title: 'Saved <> Current',
|
||||
@@ -182,7 +186,6 @@
|
||||
bind:this={scriptEditorDrawer}
|
||||
size="1200px"
|
||||
on:close={() => {
|
||||
scriptEditorDrawer?.openDrawer()
|
||||
checkForUnsavedChanges()
|
||||
}}
|
||||
>
|
||||
@@ -195,7 +198,7 @@
|
||||
scriptEditorDrawer?.closeDrawer()
|
||||
}}
|
||||
>
|
||||
{#if script}
|
||||
{#if script && displayEditor}
|
||||
{#key script.hash}
|
||||
<ScriptEditor
|
||||
showCaptures={false}
|
||||
@@ -236,7 +239,7 @@
|
||||
return
|
||||
}
|
||||
closeAnyway = true
|
||||
scriptEditorDrawer?.closeDrawer()
|
||||
displayEditor = false
|
||||
diffDrawer?.openDrawer()
|
||||
diffDrawer?.setDiff({
|
||||
mode: 'simple',
|
||||
@@ -276,10 +279,6 @@
|
||||
<DiffDrawer
|
||||
bind:this={diffDrawer}
|
||||
on:close={() => {
|
||||
if (!closeAnyway) {
|
||||
scriptEditorDrawer?.openDrawer()
|
||||
} else {
|
||||
closeAnyway = false
|
||||
}
|
||||
displayEditor = true
|
||||
}}
|
||||
/>
|
||||
|
||||
@@ -83,6 +83,13 @@
|
||||
if (Object.keys(arg).length === 1 && Object.keys(arg).includes('s3')) {
|
||||
return 's3object'
|
||||
}
|
||||
if (
|
||||
Object.keys(arg).length === 2 &&
|
||||
Object.keys(arg).includes('s3') &&
|
||||
Object.keys(arg).includes('filename')
|
||||
) {
|
||||
return 's3object'
|
||||
}
|
||||
return typeof arg
|
||||
}
|
||||
|
||||
@@ -172,9 +179,12 @@
|
||||
</span>
|
||||
{/snippet}
|
||||
|
||||
<Portal name="object-viewer">
|
||||
<S3FilePicker bind:this={s3FileViewer} readOnlyMode={true} />
|
||||
</Portal>
|
||||
{#if getTypeAsString(jsonFiltered) === 's3object'}
|
||||
<Portal name="object-viewer">
|
||||
<S3FilePicker bind:this={s3FileViewer} readOnlyMode={true} />
|
||||
</Portal>
|
||||
{/if}
|
||||
|
||||
{#if level == 0}
|
||||
<div class="float-right">
|
||||
{#if searchOpen}
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
<script lang="ts">
|
||||
import { type Schema, modalToSchema, type ModalSchemaProperty } from '$lib/common'
|
||||
import { emptySchema, sendUserToast } from '$lib/utils'
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
import AddPropertyFormV2 from './AddPropertyFormV2.svelte'
|
||||
interface Props {
|
||||
schema?: Schema | any
|
||||
trigger?: import('svelte').Snippet
|
||||
noPopover?: boolean
|
||||
onAddNew?: (argName: string) => void
|
||||
}
|
||||
|
||||
let { schema = $bindable(emptySchema()), trigger, noPopover }: Props = $props()
|
||||
let { schema = $bindable(emptySchema()), trigger, noPopover, onAddNew }: Props = $props()
|
||||
|
||||
export const DEFAULT_PROPERTY: ModalSchemaProperty = {
|
||||
selectedType: 'string',
|
||||
@@ -18,8 +18,6 @@
|
||||
required: false
|
||||
}
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
|
||||
if (!schema) {
|
||||
schema = emptySchema()
|
||||
}
|
||||
@@ -107,7 +105,6 @@
|
||||
editing = false
|
||||
oldArgName = undefined
|
||||
schema = $state.snapshot(newSchema)
|
||||
dispatch('change', schema)
|
||||
}
|
||||
|
||||
if (argError !== '') {
|
||||
@@ -139,14 +136,11 @@
|
||||
if (modifiedObject.order) {
|
||||
modifiedObject.order = modifiedObject.order.filter((arg) => arg !== argName)
|
||||
}
|
||||
dispatch('change', schema)
|
||||
} else {
|
||||
throw Error('Argument not found!')
|
||||
}
|
||||
syncOrders(modifiedObject)
|
||||
schema = $state.snapshot(modifiedObject)
|
||||
|
||||
dispatch('change', schema)
|
||||
} catch (err) {
|
||||
sendUserToast(`Could not delete argument: ${err}`, true)
|
||||
}
|
||||
@@ -164,7 +158,7 @@
|
||||
selectedType: 'string',
|
||||
name: e.detail.name
|
||||
})
|
||||
dispatch('addNew', e.detail.name)
|
||||
onAddNew?.(e.detail.name)
|
||||
} catch (err) {
|
||||
sendUserToast(`Could not add argument: ${err}`, true)
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
import AddProperty from './AddProperty.svelte'
|
||||
import { dragHandle, dragHandleZone } from '@windmill-labs/svelte-dnd-action'
|
||||
import { flip } from 'svelte/animate'
|
||||
import { emptyString } from '$lib/utils'
|
||||
import { emptyString, generateRandomString } from '$lib/utils'
|
||||
import Button from '$lib/components/common/button/Button.svelte'
|
||||
import { createEventDispatcher, tick } from 'svelte'
|
||||
import Label from '../Label.svelte'
|
||||
@@ -50,11 +50,16 @@
|
||||
|
||||
interface Props {
|
||||
schema: Schema | undefined | any
|
||||
parentId?: string | undefined
|
||||
jsonView?: boolean
|
||||
hiddenArgs?: string[]
|
||||
onClose?: () => void
|
||||
}
|
||||
|
||||
let { schema = $bindable(), parentId = undefined, jsonView = $bindable(false) }: Props = $props()
|
||||
let {
|
||||
schema = $bindable(),
|
||||
jsonView = $bindable(false),
|
||||
hiddenArgs = undefined
|
||||
}: Props = $props()
|
||||
|
||||
// let schema = $state(structuredClone($state.snapshot(schema)))
|
||||
|
||||
@@ -63,14 +68,18 @@
|
||||
let error: string | undefined = $state(undefined)
|
||||
let items = $derived([
|
||||
...new Set(
|
||||
(schema?.order ?? Object.keys(schema?.properties ?? {}))?.map((item, index) => {
|
||||
return { value: item, id: item }
|
||||
}) ?? []
|
||||
(schema?.order ?? Object.keys(schema?.properties ?? {}))
|
||||
?.map((item, index) => {
|
||||
return { value: item, id: item }
|
||||
})
|
||||
.filter((item) => !hiddenArgs?.includes(item.value)) ?? []
|
||||
)
|
||||
]) as Array<{
|
||||
value: string
|
||||
id: string
|
||||
}>
|
||||
|
||||
const rnd = generateRandomString()
|
||||
</script>
|
||||
|
||||
<div class="flex flex-col items-end mb-2 w-full">
|
||||
@@ -103,124 +112,115 @@
|
||||
/>
|
||||
|
||||
{#if !jsonView}
|
||||
<div
|
||||
use:dragHandleZone={{
|
||||
items,
|
||||
flipDurationMs,
|
||||
dropTargetStyle: {},
|
||||
type: parentId ? `app-editor-fields-${parentId}` : 'app-editor-fields'
|
||||
}}
|
||||
onconsider={handleConsider}
|
||||
onfinalize={handleFinalize}
|
||||
class="gap-1 flex flex-col mt-2"
|
||||
>
|
||||
{#if items?.length > 0}
|
||||
{#each items as item (item.id)}
|
||||
<!-- svelte-ignore a11y_no_noninteractive_tabindex -->
|
||||
<div
|
||||
animate:flip={{ duration: 200 }}
|
||||
class="w-full flex flex-col justify-between border items-center py-1 px-2 rounded-md bg-surface text-sm"
|
||||
>
|
||||
{#if schema.properties?.[item.value]}
|
||||
<div class="flex flex-row justify-between items-center w-full">
|
||||
{`${item.value}${
|
||||
schema.properties?.[item.value]?.title
|
||||
? ` (title: ${schema.properties?.[item.value]?.title})`
|
||||
: ''
|
||||
} `}
|
||||
<!-- svelte-ignore a11y_no_noninteractive_tabindex -->
|
||||
<!-- svelte-ignore a11y_no_static_element_interactions -->
|
||||
<div class="flex flex-row gap-1 item-center h-full justify-center">
|
||||
<Button
|
||||
iconOnly
|
||||
size="xs2"
|
||||
color="light"
|
||||
startIcon={{ icon: Trash }}
|
||||
on:click={() => {
|
||||
addPropertyComponent?.handleDeleteArgument([item.value])
|
||||
}}
|
||||
/>
|
||||
<Button
|
||||
iconOnly
|
||||
size="xs2"
|
||||
color="light"
|
||||
startIcon={{ icon: Pen }}
|
||||
on:click={() => {
|
||||
schemaFormDrawer?.openDrawer()
|
||||
{#key rnd}
|
||||
<div
|
||||
use:dragHandleZone={{
|
||||
items,
|
||||
flipDurationMs,
|
||||
dropTargetStyle: {},
|
||||
type: rnd
|
||||
}}
|
||||
onconsider={handleConsider}
|
||||
onfinalize={handleFinalize}
|
||||
class="gap-1 flex flex-col mt-2"
|
||||
>
|
||||
{#if items?.length > 0}
|
||||
{#each items as item (item.id)}
|
||||
<!-- svelte-ignore a11y_no_noninteractive_tabindex -->
|
||||
<div
|
||||
animate:flip={{ duration: 200 }}
|
||||
class="w-full flex flex-col justify-between border items-center py-1 px-2 rounded-md bg-surface text-sm"
|
||||
>
|
||||
{#if schema.properties?.[item.value]}
|
||||
<div class="flex flex-row justify-between items-center w-full">
|
||||
{`${item.value}${
|
||||
schema.properties?.[item.value]?.title
|
||||
? ` (title: ${schema.properties?.[item.value]?.title})`
|
||||
: ''
|
||||
} `}
|
||||
<!-- svelte-ignore a11y_no_noninteractive_tabindex -->
|
||||
<!-- svelte-ignore a11y_no_static_element_interactions -->
|
||||
<div class="flex flex-row gap-1 item-center h-full justify-center">
|
||||
<Button
|
||||
iconOnly
|
||||
size="xs2"
|
||||
color="light"
|
||||
startIcon={{ icon: Trash }}
|
||||
on:click={() => {
|
||||
addPropertyComponent?.handleDeleteArgument([item.value])
|
||||
}}
|
||||
/>
|
||||
<Button
|
||||
iconOnly
|
||||
size="xs2"
|
||||
color="light"
|
||||
startIcon={{ icon: Pen }}
|
||||
on:click={() => {
|
||||
schemaFormDrawer?.openDrawer()
|
||||
|
||||
tick().then(() => {
|
||||
editableSchemaForm?.openField(item.value)
|
||||
})
|
||||
}}
|
||||
/>
|
||||
tick().then(() => {
|
||||
editableSchemaForm?.openField(item.value)
|
||||
})
|
||||
}}
|
||||
/>
|
||||
|
||||
<div class="cursor-move flex items-center handle" use:dragHandle>
|
||||
<GripVertical size={16} />
|
||||
<div class="flex items-center handle" use:dragHandle>
|
||||
<GripVertical size={16} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{#if schema.properties[item.value]?.type === 'object' && !(schema.properties[item.value].oneOf && schema.properties[item.value].oneOf.length >= 2)}
|
||||
<div class="flex flex-col w-full mt-2">
|
||||
<Label label="Nested properties">
|
||||
<EditableSchemaDrawer
|
||||
on:change={() => {
|
||||
schema = $state.snapshot(schema)
|
||||
dispatch('change', schema)
|
||||
}}
|
||||
bind:schema={schema.properties[item.value]}
|
||||
parentId={item.value}
|
||||
/>
|
||||
</Label>
|
||||
</div>
|
||||
{#if schema.properties[item.value]?.type === 'object' && !(schema.properties[item.value].oneOf && schema.properties[item.value].oneOf.length >= 2)}
|
||||
<div class="flex flex-col w-full mt-2">
|
||||
<Label label="Nested properties">
|
||||
<EditableSchemaDrawer bind:schema={schema.properties[item.value]} />
|
||||
</Label>
|
||||
</div>
|
||||
{/if}
|
||||
{:else}
|
||||
<div class="text-tertiary"> Value is undefined </div>
|
||||
{/if}
|
||||
{:else}
|
||||
<div class="text-tertiary"> Value is undefined </div>
|
||||
{/if}
|
||||
</div>
|
||||
{/each}
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
</div>
|
||||
{/each}
|
||||
{/if}
|
||||
</div>
|
||||
{/key}
|
||||
<Drawer bind:this={schemaFormDrawer} size="1200px">
|
||||
<DrawerContent title="UI Customisation" on:close={schemaFormDrawer.closeDrawer}>
|
||||
<EditableSchemaForm
|
||||
on:change={(e) => {
|
||||
schema = $state.snapshot(schema)
|
||||
dispatch('change', schema)
|
||||
}}
|
||||
bind:this={editableSchemaForm}
|
||||
bind:schema
|
||||
isAppInput
|
||||
on:edit={(e) => {
|
||||
addPropertyComponent?.openDrawer(e.detail)
|
||||
}}
|
||||
on:delete={(e) => {
|
||||
addPropertyComponent?.handleDeleteArgument([e.detail])
|
||||
}}
|
||||
dndType="drawer"
|
||||
editTab="inputEditor"
|
||||
>
|
||||
{#snippet addProperty()}
|
||||
<AddPropertyV2
|
||||
bind:schema
|
||||
on:change
|
||||
on:addNew={(e) => {
|
||||
schema = $state.snapshot(schema)
|
||||
editableSchemaForm?.openField(e.detail)
|
||||
}}
|
||||
>
|
||||
{#snippet trigger()}
|
||||
<div
|
||||
class="w-full py-2 flex justify-center items-center border border-dashed rounded-md hover:bg-surface-hover"
|
||||
>
|
||||
<Plus size={14} />
|
||||
</div>
|
||||
{/snippet}
|
||||
</AddPropertyV2>
|
||||
{/snippet}
|
||||
</EditableSchemaForm>
|
||||
</DrawerContent>
|
||||
{#snippet children()}
|
||||
<DrawerContent title="UI Customisation" on:close={() => schemaFormDrawer?.closeDrawer()}>
|
||||
<EditableSchemaForm
|
||||
schemaFormClassName="h-full"
|
||||
bind:this={editableSchemaForm}
|
||||
bind:schema
|
||||
isAppInput
|
||||
on:edit={(e) => {
|
||||
addPropertyComponent?.openDrawer(e.detail)
|
||||
}}
|
||||
on:delete={(e) => {
|
||||
addPropertyComponent?.handleDeleteArgument([e.detail])
|
||||
}}
|
||||
{hiddenArgs}
|
||||
editTab="inputEditor"
|
||||
>
|
||||
{#snippet addProperty()}
|
||||
<AddPropertyV2
|
||||
bind:schema
|
||||
onAddNew={(argName) => {
|
||||
editableSchemaForm?.openField(argName)
|
||||
}}
|
||||
>
|
||||
{#snippet trigger()}
|
||||
<div
|
||||
class="w-full py-2 flex justify-center items-center border border-dashed rounded-md hover:bg-surface-hover"
|
||||
>
|
||||
<Plus size={14} />
|
||||
</div>
|
||||
{/snippet}
|
||||
</AddPropertyV2>
|
||||
{/snippet}
|
||||
</EditableSchemaForm>
|
||||
</DrawerContent>
|
||||
{/snippet}
|
||||
</Drawer>
|
||||
{:else}
|
||||
<div class="mt-2"></div>
|
||||
|
||||
@@ -1,10 +1,28 @@
|
||||
<script lang="ts">
|
||||
import { readFieldsRecursively } from '$lib/utils'
|
||||
import { deepEqual } from 'fast-equals'
|
||||
import type { EditableSchemaWrapperProps } from './editable_schema_wrapper'
|
||||
import EditableSchemaWrapper from './EditableSchemaWrapper.svelte'
|
||||
|
||||
let { schema: oldSchema, ...props }: EditableSchemaWrapperProps = $props()
|
||||
let {
|
||||
schema: oldSchema,
|
||||
onSchemaChange,
|
||||
...props
|
||||
}: EditableSchemaWrapperProps & { onSchemaChange?: (schema: any) => void } = $props()
|
||||
|
||||
let schema = $state(oldSchema)
|
||||
|
||||
let lastSchema = $state.snapshot(schema)
|
||||
$effect(() => {
|
||||
if (onSchemaChange) {
|
||||
readFieldsRecursively(schema)
|
||||
let newSchema = $state.snapshot(schema)
|
||||
if (!deepEqual(lastSchema, newSchema)) {
|
||||
lastSchema = newSchema
|
||||
onSchemaChange(newSchema)
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<EditableSchemaWrapper {schema} {...props} />
|
||||
<EditableSchemaWrapper bind:schema {...props} />
|
||||
|
||||
@@ -16,7 +16,6 @@
|
||||
noPreview = false,
|
||||
fullHeight = true,
|
||||
formatExtension = $bindable(undefined),
|
||||
onSchemaChange,
|
||||
customUi
|
||||
}: EditableSchemaWrapperProps = $props()
|
||||
|
||||
@@ -51,7 +50,6 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
onSchemaChange?.({ schema: $state.snapshot(schema) })
|
||||
}
|
||||
|
||||
let suggestedFileExtensions = $state([
|
||||
@@ -81,9 +79,8 @@
|
||||
noPopover={customUi?.noAddPopover}
|
||||
bind:schema
|
||||
bind:this={addPropertyComponent}
|
||||
on:change={() => onSchemaChange?.({ schema: $state.snapshot(schema) })}
|
||||
on:addNew={(e) => {
|
||||
editableSchemaForm?.openField(e.detail)
|
||||
onAddNew={(argName) => {
|
||||
editableSchemaForm?.openField(argName)
|
||||
}}
|
||||
>
|
||||
{#snippet trigger()}
|
||||
@@ -99,11 +96,7 @@
|
||||
onlyMaskPassword
|
||||
bind:this={editableSchemaForm}
|
||||
bind:schema
|
||||
on:change={() => onSchemaChange?.({ schema: $state.snapshot(schema) })}
|
||||
isFlowInput
|
||||
on:edit={(e) => {
|
||||
addPropertyComponent?.openDrawer(e.detail)
|
||||
}}
|
||||
on:delete={(e) => {
|
||||
addPropertyComponent?.handleDeleteArgument([e.detail])
|
||||
}}
|
||||
@@ -117,7 +110,6 @@
|
||||
noPopover={customUi?.noAddPopover}
|
||||
bind:schema
|
||||
bind:this={addPropertyComponent}
|
||||
on:change={() => onSchemaChange?.({ schema })}
|
||||
>
|
||||
{#snippet trigger()}
|
||||
<div
|
||||
|
||||
@@ -49,6 +49,7 @@
|
||||
order?: string[] | undefined
|
||||
requiredProperty?: string[] | undefined
|
||||
displayWebhookWarning?: boolean
|
||||
onDrawerClose?: () => void
|
||||
}
|
||||
|
||||
let {
|
||||
@@ -72,10 +73,11 @@
|
||||
properties = $bindable(undefined),
|
||||
order = $bindable(undefined),
|
||||
requiredProperty = $bindable(undefined),
|
||||
displayWebhookWarning = true
|
||||
displayWebhookWarning = true,
|
||||
onDrawerClose = undefined
|
||||
}: Props = $props()
|
||||
|
||||
let oneOfSelected: string | undefined = $state(undefined)
|
||||
let oneOfSelected: string | undefined = $state(oneOf?.[0]?.title)
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
|
||||
@@ -259,44 +261,13 @@
|
||||
{#if oneOfSelected && oneOf}
|
||||
{@const idx = oneOf.findIndex((obj) => obj.title === oneOfSelected)}
|
||||
<EditableSchemaDrawer
|
||||
bind:schema={
|
||||
() => {
|
||||
if (oneOf?.[idx]) {
|
||||
let properties = Object.fromEntries(
|
||||
Object.entries(oneOf[idx].properties ?? {}).filter(
|
||||
([k]) => k !== 'label' && k !== 'kind'
|
||||
)
|
||||
)
|
||||
return {
|
||||
...oneOf[idx],
|
||||
properties: properties,
|
||||
order: Object.keys(properties),
|
||||
required: oneOf[idx].required ?? []
|
||||
}
|
||||
}
|
||||
},
|
||||
(v) => {
|
||||
if (oneOf?.[idx]) {
|
||||
const tagKey = oneOf?.find((o) => Object.keys(o.properties ?? {}).includes('kind'))
|
||||
? 'kind'
|
||||
: 'label'
|
||||
oneOf[idx] = {
|
||||
...(v ?? {}),
|
||||
type: 'object',
|
||||
properties: {
|
||||
...(v?.properties ?? {}),
|
||||
[tagKey]: {
|
||||
type: 'string',
|
||||
enum: [v?.title ?? '']
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
on:change={() => {
|
||||
dispatch('schemaChange')
|
||||
onClose={() => {
|
||||
onDrawerClose?.()
|
||||
}}
|
||||
bind:schema={oneOf[idx]}
|
||||
hiddenArgs={[
|
||||
oneOf?.find((o) => Object.keys(o.properties ?? {}).includes('kind')) ? 'kind' : 'label'
|
||||
]}
|
||||
/>
|
||||
{/if}
|
||||
{:else if type === 'object' && format !== 'resource-s3_object' && !isDynSelect}
|
||||
@@ -359,7 +330,6 @@
|
||||
properties = v.properties
|
||||
order = v.order
|
||||
requiredProperty = v.required
|
||||
dispatch('schemaChange')
|
||||
}
|
||||
}
|
||||
/>
|
||||
@@ -382,7 +352,7 @@
|
||||
</TabContent>
|
||||
|
||||
<TabContent value="resource">
|
||||
<ObjectTypeNarrowing on:change={() => dispatch('schemaChange')} bind:format />
|
||||
<ObjectTypeNarrowing bind:format />
|
||||
</TabContent>
|
||||
</div>
|
||||
{/snippet}
|
||||
|
||||
@@ -6,9 +6,8 @@
|
||||
import type { Schema } from '$lib/common'
|
||||
import { deepEqual } from 'fast-equals'
|
||||
import type { SchemaDiff } from '$lib/components/schema/schemaUtils.svelte'
|
||||
import type { DynamicSelect } from '$lib/utils'
|
||||
import { generateRandomString, type DynamicSelect } from '$lib/utils'
|
||||
interface Props {
|
||||
dndType?: string | undefined
|
||||
schema: Schema
|
||||
args?: Record<string, any>
|
||||
prettifyHeader?: boolean
|
||||
@@ -24,10 +23,11 @@
|
||||
isValid?: boolean
|
||||
noVariablePicker?: boolean
|
||||
helperScript?: DynamicSelect.HelperScript
|
||||
className?: string
|
||||
dndType?: string
|
||||
}
|
||||
|
||||
let {
|
||||
dndType = undefined,
|
||||
schema = $bindable(),
|
||||
args = $bindable(undefined),
|
||||
prettifyHeader = false,
|
||||
@@ -42,7 +42,9 @@
|
||||
diff = {},
|
||||
nestedClasses = '',
|
||||
isValid = $bindable(true),
|
||||
noVariablePicker = false
|
||||
noVariablePicker = false,
|
||||
className = '',
|
||||
dndType = generateRandomString()
|
||||
}: Props = $props()
|
||||
|
||||
$effect.pre(() => {
|
||||
@@ -55,14 +57,16 @@
|
||||
|
||||
let items = $state(computeItems())
|
||||
|
||||
let dragDisabled = $state(true)
|
||||
let dragDisabledState = $state(true)
|
||||
|
||||
function computeItems() {
|
||||
return (
|
||||
(schema?.order ?? Object.keys(schema?.properties ?? {}) ?? []).map((key) => ({
|
||||
id: key,
|
||||
value: key
|
||||
})) ?? []
|
||||
($state.snapshot(schema?.order) ?? Object.keys(schema?.properties ?? {}) ?? []).map(
|
||||
(key) => ({
|
||||
id: key,
|
||||
value: key
|
||||
})
|
||||
) ?? []
|
||||
)
|
||||
}
|
||||
|
||||
@@ -74,23 +78,20 @@
|
||||
}
|
||||
|
||||
function handleConsider(e) {
|
||||
dragDisabled = false
|
||||
dragDisabledState = false
|
||||
const { items: newItems } = e.detail
|
||||
items = newItems
|
||||
items = $state.snapshot(newItems)
|
||||
}
|
||||
|
||||
function handleFinalize(e) {
|
||||
const { items: newItems } = e.detail
|
||||
|
||||
dragDisabled = true
|
||||
items = newItems
|
||||
|
||||
dragDisabledState = true
|
||||
items = $state.snapshot(newItems)
|
||||
const newOrder = items.map((item) => item.value)
|
||||
// console.log('handleFinalize', newOrder, e.detail)
|
||||
dispatch('reorder', newOrder)
|
||||
}
|
||||
$effect(() => {
|
||||
schema && dragDisabled && untrack(() => updateItems())
|
||||
schema && dragDisabledState && untrack(() => updateItems())
|
||||
})
|
||||
</script>
|
||||
|
||||
@@ -118,6 +119,7 @@
|
||||
{disablePortal}
|
||||
{disabled}
|
||||
{helperScript}
|
||||
{className}
|
||||
bind:schema
|
||||
dndConfig={disableDnd
|
||||
? undefined
|
||||
@@ -125,7 +127,7 @@
|
||||
items,
|
||||
flipDurationMs,
|
||||
dropTargetStyle: {},
|
||||
type: dndType ?? 'top-level'
|
||||
type: dndType
|
||||
}}
|
||||
{items}
|
||||
{diff}
|
||||
|
||||
@@ -9,5 +9,4 @@ export type EditableSchemaWrapperProps = {
|
||||
customUi?: {
|
||||
noAddPopover?: boolean
|
||||
}
|
||||
onSchemaChange?: ({ schema }: { schema: Schema }) => void
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
import type { Placement } from '@floating-ui/core'
|
||||
import { isCloudHosted } from '$lib/cloud'
|
||||
import { CloudOff } from 'lucide-svelte'
|
||||
import type { Item } from '$lib/utils'
|
||||
|
||||
interface Props {
|
||||
setDropdownWidthToButtonWidth?: boolean
|
||||
|
||||
@@ -25,5 +25,12 @@
|
||||
</script>
|
||||
|
||||
<!-- <ScriptWrapper {script} neverShowMeta={true} {customUi} /> -->
|
||||
|
||||
<EditableSchemaSdkWrapper {customUi} {schema} />
|
||||
{JSON.stringify(schema)}
|
||||
<EditableSchemaSdkWrapper
|
||||
{customUi}
|
||||
{schema}
|
||||
onSchemaChange={(newSchema) => {
|
||||
console.log('onSchemaChange', newSchema)
|
||||
schema = newSchema
|
||||
}}
|
||||
/>
|
||||
|
||||
Reference in New Issue
Block a user