mirror of
https://github.com/windmill-labs/windmill.git
synced 2026-10-27 08:02:39 +00:00
* Remove $$props.field usage * Rename slots to ensure no hyphen * _props * _trigger * OnSelectedIteration type correct capitalization * rename _content * Remove afterUpdate * Migrate everything to svelte 5 * array bind * Fix popover * type never * nit fixes * Fixed many trivial errors * onClick * Fix errors * use let: * nit typing * fix: wrap state_referenced_locally vars with untrack() Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * Add untrack import * Fix all syntax errors due to untrack migration * Fix undefined errors * Fix more undefined errors * untrack(() => initialOpen) * svelte-ignore * Fix state_descriptors_fixed error in Chart.svelte Use $state.snapshot() to pass plain copies of data/options to Chart.js instead of $state proxies. Chart.js's listenArrayEvents tries to define property descriptors on data arrays, which Svelte 5 proxies reject. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * nit typing * Merge issue * Fix "path is not set" error in resource picker / editor * Fix InputTransformForm error when rerunning some flows * fix npm run check --------- Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
401 lines
11 KiB
Svelte
401 lines
11 KiB
Svelte
<script lang="ts">
|
|
import { Badge, type AlertType } from '../common'
|
|
import Popover from '$lib/components/meltComponents/Popover.svelte'
|
|
import Toggle from '$lib/components/Toggle.svelte'
|
|
import SimpleEditor from '../SimpleEditor.svelte'
|
|
import Label from '../Label.svelte'
|
|
import Section from '../Section.svelte'
|
|
import Tooltip from '../Tooltip.svelte'
|
|
import Button from '../common/button/Button.svelte'
|
|
import { twMerge } from 'tailwind-merge'
|
|
import { ColumnIdentity, type ColumnDef } from '../apps/components/display/dbtable/utils'
|
|
import { offset, flip, shift } from 'svelte-floating-ui/dom'
|
|
|
|
import Alert from '../common/alert/Alert.svelte'
|
|
interface Props {
|
|
value: ColumnDef | undefined
|
|
trigger?: import('svelte').Snippet
|
|
}
|
|
|
|
let { value = $bindable(), trigger: trigger_render }: Props = $props()
|
|
|
|
const presets = [
|
|
{
|
|
label: 'None',
|
|
value: null
|
|
},
|
|
{
|
|
label: 'Currency CHF',
|
|
value: 'value + " CHF"'
|
|
},
|
|
{
|
|
label: 'Currency USD',
|
|
value: '"$ " + value'
|
|
},
|
|
{
|
|
label: 'Date',
|
|
value: 'new Date(value).toLocaleDateString()'
|
|
},
|
|
{
|
|
label: 'Percentage',
|
|
value: 'value + " %"'
|
|
},
|
|
{
|
|
label: 'Currency GBP',
|
|
value: 'value + " £"'
|
|
},
|
|
{
|
|
label: 'Currency EUR',
|
|
value: 'value + " €"'
|
|
},
|
|
{
|
|
label: 'Currency JPY',
|
|
value: 'value + " ¥"'
|
|
},
|
|
{
|
|
label: 'Decimal places (2)',
|
|
value: 'parseFloat(value).toFixed(2)'
|
|
},
|
|
{
|
|
label: 'Uppercase',
|
|
value: 'value.toUpperCase()'
|
|
},
|
|
{
|
|
label: 'Lowercase',
|
|
value: 'value.toLowerCase()'
|
|
},
|
|
{
|
|
label: 'Boolean (True/False)',
|
|
value: 'value ? "True" : "False"'
|
|
},
|
|
|
|
{
|
|
label: 'Object',
|
|
value: 'JSON.stringify(value, null, 2)'
|
|
}
|
|
]
|
|
|
|
let renderCount = $state(0)
|
|
|
|
function computeWarning(columnMetadata, value) {
|
|
if (columnMetadata?.isnullable === 'NO' && !columnMetadata?.defaultvalue) {
|
|
if ([ColumnIdentity.ByDefault].includes(columnMetadata?.isidentity)) {
|
|
return {
|
|
type: 'info' as AlertType,
|
|
title: 'Value will be generated',
|
|
message:
|
|
'The column is an identity column. The value will be generated by the database unless a default is provided.'
|
|
}
|
|
}
|
|
if (value?.hideInsert && columnMetadata?.isidentity !== ColumnIdentity.Always) {
|
|
return {
|
|
type: 'warning' as AlertType,
|
|
title: 'No default value',
|
|
message:
|
|
"The column is not nullable and doesn't have a default value. A default value is required."
|
|
}
|
|
}
|
|
}
|
|
|
|
if (columnMetadata?.defaultvalue !== null) {
|
|
return {
|
|
type: 'info' as AlertType,
|
|
title: 'Default value',
|
|
message: `${
|
|
value?.hideInsert ? '' : 'You may want to hide this field from insert. '
|
|
}The column has a default value defined in the database. The default value is: ${
|
|
value?.defaultvalue
|
|
}`
|
|
}
|
|
}
|
|
|
|
if (columnMetadata?.isnullable === 'YES') {
|
|
return {
|
|
type: 'info' as AlertType,
|
|
title: 'Default value',
|
|
message: `${
|
|
value?.hideInsert ? '' : 'You may want to hide this field from insert. '
|
|
}The column can be null. If no value is provided, the default value will be null.`
|
|
}
|
|
}
|
|
|
|
return null
|
|
}
|
|
|
|
let warning = $derived(computeWarning(value, value))
|
|
</script>
|
|
|
|
<Popover
|
|
floatingConfig={{
|
|
strategy: 'fixed',
|
|
placement: 'left-start',
|
|
middleware: [offset(8), flip(), shift()]
|
|
}}
|
|
contentClasses="max-h-[70vh] overflow-y-auto p-4 flex flex-col gap-4 w-96"
|
|
closeOnOtherPopoverOpen
|
|
>
|
|
{#snippet trigger()}
|
|
{@render trigger_render?.()}
|
|
{/snippet}
|
|
|
|
{#snippet content()}
|
|
{#if value}
|
|
<Section label="Column settings">
|
|
{#snippet header()}
|
|
<Badge color="blue">
|
|
{value.field}
|
|
</Badge>
|
|
{/snippet}
|
|
<Label label="Skip for select and update">
|
|
{#snippet header()}
|
|
<Tooltip>
|
|
By default, all columns are included in the select and update queries. If you want to
|
|
exclude a column from the select and update queries, you can set this property to
|
|
true.
|
|
</Tooltip>
|
|
{/snippet}
|
|
{#snippet action()}
|
|
<Toggle
|
|
on:pointerdown={(e) => {
|
|
e?.stopPropagation()
|
|
}}
|
|
bind:checked={value.ignored}
|
|
size="xs"
|
|
disabled={value?.isprimarykey}
|
|
/>
|
|
{/snippet}
|
|
{#if value?.isprimarykey}
|
|
<Alert type="warning" size="xs" title="Primary key" class="my-1">
|
|
You cannot skip a primary key.
|
|
</Alert>
|
|
{/if}
|
|
</Label>
|
|
|
|
<Label label="Hide from insert">
|
|
{#snippet header()}
|
|
<Tooltip>
|
|
By default, all columns are used to generate the submit form. If you want to exclude a
|
|
column from the submit form, you can set this property to true. If the column is not
|
|
nullable or doesn't have a default value, a default value will be required.
|
|
</Tooltip>
|
|
{/snippet}
|
|
{#snippet action()}
|
|
<Toggle
|
|
disabled={value?.isidentity === ColumnIdentity.Always}
|
|
on:pointerdown={(e) => {
|
|
e?.stopPropagation()
|
|
}}
|
|
bind:checked={value.hideInsert}
|
|
size="xs"
|
|
/>
|
|
{/snippet}
|
|
</Label>
|
|
{#if value?.isidentity === ColumnIdentity.Always}
|
|
<Alert type="warning" size="xs" title="Identity column" class="my-1">
|
|
This column is an ALWAYS identity column and so can't be provided by the user.
|
|
</Alert>
|
|
{/if}
|
|
|
|
{#if warning}
|
|
<Alert type={warning.type} size="xs" title={warning.title} class="my-2">
|
|
{warning.message}
|
|
</Alert>
|
|
{/if}
|
|
|
|
{#if value?.defaultvalue !== null && value?.hideInsert}
|
|
<Toggle
|
|
bind:checked={value.overrideDefaultValue}
|
|
size="xs"
|
|
options={{
|
|
right: `Override default value: ${value?.defaultvalue}`
|
|
}}
|
|
on:change={() => {
|
|
if (!value || !value.overrideDefaultValue) {
|
|
if (value) {
|
|
value.defaultValueNull = false
|
|
value.defaultUserValue = undefined
|
|
}
|
|
}
|
|
}}
|
|
/>
|
|
{/if}
|
|
<Label label="Default input">
|
|
{#snippet header()}
|
|
<Tooltip>
|
|
By default, all columns are used to generate the submit form. If you want to exclude a
|
|
column from the submit form, you can set this property to true. If the column is not
|
|
nullable or doesn't have a default value, a default value will be required.
|
|
</Tooltip>
|
|
{/snippet}
|
|
{#if value?.datatype}
|
|
{@const type = value?.datatype}
|
|
|
|
<div class="flex flex-row items-center gap-2">
|
|
<Badge color="dark-gray">
|
|
Type:
|
|
{type}
|
|
</Badge>
|
|
|
|
{#if value?.isnullable == 'YES' && value.hideInsert}
|
|
<Toggle
|
|
bind:checked={value.defaultValueNull}
|
|
size="xs"
|
|
options={{
|
|
right: 'Set to null'
|
|
}}
|
|
disabled={value.hideInsert &&
|
|
value?.defaultvalue !== null &&
|
|
!value?.overrideDefaultValue}
|
|
on:change={() => {
|
|
if (value?.defaultValueNull && value) {
|
|
value.defaultUserValue = null
|
|
}
|
|
}}
|
|
/>
|
|
{/if}
|
|
</div>
|
|
|
|
<input
|
|
type="text"
|
|
placeholder="Default value"
|
|
class="mt-2"
|
|
bind:value={value.defaultUserValue}
|
|
disabled={value.defaultValueNull ||
|
|
(value.hideInsert && value?.defaultvalue !== null && !value?.overrideDefaultValue)}
|
|
/>
|
|
{/if}
|
|
</Label>
|
|
</Section>
|
|
|
|
<Section label="AG Grid configuration">
|
|
<!-- svelte-ignore a11y_no_static_element_interactions -->
|
|
<div
|
|
class={twMerge('flex flex-col gap-4', value.ignored ? 'opacity-50 cursor-none ' : '')}
|
|
onpointerdown={(e) => {
|
|
if (value?.ignored) {
|
|
e?.stopPropagation()
|
|
}
|
|
}}
|
|
>
|
|
<Label label="Header name">
|
|
<input type="text" placeholder="Header name" bind:value={value.headerName} />
|
|
</Label>
|
|
|
|
<Label label="Editable value">
|
|
<Toggle
|
|
on:pointerdown={(e) => {
|
|
e?.stopPropagation()
|
|
}}
|
|
options={{ right: 'Editable' }}
|
|
bind:checked={value.editable}
|
|
size="xs"
|
|
/>
|
|
</Label>
|
|
|
|
<Label label="Min width (px)">
|
|
<input type="number" placeholder="width" bind:value={value.minWidth} />
|
|
</Label>
|
|
|
|
<Label label="Flex">
|
|
{#snippet header()}
|
|
<Tooltip
|
|
documentationLink="https://www.ag-grid.com/javascript-data-grid/column-sizing/#column-flex"
|
|
>
|
|
It's often required that one or more columns fill the entire available space in the
|
|
grid. For this scenario, it is possible to use the flex config. Some columns could
|
|
be set with a regular width config, while other columns would have a flex config.
|
|
Flex sizing works by dividing the remaining space in the grid among all flex columns
|
|
in proportion to their flex value. For example, suppose the grid has a total width
|
|
of 450px and it has three columns: the first with width: 150; the second with flex:
|
|
1; and third with flex: 2. The first column will be 150px wide, leaving 300px
|
|
remaining. The column with flex: 2 has twice the size with flex: 1. So final sizes
|
|
will be: 150px, 100px, 200px.
|
|
</Tooltip>
|
|
{/snippet}
|
|
|
|
<input type="range" step="1" bind:value={value.flex} min={1} max={12} />
|
|
<div class="text-xs">{value.flex}</div>
|
|
</Label>
|
|
|
|
<Label label="Hide">
|
|
<Toggle
|
|
on:pointerdown={(e) => {
|
|
e?.stopPropagation()
|
|
}}
|
|
options={{ right: 'Hide' }}
|
|
bind:checked={value.hide}
|
|
size="xs"
|
|
/>
|
|
</Label>
|
|
|
|
<Label label="Value formatter">
|
|
{#snippet header()}
|
|
<Tooltip
|
|
documentationLink="https://www.ag-grid.com/javascript-data-grid/value-formatters/"
|
|
>
|
|
Value formatters allow you to format values for display. This is useful when data is
|
|
one type (e.g. numeric) but needs to be converted for human reading (e.g. putting in
|
|
currency symbols and number formatting).
|
|
</Tooltip>
|
|
{/snippet}
|
|
{#snippet action()}
|
|
<Button
|
|
size="xs"
|
|
variant="default"
|
|
on:click={() => {
|
|
// @ts-ignore
|
|
value.valueFormatter = null
|
|
renderCount++
|
|
}}
|
|
>
|
|
Clear
|
|
</Button>
|
|
{/snippet}
|
|
</Label>
|
|
<div>
|
|
{#key renderCount}
|
|
<div class="flex flex-col gap-4">
|
|
<div class="relative">
|
|
{#if !presets.find((preset) => preset.value === value?.valueFormatter)}
|
|
<div
|
|
class="z-50 absolute bg-opacity-50 bg-surface top-0 left-0 bottom-0 right-0"
|
|
></div>
|
|
{/if}
|
|
<div class="text-xs font-semibold">Presets</div>
|
|
<select
|
|
bind:value={value.valueFormatter}
|
|
onchange={() => {
|
|
renderCount++
|
|
}}
|
|
placeholder="Code"
|
|
>
|
|
{#each presets as preset}
|
|
<option value={preset.value}>{preset.label}</option>
|
|
{/each}
|
|
</select>
|
|
</div>
|
|
|
|
<SimpleEditor
|
|
extraLib={'declare const value: any'}
|
|
autoHeight
|
|
lang="javascript"
|
|
bind:code={value.valueFormatter}
|
|
/>
|
|
<div class="text-xs text-secondary -mt-4">Use `value` in the formatter</div>
|
|
</div>
|
|
{/key}
|
|
</div>
|
|
|
|
<Label label="Sort">
|
|
<select bind:value={value.sort}>
|
|
<option value={null}>None</option>
|
|
<option value="asc">Ascending</option>
|
|
<option value="desc">Descending</option>
|
|
</select>
|
|
</Label>
|
|
</div>
|
|
</Section>
|
|
{/if}
|
|
{/snippet}
|
|
</Popover>
|