compote-ui 0.55.4 → 0.56.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 (53) hide show
  1. package/dist/components/data-table-v9/column-helper.d.ts +12 -0
  2. package/dist/components/data-table-v9/column-helper.js +42 -0
  3. package/dist/components/data-table-v9/create-table.svelte.d.ts +42 -0
  4. package/dist/components/data-table-v9/create-table.svelte.js +248 -0
  5. package/dist/components/data-table-v9/data-table-cell-content.svelte +66 -0
  6. package/dist/components/data-table-v9/data-table-cell-content.svelte.d.ts +28 -0
  7. package/dist/components/data-table-v9/data-table-foot.svelte +111 -0
  8. package/dist/components/data-table-v9/data-table-foot.svelte.d.ts +32 -0
  9. package/dist/components/{data-table/data-table-head.svelte.md → data-table-v9/data-table-head.svelte} +47 -39
  10. package/dist/components/data-table-v9/data-table-head.svelte.d.ts +32 -0
  11. package/dist/components/data-table-v9/data-table-title.svelte.d.ts +10 -0
  12. package/dist/components/data-table-v9/data-table-utils.d.ts +53 -0
  13. package/dist/components/data-table-v9/data-table-utils.js +181 -0
  14. package/dist/components/data-table-v9/data-table.svelte +151 -0
  15. package/dist/components/data-table-v9/data-table.svelte.d.ts +41 -0
  16. package/dist/components/data-table-v9/features.d.ts +12 -0
  17. package/dist/components/data-table-v9/features.js +14 -0
  18. package/dist/components/data-table-v9/index.d.ts +11 -0
  19. package/dist/components/data-table-v9/index.js +9 -0
  20. package/dist/components/data-table-v9/table-view-state.svelte.d.ts +74 -0
  21. package/dist/components/data-table-v9/table-view-state.svelte.js +182 -0
  22. package/dist/components/data-table-v9/toolbar/data-table-column-filter.svelte +380 -0
  23. package/dist/components/data-table-v9/toolbar/data-table-column-filter.svelte.d.ts +29 -0
  24. package/dist/components/data-table-v9/toolbar/data-table-column-visibility.svelte +73 -0
  25. package/dist/components/data-table-v9/toolbar/data-table-column-visibility.svelte.d.ts +29 -0
  26. package/dist/components/data-table-v9/toolbar/data-table-search.svelte +58 -0
  27. package/dist/components/data-table-v9/toolbar/data-table-search.svelte.d.ts +32 -0
  28. package/dist/components/{data-table/data-table-toolbar.svelte.md → data-table-v9/toolbar/data-table-toolbar.svelte} +14 -15
  29. package/dist/components/data-table-v9/toolbar/data-table-toolbar.svelte.d.ts +12 -0
  30. package/dist/components/data-table-v9/types.d.ts +74 -0
  31. package/dist/components/data-table-v9/types.js +1 -0
  32. package/dist/components/data-table-v9/virtual/data-table-virtual-rows.svelte +131 -0
  33. package/dist/components/data-table-v9/virtual/data-table-virtual-rows.svelte.d.ts +40 -0
  34. package/dist/components/data-table-v9/virtual/data-table-virtualized.svelte +79 -0
  35. package/dist/components/data-table-v9/virtual/data-table-virtualized.svelte.d.ts +41 -0
  36. package/dist/components/data-table-v9/virtual/index.d.ts +3 -0
  37. package/dist/components/data-table-v9/virtual/index.js +2 -0
  38. package/dist/index.d.ts +4 -0
  39. package/dist/index.js +4 -0
  40. package/package.json +12 -2
  41. package/dist/components/data-table/column-helper.ts.md +0 -96
  42. package/dist/components/data-table/create-table.ts.md +0 -386
  43. package/dist/components/data-table/data-table-column-filter.svelte.md +0 -249
  44. package/dist/components/data-table/data-table-column-visibility.svelte.md +0 -74
  45. package/dist/components/data-table/data-table-new.svelte.md +0 -245
  46. package/dist/components/data-table/data-table-utils.ts.md +0 -179
  47. package/dist/components/data-table/data-table-virtual-rows.svelte.md +0 -171
  48. package/dist/components/data-table/data-table-virtualized.svelte.md +0 -108
  49. package/dist/components/data-table/data-table.svelte.md +0 -214
  50. package/dist/components/data-table/index.ts.md +0 -22
  51. package/dist/components/data-table/types.ts.md +0 -101
  52. package/dist/components/data-table/virtual/index.ts.md +0 -26
  53. /package/dist/components/{data-table/data-table-title.svelte.md → data-table-v9/data-table-title.svelte} +0 -0
@@ -1,15 +1,16 @@
1
1
  <script lang="ts" generics="T extends RowData">
2
- import { FlexRender } from '@tanstack/svelte-table';
3
2
  import type { HeaderGroup, RowData } from '@tanstack/svelte-table';
3
+ import { FlexRender } from '@tanstack/svelte-table';
4
4
  import { cn } from 'tailwind-variants';
5
- import { PhCaretDown, PhCaretUp } from '$lib/icons';
6
- import Checkbox from '../checkbox/checkbox.svelte';
7
- import type { DataTableFeatures, DataTableInstance } from './create-table';
5
+ import { PhCaretDown, PhCaretUp } from '../../icons';
6
+ import type { DataTableInstance } from './data-table-utils';
7
+ import type { DataTableViewState } from './table-view-state.svelte';
8
+ import type { DataTableFeatures } from './features';
8
9
  import {
9
10
  alignClass,
10
11
  getColumnMeta,
12
+ getGroupPinningStyle,
11
13
  getHeaderAriaSort,
12
- getHeaderSortDirection,
13
14
  getHeaderSortLabel,
14
15
  getPinningStyle,
15
16
  joinStyles,
@@ -25,25 +26,12 @@
25
26
 
26
27
  type Props = {
27
28
  table: DataTableInstance<T>;
28
- headerGroups: HeaderGroup<DataTableFeatures, T>[];
29
- headerGroupCount: number;
30
- isRowSelectionEnabled: boolean;
31
- isMultiRowSelectionEnabled: boolean;
32
- allRowsSelectionState: boolean | 'indeterminate';
29
+ view: DataTableViewState<T>;
33
30
  isVirtual?: boolean;
34
31
  hasGrowColumn?: boolean;
35
32
  };
36
33
 
37
- let {
38
- table,
39
- headerGroups,
40
- headerGroupCount,
41
- isRowSelectionEnabled,
42
- isMultiRowSelectionEnabled,
43
- allRowsSelectionState,
44
- isVirtual = false,
45
- hasGrowColumn = false
46
- }: Props = $props();
34
+ let { table, view, isVirtual = false, hasGrowColumn = false }: Props = $props();
47
35
 
48
36
  type Header = HeaderGroup<DataTableFeatures, T>['headers'][number];
49
37
 
@@ -58,11 +46,17 @@
58
46
  return undefined;
59
47
  }
60
48
 
49
+ function headerSortDirection(header: Header) {
50
+ void view.sorting;
51
+ return header.column.getIsSorted();
52
+ }
53
+
61
54
  function headerCellStyle(header: Header) {
62
55
  const canPinHeader = header.subHeaders.length === 0;
63
56
 
64
57
  let virtualSizeStyle: string | undefined;
65
58
  if (isVirtual) {
59
+ void view.columnSizing;
66
60
  const isGrowLeaf = canPinHeader && getColumnMeta(header.column.columnDef)?.grow;
67
61
  if (isGrowLeaf) {
68
62
  virtualSizeStyle = virtualGrowColumnSizeStyle();
@@ -76,7 +70,14 @@
76
70
 
77
71
  return joinStyles(
78
72
  virtualSizeStyle,
79
- canPinHeader ? getPinningStyle(header.column, true, isRowSelectionEnabled) : undefined
73
+ canPinHeader
74
+ ? getPinningStyle(header.column, table, view, true, view.isRowSelectionEnabled)
75
+ : getGroupPinningStyle(
76
+ header,
77
+ view.getHeaderSection(header),
78
+ view,
79
+ view.isRowSelectionEnabled
80
+ )
80
81
  );
81
82
  }
82
83
 
@@ -99,8 +100,8 @@
99
100
  }
100
101
 
101
102
  function shouldRenderSelectionCheckbox(headerGroupIndex: number) {
102
- if (!isMultiRowSelectionEnabled) return false;
103
- return headerGroupIndex === headerGroupCount - 1;
103
+ if (!view.isMultiRowSelectionEnabled) return false;
104
+ return headerGroupIndex === view.headerGroups.length - 1;
104
105
  }
105
106
  </script>
106
107
 
@@ -108,11 +109,12 @@
108
109
  class="sticky top-0 z-20 bg-surface-2 text-left text-ink-dim"
109
110
  style={isVirtual ? 'display: grid; position: sticky; top: 0; z-index: 20' : undefined}
110
111
  >
111
- {#each headerGroups as headerGroup, headerGroupIndex (headerGroup.id)}
112
+ {#each view.headerGroups as headerGroup, headerGroupIndex (`${headerGroup.id}:${headerGroup.headers.map((header) => `${header.id}:${header.colSpan}`).join('|')}`)}
112
113
  {@const visibleHeaders = headerGroup.headers.filter((header) => header.colSpan > 0)}
113
114
  <tr class="h-9" style={headerRowStyle()}>
114
- {#if isRowSelectionEnabled}
115
+ {#if view.isRowSelectionEnabled}
115
116
  <th
117
+ scope="col"
116
118
  class={cn(
117
119
  'h-9 border-b border-surface-3 bg-surface-2 px-3 py-0 text-center align-middle leading-5 font-medium',
118
120
  isVirtual && 'items-center justify-center'
@@ -120,25 +122,29 @@
120
122
  style={selectionHeaderStyle()}
121
123
  >
122
124
  {#if shouldRenderSelectionCheckbox(headerGroupIndex)}
123
- <Checkbox
124
- size="sm"
125
+ <input
126
+ type="checkbox"
125
127
  aria-label="Select all rows"
126
- class="mx-auto size-4"
127
- checked={allRowsSelectionState}
128
- onCheckedChange={({ checked }) => table.toggleAllRowsSelected(checked === true)}
128
+ class="table-checkbox mx-auto block size-4"
129
+ checked={view.allRowsSelectionState === true}
130
+ indeterminate={view.allRowsSelectionState === 'indeterminate'}
131
+ onchange={(e) => table.toggleAllRowsSelected(e.currentTarget.checked)}
129
132
  />
130
133
  {/if}
131
134
  </th>
132
135
  {/if}
133
- {#each visibleHeaders as header, headerIndex (header.id)}
134
- {@const columnDef = getColumnMeta(header.column.columnDef)}
135
- {@const sortDirection = getHeaderSortDirection(header, table.store.state.sorting)}
136
+ {#each visibleHeaders as header, headerIndex (`${header.id}:${view.getHeaderSection(header) ?? ''}:${header.colSpan}:${header.column.getIsVisible()}`)}
137
+ {@const meta = getColumnMeta(header.column.columnDef)}
138
+ {@const sortDirection = headerSortDirection(header)}
139
+ {@const continuesInNextFragment =
140
+ visibleHeaders[headerIndex + 1]?.column.id === header.column.id}
136
141
  <th
142
+ scope="col"
137
143
  class={cn(
138
144
  'relative h-9 border-b border-surface-3 bg-surface-2 px-3 py-0 align-middle leading-5 font-medium',
139
145
  isVirtual && 'items-center',
140
- isVirtual && justifyClass(columnDef?.align),
141
- alignClass(columnDef?.align)
146
+ isVirtual && justifyClass(meta?.align),
147
+ alignClass(meta?.align)
142
148
  )}
143
149
  colspan={header.colSpan}
144
150
  aria-sort={header.column.getCanSort() ? getHeaderAriaSort(sortDirection) : undefined}
@@ -150,8 +156,8 @@
150
156
  type="button"
151
157
  class={cn(
152
158
  'inline-flex max-w-full appearance-none items-center gap-1 rounded-sm border-0 bg-transparent p-0 align-middle text-sm leading-5 text-inherit outline-none hover:text-ink data-focus-visible:outline-2 data-focus-visible:outline-offset-2 data-focus-visible:outline-ring',
153
- justifyClass(columnDef?.align),
154
- sortButtonDirectionClass(columnDef?.align)
159
+ justifyClass(meta?.align),
160
+ sortButtonDirectionClass(meta?.align)
155
161
  )}
156
162
  aria-label={`${getHeaderSortLabel(sortDirection)}. Toggle sorting.`}
157
163
  onclick={header.column.getToggleSortingHandler()}
@@ -173,11 +179,13 @@
173
179
  <FlexRender {header} />
174
180
  {/if}
175
181
  {/if}
176
- {#if header.column.getCanResize()}
182
+ <!-- A group split by pinning renders as adjacent fragments; skip the
183
+ divider between them so the group reads as one continuous header. -->
184
+ {#if header.column.getCanResize() && !continuesInNextFragment}
177
185
  <div
178
186
  aria-hidden="true"
179
187
  class={resizeHandleClass(headerIndex, visibleHeaders.length)}
180
- style={resizeHandleStyle(table, header)}
188
+ style={resizeHandleStyle(table, header, view.columnResizing)}
181
189
  ondblclick={() => header.column.resetSize()}
182
190
  onmousedown={header.getResizeHandler()}
183
191
  ontouchstart={header.getResizeHandler()}
@@ -0,0 +1,32 @@
1
+ import type { RowData } from '@tanstack/svelte-table';
2
+ import type { DataTableInstance } from './data-table-utils';
3
+ import type { DataTableViewState } from './table-view-state.svelte';
4
+ declare function $$render<T extends RowData>(): {
5
+ props: {
6
+ table: DataTableInstance<T>;
7
+ view: DataTableViewState<T>;
8
+ isVirtual?: boolean;
9
+ hasGrowColumn?: boolean;
10
+ };
11
+ exports: {};
12
+ bindings: "";
13
+ slots: {};
14
+ events: {};
15
+ };
16
+ declare class __sveltets_Render<T extends RowData> {
17
+ props(): ReturnType<typeof $$render<T>>['props'];
18
+ events(): ReturnType<typeof $$render<T>>['events'];
19
+ slots(): ReturnType<typeof $$render<T>>['slots'];
20
+ bindings(): "";
21
+ exports(): {};
22
+ }
23
+ interface $$IsomorphicComponent {
24
+ 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']>> & {
25
+ $$bindings?: ReturnType<__sveltets_Render<T>['bindings']>;
26
+ } & ReturnType<__sveltets_Render<T>['exports']>;
27
+ <T extends RowData>(internal: unknown, props: ReturnType<__sveltets_Render<T>['props']> & {}): ReturnType<__sveltets_Render<T>['exports']>;
28
+ z_$$bindings?: ReturnType<__sveltets_Render<any>['bindings']>;
29
+ }
30
+ declare const DataTableHead: $$IsomorphicComponent;
31
+ type DataTableHead<T extends RowData> = InstanceType<typeof DataTableHead<T>>;
32
+ export default DataTableHead;
@@ -0,0 +1,10 @@
1
+ import type { Snippet } from 'svelte';
2
+ import type { HTMLAttributes } from 'svelte/elements';
3
+ import { type ClassValue } from 'tailwind-variants';
4
+ type Props = Omit<HTMLAttributes<HTMLHeadingElement>, 'class'> & {
5
+ class?: ClassValue;
6
+ children?: Snippet;
7
+ };
8
+ declare const DataTableTitle: import("svelte").Component<Props, {}, "">;
9
+ type DataTableTitle = ReturnType<typeof DataTableTitle>;
10
+ export default DataTableTitle;
@@ -0,0 +1,53 @@
1
+ import type { Column, ColumnPinningState, ColumnSizingState, ColumnVisibilityState, Header, Row, RowData, SvelteTable, TableState } from '@tanstack/svelte-table';
2
+ import type { DataTableColumnMeta } from './types';
3
+ import type { DataTableFeatures } from './features';
4
+ export type DataTableInstance<T extends RowData> = SvelteTable<DataTableFeatures, T>;
5
+ export type DataTableTrackedState = {
6
+ columnPinning: ColumnPinningState;
7
+ columnResizing: TableState<DataTableFeatures>['columnResizing'];
8
+ columnSizing: ColumnSizingState;
9
+ columnVisibility: ColumnVisibilityState;
10
+ };
11
+ export declare function alignClass(align: DataTableColumnMeta['align']): "text-right" | "text-center" | "text-left";
12
+ export declare function justifyClass(align: DataTableColumnMeta['align']): "justify-end" | "justify-center" | "justify-start";
13
+ export declare function sortButtonDirectionClass(align: DataTableColumnMeta['align']): "flex-row-reverse" | "flex-row";
14
+ export declare function columnSizeStyle(size: number): string;
15
+ export declare function selectionColumnSizeStyle(): string;
16
+ export declare function virtualColumnSizeStyle(size: number): string;
17
+ export declare function virtualSelectionColumnSizeStyle(): string;
18
+ export declare function tableSizeStyle<T extends RowData>(table: DataTableInstance<T>, isRowSelectionEnabled: boolean, state: DataTableTrackedState): string;
19
+ export declare function virtualGrowColumnSizeStyle(): string;
20
+ export declare function virtualGroupWithGrowSizeStyle(fixedPortion: number): string;
21
+ export declare function resizeHandleStyle<T extends RowData>(table: DataTableInstance<T>, header: Header<DataTableFeatures, T, unknown>, columnResizing: TableState<DataTableFeatures>['columnResizing']): string | undefined;
22
+ export declare function resizeHandleClass(headerIndex: number, headerCount: number): import("tailwind-variants").CnReturn;
23
+ export declare function getHeaderSortLabel(sortDirection: false | 'asc' | 'desc'): "Sorted ascending" | "Sorted descending" | "Not sorted";
24
+ export declare function getHeaderAriaSort(sortDirection: false | 'asc' | 'desc'): "none" | "ascending" | "descending";
25
+ export declare function getRowCells<T extends RowData>(row: Row<DataTableFeatures, T>, state: DataTableTrackedState): import("@tanstack/svelte-table").Cell_Core<{
26
+ rowSortingFeature: import("@tanstack/svelte-table").TableFeature;
27
+ rowSelectionFeature: import("@tanstack/svelte-table").TableFeature;
28
+ columnFilteringFeature: import("@tanstack/svelte-table").TableFeature;
29
+ columnFacetingFeature: import("@tanstack/svelte-table").TableFeature;
30
+ globalFilteringFeature: import("@tanstack/svelte-table").TableFeature;
31
+ columnVisibilityFeature: import("@tanstack/svelte-table").TableFeature;
32
+ columnPinningFeature: import("@tanstack/svelte-table").TableFeature;
33
+ columnSizingFeature: import("@tanstack/svelte-table").TableFeature;
34
+ columnResizingFeature: import("@tanstack/svelte-table").TableFeature;
35
+ }, T, unknown>[];
36
+ export declare function getBooleanCellValue(value: unknown): boolean | undefined;
37
+ export declare function getPinningStyle<T extends RowData>(column: Column<DataTableFeatures, T, unknown>, table: DataTableInstance<T>, state: DataTableTrackedState, isHeader?: boolean, isRowSelectionEnabled?: boolean): string | undefined;
38
+ /**
39
+ * Sticky positioning for a group header whose leaf columns are pinned.
40
+ *
41
+ * A split fragment's subHeaders contain only its own section's children, so the
42
+ * fragment over the pinned columns sticks at the first/last pinned leaf's offset
43
+ * while the fragment over the scrolling columns gets no style.
44
+ */
45
+ export declare function getGroupPinningStyle<T extends RowData>(header: Header<DataTableFeatures, T, unknown>, section: 'left' | 'center' | 'right' | undefined, state: DataTableTrackedState, isRowSelectionEnabled?: boolean): string | undefined;
46
+ export declare function getUrlCellValue(value: unknown): string | undefined;
47
+ export declare function openUrlCell(value: string): void;
48
+ export declare function getColumnMeta(columnDef: {
49
+ meta?: unknown;
50
+ }): DataTableColumnMeta | undefined;
51
+ export declare function joinStyles(...styles: Array<string | undefined>): string;
52
+ export declare const TYPE_NUMBER_FORMAT_DEFAULTS: Partial<Record<string, Intl.NumberFormatOptions>>;
53
+ export declare function formatColumnFooter(meta: DataTableColumnMeta, values: unknown[], locale: string): string | undefined;
@@ -0,0 +1,181 @@
1
+ import { cn } from 'tailwind-variants';
2
+ export function alignClass(align) {
3
+ return align === 'right' ? 'text-right' : align === 'center' ? 'text-center' : 'text-left';
4
+ }
5
+ export function justifyClass(align) {
6
+ return align === 'right'
7
+ ? 'justify-end'
8
+ : align === 'center'
9
+ ? 'justify-center'
10
+ : 'justify-start';
11
+ }
12
+ export function sortButtonDirectionClass(align) {
13
+ return align === 'right' ? 'flex-row-reverse' : 'flex-row';
14
+ }
15
+ export function columnSizeStyle(size) {
16
+ return `width: ${size}px`;
17
+ }
18
+ export function selectionColumnSizeStyle() {
19
+ return 'width: 40px';
20
+ }
21
+ export function virtualColumnSizeStyle(size) {
22
+ return `display: flex; flex: 0 0 ${size}px; width: ${size}px`;
23
+ }
24
+ export function virtualSelectionColumnSizeStyle() {
25
+ return 'display: flex; flex: 0 0 40px; width: 40px';
26
+ }
27
+ export function tableSizeStyle(table, isRowSelectionEnabled, state) {
28
+ void state.columnSizing;
29
+ void state.columnVisibility;
30
+ return `width: max(100%, ${table.getTotalSize() + (isRowSelectionEnabled ? 40 : 0)}px)`;
31
+ }
32
+ export function virtualGrowColumnSizeStyle() {
33
+ return 'display: flex; flex: 1; min-width: 0';
34
+ }
35
+ export function virtualGroupWithGrowSizeStyle(fixedPortion) {
36
+ return `display: flex; flex: 1 0 ${fixedPortion}px; width: ${fixedPortion}px`;
37
+ }
38
+ export function resizeHandleStyle(table, header, columnResizing) {
39
+ if (table.options.columnResizeMode !== 'onEnd')
40
+ return undefined;
41
+ const deltaOffset = columnResizing.deltaOffset;
42
+ if (!header.column.getIsResizing() || deltaOffset === null)
43
+ return undefined;
44
+ return `transform: translateX(${deltaOffset}px)`;
45
+ }
46
+ export function resizeHandleClass(headerIndex, headerCount) {
47
+ return cn('absolute top-0 z-10 flex h-full w-2 cursor-col-resize touch-none items-center justify-center select-none before:h-4 before:w-px before:bg-border before:content-[""]', headerIndex === headerCount - 1 ? 'right-0' : '-right-1');
48
+ }
49
+ export function getHeaderSortLabel(sortDirection) {
50
+ if (sortDirection === 'asc')
51
+ return 'Sorted ascending';
52
+ if (sortDirection === 'desc')
53
+ return 'Sorted descending';
54
+ return 'Not sorted';
55
+ }
56
+ export function getHeaderAriaSort(sortDirection) {
57
+ if (sortDirection === 'asc')
58
+ return 'ascending';
59
+ if (sortDirection === 'desc')
60
+ return 'descending';
61
+ return 'none';
62
+ }
63
+ export function getRowCells(row, state) {
64
+ void state.columnVisibility;
65
+ void state.columnPinning;
66
+ void state.columnSizing;
67
+ return [
68
+ ...row.getLeftVisibleCells(),
69
+ ...row.getCenterVisibleCells(),
70
+ ...row.getRightVisibleCells()
71
+ ];
72
+ }
73
+ export function getBooleanCellValue(value) {
74
+ if (value === true)
75
+ return true;
76
+ if (value === false)
77
+ return false;
78
+ return undefined;
79
+ }
80
+ export function getPinningStyle(column, table, state, isHeader = false, isRowSelectionEnabled = false) {
81
+ void state.columnPinning;
82
+ void state.columnSizing;
83
+ const isPinned = column.getIsPinned();
84
+ if (!isPinned)
85
+ return undefined;
86
+ const zIndex = isHeader ? 15 : 1;
87
+ const selectionOffset = isRowSelectionEnabled ? 40 : 0;
88
+ if (isPinned === 'left') {
89
+ const left = column.getStart('left') + selectionOffset;
90
+ const leftCols = table.getLeftLeafColumns();
91
+ const isLastLeft = leftCols[leftCols.length - 1]?.id === column.id;
92
+ const shadow = !isHeader && isLastLeft
93
+ ? 'box-shadow: -4px 0 4px -4px var(--compote-border) inset'
94
+ : undefined;
95
+ return ['position: sticky', `z-index: ${zIndex}`, `left: ${left}px`, shadow]
96
+ .filter(Boolean)
97
+ .join('; ');
98
+ }
99
+ else {
100
+ const right = column.getAfter('right');
101
+ const rightCols = table.getRightLeafColumns();
102
+ const isFirstRight = rightCols[0]?.id === column.id;
103
+ const shadow = !isHeader && isFirstRight
104
+ ? 'box-shadow: 4px 0 4px -4px var(--compote-border) inset'
105
+ : undefined;
106
+ return ['position: sticky', `z-index: ${zIndex}`, `right: ${right}px`, shadow]
107
+ .filter(Boolean)
108
+ .join('; ');
109
+ }
110
+ }
111
+ function collectLeafHeaders(header) {
112
+ if (header.subHeaders.length === 0)
113
+ return [header];
114
+ return header.subHeaders.flatMap((sub) => collectLeafHeaders(sub));
115
+ }
116
+ /**
117
+ * Sticky positioning for a group header whose leaf columns are pinned.
118
+ *
119
+ * A split fragment's subHeaders contain only its own section's children, so the
120
+ * fragment over the pinned columns sticks at the first/last pinned leaf's offset
121
+ * while the fragment over the scrolling columns gets no style.
122
+ */
123
+ export function getGroupPinningStyle(header, section, state, isRowSelectionEnabled = false) {
124
+ void state.columnPinning;
125
+ void state.columnSizing;
126
+ if (section !== 'left' && section !== 'right')
127
+ return undefined;
128
+ const leafHeaders = collectLeafHeaders(header);
129
+ if (section === 'left') {
130
+ const first = leafHeaders[0];
131
+ if (first?.column.getIsPinned() !== 'left')
132
+ return undefined;
133
+ const left = first.column.getStart('left') + (isRowSelectionEnabled ? 40 : 0);
134
+ return `position: sticky; z-index: 15; left: ${left}px`;
135
+ }
136
+ const last = leafHeaders[leafHeaders.length - 1];
137
+ if (last?.column.getIsPinned() !== 'right')
138
+ return undefined;
139
+ return `position: sticky; z-index: 15; right: ${last.column.getAfter('right')}px`;
140
+ }
141
+ export function getUrlCellValue(value) {
142
+ if (typeof value !== 'string' || value.trim() === '')
143
+ return undefined;
144
+ return value;
145
+ }
146
+ export function openUrlCell(value) {
147
+ window.open(value, '_blank', 'noopener,noreferrer');
148
+ }
149
+ export function getColumnMeta(columnDef) {
150
+ return columnDef.meta;
151
+ }
152
+ export function joinStyles(...styles) {
153
+ return styles.filter(Boolean).join('; ');
154
+ }
155
+ // Shared with the cell formatter in create-table.svelte.ts — keep number-typed
156
+ // columns and their footer sums formatted identically.
157
+ export const TYPE_NUMBER_FORMAT_DEFAULTS = {
158
+ currency: { style: 'currency', currency: 'USD' },
159
+ percent: { style: 'percent' },
160
+ number: {}
161
+ };
162
+ export function formatColumnFooter(meta, values, locale) {
163
+ if (meta.footer) {
164
+ const result = meta.footer(values);
165
+ if (result === null || result === undefined)
166
+ return undefined;
167
+ return String(result);
168
+ }
169
+ if (meta.sum) {
170
+ const sum = values.reduce((acc, val) => acc + (typeof val === 'number' ? val : Number(val) || 0), 0);
171
+ const numDefaults = meta.type ? TYPE_NUMBER_FORMAT_DEFAULTS[meta.type] : undefined;
172
+ if (numDefaults !== undefined) {
173
+ return new Intl.NumberFormat(meta.formatLocale ?? locale, {
174
+ ...numDefaults,
175
+ ...meta.formatOptions
176
+ }).format(sum);
177
+ }
178
+ return String(sum);
179
+ }
180
+ return undefined;
181
+ }
@@ -0,0 +1,151 @@
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
+ 'truncate border-b border-b-surface-2 px-3 py-2 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>
@@ -0,0 +1,41 @@
1
+ import type { RowData } from '@tanstack/svelte-table';
2
+ import type { HTMLAttributes } from 'svelte/elements';
3
+ import { type ClassValue } from 'tailwind-variants';
4
+ import type { DataTableInstance } from './data-table-utils';
5
+ declare function $$render<T extends RowData>(): {
6
+ props: Omit<HTMLAttributes<HTMLDivElement>, "class"> & {
7
+ table: DataTableInstance<T>;
8
+ caption?: string;
9
+ emptyMessage?: string;
10
+ class?: ClassValue;
11
+ onRowClick?: (details: {
12
+ row: T;
13
+ event: MouseEvent;
14
+ }) => void;
15
+ onRowDoubleClick?: (details: {
16
+ row: T;
17
+ event: MouseEvent;
18
+ }) => void;
19
+ };
20
+ exports: {};
21
+ bindings: "";
22
+ slots: {};
23
+ events: {};
24
+ };
25
+ declare class __sveltets_Render<T extends RowData> {
26
+ props(): ReturnType<typeof $$render<T>>['props'];
27
+ events(): ReturnType<typeof $$render<T>>['events'];
28
+ slots(): ReturnType<typeof $$render<T>>['slots'];
29
+ bindings(): "";
30
+ exports(): {};
31
+ }
32
+ interface $$IsomorphicComponent {
33
+ 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']>> & {
34
+ $$bindings?: ReturnType<__sveltets_Render<T>['bindings']>;
35
+ } & ReturnType<__sveltets_Render<T>['exports']>;
36
+ <T extends RowData>(internal: unknown, props: ReturnType<__sveltets_Render<T>['props']> & {}): ReturnType<__sveltets_Render<T>['exports']>;
37
+ z_$$bindings?: ReturnType<__sveltets_Render<any>['bindings']>;
38
+ }
39
+ declare const DataTable: $$IsomorphicComponent;
40
+ type DataTable<T extends RowData> = InstanceType<typeof DataTable<T>>;
41
+ export default DataTable;
@@ -0,0 +1,12 @@
1
+ export declare const dataTableFeatures: {
2
+ rowSortingFeature: import("@tanstack/svelte-table").TableFeature;
3
+ rowSelectionFeature: import("@tanstack/svelte-table").TableFeature;
4
+ columnFilteringFeature: import("@tanstack/svelte-table").TableFeature;
5
+ columnFacetingFeature: import("@tanstack/svelte-table").TableFeature;
6
+ globalFilteringFeature: import("@tanstack/svelte-table").TableFeature;
7
+ columnVisibilityFeature: import("@tanstack/svelte-table").TableFeature;
8
+ columnPinningFeature: import("@tanstack/svelte-table").TableFeature;
9
+ columnSizingFeature: import("@tanstack/svelte-table").TableFeature;
10
+ columnResizingFeature: import("@tanstack/svelte-table").TableFeature;
11
+ };
12
+ export type DataTableFeatures = typeof dataTableFeatures;