compote-ui 0.80.0 → 0.81.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/data-table-v9/column-filter-kind.d.ts +10 -1
- package/dist/components/data-table-v9/column-filter-kind.js +7 -0
- package/dist/components/data-table-v9/data-table-head.svelte +230 -230
- package/dist/components/data-table-v9/data-table.svelte +154 -154
- package/dist/components/data-table-v9/filter-editors/boolean-filter-editor.svelte +45 -45
- package/dist/components/data-table-v9/filter-editors/column-filter-editor.svelte +39 -33
- package/dist/components/data-table-v9/filter-editors/column-filter-editor.svelte.d.ts +7 -2
- package/dist/components/data-table-v9/filter-editors/date-range-filter-editor.svelte +52 -52
- package/dist/components/data-table-v9/filter-editors/range-filter-editor.svelte +51 -51
- package/dist/components/data-table-v9/filter-editors/select-filter-editor.svelte +59 -59
- package/dist/components/data-table-v9/filter-editors/text-filter-editor.svelte +20 -20
- package/dist/components/data-table-v9/header/column-header-filter.svelte +66 -66
- package/dist/components/data-table-v9/index.d.ts +9 -1
- package/dist/components/data-table-v9/index.js +7 -0
- package/dist/components/data-table-v9/toolbar/data-table-column-filter.svelte +203 -203
- package/dist/components/data-table-v9/toolbar/data-table-toolbar.svelte +4 -1
- package/dist/components/data-table-v9/types.d.ts +3 -1
- package/dist/components/data-table-v9/virtual/data-table-virtualized.svelte +82 -82
- package/dist/components/data-table-v9/virtual/index.d.ts +2 -2
- package/dist/components/data-table-v9/virtual/index.js +1 -1
- package/dist/components/dialog/alert-dialog.svelte +73 -73
- package/dist/components/dialog/dialog-root.svelte +45 -45
- package/dist/components/drawer/drawer-content.svelte +213 -213
- package/dist/components/popover/popover-content.svelte +97 -97
- package/dist/components/popover/popover-description.svelte +19 -19
- package/dist/components/popover/popover-root.svelte +27 -27
- package/dist/components/popover/popover-title.svelte +19 -19
- package/dist/icons/PhFunnelFill.svelte +19 -19
- package/package.json +1 -1
- package/skills/component-usage/references/overlays-and-floating-ui.md +278 -278
- package/skills/data-table/SKILL.md +362 -314
- package/skills/data-table/references/column-options.md +175 -175
- package/skills/data-table/references/table-state-and-reactivity.md +65 -65
|
@@ -1,175 +1,175 @@
|
|
|
1
|
-
# Column Options
|
|
2
|
-
|
|
3
|
-
Column helper:
|
|
4
|
-
|
|
5
|
-
```ts
|
|
6
|
-
const col = DataTable.createDataTableColumnHelper<Row>();
|
|
7
|
-
```
|
|
8
|
-
|
|
9
|
-
Accessors:
|
|
10
|
-
|
|
11
|
-
```ts
|
|
12
|
-
col.accessor('fieldName', { header: 'Field' });
|
|
13
|
-
col.accessorFn((row) => row.a + row.b, { id: 'sum', header: 'Sum' });
|
|
14
|
-
col.group('Group', [col.accessor('name', { header: 'Name' })]);
|
|
15
|
-
col.columns([col.accessor('id', { header: 'ID' })]);
|
|
16
|
-
```
|
|
17
|
-
|
|
18
|
-
Common options:
|
|
19
|
-
|
|
20
|
-
- `header: string`
|
|
21
|
-
- `type?: 'text' | 'number' | 'currency' | 'percent' | 'boolean' | 'select' | 'url' | 'phone' | 'date' | 'time' | 'date-time' | 'action'`
|
|
22
|
-
- `align?: 'left' | 'center' | 'right'`
|
|
23
|
-
- `size?: number`
|
|
24
|
-
- `minSize?: number`
|
|
25
|
-
- `maxSize?: number`
|
|
26
|
-
- `enableResizing?: boolean`
|
|
27
|
-
- `enableHiding?: boolean`
|
|
28
|
-
- `enableSorting?: boolean`
|
|
29
|
-
- `enableColumnFilter?: boolean`
|
|
30
|
-
- `filterFn?: FilterFn | string`
|
|
31
|
-
- `formatOptions?: Intl.NumberFormatOptions | Intl.DateTimeFormatOptions`
|
|
32
|
-
- `formatLocale?: string`
|
|
33
|
-
- `cell?: (value, row) => string | number | boolean | null | undefined`
|
|
34
|
-
- `cellComponent?: Component`
|
|
35
|
-
- `cellProps?: (value, row) => Record<string, unknown>`
|
|
36
|
-
- `cellSnippet?: Snippet`
|
|
37
|
-
- `pinned?: 'left' | 'right'` — sticky during horizontal scroll. Inside a `col.group(...)`, pinning
|
|
38
|
-
a member column also pins the group header fragment above it, so the label stays visible while
|
|
39
|
-
the rest of that group scrolls.
|
|
40
|
-
- `grow?: boolean`
|
|
41
|
-
- `sum?: boolean`
|
|
42
|
-
- `footer?: (values: unknown[]) => string | number | undefined`
|
|
43
|
-
|
|
44
|
-
Type defaults:
|
|
45
|
-
|
|
46
|
-
- `number`, `currency`: right aligned, size `120`
|
|
47
|
-
- `percent`: right aligned, size `100`
|
|
48
|
-
- `date`: center aligned, size `110`
|
|
49
|
-
- `time`: center aligned, size `80`
|
|
50
|
-
- `date-time`: center aligned, size `160`
|
|
51
|
-
- `boolean`: center aligned, size `90`
|
|
52
|
-
- `url`: center aligned, sorting disabled, size `60`
|
|
53
|
-
- `phone`: left aligned, size `160`
|
|
54
|
-
- `action`: center aligned, size `60`, sorting/hiding/column-filter all disabled — layout-only,
|
|
55
|
-
carries no value to format or filter. Supply a `cellComponent`/`cellSnippet` for the row action
|
|
56
|
-
buttons; the defaults keep the actions column out of the column-visibility menu and filter
|
|
57
|
-
toolbar automatically.
|
|
58
|
-
|
|
59
|
-
Use one `grow: true` column to absorb extra horizontal space.
|
|
60
|
-
|
|
61
|
-
## Date columns: display and filter
|
|
62
|
-
|
|
63
|
-
`date`, `time` and `date-time` cells are read the same way for display and for the date filter,
|
|
64
|
-
in the column's display zone — `formatOptions.timeZone` when set, otherwise the viewer's local
|
|
65
|
-
zone:
|
|
66
|
-
|
|
67
|
-
- `'2024-03-15'` (and a `CalendarDate`) is a calendar day. It always shows and filters as
|
|
68
|
-
15 March, also in zones west of UTC — it is never turned into a midnight-UTC instant.
|
|
69
|
-
- `Date`, epoch milliseconds, strings with an offset (`Z`, `+02:00`, DB `+02`) and
|
|
70
|
-
`ZonedDateTime` are instants, shown and filtered on the day they fall on in the display zone.
|
|
71
|
-
- A datetime without an offset (`'2024-03-15T10:30'`) is wall-clock time in the display zone.
|
|
72
|
-
- Unreadable values (e.g. `'2024-02-30'`) render as `-` and never match an active filter.
|
|
73
|
-
|
|
74
|
-
`date` and `date-time` columns filter by calendar-day range. The filter value is
|
|
75
|
-
`[from?, to?]`, each `YYYY-MM-DD` or empty (open-ended); both days are inclusive, a reversed
|
|
76
|
-
range is swapped, and an empty range removes the filter. Instants are judged by their calendar
|
|
77
|
-
day, so 23- and 25-hour DST days need no special handling. `time` columns keep the text filter.
|
|
78
|
-
|
|
79
|
-
```ts
|
|
80
|
-
table.getColumn('createdAt')?.setFilterValue(['2024-03-01', '2024-03-31']); // March
|
|
81
|
-
table.getColumn('createdAt')?.setFilterValue([undefined, '2024-03-31']); // up to 31 March
|
|
82
|
-
```
|
|
83
|
-
|
|
84
|
-
## Select columns with multiple values
|
|
85
|
-
|
|
86
|
-
A `type: 'select'` column may return an array (tags, multi-select attributes). The Funnel then
|
|
87
|
-
lists each item as its own option with a row count, a row matches when any of its items is
|
|
88
|
-
selected, and the cell renders the items joined with `, `. A row counts once per option even
|
|
89
|
-
when its array repeats an item. Missing values — `null`, `undefined`, `''` or an empty array —
|
|
90
|
-
share one `(empty)` option.
|
|
91
|
-
|
|
92
|
-
The filter value is an array of selected options: each is the `String()` of the cell value, and
|
|
93
|
-
the empty option is `null` — so a real `'null'` string stays its own option:
|
|
94
|
-
|
|
95
|
-
```ts
|
|
96
|
-
table.getColumn('tags')?.setFilterValue(['Battery', null]); // Battery or no tags
|
|
97
|
-
```
|
|
98
|
-
|
|
99
|
-
```ts
|
|
100
|
-
col.accessorFn((row) => row.tags.map((tag) => tag.name), {
|
|
101
|
-
id: 'tags',
|
|
102
|
-
header: 'Tags',
|
|
103
|
-
type: 'select'
|
|
104
|
-
});
|
|
105
|
-
```
|
|
106
|
-
|
|
107
|
-
## Locale for number/currency/percent
|
|
108
|
-
|
|
109
|
-
Priority: `formatLocale` prop → `useLocaleContext().locale` → browser default. Wrap the app in
|
|
110
|
-
`<LocaleProvider locale="sr-RS">` to set the context locale for every formatted column.
|
|
111
|
-
|
|
112
|
-
## Custom cellComponent
|
|
113
|
-
|
|
114
|
-
```svelte
|
|
115
|
-
<!-- PriceCell.svelte -->
|
|
116
|
-
<script lang="ts">
|
|
117
|
-
let { value, prices }: { value: number; prices: { vendor: string; price: number }[] } = $props();
|
|
118
|
-
</script>
|
|
119
|
-
|
|
120
|
-
<span>{value}</span>
|
|
121
|
-
```
|
|
122
|
-
|
|
123
|
-
```ts
|
|
124
|
-
col.accessorFn((row) => Math.min(...row.prices.map((p) => p.price)), {
|
|
125
|
-
id: 'minPrice',
|
|
126
|
-
header: 'Best Price',
|
|
127
|
-
type: 'currency',
|
|
128
|
-
align: 'right',
|
|
129
|
-
cellComponent: PriceCell,
|
|
130
|
-
cellProps: (value, row) => ({ value, prices: row.prices })
|
|
131
|
-
});
|
|
132
|
-
```
|
|
133
|
-
|
|
134
|
-
## Row height and cell alignment
|
|
135
|
-
|
|
136
|
-
Body cells are fixed at `h-9` (36px), matching the header, with content vertically centered — any
|
|
137
|
-
control up to 36px tall (every `Button` `icon-xs`/`icon-sm`/`icon`/`icon-lg` size) fits without
|
|
138
|
-
changing row height. Do **not** add negative margins (e.g. `-my-1`) to cell components to
|
|
139
|
-
compensate for row growth — in the virtualized table that fights the virtualizer's 37px row
|
|
140
|
-
estimate. Cells are `truncate` (single-line, `overflow: hidden`), so give action columns a little
|
|
141
|
-
extra `size` if a focus ring on a control near the edge gets clipped.
|
|
142
|
-
|
|
143
|
-
## DataTable.Search
|
|
144
|
-
|
|
145
|
-
Global filter input searching across all text columns, debounced (default 300ms).
|
|
146
|
-
|
|
147
|
-
- `table: DataTableInstance<T>` — required
|
|
148
|
-
- `placeholder?: string` (default `'Search...'`)
|
|
149
|
-
- `class?: ClassValue`
|
|
150
|
-
- `debounceMs?: number` (default `300`)
|
|
151
|
-
|
|
152
|
-
Text/number columns stay searchable even when the first row's value is `null`/`undefined`;
|
|
153
|
-
booleans and object-valued columns are excluded from global search. Also available as
|
|
154
|
-
`VirtualDataTable.Search` for the virtualized variant.
|
|
155
|
-
|
|
156
|
-
## DataTable.Root props
|
|
157
|
-
|
|
158
|
-
`table: DataTableInstance<T>` (required), `caption?: string` (sr-only `<caption>`),
|
|
159
|
-
`emptyMessage?: string` (default `'No rows found'`), `class?: ClassValue`,
|
|
160
|
-
`headerFilters?: boolean` (default `false`; funnel filter popups in column headers — see
|
|
161
|
-
"Filter from column headers" in SKILL.md),
|
|
162
|
-
`onRowClick?: (details: { row: T; event: MouseEvent }) => void`,
|
|
163
|
-
`onRowDoubleClick?: (details: { row: T; event: MouseEvent }) => void` — `row` is `row.original`.
|
|
164
|
-
Clicks on the row-selection checkbox and url-cell buttons do **not** trigger either handler.
|
|
165
|
-
`VirtualDataTable.Root` shares the same props.
|
|
166
|
-
|
|
167
|
-
```svelte
|
|
168
|
-
<DataTable.Root
|
|
169
|
-
{table}
|
|
170
|
-
onRowDoubleClick={({ row, event }) => {
|
|
171
|
-
if (event.ctrlKey || event.metaKey) openInNewTab(row.id);
|
|
172
|
-
else navigateTo(row.id);
|
|
173
|
-
}}
|
|
174
|
-
/>
|
|
175
|
-
```
|
|
1
|
+
# Column Options
|
|
2
|
+
|
|
3
|
+
Column helper:
|
|
4
|
+
|
|
5
|
+
```ts
|
|
6
|
+
const col = DataTable.createDataTableColumnHelper<Row>();
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
Accessors:
|
|
10
|
+
|
|
11
|
+
```ts
|
|
12
|
+
col.accessor('fieldName', { header: 'Field' });
|
|
13
|
+
col.accessorFn((row) => row.a + row.b, { id: 'sum', header: 'Sum' });
|
|
14
|
+
col.group('Group', [col.accessor('name', { header: 'Name' })]);
|
|
15
|
+
col.columns([col.accessor('id', { header: 'ID' })]);
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
Common options:
|
|
19
|
+
|
|
20
|
+
- `header: string`
|
|
21
|
+
- `type?: 'text' | 'number' | 'currency' | 'percent' | 'boolean' | 'select' | 'url' | 'phone' | 'date' | 'time' | 'date-time' | 'action'`
|
|
22
|
+
- `align?: 'left' | 'center' | 'right'`
|
|
23
|
+
- `size?: number`
|
|
24
|
+
- `minSize?: number`
|
|
25
|
+
- `maxSize?: number`
|
|
26
|
+
- `enableResizing?: boolean`
|
|
27
|
+
- `enableHiding?: boolean`
|
|
28
|
+
- `enableSorting?: boolean`
|
|
29
|
+
- `enableColumnFilter?: boolean`
|
|
30
|
+
- `filterFn?: FilterFn | string`
|
|
31
|
+
- `formatOptions?: Intl.NumberFormatOptions | Intl.DateTimeFormatOptions`
|
|
32
|
+
- `formatLocale?: string`
|
|
33
|
+
- `cell?: (value, row) => string | number | boolean | null | undefined`
|
|
34
|
+
- `cellComponent?: Component`
|
|
35
|
+
- `cellProps?: (value, row) => Record<string, unknown>`
|
|
36
|
+
- `cellSnippet?: Snippet`
|
|
37
|
+
- `pinned?: 'left' | 'right'` — sticky during horizontal scroll. Inside a `col.group(...)`, pinning
|
|
38
|
+
a member column also pins the group header fragment above it, so the label stays visible while
|
|
39
|
+
the rest of that group scrolls.
|
|
40
|
+
- `grow?: boolean`
|
|
41
|
+
- `sum?: boolean`
|
|
42
|
+
- `footer?: (values: unknown[]) => string | number | undefined`
|
|
43
|
+
|
|
44
|
+
Type defaults:
|
|
45
|
+
|
|
46
|
+
- `number`, `currency`: right aligned, size `120`
|
|
47
|
+
- `percent`: right aligned, size `100`
|
|
48
|
+
- `date`: center aligned, size `110`
|
|
49
|
+
- `time`: center aligned, size `80`
|
|
50
|
+
- `date-time`: center aligned, size `160`
|
|
51
|
+
- `boolean`: center aligned, size `90`
|
|
52
|
+
- `url`: center aligned, sorting disabled, size `60`
|
|
53
|
+
- `phone`: left aligned, size `160`
|
|
54
|
+
- `action`: center aligned, size `60`, sorting/hiding/column-filter all disabled — layout-only,
|
|
55
|
+
carries no value to format or filter. Supply a `cellComponent`/`cellSnippet` for the row action
|
|
56
|
+
buttons; the defaults keep the actions column out of the column-visibility menu and filter
|
|
57
|
+
toolbar automatically.
|
|
58
|
+
|
|
59
|
+
Use one `grow: true` column to absorb extra horizontal space.
|
|
60
|
+
|
|
61
|
+
## Date columns: display and filter
|
|
62
|
+
|
|
63
|
+
`date`, `time` and `date-time` cells are read the same way for display and for the date filter,
|
|
64
|
+
in the column's display zone — `formatOptions.timeZone` when set, otherwise the viewer's local
|
|
65
|
+
zone:
|
|
66
|
+
|
|
67
|
+
- `'2024-03-15'` (and a `CalendarDate`) is a calendar day. It always shows and filters as
|
|
68
|
+
15 March, also in zones west of UTC — it is never turned into a midnight-UTC instant.
|
|
69
|
+
- `Date`, epoch milliseconds, strings with an offset (`Z`, `+02:00`, DB `+02`) and
|
|
70
|
+
`ZonedDateTime` are instants, shown and filtered on the day they fall on in the display zone.
|
|
71
|
+
- A datetime without an offset (`'2024-03-15T10:30'`) is wall-clock time in the display zone.
|
|
72
|
+
- Unreadable values (e.g. `'2024-02-30'`) render as `-` and never match an active filter.
|
|
73
|
+
|
|
74
|
+
`date` and `date-time` columns filter by calendar-day range. The filter value is
|
|
75
|
+
`[from?, to?]`, each `YYYY-MM-DD` or empty (open-ended); both days are inclusive, a reversed
|
|
76
|
+
range is swapped, and an empty range removes the filter. Instants are judged by their calendar
|
|
77
|
+
day, so 23- and 25-hour DST days need no special handling. `time` columns keep the text filter.
|
|
78
|
+
|
|
79
|
+
```ts
|
|
80
|
+
table.getColumn('createdAt')?.setFilterValue(['2024-03-01', '2024-03-31']); // March
|
|
81
|
+
table.getColumn('createdAt')?.setFilterValue([undefined, '2024-03-31']); // up to 31 March
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
## Select columns with multiple values
|
|
85
|
+
|
|
86
|
+
A `type: 'select'` column may return an array (tags, multi-select attributes). The Funnel then
|
|
87
|
+
lists each item as its own option with a row count, a row matches when any of its items is
|
|
88
|
+
selected, and the cell renders the items joined with `, `. A row counts once per option even
|
|
89
|
+
when its array repeats an item. Missing values — `null`, `undefined`, `''` or an empty array —
|
|
90
|
+
share one `(empty)` option.
|
|
91
|
+
|
|
92
|
+
The filter value is an array of selected options: each is the `String()` of the cell value, and
|
|
93
|
+
the empty option is `null` — so a real `'null'` string stays its own option:
|
|
94
|
+
|
|
95
|
+
```ts
|
|
96
|
+
table.getColumn('tags')?.setFilterValue(['Battery', null]); // Battery or no tags
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
```ts
|
|
100
|
+
col.accessorFn((row) => row.tags.map((tag) => tag.name), {
|
|
101
|
+
id: 'tags',
|
|
102
|
+
header: 'Tags',
|
|
103
|
+
type: 'select'
|
|
104
|
+
});
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
## Locale for number/currency/percent
|
|
108
|
+
|
|
109
|
+
Priority: `formatLocale` prop → `useLocaleContext().locale` → browser default. Wrap the app in
|
|
110
|
+
`<LocaleProvider locale="sr-RS">` to set the context locale for every formatted column.
|
|
111
|
+
|
|
112
|
+
## Custom cellComponent
|
|
113
|
+
|
|
114
|
+
```svelte
|
|
115
|
+
<!-- PriceCell.svelte -->
|
|
116
|
+
<script lang="ts">
|
|
117
|
+
let { value, prices }: { value: number; prices: { vendor: string; price: number }[] } = $props();
|
|
118
|
+
</script>
|
|
119
|
+
|
|
120
|
+
<span>{value}</span>
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
```ts
|
|
124
|
+
col.accessorFn((row) => Math.min(...row.prices.map((p) => p.price)), {
|
|
125
|
+
id: 'minPrice',
|
|
126
|
+
header: 'Best Price',
|
|
127
|
+
type: 'currency',
|
|
128
|
+
align: 'right',
|
|
129
|
+
cellComponent: PriceCell,
|
|
130
|
+
cellProps: (value, row) => ({ value, prices: row.prices })
|
|
131
|
+
});
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
## Row height and cell alignment
|
|
135
|
+
|
|
136
|
+
Body cells are fixed at `h-9` (36px), matching the header, with content vertically centered — any
|
|
137
|
+
control up to 36px tall (every `Button` `icon-xs`/`icon-sm`/`icon`/`icon-lg` size) fits without
|
|
138
|
+
changing row height. Do **not** add negative margins (e.g. `-my-1`) to cell components to
|
|
139
|
+
compensate for row growth — in the virtualized table that fights the virtualizer's 37px row
|
|
140
|
+
estimate. Cells are `truncate` (single-line, `overflow: hidden`), so give action columns a little
|
|
141
|
+
extra `size` if a focus ring on a control near the edge gets clipped.
|
|
142
|
+
|
|
143
|
+
## DataTable.Search
|
|
144
|
+
|
|
145
|
+
Global filter input searching across all text columns, debounced (default 300ms).
|
|
146
|
+
|
|
147
|
+
- `table: DataTableInstance<T>` — required
|
|
148
|
+
- `placeholder?: string` (default `'Search...'`)
|
|
149
|
+
- `class?: ClassValue`
|
|
150
|
+
- `debounceMs?: number` (default `300`)
|
|
151
|
+
|
|
152
|
+
Text/number columns stay searchable even when the first row's value is `null`/`undefined`;
|
|
153
|
+
booleans and object-valued columns are excluded from global search. Also available as
|
|
154
|
+
`VirtualDataTable.Search` for the virtualized variant.
|
|
155
|
+
|
|
156
|
+
## DataTable.Root props
|
|
157
|
+
|
|
158
|
+
`table: DataTableInstance<T>` (required), `caption?: string` (sr-only `<caption>`),
|
|
159
|
+
`emptyMessage?: string` (default `'No rows found'`), `class?: ClassValue`,
|
|
160
|
+
`headerFilters?: boolean` (default `false`; funnel filter popups in column headers — see
|
|
161
|
+
"Filter from column headers" in SKILL.md),
|
|
162
|
+
`onRowClick?: (details: { row: T; event: MouseEvent }) => void`,
|
|
163
|
+
`onRowDoubleClick?: (details: { row: T; event: MouseEvent }) => void` — `row` is `row.original`.
|
|
164
|
+
Clicks on the row-selection checkbox and url-cell buttons do **not** trigger either handler.
|
|
165
|
+
`VirtualDataTable.Root` shares the same props.
|
|
166
|
+
|
|
167
|
+
```svelte
|
|
168
|
+
<DataTable.Root
|
|
169
|
+
{table}
|
|
170
|
+
onRowDoubleClick={({ row, event }) => {
|
|
171
|
+
if (event.ctrlKey || event.metaKey) openInNewTab(row.id);
|
|
172
|
+
else navigateTo(row.id);
|
|
173
|
+
}}
|
|
174
|
+
/>
|
|
175
|
+
```
|
|
@@ -1,65 +1,65 @@
|
|
|
1
|
-
# Table State And Reactivity
|
|
2
|
-
|
|
3
|
-
Use reactive getters for state that changes:
|
|
4
|
-
|
|
5
|
-
```ts
|
|
6
|
-
const table = DataTable.createTable({
|
|
7
|
-
get data() {
|
|
8
|
-
return rows;
|
|
9
|
-
},
|
|
10
|
-
get columns() {
|
|
11
|
-
return columns;
|
|
12
|
-
},
|
|
13
|
-
getRowId: (row) => row.id
|
|
14
|
-
});
|
|
15
|
-
```
|
|
16
|
-
|
|
17
|
-
Use `$state.raw` for column arrays that are reassigned:
|
|
18
|
-
|
|
19
|
-
```ts
|
|
20
|
-
let columns = $state.raw([idCol, nameCol]);
|
|
21
|
-
```
|
|
22
|
-
|
|
23
|
-
Read table state:
|
|
24
|
-
|
|
25
|
-
```ts
|
|
26
|
-
const selected = $derived(table.getSelectedRowModel().rows.map((row) => row.original));
|
|
27
|
-
const sorting = $derived(table.store.state.sorting);
|
|
28
|
-
const filters = $derived(table.store.state.columnFilters);
|
|
29
|
-
```
|
|
30
|
-
|
|
31
|
-
Change filters from app code with the table API — `column.setFilterValue(...)`,
|
|
32
|
-
`table.setColumnFilters(...)`, `table.resetColumnFilters()`. `DataTable.ColumnFilter` and the
|
|
33
|
-
`headerFilters` popups show the live filter value in their inputs (they only hold edits still
|
|
34
|
-
inside the 300ms debounce), so an external change clears or updates the inputs, and editing one
|
|
35
|
-
range bound keeps the other bound's current value. An edit still inside the debounce is dropped
|
|
36
|
-
when its column's filter changes from outside — even if the filter ends on its old value again
|
|
37
|
-
(e.g. reset, then saved filters restored), since `createTable` keeps a per-column filter revision
|
|
38
|
-
that only goes up.
|
|
39
|
-
|
|
40
|
-
Pending edits belong to the table, not to the control they were typed in: the latest edit to a
|
|
41
|
-
column wins whether it came from a header popup or the toolbar, and it still commits after its
|
|
42
|
-
popup closes. `table.resetColumnFilters()` and `table.reset()` cancel every pending edit — also
|
|
43
|
-
one typed into a column that had no filter yet, which a reset would otherwise leave untouched —
|
|
44
|
-
and so does destroying the component that created the table.
|
|
45
|
-
|
|
46
|
-
```ts
|
|
47
|
-
// e.g. drop every filter on columns that belong to the previous category
|
|
48
|
-
table.setColumnFilters((filters) => filters.filter((f) => !f.id.startsWith('attr_')));
|
|
49
|
-
```
|
|
50
|
-
|
|
51
|
-
Persist column visibility with the observer callback:
|
|
52
|
-
|
|
53
|
-
```ts
|
|
54
|
-
const table = DataTable.createTable({
|
|
55
|
-
get data() {
|
|
56
|
-
return rows;
|
|
57
|
-
},
|
|
58
|
-
columns,
|
|
59
|
-
onColumnVisibilityChange: (visibility) => {
|
|
60
|
-
localStorage.setItem('column-visibility', JSON.stringify(visibility));
|
|
61
|
-
}
|
|
62
|
-
});
|
|
63
|
-
```
|
|
64
|
-
|
|
65
|
-
Initial `columnVisibility`, `columnSizing`, and `columnPinning` are computed when the table is created. Columns added later default to visible and use their own size.
|
|
1
|
+
# Table State And Reactivity
|
|
2
|
+
|
|
3
|
+
Use reactive getters for state that changes:
|
|
4
|
+
|
|
5
|
+
```ts
|
|
6
|
+
const table = DataTable.createTable({
|
|
7
|
+
get data() {
|
|
8
|
+
return rows;
|
|
9
|
+
},
|
|
10
|
+
get columns() {
|
|
11
|
+
return columns;
|
|
12
|
+
},
|
|
13
|
+
getRowId: (row) => row.id
|
|
14
|
+
});
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
Use `$state.raw` for column arrays that are reassigned:
|
|
18
|
+
|
|
19
|
+
```ts
|
|
20
|
+
let columns = $state.raw([idCol, nameCol]);
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
Read table state:
|
|
24
|
+
|
|
25
|
+
```ts
|
|
26
|
+
const selected = $derived(table.getSelectedRowModel().rows.map((row) => row.original));
|
|
27
|
+
const sorting = $derived(table.store.state.sorting);
|
|
28
|
+
const filters = $derived(table.store.state.columnFilters);
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
Change filters from app code with the table API — `column.setFilterValue(...)`,
|
|
32
|
+
`table.setColumnFilters(...)`, `table.resetColumnFilters()`. `DataTable.ColumnFilter` and the
|
|
33
|
+
`headerFilters` popups show the live filter value in their inputs (they only hold edits still
|
|
34
|
+
inside the 300ms debounce), so an external change clears or updates the inputs, and editing one
|
|
35
|
+
range bound keeps the other bound's current value. An edit still inside the debounce is dropped
|
|
36
|
+
when its column's filter changes from outside — even if the filter ends on its old value again
|
|
37
|
+
(e.g. reset, then saved filters restored), since `createTable` keeps a per-column filter revision
|
|
38
|
+
that only goes up.
|
|
39
|
+
|
|
40
|
+
Pending edits belong to the table, not to the control they were typed in: the latest edit to a
|
|
41
|
+
column wins whether it came from a header popup or the toolbar, and it still commits after its
|
|
42
|
+
popup closes. `table.resetColumnFilters()` and `table.reset()` cancel every pending edit — also
|
|
43
|
+
one typed into a column that had no filter yet, which a reset would otherwise leave untouched —
|
|
44
|
+
and so does destroying the component that created the table.
|
|
45
|
+
|
|
46
|
+
```ts
|
|
47
|
+
// e.g. drop every filter on columns that belong to the previous category
|
|
48
|
+
table.setColumnFilters((filters) => filters.filter((f) => !f.id.startsWith('attr_')));
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
Persist column visibility with the observer callback:
|
|
52
|
+
|
|
53
|
+
```ts
|
|
54
|
+
const table = DataTable.createTable({
|
|
55
|
+
get data() {
|
|
56
|
+
return rows;
|
|
57
|
+
},
|
|
58
|
+
columns,
|
|
59
|
+
onColumnVisibilityChange: (visibility) => {
|
|
60
|
+
localStorage.setItem('column-visibility', JSON.stringify(visibility));
|
|
61
|
+
}
|
|
62
|
+
});
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
Initial `columnVisibility`, `columnSizing`, and `columnPinning` are computed when the table is created. Columns added later default to visible and use their own size.
|