compote-ui 0.79.1 → 0.80.0

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.
Files changed (69) hide show
  1. package/dist/components/data-table-v9/column-filter-editing.svelte.d.ts +41 -0
  2. package/dist/components/data-table-v9/column-filter-editing.svelte.js +88 -0
  3. package/dist/components/data-table-v9/column-filter-kind.d.ts +10 -0
  4. package/dist/components/data-table-v9/column-filter-kind.js +25 -0
  5. package/dist/components/data-table-v9/create-table.svelte.js +49 -26
  6. package/dist/components/data-table-v9/data-table-head.svelte +230 -201
  7. package/dist/components/data-table-v9/data-table-head.svelte.d.ts +2 -0
  8. package/dist/components/data-table-v9/data-table-utils.d.ts +7 -0
  9. package/dist/components/data-table-v9/data-table-utils.js +4 -0
  10. package/dist/components/data-table-v9/data-table.svelte +154 -151
  11. package/dist/components/data-table-v9/data-table.svelte.d.ts +2 -0
  12. package/dist/components/data-table-v9/date-cell.d.ts +13 -0
  13. package/dist/components/data-table-v9/date-cell.js +38 -0
  14. package/dist/components/data-table-v9/filter-editors/boolean-filter-editor.svelte +45 -0
  15. package/dist/components/data-table-v9/filter-editors/boolean-filter-editor.svelte.d.ts +7 -0
  16. package/dist/components/data-table-v9/filter-editors/column-filter-editor.svelte +33 -0
  17. package/dist/components/data-table-v9/filter-editors/column-filter-editor.svelte.d.ts +30 -0
  18. package/dist/components/data-table-v9/filter-editors/date-range-filter-editor.svelte +52 -0
  19. package/dist/components/data-table-v9/filter-editors/date-range-filter-editor.svelte.d.ts +28 -0
  20. package/dist/components/data-table-v9/filter-editors/range-filter-editor.svelte +51 -0
  21. package/dist/components/data-table-v9/filter-editors/range-filter-editor.svelte.d.ts +30 -0
  22. package/dist/components/data-table-v9/filter-editors/select-filter-editor.svelte +59 -0
  23. package/dist/components/data-table-v9/filter-editors/select-filter-editor.svelte.d.ts +28 -0
  24. package/dist/components/data-table-v9/filter-editors/text-filter-editor.svelte +20 -0
  25. package/dist/components/data-table-v9/filter-editors/text-filter-editor.svelte.d.ts +8 -0
  26. package/dist/components/data-table-v9/filter-revisions.svelte.d.ts +17 -0
  27. package/dist/components/data-table-v9/filter-revisions.svelte.js +42 -0
  28. package/dist/components/data-table-v9/header/column-header-filter.svelte +66 -0
  29. package/dist/components/data-table-v9/header/column-header-filter.svelte.d.ts +30 -0
  30. package/dist/components/data-table-v9/table-view-state.svelte.d.ts +1 -0
  31. package/dist/components/data-table-v9/table-view-state.svelte.js +3 -0
  32. package/dist/components/data-table-v9/toolbar/data-table-column-filter.svelte +203 -433
  33. package/dist/components/data-table-v9/toolbar/data-table-column-filter.svelte.d.ts +1 -1
  34. package/dist/components/data-table-v9/virtual/data-table-virtualized.svelte +82 -79
  35. package/dist/components/data-table-v9/virtual/data-table-virtualized.svelte.d.ts +2 -0
  36. package/dist/components/dialog/alert-dialog.svelte +73 -71
  37. package/dist/components/dialog/dialog-root.svelte +45 -43
  38. package/dist/components/drawer/drawer-content.svelte +213 -211
  39. package/dist/components/number-input/number-input.svelte +2 -6
  40. package/dist/components/popover/popover-content.svelte +97 -71
  41. package/dist/components/popover/popover-content.svelte.d.ts +2 -1
  42. package/dist/components/popover/popover-description.svelte +19 -15
  43. package/dist/components/popover/popover-labels.svelte.d.ts +18 -0
  44. package/dist/components/popover/popover-labels.svelte.js +35 -0
  45. package/dist/components/popover/popover-root.svelte +27 -24
  46. package/dist/components/popover/popover-title.svelte +19 -15
  47. package/dist/icons/PhFunnelFill.svelte +19 -0
  48. package/dist/icons/PhFunnelFill.svelte.d.ts +5 -0
  49. package/dist/icons/index.d.ts +1 -0
  50. package/dist/icons/index.js +1 -0
  51. package/dist/utils/date-filter.d.ts +31 -0
  52. package/dist/utils/date-filter.js +58 -0
  53. package/dist/utils/date.d.ts +19 -1
  54. package/dist/utils/date.js +48 -1
  55. package/dist/utils/debounced-filter-edit.svelte.d.ts +16 -0
  56. package/dist/utils/debounced-filter-edit.svelte.js +56 -0
  57. package/dist/utils/number-input-value.d.ts +10 -0
  58. package/dist/utils/number-input-value.js +15 -0
  59. package/dist/utils/range-filter.d.ts +19 -0
  60. package/dist/utils/range-filter.js +23 -0
  61. package/dist/utils/release-focus-on-exit.d.ts +22 -0
  62. package/dist/utils/release-focus-on-exit.js +26 -0
  63. package/package.json +1 -1
  64. package/skills/component-usage/SKILL.md +1 -1
  65. package/skills/component-usage/references/forms-and-inputs.md +3 -0
  66. package/skills/component-usage/references/overlays-and-floating-ui.md +278 -273
  67. package/skills/data-table/SKILL.md +314 -283
  68. package/skills/data-table/references/column-options.md +175 -150
  69. package/skills/data-table/references/table-state-and-reactivity.md +65 -45
@@ -1,151 +1,154 @@
1
- <script lang="ts" generics="T extends RowData">
2
- import type { RowData } from '@tanstack/svelte-table';
3
- import type { HTMLAttributes } from 'svelte/elements';
4
- import { cn, type ClassValue } from 'tailwind-variants';
5
- import type { DataTableInstance } from './data-table-utils';
6
- import { createTableViewState } from './table-view-state.svelte';
7
- import DataTableHead from './data-table-head.svelte';
8
- import DataTableFoot from './data-table-foot.svelte';
9
- import DataTableCellContent from './data-table-cell-content.svelte';
10
- import {
11
- alignClass,
12
- columnSizeStyle,
13
- getColumnMeta,
14
- getPinningStyle,
15
- getRowCells,
16
- selectionColumnSizeStyle,
17
- tableSizeStyle
18
- } from './data-table-utils';
19
-
20
- type Props = Omit<HTMLAttributes<HTMLDivElement>, 'class'> & {
21
- table: DataTableInstance<T>;
22
- caption?: string;
23
- emptyMessage?: string;
24
- class?: ClassValue;
25
- onRowClick?: (details: { row: T; event: MouseEvent }) => void;
26
- onRowDoubleClick?: (details: { row: T; event: MouseEvent }) => void;
27
- };
28
-
29
- let {
30
- table,
31
- caption,
32
- emptyMessage = 'No rows found',
33
- class: className,
34
- onRowClick,
35
- onRowDoubleClick,
36
- ...rest
37
- }: Props = $props();
38
-
39
- const view = createTableViewState(() => table);
40
-
41
- const visibleColumnCount = $derived(view.visibleLeafColumns.length);
42
- const tableColumnCount = $derived(visibleColumnCount + (view.isRowSelectionEnabled ? 1 : 0));
43
- const renderedColumnCount = $derived(tableColumnCount + 1);
44
- </script>
45
-
46
- <div
47
- class={cn(
48
- 'flex max-h-full min-h-0 flex-col overflow-hidden rounded-lg border border-surface-3 bg-surface-1',
49
- className
50
- )}
51
- {...rest}
52
- >
53
- {#if view.isColumnResizing}
54
- <div aria-hidden="true" class="fixed inset-0 z-50 cursor-col-resize select-none"></div>
55
- {/if}
56
-
57
- <div class="min-h-0 flex-1 overflow-auto">
58
- <table
59
- class="table-fixed border-separate border-spacing-0 text-sm"
60
- style={tableSizeStyle(table, view.isRowSelectionEnabled, view)}
61
- >
62
- <colgroup>
63
- {#if view.isRowSelectionEnabled}
64
- <col style={selectionColumnSizeStyle()} />
65
- {/if}
66
- {#each view.visibleLeafColumns as column (column.id)}
67
- <col
68
- style={getColumnMeta(column.columnDef)?.grow
69
- ? undefined
70
- : columnSizeStyle(column.getSize())}
71
- />
72
- {/each}
73
- {#if !view.hasGrowColumn}
74
- <col />
75
- {/if}
76
- </colgroup>
77
- {#if caption}
78
- <caption class="sr-only">{caption}</caption>
79
- {/if}
80
- <DataTableHead {table} {view} hasGrowColumn={view.hasGrowColumn} />
81
- <tbody>
82
- {#each view.rowModel.rows as row (row.id)}
83
- {@const rowSelected = view.rowSelection[row.id] === true}
84
- <tr
85
- class={cn(
86
- 'group/row',
87
- '[--row-bg:var(--compote-surface-1)]',
88
- 'hover:bg-well/60 hover:[--row-bg:color-mix(in_srgb,var(--compote-well)_60%,var(--compote-surface-1))]',
89
- rowSelected &&
90
- 'bg-well/60 [--row-bg:color-mix(in_srgb,var(--compote-well)_60%,var(--compote-surface-1))]'
91
- )}
92
- onclick={(event) => onRowClick?.({ row: row.original, event })}
93
- ondblclick={(event) => onRowDoubleClick?.({ row: row.original, event })}
94
- >
95
- {#if view.isRowSelectionEnabled}
96
- <td
97
- class="border-b border-surface-2 bg-(--row-bg) px-3 py-2 text-center align-middle group-last/row:border-b-0"
98
- style="position: sticky; left: 0; z-index: 1"
99
- onclick={(event) => event.stopPropagation()}
100
- ondblclick={(event) => event.stopPropagation()}
101
- >
102
- <input
103
- type="checkbox"
104
- aria-label="Select row"
105
- class="table-checkbox mx-auto block size-4"
106
- checked={rowSelected}
107
- disabled={!row.getCanSelect()}
108
- onchange={(e) => row.toggleSelected(e.currentTarget.checked)}
109
- />
110
- </td>
111
- {/if}
112
- {#each getRowCells(row, view) as cell (cell.id)}
113
- {@const meta = getColumnMeta(cell.column.columnDef)}
114
- <td
115
- class={cn(
116
- 'h-9 truncate border-b border-b-surface-2 px-3 py-0 align-middle group-last/row:border-b-0',
117
- alignClass(meta?.align),
118
- cell.column.getIsPinned() && 'bg-(--row-bg)'
119
- )}
120
- style={getPinningStyle(cell.column, table, view, false, view.isRowSelectionEnabled)}
121
- >
122
- <DataTableCellContent {cell} />
123
- </td>
124
- {/each}
125
- {#if !view.hasGrowColumn}
126
- <td aria-hidden="true" class="border-b border-surface-2 p-0 group-last/row:border-b-0"
127
- ></td>
128
- {/if}
129
- </tr>
130
- {:else}
131
- <tr>
132
- <td class="px-3 py-10 text-center text-sm text-ink-dim" colspan={renderedColumnCount}>
133
- {emptyMessage}
134
- </td>
135
- </tr>
136
- {/each}
137
- </tbody>
138
- {#if view.hasFooter}
139
- <DataTableFoot {table} {view} hasGrowColumn={view.hasGrowColumn} />
140
- {/if}
141
- </table>
142
- </div>
143
-
144
- <div class="shrink-0 border-t border-surface-3 bg-surface-2 px-3 py-2 text-sm text-ink-dim">
145
- {#if view.isRowSelectionEnabled}
146
- {view.selectedRowCount} of {view.rowModel.rows.length} rows selected
147
- {:else}
148
- {view.rowModel.rows.length} rows
149
- {/if}
150
- </div>
151
- </div>
1
+ <script lang="ts" generics="T extends RowData">
2
+ import type { RowData } from '@tanstack/svelte-table';
3
+ import type { HTMLAttributes } from 'svelte/elements';
4
+ import { cn, type ClassValue } from 'tailwind-variants';
5
+ import type { DataTableInstance } from './data-table-utils';
6
+ import { createTableViewState } from './table-view-state.svelte';
7
+ import DataTableHead from './data-table-head.svelte';
8
+ import DataTableFoot from './data-table-foot.svelte';
9
+ import DataTableCellContent from './data-table-cell-content.svelte';
10
+ import {
11
+ alignClass,
12
+ columnSizeStyle,
13
+ getColumnMeta,
14
+ getPinningStyle,
15
+ getRowCells,
16
+ selectionColumnSizeStyle,
17
+ tableSizeStyle
18
+ } from './data-table-utils';
19
+
20
+ type Props = Omit<HTMLAttributes<HTMLDivElement>, 'class'> & {
21
+ table: DataTableInstance<T>;
22
+ caption?: string;
23
+ emptyMessage?: string;
24
+ class?: ClassValue;
25
+ /** Show a filter funnel in the header of every filterable column. */
26
+ headerFilters?: boolean;
27
+ onRowClick?: (details: { row: T; event: MouseEvent }) => void;
28
+ onRowDoubleClick?: (details: { row: T; event: MouseEvent }) => void;
29
+ };
30
+
31
+ let {
32
+ table,
33
+ caption,
34
+ emptyMessage = 'No rows found',
35
+ class: className,
36
+ headerFilters = false,
37
+ onRowClick,
38
+ onRowDoubleClick,
39
+ ...rest
40
+ }: Props = $props();
41
+
42
+ const view = createTableViewState(() => table);
43
+
44
+ const visibleColumnCount = $derived(view.visibleLeafColumns.length);
45
+ const tableColumnCount = $derived(visibleColumnCount + (view.isRowSelectionEnabled ? 1 : 0));
46
+ const renderedColumnCount = $derived(tableColumnCount + 1);
47
+ </script>
48
+
49
+ <div
50
+ class={cn(
51
+ 'flex max-h-full min-h-0 flex-col overflow-hidden rounded-lg border border-surface-3 bg-surface-1',
52
+ className
53
+ )}
54
+ {...rest}
55
+ >
56
+ {#if view.isColumnResizing}
57
+ <div aria-hidden="true" class="fixed inset-0 z-50 cursor-col-resize select-none"></div>
58
+ {/if}
59
+
60
+ <div class="min-h-0 flex-1 overflow-auto">
61
+ <table
62
+ class="table-fixed border-separate border-spacing-0 text-sm"
63
+ style={tableSizeStyle(table, view.isRowSelectionEnabled, view)}
64
+ >
65
+ <colgroup>
66
+ {#if view.isRowSelectionEnabled}
67
+ <col style={selectionColumnSizeStyle()} />
68
+ {/if}
69
+ {#each view.visibleLeafColumns as column (column.id)}
70
+ <col
71
+ style={getColumnMeta(column.columnDef)?.grow
72
+ ? undefined
73
+ : columnSizeStyle(column.getSize())}
74
+ />
75
+ {/each}
76
+ {#if !view.hasGrowColumn}
77
+ <col />
78
+ {/if}
79
+ </colgroup>
80
+ {#if caption}
81
+ <caption class="sr-only">{caption}</caption>
82
+ {/if}
83
+ <DataTableHead {table} {view} hasGrowColumn={view.hasGrowColumn} {headerFilters} />
84
+ <tbody>
85
+ {#each view.rowModel.rows as row (row.id)}
86
+ {@const rowSelected = view.rowSelection[row.id] === true}
87
+ <tr
88
+ class={cn(
89
+ 'group/row',
90
+ '[--row-bg:var(--compote-surface-1)]',
91
+ 'hover:bg-well/60 hover:[--row-bg:color-mix(in_srgb,var(--compote-well)_60%,var(--compote-surface-1))]',
92
+ rowSelected &&
93
+ 'bg-well/60 [--row-bg:color-mix(in_srgb,var(--compote-well)_60%,var(--compote-surface-1))]'
94
+ )}
95
+ onclick={(event) => onRowClick?.({ row: row.original, event })}
96
+ ondblclick={(event) => onRowDoubleClick?.({ row: row.original, event })}
97
+ >
98
+ {#if view.isRowSelectionEnabled}
99
+ <td
100
+ class="border-b border-surface-2 bg-(--row-bg) px-3 py-2 text-center align-middle group-last/row:border-b-0"
101
+ style="position: sticky; left: 0; z-index: 1"
102
+ onclick={(event) => event.stopPropagation()}
103
+ ondblclick={(event) => event.stopPropagation()}
104
+ >
105
+ <input
106
+ type="checkbox"
107
+ aria-label="Select row"
108
+ class="table-checkbox mx-auto block size-4"
109
+ checked={rowSelected}
110
+ disabled={!row.getCanSelect()}
111
+ onchange={(e) => row.toggleSelected(e.currentTarget.checked)}
112
+ />
113
+ </td>
114
+ {/if}
115
+ {#each getRowCells(row, view) as cell (cell.id)}
116
+ {@const meta = getColumnMeta(cell.column.columnDef)}
117
+ <td
118
+ class={cn(
119
+ 'h-9 truncate border-b border-b-surface-2 px-3 py-0 align-middle group-last/row:border-b-0',
120
+ alignClass(meta?.align),
121
+ cell.column.getIsPinned() && 'bg-(--row-bg)'
122
+ )}
123
+ style={getPinningStyle(cell.column, table, view, false, view.isRowSelectionEnabled)}
124
+ >
125
+ <DataTableCellContent {cell} />
126
+ </td>
127
+ {/each}
128
+ {#if !view.hasGrowColumn}
129
+ <td aria-hidden="true" class="border-b border-surface-2 p-0 group-last/row:border-b-0"
130
+ ></td>
131
+ {/if}
132
+ </tr>
133
+ {:else}
134
+ <tr>
135
+ <td class="px-3 py-10 text-center text-sm text-ink-dim" colspan={renderedColumnCount}>
136
+ {emptyMessage}
137
+ </td>
138
+ </tr>
139
+ {/each}
140
+ </tbody>
141
+ {#if view.hasFooter}
142
+ <DataTableFoot {table} {view} hasGrowColumn={view.hasGrowColumn} />
143
+ {/if}
144
+ </table>
145
+ </div>
146
+
147
+ <div class="shrink-0 border-t border-surface-3 bg-surface-2 px-3 py-2 text-sm text-ink-dim">
148
+ {#if view.isRowSelectionEnabled}
149
+ {view.selectedRowCount} of {view.rowModel.rows.length} rows selected
150
+ {:else}
151
+ {view.rowModel.rows.length} rows
152
+ {/if}
153
+ </div>
154
+ </div>
@@ -8,6 +8,8 @@ declare function $$render<T extends RowData>(): {
8
8
  caption?: string;
9
9
  emptyMessage?: string;
10
10
  class?: ClassValue;
11
+ /** Show a filter funnel in the header of every filterable column. */
12
+ headerFilters?: boolean;
11
13
  onRowClick?: (details: {
12
14
  row: T;
13
15
  event: MouseEvent;
@@ -0,0 +1,13 @@
1
+ export type DateCellType = 'date' | 'time' | 'date-time';
2
+ export declare const TYPE_DATE_FORMAT_DEFAULTS: Record<DateCellType, Intl.DateTimeFormatOptions>;
3
+ /**
4
+ * The zone a date column is shown — and therefore filtered — in: `formatOptions.timeZone`
5
+ * when the column sets one, otherwise the viewer's local zone.
6
+ */
7
+ export declare function dateCellTimeZone(formatOptions: Intl.DateTimeFormatOptions | undefined): string;
8
+ /**
9
+ * A date cell value formatted for display, or undefined when it can't be read. Reads the
10
+ * value with `readDateInZone`, exactly as the date range filter does, so a cell always shows
11
+ * the day it is filtered by.
12
+ */
13
+ export declare function formatDateCell(value: unknown, type: DateCellType, locale: string, formatOptions?: Intl.DateTimeFormatOptions): string | undefined;
@@ -0,0 +1,38 @@
1
+ import { CalendarDate, getLocalTimeZone } from '@internationalized/date';
2
+ import { readDateInZone } from '../../utils/date';
3
+ export const TYPE_DATE_FORMAT_DEFAULTS = {
4
+ date: { day: '2-digit', month: '2-digit', year: 'numeric' },
5
+ time: { hour: '2-digit', minute: '2-digit' },
6
+ 'date-time': {
7
+ day: '2-digit',
8
+ month: '2-digit',
9
+ year: 'numeric',
10
+ hour: '2-digit',
11
+ minute: '2-digit'
12
+ }
13
+ };
14
+ /**
15
+ * The zone a date column is shown — and therefore filtered — in: `formatOptions.timeZone`
16
+ * when the column sets one, otherwise the viewer's local zone.
17
+ */
18
+ export function dateCellTimeZone(formatOptions) {
19
+ return formatOptions?.timeZone ?? getLocalTimeZone();
20
+ }
21
+ /**
22
+ * A date cell value formatted for display, or undefined when it can't be read. Reads the
23
+ * value with `readDateInZone`, exactly as the date range filter does, so a cell always shows
24
+ * the day it is filtered by.
25
+ */
26
+ export function formatDateCell(value, type, locale, formatOptions) {
27
+ const timeZone = dateCellTimeZone(formatOptions);
28
+ const date = readDateInZone(value, timeZone);
29
+ if (!date)
30
+ return undefined;
31
+ const options = { ...TYPE_DATE_FORMAT_DEFAULTS[type], ...formatOptions };
32
+ // A calendar date has no instant: format its own year-month-day as UTC midnight in UTC,
33
+ // so no display zone can move it to another day.
34
+ if (date instanceof CalendarDate) {
35
+ return new Intl.DateTimeFormat(locale, { ...options, timeZone: 'UTC' }).format(date.toDate('UTC'));
36
+ }
37
+ return new Intl.DateTimeFormat(locale, { ...options, timeZone }).format(date.toDate());
38
+ }
@@ -0,0 +1,45 @@
1
+ <script lang="ts">
2
+ import { cn } from 'tailwind-variants';
3
+ import type { FilterableColumn } from '../column-filter-editing.svelte';
4
+
5
+ type Props = {
6
+ column: FilterableColumn;
7
+ };
8
+
9
+ let { column }: Props = $props();
10
+
11
+ const value = $derived(column.getFilterValue() as boolean | undefined);
12
+ </script>
13
+
14
+ <div class="flex overflow-hidden rounded border border-border text-xs">
15
+ <button
16
+ type="button"
17
+ onclick={() => column.setFilterValue(undefined)}
18
+ class={cn(
19
+ 'flex-1 px-2 py-1',
20
+ value === undefined ? 'bg-surface-3 font-medium text-ink' : 'text-ink-dim hover:bg-surface-2'
21
+ )}
22
+ >
23
+ All
24
+ </button>
25
+ <button
26
+ type="button"
27
+ onclick={() => column.setFilterValue(value === true ? undefined : true)}
28
+ class={cn(
29
+ 'flex-1 border-x border-border px-2 py-1',
30
+ value === true ? 'bg-surface-3 font-medium text-ink' : 'text-ink-dim hover:bg-surface-2'
31
+ )}
32
+ >
33
+ Yes
34
+ </button>
35
+ <button
36
+ type="button"
37
+ onclick={() => column.setFilterValue(value === false ? undefined : false)}
38
+ class={cn(
39
+ 'flex-1 px-2 py-1',
40
+ value === false ? 'bg-surface-3 font-medium text-ink' : 'text-ink-dim hover:bg-surface-2'
41
+ )}
42
+ >
43
+ No
44
+ </button>
45
+ </div>
@@ -0,0 +1,7 @@
1
+ import type { FilterableColumn } from '../column-filter-editing.svelte';
2
+ type Props = {
3
+ column: FilterableColumn;
4
+ };
5
+ declare const BooleanFilterEditor: import("svelte").Component<Props, {}, "">;
6
+ type BooleanFilterEditor = ReturnType<typeof BooleanFilterEditor>;
7
+ export default BooleanFilterEditor;
@@ -0,0 +1,33 @@
1
+ <script lang="ts" generics="T extends RowData">
2
+ import type { Column, RowData } from '@tanstack/svelte-table';
3
+ import type { DataTableFeatures } from '../features';
4
+ import { getColumnMeta } from '../data-table-utils';
5
+ import { filterKindFor } from '../column-filter-kind';
6
+ import type { ColumnFilterEditing } from '../column-filter-editing.svelte';
7
+ import TextFilterEditor from './text-filter-editor.svelte';
8
+ import RangeFilterEditor from './range-filter-editor.svelte';
9
+ import BooleanFilterEditor from './boolean-filter-editor.svelte';
10
+ import SelectFilterEditor from './select-filter-editor.svelte';
11
+ import DateRangeFilterEditor from './date-range-filter-editor.svelte';
12
+
13
+ type Props = {
14
+ column: Column<DataTableFeatures, T, unknown>;
15
+ editing: ColumnFilterEditing;
16
+ };
17
+
18
+ let { column, editing }: Props = $props();
19
+
20
+ const kind = $derived(filterKindFor(getColumnMeta(column.columnDef)?.type));
21
+ </script>
22
+
23
+ {#if kind === 'range'}
24
+ <RangeFilterEditor {column} {editing} />
25
+ {:else if kind === 'boolean'}
26
+ <BooleanFilterEditor {column} />
27
+ {:else if kind === 'select'}
28
+ <SelectFilterEditor {column} />
29
+ {:else if kind === 'date'}
30
+ <DateRangeFilterEditor {column} />
31
+ {:else if kind === 'text'}
32
+ <TextFilterEditor {column} {editing} />
33
+ {/if}
@@ -0,0 +1,30 @@
1
+ import type { Column, RowData } from '@tanstack/svelte-table';
2
+ import type { DataTableFeatures } from '../features';
3
+ import type { ColumnFilterEditing } from '../column-filter-editing.svelte';
4
+ declare function $$render<T extends RowData>(): {
5
+ props: {
6
+ column: Column<DataTableFeatures, T, unknown>;
7
+ editing: ColumnFilterEditing;
8
+ };
9
+ exports: {};
10
+ bindings: "";
11
+ slots: {};
12
+ events: {};
13
+ };
14
+ declare class __sveltets_Render<T extends RowData> {
15
+ props(): ReturnType<typeof $$render<T>>['props'];
16
+ events(): ReturnType<typeof $$render<T>>['events'];
17
+ slots(): ReturnType<typeof $$render<T>>['slots'];
18
+ bindings(): "";
19
+ exports(): {};
20
+ }
21
+ interface $$IsomorphicComponent {
22
+ new <T extends RowData>(options: import('svelte').ComponentConstructorOptions<ReturnType<__sveltets_Render<T>['props']>>): import('svelte').SvelteComponent<ReturnType<__sveltets_Render<T>['props']>, ReturnType<__sveltets_Render<T>['events']>, ReturnType<__sveltets_Render<T>['slots']>> & {
23
+ $$bindings?: ReturnType<__sveltets_Render<T>['bindings']>;
24
+ } & ReturnType<__sveltets_Render<T>['exports']>;
25
+ <T extends RowData>(internal: unknown, props: ReturnType<__sveltets_Render<T>['props']> & {}): ReturnType<__sveltets_Render<T>['exports']>;
26
+ z_$$bindings?: ReturnType<__sveltets_Render<any>['bindings']>;
27
+ }
28
+ declare const ColumnFilterEditor: $$IsomorphicComponent;
29
+ type ColumnFilterEditor<T extends RowData> = InstanceType<typeof ColumnFilterEditor<T>>;
30
+ export default ColumnFilterEditor;
@@ -0,0 +1,52 @@
1
+ <script lang="ts" generics="T extends RowData">
2
+ import type { Column, RowData } from '@tanstack/svelte-table';
3
+ import DatePicker from '../../date-picker/date-picker.svelte';
4
+ import type { DataTableFeatures } from '../features';
5
+ import { getColumnMeta } from '../data-table-utils';
6
+ import { dateCellTimeZone } from '../date-cell';
7
+ import type { DateRangeFilterValue } from '../../../utils/date-filter';
8
+
9
+ type Props = {
10
+ column: Column<DataTableFeatures, T, unknown>;
11
+ };
12
+
13
+ let { column }: Props = $props();
14
+
15
+ const meta = $derived(getColumnMeta(column.columnDef));
16
+ // Pick days in the zone the column is shown and filtered in, so "today" matches the cells.
17
+ const timeZone = $derived(
18
+ dateCellTimeZone(meta?.formatOptions as Intl.DateTimeFormatOptions | undefined)
19
+ );
20
+
21
+ function bound(index: 0 | 1): string | null {
22
+ const value = column.getFilterValue();
23
+ const bound = Array.isArray(value) ? value[index] : undefined;
24
+ return typeof bound === 'string' ? bound : null;
25
+ }
26
+
27
+ // Each picked bound applies at once. The picker emits `YYYY-MM-DD` for a day it accepted
28
+ // and null when cleared; a half-typed date never reaches here.
29
+ function setBound(index: 0 | 1, next: unknown) {
30
+ const value = column.getFilterValue();
31
+ const range: DateRangeFilterValue = Array.isArray(value)
32
+ ? [value[0] as string | undefined, value[1] as string | undefined]
33
+ : [undefined, undefined];
34
+ range[index] = typeof next === 'string' && next ? next : undefined;
35
+ column.setFilterValue(range[0] || range[1] ? range : undefined);
36
+ }
37
+ </script>
38
+
39
+ <div class="flex flex-col gap-2">
40
+ <DatePicker
41
+ label="From"
42
+ locale={meta?.formatLocale}
43
+ {timeZone}
44
+ bind:value={() => bound(0), (next) => setBound(0, next)}
45
+ />
46
+ <DatePicker
47
+ label="To"
48
+ locale={meta?.formatLocale}
49
+ {timeZone}
50
+ bind:value={() => bound(1), (next) => setBound(1, next)}
51
+ />
52
+ </div>
@@ -0,0 +1,28 @@
1
+ import type { Column, RowData } from '@tanstack/svelte-table';
2
+ import type { DataTableFeatures } from '../features';
3
+ declare function $$render<T extends RowData>(): {
4
+ props: {
5
+ column: Column<DataTableFeatures, T, unknown>;
6
+ };
7
+ exports: {};
8
+ bindings: "";
9
+ slots: {};
10
+ events: {};
11
+ };
12
+ declare class __sveltets_Render<T extends RowData> {
13
+ props(): ReturnType<typeof $$render<T>>['props'];
14
+ events(): ReturnType<typeof $$render<T>>['events'];
15
+ slots(): ReturnType<typeof $$render<T>>['slots'];
16
+ bindings(): "";
17
+ exports(): {};
18
+ }
19
+ interface $$IsomorphicComponent {
20
+ new <T extends RowData>(options: import('svelte').ComponentConstructorOptions<ReturnType<__sveltets_Render<T>['props']>>): import('svelte').SvelteComponent<ReturnType<__sveltets_Render<T>['props']>, ReturnType<__sveltets_Render<T>['events']>, ReturnType<__sveltets_Render<T>['slots']>> & {
21
+ $$bindings?: ReturnType<__sveltets_Render<T>['bindings']>;
22
+ } & ReturnType<__sveltets_Render<T>['exports']>;
23
+ <T extends RowData>(internal: unknown, props: ReturnType<__sveltets_Render<T>['props']> & {}): ReturnType<__sveltets_Render<T>['exports']>;
24
+ z_$$bindings?: ReturnType<__sveltets_Render<any>['bindings']>;
25
+ }
26
+ declare const DateRangeFilterEditor: $$IsomorphicComponent;
27
+ type DateRangeFilterEditor<T extends RowData> = InstanceType<typeof DateRangeFilterEditor<T>>;
28
+ export default DateRangeFilterEditor;
@@ -0,0 +1,51 @@
1
+ <script lang="ts" generics="T extends RowData">
2
+ import type { Column, RowData } from '@tanstack/svelte-table';
3
+ import NumberInput from '../../number-input/number-input.svelte';
4
+ import type { DataTableFeatures } from '../features';
5
+ import { getColumnMeta } from '../data-table-utils';
6
+ import type { ColumnFilterEditing } from '../column-filter-editing.svelte';
7
+
8
+ type Props = {
9
+ column: Column<DataTableFeatures, T, unknown>;
10
+ editing: ColumnFilterEditing;
11
+ };
12
+
13
+ let { column, editing }: Props = $props();
14
+
15
+ const facetBounds = $derived.by((): [number | undefined, number | undefined] => {
16
+ const vals = column.getFacetedMinMaxValues();
17
+ return vals ? [vals[0] as number, vals[1] as number] : [undefined, undefined];
18
+ });
19
+ const formatOptions = $derived(
20
+ getColumnMeta(column.columnDef)?.formatOptions as Intl.NumberFormatOptions | undefined
21
+ );
22
+ </script>
23
+
24
+ <div class="flex flex-col gap-1.5">
25
+ <div class="min-w-0 flex-1">
26
+ <NumberInput
27
+ layout="horizontal"
28
+ label="From"
29
+ bind:value={
30
+ () => editing.rangeBound(column, 'min'),
31
+ (bound) => editing.editRange(column, 'min', bound ?? null)
32
+ }
33
+ min={facetBounds[0]}
34
+ max={facetBounds[1]}
35
+ {formatOptions}
36
+ />
37
+ </div>
38
+ <div class="min-w-0 flex-1">
39
+ <NumberInput
40
+ layout="horizontal"
41
+ label="To"
42
+ bind:value={
43
+ () => editing.rangeBound(column, 'max'),
44
+ (bound) => editing.editRange(column, 'max', bound ?? null)
45
+ }
46
+ min={facetBounds[0]}
47
+ max={facetBounds[1]}
48
+ {formatOptions}
49
+ />
50
+ </div>
51
+ </div>