mirror of
https://github.com/windmill-labs/windmill.git
synced 2026-10-31 00:13:53 +00:00
* runs on svelte 5 * Line component from svelte-chartjs * Replaced all svelte-chartjs occurrences with custom wrapper * Fix props mistake * Fix illegal table structures * self-closing-tags fix * aria labels * Fixed trivial warnings and errors * @tanstack/svelte-table fix * upgrade to vite 6 * svelte-kit sync before running svelte-check * Remove on:clear which is actually on:removeAll and already handled by on:change * fix worker tags not displaying in Autoscaling * Try to fix svelte-kit sync not working during CI * remove warnings * Fix add flow page crashing * access worldStore before assignment fix * fix infinite recursions in App Editor * Replaced JSON.stringify with proper deepEqual * component mount api changed (no longer classes) * fix ci errors * Fix infinite loops in background runnable panel * factored effect on deep equal logic in onObjChange * fix "Add" not working in AgGrid Table * Replaced legacy component.$set api * Fix multiselect infinite value reaction * Fix flow input fields resetting when opening their edit tab * fix date input resetting when typing year * Remove !p-0 affecting subgrid dotted borders * fix missing debounceTemplate causing hundreds of updates * Fix AgGrid action refreshes and disppearing * resolve getItems generating random ids every rerun * fix cannot access items before init * fix sort lambda arguments being undefined * Revert "Remove !p-0 affecting subgrid dotted borders" This reverts commit c62809bb45d682a48376b071680645ed4e1c601b. * fix input not updating in decision tree editor * Update frontend/src/lib/components/schema/EditableSchemaWrapper.svelte Co-authored-by: ellipsis-dev[bot] <65095814+ellipsis-dev[bot]@users.noreply.github.com> * Re-added padding affecting subgrid dotted borders (#5479) * remove !p-0 in preset components * removed extra padding on accordion tabs subgrid * Fix non-reactive SchemaForm * dirty fix for the oneOf bug * Fix warnings and update svelte-exmarkdown for svelte 5 * fix dnd not working * don't mount component like objects --------- Co-authored-by: Diego Imbert <diegoimbert@protonmail.com> Co-authored-by: Diego Imbert <70353967+diegoimbert@users.noreply.github.com> Co-authored-by: ellipsis-dev[bot] <65095814+ellipsis-dev[bot]@users.noreply.github.com>
246 lines
7.4 KiB
Svelte
246 lines
7.4 KiB
Svelte
<script lang="ts">
|
|
import autosize from '$lib/autosize'
|
|
import { shouldDisplayPlaceholder } from '$lib/utils'
|
|
import { twMerge } from 'tailwind-merge'
|
|
import ArrayTypeNarrowing from '../ArrayTypeNarrowing.svelte'
|
|
import Label from '../Label.svelte'
|
|
import NumberTypeNarrowing from '../NumberTypeNarrowing.svelte'
|
|
import StringTypeNarrowing from '../StringTypeNarrowing.svelte'
|
|
import Tooltip from '../Tooltip.svelte'
|
|
|
|
import EditableSchemaForm from '../EditableSchemaForm.svelte'
|
|
import { deepEqual } from 'fast-equals'
|
|
import type { EnumType } from '$lib/common'
|
|
import type { SchemaProperty } from '$lib/common'
|
|
import ToggleButtonGroup from '../common/toggleButton-v2/ToggleButtonGroup.svelte'
|
|
import ToggleButton from '../common/toggleButton-v2/ToggleButton.svelte'
|
|
import { createEventDispatcher, onMount } from 'svelte'
|
|
|
|
export let description: string = ''
|
|
export let format: string | undefined = undefined
|
|
export let contentEncoding: 'base64' | 'binary' | undefined = undefined
|
|
export let type: string | undefined = undefined
|
|
export let oneOf: SchemaProperty[] | undefined = undefined
|
|
export let pattern: undefined | string = undefined
|
|
export let enum_: EnumType = undefined
|
|
export let extra: Record<string, any> = {}
|
|
export let minW = true
|
|
export let customErrorMessage: string | undefined = undefined
|
|
export let title: string | undefined = undefined
|
|
export let placeholder: string | undefined = undefined
|
|
export let properties: Record<string, any> | undefined = undefined
|
|
export let isFlowInput: boolean = false
|
|
export let isAppInput: boolean = false
|
|
export let order: string[] | undefined = undefined
|
|
export let itemsType:
|
|
| {
|
|
type?: 'string' | 'number' | 'bytes' | 'object' | 'resource'
|
|
contentEncoding?: 'base64'
|
|
enum?: string[]
|
|
resourceType?: string
|
|
multiselect?: string[]
|
|
}
|
|
| undefined = undefined
|
|
|
|
const dispatch = createEventDispatcher()
|
|
let el: HTMLTextAreaElement | undefined = undefined
|
|
|
|
let oneOfSelected: string | undefined =
|
|
(oneOf && oneOf.length >= 2 && oneOf[0]['title']) || undefined
|
|
let oneOfSchemas: any[] | undefined = undefined
|
|
function updateOneOfSchemas(oneOf: SchemaProperty[] | undefined) {
|
|
if (oneOf && oneOf.length >= 2) {
|
|
oneOfSchemas = oneOf.map((obj) => {
|
|
return {
|
|
properties: obj.properties
|
|
? Object.fromEntries(Object.entries(obj.properties).filter(([k, v]) => k !== 'label'))
|
|
: {},
|
|
order: obj.order
|
|
}
|
|
})
|
|
}
|
|
}
|
|
$: updateOneOfSchemas(oneOf)
|
|
|
|
export function focus() {
|
|
el?.focus()
|
|
if (el) {
|
|
el.style.height = '5px'
|
|
el.style.height = el.scrollHeight + 50 + 'px'
|
|
}
|
|
}
|
|
|
|
function onKeyDown(e: KeyboardEvent) {
|
|
if ((e.ctrlKey || e.metaKey) && e.key == 'Enter') {
|
|
return
|
|
}
|
|
e.stopPropagation()
|
|
}
|
|
|
|
let schema = {
|
|
properties,
|
|
order
|
|
}
|
|
|
|
let initialExtra: any = structuredClone({ order: undefined, properties: undefined, ...extra })
|
|
|
|
let mounted = false
|
|
let firstOnContentChange = true
|
|
onMount(() => {
|
|
setTimeout(() => {
|
|
mounted = true
|
|
}, 500)
|
|
})
|
|
|
|
$: extra && mounted && onContentChange()
|
|
|
|
function onContentChange() {
|
|
if (firstOnContentChange) {
|
|
firstOnContentChange = false
|
|
return
|
|
}
|
|
if (!deepEqual(extra, initialExtra)) {
|
|
initialExtra = structuredClone(extra)
|
|
console.debug('property content updated')
|
|
dispatch('change')
|
|
}
|
|
}
|
|
|
|
$: (properties || order) && updateSchema()
|
|
|
|
function updateSchema() {
|
|
if (!deepEqual(schema.properties, properties) || !deepEqual(schema.order, order)) {
|
|
schema = {
|
|
properties,
|
|
order
|
|
}
|
|
console.debug('property schema updated')
|
|
dispatch('change')
|
|
}
|
|
}
|
|
</script>
|
|
|
|
<div class="flex flex-row items-center justify-between w-full gap-2">
|
|
<!-- svelte-ignore a11y-autofocus -->
|
|
<div class={twMerge('flex flex-col w-full', 'gap-4', minW ? 'min-w-[250px]' : '')}>
|
|
<slot name="typeeditor" />
|
|
|
|
<Label label="Description">
|
|
<textarea
|
|
use:autosize
|
|
rows="2"
|
|
bind:value={description}
|
|
on:keydown={onKeyDown}
|
|
placeholder="Field description"
|
|
></textarea>
|
|
</Label>
|
|
|
|
<Label label="Custom title" class="w-full">
|
|
<svelte:fragment slot="header">
|
|
<Tooltip light>Will be displayed in the UI instead of the field name.</Tooltip>
|
|
</svelte:fragment>
|
|
<input bind:value={title} on:keydown={onKeyDown} placeholder="Field title" />
|
|
</Label>
|
|
|
|
<Label label="Placeholder">
|
|
<svelte:fragment slot="header">
|
|
<Tooltip light>
|
|
Will be displayed in the input field when the field is empty. If not set, the default
|
|
value will be used. The placeholder is disabled depending on the field type, format, etc.
|
|
</Tooltip>
|
|
</svelte:fragment>
|
|
|
|
<textarea
|
|
placeholder="Enter a placeholder"
|
|
rows="1"
|
|
bind:value={placeholder}
|
|
on:change={() => dispatch('change')}
|
|
disabled={!shouldDisplayPlaceholder(type, format, enum_, contentEncoding, pattern, extra)}
|
|
></textarea>
|
|
</Label>
|
|
|
|
{#if type == 'array'}
|
|
<ArrayTypeNarrowing
|
|
originalType={extra['originalType']}
|
|
bind:itemsType
|
|
canEditResourceType={isFlowInput || isAppInput}
|
|
/>
|
|
{:else if type == 'string' || ['number', 'integer', 'object'].includes(type ?? '')}
|
|
<div>
|
|
<Label label="Field settings">
|
|
<div>
|
|
{#if type == 'string'}
|
|
<StringTypeNarrowing
|
|
bind:customErrorMessage
|
|
bind:format
|
|
bind:pattern
|
|
bind:enum_
|
|
bind:contentEncoding
|
|
bind:password={extra['password']}
|
|
bind:minRows={extra['minRows']}
|
|
bind:disableCreate={extra['disableCreate']}
|
|
bind:disableVariablePicker={extra['disableVariablePicker']}
|
|
bind:dateFormat={extra['dateFormat']}
|
|
bind:enumLabels={extra['enumLabels']}
|
|
originalType={extra['originalType']}
|
|
overrideAllowKindChange={isFlowInput || isAppInput}
|
|
/>
|
|
{:else if type == 'number' || type == 'integer'}
|
|
<NumberTypeNarrowing
|
|
bind:min={extra['min']}
|
|
bind:max={extra['max']}
|
|
bind:currency={extra['currency']}
|
|
bind:currencyLocale={extra['currencyLocale']}
|
|
/>
|
|
{:else if type == 'object' && oneOf && oneOf.length >= 2 && !isFlowInput && !isAppInput}
|
|
<ToggleButtonGroup bind:selected={oneOfSelected} class="mb-2" let:item>
|
|
{#each oneOf as obj}
|
|
<ToggleButton value={obj.title ?? ''} label={obj.title} {item} />
|
|
{/each}
|
|
</ToggleButtonGroup>
|
|
{#if oneOfSelected && oneOfSchemas}
|
|
{@const idx = oneOf.findIndex((obj) => obj.title === oneOfSelected)}
|
|
<div class="border">
|
|
<EditableSchemaForm
|
|
on:change
|
|
noPreview
|
|
bind:schema={oneOfSchemas[idx]}
|
|
uiOnly
|
|
jsonEnabled={false}
|
|
editTab="inputEditor"
|
|
/>
|
|
</div>
|
|
{/if}
|
|
{:else if type == 'object' && format?.startsWith('dynselect-')}
|
|
<div class="text-tertiary text-xs">No settings available for Dynamic Select</div>
|
|
{:else if type == 'object' && !format?.startsWith('resource-') && !isFlowInput && !isAppInput}
|
|
<div class="border">
|
|
<EditableSchemaForm
|
|
on:change
|
|
noPreview
|
|
bind:schema
|
|
uiOnly
|
|
jsonEnabled={false}
|
|
editTab="inputEditor"
|
|
/>
|
|
</div>
|
|
{:else}
|
|
<div class="text-tertiary text-xs">No settings available for this field type</div>
|
|
{/if}
|
|
</div>
|
|
</Label>
|
|
</div>
|
|
{/if}
|
|
|
|
<slot />
|
|
</div>
|
|
</div>
|
|
|
|
<style>
|
|
input::-webkit-outer-spin-button,
|
|
input::-webkit-inner-spin-button {
|
|
-webkit-appearance: none !important;
|
|
margin: 0;
|
|
}
|
|
</style>
|