@prairielearn/ui 1.5.0 → 1.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,21 @@
1
1
  # @prairielearn/ui
2
2
 
3
+ ## 1.7.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 8326968: Debounce global filter in TanstackTable, clean up column manager API
8
+
9
+ ### Patch Changes
10
+
11
+ - 037c174: Add back support for singularLabel and pluralLabel to TanstackTableCard, make `headerButtons` optional, add `hasSelection` to TanstackTableDownloadButton
12
+
13
+ ## 1.6.0
14
+
15
+ ### Minor Changes
16
+
17
+ - 20f25f7: Add nuqs utilities for URL query state management with server-side rendering support. Includes `NuqsAdapter` component and TanStack Table state parsers (`parseAsSortingState`, `parseAsColumnVisibilityStateWithColumns`, `parseAsColumnPinningState`, `parseAsNumericFilter`).
18
+
3
19
  ## 1.5.0
4
20
 
5
21
  ### Minor Changes
package/README.md CHANGED
@@ -1,8 +1,8 @@
1
1
  # `@prairielearn/ui`
2
2
 
3
- UI components and styles shared between PrairieLearn and PrairieTest.
3
+ UI components, utilities, and styles shared between PrairieLearn and PrairieTest.
4
4
 
5
- ## Examples
5
+ ## UI Component Examples
6
6
 
7
7
  ### TanstackTableCard
8
8
 
@@ -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}
@@ -106,3 +105,61 @@ const tableOptions = {
106
105
  },
107
106
  };
108
107
  ```
108
+
109
+ ## nuqs Utilities
110
+
111
+ This package provides utilities for integrating [nuqs](https://nuqs.47ng.com/) (type-safe URL query state management) with server-side rendering and TanStack Table.
112
+
113
+ ### NuqsAdapter
114
+
115
+ `nuqs` needs to be aware of the current state of the URL search parameters during both server-side and client-side rendering. The `NuqsAdapter` component handles this by using a custom adapter on the server that reads from a provided `search` prop, while on the client it uses nuqs's built-in React adapter that reads directly from `location.search`.
116
+
117
+ ```tsx
118
+ import { NuqsAdapter } from '@prairielearn/ui';
119
+
120
+ // Wrap your component that uses nuqs hooks
121
+ <NuqsAdapter search={new URL(req.url).search}>
122
+ <MyTableComponent />
123
+ </NuqsAdapter>;
124
+ ```
125
+
126
+ ### TanStack Table State Parsers
127
+
128
+ The package provides custom parsers for syncing TanStack Table state with URL query parameters:
129
+
130
+ - **`parseAsSortingState`**: Syncs table sorting state with the URL. Format: `col:asc` or `col1:asc,col2:desc` for multi-column sorting.
131
+ - **`parseAsColumnVisibilityStateWithColumns(allColumns, defaultValueRef?)`**: Syncs column visibility. Parses comma-separated visible column IDs.
132
+ - **`parseAsColumnPinningState`**: Syncs left-pinned columns. Format: `col1,col2,col3`.
133
+ - **`parseAsNumericFilter`**: Syncs numeric filter values. URL format: `gte_5`, `lte_10`, `gt_3`, `lt_7`, `eq_5`, `empty`.
134
+
135
+ ```tsx
136
+ import {
137
+ parseAsSortingState,
138
+ parseAsColumnVisibilityStateWithColumns,
139
+ parseAsColumnPinningState,
140
+ parseAsNumericFilter,
141
+ } from '@prairielearn/ui';
142
+ import { useQueryState } from 'nuqs';
143
+
144
+ // Sorting state synced to URL
145
+ const [sorting, setSorting] = useQueryState('sort', parseAsSortingState.withDefault([]));
146
+
147
+ // Column visibility synced to URL
148
+ const allColumns = ['name', 'email', 'status'];
149
+ const [columnVisibility, setColumnVisibility] = useQueryState(
150
+ 'cols',
151
+ parseAsColumnVisibilityStateWithColumns(allColumns).withDefault({}),
152
+ );
153
+
154
+ // Column pinning synced to URL
155
+ const [columnPinning, setColumnPinning] = useQueryState(
156
+ 'pin',
157
+ parseAsColumnPinningState.withDefault({ left: [], right: [] }),
158
+ );
159
+
160
+ // Numeric filter synced to URL
161
+ const [scoreFilter, setScoreFilter] = useQueryState(
162
+ 'score',
163
+ parseAsNumericFilter.withDefault({ filterValue: '', emptyOnly: false }),
164
+ );
165
+ ```
@@ -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.columnManagerButtons - The buttons to display next to the column manager (View button)
40
- * @param params.columnManagerTopContent - Optional content to display at the top of the column manager (View) dropdown menu
41
- * @param params.globalFilter - State management for the global filter
42
- * @param params.globalFilter.value
43
- * @param params.globalFilter.setValue
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, columnManagerButtons, columnManagerTopContent, globalFilter, tableOptions, downloadButtonOptions, className, ...divProps }: {
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: JSX.Element;
54
- columnManagerButtons?: JSX.Element;
55
- columnManagerTopContent?: JSX.Element;
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;AAI9C,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,eA0VlC;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,iBAAiB,CAAC,YAAY,EAAE,EAC9C,KAAK,EACL,KAAK,EACL,aAAa,EACb,WAAW,EACX,aAAa,EACb,oBAAoB,EACpB,uBAAuB,EACvB,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,EAAE,GAAG,CAAC,OAAO,CAAC;IAC3B,oBAAoB,CAAC,EAAE,GAAG,CAAC,OAAO,CAAC;IACnC,uBAAuB,CAAC,EAAE,GAAG,CAAC,OAAO,CAAC;IACtC,YAAY,EAAE;QACZ,KAAK,EAAE,MAAM,CAAC;QACd,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;QAClC,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,CAAC;CACH,GAAG,IAAI,CAAC,cAAc,CAAC,KAAK,CAAC,EAAE,OAAO,CAAC,eAgFvC;AAED,wBAAgB,uBAAuB,CAAC,EACtC,QAAQ,EACR,QAAQ,GACT,EAAE;IACD,QAAQ,EAAE,MAAM,MAAM,EAAE,CAAC;IACzB,QAAQ,EAAE,iBAAiB,CAAC;CAC7B,eAOA"}
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,eA0VlC;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';
@@ -234,17 +235,21 @@ export function TanstackTable({ table, title, filters = DEFAULT_FILTER_MAP, rowH
234
235
  * @param params.singularLabel - The singular label for a single row in the table, e.g. "student"
235
236
  * @param params.pluralLabel - The plural label for multiple rows in the table, e.g. "students"
236
237
  * @param params.headerButtons - The buttons to display in the header
237
- * @param params.columnManagerButtons - The buttons to display next to the column manager (View button)
238
- * @param params.columnManagerTopContent - Optional content to display at the top of the column manager (View) dropdown menu
239
- * @param params.globalFilter - State management for the global filter
240
- * @param params.globalFilter.value
241
- * @param params.globalFilter.setValue
242
- * @param params.globalFilter.placeholder
238
+ * @param params.columnManager - Optional configuration for the column manager. See {@link ColumnManager} for more details.
239
+ * @param params.columnManager.buttons - The buttons to display next to the column manager (View button)
240
+ * @param params.columnManager.topContent - Optional content to display at the top of the column manager (View) dropdown menu
241
+ * @param params.globalFilter - Configuration for the global filter
242
+ * @param params.globalFilter.placeholder - Placeholder text for the search input
243
243
  * @param params.tableOptions - Specific options for the table. See {@link TanstackTableProps} for more details.
244
244
  * @param params.downloadButtonOptions - Specific options for the download button. See {@link TanstackTableDownloadButtonProps} for more details.
245
245
  */
246
- export function TanstackTableCard({ table, title, singularLabel, pluralLabel, headerButtons, columnManagerButtons, columnManagerTopContent, globalFilter, tableOptions, downloadButtonOptions, className, ...divProps }) {
246
+ export function TanstackTableCard({ table, title, singularLabel, pluralLabel, headerButtons, columnManager, globalFilter, tableOptions, downloadButtonOptions, className, ...divProps }) {
247
247
  const searchInputRef = useRef(null);
248
+ const [inputValue, setInputValue] = useState(() => table.getState().globalFilter ?? '');
249
+ // Debounce the filter update
250
+ const debouncedSetFilter = useDebouncedCallback((value) => {
251
+ table.setGlobalFilter(value);
252
+ }, 150);
248
253
  // Focus the search input when Ctrl+F is pressed
249
254
  useEffect(() => {
250
255
  function onKeyDown(event) {
@@ -260,11 +265,17 @@ export function TanstackTableCard({ table, title, singularLabel, pluralLabel, he
260
265
  }, []);
261
266
  const displayedCount = table.getRowModel().rows.length;
262
267
  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: globalFilter.value, autoComplete: "off", onInput: (e) => {
268
+ 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
269
  if (!(e.target instanceof HTMLInputElement))
265
270
  return;
266
- globalFilter.setValue(e.target.value);
267
- } }), globalFilter.value && (_jsx(OverlayTrigger, { overlay: _jsx(Tooltip, { children: "Clear search" }), children: _jsx("button", { type: "button", class: "btn btn-floating-icon", "aria-label": "Clear search", onClick: () => globalFilter.setValue(''), 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: columnManagerTopContent }), columnManagerButtons] }), _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 }) })] }));
271
+ const value = e.target.value;
272
+ setInputValue(value);
273
+ debouncedSetFilter(value);
274
+ } }), inputValue && (_jsx(OverlayTrigger, { overlay: _jsx(Tooltip, { children: "Clear search" }), children: _jsx("button", { type: "button", class: "btn btn-floating-icon", "aria-label": "Clear search", onClick: () => {
275
+ setInputValue('');
276
+ debouncedSetFilter.cancel();
277
+ table.setGlobalFilter('');
278
+ }, 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
279
  }
269
280
  export function TanstackTableEmptyState({ iconName, children, }) {
270
281
  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 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.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;CACrB;AACD;;;;;;;;;;GAUG;AACH,wBAAgB,2BAA2B,CAAC,YAAY,EAAE,EACxD,KAAK,EACL,YAAY,EACZ,YAAY,EACZ,aAAa,EACb,WAAW,GACZ,EAAE,gCAAgC,CAAC,YAAY,CAAC,yCA8GhD"}
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;AAS5E;;;;;;;;;;GAUG;AACH,MAAM,UAAU,2BAA2B,CAAe,EACxD,KAAK,EACL,YAAY,EACZ,YAAY,EACZ,aAAa,EACb,WAAW,GACoC;IAC/C,MAAM,OAAO,GAAG,KAAK,CAAC,eAAe,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;IACxE,MAAM,WAAW,GAAG,OAAO,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,KAAK,IAAI,CAAC,CAAC;IAC5E,MAAM,YAAY,GAAG,KAAK,CAAC,WAAW,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;IACzE,MAAM,gBAAgB,GAAG,YAAY,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,KAAK,IAAI,CAAC,CAAC;IACtF,MAAM,YAAY,GAAG,KAAK,CAAC,mBAAmB,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;IACjF,MAAM,gBAAgB,GAAG,YAAY,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,KAAK,IAAI,CAAC,CAAC;IAEtF,SAAS,iBAAiB,CACxB,QAAkD,EAClD,QAAgB;QAEhB,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAC1B,MAAM,IAAI,KAAK,CAAC,qBAAqB,CAAC,CAAC;QACzC,CAAC;QAED,MAAM,MAAM,GAAG,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC;QACxC,MAAM,OAAO,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC;QAC1D,aAAa,CAAC,MAAM,EAAE,OAAO,EAAE,QAAQ,CAAC,CAAC;IAC3C,CAAC;IAED,OAAO,CACL,eAAK,KAAK,EAAC,WAAW,aACpB,kBACE,IAAI,EAAC,QAAQ,oBACE,UAAU,mBACX,OAAO,mBACP,MAAM,gBACR,YAAY,WAAW,0BAA0B,EAC7D,KAAK,EAAC,sCAAsC,aAE5C,2BAAe,MAAM,EAAC,KAAK,EAAC,qBAAqB,GAAG,EACpD,eAAM,KAAK,EAAC,oBAAoB,yBAAgB,IACzC,EACT,cAAI,KAAK,EAAC,eAAe,EAAC,IAAI,EAAC,MAAM,gBAAY,kBAAkB,aACjE,aAAI,IAAI,EAAC,cAAc,YACrB,kBACE,KAAK,EAAC,eAAe,EACrB,IAAI,EAAC,QAAQ,EACb,IAAI,EAAC,UAAU,gBACH,gBAAgB,WAAW,cAAc,EACrD,QAAQ,EAAE,WAAW,CAAC,MAAM,KAAK,CAAC,EAClC,OAAO,EAAE,GAAG,EAAE,CAAC,iBAAiB,CAAC,WAAW,EAAE,GAAG,YAAY,MAAM,CAAC,qBAE/D,WAAW,eACT,GACN,EACL,aAAI,IAAI,EAAC,cAAc,YACrB,kBACE,KAAK,EAAC,eAAe,EACrB,IAAI,EAAC,QAAQ,EACb,IAAI,EAAC,UAAU,gBACH,gBAAgB,WAAW,eAAe,EACtD,QAAQ,EAAE,WAAW,CAAC,MAAM,KAAK,CAAC,EAClC,OAAO,EAAE,GAAG,EAAE,CAAC,cAAc,CAAC,WAAW,EAAE,GAAG,YAAY,OAAO,CAAC,qBAE7D,WAAW,gBACT,GACN,EACL,aAAI,IAAI,EAAC,cAAc,YACrB,kBACE,KAAK,EAAC,eAAe,EACrB,IAAI,EAAC,QAAQ,EACb,IAAI,EAAC,UAAU,gBACH,qBAAqB,WAAW,cAAc,EAC1D,QAAQ,EAAE,gBAAgB,CAAC,MAAM,KAAK,CAAC,EACvC,OAAO,EAAE,GAAG,EAAE,CAAC,iBAAiB,CAAC,gBAAgB,EAAE,GAAG,YAAY,eAAe,CAAC,0BAExE,gBAAgB,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,WAAW,eAC9D,GACN,EACL,aAAI,IAAI,EAAC,cAAc,YACrB,kBACE,KAAK,EAAC,eAAe,EACrB,IAAI,EAAC,QAAQ,EACb,IAAI,EAAC,UAAU,gBACH,qBAAqB,WAAW,eAAe,EAC3D,QAAQ,EAAE,gBAAgB,CAAC,MAAM,KAAK,CAAC,EACvC,OAAO,EAAE,GAAG,EAAE,CAAC,cAAc,CAAC,gBAAgB,EAAE,GAAG,YAAY,gBAAgB,CAAC,0BAEtE,gBAAgB,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,WAAW,gBAC9D,GACN,EACL,aAAI,IAAI,EAAC,cAAc,YACrB,kBACE,KAAK,EAAC,eAAe,EACrB,IAAI,EAAC,QAAQ,EACb,IAAI,EAAC,UAAU,gBACH,qBAAqB,WAAW,cAAc,EAC1D,QAAQ,EAAE,gBAAgB,CAAC,MAAM,KAAK,CAAC,EACvC,OAAO,EAAE,GAAG,EAAE,CAAC,iBAAiB,CAAC,gBAAgB,EAAE,GAAG,YAAY,eAAe,CAAC,0BAExE,gBAAgB,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,WAAW,eAC9D,GACN,EACL,aAAI,IAAI,EAAC,cAAc,YACrB,kBACE,KAAK,EAAC,eAAe,EACrB,IAAI,EAAC,QAAQ,EACb,IAAI,EAAC,UAAU,gBACH,qBAAqB,WAAW,eAAe,EAC3D,QAAQ,EAAE,gBAAgB,CAAC,MAAM,KAAK,CAAC,EACvC,OAAO,EAAE,GAAG,EAAE,CAAC,cAAc,CAAC,gBAAgB,EAAE,GAAG,YAAY,gBAAgB,CAAC,0BAEtE,gBAAgB,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,WAAW,gBAC9D,GACN,IACF,IACD,CACP,CAAC;AACJ,CAAC","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}\n/**\n * @param params\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.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 */\nexport function TanstackTableDownloadButton<RowDataModel>({\n table,\n filenameBase,\n mapRowToData,\n singularLabel,\n pluralLabel,\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} 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} as JSON\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 CSV file`}\n disabled={selectedRowsJSON.length === 0}\n onClick={() => downloadJSONAsCSV(selectedRowsJSON, `${filenameBase}_selected.csv`)}\n >\n Selected {selectedRowsJSON.length === 1 ? singularLabel : pluralLabel} 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} as JSON\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 CSV file`}\n disabled={filteredRowsJSON.length === 0}\n onClick={() => downloadJSONAsCSV(filteredRowsJSON, `${filenameBase}_filtered.csv`)}\n >\n Filtered {filteredRowsJSON.length === 1 ? singularLabel : pluralLabel} 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} as JSON\n </button>\n </li>\n </ul>\n </div>\n );\n}\n"]}
1
+ {"version":3,"file":"TanstackTableDownloadButton.js","sourceRoot":"","sources":["../../src/components/TanstackTableDownloadButton.tsx"],"names":[],"mappings":";AAEA,OAAO,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAC;AAU5E;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,2BAA2B,CAAe,EACxD,KAAK,EACL,YAAY,EACZ,YAAY,EACZ,aAAa,EACb,WAAW,EACX,YAAY,GACmC;IAC/C,MAAM,OAAO,GAAG,KAAK,CAAC,eAAe,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;IACxE,MAAM,WAAW,GAAG,OAAO,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,KAAK,IAAI,CAAC,CAAC;IAC5E,MAAM,YAAY,GAAG,KAAK,CAAC,WAAW,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;IACzE,MAAM,gBAAgB,GAAG,YAAY,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,KAAK,IAAI,CAAC,CAAC;IACtF,MAAM,YAAY,GAAG,KAAK,CAAC,mBAAmB,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;IACjF,MAAM,gBAAgB,GAAG,YAAY,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,KAAK,IAAI,CAAC,CAAC;IAEtF,SAAS,iBAAiB,CACxB,QAAkD,EAClD,QAAgB;QAEhB,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAC1B,MAAM,IAAI,KAAK,CAAC,qBAAqB,CAAC,CAAC;QACzC,CAAC;QAED,MAAM,MAAM,GAAG,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC;QACxC,MAAM,OAAO,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC;QAC1D,aAAa,CAAC,MAAM,EAAE,OAAO,EAAE,QAAQ,CAAC,CAAC;IAC3C,CAAC;IAED,OAAO,CACL,eAAK,KAAK,EAAC,WAAW,aACpB,kBACE,IAAI,EAAC,QAAQ,oBACE,UAAU,mBACX,OAAO,mBACP,MAAM,gBACR,YAAY,WAAW,0BAA0B,EAC7D,KAAK,EAAC,sCAAsC,aAE5C,2BAAe,MAAM,EAAC,KAAK,EAAC,qBAAqB,GAAG,EACpD,eAAM,KAAK,EAAC,oBAAoB,yBAAgB,IACzC,EACT,cAAI,KAAK,EAAC,eAAe,EAAC,IAAI,EAAC,MAAM,gBAAY,kBAAkB,aACjE,aAAI,IAAI,EAAC,cAAc,YACrB,kBACE,KAAK,EAAC,eAAe,EACrB,IAAI,EAAC,QAAQ,EACb,IAAI,EAAC,UAAU,gBACH,gBAAgB,WAAW,cAAc,EACrD,QAAQ,EAAE,WAAW,CAAC,MAAM,KAAK,CAAC,EAClC,OAAO,EAAE,GAAG,EAAE,CAAC,iBAAiB,CAAC,WAAW,EAAE,GAAG,YAAY,MAAM,CAAC,qBAE/D,WAAW,QAAI,WAAW,CAAC,MAAM,gBAC/B,GACN,EACL,aAAI,IAAI,EAAC,cAAc,YACrB,kBACE,KAAK,EAAC,eAAe,EACrB,IAAI,EAAC,QAAQ,EACb,IAAI,EAAC,UAAU,gBACH,gBAAgB,WAAW,eAAe,EACtD,QAAQ,EAAE,WAAW,CAAC,MAAM,KAAK,CAAC,EAClC,OAAO,EAAE,GAAG,EAAE,CAAC,cAAc,CAAC,WAAW,EAAE,GAAG,YAAY,OAAO,CAAC,qBAE7D,WAAW,QAAI,WAAW,CAAC,MAAM,iBAC/B,GACN,EACJ,YAAY,IAAI,CACf,8BACE,aAAI,IAAI,EAAC,cAAc,YACrB,kBACE,KAAK,EAAC,eAAe,EACrB,IAAI,EAAC,QAAQ,EACb,IAAI,EAAC,UAAU,gBACH,qBAAqB,WAAW,cAAc,EAC1D,QAAQ,EAAE,gBAAgB,CAAC,MAAM,KAAK,CAAC,EACvC,OAAO,EAAE,GAAG,EAAE,CAAC,iBAAiB,CAAC,gBAAgB,EAAE,GAAG,YAAY,eAAe,CAAC,0BAExE,gBAAgB,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,WAAW,QACpE,gBAAgB,CAAC,MAAM,gBACjB,GACN,EACL,aAAI,IAAI,EAAC,cAAc,YACrB,kBACE,KAAK,EAAC,eAAe,EACrB,IAAI,EAAC,QAAQ,EACb,IAAI,EAAC,UAAU,gBACH,qBAAqB,WAAW,eAAe,EAC3D,QAAQ,EAAE,gBAAgB,CAAC,MAAM,KAAK,CAAC,EACvC,OAAO,EAAE,GAAG,EAAE,CAAC,cAAc,CAAC,gBAAgB,EAAE,GAAG,YAAY,gBAAgB,CAAC,0BAEtE,gBAAgB,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,WAAW,QACpE,gBAAgB,CAAC,MAAM,iBACjB,GACN,IACJ,CACJ,EACD,aAAI,IAAI,EAAC,cAAc,YACrB,kBACE,KAAK,EAAC,eAAe,EACrB,IAAI,EAAC,QAAQ,EACb,IAAI,EAAC,UAAU,gBACH,qBAAqB,WAAW,cAAc,EAC1D,QAAQ,EAAE,gBAAgB,CAAC,MAAM,KAAK,CAAC,EACvC,OAAO,EAAE,GAAG,EAAE,CAAC,iBAAiB,CAAC,gBAAgB,EAAE,GAAG,YAAY,eAAe,CAAC,0BAExE,gBAAgB,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,WAAW,QACpE,gBAAgB,CAAC,MAAM,gBACjB,GACN,EACL,aAAI,IAAI,EAAC,cAAc,YACrB,kBACE,KAAK,EAAC,eAAe,EACrB,IAAI,EAAC,QAAQ,EACb,IAAI,EAAC,UAAU,gBACH,qBAAqB,WAAW,eAAe,EAC3D,QAAQ,EAAE,gBAAgB,CAAC,MAAM,KAAK,CAAC,EACvC,OAAO,EAAE,GAAG,EAAE,CAAC,cAAc,CAAC,gBAAgB,EAAE,GAAG,YAAY,gBAAgB,CAAC,0BAEtE,gBAAgB,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,WAAW,QACpE,gBAAgB,CAAC,MAAM,iBACjB,GACN,IACF,IACD,CACP,CAAC;AACJ,CAAC","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"]}
@@ -0,0 +1,52 @@
1
+ import type { ColumnPinningState, SortingState, VisibilityState } from '@tanstack/table-core';
2
+ import React from 'preact/compat';
3
+ import type { NumericColumnFilterValue } from './NumericInputColumnFilter.js';
4
+ /**
5
+ * `nuqs` needs to be aware of the current state of the URL search parameters
6
+ * during both server-side and client-side rendering. To make this work with
7
+ * our server-side rendering setup, we use a custom adapter that should be
8
+ * provided with the value of `new URL(...).search` on the server side. On the
9
+ * client, we use `NuqsReactAdapter`, which will read directly from `location.search`.
10
+ */
11
+ export declare function NuqsAdapter({ children, search }: {
12
+ children: React.ReactNode;
13
+ search: string;
14
+ }): React.JSX.Element;
15
+ /**
16
+ * Parses and serializes TanStack Table SortingState to/from a URL query string.
17
+ * Used for reflecting table sort order in the URL.
18
+ *
19
+ * Example: `sort=col:asc` <-> `[{ id: 'col', desc: false }]`
20
+ */
21
+ export declare const parseAsSortingState: import("nuqs").SingleParserBuilder<SortingState>;
22
+ /**
23
+ * Returns a parser for TanStack Table VisibilityState for a given set of columns.
24
+ * Parses a comma-separated list of visible columns from a query string, e.g. 'a,b'.
25
+ * Serializes to a comma-separated list of visible columns, omitting if all are visible.
26
+ * Used for reflecting column visibility in the URL.
27
+ *
28
+ * @param allColumns - Array of all column IDs
29
+ * @param defaultValueRef - A ref object with a `current` property that contains the default visibility state.
30
+ */
31
+ export declare function parseAsColumnVisibilityStateWithColumns(allColumns: string[], defaultValueRef?: React.RefObject<VisibilityState>): import("nuqs").SingleParserBuilder<VisibilityState>;
32
+ /**
33
+ * Parses and serializes TanStack Table ColumnPinningState to/from a URL query string.
34
+ * Used for reflecting pinned columns in the URL.
35
+ *
36
+ * Right pins aren't supported; an empty array is always returned to allow
37
+ * this hook's value to be used directly in `state.columnPinning` in `useReactTable`.
38
+ *
39
+ * Example: `a,b` <-> `{ left: ['a', 'b'], right: [] }`
40
+ */
41
+ export declare const parseAsColumnPinningState: import("nuqs").SingleParserBuilder<ColumnPinningState>;
42
+ /**
43
+ * Parses and serializes numeric filter strings to/from URL-friendly format.
44
+ * Used for numeric column filters with comparison operators.
45
+ *
46
+ * Internal format: `>=5`, `<=10`, `>3`, `<7`, `=5`
47
+ * URL format: `gte_5`, `lte_10`, `gt_3`, `lt_7`, `eq_5`, `empty`
48
+ *
49
+ * Example: `gte_5` <-> `>=5`
50
+ */
51
+ export declare const parseAsNumericFilter: import("nuqs").SingleParserBuilder<NumericColumnFilterValue>;
52
+ //# sourceMappingURL=nuqs.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"nuqs.d.ts","sourceRoot":"","sources":["../../src/components/nuqs.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,YAAY,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAO9F,OAAO,KAAK,MAAM,eAAe,CAAC;AAElC,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,+BAA+B,CAAC;AAgB9E;;;;;;GAMG;AACH,wBAAgB,WAAW,CAAC,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE;IAAE,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,qBAY9F;AAED;;;;;GAKG;AACH,eAAO,MAAM,mBAAmB,kDA8B9B,CAAC;AAEH;;;;;;;;GAQG;AACH,wBAAgB,uCAAuC,CACrD,UAAU,EAAE,MAAM,EAAE,EACpB,eAAe,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC,eAAe,CAAC,uDAyCnD;AAED;;;;;;;;GAQG;AACH,eAAO,MAAM,yBAAyB,wDAmBpC,CAAC;AAEH;;;;;;;;GAQG;AACH,eAAO,MAAM,oBAAoB,8DAoD/B,CAAC"}