@prairielearn/ui 1.9.1 → 1.10.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.
Files changed (31) hide show
  1. package/CHANGELOG.md +11 -0
  2. package/dist/components/CategoricalColumnFilter.d.ts.map +1 -1
  3. package/dist/components/CategoricalColumnFilter.js +4 -4
  4. package/dist/components/CategoricalColumnFilter.js.map +1 -1
  5. package/dist/components/ColumnManager.js +7 -7
  6. package/dist/components/ColumnManager.js.map +1 -1
  7. package/dist/components/MultiSelectColumnFilter.js +2 -2
  8. package/dist/components/MultiSelectColumnFilter.js.map +1 -1
  9. package/dist/components/NumericInputColumnFilter.d.ts.map +1 -1
  10. package/dist/components/NumericInputColumnFilter.js +5 -9
  11. package/dist/components/NumericInputColumnFilter.js.map +1 -1
  12. package/dist/components/PresetFilterDropdown.js +3 -3
  13. package/dist/components/PresetFilterDropdown.js.map +1 -1
  14. package/dist/components/TanstackTable.js +13 -13
  15. package/dist/components/TanstackTable.js.map +1 -1
  16. package/dist/components/TanstackTableDownloadButton.js +1 -1
  17. package/dist/components/TanstackTableDownloadButton.js.map +1 -1
  18. package/dist/components/TanstackTableHeaderCell.js +8 -8
  19. package/dist/components/TanstackTableHeaderCell.js.map +1 -1
  20. package/dist/components/useAutoSizeColumns.js +1 -1
  21. package/dist/components/useAutoSizeColumns.js.map +1 -1
  22. package/package.json +2 -2
  23. package/src/components/CategoricalColumnFilter.tsx +25 -22
  24. package/src/components/ColumnManager.tsx +19 -19
  25. package/src/components/MultiSelectColumnFilter.tsx +12 -12
  26. package/src/components/NumericInputColumnFilter.tsx +13 -16
  27. package/src/components/PresetFilterDropdown.tsx +3 -3
  28. package/src/components/TanstackTable.tsx +25 -25
  29. package/src/components/TanstackTableDownloadButton.tsx +11 -11
  30. package/src/components/TanstackTableHeaderCell.tsx +11 -11
  31. package/src/components/useAutoSizeColumns.tsx +1 -1
@@ -15,7 +15,7 @@ function computeSelected<T extends readonly any[]>(
15
15
  }
16
16
 
17
17
  function defaultRenderValueLabel<T>({ value }: { value: T }) {
18
- return <span class="text-nowrap">{String(value)}</span>;
18
+ return <span className="text-nowrap">{String(value)}</span>;
19
19
  }
20
20
  /**
21
21
  * A component that allows the user to filter a categorical column.
@@ -70,23 +70,26 @@ export function CategoricalColumnFilter<TData, TValue>({
70
70
  <Dropdown align="end">
71
71
  <Dropdown.Toggle
72
72
  variant="link"
73
- class="text-muted p-0"
73
+ className="text-muted p-0"
74
74
  id={`filter-${columnId}`}
75
75
  aria-label={`Filter ${label.toLowerCase()}`}
76
76
  title={`Filter ${label.toLowerCase()}`}
77
77
  >
78
78
  <i
79
- class={clsx('bi', selected.size > 0 ? ['bi-funnel-fill', 'text-primary'] : 'bi-funnel')}
79
+ className={clsx(
80
+ 'bi',
81
+ selected.size > 0 ? ['bi-funnel-fill', 'text-primary'] : 'bi-funnel',
82
+ )}
80
83
  aria-hidden="true"
81
84
  />
82
85
  </Dropdown.Toggle>
83
- <Dropdown.Menu class="p-0">
84
- <div class="p-3 pb-0">
85
- <div class="d-flex align-items-center justify-content-between mb-2">
86
- <div class="fw-semibold text-nowrap">{label}</div>
86
+ <Dropdown.Menu className="p-0">
87
+ <div className="p-3 pb-0">
88
+ <div className="d-flex align-items-center justify-content-between mb-2">
89
+ <div className="fw-semibold text-nowrap">{label}</div>
87
90
  <button
88
91
  type="button"
89
- class={clsx('btn btn-link btn-sm text-decoration-none', {
92
+ className={clsx('btn btn-link btn-sm text-decoration-none', {
90
93
  // Hide the clear button if no filters are applied.
91
94
  // Use `visibility` instead of conditional rendering to avoid layout shift.
92
95
  invisible: selected.size === 0 && mode === 'include',
@@ -97,35 +100,35 @@ export function CategoricalColumnFilter<TData, TValue>({
97
100
  </button>
98
101
  </div>
99
102
 
100
- <div class="btn-group btn-group-sm w-100 mb-2">
103
+ <div className="btn-group btn-group-sm w-100 mb-2">
101
104
  <input
102
105
  type="radio"
103
- class="btn-check"
106
+ className="btn-check"
104
107
  name={`filter-${columnId}-options`}
105
108
  id={`filter-${columnId}-include`}
106
109
  autocomplete="off"
107
110
  checked={mode === 'include'}
108
111
  onChange={() => apply('include', selected)}
109
112
  />
110
- <label class="btn btn-outline-primary" for={`filter-${columnId}-include`}>
111
- <span class="text-nowrap">
112
- {mode === 'include' && <i class="bi bi-check-lg me-1" aria-hidden="true" />}
113
+ <label className="btn btn-outline-primary" for={`filter-${columnId}-include`}>
114
+ <span className="text-nowrap">
115
+ {mode === 'include' && <i className="bi bi-check-lg me-1" aria-hidden="true" />}
113
116
  Include
114
117
  </span>
115
118
  </label>
116
119
 
117
120
  <input
118
121
  type="radio"
119
- class="btn-check"
122
+ className="btn-check"
120
123
  name={`filter-${columnId}-options`}
121
124
  id={`filter-${columnId}-exclude`}
122
125
  autocomplete="off"
123
126
  checked={mode === 'exclude'}
124
127
  onChange={() => apply('exclude', selected)}
125
128
  />
126
- <label class="btn btn-outline-primary" for={`filter-${columnId}-exclude`}>
127
- <span class="text-nowrap">
128
- {mode === 'exclude' && <i class="bi bi-check-lg me-1" aria-hidden="true" />}
129
+ <label className="btn btn-outline-primary" for={`filter-${columnId}-exclude`}>
130
+ <span className="text-nowrap">
131
+ {mode === 'exclude' && <i className="bi bi-check-lg me-1" aria-hidden="true" />}
129
132
  Exclude
130
133
  </span>
131
134
  </label>
@@ -133,7 +136,7 @@ export function CategoricalColumnFilter<TData, TValue>({
133
136
  </div>
134
137
 
135
138
  <div
136
- class="list-group list-group-flush"
139
+ className="list-group list-group-flush"
137
140
  style={{
138
141
  // This is needed to prevent the last item's background from covering
139
142
  // the dropdown's border radius.
@@ -143,16 +146,16 @@ export function CategoricalColumnFilter<TData, TValue>({
143
146
  {allColumnValues.map((value) => {
144
147
  const isSelected = selected.has(value);
145
148
  return (
146
- <div key={value} class="list-group-item d-flex align-items-center gap-3">
147
- <div class="form-check">
149
+ <div key={value} className="list-group-item d-flex align-items-center gap-3">
150
+ <div className="form-check">
148
151
  <input
149
- class="form-check-input"
152
+ className="form-check-input"
150
153
  type="checkbox"
151
154
  checked={isSelected}
152
155
  id={`${columnId}-${value}`}
153
156
  onChange={() => toggleSelected(value)}
154
157
  />
155
- <label class="form-check-label fw-normal" for={`${columnId}-${value}`}>
158
+ <label className="form-check-label fw-normal" for={`${columnId}-${value}`}>
156
159
  {renderValueLabel({
157
160
  value,
158
161
  isSelected,
@@ -27,19 +27,19 @@ function ColumnLeafItem<RowDataModel>({
27
27
  return (
28
28
  <div
29
29
  key={column.id}
30
- class={clsx('px-2 py-1 d-flex align-items-center justify-content-between', className)}
30
+ className={clsx('px-2 py-1 d-flex align-items-center justify-content-between', className)}
31
31
  >
32
- <label class="form-check me-auto text-nowrap d-flex align-items-stretch">
32
+ <label className="form-check me-auto text-nowrap d-flex align-items-stretch">
33
33
  <input
34
34
  type="checkbox"
35
- class="form-check-input"
35
+ className="form-check-input"
36
36
  checked={column.getIsVisible()}
37
37
  disabled={!column.getCanHide()}
38
38
  aria-label={column.getIsVisible() ? `Hide '${header}' column` : `Show '${header}' column`}
39
39
  aria-describedby={`${column.id}-label`}
40
40
  onChange={column.getToggleVisibilityHandler()}
41
41
  />
42
- <span class="form-check-label ms-2" id={`${column.id}-label`}>
42
+ <span className="form-check-label ms-2" id={`${column.id}-label`}>
43
43
  {header}
44
44
  </span>
45
45
  </label>
@@ -48,7 +48,7 @@ function ColumnLeafItem<RowDataModel>({
48
48
  // Since the HTML changes, but we want to refocus the pin button, we track
49
49
  // the active pin button and refocuses it when the column manager is rerendered.
50
50
  id={`${column.id}-pin`}
51
- class={clsx(
51
+ className={clsx(
52
52
  'btn btn-sm btn-ghost ms-2',
53
53
  (!column.getCanPin() || !onPinningBoundary) && 'invisible',
54
54
  )}
@@ -59,7 +59,7 @@ function ColumnLeafItem<RowDataModel>({
59
59
  data-bs-toggle="tooltip"
60
60
  onClick={() => onTogglePin(column.id)}
61
61
  >
62
- <i class={`bi ${column.getIsPinned() ? 'bi-x' : 'bi-snow'}`} aria-hidden="true" />
62
+ <i className={`bi ${column.getIsPinned() ? 'bi-x' : 'bi-snow'}`} aria-hidden="true" />
63
63
  </button>
64
64
  </div>
65
65
  );
@@ -98,12 +98,12 @@ function ColumnGroupItem<RowDataModel>({
98
98
  (typeof column.columnDef.header === 'string' ? column.columnDef.header : column.id);
99
99
 
100
100
  return (
101
- <div class="d-flex flex-column">
102
- <div class="px-2 py-1 d-flex align-items-center justify-content-between">
103
- <div class="d-flex align-items-center flex-grow-1">
101
+ <div className="d-flex flex-column">
102
+ <div className="px-2 py-1 d-flex align-items-center justify-content-between">
103
+ <div className="d-flex align-items-center flex-grow-1">
104
104
  <input
105
105
  type="checkbox"
106
- class="form-check-input flex-shrink-0"
106
+ className="form-check-input flex-shrink-0"
107
107
  checked={isAllVisible}
108
108
  indeterminate={isSomeVisible}
109
109
  aria-label={`Toggle visibility for group '${header}'`}
@@ -111,16 +111,16 @@ function ColumnGroupItem<RowDataModel>({
111
111
  />
112
112
  <button
113
113
  type="button"
114
- class="btn btn-link text-decoration-none text-reset w-100 text-start d-flex align-items-center justify-content-between ps-2 py-0 pe-0"
114
+ className="btn btn-link text-decoration-none text-reset w-100 text-start d-flex align-items-center justify-content-between ps-2 py-0 pe-0"
115
115
  aria-expanded={isExpanded}
116
116
  onClick={(e) => {
117
117
  e.stopPropagation();
118
118
  setIsExpanded(!isExpanded);
119
119
  }}
120
120
  >
121
- <span class="fw-bold text-truncate">{header}</span>
121
+ <span className="fw-bold text-truncate">{header}</span>
122
122
  <i
123
- class={clsx(
123
+ className={clsx(
124
124
  'bi ms-2 text-muted',
125
125
  isExpanded ? 'bi-chevron-down' : 'bi-chevron-right',
126
126
  )}
@@ -130,7 +130,7 @@ function ColumnGroupItem<RowDataModel>({
130
130
  </div>
131
131
  </div>
132
132
  {isExpanded && (
133
- <div class="ps-3 border-start ms-3 mb-1">
133
+ <div className="ps-3 border-start ms-3 mb-1">
134
134
  {column.columns.map((childCol) => (
135
135
  <ColumnItem
136
136
  key={childCol.id}
@@ -310,7 +310,7 @@ export function ColumnManager<RowDataModel>({
310
310
  id="column-manager"
311
311
  variant="tanstack-table"
312
312
  >
313
- <i class="bi bi-view-list me-2" aria-hidden="true" /> View{' '}
313
+ <i className="bi bi-view-list me-2" aria-hidden="true" /> View{' '}
314
314
  </Dropdown.Toggle>
315
315
  <Dropdown.Menu style={{ maxHeight: '60vh', overflowY: 'auto' }}>
316
316
  {topContent && (
@@ -321,7 +321,7 @@ export function ColumnManager<RowDataModel>({
321
321
  )}
322
322
  {pinnedMenuColumns.length > 0 && (
323
323
  <>
324
- <div class="px-2 py-1 text-muted small" role="presentation">
324
+ <div className="px-2 py-1 text-muted small" role="presentation">
325
325
  Frozen columns
326
326
  </div>
327
327
  <div role="group">
@@ -358,11 +358,11 @@ export function ColumnManager<RowDataModel>({
358
358
  </>
359
359
  )}
360
360
  {showResetButton && (
361
- <div class="px-2 py-1">
361
+ <div className="px-2 py-1">
362
362
  <Button
363
363
  variant="secondary"
364
364
  size="sm"
365
- class="w-100"
365
+ className="w-100"
366
366
  aria-label="Reset all columns to default visibility and pinning"
367
367
  onClick={() => {
368
368
  table.resetColumnVisibility();
@@ -371,7 +371,7 @@ export function ColumnManager<RowDataModel>({
371
371
  setActiveElementId('column-manager');
372
372
  }}
373
373
  >
374
- <i class="bi bi-arrow-counterclockwise me-2" aria-hidden="true" />
374
+ <i className="bi bi-arrow-counterclockwise me-2" aria-hidden="true" />
375
375
  Reset view
376
376
  </Button>
377
377
  </div>
@@ -55,23 +55,23 @@ export function MultiSelectColumnFilter<TData, TValue>({
55
55
  <Dropdown align="end">
56
56
  <Dropdown.Toggle
57
57
  variant="link"
58
- class="text-muted p-0"
58
+ className="text-muted p-0"
59
59
  id={`filter-${columnId}`}
60
60
  aria-label={`Filter ${label.toLowerCase()}`}
61
61
  title={`Filter ${label.toLowerCase()}`}
62
62
  >
63
63
  <i
64
- class={clsx('bi', hasActiveFilter ? ['bi-funnel-fill', 'text-primary'] : 'bi-funnel')}
64
+ className={clsx('bi', hasActiveFilter ? ['bi-funnel-fill', 'text-primary'] : 'bi-funnel')}
65
65
  aria-hidden="true"
66
66
  />
67
67
  </Dropdown.Toggle>
68
- <Dropdown.Menu class="p-0">
69
- <div class="p-3 pb-0" style={{ minWidth: '250px' }}>
70
- <div class="d-flex align-items-center justify-content-between mb-2">
71
- <div class="fw-semibold">{label}</div>
68
+ <Dropdown.Menu className="p-0">
69
+ <div className="p-3 pb-0" style={{ minWidth: '250px' }}>
70
+ <div className="d-flex align-items-center justify-content-between mb-2">
71
+ <div className="fw-semibold">{label}</div>
72
72
  <button
73
73
  type="button"
74
- class="btn btn-link btn-sm text-decoration-none p-0"
74
+ className="btn btn-link btn-sm text-decoration-none p-0"
75
75
  onClick={() => column.setFilterValue([])}
76
76
  >
77
77
  Clear
@@ -80,7 +80,7 @@ export function MultiSelectColumnFilter<TData, TValue>({
80
80
  </div>
81
81
 
82
82
  <div
83
- class="list-group list-group-flush"
83
+ className="list-group list-group-flush"
84
84
  style={{
85
85
  // This is needed to prevent the last item's background from covering
86
86
  // the dropdown's border radius.
@@ -90,16 +90,16 @@ export function MultiSelectColumnFilter<TData, TValue>({
90
90
  {allColumnValues.map((value) => {
91
91
  const isSelected = selected.has(value);
92
92
  return (
93
- <div key={value} class="list-group-item d-flex align-items-center gap-3">
94
- <div class="form-check">
93
+ <div key={value} className="list-group-item d-flex align-items-center gap-3">
94
+ <div className="form-check">
95
95
  <input
96
- class="form-check-input"
96
+ className="form-check-input"
97
97
  type="checkbox"
98
98
  checked={isSelected}
99
99
  id={`${columnId}-${value}`}
100
100
  onChange={() => toggleSelected(value)}
101
101
  />
102
- <label class="form-check-label fw-normal" for={`${columnId}-${value}`}>
102
+ <label className="form-check-label fw-normal" for={`${columnId}-${value}`}>
103
103
  {renderValueLabel({
104
104
  value,
105
105
  isSelected,
@@ -43,7 +43,7 @@ export function NumericInputColumnFilter<TData, TValue>({
43
43
  <Dropdown align="end">
44
44
  <Dropdown.Toggle
45
45
  variant="link"
46
- class={clsx(
46
+ className={clsx(
47
47
  'text-muted p-0',
48
48
  hasActiveFilter && (isInvalid ? 'text-warning' : 'text-primary'),
49
49
  )}
@@ -52,7 +52,7 @@ export function NumericInputColumnFilter<TData, TValue>({
52
52
  title={`Filter ${label.toLowerCase()}`}
53
53
  >
54
54
  <i
55
- class={clsx(
55
+ className={clsx(
56
56
  'bi',
57
57
  isInvalid
58
58
  ? 'bi-exclamation-triangle'
@@ -63,18 +63,15 @@ export function NumericInputColumnFilter<TData, TValue>({
63
63
  aria-hidden="true"
64
64
  />
65
65
  </Dropdown.Toggle>
66
- <Dropdown.Menu
67
- // eslint-disable-next-line @eslint-react/no-forbidden-props
68
- className="p-0"
69
- >
70
- <div class="p-3" style={{ minWidth: '240px' }}>
71
- <div class="d-flex align-items-center justify-content-between mb-2">
72
- <label class="form-label fw-semibold mb-0" id={`${columnId}-filter-label`}>
66
+ <Dropdown.Menu className="p-0">
67
+ <div className="p-3" style={{ minWidth: '240px' }}>
68
+ <div className="d-flex align-items-center justify-content-between mb-2">
69
+ <label className="form-label fw-semibold mb-0" id={`${columnId}-filter-label`}>
73
70
  {label}
74
71
  </label>
75
72
  <button
76
73
  type="button"
77
- class={clsx(
74
+ className={clsx(
78
75
  'btn btn-link btn-sm text-decoration-none',
79
76
  !hasActiveFilter && 'invisible',
80
77
  )}
@@ -87,7 +84,7 @@ export function NumericInputColumnFilter<TData, TValue>({
87
84
  </div>
88
85
  <input
89
86
  type="text"
90
- class={clsx('form-control form-control-sm', isInvalid && 'is-invalid')}
87
+ className={clsx('form-control form-control-sm', isInvalid && 'is-invalid')}
91
88
  placeholder="e.g., >0, <5, =10"
92
89
  aria-labelledby={`${columnId}-filter-label`}
93
90
  value={filterValue}
@@ -102,19 +99,19 @@ export function NumericInputColumnFilter<TData, TValue>({
102
99
  onClick={(e) => e.stopPropagation()}
103
100
  />
104
101
  {isInvalid && (
105
- <div class="invalid-feedback d-block">
102
+ <div className="invalid-feedback d-block">
106
103
  Invalid filter format. Use operators like <code>&gt;5</code> or <code>&lt;=10</code>
107
104
  </div>
108
105
  )}
109
106
  {!isInvalid && (
110
- <small class="form-text text-nowrap" id={`${columnId}-filter-description`}>
107
+ <small className="form-text text-nowrap" id={`${columnId}-filter-description`}>
111
108
  Operators: <code>&lt;</code>, <code>&gt;</code>, <code>&lt;=</code>,{' '}
112
109
  <code>&gt;=</code>, <code>=</code>
113
110
  </small>
114
111
  )}
115
- <div class="form-check mt-2">
112
+ <div className="form-check mt-2">
116
113
  <input
117
- class="form-check-input"
114
+ className="form-check-input"
118
115
  type="checkbox"
119
116
  checked={emptyOnly}
120
117
  id={`${columnId}-empty-filter`}
@@ -126,7 +123,7 @@ export function NumericInputColumnFilter<TData, TValue>({
126
123
  );
127
124
  }}
128
125
  />
129
- <label class="form-check-label" for={`${columnId}-empty-filter`}>
126
+ <label className="form-check-label" for={`${columnId}-empty-filter`}>
130
127
  Empty values
131
128
  </label>
132
129
  </div>
@@ -123,7 +123,7 @@ export function PresetFilterDropdown<OptionName extends string, TData>({
123
123
  return (
124
124
  <Dropdown as={ButtonGroup}>
125
125
  <Dropdown.Toggle variant="tanstack-table">
126
- <i class="bi bi-funnel me-2" aria-hidden="true" />
126
+ <i className="bi bi-funnel me-2" aria-hidden="true" />
127
127
  {label}: {displayLabel}
128
128
  </Dropdown.Toggle>
129
129
  <Dropdown.Menu>
@@ -137,7 +137,7 @@ export function PresetFilterDropdown<OptionName extends string, TData>({
137
137
  active={isSelected}
138
138
  onClick={() => handleOptionClick(optionName as OptionName)}
139
139
  >
140
- <i class={`bi ${isSelected ? 'bi-check-circle-fill' : 'bi-circle'} me-2`} />
140
+ <i className={`bi ${isSelected ? 'bi-check-circle-fill' : 'bi-circle'} me-2`} />
141
141
  {optionName}
142
142
  </Dropdown.Item>
143
143
  );
@@ -145,7 +145,7 @@ export function PresetFilterDropdown<OptionName extends string, TData>({
145
145
  {/* Show Custom option only when no preset matches */}
146
146
  {selectedOption === null && (
147
147
  <Dropdown.Item as="button" type="button" active disabled>
148
- <i class="bi bi-check-circle-fill me-2" />
148
+ <i className="bi bi-check-circle-fill me-2" />
149
149
  Custom
150
150
  </Dropdown.Item>
151
151
  )}
@@ -43,7 +43,7 @@ function TableCell<RowDataModel>({
43
43
  tabIndex={0}
44
44
  data-grid-cell-row={rowIdx}
45
45
  data-grid-cell-col={colIdx}
46
- class={clsx(!canSort && !canFilter && 'text-center')}
46
+ className={clsx(!canSort && !canFilter && 'text-center')}
47
47
  style={{
48
48
  display: 'flex',
49
49
  width: cell.column.getSize(),
@@ -259,7 +259,7 @@ export function TanstackTable<RowDataModel>({
259
259
  const totalCount = table.getCoreRowModel().rows.length;
260
260
 
261
261
  return (
262
- <div style={{ position: 'relative' }} class="d-flex flex-column h-100">
262
+ <div style={{ position: 'relative' }} className="d-flex flex-column h-100">
263
263
  <div
264
264
  ref={scrollContainerRef}
265
265
  style={{
@@ -280,13 +280,13 @@ export function TanstackTable<RowDataModel>({
280
280
  }}
281
281
  >
282
282
  <table
283
- class="table table-hover mb-0"
283
+ className="table table-hover mb-0"
284
284
  style={{ display: 'grid', tableLayout: 'fixed' }}
285
285
  aria-label={title}
286
286
  role="grid"
287
287
  >
288
288
  <thead
289
- class="position-sticky top-0 w-100 border-top"
289
+ className="position-sticky top-0 w-100 border-top"
290
290
  style={{
291
291
  display: 'grid',
292
292
  zIndex: 1,
@@ -295,7 +295,7 @@ export function TanstackTable<RowDataModel>({
295
295
  >
296
296
  <tr
297
297
  key={leafHeaderGroup.id}
298
- class="d-flex w-100"
298
+ className="d-flex w-100"
299
299
  style={{ minWidth: `${table.getTotalSize()}px` }}
300
300
  >
301
301
  {/* Left pinned columns */}
@@ -342,14 +342,14 @@ export function TanstackTable<RowDataModel>({
342
342
  {/* Filler to span remaining width */}
343
343
  <th
344
344
  tabIndex={-1}
345
- class="d-flex flex-grow-1 p-0"
345
+ className="d-flex flex-grow-1 p-0"
346
346
  style={{ minWidth: 0 }}
347
347
  aria-hidden="true"
348
348
  />
349
349
  </tr>
350
350
  </thead>
351
351
  <tbody
352
- class="position-relative w-100"
352
+ className="position-relative w-100"
353
353
  style={{
354
354
  display: 'grid',
355
355
  height: `${rowVirtualizer.getTotalSize()}px`,
@@ -368,7 +368,7 @@ export function TanstackTable<RowDataModel>({
368
368
  key={row.id}
369
369
  ref={(node) => rowVirtualizer.measureElement(node)}
370
370
  data-index={virtualRow.index}
371
- class="d-flex position-absolute w-100"
371
+ className="d-flex position-absolute w-100"
372
372
  style={{
373
373
  transform: `translateY(${virtualRow.start}px)`,
374
374
  minWidth: `${table.getTotalSize()}px`,
@@ -428,7 +428,7 @@ export function TanstackTable<RowDataModel>({
428
428
  {/* Filler to span remaining width */}
429
429
  <td
430
430
  tabIndex={-1}
431
- class="d-flex flex-grow-1 p-0"
431
+ className="d-flex flex-grow-1 p-0"
432
432
  style={{ minWidth: 0 }}
433
433
  aria-hidden="true"
434
434
  />
@@ -442,7 +442,7 @@ export function TanstackTable<RowDataModel>({
442
442
  {table.getVisibleLeafColumns().length === 0 || displayedCount === 0 ? (
443
443
  <div>
444
444
  <div
445
- class="d-flex flex-column justify-content-center align-items-center p-4"
445
+ className="d-flex flex-column justify-content-center align-items-center p-4"
446
446
  style={{
447
447
  position: 'absolute',
448
448
  top: 0,
@@ -456,7 +456,7 @@ export function TanstackTable<RowDataModel>({
456
456
  aria-live="polite"
457
457
  >
458
458
  <div
459
- class="col-lg-6"
459
+ className="col-lg-6"
460
460
  style={{
461
461
  // Allow selecting and interacting with the empty state content.
462
462
  pointerEvents: 'auto',
@@ -559,11 +559,11 @@ export function TanstackTableCard<RowDataModel>({
559
559
  const totalCount = table.getCoreRowModel().rows.length;
560
560
 
561
561
  return (
562
- <div class={clsx('card d-flex flex-column', className)} {...divProps}>
563
- <div class="card-header bg-primary text-white">
564
- <div class="d-flex align-items-center justify-content-between gap-2">
562
+ <div className={clsx('card d-flex flex-column', className)} {...divProps}>
563
+ <div className="card-header bg-primary text-white">
564
+ <div className="d-flex align-items-center justify-content-between gap-2">
565
565
  <div>{title}</div>
566
- <div class="d-flex gap-2">
566
+ <div className="d-flex gap-2">
567
567
  {headerButtons}
568
568
 
569
569
  {downloadButtonOptions && (
@@ -577,12 +577,12 @@ export function TanstackTableCard<RowDataModel>({
577
577
  </div>
578
578
  </div>
579
579
  </div>
580
- <div class="card-body d-flex flex-row flex-wrap flex-grow-0 align-items-center gap-2">
581
- <div class="position-relative w-100" style={{ maxWidth: 'min(400px, 100%)' }}>
580
+ <div className="card-body d-flex flex-row flex-wrap flex-grow-0 align-items-center gap-2">
581
+ <div className="position-relative w-100" style={{ maxWidth: 'min(400px, 100%)' }}>
582
582
  <input
583
583
  ref={searchInputRef}
584
584
  type="text"
585
- class="form-control pl-ui-tanstack-table-search-input pl-ui-tanstack-table-focusable-shadow"
585
+ className="form-control pl-ui-tanstack-table-search-input pl-ui-tanstack-table-focusable-shadow"
586
586
  aria-label={globalFilter.placeholder}
587
587
  placeholder={globalFilter.placeholder}
588
588
  value={inputValue}
@@ -597,7 +597,7 @@ export function TanstackTableCard<RowDataModel>({
597
597
  <OverlayTrigger overlay={<Tooltip>Clear search</Tooltip>}>
598
598
  <button
599
599
  type="button"
600
- class="btn btn-floating-icon"
600
+ className="btn btn-floating-icon"
601
601
  aria-label="Clear search"
602
602
  onClick={() => {
603
603
  setInputValue('');
@@ -605,20 +605,20 @@ export function TanstackTableCard<RowDataModel>({
605
605
  table.setGlobalFilter('');
606
606
  }}
607
607
  >
608
- <i class="bi bi-x-circle-fill" aria-hidden="true" />
608
+ <i className="bi bi-x-circle-fill" aria-hidden="true" />
609
609
  </button>
610
610
  </OverlayTrigger>
611
611
  )}
612
612
  </div>
613
- <div class="d-flex flex-wrap flex-row align-items-center gap-2">
613
+ <div className="d-flex flex-wrap flex-row align-items-center gap-2">
614
614
  <ColumnManager table={table} topContent={columnManager?.topContent} />
615
615
  {columnManager?.buttons}
616
616
  </div>
617
- <div class="ms-auto text-muted text-nowrap">
617
+ <div className="ms-auto text-muted text-nowrap">
618
618
  Showing {displayedCount} of {totalCount} {totalCount === 1 ? singularLabel : pluralLabel}
619
619
  </div>
620
620
  </div>
621
- <div class="flex-grow-1">
621
+ <div className="flex-grow-1">
622
622
  <TanstackTable table={table} title={title} {...tableOptions} />
623
623
  </div>
624
624
  </div>
@@ -633,8 +633,8 @@ export function TanstackTableEmptyState({
633
633
  children: ComponentChildren;
634
634
  }) {
635
635
  return (
636
- <div class="d-flex flex-column justify-content-center align-items-center text-muted">
637
- <i class={clsx('bi', iconName, 'display-4 mb-2')} aria-hidden="true" />
636
+ <div className="d-flex flex-column justify-content-center align-items-center text-muted">
637
+ <i className={clsx('bi', iconName, 'display-4 mb-2')} aria-hidden="true" />
638
638
  <div>{children}</div>
639
639
  </div>
640
640
  );
@@ -54,22 +54,22 @@ export function TanstackTableDownloadButton<RowDataModel>({
54
54
  }
55
55
 
56
56
  return (
57
- <div class="btn-group">
57
+ <div className="btn-group">
58
58
  <button
59
59
  type="button"
60
60
  data-bs-toggle="dropdown"
61
61
  aria-expanded="false"
62
62
  aria-haspopup="true"
63
63
  aria-label={`Download ${pluralLabel} data in various formats`}
64
- class="btn btn-light btn-sm dropdown-toggle"
64
+ className="btn btn-light btn-sm dropdown-toggle"
65
65
  >
66
- <i aria-hidden="true" class="pe-2 bi bi-download" />
67
- <span class="d-none d-sm-inline">Download</span>
66
+ <i aria-hidden="true" className="pe-2 bi bi-download" />
67
+ <span className="d-none d-sm-inline">Download</span>
68
68
  </button>
69
- <ul class="dropdown-menu" role="menu" aria-label="Download options">
69
+ <ul className="dropdown-menu" role="menu" aria-label="Download options">
70
70
  <li role="presentation">
71
71
  <button
72
- class="dropdown-item"
72
+ className="dropdown-item"
73
73
  type="button"
74
74
  role="menuitem"
75
75
  aria-label={`Download all ${pluralLabel} as CSV file`}
@@ -81,7 +81,7 @@ export function TanstackTableDownloadButton<RowDataModel>({
81
81
  </li>
82
82
  <li role="presentation">
83
83
  <button
84
- class="dropdown-item"
84
+ className="dropdown-item"
85
85
  type="button"
86
86
  role="menuitem"
87
87
  aria-label={`Download all ${pluralLabel} as JSON file`}
@@ -95,7 +95,7 @@ export function TanstackTableDownloadButton<RowDataModel>({
95
95
  <>
96
96
  <li role="presentation">
97
97
  <button
98
- class="dropdown-item"
98
+ className="dropdown-item"
99
99
  type="button"
100
100
  role="menuitem"
101
101
  aria-label={`Download selected ${pluralLabel} as CSV file`}
@@ -108,7 +108,7 @@ export function TanstackTableDownloadButton<RowDataModel>({
108
108
  </li>
109
109
  <li role="presentation">
110
110
  <button
111
- class="dropdown-item"
111
+ className="dropdown-item"
112
112
  type="button"
113
113
  role="menuitem"
114
114
  aria-label={`Download selected ${pluralLabel} as JSON file`}
@@ -123,7 +123,7 @@ export function TanstackTableDownloadButton<RowDataModel>({
123
123
  )}
124
124
  <li role="presentation">
125
125
  <button
126
- class="dropdown-item"
126
+ className="dropdown-item"
127
127
  type="button"
128
128
  role="menuitem"
129
129
  aria-label={`Download filtered ${pluralLabel} as CSV file`}
@@ -136,7 +136,7 @@ export function TanstackTableDownloadButton<RowDataModel>({
136
136
  </li>
137
137
  <li role="presentation">
138
138
  <button
139
- class="dropdown-item"
139
+ className="dropdown-item"
140
140
  type="button"
141
141
  role="menuitem"
142
142
  aria-label={`Download filtered ${pluralLabel} as JSON file`}