compote-ui 0.33.0 → 0.34.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.
- package/dist/components/data-table/column-helper.d.ts +12 -0
- package/dist/components/data-table/column-helper.js +36 -0
- package/dist/components/data-table/create-table.d.ts +38 -0
- package/dist/components/data-table/create-table.js +190 -0
- package/dist/components/data-table/data-table-column-filter.svelte +249 -0
- package/dist/components/data-table/data-table-column-filter.svelte.d.ts +29 -0
- package/dist/components/data-table/data-table-column-visibility.svelte +8 -13
- package/dist/components/data-table/data-table-column-visibility.svelte.d.ts +13 -13
- package/dist/components/data-table/data-table-title.svelte +2 -2
- package/dist/components/data-table/data-table-title.svelte.d.ts +2 -2
- package/dist/components/data-table/data-table-toolbar.svelte +28 -5
- package/dist/components/data-table/data-table-toolbar.svelte.d.ts +4 -2
- package/dist/components/data-table/data-table.svelte +293 -270
- package/dist/components/data-table/data-table.svelte.d.ts +19 -16
- package/dist/components/data-table/index.d.ts +5 -3
- package/dist/components/data-table/index.js +3 -3
- package/dist/components/data-table/types.d.ts +50 -0
- package/dist/components/data-table/types.js +1 -0
- package/dist/components/data-table-old/data-table-column-visibility.svelte +79 -0
- package/dist/components/data-table-old/data-table-column-visibility.svelte.d.ts +29 -0
- package/dist/components/data-table-old/data-table-title.svelte +16 -0
- package/dist/components/data-table-old/data-table-title.svelte.d.ts +10 -0
- package/dist/components/data-table-old/data-table-toolbar.svelte +16 -0
- package/dist/components/data-table-old/data-table-toolbar.svelte.d.ts +10 -0
- package/dist/components/data-table-old/data-table.svelte +342 -0
- package/dist/components/data-table-old/data-table.svelte.d.ts +32 -0
- package/dist/components/data-table-old/index.d.ts +7 -0
- package/dist/components/data-table-old/index.js +7 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/package.json +5 -5
- /package/dist/components/{data-table → data-table-old}/core/cells.d.ts +0 -0
- /package/dist/components/{data-table → data-table-old}/core/cells.js +0 -0
- /package/dist/components/{data-table → data-table-old}/core/create-table.svelte.d.ts +0 -0
- /package/dist/components/{data-table → data-table-old}/core/create-table.svelte.js +0 -0
- /package/dist/components/{data-table → data-table-old}/core/index.d.ts +0 -0
- /package/dist/components/{data-table → data-table-old}/core/index.js +0 -0
- /package/dist/components/{data-table → data-table-old}/data-table-filters.svelte +0 -0
- /package/dist/components/{data-table → data-table-old}/data-table-filters.svelte.d.ts +0 -0
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { RowData } from '@tanstack/svelte-table';
|
|
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 {};
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
function urlDefaults(options) {
|
|
2
|
+
if (options.type !== 'url')
|
|
3
|
+
return options;
|
|
4
|
+
return {
|
|
5
|
+
align: 'center',
|
|
6
|
+
enableSorting: false,
|
|
7
|
+
size: 60,
|
|
8
|
+
...options
|
|
9
|
+
};
|
|
10
|
+
}
|
|
11
|
+
export function createDataTableColumnHelper() {
|
|
12
|
+
return {
|
|
13
|
+
accessor(accessorKey, options) {
|
|
14
|
+
return {
|
|
15
|
+
...urlDefaults(options),
|
|
16
|
+
accessorKey
|
|
17
|
+
};
|
|
18
|
+
},
|
|
19
|
+
accessorFn(accessorFn, options) {
|
|
20
|
+
return {
|
|
21
|
+
...urlDefaults(options),
|
|
22
|
+
accessorFn
|
|
23
|
+
};
|
|
24
|
+
},
|
|
25
|
+
group(header, columns, options = {}) {
|
|
26
|
+
return {
|
|
27
|
+
...options,
|
|
28
|
+
header,
|
|
29
|
+
columns
|
|
30
|
+
};
|
|
31
|
+
},
|
|
32
|
+
columns(columns) {
|
|
33
|
+
return columns;
|
|
34
|
+
}
|
|
35
|
+
};
|
|
36
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { type ColumnFiltersState, type ColumnResizeMode, type Row, type RowData, type RowSelectionState, type SortingState, type SvelteTable, type TableState } from '@tanstack/svelte-table';
|
|
2
|
+
import type { DataTableColumn, DataTableLeafColumn } from './types';
|
|
3
|
+
declare const dataTableFeatures: {
|
|
4
|
+
columnVisibilityFeature: import("@tanstack/table-core").TableFeature<import("@tanstack/table-core").ColumnVisibilityFeatureConstructors<import("@tanstack/table-core").TableFeatures, RowData>>;
|
|
5
|
+
columnSizingFeature: import("@tanstack/table-core").TableFeature<import("@tanstack/table-core").ColumnSizingFeatureConstructors<import("@tanstack/table-core").TableFeatures, RowData>>;
|
|
6
|
+
columnResizingFeature: import("@tanstack/table-core").TableFeature<import("@tanstack/table-core").ColumnResizingFeatureConstructors<import("@tanstack/table-core").TableFeatures, RowData>>;
|
|
7
|
+
columnFilteringFeature: import("@tanstack/table-core").TableFeature<import("@tanstack/table-core").ColumnFilteringFeatureConstructors<import("@tanstack/table-core").TableFeatures, RowData>>;
|
|
8
|
+
columnFacetingFeature: import("@tanstack/table-core").TableFeature<import("@tanstack/table-core").ColumnFacetingFeatureConstructors<import("@tanstack/table-core").TableFeatures, RowData>>;
|
|
9
|
+
rowSelectionFeature: import("@tanstack/table-core").TableFeature<import("@tanstack/table-core").RowSelectionFeatureConstructors<import("@tanstack/table-core").TableFeatures, RowData>>;
|
|
10
|
+
rowSortingFeature: import("@tanstack/table-core").TableFeature<import("@tanstack/table-core").RowSortingFeatureConstructors<import("@tanstack/table-core").TableFeatures, RowData>>;
|
|
11
|
+
};
|
|
12
|
+
export type DataTableFeatures = typeof dataTableFeatures;
|
|
13
|
+
export type DataTableSelectedState = Pick<TableState<DataTableFeatures>, 'columnVisibility' | 'columnSizing' | 'columnResizing' | 'rowSelection' | 'sorting' | 'columnFilters'>;
|
|
14
|
+
export type DataTableInstance<T extends RowData> = SvelteTable<DataTableFeatures, T, DataTableSelectedState>;
|
|
15
|
+
export type CreateDataTableOptions<T extends RowData> = {
|
|
16
|
+
data: T[];
|
|
17
|
+
columns: DataTableColumn<T>[];
|
|
18
|
+
columnResizeMode?: ColumnResizeMode;
|
|
19
|
+
initialSorting?: SortingState;
|
|
20
|
+
initialRowSelection?: RowSelectionState;
|
|
21
|
+
initialColumnFilters?: ColumnFiltersState;
|
|
22
|
+
getRowId?: (row: T, index: number, parent?: Row<DataTableFeatures, T>) => string;
|
|
23
|
+
enableRowSelection?: boolean | ((row: Row<DataTableFeatures, T>) => boolean);
|
|
24
|
+
enableMultiRowSelection?: boolean | ((row: Row<DataTableFeatures, T>) => boolean);
|
|
25
|
+
enableSorting?: boolean;
|
|
26
|
+
debugTable?: boolean;
|
|
27
|
+
};
|
|
28
|
+
export declare function createTable<T extends RowData>(options: CreateDataTableOptions<T>): SvelteTable<{
|
|
29
|
+
columnVisibilityFeature: import("@tanstack/table-core").TableFeature<import("@tanstack/table-core").ColumnVisibilityFeatureConstructors<import("@tanstack/table-core").TableFeatures, RowData>>;
|
|
30
|
+
columnSizingFeature: import("@tanstack/table-core").TableFeature<import("@tanstack/table-core").ColumnSizingFeatureConstructors<import("@tanstack/table-core").TableFeatures, RowData>>;
|
|
31
|
+
columnResizingFeature: import("@tanstack/table-core").TableFeature<import("@tanstack/table-core").ColumnResizingFeatureConstructors<import("@tanstack/table-core").TableFeatures, RowData>>;
|
|
32
|
+
columnFilteringFeature: import("@tanstack/table-core").TableFeature<import("@tanstack/table-core").ColumnFilteringFeatureConstructors<import("@tanstack/table-core").TableFeatures, RowData>>;
|
|
33
|
+
columnFacetingFeature: import("@tanstack/table-core").TableFeature<import("@tanstack/table-core").ColumnFacetingFeatureConstructors<import("@tanstack/table-core").TableFeatures, RowData>>;
|
|
34
|
+
rowSelectionFeature: import("@tanstack/table-core").TableFeature<import("@tanstack/table-core").RowSelectionFeatureConstructors<import("@tanstack/table-core").TableFeatures, RowData>>;
|
|
35
|
+
rowSortingFeature: import("@tanstack/table-core").TableFeature<import("@tanstack/table-core").RowSortingFeatureConstructors<import("@tanstack/table-core").TableFeatures, RowData>>;
|
|
36
|
+
}, T, DataTableSelectedState>;
|
|
37
|
+
export declare function getColumnId<T extends RowData>(column: DataTableLeafColumn<T>): string;
|
|
38
|
+
export {};
|
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
import { columnVisibilityFeature, columnResizingFeature, columnSizingFeature, columnFilteringFeature, columnFacetingFeature, createSortedRowModel, createFilteredRowModel, createFacetedRowModel, createFacetedMinMaxValues, createFacetedUniqueValues, createTable as createTanStackTable, filterFns, renderComponent, renderSnippet, rowSelectionFeature, rowSortingFeature, sortFns, tableFeatures } from '@tanstack/svelte-table';
|
|
2
|
+
import { useLocaleContext } from '@ark-ui/svelte/locale';
|
|
3
|
+
const dataTableFeatures = tableFeatures({
|
|
4
|
+
columnVisibilityFeature,
|
|
5
|
+
columnSizingFeature,
|
|
6
|
+
columnResizingFeature,
|
|
7
|
+
columnFilteringFeature,
|
|
8
|
+
columnFacetingFeature,
|
|
9
|
+
rowSelectionFeature,
|
|
10
|
+
rowSortingFeature
|
|
11
|
+
});
|
|
12
|
+
function oneOfFilterFn(row, columnId, filterValue) {
|
|
13
|
+
return filterValue.includes(String(row.getValue(columnId)));
|
|
14
|
+
}
|
|
15
|
+
oneOfFilterFn.autoRemove = (val) => !Array.isArray(val) || val.length === 0;
|
|
16
|
+
export function createTable(options) {
|
|
17
|
+
const localeCtx = useLocaleContext();
|
|
18
|
+
return createTanStackTable({
|
|
19
|
+
_features: dataTableFeatures,
|
|
20
|
+
_rowModels: {
|
|
21
|
+
sortedRowModel: createSortedRowModel(sortFns),
|
|
22
|
+
filteredRowModel: createFilteredRowModel({ ...filterFns, oneOf: oneOfFilterFn }),
|
|
23
|
+
facetedRowModel: createFacetedRowModel(),
|
|
24
|
+
facetedMinMaxValues: createFacetedMinMaxValues(),
|
|
25
|
+
facetedUniqueValues: createFacetedUniqueValues()
|
|
26
|
+
},
|
|
27
|
+
columnResizeMode: options.columnResizeMode,
|
|
28
|
+
getRowId: options.getRowId,
|
|
29
|
+
enableRowSelection: options.enableRowSelection ?? false,
|
|
30
|
+
enableMultiRowSelection: options.enableMultiRowSelection,
|
|
31
|
+
enableSorting: options.enableSorting,
|
|
32
|
+
debugTable: options.debugTable,
|
|
33
|
+
get data() {
|
|
34
|
+
return options.data;
|
|
35
|
+
},
|
|
36
|
+
get columns() {
|
|
37
|
+
return createColumns(options.columns, localeCtx);
|
|
38
|
+
},
|
|
39
|
+
initialState: {
|
|
40
|
+
columnVisibility: createColumnVisibility(options.columns),
|
|
41
|
+
columnSizing: createColumnSizing(options.columns),
|
|
42
|
+
rowSelection: options.initialRowSelection ?? {},
|
|
43
|
+
sorting: options.initialSorting ?? [],
|
|
44
|
+
columnFilters: options.initialColumnFilters ?? []
|
|
45
|
+
}
|
|
46
|
+
}, (state) => ({
|
|
47
|
+
columnVisibility: state.columnVisibility,
|
|
48
|
+
columnSizing: state.columnSizing,
|
|
49
|
+
columnResizing: state.columnResizing,
|
|
50
|
+
rowSelection: state.rowSelection,
|
|
51
|
+
sorting: state.sorting,
|
|
52
|
+
columnFilters: state.columnFilters
|
|
53
|
+
}));
|
|
54
|
+
}
|
|
55
|
+
function createColumnVisibility(columns) {
|
|
56
|
+
return getLeafColumns(columns).reduce((visibility, column) => {
|
|
57
|
+
visibility[getColumnId(column)] = true;
|
|
58
|
+
return visibility;
|
|
59
|
+
}, {});
|
|
60
|
+
}
|
|
61
|
+
function createColumnSizing(columns) {
|
|
62
|
+
return getLeafColumns(columns).reduce((sizes, column) => {
|
|
63
|
+
if (typeof column.size === 'number') {
|
|
64
|
+
sizes[getColumnId(column)] = column.size;
|
|
65
|
+
}
|
|
66
|
+
return sizes;
|
|
67
|
+
}, {});
|
|
68
|
+
}
|
|
69
|
+
function createColumns(columns, localeCtx) {
|
|
70
|
+
return columns.map((column) => {
|
|
71
|
+
if (isGroupColumn(column)) {
|
|
72
|
+
return {
|
|
73
|
+
id: getGroupColumnId(column),
|
|
74
|
+
header: column.header,
|
|
75
|
+
columns: createColumns(column.columns, localeCtx),
|
|
76
|
+
meta: {
|
|
77
|
+
align: column.align
|
|
78
|
+
}
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
const columnId = getColumnId(column);
|
|
82
|
+
const derivedFilterFn = column.filterFn ?? getFilterFnForType(column.type);
|
|
83
|
+
const columnDef = {
|
|
84
|
+
id: columnId,
|
|
85
|
+
header: column.header,
|
|
86
|
+
size: column.size,
|
|
87
|
+
minSize: column.minSize,
|
|
88
|
+
maxSize: column.maxSize,
|
|
89
|
+
enableResizing: column.enableResizing,
|
|
90
|
+
enableHiding: getColumnEnableHiding(column, columnId),
|
|
91
|
+
enableSorting: column.enableSorting,
|
|
92
|
+
enableColumnFilter: column.enableColumnFilter,
|
|
93
|
+
sortDescFirst: column.sortDescFirst,
|
|
94
|
+
...(derivedFilterFn !== undefined ? { filterFn: derivedFilterFn } : {}),
|
|
95
|
+
meta: {
|
|
96
|
+
align: column.align,
|
|
97
|
+
type: column.type,
|
|
98
|
+
formatOptions: column.formatOptions,
|
|
99
|
+
formatLocale: column.formatLocale
|
|
100
|
+
}
|
|
101
|
+
};
|
|
102
|
+
if (typeof column.accessorFn === 'function') {
|
|
103
|
+
return {
|
|
104
|
+
...columnDef,
|
|
105
|
+
accessorFn: column.accessorFn,
|
|
106
|
+
cell: (context) => formatCellValue(column, context.getValue(), context.row.original, localeCtx)
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
return {
|
|
110
|
+
...columnDef,
|
|
111
|
+
accessorKey: column.accessorKey,
|
|
112
|
+
cell: (context) => formatCellValue(column, context.getValue(), context.row.original, localeCtx)
|
|
113
|
+
};
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
function isGroupColumn(column) {
|
|
117
|
+
return Array.isArray(column.columns);
|
|
118
|
+
}
|
|
119
|
+
function getLeafColumns(columns) {
|
|
120
|
+
return columns.flatMap((column) => isGroupColumn(column) ? getLeafColumns(column.columns) : column);
|
|
121
|
+
}
|
|
122
|
+
function getGroupColumnId(column) {
|
|
123
|
+
return column.id ?? column.header;
|
|
124
|
+
}
|
|
125
|
+
function getColumnEnableHiding(column, columnId) {
|
|
126
|
+
if (column.enableHiding !== undefined)
|
|
127
|
+
return column.enableHiding;
|
|
128
|
+
if (columnId === 'id')
|
|
129
|
+
return false;
|
|
130
|
+
return undefined;
|
|
131
|
+
}
|
|
132
|
+
export function getColumnId(column) {
|
|
133
|
+
if (column.id !== undefined)
|
|
134
|
+
return column.id;
|
|
135
|
+
if ('accessorKey' in column && column.accessorKey !== undefined)
|
|
136
|
+
return column.accessorKey;
|
|
137
|
+
throw new Error('DataTableColumn with accessorFn requires an id.');
|
|
138
|
+
}
|
|
139
|
+
const TYPE_FORMAT_DEFAULTS = {
|
|
140
|
+
currency: { style: 'currency', currency: 'USD' },
|
|
141
|
+
percent: { style: 'percent' },
|
|
142
|
+
number: {}
|
|
143
|
+
};
|
|
144
|
+
function getFilterFnForType(type) {
|
|
145
|
+
switch (type) {
|
|
146
|
+
case 'number':
|
|
147
|
+
case 'currency':
|
|
148
|
+
case 'percent':
|
|
149
|
+
return 'inNumberRange';
|
|
150
|
+
case 'boolean':
|
|
151
|
+
return 'equals';
|
|
152
|
+
case 'select':
|
|
153
|
+
return 'oneOf';
|
|
154
|
+
default:
|
|
155
|
+
return undefined;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
function applyTypeFormat(column, value, localeCtx) {
|
|
159
|
+
if (value === null || value === undefined || value === '')
|
|
160
|
+
return undefined;
|
|
161
|
+
const defaults = column.type ? TYPE_FORMAT_DEFAULTS[column.type] : undefined;
|
|
162
|
+
if (defaults !== undefined) {
|
|
163
|
+
const locale = column.formatLocale ?? localeCtx().locale;
|
|
164
|
+
return new Intl.NumberFormat(locale, {
|
|
165
|
+
...defaults,
|
|
166
|
+
...column.formatOptions
|
|
167
|
+
}).format(Number(value));
|
|
168
|
+
}
|
|
169
|
+
if (column.type === 'boolean')
|
|
170
|
+
return value ? 'Yes' : 'No';
|
|
171
|
+
return value;
|
|
172
|
+
}
|
|
173
|
+
function formatCellValue(column, value, row, localeCtx) {
|
|
174
|
+
if (column.cellComponent) {
|
|
175
|
+
return renderComponent(column.cellComponent, getCellComponentProps(column, value, row));
|
|
176
|
+
}
|
|
177
|
+
if (column.cellSnippet) {
|
|
178
|
+
return renderSnippet(column.cellSnippet, { value, row });
|
|
179
|
+
}
|
|
180
|
+
const rendered = column.cell
|
|
181
|
+
? column.cell(value, row)
|
|
182
|
+
: applyTypeFormat(column, value, localeCtx);
|
|
183
|
+
if (rendered === null || rendered === undefined || rendered === '') {
|
|
184
|
+
return '-';
|
|
185
|
+
}
|
|
186
|
+
return String(rendered);
|
|
187
|
+
}
|
|
188
|
+
function getCellComponentProps(column, value, row) {
|
|
189
|
+
return column.cellProps ? column.cellProps(value, row) : { value, row };
|
|
190
|
+
}
|
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
<script lang="ts" generics="T extends RowData">
|
|
2
|
+
import { onDestroy } from 'svelte';
|
|
3
|
+
import type { CellData, Column, RowData } from '@tanstack/svelte-table';
|
|
4
|
+
import * as Popover from '../popover';
|
|
5
|
+
import * as ScrollArea from '../scroll-area';
|
|
6
|
+
import Checkbox from '../checkbox/checkbox.svelte';
|
|
7
|
+
import { cn } from 'tailwind-variants';
|
|
8
|
+
import type { DataTableFeatures, DataTableInstance } from './create-table';
|
|
9
|
+
import NumberInput from '../number-input/number-input.svelte';
|
|
10
|
+
import * as Field from '../field';
|
|
11
|
+
|
|
12
|
+
type Props = {
|
|
13
|
+
table: DataTableInstance<T>;
|
|
14
|
+
triggerLabel?: string;
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
let { table, triggerLabel = 'Filters' }: Props = $props();
|
|
18
|
+
|
|
19
|
+
let localText: Record<string, string> = $state({});
|
|
20
|
+
let localNumMin: Record<string, number> = $state({});
|
|
21
|
+
let localNumMax: Record<string, number> = $state({});
|
|
22
|
+
const timers: Record<string, ReturnType<typeof setTimeout>> = {};
|
|
23
|
+
|
|
24
|
+
const columnFilters = $derived(table.store.state.columnFilters);
|
|
25
|
+
const activeCount = $derived(columnFilters.length);
|
|
26
|
+
const filterableColumns = $derived(table.getAllLeafColumns().filter((col) => col.getCanFilter()));
|
|
27
|
+
|
|
28
|
+
onDestroy(() => {
|
|
29
|
+
Object.values(timers).forEach(clearTimeout);
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
function getColumnType(column: Column<DataTableFeatures, T, CellData>): string | undefined {
|
|
33
|
+
return (column.columnDef.meta as Record<string, unknown> | undefined)?.type as
|
|
34
|
+
| string
|
|
35
|
+
| undefined;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function getColumnLabel(column: Column<DataTableFeatures, T, CellData>): string {
|
|
39
|
+
return typeof column.columnDef.header === 'string' ? column.columnDef.header : column.id;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function clearFilters() {
|
|
43
|
+
Object.values(timers).forEach(clearTimeout);
|
|
44
|
+
for (const key of Object.keys(timers)) delete timers[key];
|
|
45
|
+
localText = {};
|
|
46
|
+
localNumMin = {};
|
|
47
|
+
localNumMax = {};
|
|
48
|
+
table.resetColumnFilters();
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function handleTextInput(column: Column<DataTableFeatures, T, CellData>, value: string) {
|
|
52
|
+
localText[column.id] = value;
|
|
53
|
+
clearTimeout(timers[column.id]);
|
|
54
|
+
timers[column.id] = setTimeout(() => {
|
|
55
|
+
column.setFilterValue(value || undefined);
|
|
56
|
+
}, 300);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function handleNumericInput(
|
|
60
|
+
column: Column<DataTableFeatures, T, CellData>,
|
|
61
|
+
which: 'min' | 'max',
|
|
62
|
+
value: number | null
|
|
63
|
+
) {
|
|
64
|
+
if (value === null) {
|
|
65
|
+
if (which === 'min') delete localNumMin[column.id];
|
|
66
|
+
else delete localNumMax[column.id];
|
|
67
|
+
} else {
|
|
68
|
+
if (which === 'min') localNumMin[column.id] = value;
|
|
69
|
+
else localNumMax[column.id] = value;
|
|
70
|
+
}
|
|
71
|
+
clearTimeout(timers[`${column.id}_${which}`]);
|
|
72
|
+
timers[`${column.id}_${which}`] = setTimeout(() => {
|
|
73
|
+
const min = localNumMin[column.id];
|
|
74
|
+
const max = localNumMax[column.id];
|
|
75
|
+
column.setFilterValue(min === undefined && max === undefined ? undefined : [min, max]);
|
|
76
|
+
}, 300);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function getSelectValues(column: Column<DataTableFeatures, T, CellData>): string[] {
|
|
80
|
+
return (column.getFilterValue() as string[] | undefined) ?? [];
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function handleSelectChange(
|
|
84
|
+
column: Column<DataTableFeatures, T, CellData>,
|
|
85
|
+
value: string,
|
|
86
|
+
checked: boolean
|
|
87
|
+
) {
|
|
88
|
+
const current = getSelectValues(column);
|
|
89
|
+
const next = checked ? [...current, value] : current.filter((v) => v !== value);
|
|
90
|
+
column.setFilterValue(next.length ? next : undefined);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function getFacetedValues(column: Column<DataTableFeatures, T, CellData>): string[] {
|
|
94
|
+
return Array.from(column.getFacetedUniqueValues().keys()).map(String).sort();
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function getFacetedMinMax(
|
|
98
|
+
column: Column<DataTableFeatures, T, CellData>
|
|
99
|
+
): [number | undefined, number | undefined] {
|
|
100
|
+
const vals = column.getFacetedMinMaxValues();
|
|
101
|
+
return vals ? [vals[0] as number, vals[1] as number] : [undefined, undefined];
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
function getColumnFormatOptions(
|
|
105
|
+
column: Column<DataTableFeatures, T, CellData>
|
|
106
|
+
): Intl.NumberFormatOptions | undefined {
|
|
107
|
+
return (column.columnDef.meta as Record<string, unknown> | undefined)?.formatOptions as
|
|
108
|
+
| Intl.NumberFormatOptions
|
|
109
|
+
| undefined;
|
|
110
|
+
}
|
|
111
|
+
</script>
|
|
112
|
+
|
|
113
|
+
<Popover.Root positioning={{ placement: 'bottom-end' }}>
|
|
114
|
+
<Popover.Trigger
|
|
115
|
+
class="flex h-9 cursor-pointer items-center rounded-md border border-surface-3 bg-surface-1 px-3 text-sm font-medium text-ink shadow-sm outline-none hover:bg-surface-2 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ring"
|
|
116
|
+
>
|
|
117
|
+
{triggerLabel}{#if activeCount > 0}
|
|
118
|
+
({activeCount}){/if}
|
|
119
|
+
</Popover.Trigger>
|
|
120
|
+
|
|
121
|
+
<Popover.Content class="w-72 px-0" showArrow={false}>
|
|
122
|
+
<div class="mb-2 flex items-center justify-between px-4">
|
|
123
|
+
<span class="text-sm font-medium text-ink">Filters</span>
|
|
124
|
+
{#if activeCount > 0}
|
|
125
|
+
<button type="button" onclick={clearFilters} class="text-xs text-primary hover:underline">
|
|
126
|
+
Clear all
|
|
127
|
+
</button>
|
|
128
|
+
{/if}
|
|
129
|
+
</div>
|
|
130
|
+
<div class="mb-2 border-b border-surface-3"></div>
|
|
131
|
+
|
|
132
|
+
<ScrollArea.Root class="h-80">
|
|
133
|
+
<ScrollArea.Viewport>
|
|
134
|
+
<ScrollArea.Content>
|
|
135
|
+
{#each filterableColumns as column (column.id)}
|
|
136
|
+
<div class="mb-3">
|
|
137
|
+
<p class="mb-1 text-xs font-medium text-ink">{getColumnLabel(column)}</p>
|
|
138
|
+
|
|
139
|
+
{#if getColumnType(column) === 'number' || getColumnType(column) === 'currency' || getColumnType(column) === 'percent'}
|
|
140
|
+
{@const [facetMin, facetMax] = getFacetedMinMax(column)}
|
|
141
|
+
{@const colFormatOptions = getColumnFormatOptions(column)}
|
|
142
|
+
<div class="flex gap-1.5">
|
|
143
|
+
<div class="min-w-0 flex-1">
|
|
144
|
+
<p class="mb-1 text-xs text-ink-dim">From</p>
|
|
145
|
+
<NumberInput
|
|
146
|
+
value={localNumMin[column.id] ?? facetMin ?? null}
|
|
147
|
+
min={facetMin}
|
|
148
|
+
max={facetMax}
|
|
149
|
+
formatOptions={colFormatOptions}
|
|
150
|
+
onValueChange={({ valueAsNumber }) =>
|
|
151
|
+
handleNumericInput(
|
|
152
|
+
column,
|
|
153
|
+
'min',
|
|
154
|
+
isNaN(valueAsNumber) ? null : valueAsNumber
|
|
155
|
+
)}
|
|
156
|
+
/>
|
|
157
|
+
</div>
|
|
158
|
+
<div class="min-w-0 flex-1">
|
|
159
|
+
<p class="mb-1 text-xs text-ink-dim">To</p>
|
|
160
|
+
<NumberInput
|
|
161
|
+
value={localNumMax[column.id] ?? facetMax ?? null}
|
|
162
|
+
min={facetMin}
|
|
163
|
+
max={facetMax}
|
|
164
|
+
formatOptions={colFormatOptions}
|
|
165
|
+
onValueChange={({ valueAsNumber }) =>
|
|
166
|
+
handleNumericInput(
|
|
167
|
+
column,
|
|
168
|
+
'max',
|
|
169
|
+
isNaN(valueAsNumber) ? null : valueAsNumber
|
|
170
|
+
)}
|
|
171
|
+
/>
|
|
172
|
+
</div>
|
|
173
|
+
</div>
|
|
174
|
+
{:else if getColumnType(column) === 'boolean'}
|
|
175
|
+
{@const boolFilter = column.getFilterValue() as boolean | undefined}
|
|
176
|
+
<div class="flex overflow-hidden rounded border border-border text-xs">
|
|
177
|
+
<button
|
|
178
|
+
type="button"
|
|
179
|
+
onclick={() => column.setFilterValue(undefined)}
|
|
180
|
+
class={cn(
|
|
181
|
+
'flex-1 px-2 py-1',
|
|
182
|
+
boolFilter === undefined
|
|
183
|
+
? 'bg-surface-3 font-medium text-ink'
|
|
184
|
+
: 'text-ink-dim hover:bg-surface-2'
|
|
185
|
+
)}
|
|
186
|
+
>
|
|
187
|
+
All
|
|
188
|
+
</button>
|
|
189
|
+
<button
|
|
190
|
+
type="button"
|
|
191
|
+
onclick={() => column.setFilterValue(boolFilter === true ? undefined : true)}
|
|
192
|
+
class={cn(
|
|
193
|
+
'flex-1 border-x border-border px-2 py-1',
|
|
194
|
+
boolFilter === true
|
|
195
|
+
? 'bg-surface-3 font-medium text-ink'
|
|
196
|
+
: 'text-ink-dim hover:bg-surface-2'
|
|
197
|
+
)}
|
|
198
|
+
>
|
|
199
|
+
Yes
|
|
200
|
+
</button>
|
|
201
|
+
<button
|
|
202
|
+
type="button"
|
|
203
|
+
onclick={() => column.setFilterValue(boolFilter === false ? undefined : false)}
|
|
204
|
+
class={cn(
|
|
205
|
+
'flex-1 px-2 py-1',
|
|
206
|
+
boolFilter === false
|
|
207
|
+
? 'bg-surface-3 font-medium text-ink'
|
|
208
|
+
: 'text-ink-dim hover:bg-surface-2'
|
|
209
|
+
)}
|
|
210
|
+
>
|
|
211
|
+
No
|
|
212
|
+
</button>
|
|
213
|
+
</div>
|
|
214
|
+
{:else if getColumnType(column) === 'select'}
|
|
215
|
+
{@const options = getFacetedValues(column)}
|
|
216
|
+
{@const selected = getSelectValues(column)}
|
|
217
|
+
<div class="flex flex-col gap-0.5">
|
|
218
|
+
{#each options as option (option)}
|
|
219
|
+
<Checkbox
|
|
220
|
+
size="sm"
|
|
221
|
+
label={option}
|
|
222
|
+
class="min-h-7 rounded-sm px-2 hover:bg-surface-2"
|
|
223
|
+
checked={selected.includes(option)}
|
|
224
|
+
onCheckedChange={({ checked }) =>
|
|
225
|
+
handleSelectChange(column, option, checked === true)}
|
|
226
|
+
/>
|
|
227
|
+
{/each}
|
|
228
|
+
</div>
|
|
229
|
+
{:else}
|
|
230
|
+
<Field.Root>
|
|
231
|
+
<Field.Input
|
|
232
|
+
placeholder="Search..."
|
|
233
|
+
value={localText[column.id] ?? ''}
|
|
234
|
+
oninput={(e: Event) =>
|
|
235
|
+
handleTextInput(column, (e.currentTarget as HTMLInputElement).value)}
|
|
236
|
+
/>
|
|
237
|
+
</Field.Root>
|
|
238
|
+
{/if}
|
|
239
|
+
</div>
|
|
240
|
+
{/each}
|
|
241
|
+
</ScrollArea.Content>
|
|
242
|
+
</ScrollArea.Viewport>
|
|
243
|
+
<ScrollArea.Scrollbar orientation="vertical">
|
|
244
|
+
<ScrollArea.Thumb />
|
|
245
|
+
</ScrollArea.Scrollbar>
|
|
246
|
+
<ScrollArea.Corner />
|
|
247
|
+
</ScrollArea.Root>
|
|
248
|
+
</Popover.Content>
|
|
249
|
+
</Popover.Root>
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { RowData } from '@tanstack/svelte-table';
|
|
2
|
+
import type { DataTableInstance } from './create-table';
|
|
3
|
+
declare function $$render<T extends RowData>(): {
|
|
4
|
+
props: {
|
|
5
|
+
table: DataTableInstance<T>;
|
|
6
|
+
triggerLabel?: string;
|
|
7
|
+
};
|
|
8
|
+
exports: {};
|
|
9
|
+
bindings: "";
|
|
10
|
+
slots: {};
|
|
11
|
+
events: {};
|
|
12
|
+
};
|
|
13
|
+
declare class __sveltets_Render<T extends RowData> {
|
|
14
|
+
props(): ReturnType<typeof $$render<T>>['props'];
|
|
15
|
+
events(): ReturnType<typeof $$render<T>>['events'];
|
|
16
|
+
slots(): ReturnType<typeof $$render<T>>['slots'];
|
|
17
|
+
bindings(): "";
|
|
18
|
+
exports(): {};
|
|
19
|
+
}
|
|
20
|
+
interface $$IsomorphicComponent {
|
|
21
|
+
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']>> & {
|
|
22
|
+
$$bindings?: ReturnType<__sveltets_Render<T>['bindings']>;
|
|
23
|
+
} & ReturnType<__sveltets_Render<T>['exports']>;
|
|
24
|
+
<T extends RowData>(internal: unknown, props: ReturnType<__sveltets_Render<T>['props']> & {}): ReturnType<__sveltets_Render<T>['exports']>;
|
|
25
|
+
z_$$bindings?: ReturnType<__sveltets_Render<any>['bindings']>;
|
|
26
|
+
}
|
|
27
|
+
declare const DataTableColumnFilter: $$IsomorphicComponent;
|
|
28
|
+
type DataTableColumnFilter<T extends RowData> = InstanceType<typeof DataTableColumnFilter<T>>;
|
|
29
|
+
export default DataTableColumnFilter;
|
|
@@ -1,20 +1,18 @@
|
|
|
1
|
-
<script lang="ts" generics="
|
|
1
|
+
<script lang="ts" generics="T extends RowData">
|
|
2
|
+
import type { CellData, Column, RowData } from '@tanstack/svelte-table';
|
|
2
3
|
import * as Popover from '../popover';
|
|
3
4
|
import * as ScrollArea from '../scroll-area';
|
|
4
5
|
import Checkbox from '../checkbox/checkbox.svelte';
|
|
5
|
-
import type {
|
|
6
|
-
import type { DataTable, DataTableFeatures, DataTableSelectedState } from './core';
|
|
6
|
+
import type { DataTableFeatures, DataTableInstance } from './create-table';
|
|
7
7
|
|
|
8
8
|
type Props = {
|
|
9
|
-
table:
|
|
9
|
+
table: DataTableInstance<T>;
|
|
10
10
|
triggerLabel?: string;
|
|
11
11
|
};
|
|
12
12
|
|
|
13
13
|
let { table, triggerLabel = 'Columns' }: Props = $props();
|
|
14
14
|
|
|
15
|
-
const columnVisibility = $derived(
|
|
16
|
-
(table.state as unknown as DataTableSelectedState).columnVisibility
|
|
17
|
-
);
|
|
15
|
+
const columnVisibility = $derived(table.store.state.columnVisibility);
|
|
18
16
|
const allColumnsVisible = $derived(table.getIsAllColumnsVisible());
|
|
19
17
|
const someColumnsVisible = $derived(
|
|
20
18
|
table.getAllLeafColumns().some((column) => column.getCanHide() && column.getIsVisible())
|
|
@@ -23,15 +21,12 @@
|
|
|
23
21
|
allColumnsVisible ? true : someColumnsVisible ? 'indeterminate' : false
|
|
24
22
|
);
|
|
25
23
|
|
|
26
|
-
function getColumnLabel(column: Column<DataTableFeatures,
|
|
24
|
+
function getColumnLabel(column: Column<DataTableFeatures, T, CellData>) {
|
|
27
25
|
return typeof column.columnDef.header === 'string' ? column.columnDef.header : column.id;
|
|
28
26
|
}
|
|
29
27
|
|
|
30
|
-
function getColumnIsVisible(
|
|
31
|
-
|
|
32
|
-
visibilityState: unknown
|
|
33
|
-
) {
|
|
34
|
-
void visibilityState;
|
|
28
|
+
function getColumnIsVisible(column: Column<DataTableFeatures, T, CellData>, state: unknown) {
|
|
29
|
+
void state;
|
|
35
30
|
return column.getIsVisible();
|
|
36
31
|
}
|
|
37
32
|
</script>
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import type { RowData } from '@tanstack/svelte-table';
|
|
2
|
-
import type {
|
|
3
|
-
declare function $$render<
|
|
2
|
+
import type { DataTableInstance } from './create-table';
|
|
3
|
+
declare function $$render<T extends RowData>(): {
|
|
4
4
|
props: {
|
|
5
|
-
table:
|
|
5
|
+
table: DataTableInstance<T>;
|
|
6
6
|
triggerLabel?: string;
|
|
7
7
|
};
|
|
8
8
|
exports: {};
|
|
@@ -10,20 +10,20 @@ declare function $$render<TData extends RowData, TSelected = object>(): {
|
|
|
10
10
|
slots: {};
|
|
11
11
|
events: {};
|
|
12
12
|
};
|
|
13
|
-
declare class __sveltets_Render<
|
|
14
|
-
props(): ReturnType<typeof $$render<
|
|
15
|
-
events(): ReturnType<typeof $$render<
|
|
16
|
-
slots(): ReturnType<typeof $$render<
|
|
13
|
+
declare class __sveltets_Render<T extends RowData> {
|
|
14
|
+
props(): ReturnType<typeof $$render<T>>['props'];
|
|
15
|
+
events(): ReturnType<typeof $$render<T>>['events'];
|
|
16
|
+
slots(): ReturnType<typeof $$render<T>>['slots'];
|
|
17
17
|
bindings(): "";
|
|
18
18
|
exports(): {};
|
|
19
19
|
}
|
|
20
20
|
interface $$IsomorphicComponent {
|
|
21
|
-
new <
|
|
22
|
-
$$bindings?: ReturnType<__sveltets_Render<
|
|
23
|
-
} & ReturnType<__sveltets_Render<
|
|
24
|
-
<
|
|
25
|
-
z_$$bindings?: ReturnType<__sveltets_Render<any
|
|
21
|
+
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']>> & {
|
|
22
|
+
$$bindings?: ReturnType<__sveltets_Render<T>['bindings']>;
|
|
23
|
+
} & ReturnType<__sveltets_Render<T>['exports']>;
|
|
24
|
+
<T extends RowData>(internal: unknown, props: ReturnType<__sveltets_Render<T>['props']> & {}): ReturnType<__sveltets_Render<T>['exports']>;
|
|
25
|
+
z_$$bindings?: ReturnType<__sveltets_Render<any>['bindings']>;
|
|
26
26
|
}
|
|
27
27
|
declare const DataTableColumnVisibility: $$IsomorphicComponent;
|
|
28
|
-
type DataTableColumnVisibility<
|
|
28
|
+
type DataTableColumnVisibility<T extends RowData> = InstanceType<typeof DataTableColumnVisibility<T>>;
|
|
29
29
|
export default DataTableColumnVisibility;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
-
import { cn, type ClassValue } from 'tailwind-variants';
|
|
3
|
-
import type { HTMLAttributes } from 'svelte/elements';
|
|
4
2
|
import type { Snippet } from 'svelte';
|
|
3
|
+
import type { HTMLAttributes } from 'svelte/elements';
|
|
4
|
+
import { cn, type ClassValue } from 'tailwind-variants';
|
|
5
5
|
|
|
6
6
|
type Props = Omit<HTMLAttributes<HTMLHeadingElement>, 'class'> & {
|
|
7
7
|
class?: ClassValue;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { type ClassValue } from 'tailwind-variants';
|
|
2
|
-
import type { HTMLAttributes } from 'svelte/elements';
|
|
3
1
|
import type { Snippet } from 'svelte';
|
|
2
|
+
import type { HTMLAttributes } from 'svelte/elements';
|
|
3
|
+
import { type ClassValue } from 'tailwind-variants';
|
|
4
4
|
type Props = Omit<HTMLAttributes<HTMLHeadingElement>, 'class'> & {
|
|
5
5
|
class?: ClassValue;
|
|
6
6
|
children?: Snippet;
|