@dxos/react-ui-table 0.3.7 → 0.3.8-main.0694b57
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/lib/browser/index.mjs +546 -475
- package/dist/lib/browser/index.mjs.map +4 -4
- package/dist/lib/browser/meta.json +1 -1
- package/dist/types/src/components/Cell/Cell.d.ts +12 -0
- package/dist/types/src/components/Cell/Cell.d.ts.map +1 -0
- package/dist/types/src/components/Cell/CellCombobox.d.ts +12 -0
- package/dist/types/src/components/Cell/CellCombobox.d.ts.map +1 -0
- package/dist/types/src/components/Cell/index.d.ts +2 -0
- package/dist/types/src/components/Cell/index.d.ts.map +1 -0
- package/dist/types/src/components/Table/Table.d.ts +3 -0
- package/dist/types/src/components/Table/Table.d.ts.map +1 -0
- package/dist/types/src/components/{Table.stories.d.ts → Table/Table.stories.d.ts} +4 -7
- package/dist/types/src/components/Table/Table.stories.d.ts.map +1 -0
- package/dist/types/src/components/Table/TableBody.d.ts +11 -0
- package/dist/types/src/components/Table/TableBody.d.ts.map +1 -0
- package/dist/types/src/components/Table/TableContext.d.ts +14 -0
- package/dist/types/src/components/Table/TableContext.d.ts.map +1 -0
- package/dist/types/src/components/Table/TableFooter.d.ts +7 -0
- package/dist/types/src/components/Table/TableFooter.d.ts.map +1 -0
- package/dist/types/src/components/Table/TableHead.d.ts +8 -0
- package/dist/types/src/components/Table/TableHead.d.ts.map +1 -0
- package/dist/types/src/components/Table/index.d.ts +3 -0
- package/dist/types/src/components/Table/index.d.ts.map +1 -0
- package/dist/types/src/components/Table/props.d.ts +37 -0
- package/dist/types/src/components/Table/props.d.ts.map +1 -0
- package/dist/types/src/components/index.d.ts +1 -0
- package/dist/types/src/components/index.d.ts.map +1 -1
- package/dist/types/src/helpers.d.ts +20 -14
- package/dist/types/src/helpers.d.ts.map +1 -1
- package/dist/types/src/theme.d.ts +32 -33
- package/dist/types/src/theme.d.ts.map +1 -1
- package/dist/types/src/translations.d.ts +10 -0
- package/dist/types/src/translations.d.ts.map +1 -0
- package/dist/types/src/types.d.ts +13 -10
- package/dist/types/src/types.d.ts.map +1 -1
- package/package.json +12 -10
- package/src/components/Cell/Cell.tsx +43 -0
- package/src/components/Cell/CellCombobox.tsx +97 -0
- package/src/components/Cell/index.ts +5 -0
- package/src/components/{Table.stories.tsx → Table/Table.stories.tsx} +51 -50
- package/src/components/Table/Table.tsx +179 -0
- package/src/components/Table/TableBody.tsx +69 -0
- package/src/components/Table/TableContext.tsx +28 -0
- package/src/components/Table/TableFooter.tsx +42 -0
- package/src/components/Table/TableHead.tsx +79 -0
- package/src/components/Table/index.ts +6 -0
- package/src/components/Table/props.ts +53 -0
- package/src/components/index.ts +1 -0
- package/src/helpers.tsx +250 -282
- package/src/theme.ts +95 -63
- package/src/translations.ts +15 -0
- package/src/types.ts +15 -10
- package/dist/types/src/components/Table.d.ts +0 -29
- package/dist/types/src/components/Table.d.ts.map +0 -1
- package/dist/types/src/components/Table.stories.d.ts.map +0 -1
- package/src/components/Table.tsx +0 -551
package/src/components/Table.tsx
DELETED
|
@@ -1,551 +0,0 @@
|
|
|
1
|
-
//
|
|
2
|
-
// Copyright 2023 DXOS.org
|
|
3
|
-
//
|
|
4
|
-
|
|
5
|
-
import {
|
|
6
|
-
flexRender,
|
|
7
|
-
getCoreRowModel,
|
|
8
|
-
getGroupedRowModel,
|
|
9
|
-
useReactTable,
|
|
10
|
-
type ColumnSizingInfoState,
|
|
11
|
-
type ColumnSizingState,
|
|
12
|
-
type GroupingState,
|
|
13
|
-
type HeaderGroup,
|
|
14
|
-
type Row,
|
|
15
|
-
type RowData,
|
|
16
|
-
type RowSelectionState,
|
|
17
|
-
type TableState,
|
|
18
|
-
type VisibilityState,
|
|
19
|
-
} from '@tanstack/react-table';
|
|
20
|
-
import React, { Fragment, useEffect, useRef, useState } from 'react';
|
|
21
|
-
|
|
22
|
-
import { debounce } from '@dxos/async';
|
|
23
|
-
import { groupBorder, inputSurface, mx } from '@dxos/react-ui-theme';
|
|
24
|
-
|
|
25
|
-
import { defaultTableSlots, type TableSlots } from '../theme';
|
|
26
|
-
import { type TableColumnDef, type KeyValue } from '../types';
|
|
27
|
-
|
|
28
|
-
// TODO(burdon): Sort/filter.
|
|
29
|
-
// TODO(burdon): Drag-and-drop.
|
|
30
|
-
// TODO(burdon): Virtual (e.g., log panel).
|
|
31
|
-
|
|
32
|
-
export type TableSelection<TData extends RowData> = {
|
|
33
|
-
// Controlled if undefined (by default).
|
|
34
|
-
select?: 'single' | 'single-toggle' | 'multiple' | 'multiple-toggle' | undefined;
|
|
35
|
-
selected?: TData[];
|
|
36
|
-
onSelectedChange?: (selected: TData[] | undefined) => void;
|
|
37
|
-
};
|
|
38
|
-
|
|
39
|
-
/**
|
|
40
|
-
* Util to update the selection based on the mode.
|
|
41
|
-
*/
|
|
42
|
-
export const updateSelection = (
|
|
43
|
-
selectionState: RowSelectionState,
|
|
44
|
-
id: string,
|
|
45
|
-
selection: TableSelection<any>['select'],
|
|
46
|
-
): RowSelectionState => {
|
|
47
|
-
switch (selection) {
|
|
48
|
-
case 'single': {
|
|
49
|
-
return {
|
|
50
|
-
[id]: true,
|
|
51
|
-
};
|
|
52
|
-
}
|
|
53
|
-
case 'single-toggle': {
|
|
54
|
-
return selectionState[id]
|
|
55
|
-
? {}
|
|
56
|
-
: {
|
|
57
|
-
[id]: true,
|
|
58
|
-
};
|
|
59
|
-
}
|
|
60
|
-
case 'multiple': {
|
|
61
|
-
return {
|
|
62
|
-
[id]: true,
|
|
63
|
-
...selectionState,
|
|
64
|
-
};
|
|
65
|
-
}
|
|
66
|
-
case 'multiple-toggle': {
|
|
67
|
-
if (selectionState[id]) {
|
|
68
|
-
const newSelectionState = { ...selectionState };
|
|
69
|
-
delete newSelectionState[id];
|
|
70
|
-
return newSelectionState;
|
|
71
|
-
} else {
|
|
72
|
-
return {
|
|
73
|
-
[id]: true,
|
|
74
|
-
...selectionState,
|
|
75
|
-
};
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
return selectionState;
|
|
81
|
-
};
|
|
82
|
-
|
|
83
|
-
export type TableProps<TData extends RowData> = {
|
|
84
|
-
keyAccessor?: KeyValue<TData>;
|
|
85
|
-
data?: TData[];
|
|
86
|
-
columns?: TableColumnDef<TData>[];
|
|
87
|
-
columnVisibility?: VisibilityState;
|
|
88
|
-
onColumnResize?: (state: Record<string, number>) => void;
|
|
89
|
-
grouping?: string[];
|
|
90
|
-
header?: boolean;
|
|
91
|
-
footer?: boolean;
|
|
92
|
-
border?: boolean;
|
|
93
|
-
fullWidth?: boolean;
|
|
94
|
-
pinToBottom?: boolean;
|
|
95
|
-
slots?: TableSlots;
|
|
96
|
-
debug?: boolean;
|
|
97
|
-
} & TableSelection<TData>;
|
|
98
|
-
|
|
99
|
-
export const Table = <TData extends RowData>({ slots = defaultTableSlots, ...props }: TableProps<TData>) => {
|
|
100
|
-
const {
|
|
101
|
-
data = [],
|
|
102
|
-
columns = [],
|
|
103
|
-
onColumnResize,
|
|
104
|
-
columnVisibility,
|
|
105
|
-
header = true,
|
|
106
|
-
footer,
|
|
107
|
-
select,
|
|
108
|
-
selected,
|
|
109
|
-
onSelectedChange,
|
|
110
|
-
fullWidth,
|
|
111
|
-
pinToBottom,
|
|
112
|
-
debug,
|
|
113
|
-
} = props;
|
|
114
|
-
|
|
115
|
-
// Update controlled selection.
|
|
116
|
-
// https://tanstack.com/table/v8/docs/api/features/row-selection
|
|
117
|
-
const [focus, setFocus] = useState<string>();
|
|
118
|
-
const [rowSelection, setRowSelection] = useState<RowSelectionState>({});
|
|
119
|
-
useEffect(() => {
|
|
120
|
-
setRowSelection(
|
|
121
|
-
selected?.reduce((selectionState: RowSelectionState, selected) => {
|
|
122
|
-
const row = table.getRowModel().rows.find((row) => row.original === selected);
|
|
123
|
-
if (row) {
|
|
124
|
-
selectionState[row.id] = true;
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
return selectionState;
|
|
128
|
-
}, {}) ?? {},
|
|
129
|
-
);
|
|
130
|
-
}, [select, selected]);
|
|
131
|
-
|
|
132
|
-
// Resizing.
|
|
133
|
-
const [columnSizingInfo, setColumnSizingInfo] = useState<ColumnSizingInfoState>({} as ColumnSizingInfoState);
|
|
134
|
-
const onColumnResizeDebounced = debounce<ColumnSizingState>((info) => onColumnResize?.(info), 500);
|
|
135
|
-
useEffect(() => {
|
|
136
|
-
if (columnSizingInfo.columnSizingStart?.length === 0) {
|
|
137
|
-
onColumnResizeDebounced(table.getState().columnSizing);
|
|
138
|
-
}
|
|
139
|
-
}, [columnSizingInfo]);
|
|
140
|
-
|
|
141
|
-
const [grouping, setGrouping] = useState<GroupingState>(props.grouping ?? []);
|
|
142
|
-
useEffect(() => setGrouping(props.grouping ?? []), [props.grouping]);
|
|
143
|
-
|
|
144
|
-
//
|
|
145
|
-
// Update table state.
|
|
146
|
-
// https://tanstack.com/table/v8/docs/api/core/table
|
|
147
|
-
//
|
|
148
|
-
const table = useReactTable({
|
|
149
|
-
data,
|
|
150
|
-
columns,
|
|
151
|
-
defaultColumn: {
|
|
152
|
-
size: 400, // Required in order remove default width.
|
|
153
|
-
maxSize: 800,
|
|
154
|
-
},
|
|
155
|
-
getCoreRowModel: getCoreRowModel(),
|
|
156
|
-
meta: {},
|
|
157
|
-
|
|
158
|
-
// TODO(burdon): Pagination.
|
|
159
|
-
// TODO(burdon): Sorting.
|
|
160
|
-
// TODO(burdon): Filtering.
|
|
161
|
-
state: {
|
|
162
|
-
columnVisibility,
|
|
163
|
-
columnSizingInfo,
|
|
164
|
-
rowSelection,
|
|
165
|
-
grouping,
|
|
166
|
-
},
|
|
167
|
-
|
|
168
|
-
// Grouping.
|
|
169
|
-
getGroupedRowModel: getGroupedRowModel(),
|
|
170
|
-
onGroupingChange: setGrouping,
|
|
171
|
-
|
|
172
|
-
// Selection.
|
|
173
|
-
enableRowSelection: select === 'single' || select === 'single-toggle',
|
|
174
|
-
enableMultiRowSelection: select === 'multiple' || select === 'multiple-toggle',
|
|
175
|
-
onRowSelectionChange: (rows) => {
|
|
176
|
-
setRowSelection(rows);
|
|
177
|
-
},
|
|
178
|
-
|
|
179
|
-
// Resize columns.
|
|
180
|
-
// TODO(burdon): Drag to re-order columns.
|
|
181
|
-
columnResizeMode: 'onChange',
|
|
182
|
-
enableColumnResizing: true,
|
|
183
|
-
onColumnSizingInfoChange: setColumnSizingInfo,
|
|
184
|
-
|
|
185
|
-
debugTable: debug,
|
|
186
|
-
});
|
|
187
|
-
|
|
188
|
-
// TODO(burdon): Add flex if not resizable.
|
|
189
|
-
// Create additional expansion column if all columns have fixed width.
|
|
190
|
-
const expand = false; // columns.map((column) => column.size).filter(Boolean).length === columns?.length;
|
|
191
|
-
|
|
192
|
-
// Pin scrollbar to bottom.
|
|
193
|
-
const containerRef = usePinToBottom(data, pinToBottom);
|
|
194
|
-
|
|
195
|
-
const handleSelect = (row: Row<TData>) => {
|
|
196
|
-
if (select) {
|
|
197
|
-
// Uncontrolled.
|
|
198
|
-
setRowSelection((selectionState: RowSelectionState) => {
|
|
199
|
-
const newSelectionState = updateSelection(selectionState, row.id, select);
|
|
200
|
-
if (onSelectedChange) {
|
|
201
|
-
const rows: TData[] = Object.keys(newSelectionState).map((id) => table.getRowModel().rowsById[id].original);
|
|
202
|
-
onSelectedChange(rows);
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
return newSelectionState;
|
|
206
|
-
});
|
|
207
|
-
} else {
|
|
208
|
-
// Controlled.
|
|
209
|
-
onSelectedChange?.([row.original]);
|
|
210
|
-
}
|
|
211
|
-
};
|
|
212
|
-
|
|
213
|
-
// TODO(burdon): Use radix ScrollArea.
|
|
214
|
-
// https://www.radix-ui.com/primitives/docs/components/scroll-area
|
|
215
|
-
return (
|
|
216
|
-
<div ref={containerRef} className={mx('grow overflow-auto', slots?.root?.className)}>
|
|
217
|
-
<table
|
|
218
|
-
// Styles:
|
|
219
|
-
// table-fixed: Prevents fixed sized columns from shrinking.
|
|
220
|
-
className={mx(!fullWidth && 'table-fixed', inputSurface, slots?.table?.className)}
|
|
221
|
-
style={{
|
|
222
|
-
width: fullWidth ? '100%' : table.getTotalSize(),
|
|
223
|
-
}}
|
|
224
|
-
>
|
|
225
|
-
{/* Head */}
|
|
226
|
-
<TableHead
|
|
227
|
-
{...props}
|
|
228
|
-
slots={slots}
|
|
229
|
-
header={header}
|
|
230
|
-
state={table.getState()}
|
|
231
|
-
headers={table.getHeaderGroups()}
|
|
232
|
-
/>
|
|
233
|
-
|
|
234
|
-
{/* Rows */}
|
|
235
|
-
{grouping.length === 0 && (
|
|
236
|
-
<TableBody
|
|
237
|
-
{...props}
|
|
238
|
-
slots={slots}
|
|
239
|
-
rowSelection={rowSelection}
|
|
240
|
-
expand={expand}
|
|
241
|
-
focus={focus}
|
|
242
|
-
onFocus={setFocus}
|
|
243
|
-
onSelect={handleSelect}
|
|
244
|
-
rows={table.getRowModel().rows}
|
|
245
|
-
/>
|
|
246
|
-
)}
|
|
247
|
-
|
|
248
|
-
{/* Groups */}
|
|
249
|
-
{grouping.length !== 0 &&
|
|
250
|
-
table.getGroupedRowModel().rows.map((row, i) => {
|
|
251
|
-
return (
|
|
252
|
-
<Fragment key={i}>
|
|
253
|
-
{/* TODO(burdon): Customize group header renderer. */}
|
|
254
|
-
<thead>
|
|
255
|
-
<tr>
|
|
256
|
-
{slots?.margin && <th className={mx(slots?.margin?.className)} />}
|
|
257
|
-
{debug && <th />}
|
|
258
|
-
<th
|
|
259
|
-
// TODO(burdon): Calculate row span.
|
|
260
|
-
colSpan={table.getHeaderGroups()[0].headers.length}
|
|
261
|
-
className={mx('text-left', slots?.group?.className)}
|
|
262
|
-
>
|
|
263
|
-
{table.getState().grouping[0]}[{String(row.getGroupingValue(table.getState().grouping[0]))}]
|
|
264
|
-
</th>
|
|
265
|
-
</tr>
|
|
266
|
-
</thead>
|
|
267
|
-
|
|
268
|
-
<TableBody
|
|
269
|
-
{...props}
|
|
270
|
-
slots={slots}
|
|
271
|
-
rowSelection={rowSelection}
|
|
272
|
-
expand={expand}
|
|
273
|
-
focus={focus}
|
|
274
|
-
onFocus={setFocus}
|
|
275
|
-
onSelect={handleSelect}
|
|
276
|
-
rows={row.subRows}
|
|
277
|
-
/>
|
|
278
|
-
</Fragment>
|
|
279
|
-
);
|
|
280
|
-
})}
|
|
281
|
-
|
|
282
|
-
{/* Foot */}
|
|
283
|
-
{footer && <TableFoot {...props} slots={slots} footers={table.getFooterGroups()} />}
|
|
284
|
-
</table>
|
|
285
|
-
|
|
286
|
-
{debug && (
|
|
287
|
-
<pre className='font-mono text-xs text-neutral-500 my-4 p-2 ring'>
|
|
288
|
-
<code>{JSON.stringify(table.getState(), undefined, 2)}</code>
|
|
289
|
-
</pre>
|
|
290
|
-
)}
|
|
291
|
-
</div>
|
|
292
|
-
);
|
|
293
|
-
};
|
|
294
|
-
|
|
295
|
-
/**
|
|
296
|
-
* Glue to bottom as rows are added.
|
|
297
|
-
*/
|
|
298
|
-
// TODO(burdon): Causes scrollbar to be constantly visible.
|
|
299
|
-
// https://css-tricks.com/books/greatest-css-tricks/pin-scrolling-to-bottom
|
|
300
|
-
const usePinToBottom = <TData extends RowData>(data: TData[], pinToBottom?: boolean) => {
|
|
301
|
-
const containerRef = useRef<HTMLDivElement | null>(null);
|
|
302
|
-
const [stickyScrolling, setStickyScrolling] = useState(true);
|
|
303
|
-
useEffect(() => {
|
|
304
|
-
const container = containerRef.current;
|
|
305
|
-
if (!pinToBottom || !container) {
|
|
306
|
-
return;
|
|
307
|
-
}
|
|
308
|
-
|
|
309
|
-
// TODO(burdon): Set when scrolled to bottom and unset when manually scrolled away.
|
|
310
|
-
const handler = () => {
|
|
311
|
-
const bottom = container.scrollHeight - container.scrollTop - container.clientHeight === 0;
|
|
312
|
-
setStickyScrolling(bottom);
|
|
313
|
-
};
|
|
314
|
-
|
|
315
|
-
container.addEventListener('scroll', handler);
|
|
316
|
-
return () => container.removeEventListener('scroll', handler);
|
|
317
|
-
}, []);
|
|
318
|
-
|
|
319
|
-
useEffect(() => {
|
|
320
|
-
if (!pinToBottom || !containerRef.current || !stickyScrolling) {
|
|
321
|
-
return;
|
|
322
|
-
}
|
|
323
|
-
|
|
324
|
-
containerRef.current?.scroll({ top: containerRef.current.scrollHeight });
|
|
325
|
-
}, [data]);
|
|
326
|
-
|
|
327
|
-
return containerRef;
|
|
328
|
-
};
|
|
329
|
-
|
|
330
|
-
//
|
|
331
|
-
// Head.
|
|
332
|
-
//
|
|
333
|
-
|
|
334
|
-
type TableHeadProps<TData extends RowData> = Partial<TableProps<TData>> & {
|
|
335
|
-
state: TableState;
|
|
336
|
-
headers: HeaderGroup<TData>[];
|
|
337
|
-
expand?: boolean;
|
|
338
|
-
};
|
|
339
|
-
|
|
340
|
-
const TableHead = <TData extends RowData>({
|
|
341
|
-
state,
|
|
342
|
-
headers,
|
|
343
|
-
expand,
|
|
344
|
-
header,
|
|
345
|
-
debug,
|
|
346
|
-
fullWidth,
|
|
347
|
-
border,
|
|
348
|
-
slots,
|
|
349
|
-
}: TableHeadProps<TData>) => {
|
|
350
|
-
return (
|
|
351
|
-
<thead className={mx(header ? ['sticky top-0 z-10'] : 'collapse')}>
|
|
352
|
-
{headers.map((headerGroup) => {
|
|
353
|
-
return (
|
|
354
|
-
// Group element to hover resize handles.
|
|
355
|
-
<tr key={headerGroup.id} className='font-light group'>
|
|
356
|
-
{slots?.margin && <th className={mx(slots?.margin?.className)} />}
|
|
357
|
-
|
|
358
|
-
{/* TODO(burdon): Calc. width. */}
|
|
359
|
-
{debug && (
|
|
360
|
-
<th className='text-left' style={{ width: 32 }}>
|
|
361
|
-
#
|
|
362
|
-
</th>
|
|
363
|
-
)}
|
|
364
|
-
|
|
365
|
-
{headerGroup.headers.map((header) => {
|
|
366
|
-
return (
|
|
367
|
-
<th
|
|
368
|
-
key={header.id}
|
|
369
|
-
style={{
|
|
370
|
-
// Don't set width if fullWidth and no explicit size.
|
|
371
|
-
width: fullWidth && header.column.columnDef.meta?.expand ? undefined : header.getSize(),
|
|
372
|
-
}}
|
|
373
|
-
// Relative for resize handle.
|
|
374
|
-
// TODO(burdon): Border scrolls with main content.
|
|
375
|
-
// https://stackoverflow.com/questions/50361698/border-style-do-not-work-with-sticky-position-element
|
|
376
|
-
className={mx(
|
|
377
|
-
'relative text-left',
|
|
378
|
-
border && groupBorder,
|
|
379
|
-
slots?.header?.className,
|
|
380
|
-
header.column.columnDef.meta?.slots?.header?.className,
|
|
381
|
-
)}
|
|
382
|
-
>
|
|
383
|
-
{!header || header.isPlaceholder
|
|
384
|
-
? null
|
|
385
|
-
: flexRender(header.column.columnDef.header, header.getContext())}
|
|
386
|
-
|
|
387
|
-
{/*
|
|
388
|
-
* Resize handle.
|
|
389
|
-
* https://codesandbox.io/p/sandbox/github/tanstack/table/tree/main/examples/react/column-sizing
|
|
390
|
-
*/}
|
|
391
|
-
{header.column.columnDef.meta?.resizable && (
|
|
392
|
-
<div
|
|
393
|
-
className={mx(
|
|
394
|
-
'absolute top-0 pl-1 h-full z-[10] w-[7px] -right-[5px]',
|
|
395
|
-
'cursor-col-resize select-none touch-none opacity-20 hover:opacity-100',
|
|
396
|
-
header.column.getIsResizing() && 'hidden',
|
|
397
|
-
)}
|
|
398
|
-
style={{
|
|
399
|
-
transform: header.column.getIsResizing()
|
|
400
|
-
? `translateX(${state.columnSizingInfo.deltaOffset}px)`
|
|
401
|
-
: undefined,
|
|
402
|
-
}}
|
|
403
|
-
onMouseDown={header.getResizeHandler()}
|
|
404
|
-
onTouchStart={header.getResizeHandler()}
|
|
405
|
-
>
|
|
406
|
-
<div className='flex group-hover:bg-neutral-700 -ml-[2px] w-[1px] h-full' />
|
|
407
|
-
</div>
|
|
408
|
-
)}
|
|
409
|
-
</th>
|
|
410
|
-
);
|
|
411
|
-
})}
|
|
412
|
-
{expand && <th />}
|
|
413
|
-
{slots?.margin && <th className={mx(slots?.margin?.className)} />}
|
|
414
|
-
</tr>
|
|
415
|
-
);
|
|
416
|
-
})}
|
|
417
|
-
</thead>
|
|
418
|
-
);
|
|
419
|
-
};
|
|
420
|
-
|
|
421
|
-
//
|
|
422
|
-
// Body.
|
|
423
|
-
//
|
|
424
|
-
|
|
425
|
-
type TableBodyProps<TData extends RowData> = Partial<TableProps<TData>> & {
|
|
426
|
-
rows: Row<TData>[];
|
|
427
|
-
rowSelection: RowSelectionState;
|
|
428
|
-
expand?: boolean;
|
|
429
|
-
focus?: string;
|
|
430
|
-
onFocus?: (id?: string) => void;
|
|
431
|
-
onSelect?: (row: Row<TData>) => void;
|
|
432
|
-
};
|
|
433
|
-
|
|
434
|
-
const TableBody = <TData extends RowData>({
|
|
435
|
-
keyAccessor,
|
|
436
|
-
rows,
|
|
437
|
-
rowSelection,
|
|
438
|
-
focus,
|
|
439
|
-
onFocus,
|
|
440
|
-
onSelect,
|
|
441
|
-
debug,
|
|
442
|
-
border,
|
|
443
|
-
expand,
|
|
444
|
-
slots,
|
|
445
|
-
}: TableBodyProps<TData>) => {
|
|
446
|
-
return (
|
|
447
|
-
<tbody>
|
|
448
|
-
{rows.map((row) => {
|
|
449
|
-
return (
|
|
450
|
-
<tr
|
|
451
|
-
key={keyAccessor ? keyAccessor(row.original) : row.id}
|
|
452
|
-
onClick={() => onSelect?.(row)}
|
|
453
|
-
role='button' // TODO(burdon): ???
|
|
454
|
-
className={mx(
|
|
455
|
-
'group',
|
|
456
|
-
rowSelection[row.id] && slots?.selected?.className,
|
|
457
|
-
focus === row.id && slots?.focus?.className,
|
|
458
|
-
slots?.row?.className,
|
|
459
|
-
)}
|
|
460
|
-
>
|
|
461
|
-
{/* TODO(burdon): Dummy button for focus (don't alter geometry). */}
|
|
462
|
-
{slots?.margin && (
|
|
463
|
-
<td className={mx(slots?.margin?.className)}>
|
|
464
|
-
<button
|
|
465
|
-
role='button'
|
|
466
|
-
style={{ width: 1, height: 1 }}
|
|
467
|
-
className='focus:outline-none'
|
|
468
|
-
onFocus={() => onFocus?.(keyAccessor ? keyAccessor(row.original) : row.id)}
|
|
469
|
-
onBlur={() => onFocus?.(undefined)}
|
|
470
|
-
onKeyDown={(event) => {
|
|
471
|
-
// TODO(burdon): Move focus.
|
|
472
|
-
switch (event.key) {
|
|
473
|
-
case 'ArrowUp': {
|
|
474
|
-
break;
|
|
475
|
-
}
|
|
476
|
-
case 'ArrowDown': {
|
|
477
|
-
break;
|
|
478
|
-
}
|
|
479
|
-
}
|
|
480
|
-
}}
|
|
481
|
-
/>
|
|
482
|
-
</td>
|
|
483
|
-
)}
|
|
484
|
-
|
|
485
|
-
{debug && <td>{row.id}</td>}
|
|
486
|
-
|
|
487
|
-
{row.getVisibleCells().map((cell) => {
|
|
488
|
-
// TODO(burdon): Allow class override from column.
|
|
489
|
-
return (
|
|
490
|
-
<td
|
|
491
|
-
key={cell.id}
|
|
492
|
-
className={mx(
|
|
493
|
-
border && groupBorder,
|
|
494
|
-
slots?.cell?.className,
|
|
495
|
-
cell.column.columnDef.meta?.slots?.cell?.className,
|
|
496
|
-
)}
|
|
497
|
-
>
|
|
498
|
-
{flexRender(cell.column.columnDef.cell, { className: 'px-2', ...cell.getContext() })}
|
|
499
|
-
</td>
|
|
500
|
-
);
|
|
501
|
-
})}
|
|
502
|
-
|
|
503
|
-
{expand && <td />}
|
|
504
|
-
{slots?.margin && <td className={mx(slots?.margin?.className)} />}
|
|
505
|
-
</tr>
|
|
506
|
-
);
|
|
507
|
-
})}
|
|
508
|
-
</tbody>
|
|
509
|
-
);
|
|
510
|
-
};
|
|
511
|
-
|
|
512
|
-
//
|
|
513
|
-
// Footer
|
|
514
|
-
//
|
|
515
|
-
|
|
516
|
-
type TableFootProps<TData extends RowData> = Partial<TableProps<TData>> & {
|
|
517
|
-
footers: HeaderGroup<TData>[];
|
|
518
|
-
expand?: boolean;
|
|
519
|
-
};
|
|
520
|
-
|
|
521
|
-
const TableFoot = <TData extends RowData>({ footers, expand, slots, debug, border }: TableFootProps<TData>) => {
|
|
522
|
-
return (
|
|
523
|
-
<tfoot className={mx('sticky bottom-0 z-[10]', slots?.footer?.className)}>
|
|
524
|
-
{footers.map((footerGroup) => (
|
|
525
|
-
<tr key={footerGroup.id} className='font-thin'>
|
|
526
|
-
{slots?.margin && <th className={mx(slots?.margin?.className)} />}
|
|
527
|
-
{debug && <th />}
|
|
528
|
-
|
|
529
|
-
{footerGroup.headers.map((footer) => {
|
|
530
|
-
return (
|
|
531
|
-
<th
|
|
532
|
-
key={footer.id}
|
|
533
|
-
className={mx(
|
|
534
|
-
border && groupBorder,
|
|
535
|
-
'text-left',
|
|
536
|
-
slots?.footer?.className,
|
|
537
|
-
footer.column.columnDef.meta?.slots?.footer?.className,
|
|
538
|
-
)}
|
|
539
|
-
>
|
|
540
|
-
{footer.isPlaceholder ? null : flexRender(footer.column.columnDef.footer, footer.getContext())}
|
|
541
|
-
</th>
|
|
542
|
-
);
|
|
543
|
-
})}
|
|
544
|
-
|
|
545
|
-
{expand && <th />}
|
|
546
|
-
{slots?.margin && <th className={mx(slots?.margin?.className)} />}
|
|
547
|
-
</tr>
|
|
548
|
-
))}
|
|
549
|
-
</tfoot>
|
|
550
|
-
);
|
|
551
|
-
};
|