mirror of
https://github.com/windmill-labs/windmill.git
synced 2026-08-18 16:02:10 +00:00
feat(frontend): column definition helper (#2399)
* feat(frontend): add helper to configure AG Grid columns def * feat(frontend): add all options * feat(frontend): column definition helper * feat(frontend): add header name * feat(frontend): add presets * feat(frontend): rework preset * feat(frontend): rework preset * feat(frontend): fix presets * feat(frontend): add flex + fix adding empty column def * feat(frontend): add missing unit
This commit is contained in:
@@ -3,12 +3,13 @@
|
||||
</script>
|
||||
|
||||
<label>
|
||||
<div class="flex flex-row justify-between items-center">
|
||||
<div class="flex flex-row items-center gap-2">
|
||||
<div class="flex flex-row justify-between items-center w-full">
|
||||
<div class="flex flex-row items-center gap-1">
|
||||
<span class="text-secondary text-sm leading-6">{label}</span>
|
||||
<slot name="header" />
|
||||
</div>
|
||||
<slot name="error" />
|
||||
<slot name="action" />
|
||||
</div>
|
||||
<slot />
|
||||
</label>
|
||||
|
||||
@@ -479,7 +479,6 @@ const paginationOneOf = {
|
||||
|
||||
const documentationBaseUrl = 'https://www.windmill.dev/docs/apps/app_configuration_settings'
|
||||
|
||||
|
||||
const aggridcomponentconst = {
|
||||
name: 'AgGrid Table',
|
||||
icon: Table2,
|
||||
@@ -491,8 +490,12 @@ const aggridcomponentconst = {
|
||||
columnDefs: {
|
||||
type: 'static',
|
||||
fieldType: 'array',
|
||||
subFieldType: 'object',
|
||||
value: [{ field: 'id' }, { field: 'name', editable: true }, { field: 'age' }]
|
||||
subFieldType: 'ag-grid',
|
||||
value: [
|
||||
{ field: 'id', flex: 1 },
|
||||
{ field: 'name', editable: true, flex: 1 },
|
||||
{ field: 'age', flex: 1 }
|
||||
]
|
||||
} as StaticAppInput,
|
||||
flex: {
|
||||
type: 'static',
|
||||
@@ -1430,7 +1433,7 @@ This is a paragraph.
|
||||
}
|
||||
},
|
||||
aggridcomponent: aggridcomponentconst,
|
||||
aggridcomponentee: {...aggridcomponentconst, name: 'AgGrid Table EE'},
|
||||
aggridcomponentee: { ...aggridcomponentconst, name: 'AgGrid Table EE' },
|
||||
checkboxcomponent: {
|
||||
name: 'Toggle',
|
||||
icon: ToggleLeft,
|
||||
|
||||
+23
-2
@@ -8,6 +8,7 @@
|
||||
import { flip } from 'svelte/animate'
|
||||
import { dndzone, SOURCES, TRIGGERS } from 'svelte-dnd-action'
|
||||
import { generateRandomString, pluralize } from '$lib/utils'
|
||||
import Toggle from '$lib/components/Toggle.svelte'
|
||||
|
||||
const flipDurationMs = 200
|
||||
|
||||
@@ -44,6 +45,8 @@
|
||||
value.push('')
|
||||
} else if (subFieldType === 'select' && selectOptions) {
|
||||
value.push(selectOptions[0])
|
||||
} else if (subFieldType === 'ag-grid') {
|
||||
value.push({ field: 'newField', editable: true, flex: 1 })
|
||||
}
|
||||
} else {
|
||||
value.push('')
|
||||
@@ -120,11 +123,25 @@
|
||||
function handleItemsChange() {
|
||||
componentInput.value = items.map((item) => item.value)
|
||||
}
|
||||
|
||||
let raw: boolean = false
|
||||
</script>
|
||||
|
||||
<div class="flex gap-2 flex-col mt-2 w-full">
|
||||
{#if Array.isArray(items) && componentInput.value}
|
||||
<div class="text-xs text-tertiary font-semibold">{pluralize(items.length, 'item')}</div>
|
||||
<div class="flex flex-row items-center justify-between">
|
||||
<div class="text-xs text-tertiary font-semibold">{pluralize(items.length, 'item')}</div>
|
||||
|
||||
{#if subFieldType === 'ag-grid'}
|
||||
<Toggle
|
||||
options={{
|
||||
right: 'Raw'
|
||||
}}
|
||||
size="xs"
|
||||
bind:checked={raw}
|
||||
/>
|
||||
{/if}
|
||||
</div>
|
||||
<section
|
||||
use:dndzone={{
|
||||
items,
|
||||
@@ -141,7 +158,11 @@
|
||||
|
||||
<div class="flex flex-row gap-2 items-center relative my-1 w-full">
|
||||
<div class="grow min-w-0">
|
||||
<SubTypeEditor {subFieldType} bind:componentInput bind:value={item.value} />
|
||||
<SubTypeEditor
|
||||
subFieldType={raw ? 'object' : subFieldType}
|
||||
bind:componentInput
|
||||
bind:value={item.value}
|
||||
/>
|
||||
</div>
|
||||
<div class="flex justify-between flex-col items-center">
|
||||
<div
|
||||
|
||||
+24
@@ -11,6 +11,9 @@
|
||||
import Toggle from '$lib/components/Toggle.svelte'
|
||||
import SchemaEditor from '$lib/components/SchemaEditor.svelte'
|
||||
import autosize from 'svelte-autosize'
|
||||
import Button from '$lib/components/common/button/Button.svelte'
|
||||
import { Settings } from 'lucide-svelte'
|
||||
import AgGridWizard from '$lib/components/wizards/AgGridWizard.svelte'
|
||||
|
||||
export let componentInput: StaticInput<any> | undefined
|
||||
export let fieldType: InputType | undefined = undefined
|
||||
@@ -115,6 +118,27 @@
|
||||
<div class="w-full">
|
||||
<SchemaEditor bind:schema={componentInput.value} lightMode />
|
||||
</div>
|
||||
{:else if fieldType === 'ag-grid'}
|
||||
<div class="flex flex-row rounded-md bg-surface items-center h-full">
|
||||
<div class="relative w-full">
|
||||
<input
|
||||
class="text-xs px-2 border-y w-full flex flex-row items-center border-r rounded-r-md h-8"
|
||||
bind:value={componentInput.value.field}
|
||||
placeholder="Field"
|
||||
/>
|
||||
<div class="absolute top-1 right-1">
|
||||
<AgGridWizard bind:value={componentInput.value}>
|
||||
<svelte:fragment slot="trigger">
|
||||
<Button color="light" size="xs2" nonCaptureEvent={true}>
|
||||
<div class="flex flex-row items-center gap-2 text-xs font-normal">
|
||||
<Settings size={16} />
|
||||
</div>
|
||||
</Button>
|
||||
</svelte:fragment>
|
||||
</AgGridWizard>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{:else}
|
||||
<div class="flex gap-1 relative w-full">
|
||||
<textarea
|
||||
|
||||
@@ -22,6 +22,7 @@ export type InputType =
|
||||
| 'labeledselect'
|
||||
| 'tab-select'
|
||||
| 'schema'
|
||||
| 'ag-grid'
|
||||
|
||||
// Connection to an output of another component
|
||||
// defined by the id of the component and the path of the output
|
||||
@@ -185,6 +186,7 @@ export type AppInput =
|
||||
| AppInputSpec<'labeledresource', object>
|
||||
| AppInputSpec<'array', object[], 'tab-select'>
|
||||
| AppInputSpec<'schema', object>
|
||||
| AppInputSpec<'array', object[], 'ag-grid'>
|
||||
|
||||
export type RowAppInput = Extract<AppInput, { type: 'row' }>
|
||||
export type StaticAppInput = Extract<AppInput, { type: 'static' }>
|
||||
|
||||
@@ -22,12 +22,6 @@
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
let menuRef: Menu<any> | undefined = undefined
|
||||
|
||||
export function toggleOpen() {
|
||||
menuRef?.openMenu()
|
||||
}
|
||||
</script>
|
||||
|
||||
<Menu let:open as="div" class="relative hover:z-50 flex w-full h-full">
|
||||
|
||||
@@ -0,0 +1,234 @@
|
||||
<script lang="ts">
|
||||
import { Popup } from '../common'
|
||||
import Toggle from '$lib/components/Toggle.svelte'
|
||||
import SimpleEditor from '../SimpleEditor.svelte'
|
||||
import Label from '../Label.svelte'
|
||||
import Tooltip from '../Tooltip.svelte'
|
||||
|
||||
import Button from '../common/button/Button.svelte'
|
||||
|
||||
type Column = {
|
||||
minWidth: number
|
||||
hide: boolean
|
||||
flex: number
|
||||
sort: 'asc' | 'desc'
|
||||
sortIndex: number
|
||||
aggFunc: string
|
||||
pivot: boolean
|
||||
pivotIndex: number
|
||||
pinned: 'left' | 'right' | boolean
|
||||
rowGroup: boolean
|
||||
rowGroupIndex: number
|
||||
valueFormatter: string
|
||||
valueParser: string
|
||||
field: string
|
||||
headerName: string
|
||||
}
|
||||
|
||||
export let value: Column | undefined
|
||||
|
||||
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"'
|
||||
}
|
||||
]
|
||||
|
||||
let renderCount = 0
|
||||
</script>
|
||||
|
||||
<Popup
|
||||
floatingConfig={{ strategy: 'fixed', placement: 'left-end' }}
|
||||
containerClasses="border rounded-lg shadow-lg bg-surface p-4"
|
||||
>
|
||||
<svelte:fragment slot="button">
|
||||
<slot name="trigger" />
|
||||
</svelte:fragment>
|
||||
{#if value}
|
||||
<div class="flex flex-col w-96 p-2 gap-4">
|
||||
<span class="text-sm mb-2 leading-6 font-semibold">
|
||||
Column definition
|
||||
<Tooltip
|
||||
documentationLink="https://www.ag-grid.com/javascript-data-grid/column-definitions/"
|
||||
>
|
||||
Column definitions are used to define columns in ag-Grid.
|
||||
</Tooltip>
|
||||
</span>
|
||||
|
||||
<Label label="Header name">
|
||||
<input type="text" placeholder="Header name" bind:value={value.headerName} />
|
||||
</Label>
|
||||
|
||||
<Label label="Min width (px)">
|
||||
<input type="number" placeholder="width" bind:value={value.minWidth} />
|
||||
</Label>
|
||||
|
||||
<Label label="Flex">
|
||||
<svelte:fragment slot="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>
|
||||
</svelte:fragment>
|
||||
|
||||
<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">
|
||||
<svelte:fragment slot="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>
|
||||
</svelte:fragment>
|
||||
<svelte:fragment slot="action">
|
||||
<Button
|
||||
size="xs"
|
||||
color="light"
|
||||
variant="border"
|
||||
on:click={() => {
|
||||
// @ts-ignore
|
||||
value.valueFormatter = null
|
||||
renderCount++
|
||||
}}
|
||||
>
|
||||
Clear
|
||||
</Button>
|
||||
</svelte:fragment>
|
||||
</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" />
|
||||
{/if}
|
||||
<div class="text-xs font-semibold">Presets</div>
|
||||
<select
|
||||
bind:value={value.valueFormatter}
|
||||
on:change={() => {
|
||||
renderCount++
|
||||
}}
|
||||
placeholder="Code"
|
||||
>
|
||||
{#each presets as preset}
|
||||
<option value={preset.value}>{preset.label}</option>
|
||||
{/each}
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<SimpleEditor autoHeight lang="javascript" bind:code={value.valueFormatter} />
|
||||
</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>
|
||||
|
||||
<!--
|
||||
EE only
|
||||
|
||||
<Label label="Aggregation Function">
|
||||
<SimpleEditor autoHeight lang="javascript" bind:code={value.aggFunc} />
|
||||
</Label>
|
||||
|
||||
<Label label="Pivot">
|
||||
<Toggle bind:checked={value.pivot} size="xs" />
|
||||
</Label>
|
||||
|
||||
<Label label="Pivot Index">
|
||||
<input type="number" placeholder="pivot index" bind:value={value.pivotIndex} />
|
||||
</Label>
|
||||
|
||||
<Label label="Pinned">
|
||||
<select bind:value={value.pinned}>
|
||||
<option value={null}>None</option>
|
||||
<option value="left">Left</option>
|
||||
<option value="right">Right</option>
|
||||
</select>
|
||||
</Label>
|
||||
|
||||
<Label label="Row Group">
|
||||
<Toggle bind:checked={value.rowGroup} size="xs" />
|
||||
</Label>
|
||||
|
||||
<Label label="Row Group Index">
|
||||
<input type="number" placeholder="row group index" bind:value={value.rowGroupIndex} />
|
||||
</Label>
|
||||
-->
|
||||
</div>
|
||||
{/if}
|
||||
</Popup>
|
||||
Reference in New Issue
Block a user