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:
Faton Ramadani
2023-10-10 15:25:40 +02:00
committed by GitHub
parent 8491f248a0
commit ade3d90d70
7 changed files with 293 additions and 14 deletions
+3 -2
View File
@@ -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,
@@ -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
@@ -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>