@prairielearn/ui 1.6.0 → 1.7.1
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/CHANGELOG.md +16 -0
- package/README.md +1 -2
- package/dist/components/TanstackTable.d.ts +15 -13
- package/dist/components/TanstackTable.d.ts.map +1 -1
- package/dist/components/TanstackTable.js +29 -22
- package/dist/components/TanstackTable.js.map +1 -1
- package/dist/components/TanstackTableDownloadButton.d.ts +5 -3
- package/dist/components/TanstackTableDownloadButton.d.ts.map +1 -1
- package/dist/components/TanstackTableDownloadButton.js +6 -5
- package/dist/components/TanstackTableDownloadButton.js.map +1 -1
- package/dist/components/TanstackTableHeaderCell.d.ts.map +1 -1
- package/dist/components/TanstackTableHeaderCell.js +0 -1
- package/dist/components/TanstackTableHeaderCell.js.map +1 -1
- package/package.json +3 -2
- package/src/components/TanstackTable.tsx +58 -28
- package/src/components/TanstackTableDownloadButton.tsx +41 -30
- package/src/components/TanstackTableHeaderCell.tsx +0 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,21 @@
|
|
|
1
1
|
# @prairielearn/ui
|
|
2
2
|
|
|
3
|
+
## 1.7.1
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 90c712d: Improve style of table when it is less than viewport width
|
|
8
|
+
|
|
9
|
+
## 1.7.0
|
|
10
|
+
|
|
11
|
+
### Minor Changes
|
|
12
|
+
|
|
13
|
+
- 8326968: Debounce global filter in TanstackTable, clean up column manager API
|
|
14
|
+
|
|
15
|
+
### Patch Changes
|
|
16
|
+
|
|
17
|
+
- 037c174: Add back support for singularLabel and pluralLabel to TanstackTableCard, make `headerButtons` optional, add `hasSelection` to TanstackTableDownloadButton
|
|
18
|
+
|
|
3
19
|
## 1.6.0
|
|
4
20
|
|
|
5
21
|
### Minor Changes
|
package/README.md
CHANGED
|
@@ -32,6 +32,7 @@ import { TanstackTableCard } from '@prairielearn/ui';
|
|
|
32
32
|
: null,
|
|
33
33
|
};
|
|
34
34
|
},
|
|
35
|
+
hasSelection: false,
|
|
35
36
|
}}
|
|
36
37
|
headerButtons={
|
|
37
38
|
<>
|
|
@@ -48,8 +49,6 @@ import { TanstackTableCard } from '@prairielearn/ui';
|
|
|
48
49
|
</>
|
|
49
50
|
}
|
|
50
51
|
globalFilter={{
|
|
51
|
-
value: globalFilter,
|
|
52
|
-
setValue: setGlobalFilter,
|
|
53
52
|
placeholder: 'Search by UID, name, email...',
|
|
54
53
|
}}
|
|
55
54
|
tableOptions={tableOptions}
|
|
@@ -36,30 +36,32 @@ export declare function TanstackTable<RowDataModel>({ table, title, filters, row
|
|
|
36
36
|
* @param params.singularLabel - The singular label for a single row in the table, e.g. "student"
|
|
37
37
|
* @param params.pluralLabel - The plural label for multiple rows in the table, e.g. "students"
|
|
38
38
|
* @param params.headerButtons - The buttons to display in the header
|
|
39
|
-
* @param params.
|
|
40
|
-
* @param params.
|
|
41
|
-
* @param params.
|
|
42
|
-
* @param params.globalFilter
|
|
43
|
-
* @param params.globalFilter.
|
|
44
|
-
* @param params.globalFilter.placeholder
|
|
39
|
+
* @param params.columnManager - Optional configuration for the column manager. See {@link ColumnManager} for more details.
|
|
40
|
+
* @param params.columnManager.buttons - The buttons to display next to the column manager (View button)
|
|
41
|
+
* @param params.columnManager.topContent - Optional content to display at the top of the column manager (View) dropdown menu
|
|
42
|
+
* @param params.globalFilter - Configuration for the global filter
|
|
43
|
+
* @param params.globalFilter.placeholder - Placeholder text for the search input
|
|
45
44
|
* @param params.tableOptions - Specific options for the table. See {@link TanstackTableProps} for more details.
|
|
46
45
|
* @param params.downloadButtonOptions - Specific options for the download button. See {@link TanstackTableDownloadButtonProps} for more details.
|
|
47
46
|
*/
|
|
48
|
-
export declare function TanstackTableCard<RowDataModel>({ table, title, singularLabel, pluralLabel, headerButtons,
|
|
47
|
+
export declare function TanstackTableCard<RowDataModel>({ table, title, singularLabel, pluralLabel, headerButtons, columnManager, globalFilter, tableOptions, downloadButtonOptions, className, ...divProps }: {
|
|
49
48
|
table: Table<RowDataModel>;
|
|
50
49
|
title: string;
|
|
51
50
|
singularLabel: string;
|
|
52
51
|
pluralLabel: string;
|
|
53
|
-
headerButtons
|
|
54
|
-
|
|
55
|
-
|
|
52
|
+
headerButtons?: JSX.Element;
|
|
53
|
+
columnManager?: {
|
|
54
|
+
buttons?: JSX.Element;
|
|
55
|
+
topContent?: JSX.Element;
|
|
56
|
+
};
|
|
56
57
|
globalFilter: {
|
|
57
|
-
value: string;
|
|
58
|
-
setValue: (value: string) => void;
|
|
59
58
|
placeholder: string;
|
|
60
59
|
};
|
|
61
60
|
tableOptions: Partial<Omit<TanstackTableProps<RowDataModel>, 'table'>>;
|
|
62
|
-
downloadButtonOptions?: Omit<TanstackTableDownloadButtonProps<RowDataModel>, 'table' | 'singularLabel' | 'pluralLabel'
|
|
61
|
+
downloadButtonOptions?: Omit<TanstackTableDownloadButtonProps<RowDataModel>, 'table' | 'singularLabel' | 'pluralLabel'> & {
|
|
62
|
+
pluralLabel?: string;
|
|
63
|
+
singularLabel?: string;
|
|
64
|
+
};
|
|
63
65
|
} & Omit<ComponentProps<'div'>, 'class'>): JSX.Element;
|
|
64
66
|
export declare function TanstackTableEmptyState({ iconName, children, }: {
|
|
65
67
|
iconName: `bi-${string}`;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TanstackTable.d.ts","sourceRoot":"","sources":["../../src/components/TanstackTable.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAQ,MAAM,EAAO,KAAK,EAAE,MAAM,sBAAsB,CAAC;AAErE,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,QAAQ,CAAC;AAEhD,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,oBAAoB,CAAC;
|
|
1
|
+
{"version":3,"file":"TanstackTable.d.ts","sourceRoot":"","sources":["../../src/components/TanstackTable.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAQ,MAAM,EAAO,KAAK,EAAE,MAAM,sBAAsB,CAAC;AAErE,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,QAAQ,CAAC;AAEhD,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,oBAAoB,CAAC;AAK9C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAI/D,OAAO,EAEL,KAAK,gCAAgC,EACtC,MAAM,kCAAkC,CAAC;AAoE1C,UAAU,kBAAkB,CAAC,YAAY;IACvC,KAAK,EAAE,KAAK,CAAC,YAAY,CAAC,CAAC;IAC3B,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC,KAAK,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC,YAAY,EAAE,OAAO,CAAC,CAAA;KAAE,KAAK,GAAG,CAAC,OAAO,CAAC,CAAC;IAC5F,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,cAAc,CAAC,EAAE,GAAG,CAAC,OAAO,CAAC;IAC7B,UAAU,CAAC,EAAE,GAAG,CAAC,OAAO,CAAC;IACzB,SAAS,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC,cAAc,CAAC,GAAG,IAAI,CAAC;CACpD;AAID;;;;;;;;;;GAUG;AACH,wBAAgB,aAAa,CAAC,YAAY,EAAE,EAC1C,KAAK,EACL,KAAK,EACL,OAA4B,EAC5B,SAAc,EACd,cAAsC,EACtC,UAA8B,EAC9B,SAAS,GACV,EAAE,kBAAkB,CAAC,YAAY,CAAC,eA0WlC;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,iBAAiB,CAAC,YAAY,EAAE,EAC9C,KAAK,EACL,KAAK,EACL,aAAa,EACb,WAAW,EACX,aAAa,EACb,aAAa,EACb,YAAY,EACZ,YAAY,EACZ,qBAAqB,EACrB,SAAS,EACT,GAAG,QAAQ,EACZ,EAAE;IACD,KAAK,EAAE,KAAK,CAAC,YAAY,CAAC,CAAC;IAC3B,KAAK,EAAE,MAAM,CAAC;IACd,aAAa,EAAE,MAAM,CAAC;IACtB,WAAW,EAAE,MAAM,CAAC;IACpB,aAAa,CAAC,EAAE,GAAG,CAAC,OAAO,CAAC;IAC5B,aAAa,CAAC,EAAE;QACd,OAAO,CAAC,EAAE,GAAG,CAAC,OAAO,CAAC;QACtB,UAAU,CAAC,EAAE,GAAG,CAAC,OAAO,CAAC;KAC1B,CAAC;IACF,YAAY,EAAE;QACZ,WAAW,EAAE,MAAM,CAAC;KACrB,CAAC;IACF,YAAY,EAAE,OAAO,CAAC,IAAI,CAAC,kBAAkB,CAAC,YAAY,CAAC,EAAE,OAAO,CAAC,CAAC,CAAC;IACvE,qBAAqB,CAAC,EAAE,IAAI,CAC1B,gCAAgC,CAAC,YAAY,CAAC,EAC9C,OAAO,GAAG,eAAe,GAAG,aAAa,CAC1C,GAAG;QAAE,WAAW,CAAC,EAAE,MAAM,CAAC;QAAC,aAAa,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;CACtD,GAAG,IAAI,CAAC,cAAc,CAAC,KAAK,CAAC,EAAE,OAAO,CAAC,eA+FvC;AAED,wBAAgB,uBAAuB,CAAC,EACtC,QAAQ,EACR,QAAQ,GACT,EAAE;IACD,QAAQ,EAAE,MAAM,MAAM,EAAE,CAAC;IACzB,QAAQ,EAAE,iBAAiB,CAAC;CAC7B,eAOA"}
|
|
@@ -2,9 +2,10 @@ import { jsx as _jsx, jsxs as _jsxs } from "@prairielearn/preact-cjs/jsx-runtime
|
|
|
2
2
|
import { flexRender } from '@tanstack/react-table';
|
|
3
3
|
import { useVirtualizer } from '@tanstack/react-virtual';
|
|
4
4
|
import clsx from 'clsx';
|
|
5
|
-
import { useEffect, useMemo, useRef } from 'preact/hooks';
|
|
5
|
+
import { useEffect, useMemo, useRef, useState } from 'preact/hooks';
|
|
6
6
|
import OverlayTrigger from 'react-bootstrap/OverlayTrigger';
|
|
7
7
|
import Tooltip from 'react-bootstrap/Tooltip';
|
|
8
|
+
import { useDebouncedCallback } from 'use-debounce';
|
|
8
9
|
import { run } from '@prairielearn/run';
|
|
9
10
|
import { ColumnManager } from './ColumnManager.js';
|
|
10
11
|
import { TanstackTableDownloadButton, } from './TanstackTableDownloadButton.js';
|
|
@@ -168,33 +169,29 @@ export function TanstackTable({ table, title, filters = DEFAULT_FILTER_MAP, rowH
|
|
|
168
169
|
}, children: _jsx("div", { ref: tableRef, style: {
|
|
169
170
|
position: 'relative',
|
|
170
171
|
width: `max(${table.getTotalSize()}px, 100%)`,
|
|
171
|
-
}, children: _jsxs("table", { class: "table table-hover mb-0", style: { display: 'grid', tableLayout: 'fixed' }, "aria-label": title, role: "grid", children: [_jsx("thead", { style: {
|
|
172
|
+
}, children: _jsxs("table", { class: "table table-hover mb-0", style: { display: 'grid', tableLayout: 'fixed' }, "aria-label": title, role: "grid", children: [_jsx("thead", { class: "position-sticky top-0 w-100 border-top", style: {
|
|
172
173
|
display: 'grid',
|
|
173
|
-
position: 'sticky',
|
|
174
|
-
top: 0,
|
|
175
174
|
zIndex: 1,
|
|
176
|
-
|
|
175
|
+
borderBottom: 'var(--bs-border-width) solid black',
|
|
176
|
+
}, children: _jsxs("tr", { class: "d-flex w-100", style: { minWidth: `${table.getTotalSize()}px` }, children: [leftPinnedHeaders.map((header) => {
|
|
177
177
|
return (_jsx(TanstackTableHeaderCell, { header: header, filters: filters, table: table, handleResizeEnd: handleResizeEnd, isPinned: "left" }, header.id));
|
|
178
178
|
}), virtualPaddingLeft ? (_jsx("th", { style: { display: 'flex', width: virtualPaddingLeft } })) : null, virtualColumns.map((virtualColumn) => {
|
|
179
179
|
const header = centerHeaders[virtualColumn.index];
|
|
180
180
|
if (!header)
|
|
181
181
|
return null;
|
|
182
182
|
return (_jsx(TanstackTableHeaderCell, { header: header, filters: filters, table: table, handleResizeEnd: handleResizeEnd, isPinned: false }, header.id));
|
|
183
|
-
}), virtualPaddingRight ? (_jsx("th", { style: { display: 'flex', width: virtualPaddingRight } })) : null] }, leafHeaderGroup.id) }), _jsx("tbody", { style: {
|
|
183
|
+
}), virtualPaddingRight ? (_jsx("th", { style: { display: 'flex', width: virtualPaddingRight } })) : null, _jsx("th", { tabIndex: -1, class: "d-flex flex-grow-1 p-0", style: { minWidth: 0 }, "aria-hidden": "true" })] }, leafHeaderGroup.id) }), _jsx("tbody", { class: "position-relative w-100", style: {
|
|
184
184
|
display: 'grid',
|
|
185
185
|
height: `${rowVirtualizer.getTotalSize()}px`,
|
|
186
|
-
position: 'relative',
|
|
187
186
|
}, children: virtualRows.map((virtualRow) => {
|
|
188
187
|
const row = rows[virtualRow.index];
|
|
189
188
|
const rowIdx = virtualRow.index;
|
|
190
189
|
const leftPinnedCells = row.getLeftVisibleCells();
|
|
191
190
|
const centerCells = row.getCenterVisibleCells();
|
|
192
191
|
let currentColIdx = 0;
|
|
193
|
-
return (_jsxs("tr", { ref: (node) => rowVirtualizer.measureElement(node), "data-index": virtualRow.index, style: {
|
|
194
|
-
display: 'flex',
|
|
195
|
-
position: 'absolute',
|
|
192
|
+
return (_jsxs("tr", { ref: (node) => rowVirtualizer.measureElement(node), "data-index": virtualRow.index, class: "d-flex position-absolute w-100", style: {
|
|
196
193
|
transform: `translateY(${virtualRow.start}px)`,
|
|
197
|
-
|
|
194
|
+
minWidth: `${table.getTotalSize()}px`,
|
|
198
195
|
}, children: [leftPinnedCells.map((cell) => {
|
|
199
196
|
const colIdx = currentColIdx++;
|
|
200
197
|
const canSort = cell.column.getCanSort();
|
|
@@ -210,7 +207,7 @@ export function TanstackTable({ table, title, filters = DEFAULT_FILTER_MAP, rowH
|
|
|
210
207
|
const canFilter = cell.column.getCanFilter();
|
|
211
208
|
const wrapText = cell.column.columnDef.meta?.wrapText ?? false;
|
|
212
209
|
return (_jsx(TableCell, { cell: cell, rowIdx: rowIdx, colIdx: colIdx, canSort: canSort, canFilter: canFilter, wrapText: wrapText, handleGridKeyDown: handleGridKeyDown }, cell.id));
|
|
213
|
-
}), virtualPaddingRight ? (_jsx("td", { style: { display: 'flex', width: virtualPaddingRight } })) : null] }, row.id));
|
|
210
|
+
}), virtualPaddingRight ? (_jsx("td", { style: { display: 'flex', width: virtualPaddingRight } })) : null, _jsx("td", { tabIndex: -1, class: "d-flex flex-grow-1 p-0", style: { minWidth: 0 }, "aria-hidden": "true" })] }, row.id));
|
|
214
211
|
}) })] }) }) }), table.getVisibleLeafColumns().length === 0 || displayedCount === 0 ? (_jsx("div", { children: _jsx("div", { class: "d-flex flex-column justify-content-center align-items-center p-4", style: {
|
|
215
212
|
position: 'absolute',
|
|
216
213
|
top: 0,
|
|
@@ -234,17 +231,21 @@ export function TanstackTable({ table, title, filters = DEFAULT_FILTER_MAP, rowH
|
|
|
234
231
|
* @param params.singularLabel - The singular label for a single row in the table, e.g. "student"
|
|
235
232
|
* @param params.pluralLabel - The plural label for multiple rows in the table, e.g. "students"
|
|
236
233
|
* @param params.headerButtons - The buttons to display in the header
|
|
237
|
-
* @param params.
|
|
238
|
-
* @param params.
|
|
239
|
-
* @param params.
|
|
240
|
-
* @param params.globalFilter
|
|
241
|
-
* @param params.globalFilter.
|
|
242
|
-
* @param params.globalFilter.placeholder
|
|
234
|
+
* @param params.columnManager - Optional configuration for the column manager. See {@link ColumnManager} for more details.
|
|
235
|
+
* @param params.columnManager.buttons - The buttons to display next to the column manager (View button)
|
|
236
|
+
* @param params.columnManager.topContent - Optional content to display at the top of the column manager (View) dropdown menu
|
|
237
|
+
* @param params.globalFilter - Configuration for the global filter
|
|
238
|
+
* @param params.globalFilter.placeholder - Placeholder text for the search input
|
|
243
239
|
* @param params.tableOptions - Specific options for the table. See {@link TanstackTableProps} for more details.
|
|
244
240
|
* @param params.downloadButtonOptions - Specific options for the download button. See {@link TanstackTableDownloadButtonProps} for more details.
|
|
245
241
|
*/
|
|
246
|
-
export function TanstackTableCard({ table, title, singularLabel, pluralLabel, headerButtons,
|
|
242
|
+
export function TanstackTableCard({ table, title, singularLabel, pluralLabel, headerButtons, columnManager, globalFilter, tableOptions, downloadButtonOptions, className, ...divProps }) {
|
|
247
243
|
const searchInputRef = useRef(null);
|
|
244
|
+
const [inputValue, setInputValue] = useState(() => table.getState().globalFilter ?? '');
|
|
245
|
+
// Debounce the filter update
|
|
246
|
+
const debouncedSetFilter = useDebouncedCallback((value) => {
|
|
247
|
+
table.setGlobalFilter(value);
|
|
248
|
+
}, 150);
|
|
248
249
|
// Focus the search input when Ctrl+F is pressed
|
|
249
250
|
useEffect(() => {
|
|
250
251
|
function onKeyDown(event) {
|
|
@@ -260,11 +261,17 @@ export function TanstackTableCard({ table, title, singularLabel, pluralLabel, he
|
|
|
260
261
|
}, []);
|
|
261
262
|
const displayedCount = table.getRowModel().rows.length;
|
|
262
263
|
const totalCount = table.getCoreRowModel().rows.length;
|
|
263
|
-
return (_jsxs("div", { class: clsx('card d-flex flex-column', className), ...divProps, children: [_jsx("div", { class: "card-header bg-primary text-white", children: _jsxs("div", { class: "d-flex align-items-center justify-content-between gap-2", children: [_jsx("div", { children: title }), _jsxs("div", { class: "d-flex gap-2", children: [headerButtons, downloadButtonOptions && (_jsx(TanstackTableDownloadButton, { table: table, pluralLabel: pluralLabel, singularLabel: singularLabel, ...downloadButtonOptions }))] })] }) }), _jsxs("div", { class: "card-body d-flex flex-row flex-wrap flex-grow-0 align-items-center gap-2", children: [_jsxs("div", { class: "position-relative w-100", style: { maxWidth: 'min(400px, 100%)' }, children: [_jsx("input", { ref: searchInputRef, type: "text", class: "form-control pl-ui-tanstack-table-search-input pl-ui-tanstack-table-focusable-shadow", "aria-label": globalFilter.placeholder, placeholder: globalFilter.placeholder, value:
|
|
264
|
+
return (_jsxs("div", { class: clsx('card d-flex flex-column', className), ...divProps, children: [_jsx("div", { class: "card-header bg-primary text-white", children: _jsxs("div", { class: "d-flex align-items-center justify-content-between gap-2", children: [_jsx("div", { children: title }), _jsxs("div", { class: "d-flex gap-2", children: [headerButtons, downloadButtonOptions && (_jsx(TanstackTableDownloadButton, { table: table, pluralLabel: pluralLabel, singularLabel: singularLabel, ...downloadButtonOptions }))] })] }) }), _jsxs("div", { class: "card-body d-flex flex-row flex-wrap flex-grow-0 align-items-center gap-2", children: [_jsxs("div", { class: "position-relative w-100", style: { maxWidth: 'min(400px, 100%)' }, children: [_jsx("input", { ref: searchInputRef, type: "text", class: "form-control pl-ui-tanstack-table-search-input pl-ui-tanstack-table-focusable-shadow", "aria-label": globalFilter.placeholder, placeholder: globalFilter.placeholder, value: inputValue, autoComplete: "off", onInput: (e) => {
|
|
264
265
|
if (!(e.target instanceof HTMLInputElement))
|
|
265
266
|
return;
|
|
266
|
-
|
|
267
|
-
|
|
267
|
+
const value = e.target.value;
|
|
268
|
+
setInputValue(value);
|
|
269
|
+
debouncedSetFilter(value);
|
|
270
|
+
} }), inputValue && (_jsx(OverlayTrigger, { overlay: _jsx(Tooltip, { children: "Clear search" }), children: _jsx("button", { type: "button", class: "btn btn-floating-icon", "aria-label": "Clear search", onClick: () => {
|
|
271
|
+
setInputValue('');
|
|
272
|
+
debouncedSetFilter.cancel();
|
|
273
|
+
table.setGlobalFilter('');
|
|
274
|
+
}, children: _jsx("i", { class: "bi bi-x-circle-fill", "aria-hidden": "true" }) }) }))] }), _jsxs("div", { class: "d-flex flex-wrap flex-row align-items-center gap-2", children: [_jsx(ColumnManager, { table: table, topContent: columnManager?.topContent }), columnManager?.buttons] }), _jsxs("div", { class: "ms-auto text-muted text-nowrap", children: ["Showing ", displayedCount, " of ", totalCount, " ", totalCount === 1 ? singularLabel : pluralLabel] })] }), _jsx("div", { class: "flex-grow-1", children: _jsx(TanstackTable, { table: table, title: title, ...tableOptions }) })] }));
|
|
268
275
|
}
|
|
269
276
|
export function TanstackTableEmptyState({ iconName, children, }) {
|
|
270
277
|
return (_jsxs("div", { class: "d-flex flex-column justify-content-center align-items-center text-muted", children: [_jsx("i", { class: clsx('bi', iconName, 'display-4 mb-2'), "aria-hidden": "true" }), _jsx("div", { children: children })] }));
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TanstackTable.js","sourceRoot":"","sources":["../../src/components/TanstackTable.tsx"],"names":[],"mappings":"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","sourcesContent":["import { flexRender } from '@tanstack/react-table';\nimport { useVirtualizer } from '@tanstack/react-virtual';\nimport type { Cell, Header, Row, Table } from '@tanstack/table-core';\nimport clsx from 'clsx';\nimport type { ComponentChildren } from 'preact';\nimport { useEffect, useMemo, useRef } from 'preact/hooks';\nimport type { JSX } from 'preact/jsx-runtime';\nimport OverlayTrigger from 'react-bootstrap/OverlayTrigger';\nimport Tooltip from 'react-bootstrap/Tooltip';\n\nimport type { ComponentProps } from '@prairielearn/preact-cjs';\nimport { run } from '@prairielearn/run';\n\nimport { ColumnManager } from './ColumnManager.js';\nimport {\n TanstackTableDownloadButton,\n type TanstackTableDownloadButtonProps,\n} from './TanstackTableDownloadButton.js';\nimport { TanstackTableHeaderCell } from './TanstackTableHeaderCell.js';\nimport { useAutoSizeColumns } from './useAutoSizeColumns.js';\n\nfunction TableCell<RowDataModel>({\n cell,\n rowIdx,\n colIdx,\n canSort,\n canFilter,\n wrapText,\n handleGridKeyDown,\n}: {\n cell: Cell<RowDataModel, unknown>;\n rowIdx: number;\n colIdx: number;\n canSort: boolean;\n canFilter: boolean;\n wrapText: boolean;\n handleGridKeyDown: (e: KeyboardEvent, rowIdx: number, colIdx: number) => void;\n}) {\n return (\n <td\n key={cell.id}\n tabIndex={0}\n data-grid-cell-row={rowIdx}\n data-grid-cell-col={colIdx}\n class={clsx(!canSort && !canFilter && 'text-center')}\n style={{\n display: 'flex',\n width: cell.column.getSize(),\n minWidth: 0,\n maxWidth: cell.column.getSize(),\n flexShrink: 0,\n position: cell.column.getIsPinned() === 'left' ? 'sticky' : undefined,\n left: cell.column.getIsPinned() === 'left' ? cell.column.getStart() : undefined,\n verticalAlign: 'middle',\n }}\n onKeyDown={(e) => handleGridKeyDown(e, rowIdx, colIdx)}\n >\n <div\n style={{\n display: 'block',\n minWidth: 0,\n maxWidth: '100%',\n overflow: wrapText ? 'visible' : 'hidden',\n textOverflow: wrapText ? undefined : 'ellipsis',\n whiteSpace: wrapText ? 'normal' : 'nowrap',\n flex: '1 1 0%',\n width: 0, // Allow flex to control width, but start from 0\n }}\n >\n {flexRender(cell.column.columnDef.cell, cell.getContext())}\n </div>\n </td>\n );\n}\n\nconst DefaultNoResultsState = (\n <TanstackTableEmptyState iconName=\"bi-search\">\n No results found matching your search criteria.\n </TanstackTableEmptyState>\n);\n\nconst DefaultEmptyState = (\n <TanstackTableEmptyState iconName=\"bi-eye-slash\">No results found.</TanstackTableEmptyState>\n);\n\ninterface TanstackTableProps<RowDataModel> {\n table: Table<RowDataModel>;\n title: string;\n filters?: Record<string, (props: { header: Header<RowDataModel, unknown> }) => JSX.Element>;\n rowHeight?: number;\n noResultsState?: JSX.Element;\n emptyState?: JSX.Element;\n scrollRef?: React.RefObject<HTMLDivElement> | null;\n}\n\nconst DEFAULT_FILTER_MAP = {};\n\n/**\n * A generic component that renders a full-width, resizeable Tanstack Table.\n * @param params\n * @param params.table - The table model\n * @param params.title - The title of the table\n * @param params.filters - The filters for the table\n * @param params.rowHeight - The height of the rows in the table\n * @param params.noResultsState - The no results state for the table\n * @param params.emptyState - The empty state for the table\n * @param params.scrollRef - Optional ref that will be attached to the scroll container element.\n */\nexport function TanstackTable<RowDataModel>({\n table,\n title,\n filters = DEFAULT_FILTER_MAP,\n rowHeight = 42,\n noResultsState = DefaultNoResultsState,\n emptyState = DefaultEmptyState,\n scrollRef,\n}: TanstackTableProps<RowDataModel>) {\n const parentRef = useRef<HTMLDivElement>(null);\n const tableRef = useRef<HTMLDivElement>(null);\n const scrollContainerRef = scrollRef ?? parentRef;\n\n const rows = [...table.getTopRows(), ...table.getCenterRows()];\n const rowVirtualizer = useVirtualizer({\n count: rows.length,\n getScrollElement: () => scrollContainerRef.current,\n estimateSize: () => rowHeight,\n overscan: 10,\n measureElement: (el) => el?.getBoundingClientRect().height ?? rowHeight,\n });\n\n const visibleColumns = table.getVisibleLeafColumns();\n const centerColumns = visibleColumns.filter((col) => !col.getIsPinned());\n\n const columnVirtualizer = useVirtualizer({\n count: centerColumns.length,\n estimateSize: (index) => centerColumns[index]?.getSize(),\n // `useAutoSizeColumns` solves a different problem (happens once when the column set changes)\n // and we don't need to measure the cells themselves, so we can use the default estimateSize.\n getScrollElement: () => scrollContainerRef.current,\n horizontal: true,\n overscan: 3,\n });\n\n const virtualColumns = columnVirtualizer.getVirtualItems();\n\n const virtualPaddingLeft = run(() => {\n if (columnVirtualizer && virtualColumns?.length > 0) {\n return virtualColumns[0]?.start ?? 0;\n }\n return null;\n });\n\n const virtualPaddingRight = run(() => {\n if (columnVirtualizer && virtualColumns?.length > 0) {\n return (\n columnVirtualizer.getTotalSize() - (virtualColumns[virtualColumns.length - 1]?.end ?? 0)\n );\n }\n return null;\n });\n\n // Check if any column has wrapping enabled\n const hasWrappedColumns = table.getAllLeafColumns().some((col) => col.columnDef.meta?.wrapText);\n\n // Create callback for remeasuring after resize\n const handleResizeEnd = useMemo(() => {\n if (!hasWrappedColumns) return undefined;\n return () => rowVirtualizer.measure();\n }, [hasWrappedColumns, rowVirtualizer]);\n\n const getVisibleCells = (row: Row<RowDataModel>) => [\n ...row.getLeftVisibleCells(),\n ...row.getCenterVisibleCells(),\n ];\n\n const handleGridKeyDown = (e: KeyboardEvent, rowIdx: number, colIdx: number) => {\n const rowLength = getVisibleCells(rows[rowIdx]).length;\n const adjacentCells: Record<KeyboardEvent['key'], { row: number; col: number }> = {\n ArrowDown: {\n row: Math.min(rows.length - 1, rowIdx + 1),\n col: colIdx,\n },\n ArrowUp: {\n row: Math.max(0, rowIdx - 1),\n col: colIdx,\n },\n ArrowRight: {\n row: rowIdx,\n col: Math.min(rowLength - 1, colIdx + 1),\n },\n ArrowLeft: {\n row: rowIdx,\n col: Math.max(0, colIdx - 1),\n },\n };\n\n const next = adjacentCells[e.key];\n\n if (!next) {\n return;\n }\n\n // Only handle arrow keys if we're in the cell itself, not in an interactive element\n const target = e.target as HTMLElement;\n if (target.tagName === 'TD') {\n // If we are on the leftmost column, we should allow left scrolling.\n if (colIdx === 0 && e.key === 'ArrowLeft') {\n return;\n }\n\n // If we are on the top row, we should allow up scrolling.\n if (rowIdx === 0 && e.key === 'ArrowUp') {\n return;\n }\n\n // If we are on the rightmost column, we should allow right scrolling.\n if (colIdx === rowLength - 1 && e.key === 'ArrowRight') {\n return;\n }\n\n e.preventDefault();\n const selector = `[data-grid-cell-row=\"${next.row}\"][data-grid-cell-col=\"${next.col}\"]`;\n const nextCell = tableRef.current?.querySelector(selector) as HTMLElement | null;\n nextCell?.focus();\n }\n };\n\n const virtualRows = rowVirtualizer.getVirtualItems();\n\n const headerGroups = table.getHeaderGroups();\n\n const leafHeaderGroup = headerGroups[headerGroups.length - 1];\n\n const leftPinnedHeaders = leafHeaderGroup.headers.filter(\n (header) => header.column.getIsPinned() === 'left',\n );\n const centerHeaders = leafHeaderGroup.headers.filter((header) => !header.column.getIsPinned());\n\n const isTableResizing = leafHeaderGroup.headers.some((header) => header.column.getIsResizing());\n\n // We toggle this here instead of in the parent since this component logically manages all UI for the table.\n useEffect(() => {\n document.body.classList.toggle('pl-ui-no-user-select', isTableResizing);\n }, [isTableResizing]);\n\n const hasAutoSized = useAutoSizeColumns(table, tableRef, filters);\n\n // Re-measure the virtualizer when auto-sizing completes\n useEffect(() => {\n if (hasAutoSized) {\n columnVirtualizer.measure();\n }\n }, [columnVirtualizer, hasAutoSized]);\n\n const displayedCount = table.getRowModel().rows.length;\n const totalCount = table.getCoreRowModel().rows.length;\n\n return (\n <div style={{ position: 'relative' }} class=\"d-flex flex-column h-100\">\n <div\n ref={scrollContainerRef}\n style={{\n position: 'absolute',\n top: 0,\n left: 0,\n right: 0,\n bottom: 0,\n overflow: 'auto',\n overflowAnchor: 'none',\n }}\n >\n <div\n ref={tableRef}\n style={{\n position: 'relative',\n width: `max(${table.getTotalSize()}px, 100%)`,\n }}\n >\n <table\n class=\"table table-hover mb-0\"\n style={{ display: 'grid', tableLayout: 'fixed' }}\n aria-label={title}\n role=\"grid\"\n >\n <thead\n style={{\n display: 'grid',\n position: 'sticky',\n top: 0,\n zIndex: 1,\n }}\n >\n <tr\n key={leafHeaderGroup.id}\n style={{ display: 'flex', width: `${table.getTotalSize()}px` }}\n >\n {/* Left pinned columns */}\n {leftPinnedHeaders.map((header) => {\n return (\n <TanstackTableHeaderCell\n key={header.id}\n header={header}\n filters={filters}\n table={table}\n handleResizeEnd={handleResizeEnd}\n isPinned=\"left\"\n />\n );\n })}\n\n {/* Virtual padding for left side of center columns */}\n {virtualPaddingLeft ? (\n <th style={{ display: 'flex', width: virtualPaddingLeft }} />\n ) : null}\n\n {/* Virtualized center columns */}\n {virtualColumns.map((virtualColumn) => {\n const header = centerHeaders[virtualColumn.index];\n if (!header) return null;\n\n return (\n <TanstackTableHeaderCell\n key={header.id}\n header={header}\n filters={filters}\n table={table}\n handleResizeEnd={handleResizeEnd}\n isPinned={false}\n />\n );\n })}\n\n {/* Virtual padding for right side of center columns */}\n {virtualPaddingRight ? (\n <th style={{ display: 'flex', width: virtualPaddingRight }} />\n ) : null}\n </tr>\n </thead>\n <tbody\n style={{\n display: 'grid',\n height: `${rowVirtualizer.getTotalSize()}px`,\n position: 'relative',\n }}\n >\n {virtualRows.map((virtualRow) => {\n const row = rows[virtualRow.index];\n const rowIdx = virtualRow.index;\n const leftPinnedCells = row.getLeftVisibleCells();\n const centerCells = row.getCenterVisibleCells();\n\n let currentColIdx = 0;\n\n return (\n <tr\n key={row.id}\n ref={(node) => rowVirtualizer.measureElement(node)}\n data-index={virtualRow.index}\n style={{\n display: 'flex',\n position: 'absolute',\n transform: `translateY(${virtualRow.start}px)`,\n width: `${table.getTotalSize()}px`,\n }}\n >\n {leftPinnedCells.map((cell) => {\n const colIdx = currentColIdx++;\n const canSort = cell.column.getCanSort();\n const canFilter = cell.column.getCanFilter();\n const wrapText = cell.column.columnDef.meta?.wrapText ?? false;\n\n return (\n <TableCell\n key={cell.id}\n cell={cell}\n rowIdx={rowIdx}\n colIdx={colIdx}\n canSort={canSort}\n canFilter={canFilter}\n wrapText={wrapText}\n handleGridKeyDown={handleGridKeyDown}\n />\n );\n })}\n\n {virtualPaddingLeft ? (\n <td style={{ display: 'flex', width: virtualPaddingLeft }} />\n ) : null}\n\n {virtualColumns.map((virtualColumn) => {\n const cell = centerCells[virtualColumn.index];\n if (!cell) return null;\n\n const colIdx = currentColIdx++;\n const canSort = cell.column.getCanSort();\n const canFilter = cell.column.getCanFilter();\n const wrapText = cell.column.columnDef.meta?.wrapText ?? false;\n\n return (\n <TableCell\n key={cell.id}\n cell={cell}\n rowIdx={rowIdx}\n colIdx={colIdx}\n canSort={canSort}\n canFilter={canFilter}\n wrapText={wrapText}\n handleGridKeyDown={handleGridKeyDown}\n />\n );\n })}\n\n {virtualPaddingRight ? (\n <td style={{ display: 'flex', width: virtualPaddingRight }} />\n ) : null}\n </tr>\n );\n })}\n </tbody>\n </table>\n </div>\n </div>\n {table.getVisibleLeafColumns().length === 0 || displayedCount === 0 ? (\n <div>\n <div\n class=\"d-flex flex-column justify-content-center align-items-center p-4\"\n style={{\n position: 'absolute',\n top: 0,\n left: 0,\n right: 0,\n bottom: 0,\n // Allow pointer events (e.g. scrolling) to reach the underlying table.\n pointerEvents: 'none',\n }}\n role=\"status\"\n aria-live=\"polite\"\n >\n <div\n class=\"col-lg-6\"\n style={{\n // Allow selecting and interacting with the empty state content.\n pointerEvents: 'auto',\n }}\n >\n {table.getVisibleLeafColumns().length === 0 ? (\n <TanstackTableEmptyState iconName=\"bi-eye-slash\">\n No columns selected. Use the View menu to show columns.\n </TanstackTableEmptyState>\n ) : displayedCount === 0 ? (\n totalCount > 0 ? (\n noResultsState\n ) : (\n emptyState\n )\n ) : null}\n </div>\n </div>\n </div>\n ) : null}\n </div>\n );\n}\n\n/**\n * A generic component that wraps the TanstackTable component in a card.\n * @param params\n * @param params.table - The table model\n * @param params.title - The title of the card\n * @param params.className - The class name to apply to the card\n * @param params.style - The style to apply to the card\n * @param params.singularLabel - The singular label for a single row in the table, e.g. \"student\"\n * @param params.pluralLabel - The plural label for multiple rows in the table, e.g. \"students\"\n * @param params.headerButtons - The buttons to display in the header\n * @param params.columnManagerButtons - The buttons to display next to the column manager (View button)\n * @param params.columnManagerTopContent - Optional content to display at the top of the column manager (View) dropdown menu\n * @param params.globalFilter - State management for the global filter\n * @param params.globalFilter.value\n * @param params.globalFilter.setValue\n * @param params.globalFilter.placeholder\n * @param params.tableOptions - Specific options for the table. See {@link TanstackTableProps} for more details.\n * @param params.downloadButtonOptions - Specific options for the download button. See {@link TanstackTableDownloadButtonProps} for more details.\n */\nexport function TanstackTableCard<RowDataModel>({\n table,\n title,\n singularLabel,\n pluralLabel,\n headerButtons,\n columnManagerButtons,\n columnManagerTopContent,\n globalFilter,\n tableOptions,\n downloadButtonOptions,\n className,\n ...divProps\n}: {\n table: Table<RowDataModel>;\n title: string;\n singularLabel: string;\n pluralLabel: string;\n headerButtons: JSX.Element;\n columnManagerButtons?: JSX.Element;\n columnManagerTopContent?: JSX.Element;\n globalFilter: {\n value: string;\n setValue: (value: string) => void;\n placeholder: string;\n };\n tableOptions: Partial<Omit<TanstackTableProps<RowDataModel>, 'table'>>;\n downloadButtonOptions?: Omit<\n TanstackTableDownloadButtonProps<RowDataModel>,\n 'table' | 'singularLabel' | 'pluralLabel'\n >;\n} & Omit<ComponentProps<'div'>, 'class'>) {\n const searchInputRef = useRef<HTMLInputElement>(null);\n\n // Focus the search input when Ctrl+F is pressed\n useEffect(() => {\n function onKeyDown(event: KeyboardEvent) {\n if ((event.ctrlKey || event.metaKey) && event.key.toLowerCase() === 'f') {\n if (searchInputRef.current && searchInputRef.current !== document.activeElement) {\n searchInputRef.current.focus();\n event.preventDefault();\n }\n }\n }\n document.addEventListener('keydown', onKeyDown);\n return () => document.removeEventListener('keydown', onKeyDown);\n }, []);\n\n const displayedCount = table.getRowModel().rows.length;\n const totalCount = table.getCoreRowModel().rows.length;\n\n return (\n <div class={clsx('card d-flex flex-column', className)} {...divProps}>\n <div class=\"card-header bg-primary text-white\">\n <div class=\"d-flex align-items-center justify-content-between gap-2\">\n <div>{title}</div>\n <div class=\"d-flex gap-2\">\n {headerButtons}\n\n {downloadButtonOptions && (\n <TanstackTableDownloadButton\n table={table}\n pluralLabel={pluralLabel}\n singularLabel={singularLabel}\n {...downloadButtonOptions}\n />\n )}\n </div>\n </div>\n </div>\n <div class=\"card-body d-flex flex-row flex-wrap flex-grow-0 align-items-center gap-2\">\n <div class=\"position-relative w-100\" style={{ maxWidth: 'min(400px, 100%)' }}>\n <input\n ref={searchInputRef}\n type=\"text\"\n class=\"form-control pl-ui-tanstack-table-search-input pl-ui-tanstack-table-focusable-shadow\"\n aria-label={globalFilter.placeholder}\n placeholder={globalFilter.placeholder}\n value={globalFilter.value}\n autoComplete=\"off\"\n onInput={(e) => {\n if (!(e.target instanceof HTMLInputElement)) return;\n globalFilter.setValue(e.target.value);\n }}\n />\n {globalFilter.value && (\n <OverlayTrigger overlay={<Tooltip>Clear search</Tooltip>}>\n <button\n type=\"button\"\n class=\"btn btn-floating-icon\"\n aria-label=\"Clear search\"\n onClick={() => globalFilter.setValue('')}\n >\n <i class=\"bi bi-x-circle-fill\" aria-hidden=\"true\" />\n </button>\n </OverlayTrigger>\n )}\n </div>\n <div class=\"d-flex flex-wrap flex-row align-items-center gap-2\">\n <ColumnManager table={table} topContent={columnManagerTopContent} />\n {columnManagerButtons}\n </div>\n <div class=\"ms-auto text-muted text-nowrap\">\n Showing {displayedCount} of {totalCount} {totalCount === 1 ? singularLabel : pluralLabel}\n </div>\n </div>\n <div class=\"flex-grow-1\">\n <TanstackTable table={table} title={title} {...tableOptions} />\n </div>\n </div>\n );\n}\n\nexport function TanstackTableEmptyState({\n iconName,\n children,\n}: {\n iconName: `bi-${string}`;\n children: ComponentChildren;\n}) {\n return (\n <div class=\"d-flex flex-column justify-content-center align-items-center text-muted\">\n <i class={clsx('bi', iconName, 'display-4 mb-2')} aria-hidden=\"true\" />\n <div>{children}</div>\n </div>\n );\n}\n"]}
|
|
1
|
+
{"version":3,"file":"TanstackTable.js","sourceRoot":"","sources":["../../src/components/TanstackTable.tsx"],"names":[],"mappings":"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","sourcesContent":["import { flexRender } from '@tanstack/react-table';\nimport { useVirtualizer } from '@tanstack/react-virtual';\nimport type { Cell, Header, Row, Table } from '@tanstack/table-core';\nimport clsx from 'clsx';\nimport type { ComponentChildren } from 'preact';\nimport { useEffect, useMemo, useRef, useState } from 'preact/hooks';\nimport type { JSX } from 'preact/jsx-runtime';\nimport OverlayTrigger from 'react-bootstrap/OverlayTrigger';\nimport Tooltip from 'react-bootstrap/Tooltip';\nimport { useDebouncedCallback } from 'use-debounce';\n\nimport type { ComponentProps } from '@prairielearn/preact-cjs';\nimport { run } from '@prairielearn/run';\n\nimport { ColumnManager } from './ColumnManager.js';\nimport {\n TanstackTableDownloadButton,\n type TanstackTableDownloadButtonProps,\n} from './TanstackTableDownloadButton.js';\nimport { TanstackTableHeaderCell } from './TanstackTableHeaderCell.js';\nimport { useAutoSizeColumns } from './useAutoSizeColumns.js';\n\nfunction TableCell<RowDataModel>({\n cell,\n rowIdx,\n colIdx,\n canSort,\n canFilter,\n wrapText,\n handleGridKeyDown,\n}: {\n cell: Cell<RowDataModel, unknown>;\n rowIdx: number;\n colIdx: number;\n canSort: boolean;\n canFilter: boolean;\n wrapText: boolean;\n handleGridKeyDown: (e: KeyboardEvent, rowIdx: number, colIdx: number) => void;\n}) {\n return (\n <td\n key={cell.id}\n tabIndex={0}\n data-grid-cell-row={rowIdx}\n data-grid-cell-col={colIdx}\n class={clsx(!canSort && !canFilter && 'text-center')}\n style={{\n display: 'flex',\n width: cell.column.getSize(),\n minWidth: 0,\n maxWidth: cell.column.getSize(),\n flexShrink: 0,\n position: cell.column.getIsPinned() === 'left' ? 'sticky' : undefined,\n left: cell.column.getIsPinned() === 'left' ? cell.column.getStart() : undefined,\n verticalAlign: 'middle',\n }}\n onKeyDown={(e) => handleGridKeyDown(e, rowIdx, colIdx)}\n >\n <div\n style={{\n display: 'block',\n minWidth: 0,\n maxWidth: '100%',\n overflow: wrapText ? 'visible' : 'hidden',\n textOverflow: wrapText ? undefined : 'ellipsis',\n whiteSpace: wrapText ? 'normal' : 'nowrap',\n flex: '1 1 0%',\n width: 0, // Allow flex to control width, but start from 0\n }}\n >\n {flexRender(cell.column.columnDef.cell, cell.getContext())}\n </div>\n </td>\n );\n}\n\nconst DefaultNoResultsState = (\n <TanstackTableEmptyState iconName=\"bi-search\">\n No results found matching your search criteria.\n </TanstackTableEmptyState>\n);\n\nconst DefaultEmptyState = (\n <TanstackTableEmptyState iconName=\"bi-eye-slash\">No results found.</TanstackTableEmptyState>\n);\n\ninterface TanstackTableProps<RowDataModel> {\n table: Table<RowDataModel>;\n title: string;\n filters?: Record<string, (props: { header: Header<RowDataModel, unknown> }) => JSX.Element>;\n rowHeight?: number;\n noResultsState?: JSX.Element;\n emptyState?: JSX.Element;\n scrollRef?: React.RefObject<HTMLDivElement> | null;\n}\n\nconst DEFAULT_FILTER_MAP = {};\n\n/**\n * A generic component that renders a full-width, resizeable Tanstack Table.\n * @param params\n * @param params.table - The table model\n * @param params.title - The title of the table\n * @param params.filters - The filters for the table\n * @param params.rowHeight - The height of the rows in the table\n * @param params.noResultsState - The no results state for the table\n * @param params.emptyState - The empty state for the table\n * @param params.scrollRef - Optional ref that will be attached to the scroll container element.\n */\nexport function TanstackTable<RowDataModel>({\n table,\n title,\n filters = DEFAULT_FILTER_MAP,\n rowHeight = 42,\n noResultsState = DefaultNoResultsState,\n emptyState = DefaultEmptyState,\n scrollRef,\n}: TanstackTableProps<RowDataModel>) {\n const parentRef = useRef<HTMLDivElement>(null);\n const tableRef = useRef<HTMLDivElement>(null);\n const scrollContainerRef = scrollRef ?? parentRef;\n\n const rows = [...table.getTopRows(), ...table.getCenterRows()];\n const rowVirtualizer = useVirtualizer({\n count: rows.length,\n getScrollElement: () => scrollContainerRef.current,\n estimateSize: () => rowHeight,\n overscan: 10,\n measureElement: (el) => el?.getBoundingClientRect().height ?? rowHeight,\n });\n\n const visibleColumns = table.getVisibleLeafColumns();\n const centerColumns = visibleColumns.filter((col) => !col.getIsPinned());\n\n const columnVirtualizer = useVirtualizer({\n count: centerColumns.length,\n estimateSize: (index) => centerColumns[index]?.getSize(),\n // `useAutoSizeColumns` solves a different problem (happens once when the column set changes)\n // and we don't need to measure the cells themselves, so we can use the default estimateSize.\n getScrollElement: () => scrollContainerRef.current,\n horizontal: true,\n overscan: 3,\n });\n\n const virtualColumns = columnVirtualizer.getVirtualItems();\n\n const virtualPaddingLeft = run(() => {\n if (columnVirtualizer && virtualColumns?.length > 0) {\n return virtualColumns[0]?.start ?? 0;\n }\n return null;\n });\n\n const virtualPaddingRight = run(() => {\n if (columnVirtualizer && virtualColumns?.length > 0) {\n return (\n columnVirtualizer.getTotalSize() - (virtualColumns[virtualColumns.length - 1]?.end ?? 0)\n );\n }\n return null;\n });\n\n // Check if any column has wrapping enabled\n const hasWrappedColumns = table.getAllLeafColumns().some((col) => col.columnDef.meta?.wrapText);\n\n // Create callback for remeasuring after resize\n const handleResizeEnd = useMemo(() => {\n if (!hasWrappedColumns) return undefined;\n return () => rowVirtualizer.measure();\n }, [hasWrappedColumns, rowVirtualizer]);\n\n const getVisibleCells = (row: Row<RowDataModel>) => [\n ...row.getLeftVisibleCells(),\n ...row.getCenterVisibleCells(),\n ];\n\n const handleGridKeyDown = (e: KeyboardEvent, rowIdx: number, colIdx: number) => {\n const rowLength = getVisibleCells(rows[rowIdx]).length;\n const adjacentCells: Record<KeyboardEvent['key'], { row: number; col: number }> = {\n ArrowDown: {\n row: Math.min(rows.length - 1, rowIdx + 1),\n col: colIdx,\n },\n ArrowUp: {\n row: Math.max(0, rowIdx - 1),\n col: colIdx,\n },\n ArrowRight: {\n row: rowIdx,\n col: Math.min(rowLength - 1, colIdx + 1),\n },\n ArrowLeft: {\n row: rowIdx,\n col: Math.max(0, colIdx - 1),\n },\n };\n\n const next = adjacentCells[e.key];\n\n if (!next) {\n return;\n }\n\n // Only handle arrow keys if we're in the cell itself, not in an interactive element\n const target = e.target as HTMLElement;\n if (target.tagName === 'TD') {\n // If we are on the leftmost column, we should allow left scrolling.\n if (colIdx === 0 && e.key === 'ArrowLeft') {\n return;\n }\n\n // If we are on the top row, we should allow up scrolling.\n if (rowIdx === 0 && e.key === 'ArrowUp') {\n return;\n }\n\n // If we are on the rightmost column, we should allow right scrolling.\n if (colIdx === rowLength - 1 && e.key === 'ArrowRight') {\n return;\n }\n\n e.preventDefault();\n const selector = `[data-grid-cell-row=\"${next.row}\"][data-grid-cell-col=\"${next.col}\"]`;\n const nextCell = tableRef.current?.querySelector(selector) as HTMLElement | null;\n nextCell?.focus();\n }\n };\n\n const virtualRows = rowVirtualizer.getVirtualItems();\n\n const headerGroups = table.getHeaderGroups();\n\n const leafHeaderGroup = headerGroups[headerGroups.length - 1];\n\n const leftPinnedHeaders = leafHeaderGroup.headers.filter(\n (header) => header.column.getIsPinned() === 'left',\n );\n const centerHeaders = leafHeaderGroup.headers.filter((header) => !header.column.getIsPinned());\n\n const isTableResizing = leafHeaderGroup.headers.some((header) => header.column.getIsResizing());\n\n // We toggle this here instead of in the parent since this component logically manages all UI for the table.\n useEffect(() => {\n document.body.classList.toggle('pl-ui-no-user-select', isTableResizing);\n }, [isTableResizing]);\n\n const hasAutoSized = useAutoSizeColumns(table, tableRef, filters);\n\n // Re-measure the virtualizer when auto-sizing completes\n useEffect(() => {\n if (hasAutoSized) {\n columnVirtualizer.measure();\n }\n }, [columnVirtualizer, hasAutoSized]);\n\n const displayedCount = table.getRowModel().rows.length;\n const totalCount = table.getCoreRowModel().rows.length;\n\n return (\n <div style={{ position: 'relative' }} class=\"d-flex flex-column h-100\">\n <div\n ref={scrollContainerRef}\n style={{\n position: 'absolute',\n top: 0,\n left: 0,\n right: 0,\n bottom: 0,\n overflow: 'auto',\n overflowAnchor: 'none',\n }}\n >\n <div\n ref={tableRef}\n style={{\n position: 'relative',\n width: `max(${table.getTotalSize()}px, 100%)`,\n }}\n >\n <table\n class=\"table table-hover mb-0\"\n style={{ display: 'grid', tableLayout: 'fixed' }}\n aria-label={title}\n role=\"grid\"\n >\n <thead\n class=\"position-sticky top-0 w-100 border-top\"\n style={{\n display: 'grid',\n zIndex: 1,\n borderBottom: 'var(--bs-border-width) solid black',\n }}\n >\n <tr\n key={leafHeaderGroup.id}\n class=\"d-flex w-100\"\n style={{ minWidth: `${table.getTotalSize()}px` }}\n >\n {/* Left pinned columns */}\n {leftPinnedHeaders.map((header) => {\n return (\n <TanstackTableHeaderCell\n key={header.id}\n header={header}\n filters={filters}\n table={table}\n handleResizeEnd={handleResizeEnd}\n isPinned=\"left\"\n />\n );\n })}\n\n {/* Virtual padding for left side of center columns */}\n {virtualPaddingLeft ? (\n <th style={{ display: 'flex', width: virtualPaddingLeft }} />\n ) : null}\n\n {/* Virtualized center columns */}\n {virtualColumns.map((virtualColumn) => {\n const header = centerHeaders[virtualColumn.index];\n if (!header) return null;\n\n return (\n <TanstackTableHeaderCell\n key={header.id}\n header={header}\n filters={filters}\n table={table}\n handleResizeEnd={handleResizeEnd}\n isPinned={false}\n />\n );\n })}\n\n {/* Virtual padding for right side of center columns */}\n {virtualPaddingRight ? (\n <th style={{ display: 'flex', width: virtualPaddingRight }} />\n ) : null}\n\n {/* Filler to span remaining width */}\n <th\n tabIndex={-1}\n class=\"d-flex flex-grow-1 p-0\"\n style={{ minWidth: 0 }}\n aria-hidden=\"true\"\n />\n </tr>\n </thead>\n <tbody\n class=\"position-relative w-100\"\n style={{\n display: 'grid',\n height: `${rowVirtualizer.getTotalSize()}px`,\n }}\n >\n {virtualRows.map((virtualRow) => {\n const row = rows[virtualRow.index];\n const rowIdx = virtualRow.index;\n const leftPinnedCells = row.getLeftVisibleCells();\n const centerCells = row.getCenterVisibleCells();\n\n let currentColIdx = 0;\n\n return (\n <tr\n key={row.id}\n ref={(node) => rowVirtualizer.measureElement(node)}\n data-index={virtualRow.index}\n class=\"d-flex position-absolute w-100\"\n style={{\n transform: `translateY(${virtualRow.start}px)`,\n minWidth: `${table.getTotalSize()}px`,\n }}\n >\n {leftPinnedCells.map((cell) => {\n const colIdx = currentColIdx++;\n const canSort = cell.column.getCanSort();\n const canFilter = cell.column.getCanFilter();\n const wrapText = cell.column.columnDef.meta?.wrapText ?? false;\n\n return (\n <TableCell\n key={cell.id}\n cell={cell}\n rowIdx={rowIdx}\n colIdx={colIdx}\n canSort={canSort}\n canFilter={canFilter}\n wrapText={wrapText}\n handleGridKeyDown={handleGridKeyDown}\n />\n );\n })}\n\n {virtualPaddingLeft ? (\n <td style={{ display: 'flex', width: virtualPaddingLeft }} />\n ) : null}\n\n {virtualColumns.map((virtualColumn) => {\n const cell = centerCells[virtualColumn.index];\n if (!cell) return null;\n\n const colIdx = currentColIdx++;\n const canSort = cell.column.getCanSort();\n const canFilter = cell.column.getCanFilter();\n const wrapText = cell.column.columnDef.meta?.wrapText ?? false;\n\n return (\n <TableCell\n key={cell.id}\n cell={cell}\n rowIdx={rowIdx}\n colIdx={colIdx}\n canSort={canSort}\n canFilter={canFilter}\n wrapText={wrapText}\n handleGridKeyDown={handleGridKeyDown}\n />\n );\n })}\n\n {virtualPaddingRight ? (\n <td style={{ display: 'flex', width: virtualPaddingRight }} />\n ) : null}\n\n {/* Filler to span remaining width */}\n <td\n tabIndex={-1}\n class=\"d-flex flex-grow-1 p-0\"\n style={{ minWidth: 0 }}\n aria-hidden=\"true\"\n />\n </tr>\n );\n })}\n </tbody>\n </table>\n </div>\n </div>\n {table.getVisibleLeafColumns().length === 0 || displayedCount === 0 ? (\n <div>\n <div\n class=\"d-flex flex-column justify-content-center align-items-center p-4\"\n style={{\n position: 'absolute',\n top: 0,\n left: 0,\n right: 0,\n bottom: 0,\n // Allow pointer events (e.g. scrolling) to reach the underlying table.\n pointerEvents: 'none',\n }}\n role=\"status\"\n aria-live=\"polite\"\n >\n <div\n class=\"col-lg-6\"\n style={{\n // Allow selecting and interacting with the empty state content.\n pointerEvents: 'auto',\n }}\n >\n {table.getVisibleLeafColumns().length === 0 ? (\n <TanstackTableEmptyState iconName=\"bi-eye-slash\">\n No columns selected. Use the View menu to show columns.\n </TanstackTableEmptyState>\n ) : displayedCount === 0 ? (\n totalCount > 0 ? (\n noResultsState\n ) : (\n emptyState\n )\n ) : null}\n </div>\n </div>\n </div>\n ) : null}\n </div>\n );\n}\n\n/**\n * A generic component that wraps the TanstackTable component in a card.\n * @param params\n * @param params.table - The table model\n * @param params.title - The title of the card\n * @param params.className - The class name to apply to the card\n * @param params.style - The style to apply to the card\n * @param params.singularLabel - The singular label for a single row in the table, e.g. \"student\"\n * @param params.pluralLabel - The plural label for multiple rows in the table, e.g. \"students\"\n * @param params.headerButtons - The buttons to display in the header\n * @param params.columnManager - Optional configuration for the column manager. See {@link ColumnManager} for more details.\n * @param params.columnManager.buttons - The buttons to display next to the column manager (View button)\n * @param params.columnManager.topContent - Optional content to display at the top of the column manager (View) dropdown menu\n * @param params.globalFilter - Configuration for the global filter\n * @param params.globalFilter.placeholder - Placeholder text for the search input\n * @param params.tableOptions - Specific options for the table. See {@link TanstackTableProps} for more details.\n * @param params.downloadButtonOptions - Specific options for the download button. See {@link TanstackTableDownloadButtonProps} for more details.\n */\nexport function TanstackTableCard<RowDataModel>({\n table,\n title,\n singularLabel,\n pluralLabel,\n headerButtons,\n columnManager,\n globalFilter,\n tableOptions,\n downloadButtonOptions,\n className,\n ...divProps\n}: {\n table: Table<RowDataModel>;\n title: string;\n singularLabel: string;\n pluralLabel: string;\n headerButtons?: JSX.Element;\n columnManager?: {\n buttons?: JSX.Element;\n topContent?: JSX.Element;\n };\n globalFilter: {\n placeholder: string;\n };\n tableOptions: Partial<Omit<TanstackTableProps<RowDataModel>, 'table'>>;\n downloadButtonOptions?: Omit<\n TanstackTableDownloadButtonProps<RowDataModel>,\n 'table' | 'singularLabel' | 'pluralLabel'\n > & { pluralLabel?: string; singularLabel?: string };\n} & Omit<ComponentProps<'div'>, 'class'>) {\n const searchInputRef = useRef<HTMLInputElement>(null);\n\n const [inputValue, setInputValue] = useState(\n () => (table.getState().globalFilter as string) ?? '',\n );\n\n // Debounce the filter update\n const debouncedSetFilter = useDebouncedCallback((value: string) => {\n table.setGlobalFilter(value);\n }, 150);\n\n // Focus the search input when Ctrl+F is pressed\n useEffect(() => {\n function onKeyDown(event: KeyboardEvent) {\n if ((event.ctrlKey || event.metaKey) && event.key.toLowerCase() === 'f') {\n if (searchInputRef.current && searchInputRef.current !== document.activeElement) {\n searchInputRef.current.focus();\n event.preventDefault();\n }\n }\n }\n document.addEventListener('keydown', onKeyDown);\n return () => document.removeEventListener('keydown', onKeyDown);\n }, []);\n\n const displayedCount = table.getRowModel().rows.length;\n const totalCount = table.getCoreRowModel().rows.length;\n\n return (\n <div class={clsx('card d-flex flex-column', className)} {...divProps}>\n <div class=\"card-header bg-primary text-white\">\n <div class=\"d-flex align-items-center justify-content-between gap-2\">\n <div>{title}</div>\n <div class=\"d-flex gap-2\">\n {headerButtons}\n\n {downloadButtonOptions && (\n <TanstackTableDownloadButton\n table={table}\n pluralLabel={pluralLabel}\n singularLabel={singularLabel}\n {...downloadButtonOptions}\n />\n )}\n </div>\n </div>\n </div>\n <div class=\"card-body d-flex flex-row flex-wrap flex-grow-0 align-items-center gap-2\">\n <div class=\"position-relative w-100\" style={{ maxWidth: 'min(400px, 100%)' }}>\n <input\n ref={searchInputRef}\n type=\"text\"\n class=\"form-control pl-ui-tanstack-table-search-input pl-ui-tanstack-table-focusable-shadow\"\n aria-label={globalFilter.placeholder}\n placeholder={globalFilter.placeholder}\n value={inputValue}\n autoComplete=\"off\"\n onInput={(e) => {\n if (!(e.target instanceof HTMLInputElement)) return;\n const value = e.target.value;\n setInputValue(value);\n debouncedSetFilter(value);\n }}\n />\n {inputValue && (\n <OverlayTrigger overlay={<Tooltip>Clear search</Tooltip>}>\n <button\n type=\"button\"\n class=\"btn btn-floating-icon\"\n aria-label=\"Clear search\"\n onClick={() => {\n setInputValue('');\n debouncedSetFilter.cancel();\n table.setGlobalFilter('');\n }}\n >\n <i class=\"bi bi-x-circle-fill\" aria-hidden=\"true\" />\n </button>\n </OverlayTrigger>\n )}\n </div>\n <div class=\"d-flex flex-wrap flex-row align-items-center gap-2\">\n <ColumnManager table={table} topContent={columnManager?.topContent} />\n {columnManager?.buttons}\n </div>\n <div class=\"ms-auto text-muted text-nowrap\">\n Showing {displayedCount} of {totalCount} {totalCount === 1 ? singularLabel : pluralLabel}\n </div>\n </div>\n <div class=\"flex-grow-1\">\n <TanstackTable table={table} title={title} {...tableOptions} />\n </div>\n </div>\n );\n}\n\nexport function TanstackTableEmptyState({\n iconName,\n children,\n}: {\n iconName: `bi-${string}`;\n children: ComponentChildren;\n}) {\n return (\n <div class=\"d-flex flex-column justify-content-center align-items-center text-muted\">\n <i class={clsx('bi', iconName, 'display-4 mb-2')} aria-hidden=\"true\" />\n <div>{children}</div>\n </div>\n );\n}\n"]}
|
|
@@ -5,17 +5,19 @@ export interface TanstackTableDownloadButtonProps<RowDataModel> {
|
|
|
5
5
|
mapRowToData: (row: RowDataModel) => Record<string, string | number | null> | null;
|
|
6
6
|
singularLabel: string;
|
|
7
7
|
pluralLabel: string;
|
|
8
|
+
hasSelection: boolean;
|
|
8
9
|
}
|
|
9
10
|
/**
|
|
10
11
|
* @param params
|
|
11
|
-
* @param params.singularLabel - The singular label for a single row in the table, e.g. "student"
|
|
12
|
-
* @param params.pluralLabel - The plural label for multiple rows in the table, e.g. "students"
|
|
13
12
|
* @param params.table - The table model
|
|
14
13
|
* @param params.filenameBase - The base filename for the downloads
|
|
15
14
|
* @param params.mapRowToData - A function that maps a row to a record where the
|
|
16
15
|
* keys are the column names, and the values are the cell values. The key order is important,
|
|
17
16
|
* and should match the expected order of the columns in the CSV file. If the function returns null,
|
|
18
17
|
* the row will be skipped.
|
|
18
|
+
* @param params.singularLabel - The singular label for a single row in the table, e.g. "student"
|
|
19
|
+
* @param params.pluralLabel - The plural label for multiple rows in the table, e.g. "students"
|
|
20
|
+
* @param params.hasSelection - Whether the table has selection enabled
|
|
19
21
|
*/
|
|
20
|
-
export declare function TanstackTableDownloadButton<RowDataModel>({ table, filenameBase, mapRowToData, singularLabel, pluralLabel, }: TanstackTableDownloadButtonProps<RowDataModel>): import("original-preact").JSX.Element;
|
|
22
|
+
export declare function TanstackTableDownloadButton<RowDataModel>({ table, filenameBase, mapRowToData, singularLabel, pluralLabel, hasSelection, }: TanstackTableDownloadButtonProps<RowDataModel>): import("original-preact").JSX.Element;
|
|
21
23
|
//# sourceMappingURL=TanstackTableDownloadButton.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TanstackTableDownloadButton.d.ts","sourceRoot":"","sources":["../../src/components/TanstackTableDownloadButton.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,uBAAuB,CAAC;AAInD,MAAM,WAAW,gCAAgC,CAAC,YAAY;IAC5D,KAAK,EAAE,KAAK,CAAC,YAAY,CAAC,CAAC;IAC3B,YAAY,EAAE,MAAM,CAAC;IACrB,YAAY,EAAE,CAAC,GAAG,EAAE,YAAY,KAAK,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAAC,GAAG,IAAI,CAAC;IACnF,aAAa,EAAE,MAAM,CAAC;IACtB,WAAW,EAAE,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"TanstackTableDownloadButton.d.ts","sourceRoot":"","sources":["../../src/components/TanstackTableDownloadButton.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,uBAAuB,CAAC;AAInD,MAAM,WAAW,gCAAgC,CAAC,YAAY;IAC5D,KAAK,EAAE,KAAK,CAAC,YAAY,CAAC,CAAC;IAC3B,YAAY,EAAE,MAAM,CAAC;IACrB,YAAY,EAAE,CAAC,GAAG,EAAE,YAAY,KAAK,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAAC,GAAG,IAAI,CAAC;IACnF,aAAa,EAAE,MAAM,CAAC;IACtB,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,OAAO,CAAC;CACvB;AACD;;;;;;;;;;;GAWG;AACH,wBAAgB,2BAA2B,CAAC,YAAY,EAAE,EACxD,KAAK,EACL,YAAY,EACZ,YAAY,EACZ,aAAa,EACb,WAAW,EACX,YAAY,GACb,EAAE,gCAAgC,CAAC,YAAY,CAAC,yCAsHhD"}
|
|
@@ -1,17 +1,18 @@
|
|
|
1
|
-
import { jsx as _jsx, jsxs as _jsxs } from "@prairielearn/preact-cjs/jsx-runtime";
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "@prairielearn/preact-cjs/jsx-runtime";
|
|
2
2
|
import { downloadAsCSV, downloadAsJSON } from '@prairielearn/browser-utils';
|
|
3
3
|
/**
|
|
4
4
|
* @param params
|
|
5
|
-
* @param params.singularLabel - The singular label for a single row in the table, e.g. "student"
|
|
6
|
-
* @param params.pluralLabel - The plural label for multiple rows in the table, e.g. "students"
|
|
7
5
|
* @param params.table - The table model
|
|
8
6
|
* @param params.filenameBase - The base filename for the downloads
|
|
9
7
|
* @param params.mapRowToData - A function that maps a row to a record where the
|
|
10
8
|
* keys are the column names, and the values are the cell values. The key order is important,
|
|
11
9
|
* and should match the expected order of the columns in the CSV file. If the function returns null,
|
|
12
10
|
* the row will be skipped.
|
|
11
|
+
* @param params.singularLabel - The singular label for a single row in the table, e.g. "student"
|
|
12
|
+
* @param params.pluralLabel - The plural label for multiple rows in the table, e.g. "students"
|
|
13
|
+
* @param params.hasSelection - Whether the table has selection enabled
|
|
13
14
|
*/
|
|
14
|
-
export function TanstackTableDownloadButton({ table, filenameBase, mapRowToData, singularLabel, pluralLabel, }) {
|
|
15
|
+
export function TanstackTableDownloadButton({ table, filenameBase, mapRowToData, singularLabel, pluralLabel, hasSelection, }) {
|
|
15
16
|
const allRows = table.getCoreRowModel().rows.map((row) => row.original);
|
|
16
17
|
const allRowsJSON = allRows.map(mapRowToData).filter((row) => row !== null);
|
|
17
18
|
const filteredRows = table.getRowModel().rows.map((row) => row.original);
|
|
@@ -26,6 +27,6 @@ export function TanstackTableDownloadButton({ table, filenameBase, mapRowToData,
|
|
|
26
27
|
const csvRows = jsonRows.map((row) => Object.values(row));
|
|
27
28
|
downloadAsCSV(header, csvRows, filename);
|
|
28
29
|
}
|
|
29
|
-
return (_jsxs("div", { class: "btn-group", children: [_jsxs("button", { type: "button", "data-bs-toggle": "dropdown", "aria-expanded": "false", "aria-haspopup": "true", "aria-label": `Download ${pluralLabel} data in various formats`, class: "btn btn-light btn-sm dropdown-toggle", children: [_jsx("i", { "aria-hidden": "true", class: "pe-2 bi bi-download" }), _jsx("span", { class: "d-none d-sm-inline", children: "Download" })] }), _jsxs("ul", { class: "dropdown-menu", role: "menu", "aria-label": "Download options", children: [_jsx("li", { role: "presentation", children: _jsxs("button", { class: "dropdown-item", type: "button", role: "menuitem", "aria-label": `Download all ${pluralLabel} as CSV file`, disabled: allRowsJSON.length === 0, onClick: () => downloadJSONAsCSV(allRowsJSON, `${filenameBase}.csv`), children: ["All ", pluralLabel, " as CSV"] }) }), _jsx("li", { role: "presentation", children: _jsxs("button", { class: "dropdown-item", type: "button", role: "menuitem", "aria-label": `Download all ${pluralLabel} as JSON file`, disabled: allRowsJSON.length === 0, onClick: () => downloadAsJSON(allRowsJSON, `${filenameBase}.json`), children: ["All ", pluralLabel, " as JSON"] }) }), _jsx("li", { role: "presentation", children: _jsxs("button", { class: "dropdown-item", type: "button", role: "menuitem", "aria-label": `Download selected ${pluralLabel} as CSV file`, disabled: selectedRowsJSON.length === 0, onClick: () => downloadJSONAsCSV(selectedRowsJSON, `${filenameBase}_selected.csv`), children: ["Selected ", selectedRowsJSON.length === 1 ? singularLabel : pluralLabel, " as CSV"] }) }), _jsx("li", { role: "presentation", children: _jsxs("button", { class: "dropdown-item", type: "button", role: "menuitem", "aria-label": `Download selected ${pluralLabel} as JSON file`, disabled: selectedRowsJSON.length === 0, onClick: () => downloadAsJSON(selectedRowsJSON, `${filenameBase}_selected.json`), children: ["Selected ", selectedRowsJSON.length === 1 ? singularLabel : pluralLabel, " as JSON"] }) }), _jsx("li", { role: "presentation", children: _jsxs("button", { class: "dropdown-item", type: "button", role: "menuitem", "aria-label": `Download filtered ${pluralLabel} as CSV file`, disabled: filteredRowsJSON.length === 0, onClick: () => downloadJSONAsCSV(filteredRowsJSON, `${filenameBase}_filtered.csv`), children: ["Filtered ", filteredRowsJSON.length === 1 ? singularLabel : pluralLabel, " as CSV"] }) }), _jsx("li", { role: "presentation", children: _jsxs("button", { class: "dropdown-item", type: "button", role: "menuitem", "aria-label": `Download filtered ${pluralLabel} as JSON file`, disabled: filteredRowsJSON.length === 0, onClick: () => downloadAsJSON(filteredRowsJSON, `${filenameBase}_filtered.json`), children: ["Filtered ", filteredRowsJSON.length === 1 ? singularLabel : pluralLabel, " as JSON"] }) })] })] }));
|
|
30
|
+
return (_jsxs("div", { class: "btn-group", children: [_jsxs("button", { type: "button", "data-bs-toggle": "dropdown", "aria-expanded": "false", "aria-haspopup": "true", "aria-label": `Download ${pluralLabel} data in various formats`, class: "btn btn-light btn-sm dropdown-toggle", children: [_jsx("i", { "aria-hidden": "true", class: "pe-2 bi bi-download" }), _jsx("span", { class: "d-none d-sm-inline", children: "Download" })] }), _jsxs("ul", { class: "dropdown-menu", role: "menu", "aria-label": "Download options", children: [_jsx("li", { role: "presentation", children: _jsxs("button", { class: "dropdown-item", type: "button", role: "menuitem", "aria-label": `Download all ${pluralLabel} as CSV file`, disabled: allRowsJSON.length === 0, onClick: () => downloadJSONAsCSV(allRowsJSON, `${filenameBase}.csv`), children: ["All ", pluralLabel, " (", allRowsJSON.length, ") as CSV"] }) }), _jsx("li", { role: "presentation", children: _jsxs("button", { class: "dropdown-item", type: "button", role: "menuitem", "aria-label": `Download all ${pluralLabel} as JSON file`, disabled: allRowsJSON.length === 0, onClick: () => downloadAsJSON(allRowsJSON, `${filenameBase}.json`), children: ["All ", pluralLabel, " (", allRowsJSON.length, ") as JSON"] }) }), hasSelection && (_jsxs(_Fragment, { children: [_jsx("li", { role: "presentation", children: _jsxs("button", { class: "dropdown-item", type: "button", role: "menuitem", "aria-label": `Download selected ${pluralLabel} as CSV file`, disabled: selectedRowsJSON.length === 0, onClick: () => downloadJSONAsCSV(selectedRowsJSON, `${filenameBase}_selected.csv`), children: ["Selected ", selectedRowsJSON.length === 1 ? singularLabel : pluralLabel, " (", selectedRowsJSON.length, ") as CSV"] }) }), _jsx("li", { role: "presentation", children: _jsxs("button", { class: "dropdown-item", type: "button", role: "menuitem", "aria-label": `Download selected ${pluralLabel} as JSON file`, disabled: selectedRowsJSON.length === 0, onClick: () => downloadAsJSON(selectedRowsJSON, `${filenameBase}_selected.json`), children: ["Selected ", selectedRowsJSON.length === 1 ? singularLabel : pluralLabel, " (", selectedRowsJSON.length, ") as JSON"] }) })] })), _jsx("li", { role: "presentation", children: _jsxs("button", { class: "dropdown-item", type: "button", role: "menuitem", "aria-label": `Download filtered ${pluralLabel} as CSV file`, disabled: filteredRowsJSON.length === 0, onClick: () => downloadJSONAsCSV(filteredRowsJSON, `${filenameBase}_filtered.csv`), children: ["Filtered ", filteredRowsJSON.length === 1 ? singularLabel : pluralLabel, " (", filteredRowsJSON.length, ") as CSV"] }) }), _jsx("li", { role: "presentation", children: _jsxs("button", { class: "dropdown-item", type: "button", role: "menuitem", "aria-label": `Download filtered ${pluralLabel} as JSON file`, disabled: filteredRowsJSON.length === 0, onClick: () => downloadAsJSON(filteredRowsJSON, `${filenameBase}_filtered.json`), children: ["Filtered ", filteredRowsJSON.length === 1 ? singularLabel : pluralLabel, " (", filteredRowsJSON.length, ") as JSON"] }) })] })] }));
|
|
30
31
|
}
|
|
31
32
|
//# sourceMappingURL=TanstackTableDownloadButton.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TanstackTableDownloadButton.js","sourceRoot":"","sources":["../../src/components/TanstackTableDownloadButton.tsx"],"names":[],"mappings":";AAEA,OAAO,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAC;
|
|
1
|
+
{"version":3,"file":"TanstackTableDownloadButton.js","sourceRoot":"","sources":["../../src/components/TanstackTableDownloadButton.tsx"],"names":[],"mappings":"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","sourcesContent":["import type { Table } from '@tanstack/react-table';\n\nimport { downloadAsCSV, downloadAsJSON } from '@prairielearn/browser-utils';\n\nexport interface TanstackTableDownloadButtonProps<RowDataModel> {\n table: Table<RowDataModel>;\n filenameBase: string;\n mapRowToData: (row: RowDataModel) => Record<string, string | number | null> | null;\n singularLabel: string;\n pluralLabel: string;\n hasSelection: boolean;\n}\n/**\n * @param params\n * @param params.table - The table model\n * @param params.filenameBase - The base filename for the downloads\n * @param params.mapRowToData - A function that maps a row to a record where the\n * keys are the column names, and the values are the cell values. The key order is important,\n * and should match the expected order of the columns in the CSV file. If the function returns null,\n * the row will be skipped.\n * @param params.singularLabel - The singular label for a single row in the table, e.g. \"student\"\n * @param params.pluralLabel - The plural label for multiple rows in the table, e.g. \"students\"\n * @param params.hasSelection - Whether the table has selection enabled\n */\nexport function TanstackTableDownloadButton<RowDataModel>({\n table,\n filenameBase,\n mapRowToData,\n singularLabel,\n pluralLabel,\n hasSelection,\n}: TanstackTableDownloadButtonProps<RowDataModel>) {\n const allRows = table.getCoreRowModel().rows.map((row) => row.original);\n const allRowsJSON = allRows.map(mapRowToData).filter((row) => row !== null);\n const filteredRows = table.getRowModel().rows.map((row) => row.original);\n const filteredRowsJSON = filteredRows.map(mapRowToData).filter((row) => row !== null);\n const selectedRows = table.getSelectedRowModel().rows.map((row) => row.original);\n const selectedRowsJSON = selectedRows.map(mapRowToData).filter((row) => row !== null);\n\n function downloadJSONAsCSV(\n jsonRows: Record<string, string | number | null>[],\n filename: string,\n ): void {\n if (jsonRows.length === 0) {\n throw new Error('No rows to download');\n }\n\n const header = Object.keys(jsonRows[0]);\n const csvRows = jsonRows.map((row) => Object.values(row));\n downloadAsCSV(header, csvRows, filename);\n }\n\n return (\n <div class=\"btn-group\">\n <button\n type=\"button\"\n data-bs-toggle=\"dropdown\"\n aria-expanded=\"false\"\n aria-haspopup=\"true\"\n aria-label={`Download ${pluralLabel} data in various formats`}\n class=\"btn btn-light btn-sm dropdown-toggle\"\n >\n <i aria-hidden=\"true\" class=\"pe-2 bi bi-download\" />\n <span class=\"d-none d-sm-inline\">Download</span>\n </button>\n <ul class=\"dropdown-menu\" role=\"menu\" aria-label=\"Download options\">\n <li role=\"presentation\">\n <button\n class=\"dropdown-item\"\n type=\"button\"\n role=\"menuitem\"\n aria-label={`Download all ${pluralLabel} as CSV file`}\n disabled={allRowsJSON.length === 0}\n onClick={() => downloadJSONAsCSV(allRowsJSON, `${filenameBase}.csv`)}\n >\n All {pluralLabel} ({allRowsJSON.length}) as CSV\n </button>\n </li>\n <li role=\"presentation\">\n <button\n class=\"dropdown-item\"\n type=\"button\"\n role=\"menuitem\"\n aria-label={`Download all ${pluralLabel} as JSON file`}\n disabled={allRowsJSON.length === 0}\n onClick={() => downloadAsJSON(allRowsJSON, `${filenameBase}.json`)}\n >\n All {pluralLabel} ({allRowsJSON.length}) as JSON\n </button>\n </li>\n {hasSelection && (\n <>\n <li role=\"presentation\">\n <button\n class=\"dropdown-item\"\n type=\"button\"\n role=\"menuitem\"\n aria-label={`Download selected ${pluralLabel} as CSV file`}\n disabled={selectedRowsJSON.length === 0}\n onClick={() => downloadJSONAsCSV(selectedRowsJSON, `${filenameBase}_selected.csv`)}\n >\n Selected {selectedRowsJSON.length === 1 ? singularLabel : pluralLabel} (\n {selectedRowsJSON.length}) as CSV\n </button>\n </li>\n <li role=\"presentation\">\n <button\n class=\"dropdown-item\"\n type=\"button\"\n role=\"menuitem\"\n aria-label={`Download selected ${pluralLabel} as JSON file`}\n disabled={selectedRowsJSON.length === 0}\n onClick={() => downloadAsJSON(selectedRowsJSON, `${filenameBase}_selected.json`)}\n >\n Selected {selectedRowsJSON.length === 1 ? singularLabel : pluralLabel} (\n {selectedRowsJSON.length}) as JSON\n </button>\n </li>\n </>\n )}\n <li role=\"presentation\">\n <button\n class=\"dropdown-item\"\n type=\"button\"\n role=\"menuitem\"\n aria-label={`Download filtered ${pluralLabel} as CSV file`}\n disabled={filteredRowsJSON.length === 0}\n onClick={() => downloadJSONAsCSV(filteredRowsJSON, `${filenameBase}_filtered.csv`)}\n >\n Filtered {filteredRowsJSON.length === 1 ? singularLabel : pluralLabel} (\n {filteredRowsJSON.length}) as CSV\n </button>\n </li>\n <li role=\"presentation\">\n <button\n class=\"dropdown-item\"\n type=\"button\"\n role=\"menuitem\"\n aria-label={`Download filtered ${pluralLabel} as JSON file`}\n disabled={filteredRowsJSON.length === 0}\n onClick={() => downloadAsJSON(filteredRowsJSON, `${filenameBase}_filtered.json`)}\n >\n Filtered {filteredRowsJSON.length === 1 ? singularLabel : pluralLabel} (\n {filteredRowsJSON.length}) as JSON\n </button>\n </li>\n </ul>\n </div>\n );\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TanstackTableHeaderCell.d.ts","sourceRoot":"","sources":["../../src/components/TanstackTableHeaderCell.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,MAAM,EAAiB,KAAK,EAAE,MAAM,sBAAsB,CAAC;AAEzE,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,oBAAoB,CAAC;AA8F9C,wBAAgB,uBAAuB,CAAC,YAAY,EAAE,EACpD,MAAM,EACN,OAAO,EACP,KAAK,EACL,eAAe,EACf,QAAQ,EACR,eAAuB,GACxB,EAAE;IACD,MAAM,EAAE,MAAM,CAAC,YAAY,EAAE,OAAO,CAAC,CAAC;IACtC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC,KAAK,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC,YAAY,EAAE,OAAO,CAAC,CAAA;KAAE,KAAK,GAAG,CAAC,OAAO,CAAC,CAAC;IAC3F,KAAK,EAAE,KAAK,CAAC,YAAY,CAAC,CAAC;IAC3B,eAAe,CAAC,EAAE,MAAM,IAAI,CAAC;IAC7B,QAAQ,EAAE,MAAM,GAAG,KAAK,CAAC;IACzB,eAAe,CAAC,EAAE,OAAO,CAAC;CAC3B,
|
|
1
|
+
{"version":3,"file":"TanstackTableHeaderCell.d.ts","sourceRoot":"","sources":["../../src/components/TanstackTableHeaderCell.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,MAAM,EAAiB,KAAK,EAAE,MAAM,sBAAsB,CAAC;AAEzE,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,oBAAoB,CAAC;AA8F9C,wBAAgB,uBAAuB,CAAC,YAAY,EAAE,EACpD,MAAM,EACN,OAAO,EACP,KAAK,EACL,eAAe,EACf,QAAQ,EACR,eAAuB,GACxB,EAAE;IACD,MAAM,EAAE,MAAM,CAAC,YAAY,EAAE,OAAO,CAAC,CAAC;IACtC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC,KAAK,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC,YAAY,EAAE,OAAO,CAAC,CAAA;KAAE,KAAK,GAAG,CAAC,OAAO,CAAC,CAAC;IAC3F,KAAK,EAAE,KAAK,CAAC,YAAY,CAAC,CAAC;IAC3B,eAAe,CAAC,EAAE,MAAM,IAAI,CAAC;IAC7B,QAAQ,EAAE,MAAM,GAAG,KAAK,CAAC;IACzB,eAAe,CAAC,EAAE,OAAO,CAAC;CAC3B,eA6FA"}
|
|
@@ -77,7 +77,6 @@ export function TanstackTableHeaderCell({ header, filters, table, handleResizeEn
|
|
|
77
77
|
top: 0,
|
|
78
78
|
zIndex: isPinned === 'left' ? 2 : 1,
|
|
79
79
|
left: isPinned === 'left' ? header.getStart() : undefined,
|
|
80
|
-
boxShadow: 'inset 0 calc(-1 * var(--bs-border-width)) 0 0 rgba(0, 0, 0, 1), inset 0 var(--bs-border-width) 0 0 var(--bs-border-color)',
|
|
81
80
|
};
|
|
82
81
|
const isNormalColumn = canSort || canFilter;
|
|
83
82
|
return (_jsxs("th", { "data-column-id": header.column.id, class: clsx(isPinned === 'left' && 'bg-light'), style: style, "aria-sort": canSort ? getAriaSort(sortDirection) : undefined, role: "columnheader", children: [_jsxs("div", { class: clsx('d-flex align-items-center flex-grow-1', isNormalColumn ? 'justify-content-between' : 'justify-content-center'), style: {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TanstackTableHeaderCell.js","sourceRoot":"","sources":["../../src/components/TanstackTableHeaderCell.tsx"],"names":[],"mappings":"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","sourcesContent":["import { flexRender } from '@tanstack/react-table';\nimport type { Header, SortDirection, Table } from '@tanstack/table-core';\nimport clsx from 'clsx';\nimport type { JSX } from 'preact/jsx-runtime';\n\nfunction SortIcon({ sortMethod }: { sortMethod: false | SortDirection }) {\n if (sortMethod === 'asc') {\n return <i class=\"bi bi-sort-up-alt\" aria-hidden=\"true\" />;\n } else if (sortMethod === 'desc') {\n return <i class=\"bi bi-sort-down\" aria-hidden=\"true\" />;\n } else {\n return <i class=\"bi bi-arrow-down-up opacity-75 text-muted\" aria-hidden=\"true\" />;\n }\n}\n\nfunction ResizeHandle<RowDataModel>({\n header,\n setColumnSizing,\n onResizeEnd,\n}: {\n header: Header<RowDataModel, unknown>;\n setColumnSizing: Table<RowDataModel>['setColumnSizing'];\n onResizeEnd?: () => void;\n}) {\n const minSize = header.column.columnDef.minSize ?? 0;\n const maxSize = header.column.columnDef.maxSize ?? 0;\n const handleKeyDown = (e: KeyboardEvent) => {\n if (e.key === 'ArrowLeft' || e.key === 'ArrowRight') {\n e.preventDefault();\n const currentSize = header.getSize();\n const increment = e.shiftKey ? 20 : 5; // Larger increment with Shift key\n const newSize =\n e.key === 'ArrowLeft'\n ? Math.max(minSize, currentSize - increment)\n : Math.min(maxSize, currentSize + increment);\n\n setColumnSizing((prevSizing) => ({\n ...prevSizing,\n [header.column.id]: newSize,\n }));\n } else if (e.key === 'Home') {\n e.preventDefault();\n header.column.resetSize();\n }\n };\n\n const columnName =\n typeof header.column.columnDef.header === 'string'\n ? header.column.columnDef.header\n : header.column.id;\n\n return (\n <div class=\"py-1 h-100\" style={{ position: 'absolute', right: 0, top: 0, width: '4px' }}>\n {/* separator role is focusable, so these jsx-a11y-x rules are false positives.\n https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Reference/Roles/separator_role#focusable_separator\n */}\n {/* eslint-disable-next-line jsx-a11y-x/no-noninteractive-element-interactions */}\n <div\n role=\"separator\"\n aria-label={`Resize '${columnName}' column`}\n aria-valuetext={`${header.getSize()}px`}\n aria-orientation=\"vertical\"\n aria-valuemin={minSize}\n aria-valuemax={maxSize}\n aria-valuenow={header.getSize()}\n // eslint-disable-next-line jsx-a11y-x/no-noninteractive-tabindex\n tabIndex={0}\n class=\"h-100\"\n style={{\n background: header.column.getIsResizing() ? 'var(--bs-primary)' : 'var(--bs-gray-400)',\n cursor: 'col-resize',\n transition: 'background-color 0.2s',\n }}\n onMouseDown={header.getResizeHandler()}\n onMouseUp={onResizeEnd}\n onTouchStart={header.getResizeHandler()}\n onTouchEnd={onResizeEnd}\n onKeyDown={handleKeyDown}\n />\n </div>\n );\n}\n\n/**\n * Helper function to get aria-sort value\n */\nfunction getAriaSort(sortDirection: false | SortDirection) {\n switch (sortDirection) {\n case 'asc':\n return 'ascending';\n case 'desc':\n return 'descending';\n default:\n return 'none';\n }\n}\n\nexport function TanstackTableHeaderCell<RowDataModel>({\n header,\n filters,\n table,\n handleResizeEnd,\n isPinned,\n measurementMode = false,\n}: {\n header: Header<RowDataModel, unknown>;\n filters: Record<string, (props: { header: Header<RowDataModel, unknown> }) => JSX.Element>;\n table: Table<RowDataModel>;\n handleResizeEnd?: () => void;\n isPinned: 'left' | false;\n measurementMode?: boolean;\n}) {\n const sortDirection = header.column.getIsSorted();\n const canSort = header.column.getCanSort();\n const canFilter = header.column.getCanFilter();\n const columnName =\n header.column.columnDef.meta?.label ??\n (typeof header.column.columnDef.header === 'string'\n ? header.column.columnDef.header\n : header.column.id);\n\n // In measurement mode, we don't want to set the size of the header from tanstack.\n const headerSize = measurementMode ? undefined : header.getSize();\n const style: JSX.CSSProperties = {\n display: 'flex',\n width: headerSize,\n minWidth: 0,\n maxWidth: headerSize,\n flexShrink: 0,\n position: isPinned === 'left' ? 'sticky' : 'relative',\n top: 0,\n zIndex: isPinned === 'left' ? 2 : 1,\n left: isPinned === 'left' ? header.getStart() : undefined,\n boxShadow:\n 'inset 0 calc(-1 * var(--bs-border-width)) 0 0 rgba(0, 0, 0, 1), inset 0 var(--bs-border-width) 0 0 var(--bs-border-color)',\n };\n\n const isNormalColumn = canSort || canFilter;\n\n return (\n <th\n key={header.id}\n data-column-id={header.column.id}\n class={clsx(isPinned === 'left' && 'bg-light')}\n style={style}\n aria-sort={canSort ? getAriaSort(sortDirection) : undefined}\n role=\"columnheader\"\n >\n <div\n class={clsx(\n 'd-flex align-items-center flex-grow-1',\n isNormalColumn ? 'justify-content-between' : 'justify-content-center',\n )}\n style={{\n minWidth: 0,\n }}\n >\n <div\n class={clsx(\n 'text-nowrap text-start',\n // e.g. checkboxes\n !isNormalColumn && 'd-flex align-items-center justify-content-center',\n )}\n style={{\n minWidth: 0,\n flex: '1 1 0%',\n overflow: 'hidden',\n textOverflow: 'ellipsis',\n background: 'transparent',\n border: 'none',\n }}\n >\n {header.isPlaceholder\n ? null\n : flexRender(header.column.columnDef.header, header.getContext())}\n {canSort && (\n <span class=\"visually-hidden\">, {getAriaSort(sortDirection)}, click to sort</span>\n )}\n </div>\n\n {(canSort || canFilter) && (\n <div class=\"d-flex align-items-center\" style={{ flexShrink: 0 }}>\n {canSort && (\n <button\n type=\"button\"\n class=\"btn btn-link text-muted p-0\"\n aria-label={`Sort ${columnName.toLowerCase()}, current sort is ${getAriaSort(sortDirection)}`}\n title={`Sort ${columnName.toLowerCase()}`}\n onClick={header.column.getToggleSortingHandler()}\n >\n <SortIcon sortMethod={sortDirection} />\n </button>\n )}\n {canFilter && filters[header.column.id]?.({ header })}\n </div>\n )}\n </div>\n {header.column.getCanResize() && (\n <ResizeHandle\n header={header}\n setColumnSizing={table.setColumnSizing}\n onResizeEnd={handleResizeEnd}\n />\n )}\n </th>\n );\n}\n"]}
|
|
1
|
+
{"version":3,"file":"TanstackTableHeaderCell.js","sourceRoot":"","sources":["../../src/components/TanstackTableHeaderCell.tsx"],"names":[],"mappings":"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","sourcesContent":["import { flexRender } from '@tanstack/react-table';\nimport type { Header, SortDirection, Table } from '@tanstack/table-core';\nimport clsx from 'clsx';\nimport type { JSX } from 'preact/jsx-runtime';\n\nfunction SortIcon({ sortMethod }: { sortMethod: false | SortDirection }) {\n if (sortMethod === 'asc') {\n return <i class=\"bi bi-sort-up-alt\" aria-hidden=\"true\" />;\n } else if (sortMethod === 'desc') {\n return <i class=\"bi bi-sort-down\" aria-hidden=\"true\" />;\n } else {\n return <i class=\"bi bi-arrow-down-up opacity-75 text-muted\" aria-hidden=\"true\" />;\n }\n}\n\nfunction ResizeHandle<RowDataModel>({\n header,\n setColumnSizing,\n onResizeEnd,\n}: {\n header: Header<RowDataModel, unknown>;\n setColumnSizing: Table<RowDataModel>['setColumnSizing'];\n onResizeEnd?: () => void;\n}) {\n const minSize = header.column.columnDef.minSize ?? 0;\n const maxSize = header.column.columnDef.maxSize ?? 0;\n const handleKeyDown = (e: KeyboardEvent) => {\n if (e.key === 'ArrowLeft' || e.key === 'ArrowRight') {\n e.preventDefault();\n const currentSize = header.getSize();\n const increment = e.shiftKey ? 20 : 5; // Larger increment with Shift key\n const newSize =\n e.key === 'ArrowLeft'\n ? Math.max(minSize, currentSize - increment)\n : Math.min(maxSize, currentSize + increment);\n\n setColumnSizing((prevSizing) => ({\n ...prevSizing,\n [header.column.id]: newSize,\n }));\n } else if (e.key === 'Home') {\n e.preventDefault();\n header.column.resetSize();\n }\n };\n\n const columnName =\n typeof header.column.columnDef.header === 'string'\n ? header.column.columnDef.header\n : header.column.id;\n\n return (\n <div class=\"py-1 h-100\" style={{ position: 'absolute', right: 0, top: 0, width: '4px' }}>\n {/* separator role is focusable, so these jsx-a11y-x rules are false positives.\n https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Reference/Roles/separator_role#focusable_separator\n */}\n {/* eslint-disable-next-line jsx-a11y-x/no-noninteractive-element-interactions */}\n <div\n role=\"separator\"\n aria-label={`Resize '${columnName}' column`}\n aria-valuetext={`${header.getSize()}px`}\n aria-orientation=\"vertical\"\n aria-valuemin={minSize}\n aria-valuemax={maxSize}\n aria-valuenow={header.getSize()}\n // eslint-disable-next-line jsx-a11y-x/no-noninteractive-tabindex\n tabIndex={0}\n class=\"h-100\"\n style={{\n background: header.column.getIsResizing() ? 'var(--bs-primary)' : 'var(--bs-gray-400)',\n cursor: 'col-resize',\n transition: 'background-color 0.2s',\n }}\n onMouseDown={header.getResizeHandler()}\n onMouseUp={onResizeEnd}\n onTouchStart={header.getResizeHandler()}\n onTouchEnd={onResizeEnd}\n onKeyDown={handleKeyDown}\n />\n </div>\n );\n}\n\n/**\n * Helper function to get aria-sort value\n */\nfunction getAriaSort(sortDirection: false | SortDirection) {\n switch (sortDirection) {\n case 'asc':\n return 'ascending';\n case 'desc':\n return 'descending';\n default:\n return 'none';\n }\n}\n\nexport function TanstackTableHeaderCell<RowDataModel>({\n header,\n filters,\n table,\n handleResizeEnd,\n isPinned,\n measurementMode = false,\n}: {\n header: Header<RowDataModel, unknown>;\n filters: Record<string, (props: { header: Header<RowDataModel, unknown> }) => JSX.Element>;\n table: Table<RowDataModel>;\n handleResizeEnd?: () => void;\n isPinned: 'left' | false;\n measurementMode?: boolean;\n}) {\n const sortDirection = header.column.getIsSorted();\n const canSort = header.column.getCanSort();\n const canFilter = header.column.getCanFilter();\n const columnName =\n header.column.columnDef.meta?.label ??\n (typeof header.column.columnDef.header === 'string'\n ? header.column.columnDef.header\n : header.column.id);\n\n // In measurement mode, we don't want to set the size of the header from tanstack.\n const headerSize = measurementMode ? undefined : header.getSize();\n const style: JSX.CSSProperties = {\n display: 'flex',\n width: headerSize,\n minWidth: 0,\n maxWidth: headerSize,\n flexShrink: 0,\n position: isPinned === 'left' ? 'sticky' : 'relative',\n top: 0,\n zIndex: isPinned === 'left' ? 2 : 1,\n left: isPinned === 'left' ? header.getStart() : undefined,\n };\n\n const isNormalColumn = canSort || canFilter;\n\n return (\n <th\n key={header.id}\n data-column-id={header.column.id}\n class={clsx(isPinned === 'left' && 'bg-light')}\n style={style}\n aria-sort={canSort ? getAriaSort(sortDirection) : undefined}\n role=\"columnheader\"\n >\n <div\n class={clsx(\n 'd-flex align-items-center flex-grow-1',\n isNormalColumn ? 'justify-content-between' : 'justify-content-center',\n )}\n style={{\n minWidth: 0,\n }}\n >\n <div\n class={clsx(\n 'text-nowrap text-start',\n // e.g. checkboxes\n !isNormalColumn && 'd-flex align-items-center justify-content-center',\n )}\n style={{\n minWidth: 0,\n flex: '1 1 0%',\n overflow: 'hidden',\n textOverflow: 'ellipsis',\n background: 'transparent',\n border: 'none',\n }}\n >\n {header.isPlaceholder\n ? null\n : flexRender(header.column.columnDef.header, header.getContext())}\n {canSort && (\n <span class=\"visually-hidden\">, {getAriaSort(sortDirection)}, click to sort</span>\n )}\n </div>\n\n {(canSort || canFilter) && (\n <div class=\"d-flex align-items-center\" style={{ flexShrink: 0 }}>\n {canSort && (\n <button\n type=\"button\"\n class=\"btn btn-link text-muted p-0\"\n aria-label={`Sort ${columnName.toLowerCase()}, current sort is ${getAriaSort(sortDirection)}`}\n title={`Sort ${columnName.toLowerCase()}`}\n onClick={header.column.getToggleSortingHandler()}\n >\n <SortIcon sortMethod={sortDirection} />\n </button>\n )}\n {canFilter && filters[header.column.id]?.({ header })}\n </div>\n )}\n </div>\n {header.column.getCanResize() && (\n <ResizeHandle\n header={header}\n setColumnSizing={table.setColumnSizing}\n onResizeEnd={handleResizeEnd}\n />\n )}\n </th>\n );\n}\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@prairielearn/ui",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.7.1",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -24,7 +24,8 @@
|
|
|
24
24
|
"@tanstack/table-core": "^8.21.3",
|
|
25
25
|
"clsx": "^2.1.1",
|
|
26
26
|
"nuqs": "^2.8.2",
|
|
27
|
-
"react-bootstrap": "3.0.0-beta.5"
|
|
27
|
+
"react-bootstrap": "3.0.0-beta.5",
|
|
28
|
+
"use-debounce": "^10.0.6"
|
|
28
29
|
},
|
|
29
30
|
"devDependencies": {
|
|
30
31
|
"@prairielearn/tsconfig": "^0.0.0",
|
|
@@ -3,10 +3,11 @@ import { useVirtualizer } from '@tanstack/react-virtual';
|
|
|
3
3
|
import type { Cell, Header, Row, Table } from '@tanstack/table-core';
|
|
4
4
|
import clsx from 'clsx';
|
|
5
5
|
import type { ComponentChildren } from 'preact';
|
|
6
|
-
import { useEffect, useMemo, useRef } from 'preact/hooks';
|
|
6
|
+
import { useEffect, useMemo, useRef, useState } from 'preact/hooks';
|
|
7
7
|
import type { JSX } from 'preact/jsx-runtime';
|
|
8
8
|
import OverlayTrigger from 'react-bootstrap/OverlayTrigger';
|
|
9
9
|
import Tooltip from 'react-bootstrap/Tooltip';
|
|
10
|
+
import { useDebouncedCallback } from 'use-debounce';
|
|
10
11
|
|
|
11
12
|
import type { ComponentProps } from '@prairielearn/preact-cjs';
|
|
12
13
|
import { run } from '@prairielearn/run';
|
|
@@ -283,16 +284,17 @@ export function TanstackTable<RowDataModel>({
|
|
|
283
284
|
role="grid"
|
|
284
285
|
>
|
|
285
286
|
<thead
|
|
287
|
+
class="position-sticky top-0 w-100 border-top"
|
|
286
288
|
style={{
|
|
287
289
|
display: 'grid',
|
|
288
|
-
position: 'sticky',
|
|
289
|
-
top: 0,
|
|
290
290
|
zIndex: 1,
|
|
291
|
+
borderBottom: 'var(--bs-border-width) solid black',
|
|
291
292
|
}}
|
|
292
293
|
>
|
|
293
294
|
<tr
|
|
294
295
|
key={leafHeaderGroup.id}
|
|
295
|
-
|
|
296
|
+
class="d-flex w-100"
|
|
297
|
+
style={{ minWidth: `${table.getTotalSize()}px` }}
|
|
296
298
|
>
|
|
297
299
|
{/* Left pinned columns */}
|
|
298
300
|
{leftPinnedHeaders.map((header) => {
|
|
@@ -334,13 +336,21 @@ export function TanstackTable<RowDataModel>({
|
|
|
334
336
|
{virtualPaddingRight ? (
|
|
335
337
|
<th style={{ display: 'flex', width: virtualPaddingRight }} />
|
|
336
338
|
) : null}
|
|
339
|
+
|
|
340
|
+
{/* Filler to span remaining width */}
|
|
341
|
+
<th
|
|
342
|
+
tabIndex={-1}
|
|
343
|
+
class="d-flex flex-grow-1 p-0"
|
|
344
|
+
style={{ minWidth: 0 }}
|
|
345
|
+
aria-hidden="true"
|
|
346
|
+
/>
|
|
337
347
|
</tr>
|
|
338
348
|
</thead>
|
|
339
349
|
<tbody
|
|
350
|
+
class="position-relative w-100"
|
|
340
351
|
style={{
|
|
341
352
|
display: 'grid',
|
|
342
353
|
height: `${rowVirtualizer.getTotalSize()}px`,
|
|
343
|
-
position: 'relative',
|
|
344
354
|
}}
|
|
345
355
|
>
|
|
346
356
|
{virtualRows.map((virtualRow) => {
|
|
@@ -356,11 +366,10 @@ export function TanstackTable<RowDataModel>({
|
|
|
356
366
|
key={row.id}
|
|
357
367
|
ref={(node) => rowVirtualizer.measureElement(node)}
|
|
358
368
|
data-index={virtualRow.index}
|
|
369
|
+
class="d-flex position-absolute w-100"
|
|
359
370
|
style={{
|
|
360
|
-
display: 'flex',
|
|
361
|
-
position: 'absolute',
|
|
362
371
|
transform: `translateY(${virtualRow.start}px)`,
|
|
363
|
-
|
|
372
|
+
minWidth: `${table.getTotalSize()}px`,
|
|
364
373
|
}}
|
|
365
374
|
>
|
|
366
375
|
{leftPinnedCells.map((cell) => {
|
|
@@ -413,6 +422,14 @@ export function TanstackTable<RowDataModel>({
|
|
|
413
422
|
{virtualPaddingRight ? (
|
|
414
423
|
<td style={{ display: 'flex', width: virtualPaddingRight }} />
|
|
415
424
|
) : null}
|
|
425
|
+
|
|
426
|
+
{/* Filler to span remaining width */}
|
|
427
|
+
<td
|
|
428
|
+
tabIndex={-1}
|
|
429
|
+
class="d-flex flex-grow-1 p-0"
|
|
430
|
+
style={{ minWidth: 0 }}
|
|
431
|
+
aria-hidden="true"
|
|
432
|
+
/>
|
|
416
433
|
</tr>
|
|
417
434
|
);
|
|
418
435
|
})}
|
|
@@ -472,12 +489,11 @@ export function TanstackTable<RowDataModel>({
|
|
|
472
489
|
* @param params.singularLabel - The singular label for a single row in the table, e.g. "student"
|
|
473
490
|
* @param params.pluralLabel - The plural label for multiple rows in the table, e.g. "students"
|
|
474
491
|
* @param params.headerButtons - The buttons to display in the header
|
|
475
|
-
* @param params.
|
|
476
|
-
* @param params.
|
|
477
|
-
* @param params.
|
|
478
|
-
* @param params.globalFilter
|
|
479
|
-
* @param params.globalFilter.
|
|
480
|
-
* @param params.globalFilter.placeholder
|
|
492
|
+
* @param params.columnManager - Optional configuration for the column manager. See {@link ColumnManager} for more details.
|
|
493
|
+
* @param params.columnManager.buttons - The buttons to display next to the column manager (View button)
|
|
494
|
+
* @param params.columnManager.topContent - Optional content to display at the top of the column manager (View) dropdown menu
|
|
495
|
+
* @param params.globalFilter - Configuration for the global filter
|
|
496
|
+
* @param params.globalFilter.placeholder - Placeholder text for the search input
|
|
481
497
|
* @param params.tableOptions - Specific options for the table. See {@link TanstackTableProps} for more details.
|
|
482
498
|
* @param params.downloadButtonOptions - Specific options for the download button. See {@link TanstackTableDownloadButtonProps} for more details.
|
|
483
499
|
*/
|
|
@@ -487,8 +503,7 @@ export function TanstackTableCard<RowDataModel>({
|
|
|
487
503
|
singularLabel,
|
|
488
504
|
pluralLabel,
|
|
489
505
|
headerButtons,
|
|
490
|
-
|
|
491
|
-
columnManagerTopContent,
|
|
506
|
+
columnManager,
|
|
492
507
|
globalFilter,
|
|
493
508
|
tableOptions,
|
|
494
509
|
downloadButtonOptions,
|
|
@@ -499,22 +514,31 @@ export function TanstackTableCard<RowDataModel>({
|
|
|
499
514
|
title: string;
|
|
500
515
|
singularLabel: string;
|
|
501
516
|
pluralLabel: string;
|
|
502
|
-
headerButtons
|
|
503
|
-
|
|
504
|
-
|
|
517
|
+
headerButtons?: JSX.Element;
|
|
518
|
+
columnManager?: {
|
|
519
|
+
buttons?: JSX.Element;
|
|
520
|
+
topContent?: JSX.Element;
|
|
521
|
+
};
|
|
505
522
|
globalFilter: {
|
|
506
|
-
value: string;
|
|
507
|
-
setValue: (value: string) => void;
|
|
508
523
|
placeholder: string;
|
|
509
524
|
};
|
|
510
525
|
tableOptions: Partial<Omit<TanstackTableProps<RowDataModel>, 'table'>>;
|
|
511
526
|
downloadButtonOptions?: Omit<
|
|
512
527
|
TanstackTableDownloadButtonProps<RowDataModel>,
|
|
513
528
|
'table' | 'singularLabel' | 'pluralLabel'
|
|
514
|
-
|
|
529
|
+
> & { pluralLabel?: string; singularLabel?: string };
|
|
515
530
|
} & Omit<ComponentProps<'div'>, 'class'>) {
|
|
516
531
|
const searchInputRef = useRef<HTMLInputElement>(null);
|
|
517
532
|
|
|
533
|
+
const [inputValue, setInputValue] = useState(
|
|
534
|
+
() => (table.getState().globalFilter as string) ?? '',
|
|
535
|
+
);
|
|
536
|
+
|
|
537
|
+
// Debounce the filter update
|
|
538
|
+
const debouncedSetFilter = useDebouncedCallback((value: string) => {
|
|
539
|
+
table.setGlobalFilter(value);
|
|
540
|
+
}, 150);
|
|
541
|
+
|
|
518
542
|
// Focus the search input when Ctrl+F is pressed
|
|
519
543
|
useEffect(() => {
|
|
520
544
|
function onKeyDown(event: KeyboardEvent) {
|
|
@@ -559,20 +583,26 @@ export function TanstackTableCard<RowDataModel>({
|
|
|
559
583
|
class="form-control pl-ui-tanstack-table-search-input pl-ui-tanstack-table-focusable-shadow"
|
|
560
584
|
aria-label={globalFilter.placeholder}
|
|
561
585
|
placeholder={globalFilter.placeholder}
|
|
562
|
-
value={
|
|
586
|
+
value={inputValue}
|
|
563
587
|
autoComplete="off"
|
|
564
588
|
onInput={(e) => {
|
|
565
589
|
if (!(e.target instanceof HTMLInputElement)) return;
|
|
566
|
-
|
|
590
|
+
const value = e.target.value;
|
|
591
|
+
setInputValue(value);
|
|
592
|
+
debouncedSetFilter(value);
|
|
567
593
|
}}
|
|
568
594
|
/>
|
|
569
|
-
{
|
|
595
|
+
{inputValue && (
|
|
570
596
|
<OverlayTrigger overlay={<Tooltip>Clear search</Tooltip>}>
|
|
571
597
|
<button
|
|
572
598
|
type="button"
|
|
573
599
|
class="btn btn-floating-icon"
|
|
574
600
|
aria-label="Clear search"
|
|
575
|
-
onClick={() =>
|
|
601
|
+
onClick={() => {
|
|
602
|
+
setInputValue('');
|
|
603
|
+
debouncedSetFilter.cancel();
|
|
604
|
+
table.setGlobalFilter('');
|
|
605
|
+
}}
|
|
576
606
|
>
|
|
577
607
|
<i class="bi bi-x-circle-fill" aria-hidden="true" />
|
|
578
608
|
</button>
|
|
@@ -580,8 +610,8 @@ export function TanstackTableCard<RowDataModel>({
|
|
|
580
610
|
)}
|
|
581
611
|
</div>
|
|
582
612
|
<div class="d-flex flex-wrap flex-row align-items-center gap-2">
|
|
583
|
-
<ColumnManager table={table} topContent={
|
|
584
|
-
{
|
|
613
|
+
<ColumnManager table={table} topContent={columnManager?.topContent} />
|
|
614
|
+
{columnManager?.buttons}
|
|
585
615
|
</div>
|
|
586
616
|
<div class="ms-auto text-muted text-nowrap">
|
|
587
617
|
Showing {displayedCount} of {totalCount} {totalCount === 1 ? singularLabel : pluralLabel}
|
|
@@ -8,17 +8,19 @@ export interface TanstackTableDownloadButtonProps<RowDataModel> {
|
|
|
8
8
|
mapRowToData: (row: RowDataModel) => Record<string, string | number | null> | null;
|
|
9
9
|
singularLabel: string;
|
|
10
10
|
pluralLabel: string;
|
|
11
|
+
hasSelection: boolean;
|
|
11
12
|
}
|
|
12
13
|
/**
|
|
13
14
|
* @param params
|
|
14
|
-
* @param params.singularLabel - The singular label for a single row in the table, e.g. "student"
|
|
15
|
-
* @param params.pluralLabel - The plural label for multiple rows in the table, e.g. "students"
|
|
16
15
|
* @param params.table - The table model
|
|
17
16
|
* @param params.filenameBase - The base filename for the downloads
|
|
18
17
|
* @param params.mapRowToData - A function that maps a row to a record where the
|
|
19
18
|
* keys are the column names, and the values are the cell values. The key order is important,
|
|
20
19
|
* and should match the expected order of the columns in the CSV file. If the function returns null,
|
|
21
20
|
* the row will be skipped.
|
|
21
|
+
* @param params.singularLabel - The singular label for a single row in the table, e.g. "student"
|
|
22
|
+
* @param params.pluralLabel - The plural label for multiple rows in the table, e.g. "students"
|
|
23
|
+
* @param params.hasSelection - Whether the table has selection enabled
|
|
22
24
|
*/
|
|
23
25
|
export function TanstackTableDownloadButton<RowDataModel>({
|
|
24
26
|
table,
|
|
@@ -26,6 +28,7 @@ export function TanstackTableDownloadButton<RowDataModel>({
|
|
|
26
28
|
mapRowToData,
|
|
27
29
|
singularLabel,
|
|
28
30
|
pluralLabel,
|
|
31
|
+
hasSelection,
|
|
29
32
|
}: TanstackTableDownloadButtonProps<RowDataModel>) {
|
|
30
33
|
const allRows = table.getCoreRowModel().rows.map((row) => row.original);
|
|
31
34
|
const allRowsJSON = allRows.map(mapRowToData).filter((row) => row !== null);
|
|
@@ -70,7 +73,7 @@ export function TanstackTableDownloadButton<RowDataModel>({
|
|
|
70
73
|
disabled={allRowsJSON.length === 0}
|
|
71
74
|
onClick={() => downloadJSONAsCSV(allRowsJSON, `${filenameBase}.csv`)}
|
|
72
75
|
>
|
|
73
|
-
All {pluralLabel} as CSV
|
|
76
|
+
All {pluralLabel} ({allRowsJSON.length}) as CSV
|
|
74
77
|
</button>
|
|
75
78
|
</li>
|
|
76
79
|
<li role="presentation">
|
|
@@ -82,33 +85,39 @@ export function TanstackTableDownloadButton<RowDataModel>({
|
|
|
82
85
|
disabled={allRowsJSON.length === 0}
|
|
83
86
|
onClick={() => downloadAsJSON(allRowsJSON, `${filenameBase}.json`)}
|
|
84
87
|
>
|
|
85
|
-
All {pluralLabel} as JSON
|
|
86
|
-
</button>
|
|
87
|
-
</li>
|
|
88
|
-
<li role="presentation">
|
|
89
|
-
<button
|
|
90
|
-
class="dropdown-item"
|
|
91
|
-
type="button"
|
|
92
|
-
role="menuitem"
|
|
93
|
-
aria-label={`Download selected ${pluralLabel} as CSV file`}
|
|
94
|
-
disabled={selectedRowsJSON.length === 0}
|
|
95
|
-
onClick={() => downloadJSONAsCSV(selectedRowsJSON, `${filenameBase}_selected.csv`)}
|
|
96
|
-
>
|
|
97
|
-
Selected {selectedRowsJSON.length === 1 ? singularLabel : pluralLabel} as CSV
|
|
98
|
-
</button>
|
|
99
|
-
</li>
|
|
100
|
-
<li role="presentation">
|
|
101
|
-
<button
|
|
102
|
-
class="dropdown-item"
|
|
103
|
-
type="button"
|
|
104
|
-
role="menuitem"
|
|
105
|
-
aria-label={`Download selected ${pluralLabel} as JSON file`}
|
|
106
|
-
disabled={selectedRowsJSON.length === 0}
|
|
107
|
-
onClick={() => downloadAsJSON(selectedRowsJSON, `${filenameBase}_selected.json`)}
|
|
108
|
-
>
|
|
109
|
-
Selected {selectedRowsJSON.length === 1 ? singularLabel : pluralLabel} as JSON
|
|
88
|
+
All {pluralLabel} ({allRowsJSON.length}) as JSON
|
|
110
89
|
</button>
|
|
111
90
|
</li>
|
|
91
|
+
{hasSelection && (
|
|
92
|
+
<>
|
|
93
|
+
<li role="presentation">
|
|
94
|
+
<button
|
|
95
|
+
class="dropdown-item"
|
|
96
|
+
type="button"
|
|
97
|
+
role="menuitem"
|
|
98
|
+
aria-label={`Download selected ${pluralLabel} as CSV file`}
|
|
99
|
+
disabled={selectedRowsJSON.length === 0}
|
|
100
|
+
onClick={() => downloadJSONAsCSV(selectedRowsJSON, `${filenameBase}_selected.csv`)}
|
|
101
|
+
>
|
|
102
|
+
Selected {selectedRowsJSON.length === 1 ? singularLabel : pluralLabel} (
|
|
103
|
+
{selectedRowsJSON.length}) as CSV
|
|
104
|
+
</button>
|
|
105
|
+
</li>
|
|
106
|
+
<li role="presentation">
|
|
107
|
+
<button
|
|
108
|
+
class="dropdown-item"
|
|
109
|
+
type="button"
|
|
110
|
+
role="menuitem"
|
|
111
|
+
aria-label={`Download selected ${pluralLabel} as JSON file`}
|
|
112
|
+
disabled={selectedRowsJSON.length === 0}
|
|
113
|
+
onClick={() => downloadAsJSON(selectedRowsJSON, `${filenameBase}_selected.json`)}
|
|
114
|
+
>
|
|
115
|
+
Selected {selectedRowsJSON.length === 1 ? singularLabel : pluralLabel} (
|
|
116
|
+
{selectedRowsJSON.length}) as JSON
|
|
117
|
+
</button>
|
|
118
|
+
</li>
|
|
119
|
+
</>
|
|
120
|
+
)}
|
|
112
121
|
<li role="presentation">
|
|
113
122
|
<button
|
|
114
123
|
class="dropdown-item"
|
|
@@ -118,7 +127,8 @@ export function TanstackTableDownloadButton<RowDataModel>({
|
|
|
118
127
|
disabled={filteredRowsJSON.length === 0}
|
|
119
128
|
onClick={() => downloadJSONAsCSV(filteredRowsJSON, `${filenameBase}_filtered.csv`)}
|
|
120
129
|
>
|
|
121
|
-
Filtered {filteredRowsJSON.length === 1 ? singularLabel : pluralLabel}
|
|
130
|
+
Filtered {filteredRowsJSON.length === 1 ? singularLabel : pluralLabel} (
|
|
131
|
+
{filteredRowsJSON.length}) as CSV
|
|
122
132
|
</button>
|
|
123
133
|
</li>
|
|
124
134
|
<li role="presentation">
|
|
@@ -130,7 +140,8 @@ export function TanstackTableDownloadButton<RowDataModel>({
|
|
|
130
140
|
disabled={filteredRowsJSON.length === 0}
|
|
131
141
|
onClick={() => downloadAsJSON(filteredRowsJSON, `${filenameBase}_filtered.json`)}
|
|
132
142
|
>
|
|
133
|
-
Filtered {filteredRowsJSON.length === 1 ? singularLabel : pluralLabel}
|
|
143
|
+
Filtered {filteredRowsJSON.length === 1 ? singularLabel : pluralLabel} (
|
|
144
|
+
{filteredRowsJSON.length}) as JSON
|
|
134
145
|
</button>
|
|
135
146
|
</li>
|
|
136
147
|
</ul>
|
|
@@ -131,8 +131,6 @@ export function TanstackTableHeaderCell<RowDataModel>({
|
|
|
131
131
|
top: 0,
|
|
132
132
|
zIndex: isPinned === 'left' ? 2 : 1,
|
|
133
133
|
left: isPinned === 'left' ? header.getStart() : undefined,
|
|
134
|
-
boxShadow:
|
|
135
|
-
'inset 0 calc(-1 * var(--bs-border-width)) 0 0 rgba(0, 0, 0, 1), inset 0 var(--bs-border-width) 0 0 var(--bs-border-color)',
|
|
136
134
|
};
|
|
137
135
|
|
|
138
136
|
const isNormalColumn = canSort || canFilter;
|