compote-ui 0.80.0 → 0.81.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 (32) hide show
  1. package/dist/components/data-table-v9/column-filter-kind.d.ts +10 -1
  2. package/dist/components/data-table-v9/column-filter-kind.js +7 -0
  3. package/dist/components/data-table-v9/data-table-head.svelte +230 -230
  4. package/dist/components/data-table-v9/data-table.svelte +154 -154
  5. package/dist/components/data-table-v9/filter-editors/boolean-filter-editor.svelte +45 -45
  6. package/dist/components/data-table-v9/filter-editors/column-filter-editor.svelte +39 -33
  7. package/dist/components/data-table-v9/filter-editors/column-filter-editor.svelte.d.ts +7 -2
  8. package/dist/components/data-table-v9/filter-editors/date-range-filter-editor.svelte +52 -52
  9. package/dist/components/data-table-v9/filter-editors/range-filter-editor.svelte +51 -51
  10. package/dist/components/data-table-v9/filter-editors/select-filter-editor.svelte +59 -59
  11. package/dist/components/data-table-v9/filter-editors/text-filter-editor.svelte +20 -20
  12. package/dist/components/data-table-v9/header/column-header-filter.svelte +66 -66
  13. package/dist/components/data-table-v9/index.d.ts +9 -1
  14. package/dist/components/data-table-v9/index.js +7 -0
  15. package/dist/components/data-table-v9/toolbar/data-table-column-filter.svelte +203 -203
  16. package/dist/components/data-table-v9/types.d.ts +3 -1
  17. package/dist/components/data-table-v9/virtual/data-table-virtualized.svelte +82 -82
  18. package/dist/components/data-table-v9/virtual/index.d.ts +2 -2
  19. package/dist/components/data-table-v9/virtual/index.js +1 -1
  20. package/dist/components/dialog/alert-dialog.svelte +73 -73
  21. package/dist/components/dialog/dialog-root.svelte +45 -45
  22. package/dist/components/drawer/drawer-content.svelte +213 -213
  23. package/dist/components/popover/popover-content.svelte +97 -97
  24. package/dist/components/popover/popover-description.svelte +19 -19
  25. package/dist/components/popover/popover-root.svelte +27 -27
  26. package/dist/components/popover/popover-title.svelte +19 -19
  27. package/dist/icons/PhFunnelFill.svelte +19 -19
  28. package/package.json +1 -1
  29. package/skills/component-usage/references/overlays-and-floating-ui.md +278 -278
  30. package/skills/data-table/SKILL.md +362 -314
  31. package/skills/data-table/references/column-options.md +175 -175
  32. package/skills/data-table/references/table-state-and-reactivity.md +65 -65
@@ -1,51 +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>
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>
@@ -1,59 +1,59 @@
1
- <script lang="ts" generics="T extends RowData">
2
- import type { Column, RowData } from '@tanstack/svelte-table';
3
- import * as Field from '../../field';
4
- import * as ScrollArea from '../../scroll-area';
5
- import Checkbox from '../../checkbox/checkbox.svelte';
6
- import type { DataTableFeatures } from '../features';
7
- import { selectFilterOptions, type SelectFilterValue } from '../../../utils/select-filter';
8
-
9
- type Props = {
10
- column: Column<DataTableFeatures, T, unknown>;
11
- };
12
-
13
- let { column }: Props = $props();
14
-
15
- let search = $state('');
16
-
17
- const allOptions = $derived(selectFilterOptions(column.getFacetedUniqueValues()));
18
- const options = $derived(
19
- search
20
- ? allOptions.filter((o) => o.label.toLowerCase().includes(search.toLowerCase()))
21
- : allOptions
22
- );
23
- const selected = $derived((column.getFilterValue() as SelectFilterValue[] | undefined) ?? []);
24
-
25
- function toggle(value: SelectFilterValue, checked: boolean) {
26
- const next = checked ? [...selected, value] : selected.filter((v) => v !== value);
27
- column.setFilterValue(next.length ? next : undefined);
28
- }
29
- </script>
30
-
31
- <div class="flex flex-col gap-1">
32
- <Field.Root hideMessageLine>
33
- <Field.Input placeholder="Search..." bind:value={search} />
34
- </Field.Root>
35
- <ScrollArea.Root>
36
- <ScrollArea.Viewport class="max-h-40">
37
- <ScrollArea.Content>
38
- <div class="flex flex-col gap-0.5">
39
- {#each options as option (option.value)}
40
- <div class="flex min-h-7 items-center gap-2 rounded-sm pr-2 hover:bg-surface-2">
41
- <Checkbox
42
- size="sm"
43
- label={option.label}
44
- class="min-h-7 flex-1 px-2"
45
- checked={selected.includes(option.value)}
46
- onCheckedChange={({ checked }) => toggle(option.value, checked === true)}
47
- />
48
- <span class="text-xs text-ink-dim tabular-nums">{option.count}</span>
49
- </div>
50
- {/each}
51
- </div>
52
- </ScrollArea.Content>
53
- </ScrollArea.Viewport>
54
- <ScrollArea.Scrollbar orientation="vertical">
55
- <ScrollArea.Thumb />
56
- </ScrollArea.Scrollbar>
57
- <ScrollArea.Corner />
58
- </ScrollArea.Root>
59
- </div>
1
+ <script lang="ts" generics="T extends RowData">
2
+ import type { Column, RowData } from '@tanstack/svelte-table';
3
+ import * as Field from '../../field';
4
+ import * as ScrollArea from '../../scroll-area';
5
+ import Checkbox from '../../checkbox/checkbox.svelte';
6
+ import type { DataTableFeatures } from '../features';
7
+ import { selectFilterOptions, type SelectFilterValue } from '../../../utils/select-filter';
8
+
9
+ type Props = {
10
+ column: Column<DataTableFeatures, T, unknown>;
11
+ };
12
+
13
+ let { column }: Props = $props();
14
+
15
+ let search = $state('');
16
+
17
+ const allOptions = $derived(selectFilterOptions(column.getFacetedUniqueValues()));
18
+ const options = $derived(
19
+ search
20
+ ? allOptions.filter((o) => o.label.toLowerCase().includes(search.toLowerCase()))
21
+ : allOptions
22
+ );
23
+ const selected = $derived((column.getFilterValue() as SelectFilterValue[] | undefined) ?? []);
24
+
25
+ function toggle(value: SelectFilterValue, checked: boolean) {
26
+ const next = checked ? [...selected, value] : selected.filter((v) => v !== value);
27
+ column.setFilterValue(next.length ? next : undefined);
28
+ }
29
+ </script>
30
+
31
+ <div class="flex flex-col gap-1">
32
+ <Field.Root hideMessageLine>
33
+ <Field.Input placeholder="Search..." bind:value={search} />
34
+ </Field.Root>
35
+ <ScrollArea.Root>
36
+ <ScrollArea.Viewport class="max-h-40">
37
+ <ScrollArea.Content>
38
+ <div class="flex flex-col gap-0.5">
39
+ {#each options as option (option.value)}
40
+ <div class="flex min-h-7 items-center gap-2 rounded-sm pr-2 hover:bg-surface-2">
41
+ <Checkbox
42
+ size="sm"
43
+ label={option.label}
44
+ class="min-h-7 flex-1 px-2"
45
+ checked={selected.includes(option.value)}
46
+ onCheckedChange={({ checked }) => toggle(option.value, checked === true)}
47
+ />
48
+ <span class="text-xs text-ink-dim tabular-nums">{option.count}</span>
49
+ </div>
50
+ {/each}
51
+ </div>
52
+ </ScrollArea.Content>
53
+ </ScrollArea.Viewport>
54
+ <ScrollArea.Scrollbar orientation="vertical">
55
+ <ScrollArea.Thumb />
56
+ </ScrollArea.Scrollbar>
57
+ <ScrollArea.Corner />
58
+ </ScrollArea.Root>
59
+ </div>
@@ -1,20 +1,20 @@
1
- <script lang="ts">
2
- import * as Field from '../../field';
3
- import type { ColumnFilterEditing, FilterableColumn } from '../column-filter-editing.svelte';
4
-
5
- type Props = {
6
- column: FilterableColumn;
7
- editing: ColumnFilterEditing;
8
- };
9
-
10
- let { column, editing }: Props = $props();
11
- </script>
12
-
13
- <Field.Root hideMessageLine>
14
- <Field.Input
15
- placeholder="Search..."
16
- bind:value={
17
- () => editing.textValue(column), (text) => editing.editText(column, String(text ?? ''))
18
- }
19
- />
20
- </Field.Root>
1
+ <script lang="ts">
2
+ import * as Field from '../../field';
3
+ import type { ColumnFilterEditing, FilterableColumn } from '../column-filter-editing.svelte';
4
+
5
+ type Props = {
6
+ column: FilterableColumn;
7
+ editing: ColumnFilterEditing;
8
+ };
9
+
10
+ let { column, editing }: Props = $props();
11
+ </script>
12
+
13
+ <Field.Root hideMessageLine>
14
+ <Field.Input
15
+ placeholder="Search..."
16
+ bind:value={
17
+ () => editing.textValue(column), (text) => editing.editText(column, String(text ?? ''))
18
+ }
19
+ />
20
+ </Field.Root>
@@ -1,66 +1,66 @@
1
- <script lang="ts" generics="T extends RowData">
2
- import type { Column, RowData } from '@tanstack/svelte-table';
3
- import * as Popover from '../../popover';
4
- import { PhFunnel, PhFunnelFill } from '../../../icons';
5
- import type { DataTableFeatures } from '../features';
6
- import { cn } from 'tailwind-variants';
7
- import { getColumnLabel, getColumnMeta, type DataTableInstance } from '../data-table-utils';
8
- import { ColumnFilterEditing } from '../column-filter-editing.svelte';
9
- import ColumnFilterEditor from '../filter-editors/column-filter-editor.svelte';
10
-
11
- type Props = {
12
- table: DataTableInstance<T>;
13
- column: Column<DataTableFeatures, T, unknown>;
14
- };
15
-
16
- let { table, column }: Props = $props();
17
-
18
- // Pending edits belong to the table: shared with the other filter hosts, and they still
19
- // commit after this popover closes.
20
- const editing = new ColumnFilterEditing({ table: () => table });
21
-
22
- const isFiltered = $derived(
23
- table.atoms.columnFilters.get().some((filter) => filter.id === column.id)
24
- );
25
- const label = $derived(getColumnLabel(column));
26
- const align = $derived(getColumnMeta(column.columnDef)?.align);
27
-
28
- function clear() {
29
- editing.cancel(column.id);
30
- column.setFilterValue(undefined);
31
- }
32
- </script>
33
-
34
- <Popover.Root positioning={{ placement: 'bottom-start' }}>
35
- <Popover.Trigger
36
- aria-label={`Filter ${label}`}
37
- data-active={isFiltered ? '' : undefined}
38
- class={cn(
39
- 'inline-flex size-5 shrink-0 items-center justify-center rounded-sm bg-surface-2 text-ink-dim opacity-0 outline-none group-hover/th:opacity-100 hover:bg-surface-3 hover:text-ink focus-visible:opacity-100 focus-visible:ring-2 focus-visible:ring-ring data-[state=open]:opacity-100 pointer-coarse:opacity-100',
40
- // Floats over the cell's inner edge, opposite the label, so while inactive it never
41
- // takes width from a narrow column; its background hides label text running under it.
42
- // Once a filter is set the header cell pads that edge (see data-table-head).
43
- 'absolute top-1/2 -translate-y-1/2',
44
- align === 'right' ? 'left-1' : 'right-1',
45
- 'data-active:text-primary data-active:opacity-100'
46
- )}
47
- >
48
- {#if isFiltered}
49
- <PhFunnelFill class="size-3.5" />
50
- {:else}
51
- <PhFunnel class="size-3.5" />
52
- {/if}
53
- </Popover.Trigger>
54
-
55
- <Popover.Content class="flex w-64 flex-col gap-3 p-3" showArrow={false}>
56
- <div class="flex min-h-5 items-center justify-between gap-2">
57
- <Popover.Title class="truncate text-sm font-medium text-ink">{label}</Popover.Title>
58
- {#if isFiltered}
59
- <button type="button" onclick={clear} class="text-xs text-primary hover:underline">
60
- Clear
61
- </button>
62
- {/if}
63
- </div>
64
- <ColumnFilterEditor {column} {editing} />
65
- </Popover.Content>
66
- </Popover.Root>
1
+ <script lang="ts" generics="T extends RowData">
2
+ import type { Column, RowData } from '@tanstack/svelte-table';
3
+ import * as Popover from '../../popover';
4
+ import { PhFunnel, PhFunnelFill } from '../../../icons';
5
+ import type { DataTableFeatures } from '../features';
6
+ import { cn } from 'tailwind-variants';
7
+ import { getColumnLabel, getColumnMeta, type DataTableInstance } from '../data-table-utils';
8
+ import { ColumnFilterEditing } from '../column-filter-editing.svelte';
9
+ import ColumnFilterEditor from '../filter-editors/column-filter-editor.svelte';
10
+
11
+ type Props = {
12
+ table: DataTableInstance<T>;
13
+ column: Column<DataTableFeatures, T, unknown>;
14
+ };
15
+
16
+ let { table, column }: Props = $props();
17
+
18
+ // Pending edits belong to the table: shared with the other filter hosts, and they still
19
+ // commit after this popover closes.
20
+ const editing = new ColumnFilterEditing({ table: () => table });
21
+
22
+ const isFiltered = $derived(
23
+ table.atoms.columnFilters.get().some((filter) => filter.id === column.id)
24
+ );
25
+ const label = $derived(getColumnLabel(column));
26
+ const align = $derived(getColumnMeta(column.columnDef)?.align);
27
+
28
+ function clear() {
29
+ editing.cancel(column.id);
30
+ column.setFilterValue(undefined);
31
+ }
32
+ </script>
33
+
34
+ <Popover.Root positioning={{ placement: 'bottom-start' }}>
35
+ <Popover.Trigger
36
+ aria-label={`Filter ${label}`}
37
+ data-active={isFiltered ? '' : undefined}
38
+ class={cn(
39
+ 'inline-flex size-5 shrink-0 items-center justify-center rounded-sm bg-surface-2 text-ink-dim opacity-0 outline-none group-hover/th:opacity-100 hover:bg-surface-3 hover:text-ink focus-visible:opacity-100 focus-visible:ring-2 focus-visible:ring-ring data-[state=open]:opacity-100 pointer-coarse:opacity-100',
40
+ // Floats over the cell's inner edge, opposite the label, so while inactive it never
41
+ // takes width from a narrow column; its background hides label text running under it.
42
+ // Once a filter is set the header cell pads that edge (see data-table-head).
43
+ 'absolute top-1/2 -translate-y-1/2',
44
+ align === 'right' ? 'left-1' : 'right-1',
45
+ 'data-active:text-primary data-active:opacity-100'
46
+ )}
47
+ >
48
+ {#if isFiltered}
49
+ <PhFunnelFill class="size-3.5" />
50
+ {:else}
51
+ <PhFunnel class="size-3.5" />
52
+ {/if}
53
+ </Popover.Trigger>
54
+
55
+ <Popover.Content class="flex w-64 flex-col gap-3 p-3" showArrow={false}>
56
+ <div class="flex min-h-5 items-center justify-between gap-2">
57
+ <Popover.Title class="truncate text-sm font-medium text-ink">{label}</Popover.Title>
58
+ {#if isFiltered}
59
+ <button type="button" onclick={clear} class="text-xs text-primary hover:underline">
60
+ Clear
61
+ </button>
62
+ {/if}
63
+ </div>
64
+ <ColumnFilterEditor {column} {editing} />
65
+ </Popover.Content>
66
+ </Popover.Root>
@@ -8,5 +8,13 @@ export { default as ColumnFilter } from './toolbar/data-table-column-filter.svel
8
8
  export { default as ColumnVisibility } from './toolbar/data-table-column-visibility.svelte';
9
9
  export { default as Search } from './toolbar/data-table-search.svelte';
10
10
  export { default as Refresh } from './toolbar/data-table-refresh.svelte';
11
+ export { default as ColumnFilterEditor } from './filter-editors/column-filter-editor.svelte';
12
+ export { ColumnFilterEditing } from './column-filter-editing.svelte';
13
+ export { filterKindFor, getColumnFilterKind } from './column-filter-kind';
14
+ export { getColumnLabel } from './data-table-utils';
15
+ export { EMPTY_SELECT_VALUE, selectFilterOptions } from '../../utils/select-filter';
11
16
  export type { CreateDataTableOptions, DataTableInstance } from './create-table.svelte';
12
- export type { DataTableAlign, DataTableAccessorFnColumn, DataTableAccessorKeyColumn, DataTableColumn, DataTableColumnBase, DataTableColumnOptions, DataTableColumnType, DataTableCellPropsResolver, DataTableCellRenderProps, DataTableGroupColumn, DataTableLeafColumnBase, DataTableLeafColumn } from './types';
17
+ export type { ColumnFilterEditingOptions, FilterableColumn } from './column-filter-editing.svelte';
18
+ export type { ColumnFilterKind } from './column-filter-kind';
19
+ export type { SelectFilterOption, SelectFilterValue } from '../../utils/select-filter';
20
+ export type { DataTableAlign, DataTableAccessorFnColumn, DataTableAccessorKeyColumn, DataTableColumn, DataTableColumnBase, DataTableColumnInstance, DataTableColumnOptions, DataTableColumnType, DataTableCellPropsResolver, DataTableCellRenderProps, DataTableGroupColumn, DataTableLeafColumnBase, DataTableLeafColumn } from './types';
@@ -8,3 +8,10 @@ export { default as ColumnFilter } from './toolbar/data-table-column-filter.svel
8
8
  export { default as ColumnVisibility } from './toolbar/data-table-column-visibility.svelte';
9
9
  export { default as Search } from './toolbar/data-table-search.svelte';
10
10
  export { default as Refresh } from './toolbar/data-table-refresh.svelte';
11
+ // Building blocks for filter UI outside the toolbar and header popups (e.g. a facet panel):
12
+ // the same editors, pending-edit handling and select options the Funnel uses.
13
+ export { default as ColumnFilterEditor } from './filter-editors/column-filter-editor.svelte';
14
+ export { ColumnFilterEditing } from './column-filter-editing.svelte';
15
+ export { filterKindFor, getColumnFilterKind } from './column-filter-kind';
16
+ export { getColumnLabel } from './data-table-utils';
17
+ export { EMPTY_SELECT_VALUE, selectFilterOptions } from '../../utils/select-filter';