compote-ui 0.56.1 → 0.58.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 (46) hide show
  1. package/dist/components/data-table-v9/create-table.svelte.js +15 -14
  2. package/dist/components/toast/index.d.ts +3 -0
  3. package/dist/components/toast/index.js +2 -0
  4. package/dist/components/toast/toaster.svelte +49 -0
  5. package/dist/components/toast/toaster.svelte.d.ts +9 -0
  6. package/dist/index.d.ts +3 -2
  7. package/dist/index.js +2 -2
  8. package/package.json +6 -19
  9. package/dist/components/data-table-v8/column-helper.d.ts +0 -12
  10. package/dist/components/data-table-v8/column-helper.js +0 -42
  11. package/dist/components/data-table-v8/create-svelte-table.svelte.d.ts +0 -8
  12. package/dist/components/data-table-v8/create-svelte-table.svelte.js +0 -85
  13. package/dist/components/data-table-v8/create-table.svelte.d.ts +0 -35
  14. package/dist/components/data-table-v8/create-table.svelte.js +0 -234
  15. package/dist/components/data-table-v8/data-table-foot.svelte +0 -96
  16. package/dist/components/data-table-v8/data-table-foot.svelte.d.ts +0 -33
  17. package/dist/components/data-table-v8/data-table-head.svelte +0 -196
  18. package/dist/components/data-table-v8/data-table-head.svelte.d.ts +0 -35
  19. package/dist/components/data-table-v8/data-table-title.svelte +0 -16
  20. package/dist/components/data-table-v8/data-table-title.svelte.d.ts +0 -10
  21. package/dist/components/data-table-v8/data-table-utils.d.ts +0 -32
  22. package/dist/components/data-table-v8/data-table-utils.js +0 -160
  23. package/dist/components/data-table-v8/data-table.svelte +0 -273
  24. package/dist/components/data-table-v8/data-table.svelte.d.ts +0 -41
  25. package/dist/components/data-table-v8/flex-render.svelte +0 -35
  26. package/dist/components/data-table-v8/flex-render.svelte.d.ts +0 -28
  27. package/dist/components/data-table-v8/index.d.ts +0 -12
  28. package/dist/components/data-table-v8/index.js +0 -10
  29. package/dist/components/data-table-v8/render-helpers.d.ts +0 -13
  30. package/dist/components/data-table-v8/render-helpers.js +0 -23
  31. package/dist/components/data-table-v8/toolbar/data-table-column-filter.svelte +0 -373
  32. package/dist/components/data-table-v8/toolbar/data-table-column-filter.svelte.d.ts +0 -29
  33. package/dist/components/data-table-v8/toolbar/data-table-column-visibility.svelte +0 -73
  34. package/dist/components/data-table-v8/toolbar/data-table-column-visibility.svelte.d.ts +0 -29
  35. package/dist/components/data-table-v8/toolbar/data-table-search.svelte +0 -57
  36. package/dist/components/data-table-v8/toolbar/data-table-search.svelte.d.ts +0 -31
  37. package/dist/components/data-table-v8/toolbar/data-table-toolbar.svelte +0 -39
  38. package/dist/components/data-table-v8/toolbar/data-table-toolbar.svelte.d.ts +0 -12
  39. package/dist/components/data-table-v8/types.d.ts +0 -73
  40. package/dist/components/data-table-v8/types.js +0 -1
  41. package/dist/components/data-table-v8/virtual/data-table-virtual-rows.svelte +0 -176
  42. package/dist/components/data-table-v8/virtual/data-table-virtual-rows.svelte.d.ts +0 -40
  43. package/dist/components/data-table-v8/virtual/data-table-virtualized.svelte +0 -146
  44. package/dist/components/data-table-v8/virtual/data-table-virtualized.svelte.d.ts +0 -41
  45. package/dist/components/data-table-v8/virtual/index.d.ts +0 -3
  46. package/dist/components/data-table-v8/virtual/index.js +0 -2
@@ -1,6 +1,6 @@
1
1
  import { createTable as createSvelteTable, createSortedRowModel, createFilteredRowModel, createFacetedRowModel, createFacetedUniqueValues, createFacetedMinMaxValues, sortFns, filterFns } from '@tanstack/svelte-table';
2
2
  import { useLocaleContext } from '@ark-ui/svelte/locale';
3
- import { untrack } from 'svelte';
3
+ import { onDestroy, untrack } from 'svelte';
4
4
  import { renderComponent, renderSnippet } from '@tanstack/svelte-table';
5
5
  import { dataTableFeatures } from './features';
6
6
  import { TYPE_NUMBER_FORMAT_DEFAULTS } from './data-table-utils';
@@ -72,22 +72,23 @@ export function createTable(options) {
72
72
  }));
73
73
  });
74
74
  });
75
- // Notify consumers of visibility changes without taking control of the slice.
76
- // v9 setters are atom-aware, so we observe the reactive state instead of
77
- // overriding `onColumnVisibilityChange` (which would suppress internal updates).
78
- // The callback runs untracked: it must only depend on the visibility atom,
79
- // otherwise consumer-side reads (e.g. a persisted-state proxy the callback
80
- // writes into) become dependencies of this effect and re-trigger it forever.
75
+ // Notify consumers of visibility changes without taking control of the slice
76
+ // (overriding the table's own `onColumnVisibilityChange` option would suppress
77
+ // internal updates). The atom's `subscribe` is an explicit observer that runs
78
+ // the callback outside Svelte's dependency tracking, so consumer-side reads
79
+ // (e.g. a persisted-state proxy the callback writes into) can never become
80
+ // dependencies that re-trigger the notification the failure mode of
81
+ // observing via $effect. The initial-replay guard compares by reference:
82
+ // table state updates are immutable, so only the subscription's initial
83
+ // emission (if the atom flavor emits one) can match the snapshot.
81
84
  if (options.onColumnVisibilityChange) {
82
- let first = true;
83
- $effect(() => {
84
- const visibility = table.atoms.columnVisibility.get();
85
- if (first) {
86
- first = false;
85
+ const initialVisibility = table.atoms.columnVisibility.get();
86
+ const subscription = table.atoms.columnVisibility.subscribe((visibility) => {
87
+ if (visibility === initialVisibility)
87
88
  return;
88
- }
89
- untrack(() => options.onColumnVisibilityChange?.(visibility));
89
+ options.onColumnVisibilityChange?.(visibility);
90
90
  });
91
+ onDestroy(() => subscription.unsubscribe());
91
92
  }
92
93
  return table;
93
94
  }
@@ -0,0 +1,3 @@
1
+ export { default as Toaster } from './toaster.svelte';
2
+ export { createToaster } from '@ark-ui/svelte/toast';
3
+ export type { CreateToasterReturn } from '@ark-ui/svelte/toast';
@@ -0,0 +1,2 @@
1
+ export { default as Toaster } from './toaster.svelte';
2
+ export { createToaster } from '@ark-ui/svelte/toast';
@@ -0,0 +1,49 @@
1
+ <script lang="ts">
2
+ import { Toast, Toaster as ArkToaster } from '@ark-ui/svelte/toast';
3
+ import { Portal } from '@ark-ui/svelte/portal';
4
+ import { cn } from 'tailwind-variants';
5
+ import type { ClassValue } from 'tailwind-variants';
6
+ import type { CreateToasterReturn } from '@ark-ui/svelte/toast';
7
+ import PhX from '../../icons/PhX.svelte';
8
+
9
+ interface Props {
10
+ toaster: CreateToasterReturn;
11
+ class?: ClassValue;
12
+ }
13
+
14
+ let { toaster, class: className }: Props = $props();
15
+ </script>
16
+
17
+ <Portal>
18
+ <ArkToaster {toaster}>
19
+ {#snippet children(toast)}
20
+ {@const t = toast()}
21
+ <Toast.Root
22
+ class={cn(
23
+ 'relative min-w-80 rounded-lg border border-border bg-surface-1 p-4 pr-8 shadow-lg transition-all duration-200',
24
+ 'data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:slide-in-from-top-2',
25
+ 'data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:slide-out-to-right-full',
26
+ 'data-[type=success]:border-green-500/50 data-[type=success]:bg-green-50 dark:data-[type=success]:bg-green-950/30',
27
+ 'data-[type=error]:border-red-500/50 data-[type=error]:bg-red-50 dark:data-[type=error]:bg-red-950/30',
28
+ 'data-[type=warning]:border-yellow-500/50 data-[type=warning]:bg-yellow-50 dark:data-[type=warning]:bg-yellow-950/30',
29
+ 'data-[type=info]:border-blue-500/50 data-[type=info]:bg-blue-50 dark:data-[type=info]:bg-blue-950/30',
30
+ className
31
+ )}
32
+ style="translate: var(--x) var(--y); scale: var(--scale); z-index: var(--z-index); height: var(--height); opacity: var(--opacity);"
33
+ >
34
+ {#if t.title}
35
+ <Toast.Title class="text-sm font-medium text-ink">{t.title}</Toast.Title>
36
+ {/if}
37
+ {#if t.description}
38
+ <Toast.Description class="mt-1 text-sm text-ink-dim">{t.description}</Toast.Description>
39
+ {/if}
40
+ <Toast.CloseTrigger
41
+ class="absolute right-1.5 top-1.5 rounded p-1 text-ink-dim hover:bg-surface-2 hover:text-ink"
42
+ aria-label="Close notification"
43
+ >
44
+ <PhX class="size-3.5" />
45
+ </Toast.CloseTrigger>
46
+ </Toast.Root>
47
+ {/snippet}
48
+ </ArkToaster>
49
+ </Portal>
@@ -0,0 +1,9 @@
1
+ import type { ClassValue } from 'tailwind-variants';
2
+ import type { CreateToasterReturn } from '@ark-ui/svelte/toast';
3
+ interface Props {
4
+ toaster: CreateToasterReturn;
5
+ class?: ClassValue;
6
+ }
7
+ declare const Toaster: import("svelte").Component<Props, {}, "">;
8
+ type Toaster = ReturnType<typeof Toaster>;
9
+ export default Toaster;
package/dist/index.d.ts CHANGED
@@ -25,8 +25,6 @@ export { default as AlertDialog } from './components/dialog/alert-dialog.svelte'
25
25
  export type { AlertDialogProps } from './components/dialog/dialog.types';
26
26
  export * as DataTable from './components/data-table-v9';
27
27
  export * as VirtualDataTable from './components/data-table-v9/virtual';
28
- export * as DataTableV8 from './components/data-table-v8';
29
- export * as VirtualDataTableV8 from './components/data-table-v8/virtual';
30
28
  export * as Drawer from './components/drawer';
31
29
  export { default as FileUploadDropzone } from './components/file-upload/file-upload-dropzone.svelte';
32
30
  export { default as FileUpload } from './components/file-upload/file-upload.svelte';
@@ -46,6 +44,9 @@ export { default as Splitter } from './components/splitter/splitter.svelte';
46
44
  export type { SplitterPanelConfig, SplitterProps } from './components/splitter/types';
47
45
  export { default as Switch } from './components/switch/switch.svelte';
48
46
  export * as Tabs from './components/tabs';
47
+ export * as Toast from './components/toast';
48
+ export { createToaster } from '@ark-ui/svelte/toast';
49
+ export type { CreateToasterReturn } from '@ark-ui/svelte/toast';
49
50
  export { default as Toggle } from './components/toggle/toggle.svelte';
50
51
  export type { ToggleProps, ToggleSize } from './components/toggle/types';
51
52
  export * as ToggleGroup from './components/toggle-group';
package/dist/index.js CHANGED
@@ -19,8 +19,6 @@ export * as Dialog from './components/dialog';
19
19
  export { default as AlertDialog } from './components/dialog/alert-dialog.svelte';
20
20
  export * as DataTable from './components/data-table-v9';
21
21
  export * as VirtualDataTable from './components/data-table-v9/virtual';
22
- export * as DataTableV8 from './components/data-table-v8';
23
- export * as VirtualDataTableV8 from './components/data-table-v8/virtual';
24
22
  export * as Drawer from './components/drawer';
25
23
  export { default as FileUploadDropzone } from './components/file-upload/file-upload-dropzone.svelte';
26
24
  export { default as FileUpload } from './components/file-upload/file-upload.svelte';
@@ -35,6 +33,8 @@ export { default as Select } from './components/select/select.svelte';
35
33
  export { default as Splitter } from './components/splitter/splitter.svelte';
36
34
  export { default as Switch } from './components/switch/switch.svelte';
37
35
  export * as Tabs from './components/tabs';
36
+ export * as Toast from './components/toast';
37
+ export { createToaster } from '@ark-ui/svelte/toast';
38
38
  export { default as Toggle } from './components/toggle/toggle.svelte';
39
39
  export * as ToggleGroup from './components/toggle-group';
40
40
  export * as Menu from './components/menu';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "compote-ui",
3
- "version": "0.56.1",
3
+ "version": "0.58.0",
4
4
  "license": "MIT",
5
5
  "scripts": {
6
6
  "dev": "vite dev --open",
@@ -40,26 +40,14 @@
40
40
  "types": "./dist/components/data-table-v9/virtual/index.d.ts",
41
41
  "svelte": "./dist/components/data-table-v9/virtual/index.js"
42
42
  },
43
- "./data-table-v8": {
44
- "types": "./dist/components/data-table-v8/index.d.ts",
45
- "svelte": "./dist/components/data-table-v8/index.js"
46
- },
47
- "./data-table-v8/virtual": {
48
- "types": "./dist/components/data-table-v8/virtual/index.d.ts",
49
- "svelte": "./dist/components/data-table-v8/virtual/index.js"
50
- },
51
43
  "./theme.css": "./dist/theme.css"
52
44
  },
53
45
  "peerDependencies": {
54
46
  "@tanstack/svelte-table": ">=9.0.0-beta.6 <10",
55
- "@tanstack/table-core": ">=8.0.0",
56
47
  "@tanstack/svelte-virtual": ">=3.0.0",
57
48
  "svelte": "^5.0.0"
58
49
  },
59
50
  "peerDependenciesMeta": {
60
- "@tanstack/table-core": {
61
- "optional": true
62
- },
63
51
  "@tanstack/svelte-virtual": {
64
52
  "optional": true
65
53
  }
@@ -70,28 +58,27 @@
70
58
  "@iconify-json/ph": "^1.2.2",
71
59
  "@sveltejs/adapter-auto": "^7.0.0",
72
60
  "@sveltejs/adapter-cloudflare": "^7.2.8",
73
- "@sveltejs/kit": "^2.63.0",
61
+ "@sveltejs/kit": "^2.65.0",
74
62
  "@sveltejs/package": "^2.5.8",
75
63
  "@sveltejs/vite-plugin-svelte": "7.1.2",
76
64
  "@tailwindcss/vite": "^4.2.4",
77
65
  "@tanstack/svelte-table": "9.0.0-beta.6",
78
66
  "@tanstack/svelte-virtual": "^3.13.28",
79
- "@tanstack/table-core": "^8.21.3",
80
67
  "@types/node": "^22.19.18",
81
68
  "eslint": "^10.4.1",
82
69
  "eslint-config-prettier": "^10.1.8",
83
70
  "eslint-plugin-svelte": "^3.19.0",
84
71
  "globals": "^17.5.0",
85
- "prettier": "^3.8.3",
72
+ "prettier": "^3.8.4",
86
73
  "prettier-plugin-svelte": "^4.1.0",
87
74
  "prettier-plugin-tailwindcss": "^0.8.0",
88
75
  "publint": "^0.3.21",
89
- "svelte": "^5.56.2",
76
+ "svelte": "^5.56.3",
90
77
  "svelte-check": "^4.6.0",
91
78
  "tailwindcss": "^4.2.4",
92
79
  "tw-animate-css": "^1.4.0",
93
80
  "typescript": "^6.0.3",
94
- "typescript-eslint": "^8.60.1",
81
+ "typescript-eslint": "^8.61.0",
95
82
  "unplugin-icons": "^23.0.1",
96
83
  "vite": "^8.0.16"
97
84
  },
@@ -99,7 +86,7 @@
99
86
  "svelte"
100
87
  ],
101
88
  "dependencies": {
102
- "@ark-ui/svelte": "^5.22.0",
89
+ "@ark-ui/svelte": "^5.22.1",
103
90
  "@fontsource-variable/nunito-sans": "^5.2.7",
104
91
  "@iconify/svelte": "^5.2.1",
105
92
  "@internationalized/date": "^3.12.2",
@@ -1,12 +0,0 @@
1
- import type { RowData } from '@tanstack/table-core';
2
- import type { DataTableAccessorFnColumn, DataTableAccessorKeyColumn, DataTableColumn, DataTableGroupColumn } from './types';
3
- type AccessorKeyColumnOptions<T extends RowData> = Omit<DataTableAccessorKeyColumn<T>, 'accessorKey' | 'accessorFn' | 'columns'>;
4
- type AccessorFnColumnOptions<T extends RowData> = Omit<DataTableAccessorFnColumn<T>, 'accessorFn' | 'accessorKey' | 'columns'>;
5
- type GroupColumnOptions<T extends RowData> = Omit<DataTableGroupColumn<T>, 'columns' | 'accessorKey' | 'accessorFn' | 'cell' | 'cellComponent' | 'cellProps' | 'cellSnippet' | 'type' | 'formatOptions' | 'formatLocale'>;
6
- export declare function createDataTableColumnHelper<T extends RowData>(): {
7
- accessor<K extends Extract<keyof T, string>>(accessorKey: K, options: AccessorKeyColumnOptions<T>): DataTableAccessorKeyColumn<T>;
8
- accessorFn(accessorFn: (row: T) => unknown, options: AccessorFnColumnOptions<T>): DataTableAccessorFnColumn<T>;
9
- group(header: string, columns: DataTableColumn<T>[], options?: Omit<GroupColumnOptions<T>, "header">): DataTableGroupColumn<T>;
10
- columns(columns: DataTableColumn<T>[]): DataTableColumn<T>[];
11
- };
12
- export {};
@@ -1,42 +0,0 @@
1
- const TYPE_DEFAULTS = {
2
- number: { align: 'right', size: 120 },
3
- currency: { align: 'right', size: 120 },
4
- percent: { align: 'right', size: 100 },
5
- date: { align: 'center', size: 110 },
6
- time: { align: 'center', size: 80 },
7
- 'date-time': { align: 'center', size: 160 },
8
- boolean: { align: 'center', size: 90 },
9
- url: { align: 'center', size: 60, enableSorting: false }
10
- };
11
- function applyTypeDefaults(options) {
12
- const defaults = options.type ? TYPE_DEFAULTS[options.type] : undefined;
13
- if (!defaults)
14
- return options;
15
- return { ...defaults, ...options };
16
- }
17
- export function createDataTableColumnHelper() {
18
- return {
19
- accessor(accessorKey, options) {
20
- return {
21
- ...applyTypeDefaults(options),
22
- accessorKey
23
- };
24
- },
25
- accessorFn(accessorFn, options) {
26
- return {
27
- ...applyTypeDefaults(options),
28
- accessorFn
29
- };
30
- },
31
- group(header, columns, options = {}) {
32
- return {
33
- ...options,
34
- header,
35
- columns
36
- };
37
- },
38
- columns(columns) {
39
- return columns;
40
- }
41
- };
42
- }
@@ -1,8 +0,0 @@
1
- import { type RowData, type TableOptions } from '@tanstack/table-core';
2
- export declare function createSvelteTable<TData extends RowData>(options: TableOptions<TData>): import("@tanstack/table-core").Table<TData>;
3
- type MaybeThunk<T extends object> = T | (() => T | null | undefined);
4
- type Intersection<T extends readonly unknown[]> = (T extends [infer H, ...infer R] ? H & Intersection<R> : unknown) & {};
5
- export declare function mergeObjects<Sources extends readonly MaybeThunk<any>[]>(...sources: Sources): Intersection<{
6
- [K in keyof Sources]: Sources[K];
7
- }>;
8
- export {};
@@ -1,85 +0,0 @@
1
- import { createTable } from '@tanstack/table-core';
2
- export function createSvelteTable(options) {
3
- const resolvedOptions = mergeObjects({
4
- state: {},
5
- onStateChange() { },
6
- renderFallbackValue: null,
7
- mergeOptions: (defaultOptions, options) => {
8
- return mergeObjects(defaultOptions, options);
9
- }
10
- }, options);
11
- const table = createTable(resolvedOptions);
12
- let state = $state(table.initialState);
13
- function updateOptions() {
14
- table.setOptions(() => {
15
- return mergeObjects(resolvedOptions, options, {
16
- state: mergeObjects(() => state, options.state || {}),
17
- onStateChange: (updater) => {
18
- if (typeof updater === 'function')
19
- state = updater(state);
20
- else
21
- state = mergeObjects(state, updater);
22
- options.onStateChange?.(updater);
23
- }
24
- });
25
- });
26
- }
27
- updateOptions();
28
- $effect.pre(() => {
29
- updateOptions();
30
- });
31
- Object.defineProperty(table, '_svelteState', {
32
- get() {
33
- return state;
34
- },
35
- enumerable: false,
36
- configurable: true
37
- });
38
- return table;
39
- }
40
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
41
- export function mergeObjects(...sources) {
42
- const resolve = (src) => typeof src === 'function' ? (src() ?? undefined) : src;
43
- const findSourceWithKey = (key) => {
44
- for (let i = sources.length - 1; i >= 0; i--) {
45
- const obj = resolve(sources[i]);
46
- if (obj && key in obj)
47
- return obj;
48
- }
49
- return undefined;
50
- };
51
- return new Proxy(Object.create(null), {
52
- get(_, key) {
53
- const src = findSourceWithKey(key);
54
- return src?.[key];
55
- },
56
- has(_, key) {
57
- return !!findSourceWithKey(key);
58
- },
59
- ownKeys() {
60
- // eslint-disable-next-line svelte/prefer-svelte-reactivity
61
- const all = new Set();
62
- for (const s of sources) {
63
- const obj = resolve(s);
64
- if (obj) {
65
- for (const k of Reflect.ownKeys(obj)) {
66
- all.add(k);
67
- }
68
- }
69
- }
70
- return [...all];
71
- },
72
- getOwnPropertyDescriptor(_, key) {
73
- const src = findSourceWithKey(key);
74
- if (!src)
75
- return undefined;
76
- return {
77
- configurable: true,
78
- enumerable: true,
79
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
80
- value: src[key],
81
- writable: true
82
- };
83
- }
84
- });
85
- }
@@ -1,35 +0,0 @@
1
- import { type ColumnPinningState, type ColumnResizeMode, type ColumnSizingState, type VisibilityState, type FilterFn, type Row, type RowData, type Table } from '@tanstack/table-core';
2
- import type { DataTableColumn, DataTableLeafColumn } from './types';
3
- declare module '@tanstack/table-core' {
4
- interface FilterFns {
5
- oneOf: FilterFn<unknown>;
6
- }
7
- }
8
- export type DataTableInstance<T extends RowData> = Table<T>;
9
- export type CreateDataTableOptions<T extends RowData> = {
10
- data: T[];
11
- columns: DataTableColumn<T>[];
12
- columnResizeMode?: ColumnResizeMode;
13
- initialState?: {
14
- columnVisibility?: VisibilityState;
15
- columnSizing?: ColumnSizingState;
16
- columnPinning?: ColumnPinningState;
17
- rowSelection?: Record<string, boolean>;
18
- sorting?: {
19
- id: string;
20
- desc: boolean;
21
- }[];
22
- columnFilters?: {
23
- id: string;
24
- value: unknown;
25
- }[];
26
- };
27
- getRowId?: (row: T, index: number, parent?: Row<T>) => string;
28
- enableRowSelection?: boolean | ((row: Row<T>) => boolean);
29
- enableMultiRowSelection?: boolean | ((row: Row<T>) => boolean);
30
- enableSorting?: boolean;
31
- debugTable?: boolean;
32
- onColumnVisibilityChange?: (visibility: VisibilityState) => void;
33
- };
34
- export declare function createTable<T extends RowData>(options: CreateDataTableOptions<T>): Table<T>;
35
- export declare function getColumnId<T extends RowData>(column: DataTableLeafColumn<T>): string;
@@ -1,234 +0,0 @@
1
- import { getCoreRowModel, getSortedRowModel, getFilteredRowModel, getFacetedRowModel, getFacetedUniqueValues, getFacetedMinMaxValues } from '@tanstack/table-core';
2
- import { useLocaleContext } from '@ark-ui/svelte/locale';
3
- import { createSvelteTable } from './create-svelte-table.svelte';
4
- import { renderComponent, renderSnippet } from './render-helpers';
5
- function oneOfFilterFn(row, columnId, filterValue) {
6
- return filterValue.includes(String(row.getValue(columnId)));
7
- }
8
- oneOfFilterFn.autoRemove = (val) => !Array.isArray(val) || val.length === 0;
9
- export function createTable(options) {
10
- const localeCtx = useLocaleContext();
11
- const initialColumnVisibility = {
12
- ...createColumnVisibility(options.columns),
13
- ...options.initialState?.columnVisibility
14
- };
15
- // Recompute the resolved column defs only when the source columns change.
16
- // $derived keeps the reference stable between changes so TanStack's column
17
- // memoization isn't invalidated on every access. Pass `columns` through a
18
- // getter (like `data`) to make adding/removing/reordering columns reactive.
19
- const columnDefs = $derived(createColumns(options.columns, localeCtx));
20
- const table = createSvelteTable({
21
- get data() {
22
- return options.data;
23
- },
24
- get columns() {
25
- return columnDefs;
26
- },
27
- getCoreRowModel: getCoreRowModel(),
28
- getSortedRowModel: getSortedRowModel(),
29
- getFilteredRowModel: getFilteredRowModel(),
30
- getFacetedRowModel: getFacetedRowModel(),
31
- getFacetedUniqueValues: getFacetedUniqueValues(),
32
- getFacetedMinMaxValues: getFacetedMinMaxValues(),
33
- columnResizeMode: options.columnResizeMode,
34
- getRowId: options.getRowId,
35
- enableRowSelection: options.enableRowSelection ?? false,
36
- enableMultiRowSelection: options.enableMultiRowSelection,
37
- enableSorting: options.enableSorting,
38
- debugTable: options.debugTable,
39
- filterFns: {
40
- oneOf: oneOfFilterFn
41
- },
42
- onColumnVisibilityChange: (updater) => {
43
- // Without this, overriding onColumnVisibilityChange bypasses makeStateUpdater,
44
- // so createSvelteTable's $state never updates and Svelte doesn't re-render.
45
- table.options.onStateChange?.((old) => {
46
- const columnVisibility = typeof updater === 'function' ? updater(old.columnVisibility) : updater;
47
- options.onColumnVisibilityChange?.(columnVisibility);
48
- return {
49
- ...old,
50
- columnVisibility
51
- };
52
- });
53
- },
54
- initialState: {
55
- columnVisibility: initialColumnVisibility,
56
- columnSizing: {
57
- ...createColumnSizing(options.columns),
58
- ...options.initialState?.columnSizing
59
- },
60
- columnPinning: options.initialState?.columnPinning ?? createColumnPinning(options.columns),
61
- rowSelection: options.initialState?.rowSelection ?? {},
62
- sorting: options.initialState?.sorting ?? [],
63
- columnFilters: options.initialState?.columnFilters ?? []
64
- }
65
- });
66
- return table;
67
- }
68
- function createColumnVisibility(columns) {
69
- return getLeafColumns(columns).reduce((visibility, column) => {
70
- visibility[getColumnId(column)] = true;
71
- return visibility;
72
- }, {});
73
- }
74
- function createColumnSizing(columns) {
75
- return getLeafColumns(columns).reduce((sizes, column) => {
76
- if (typeof column.size === 'number') {
77
- sizes[getColumnId(column)] = column.size;
78
- }
79
- return sizes;
80
- }, {});
81
- }
82
- function createColumnPinning(columns) {
83
- const leafCols = getLeafColumns(columns);
84
- return {
85
- left: leafCols.filter((c) => c.pinned === 'left').map(getColumnId),
86
- right: leafCols.filter((c) => c.pinned === 'right').map(getColumnId)
87
- };
88
- }
89
- function createColumns(columns, localeCtx) {
90
- return columns.map((column) => {
91
- if (isGroupColumn(column)) {
92
- return {
93
- id: getGroupColumnId(column),
94
- header: column.header,
95
- columns: createColumns(column.columns, localeCtx),
96
- meta: {
97
- align: column.align
98
- }
99
- };
100
- }
101
- const columnId = getColumnId(column);
102
- const derivedFilterFn = column.filterFn ?? getFilterFnForType(column.type);
103
- const columnDef = {
104
- id: columnId,
105
- header: column.header,
106
- size: column.size,
107
- minSize: column.minSize,
108
- maxSize: column.maxSize,
109
- enableResizing: column.enableResizing,
110
- enableHiding: getColumnEnableHiding(column, columnId),
111
- enableSorting: column.enableSorting,
112
- sortDescFirst: column.sortDescFirst,
113
- enableColumnFilter: column.enableColumnFilter,
114
- ...(derivedFilterFn !== undefined ? { filterFn: derivedFilterFn } : {}),
115
- meta: {
116
- align: column.align,
117
- type: column.type,
118
- formatOptions: column.formatOptions,
119
- formatLocale: column.formatLocale,
120
- grow: column.grow,
121
- sum: column.sum,
122
- footer: column.footer
123
- }
124
- };
125
- if (typeof column.accessorFn === 'function') {
126
- return {
127
- ...columnDef,
128
- accessorFn: column.accessorFn,
129
- cell: (context) => formatCellValue(column, context.getValue(), context.row.original, localeCtx)
130
- };
131
- }
132
- return {
133
- ...columnDef,
134
- accessorKey: column.accessorKey,
135
- cell: (context) => formatCellValue(column, context.getValue(), context.row.original, localeCtx)
136
- };
137
- });
138
- }
139
- function isGroupColumn(column) {
140
- return Array.isArray(column.columns);
141
- }
142
- function getLeafColumns(columns) {
143
- return columns.flatMap((column) => isGroupColumn(column) ? getLeafColumns(column.columns) : column);
144
- }
145
- function getGroupColumnId(column) {
146
- return column.id ?? column.header;
147
- }
148
- function getColumnEnableHiding(column, columnId) {
149
- if (column.enableHiding !== undefined)
150
- return column.enableHiding;
151
- if (columnId === 'id')
152
- return false;
153
- return undefined;
154
- }
155
- export function getColumnId(column) {
156
- if (column.id !== undefined)
157
- return column.id;
158
- if ('accessorKey' in column && column.accessorKey !== undefined)
159
- return column.accessorKey;
160
- throw new Error('DataTableColumn with accessorFn requires an id.');
161
- }
162
- const TYPE_FORMAT_DEFAULTS = {
163
- currency: { style: 'currency', currency: 'USD' },
164
- percent: { style: 'percent' },
165
- number: {}
166
- };
167
- const TYPE_DATE_FORMAT_DEFAULTS = {
168
- date: { day: '2-digit', month: '2-digit', year: 'numeric' },
169
- time: { hour: '2-digit', minute: '2-digit' },
170
- 'date-time': {
171
- day: '2-digit',
172
- month: '2-digit',
173
- year: 'numeric',
174
- hour: '2-digit',
175
- minute: '2-digit'
176
- }
177
- };
178
- function getFilterFnForType(type) {
179
- switch (type) {
180
- case 'number':
181
- case 'currency':
182
- case 'percent':
183
- return 'inNumberRange';
184
- case 'boolean':
185
- return 'equals';
186
- case 'select':
187
- return 'oneOf';
188
- default:
189
- return undefined;
190
- }
191
- }
192
- function applyTypeFormat(column, value, localeCtx) {
193
- if (value === null || value === undefined || value === '')
194
- return undefined;
195
- const numDefaults = column.type ? TYPE_FORMAT_DEFAULTS[column.type] : undefined;
196
- if (numDefaults !== undefined) {
197
- const locale = column.formatLocale ?? localeCtx().locale;
198
- return new Intl.NumberFormat(locale, {
199
- ...numDefaults,
200
- ...column.formatOptions
201
- }).format(Number(value));
202
- }
203
- if (column.type === 'date' || column.type === 'time' || column.type === 'date-time') {
204
- const dateValue = value instanceof Date ? value : new Date(value);
205
- if (isNaN(dateValue.getTime()))
206
- return undefined;
207
- const locale = column.formatLocale ?? localeCtx().locale;
208
- return new Intl.DateTimeFormat(locale, {
209
- ...TYPE_DATE_FORMAT_DEFAULTS[column.type],
210
- ...column.formatOptions
211
- }).format(dateValue);
212
- }
213
- if (column.type === 'boolean')
214
- return value ? 'Yes' : 'No';
215
- return value;
216
- }
217
- function formatCellValue(column, value, row, localeCtx) {
218
- if (column.cellComponent) {
219
- return renderComponent(column.cellComponent, getCellComponentProps(column, value, row));
220
- }
221
- if (column.cellSnippet) {
222
- return renderSnippet(column.cellSnippet, { value, row });
223
- }
224
- const rendered = column.cell
225
- ? column.cell(value, row)
226
- : applyTypeFormat(column, value, localeCtx);
227
- if (rendered === null || rendered === undefined || rendered === '') {
228
- return '-';
229
- }
230
- return String(rendered);
231
- }
232
- function getCellComponentProps(column, value, row) {
233
- return column.cellProps ? column.cellProps(value, row) : { value, row };
234
- }