compote-ui 0.33.1 → 0.35.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/carousel/carousel-item-group.svelte +1 -1
- 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 → data-table-old}/core/create-table.svelte.js +0 -8
- 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/components/scroll-area/scroll-area-viewport.svelte +1 -1
- 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/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
|
@@ -1,126 +1,43 @@
|
|
|
1
|
-
<script lang="ts" generics="
|
|
2
|
-
import { useLocaleContext } from '@ark-ui/svelte/locale';
|
|
1
|
+
<script lang="ts" generics="T extends RowData">
|
|
3
2
|
import { FlexRender } from '@tanstack/svelte-table';
|
|
3
|
+
import type { CellData, Header, RowData } from '@tanstack/svelte-table';
|
|
4
|
+
import type { HTMLAttributes } from 'svelte/elements';
|
|
4
5
|
import { cn, type ClassValue } from 'tailwind-variants';
|
|
5
|
-
import { PhArrowSquareOut, PhCaretDown, PhCaretUp } from '../../icons';
|
|
6
|
+
import { PhArrowSquareOut, PhCaretDown, PhCaretUp, PhCheck, PhX } from '../../icons';
|
|
6
7
|
import Checkbox from '../checkbox/checkbox.svelte';
|
|
7
|
-
import type
|
|
8
|
-
import type {
|
|
9
|
-
import {
|
|
10
|
-
getDataTableCellConfig,
|
|
11
|
-
hasCustomDataTableCell,
|
|
12
|
-
type DataTable,
|
|
13
|
-
type DataTableCell,
|
|
14
|
-
type DataTableCellConfig,
|
|
15
|
-
type DataTableColumnAlign,
|
|
16
|
-
type DataTableColumnMeta,
|
|
17
|
-
type DataTableFeatures
|
|
18
|
-
} from './core';
|
|
8
|
+
import { getColumnId, type DataTableFeatures, type DataTableInstance } from './create-table';
|
|
9
|
+
import type { DataTableColumn, DataTableGroupColumn, DataTableLeafColumn } from './types';
|
|
19
10
|
|
|
20
11
|
type Props = Omit<HTMLAttributes<HTMLDivElement>, 'class'> & {
|
|
21
|
-
table:
|
|
12
|
+
table: DataTableInstance<T>;
|
|
13
|
+
columns: DataTableColumn<T>[];
|
|
14
|
+
caption?: string;
|
|
15
|
+
emptyMessage?: string;
|
|
22
16
|
class?: ClassValue;
|
|
23
|
-
tableClass?: ClassValue;
|
|
24
17
|
};
|
|
25
18
|
|
|
26
|
-
let {
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
const allRowsSelected = $derived(table.getIsAllRowsSelected());
|
|
35
|
-
const someRowsSelected = $derived(table.getIsSomeRowsSelected());
|
|
36
|
-
const allRowsSelectionState = $derived(
|
|
37
|
-
allRowsSelected ? true : someRowsSelected ? 'indeterminate' : false
|
|
38
|
-
);
|
|
39
|
-
const rowSelectionColumnSize = 40;
|
|
40
|
-
const tableSize = $derived(
|
|
41
|
-
table.getTotalSize() + (isRowSelectionEnabled ? rowSelectionColumnSize : 0)
|
|
42
|
-
);
|
|
43
|
-
const isColumnResizing = $derived(table.store.state.columnResizing.isResizingColumn !== false);
|
|
44
|
-
|
|
45
|
-
function formatNumericValue(
|
|
46
|
-
value: unknown,
|
|
47
|
-
options: Intl.NumberFormatOptions & { locale?: string }
|
|
48
|
-
) {
|
|
49
|
-
if (typeof value !== 'number') return formatTextValue(value);
|
|
50
|
-
const { locale: optionLocale, ...numberFormatOptions } = options;
|
|
51
|
-
return new Intl.NumberFormat(optionLocale ?? locale().locale, numberFormatOptions).format(
|
|
52
|
-
value
|
|
53
|
-
);
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
function formatTextValue(value: unknown, fallback = '') {
|
|
57
|
-
if (value === null || value === undefined || value === '') return fallback;
|
|
58
|
-
return String(value);
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
function fractionOptions(options: { fractionDigits?: number }) {
|
|
62
|
-
if (options.fractionDigits === undefined) return {};
|
|
63
|
-
|
|
64
|
-
return {
|
|
65
|
-
minimumFractionDigits: options.fractionDigits,
|
|
66
|
-
maximumFractionDigits: options.fractionDigits
|
|
67
|
-
};
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
function isNumericCellConfig(cellConfig: DataTableCellConfig<TData> | undefined) {
|
|
71
|
-
return (
|
|
72
|
-
cellConfig?.type === 'number' ||
|
|
73
|
-
cellConfig?.type === 'currency' ||
|
|
74
|
-
cellConfig?.type === 'percentage'
|
|
75
|
-
);
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
function getLinkHref<TValue>(
|
|
79
|
-
cell: DataTableCell<TData, TValue>,
|
|
80
|
-
href: string | ((value: TValue, row: TData) => string) | undefined
|
|
81
|
-
) {
|
|
82
|
-
const value = cell.getValue();
|
|
83
|
-
if (!href) return formatTextValue(value);
|
|
84
|
-
return typeof href === 'function' ? href(value, cell.row.original) : href;
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
function getLinkLabel(value: unknown, fallback = 'Open link') {
|
|
88
|
-
return formatTextValue(value, fallback);
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
function getCellMeta<TValue>(cell: DataTableCell<TData, TValue>) {
|
|
92
|
-
return (cell.column.columnDef.meta as DataTableColumnMeta<TData, TValue> | undefined)
|
|
93
|
-
?.dataTable;
|
|
94
|
-
}
|
|
19
|
+
let {
|
|
20
|
+
table,
|
|
21
|
+
columns,
|
|
22
|
+
caption,
|
|
23
|
+
emptyMessage = 'No rows found',
|
|
24
|
+
class: className,
|
|
25
|
+
...rest
|
|
26
|
+
}: Props = $props();
|
|
95
27
|
|
|
96
|
-
function
|
|
97
|
-
return
|
|
28
|
+
function alignClass(align: DataTableColumn<T>['align']) {
|
|
29
|
+
return align === 'right' ? 'text-right' : align === 'center' ? 'text-center' : 'text-left';
|
|
98
30
|
}
|
|
99
31
|
|
|
100
|
-
function
|
|
101
|
-
align
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
32
|
+
function justifyClass(align: DataTableColumn<T>['align']) {
|
|
33
|
+
return align === 'right'
|
|
34
|
+
? 'justify-end'
|
|
35
|
+
: align === 'center'
|
|
36
|
+
? 'justify-center'
|
|
37
|
+
: 'justify-start';
|
|
105
38
|
}
|
|
106
39
|
|
|
107
|
-
function
|
|
108
|
-
return {
|
|
109
|
-
left: 'text-left',
|
|
110
|
-
center: 'text-center',
|
|
111
|
-
right: 'text-right'
|
|
112
|
-
}[align ?? 'left'];
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
function justifyClass(align: DataTableColumnAlign | undefined) {
|
|
116
|
-
return {
|
|
117
|
-
left: 'justify-start',
|
|
118
|
-
center: 'justify-center',
|
|
119
|
-
right: 'justify-end'
|
|
120
|
-
}[align ?? 'left'];
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
function sortButtonDirectionClass(align: DataTableColumnAlign | undefined) {
|
|
40
|
+
function sortButtonDirectionClass(align: DataTableColumn<T>['align']) {
|
|
124
41
|
return align === 'right' ? 'flex-row-reverse' : 'flex-row';
|
|
125
42
|
}
|
|
126
43
|
|
|
@@ -128,19 +45,30 @@
|
|
|
128
45
|
return `width: ${size}px`;
|
|
129
46
|
}
|
|
130
47
|
|
|
48
|
+
function selectionColumnSizeStyle() {
|
|
49
|
+
return 'width: 40px';
|
|
50
|
+
}
|
|
51
|
+
|
|
131
52
|
function tableSizeStyle() {
|
|
132
|
-
return `width: max(100%, ${
|
|
53
|
+
return `width: max(100%, ${table.getTotalSize() + (isRowSelectionEnabled ? 40 : 0)}px)`;
|
|
133
54
|
}
|
|
134
55
|
|
|
135
|
-
function resizeHandleStyle(header: Header<DataTableFeatures,
|
|
56
|
+
function resizeHandleStyle(header: Header<DataTableFeatures, T, CellData>) {
|
|
136
57
|
if (table.options.columnResizeMode !== 'onEnd') return undefined;
|
|
137
58
|
const deltaOffset = table.store.state.columnResizing.deltaOffset;
|
|
138
59
|
if (!header.column.getIsResizing() || deltaOffset === null) return undefined;
|
|
139
60
|
return `transform: translateX(${deltaOffset}px)`;
|
|
140
61
|
}
|
|
141
62
|
|
|
63
|
+
function resizeHandleClass(headerIndex: number, headerCount: number) {
|
|
64
|
+
return cn(
|
|
65
|
+
'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-[""]',
|
|
66
|
+
headerIndex === headerCount - 1 ? 'right-0' : '-right-1'
|
|
67
|
+
);
|
|
68
|
+
}
|
|
69
|
+
|
|
142
70
|
function getHeaderSortDirection(
|
|
143
|
-
header: Header<DataTableFeatures,
|
|
71
|
+
header: Header<DataTableFeatures, T, CellData>,
|
|
144
72
|
sortingState: unknown
|
|
145
73
|
) {
|
|
146
74
|
void sortingState;
|
|
@@ -158,185 +86,280 @@
|
|
|
158
86
|
if (sortDirection === 'desc') return 'descending';
|
|
159
87
|
return 'none';
|
|
160
88
|
}
|
|
89
|
+
|
|
90
|
+
function getAllRowsSelectionState(rowSelection: unknown) {
|
|
91
|
+
void rowSelection;
|
|
92
|
+
const allRowsSelected = table.getIsAllRowsSelected();
|
|
93
|
+
const someRowsSelected = table.getIsSomeRowsSelected();
|
|
94
|
+
return allRowsSelected ? true : someRowsSelected ? 'indeterminate' : false;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function getRowSelectionState(rowSelection: unknown, rowId: string) {
|
|
98
|
+
void rowSelection;
|
|
99
|
+
return table.getRow(rowId).getIsSelected();
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
function getSelectedRowCount(rowSelection: unknown) {
|
|
103
|
+
void rowSelection;
|
|
104
|
+
return table.getSelectedRowModel().rows.length;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function getBooleanCellValue(value: unknown) {
|
|
108
|
+
if (value === true) return true;
|
|
109
|
+
if (value === false) return false;
|
|
110
|
+
return undefined;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
function getUrlCellValue(value: unknown) {
|
|
114
|
+
if (typeof value !== 'string' || value.trim() === '') return undefined;
|
|
115
|
+
return value;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
function openUrlCell(value: string) {
|
|
119
|
+
window.open(value, '_blank', 'noopener,noreferrer');
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
function isGroupColumn(column: DataTableColumn<T>): column is DataTableGroupColumn<T> {
|
|
123
|
+
return Array.isArray(column.columns);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
function isLeafColumn(column: DataTableColumn<T>): column is DataTableLeafColumn<T> {
|
|
127
|
+
return !isGroupColumn(column);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
function findColumnById(
|
|
131
|
+
columnId: string,
|
|
132
|
+
candidates: DataTableColumn<T>[]
|
|
133
|
+
): DataTableColumn<T> | undefined {
|
|
134
|
+
for (const column of candidates) {
|
|
135
|
+
if (isGroupColumn(column)) {
|
|
136
|
+
if ((column.id ?? column.header) === columnId) return column;
|
|
137
|
+
|
|
138
|
+
const found = findColumnById(columnId, column.columns);
|
|
139
|
+
if (found) return found;
|
|
140
|
+
|
|
141
|
+
continue;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
if (isLeafColumn(column) && getColumnId(column) === columnId) return column;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
const tableStateKey = $derived(JSON.stringify(table.store.state));
|
|
149
|
+
function trackTableState() {
|
|
150
|
+
return tableStateKey;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
const rowModel = $derived.by(() => {
|
|
154
|
+
trackTableState();
|
|
155
|
+
return table.getRowModel();
|
|
156
|
+
});
|
|
157
|
+
const headerGroups = $derived.by(() => {
|
|
158
|
+
trackTableState();
|
|
159
|
+
return table.getHeaderGroups();
|
|
160
|
+
});
|
|
161
|
+
const visibleLeafColumns = $derived.by(() => {
|
|
162
|
+
trackTableState();
|
|
163
|
+
return table.getVisibleLeafColumns();
|
|
164
|
+
});
|
|
165
|
+
const visibleColumnCount = $derived(visibleLeafColumns.length);
|
|
166
|
+
const isRowSelectionEnabled = $derived(Boolean(table.options.enableRowSelection));
|
|
167
|
+
const isMultiRowSelectionEnabled = $derived(table.options.enableMultiRowSelection !== false);
|
|
168
|
+
const tableColumnCount = $derived(visibleColumnCount + (isRowSelectionEnabled ? 1 : 0));
|
|
169
|
+
const renderedColumnCount = $derived(tableColumnCount + 1);
|
|
170
|
+
const headerGroupCount = $derived(headerGroups.length);
|
|
171
|
+
const allRowsSelectionState = $derived(getAllRowsSelectionState(table.store.state.rowSelection));
|
|
172
|
+
const selectedRowCount = $derived(getSelectedRowCount(table.store.state.rowSelection));
|
|
173
|
+
const isColumnResizing = $derived(table.store.state.columnResizing.isResizingColumn !== false);
|
|
161
174
|
</script>
|
|
162
175
|
|
|
163
176
|
<div
|
|
164
|
-
class={cn(
|
|
177
|
+
class={cn(
|
|
178
|
+
'flex max-h-full min-h-0 flex-col overflow-hidden rounded-lg border border-surface-3 bg-surface-1',
|
|
179
|
+
className
|
|
180
|
+
)}
|
|
165
181
|
{...rest}
|
|
166
182
|
>
|
|
167
183
|
{#if isColumnResizing}
|
|
168
184
|
<div aria-hidden="true" class="fixed inset-0 z-50 cursor-col-resize select-none"></div>
|
|
169
185
|
{/if}
|
|
170
186
|
|
|
171
|
-
<
|
|
172
|
-
<
|
|
173
|
-
|
|
174
|
-
|
|
187
|
+
<div class="min-h-0 flex-1 overflow-auto">
|
|
188
|
+
<table class="table-fixed border-collapse text-sm" style={tableSizeStyle()}>
|
|
189
|
+
<colgroup>
|
|
190
|
+
{#if isRowSelectionEnabled}
|
|
191
|
+
<col style={selectionColumnSizeStyle()} />
|
|
192
|
+
{/if}
|
|
193
|
+
{#each visibleLeafColumns as column (column.id)}
|
|
194
|
+
<col style={columnSizeStyle(column.getSize())} />
|
|
195
|
+
{/each}
|
|
196
|
+
<col />
|
|
197
|
+
</colgroup>
|
|
198
|
+
{#if caption}
|
|
199
|
+
<caption class="sr-only">{caption}</caption>
|
|
175
200
|
{/if}
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
201
|
+
<thead class="sticky top-0 z-20 bg-surface-2 text-left text-ink-dim">
|
|
202
|
+
{#each headerGroups as headerGroup, headerGroupIndex (headerGroup.id)}
|
|
203
|
+
{@const visibleHeaders = headerGroup.headers.filter((header) => header.colSpan > 0)}
|
|
204
|
+
<tr>
|
|
205
|
+
{#if isRowSelectionEnabled && headerGroupIndex === 0}
|
|
206
|
+
<th
|
|
207
|
+
class="border-b border-surface-3 bg-surface-2 px-3 py-2 text-center align-middle font-medium"
|
|
208
|
+
rowspan={headerGroupCount}
|
|
209
|
+
>
|
|
210
|
+
{#if isMultiRowSelectionEnabled}
|
|
211
|
+
<Checkbox
|
|
212
|
+
size="sm"
|
|
213
|
+
aria-label="Select all rows"
|
|
214
|
+
class="mx-auto size-4"
|
|
215
|
+
checked={allRowsSelectionState}
|
|
216
|
+
onCheckedChange={({ checked }) => table.toggleAllRowsSelected(checked === true)}
|
|
217
|
+
/>
|
|
218
|
+
{/if}
|
|
219
|
+
</th>
|
|
220
|
+
{/if}
|
|
221
|
+
{#each visibleHeaders as header, headerIndex (header.id)}
|
|
222
|
+
{@const columnDef = findColumnById(header.column.id, columns)}
|
|
223
|
+
{@const sortDirection = getHeaderSortDirection(header, table.store.state.sorting)}
|
|
224
|
+
<th
|
|
225
|
+
class={cn(
|
|
226
|
+
'relative border-b border-surface-3 bg-surface-2 px-3 py-2 font-medium',
|
|
227
|
+
alignClass(columnDef?.align)
|
|
228
|
+
)}
|
|
229
|
+
colspan={header.colSpan}
|
|
230
|
+
aria-sort={header.column.getCanSort()
|
|
231
|
+
? getHeaderAriaSort(sortDirection)
|
|
232
|
+
: undefined}
|
|
233
|
+
>
|
|
234
|
+
{#if !header.isPlaceholder}
|
|
235
|
+
{#if header.column.getCanSort()}
|
|
236
|
+
<button
|
|
237
|
+
type="button"
|
|
238
|
+
class={cn(
|
|
239
|
+
'inline-flex max-w-full items-center gap-1 rounded-sm outline-none hover:text-ink data-focus-visible:outline-2 data-focus-visible:outline-offset-2 data-focus-visible:outline-ring',
|
|
240
|
+
justifyClass(columnDef?.align),
|
|
241
|
+
sortButtonDirectionClass(columnDef?.align)
|
|
242
|
+
)}
|
|
243
|
+
aria-label={`${getHeaderSortLabel(sortDirection)}. Toggle sorting.`}
|
|
244
|
+
onclick={header.column.getToggleSortingHandler()}
|
|
245
|
+
>
|
|
246
|
+
<span class="min-w-0 truncate">
|
|
247
|
+
<FlexRender {header} />
|
|
248
|
+
</span>
|
|
249
|
+
<span
|
|
250
|
+
class="inline-flex size-3.5 shrink-0 items-center justify-center text-ink-dim"
|
|
251
|
+
>
|
|
252
|
+
{#if sortDirection === 'asc'}
|
|
253
|
+
<PhCaretUp class="size-3.5" />
|
|
254
|
+
{:else if sortDirection === 'desc'}
|
|
255
|
+
<PhCaretDown class="size-3.5" />
|
|
256
|
+
{/if}
|
|
257
|
+
</span>
|
|
258
|
+
</button>
|
|
259
|
+
{:else}
|
|
260
|
+
<FlexRender {header} />
|
|
261
|
+
{/if}
|
|
262
|
+
{/if}
|
|
263
|
+
{#if header.column.getCanResize()}
|
|
264
|
+
<div
|
|
265
|
+
aria-hidden="true"
|
|
266
|
+
class={resizeHandleClass(headerIndex, visibleHeaders.length)}
|
|
267
|
+
style={resizeHandleStyle(header)}
|
|
268
|
+
ondblclick={() => header.column.resetSize()}
|
|
269
|
+
onmousedown={header.getResizeHandler()}
|
|
270
|
+
ontouchstart={header.getResizeHandler()}
|
|
271
|
+
></div>
|
|
272
|
+
{/if}
|
|
273
|
+
</th>
|
|
274
|
+
{/each}
|
|
275
|
+
<th aria-hidden="true" class="border-b border-surface-3 bg-surface-2 p-0"></th>
|
|
276
|
+
</tr>
|
|
277
|
+
{/each}
|
|
278
|
+
</thead>
|
|
279
|
+
<tbody>
|
|
280
|
+
{#each rowModel.rows as row (row.id)}
|
|
281
|
+
{@const rowSelected = getRowSelectionState(table.store.state.rowSelection, row.id)}
|
|
282
|
+
<tr
|
|
283
|
+
class={cn(
|
|
284
|
+
'border-b border-surface-3 last:border-b-0 hover:bg-well/60',
|
|
285
|
+
rowSelected && 'bg-well/60'
|
|
286
|
+
)}
|
|
287
|
+
>
|
|
288
|
+
{#if isRowSelectionEnabled}
|
|
289
|
+
<td class="px-3 py-2 text-center align-middle">
|
|
189
290
|
<Checkbox
|
|
190
291
|
size="sm"
|
|
191
|
-
aria-label="Select
|
|
292
|
+
aria-label="Select row"
|
|
192
293
|
class="mx-auto size-4"
|
|
193
|
-
checked={
|
|
194
|
-
|
|
294
|
+
checked={rowSelected}
|
|
295
|
+
disabled={!row.getCanSelect()}
|
|
296
|
+
onCheckedChange={({ checked }) => row.toggleSelected(checked === true)}
|
|
195
297
|
/>
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
>
|
|
211
|
-
{#if !header.isPlaceholder}
|
|
212
|
-
{#if header.column.getCanSort()}
|
|
213
|
-
<button
|
|
214
|
-
type="button"
|
|
215
|
-
class={cn(
|
|
216
|
-
'inline-flex max-w-full items-center gap-1 rounded-sm outline-none hover:text-ink data-focus-visible:outline-2 data-focus-visible:outline-offset-2 data-focus-visible:outline-ring',
|
|
217
|
-
justifyClass(headerAlign),
|
|
218
|
-
sortButtonDirectionClass(headerAlign)
|
|
219
|
-
)}
|
|
220
|
-
aria-label={`${getHeaderSortLabel(sortDirection)}. Toggle sorting.`}
|
|
221
|
-
onclick={header.column.getToggleSortingHandler()}
|
|
222
|
-
>
|
|
223
|
-
<span class="min-w-0 truncate">
|
|
224
|
-
<FlexRender {header} />
|
|
298
|
+
</td>
|
|
299
|
+
{/if}
|
|
300
|
+
{#each row.getVisibleCells() as cell (cell.id)}
|
|
301
|
+
{@const columnDef = findColumnById(cell.column.id, columns)}
|
|
302
|
+
<td class={cn('px-3 py-2 text-ink-dim', alignClass(columnDef?.align))}>
|
|
303
|
+
{#if columnDef?.type === 'boolean'}
|
|
304
|
+
{@const value = getBooleanCellValue(cell.getValue())}
|
|
305
|
+
{#if value === true}
|
|
306
|
+
<span
|
|
307
|
+
class="inline-flex size-5 items-center justify-center text-success"
|
|
308
|
+
role="img"
|
|
309
|
+
aria-label="Yes"
|
|
310
|
+
>
|
|
311
|
+
<PhCheck class="size-4" />
|
|
225
312
|
</span>
|
|
313
|
+
{:else if value === false}
|
|
226
314
|
<span
|
|
227
|
-
class="inline-flex size-
|
|
315
|
+
class="inline-flex size-5 items-center justify-center text-danger"
|
|
316
|
+
role="img"
|
|
317
|
+
aria-label="No"
|
|
228
318
|
>
|
|
229
|
-
|
|
230
|
-
<PhCaretUp class="size-3.5" />
|
|
231
|
-
{:else if sortDirection === 'desc'}
|
|
232
|
-
<PhCaretDown class="size-3.5" />
|
|
233
|
-
{/if}
|
|
319
|
+
<PhX class="size-4" />
|
|
234
320
|
</span>
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
{
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
onmousedown={header.getResizeHandler()}
|
|
249
|
-
ontouchstart={header.getResizeHandler()}
|
|
250
|
-
></div>
|
|
251
|
-
{/if}
|
|
252
|
-
</th>
|
|
253
|
-
{/each}
|
|
254
|
-
</tr>
|
|
255
|
-
{/each}
|
|
256
|
-
</thead>
|
|
257
|
-
<tbody>
|
|
258
|
-
{#each table.getRowModel().rows as row (row.id)}
|
|
259
|
-
<tr class="border-b border-surface-3 last:border-b-0">
|
|
260
|
-
{#if isRowSelectionEnabled}
|
|
261
|
-
<td class="px-3 py-2 text-center align-middle">
|
|
262
|
-
<Checkbox
|
|
263
|
-
size="sm"
|
|
264
|
-
aria-label="Select row"
|
|
265
|
-
class="mx-auto size-4"
|
|
266
|
-
checked={row.getIsSelected()}
|
|
267
|
-
disabled={!row.getCanSelect()}
|
|
268
|
-
onCheckedChange={({ checked }) => row.toggleSelected(checked === true)}
|
|
269
|
-
/>
|
|
270
|
-
</td>
|
|
271
|
-
{/if}
|
|
272
|
-
{#each row.getVisibleCells() as cell (cell.id)}
|
|
273
|
-
{@const cellMeta = getCellMeta(cell)}
|
|
274
|
-
{@const cellConfig = getDataTableCellConfig(cell)}
|
|
275
|
-
{@const cellAlign = getColumnAlign(cellMeta?.align, cellConfig)}
|
|
276
|
-
<td
|
|
277
|
-
class={cn(
|
|
278
|
-
'px-3 py-2',
|
|
279
|
-
alignClass(cellAlign),
|
|
280
|
-
isNumericCellConfig(cellConfig) && 'tabular-nums'
|
|
281
|
-
)}
|
|
282
|
-
>
|
|
283
|
-
{#if cellConfig && !hasCustomDataTableCell(cell)}
|
|
284
|
-
{@const value = cell.getValue()}
|
|
285
|
-
{#if cellConfig.type === 'number'}
|
|
286
|
-
{formatNumericValue(value, {
|
|
287
|
-
...cellConfig,
|
|
288
|
-
...fractionOptions(cellConfig)
|
|
289
|
-
})}
|
|
290
|
-
{:else if cellConfig.type === 'currency'}
|
|
291
|
-
{formatNumericValue(value, {
|
|
292
|
-
...cellConfig,
|
|
293
|
-
...fractionOptions(cellConfig),
|
|
294
|
-
style: 'currency'
|
|
295
|
-
})}
|
|
296
|
-
{:else if cellConfig.type === 'percentage'}
|
|
297
|
-
{formatNumericValue(value, {
|
|
298
|
-
...cellConfig,
|
|
299
|
-
...fractionOptions(cellConfig),
|
|
300
|
-
style: 'percent'
|
|
301
|
-
})}
|
|
302
|
-
{:else if cellConfig.type === 'boolean'}
|
|
303
|
-
{value === true
|
|
304
|
-
? (cellConfig.trueLabel ?? 'Yes')
|
|
305
|
-
: value === false
|
|
306
|
-
? (cellConfig.falseLabel ?? 'No')
|
|
307
|
-
: (cellConfig.nullLabel ?? '')}
|
|
308
|
-
{:else if cellConfig.type === 'link'}
|
|
309
|
-
{@const href = getLinkHref(cell, cellConfig.href)}
|
|
310
|
-
{#if href}
|
|
311
|
-
<a
|
|
312
|
-
class="inline-flex size-7 items-center justify-center rounded-md text-primary hover:bg-surface-2"
|
|
313
|
-
{href}
|
|
314
|
-
target={cellConfig.target ?? '_blank'}
|
|
315
|
-
rel="external noreferrer"
|
|
316
|
-
aria-label={getLinkLabel(value, cellConfig.fallback)}
|
|
317
|
-
title={getLinkLabel(value, cellConfig.fallback)}
|
|
321
|
+
{:else}
|
|
322
|
+
-
|
|
323
|
+
{/if}
|
|
324
|
+
{:else if columnDef?.type === 'url'}
|
|
325
|
+
{@const value = getUrlCellValue(cell.getValue())}
|
|
326
|
+
{#if value}
|
|
327
|
+
<button
|
|
328
|
+
type="button"
|
|
329
|
+
class={cn(
|
|
330
|
+
'inline-flex max-w-full items-center gap-1.5 rounded-sm font-medium text-ink underline decoration-border decoration-dotted underline-offset-4 outline-none hover:text-primary focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ring',
|
|
331
|
+
justifyClass(columnDef.align)
|
|
332
|
+
)}
|
|
333
|
+
onclick={() => openUrlCell(value)}
|
|
318
334
|
>
|
|
319
|
-
<PhArrowSquareOut class="size-
|
|
320
|
-
</
|
|
335
|
+
<PhArrowSquareOut class="size-3.5 shrink-0" />
|
|
336
|
+
</button>
|
|
321
337
|
{:else}
|
|
322
|
-
|
|
338
|
+
-
|
|
323
339
|
{/if}
|
|
324
340
|
{:else}
|
|
325
|
-
{
|
|
341
|
+
<FlexRender {cell} />
|
|
326
342
|
{/if}
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
343
|
+
</td>
|
|
344
|
+
{/each}
|
|
345
|
+
<td aria-hidden="true" class="p-0"></td>
|
|
346
|
+
</tr>
|
|
347
|
+
{:else}
|
|
348
|
+
<tr>
|
|
349
|
+
<td class="px-3 py-10 text-center text-sm text-ink-dim" colspan={renderedColumnCount}>
|
|
350
|
+
{emptyMessage}
|
|
330
351
|
</td>
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
{
|
|
340
|
-
|
|
341
|
-
|
|
352
|
+
</tr>
|
|
353
|
+
{/each}
|
|
354
|
+
</tbody>
|
|
355
|
+
</table>
|
|
356
|
+
</div>
|
|
357
|
+
|
|
358
|
+
<div class="shrink-0 border-t border-surface-3 bg-surface-2 px-3 py-2 text-sm text-ink-dim">
|
|
359
|
+
{#if isRowSelectionEnabled}
|
|
360
|
+
{selectedRowCount} of {rowModel.rows.length} rows selected
|
|
361
|
+
{:else}
|
|
362
|
+
{rowModel.rows.length} rows
|
|
363
|
+
{/if}
|
|
364
|
+
</div>
|
|
342
365
|
</div>
|