@prairielearn/ui 1.7.1 → 1.7.2

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,11 @@
1
1
  # @prairielearn/ui
2
2
 
3
+ ## 1.7.2
4
+
5
+ ### Patch Changes
6
+
7
+ - 7ba5db4: Use `currentTarget` instead of `target` in event handlers
8
+
3
9
  ## 1.7.1
4
10
 
5
11
  ### Patch Changes
@@ -1 +1 @@
1
- {"version":3,"file":"NumericInputColumnFilter.d.ts","sourceRoot":"","sources":["../../src/components/NumericInputColumnFilter.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,sBAAsB,CAAC;AAInD,MAAM,MAAM,wBAAwB,GAChC;IACE,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,KAAK,CAAC;CAClB,GACD;IACE,WAAW,EAAE,EAAE,CAAC;IAChB,SAAS,EAAE,IAAI,CAAC;CACjB,CAAC;AAEN;;;;;;GAMG;AACH,wBAAgB,wBAAwB,CAAC,KAAK,EAAE,MAAM,EAAE,EACtD,MAAM,GACP,EAAE;IACD,MAAM,EAAE,MAAM,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC;CAC/B,yCAmHA;AAED;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAAC,WAAW,EAAE,MAAM,GAAG;IACvD,QAAQ,EAAE,GAAG,GAAG,GAAG,GAAG,IAAI,GAAG,IAAI,GAAG,GAAG,CAAC;IACxC,KAAK,EAAE,MAAM,CAAC;CACf,GAAG,IAAI,CAYP;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,qBAAqB,CACnC,GAAG,EAAE,GAAG,EACR,QAAQ,EAAE,MAAM,EAChB,EAAE,WAAW,EAAE,SAAS,EAAE,EAAE,wBAAwB,GACnD,OAAO,CA+BT"}
1
+ {"version":3,"file":"NumericInputColumnFilter.d.ts","sourceRoot":"","sources":["../../src/components/NumericInputColumnFilter.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,sBAAsB,CAAC;AAInD,MAAM,MAAM,wBAAwB,GAChC;IACE,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,KAAK,CAAC;CAClB,GACD;IACE,WAAW,EAAE,EAAE,CAAC;IAChB,SAAS,EAAE,IAAI,CAAC;CACjB,CAAC;AAEN;;;;;;GAMG;AACH,wBAAgB,wBAAwB,CAAC,KAAK,EAAE,MAAM,EAAE,EACtD,MAAM,GACP,EAAE;IACD,MAAM,EAAE,MAAM,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC;CAC/B,yCA+GA;AAED;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAAC,WAAW,EAAE,MAAM,GAAG;IACvD,QAAQ,EAAE,GAAG,GAAG,GAAG,GAAG,IAAI,GAAG,IAAI,GAAG,GAAG,CAAC;IACxC,KAAK,EAAE,MAAM,CAAC;CACf,GAAG,IAAI,CAYP;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,qBAAqB,CACnC,GAAG,EAAE,GAAG,EACR,QAAQ,EAAE,MAAM,EAChB,EAAE,WAAW,EAAE,SAAS,EAAE,EAAE,wBAAwB,GACnD,OAAO,CA+BT"}
@@ -31,18 +31,14 @@ export function NumericInputColumnFilter({ column, }) {
31
31
  className: "p-0", children: _jsxs("div", { class: "p-3", style: { minWidth: '240px' }, children: [_jsxs("div", { class: "d-flex align-items-center justify-content-between mb-2", children: [_jsx("label", { class: "form-label fw-semibold mb-0", id: `${columnId}-filter-label`, children: label }), _jsx("button", { type: "button", class: clsx('btn btn-link btn-sm text-decoration-none', !hasActiveFilter && 'invisible'), onClick: () => {
32
32
  column.setFilterValue({ filterValue: '', emptyOnly: false });
33
33
  }, children: "Clear" })] }), _jsx("input", { type: "text", class: clsx('form-control form-control-sm', isInvalid && 'is-invalid'), placeholder: "e.g., >0, <5, =10", "aria-labelledby": `${columnId}-filter-label`, value: filterValue, disabled: emptyOnly, "aria-describedby": `${columnId}-filter-description`, onInput: (e) => {
34
- if (e.target instanceof HTMLInputElement) {
35
- column.setFilterValue({
36
- filterValue: e.target.value,
37
- emptyOnly: false,
38
- });
39
- }
34
+ column.setFilterValue({
35
+ filterValue: e.currentTarget.value,
36
+ emptyOnly: false,
37
+ });
40
38
  }, onClick: (e) => e.stopPropagation() }), isInvalid && (_jsxs("div", { class: "invalid-feedback d-block", children: ["Invalid filter format. Use operators like ", _jsx("code", { children: ">5" }), " or ", _jsx("code", { children: "<=10" })] })), !isInvalid && (_jsxs("small", { class: "form-text text-nowrap", id: `${columnId}-filter-description`, children: ["Operators: ", _jsx("code", { children: "<" }), ", ", _jsx("code", { children: ">" }), ", ", _jsx("code", { children: "<=" }), ",", ' ', _jsx("code", { children: ">=" }), ", ", _jsx("code", { children: "=" })] })), _jsxs("div", { class: "form-check mt-2", children: [_jsx("input", { class: "form-check-input", type: "checkbox", checked: emptyOnly, id: `${columnId}-empty-filter`, onChange: (e) => {
41
- if (e.target instanceof HTMLInputElement) {
42
- column.setFilterValue(e.target.checked
43
- ? { filterValue: '', emptyOnly: true }
44
- : { filterValue: '', emptyOnly: false });
45
- }
39
+ column.setFilterValue(e.currentTarget.checked
40
+ ? { filterValue: '', emptyOnly: true }
41
+ : { filterValue: '', emptyOnly: false });
46
42
  } }), _jsx("label", { class: "form-check-label", for: `${columnId}-empty-filter`, children: "Empty values" })] })] }) })] }));
47
43
  }
48
44
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"NumericInputColumnFilter.js","sourceRoot":"","sources":["../../src/components/NumericInputColumnFilter.tsx"],"names":[],"mappings":"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","sourcesContent":["import type { Column } from '@tanstack/table-core';\nimport clsx from 'clsx';\nimport Dropdown from 'react-bootstrap/Dropdown';\n\nexport type NumericColumnFilterValue =\n | {\n filterValue: string;\n emptyOnly: false;\n }\n | {\n filterValue: '';\n emptyOnly: true;\n };\n\n/**\n * A component that allows the user to filter a numeric column using comparison operators.\n * Supports syntax like: <1, >0, <=5, >=10, =5, or just 5 (implicit equals)\n *\n * @param params\n * @param params.column - The TanStack Table column object\n */\nexport function NumericInputColumnFilter<TData, TValue>({\n column,\n}: {\n column: Column<TData, TValue>;\n}) {\n const columnId = column.id;\n const value = (column.getFilterValue() as NumericColumnFilterValue | undefined) ?? {\n filterValue: '',\n emptyOnly: false,\n };\n\n const label =\n column.columnDef.meta?.label ??\n (typeof column.columnDef.header === 'string' ? column.columnDef.header : column.id);\n\n const filterValue = value.filterValue;\n const emptyOnly = value.emptyOnly;\n const hasActiveFilter = filterValue.trim().length > 0 || emptyOnly;\n const isInvalid = filterValue.trim().length > 0 && parseNumericFilter(filterValue) === null;\n\n return (\n <Dropdown align=\"end\">\n <Dropdown.Toggle\n variant=\"link\"\n class={clsx(\n 'text-muted p-0',\n hasActiveFilter && (isInvalid ? 'text-warning' : 'text-primary'),\n )}\n id={`filter-${columnId}`}\n aria-label={`Filter ${label.toLowerCase()}`}\n title={`Filter ${label.toLowerCase()}`}\n >\n <i\n class={clsx(\n 'bi',\n isInvalid\n ? 'bi-exclamation-triangle'\n : hasActiveFilter\n ? 'bi-funnel-fill'\n : 'bi-funnel',\n )}\n aria-hidden=\"true\"\n />\n </Dropdown.Toggle>\n <Dropdown.Menu\n // eslint-disable-next-line @eslint-react/no-forbidden-props\n className=\"p-0\"\n >\n <div class=\"p-3\" style={{ minWidth: '240px' }}>\n <div class=\"d-flex align-items-center justify-content-between mb-2\">\n <label class=\"form-label fw-semibold mb-0\" id={`${columnId}-filter-label`}>\n {label}\n </label>\n <button\n type=\"button\"\n class={clsx(\n 'btn btn-link btn-sm text-decoration-none',\n !hasActiveFilter && 'invisible',\n )}\n onClick={() => {\n column.setFilterValue({ filterValue: '', emptyOnly: false });\n }}\n >\n Clear\n </button>\n </div>\n <input\n type=\"text\"\n class={clsx('form-control form-control-sm', isInvalid && 'is-invalid')}\n placeholder=\"e.g., >0, <5, =10\"\n aria-labelledby={`${columnId}-filter-label`}\n value={filterValue}\n disabled={emptyOnly}\n aria-describedby={`${columnId}-filter-description`}\n onInput={(e) => {\n if (e.target instanceof HTMLInputElement) {\n column.setFilterValue({\n filterValue: e.target.value,\n emptyOnly: false,\n });\n }\n }}\n onClick={(e) => e.stopPropagation()}\n />\n {isInvalid && (\n <div class=\"invalid-feedback d-block\">\n Invalid filter format. Use operators like <code>&gt;5</code> or <code>&lt;=10</code>\n </div>\n )}\n {!isInvalid && (\n <small class=\"form-text text-nowrap\" id={`${columnId}-filter-description`}>\n Operators: <code>&lt;</code>, <code>&gt;</code>, <code>&lt;=</code>,{' '}\n <code>&gt;=</code>, <code>=</code>\n </small>\n )}\n <div class=\"form-check mt-2\">\n <input\n class=\"form-check-input\"\n type=\"checkbox\"\n checked={emptyOnly}\n id={`${columnId}-empty-filter`}\n onChange={(e) => {\n if (e.target instanceof HTMLInputElement) {\n column.setFilterValue(\n e.target.checked\n ? { filterValue: '', emptyOnly: true }\n : { filterValue: '', emptyOnly: false },\n );\n }\n }}\n />\n <label class=\"form-check-label\" for={`${columnId}-empty-filter`}>\n Empty values\n </label>\n </div>\n </div>\n </Dropdown.Menu>\n </Dropdown>\n );\n}\n\n/**\n * Helper function to parse a numeric filter value.\n * Returns null if the filter is invalid or empty.\n *\n * @param filterValue - The filter string (e.g., \">5\", \"<=10\", \"3\")\n * @returns Parsed operator and value, or null if invalid\n */\nexport function parseNumericFilter(filterValue: string): {\n operator: '<' | '>' | '<=' | '>=' | '=';\n value: number;\n} | null {\n if (!filterValue.trim()) return null;\n\n const match = filterValue.trim().match(/^(<=?|>=?|=)?\\s*(-?\\d+\\.?\\d*)$/);\n if (!match) return null;\n\n const operator = (match[1] || '=') as '<' | '>' | '<=' | '>=' | '=';\n const value = Number.parseFloat(match[2]);\n\n if (Number.isNaN(value)) return null;\n\n return { operator, value };\n}\n\n/**\n * TanStack Table filter function for numeric columns.\n * Use this as the `filterFn` for numeric columns.\n *\n * @example\n * {\n * id: 'manual_points',\n * accessorKey: 'manual_points',\n * filterFn: numericColumnFilterFn,\n * }\n */\nexport function numericColumnFilterFn(\n row: any,\n columnId: string,\n { filterValue, emptyOnly }: NumericColumnFilterValue,\n): boolean {\n // Handle object-based filter value\n const cellValue = row.getValue(columnId) as number | null;\n const isEmpty = cellValue == null;\n\n if (emptyOnly) {\n return isEmpty;\n }\n\n // If there's no numeric filter, show all rows\n const parsed = parseNumericFilter(filterValue);\n if (!parsed) return true;\n\n // If cell is empty and we're doing numeric filtering, don't show it\n if (isEmpty) return false;\n\n // Apply numeric filter\n switch (parsed.operator) {\n case '<':\n return cellValue < parsed.value;\n case '>':\n return cellValue > parsed.value;\n case '<=':\n return cellValue <= parsed.value;\n case '>=':\n return cellValue >= parsed.value;\n case '=':\n return cellValue === parsed.value;\n default:\n return true;\n }\n}\n"]}
1
+ {"version":3,"file":"NumericInputColumnFilter.js","sourceRoot":"","sources":["../../src/components/NumericInputColumnFilter.tsx"],"names":[],"mappings":"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","sourcesContent":["import type { Column } from '@tanstack/table-core';\nimport clsx from 'clsx';\nimport Dropdown from 'react-bootstrap/Dropdown';\n\nexport type NumericColumnFilterValue =\n | {\n filterValue: string;\n emptyOnly: false;\n }\n | {\n filterValue: '';\n emptyOnly: true;\n };\n\n/**\n * A component that allows the user to filter a numeric column using comparison operators.\n * Supports syntax like: <1, >0, <=5, >=10, =5, or just 5 (implicit equals)\n *\n * @param params\n * @param params.column - The TanStack Table column object\n */\nexport function NumericInputColumnFilter<TData, TValue>({\n column,\n}: {\n column: Column<TData, TValue>;\n}) {\n const columnId = column.id;\n const value = (column.getFilterValue() as NumericColumnFilterValue | undefined) ?? {\n filterValue: '',\n emptyOnly: false,\n };\n\n const label =\n column.columnDef.meta?.label ??\n (typeof column.columnDef.header === 'string' ? column.columnDef.header : column.id);\n\n const filterValue = value.filterValue;\n const emptyOnly = value.emptyOnly;\n const hasActiveFilter = filterValue.trim().length > 0 || emptyOnly;\n const isInvalid = filterValue.trim().length > 0 && parseNumericFilter(filterValue) === null;\n\n return (\n <Dropdown align=\"end\">\n <Dropdown.Toggle\n variant=\"link\"\n class={clsx(\n 'text-muted p-0',\n hasActiveFilter && (isInvalid ? 'text-warning' : 'text-primary'),\n )}\n id={`filter-${columnId}`}\n aria-label={`Filter ${label.toLowerCase()}`}\n title={`Filter ${label.toLowerCase()}`}\n >\n <i\n class={clsx(\n 'bi',\n isInvalid\n ? 'bi-exclamation-triangle'\n : hasActiveFilter\n ? 'bi-funnel-fill'\n : 'bi-funnel',\n )}\n aria-hidden=\"true\"\n />\n </Dropdown.Toggle>\n <Dropdown.Menu\n // eslint-disable-next-line @eslint-react/no-forbidden-props\n className=\"p-0\"\n >\n <div class=\"p-3\" style={{ minWidth: '240px' }}>\n <div class=\"d-flex align-items-center justify-content-between mb-2\">\n <label class=\"form-label fw-semibold mb-0\" id={`${columnId}-filter-label`}>\n {label}\n </label>\n <button\n type=\"button\"\n class={clsx(\n 'btn btn-link btn-sm text-decoration-none',\n !hasActiveFilter && 'invisible',\n )}\n onClick={() => {\n column.setFilterValue({ filterValue: '', emptyOnly: false });\n }}\n >\n Clear\n </button>\n </div>\n <input\n type=\"text\"\n class={clsx('form-control form-control-sm', isInvalid && 'is-invalid')}\n placeholder=\"e.g., >0, <5, =10\"\n aria-labelledby={`${columnId}-filter-label`}\n value={filterValue}\n disabled={emptyOnly}\n aria-describedby={`${columnId}-filter-description`}\n onInput={(e) => {\n column.setFilterValue({\n filterValue: e.currentTarget.value,\n emptyOnly: false,\n });\n }}\n onClick={(e) => e.stopPropagation()}\n />\n {isInvalid && (\n <div class=\"invalid-feedback d-block\">\n Invalid filter format. Use operators like <code>&gt;5</code> or <code>&lt;=10</code>\n </div>\n )}\n {!isInvalid && (\n <small class=\"form-text text-nowrap\" id={`${columnId}-filter-description`}>\n Operators: <code>&lt;</code>, <code>&gt;</code>, <code>&lt;=</code>,{' '}\n <code>&gt;=</code>, <code>=</code>\n </small>\n )}\n <div class=\"form-check mt-2\">\n <input\n class=\"form-check-input\"\n type=\"checkbox\"\n checked={emptyOnly}\n id={`${columnId}-empty-filter`}\n onChange={(e) => {\n column.setFilterValue(\n e.currentTarget.checked\n ? { filterValue: '', emptyOnly: true }\n : { filterValue: '', emptyOnly: false },\n );\n }}\n />\n <label class=\"form-check-label\" for={`${columnId}-empty-filter`}>\n Empty values\n </label>\n </div>\n </div>\n </Dropdown.Menu>\n </Dropdown>\n );\n}\n\n/**\n * Helper function to parse a numeric filter value.\n * Returns null if the filter is invalid or empty.\n *\n * @param filterValue - The filter string (e.g., \">5\", \"<=10\", \"3\")\n * @returns Parsed operator and value, or null if invalid\n */\nexport function parseNumericFilter(filterValue: string): {\n operator: '<' | '>' | '<=' | '>=' | '=';\n value: number;\n} | null {\n if (!filterValue.trim()) return null;\n\n const match = filterValue.trim().match(/^(<=?|>=?|=)?\\s*(-?\\d+\\.?\\d*)$/);\n if (!match) return null;\n\n const operator = (match[1] || '=') as '<' | '>' | '<=' | '>=' | '=';\n const value = Number.parseFloat(match[2]);\n\n if (Number.isNaN(value)) return null;\n\n return { operator, value };\n}\n\n/**\n * TanStack Table filter function for numeric columns.\n * Use this as the `filterFn` for numeric columns.\n *\n * @example\n * {\n * id: 'manual_points',\n * accessorKey: 'manual_points',\n * filterFn: numericColumnFilterFn,\n * }\n */\nexport function numericColumnFilterFn(\n row: any,\n columnId: string,\n { filterValue, emptyOnly }: NumericColumnFilterValue,\n): boolean {\n // Handle object-based filter value\n const cellValue = row.getValue(columnId) as number | null;\n const isEmpty = cellValue == null;\n\n if (emptyOnly) {\n return isEmpty;\n }\n\n // If there's no numeric filter, show all rows\n const parsed = parseNumericFilter(filterValue);\n if (!parsed) return true;\n\n // If cell is empty and we're doing numeric filtering, don't show it\n if (isEmpty) return false;\n\n // Apply numeric filter\n switch (parsed.operator) {\n case '<':\n return cellValue < parsed.value;\n case '>':\n return cellValue > parsed.value;\n case '<=':\n return cellValue <= parsed.value;\n case '>=':\n return cellValue >= parsed.value;\n case '=':\n return cellValue === parsed.value;\n default:\n return true;\n }\n}\n"]}
@@ -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;AAK9C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAI/D,OAAO,EAEL,KAAK,gCAAgC,EACtC,MAAM,kCAAkC,CAAC;AAoE1C,UAAU,kBAAkB,CAAC,YAAY;IACvC,KAAK,EAAE,KAAK,CAAC,YAAY,CAAC,CAAC;IAC3B,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC,KAAK,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC,YAAY,EAAE,OAAO,CAAC,CAAA;KAAE,KAAK,GAAG,CAAC,OAAO,CAAC,CAAC;IAC5F,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,cAAc,CAAC,EAAE,GAAG,CAAC,OAAO,CAAC;IAC7B,UAAU,CAAC,EAAE,GAAG,CAAC,OAAO,CAAC;IACzB,SAAS,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC,cAAc,CAAC,GAAG,IAAI,CAAC;CACpD;AAID;;;;;;;;;;GAUG;AACH,wBAAgB,aAAa,CAAC,YAAY,EAAE,EAC1C,KAAK,EACL,KAAK,EACL,OAA4B,EAC5B,SAAc,EACd,cAAsC,EACtC,UAA8B,EAC9B,SAAS,GACV,EAAE,kBAAkB,CAAC,YAAY,CAAC,eA0WlC;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,iBAAiB,CAAC,YAAY,EAAE,EAC9C,KAAK,EACL,KAAK,EACL,aAAa,EACb,WAAW,EACX,aAAa,EACb,aAAa,EACb,YAAY,EACZ,YAAY,EACZ,qBAAqB,EACrB,SAAS,EACT,GAAG,QAAQ,EACZ,EAAE;IACD,KAAK,EAAE,KAAK,CAAC,YAAY,CAAC,CAAC;IAC3B,KAAK,EAAE,MAAM,CAAC;IACd,aAAa,EAAE,MAAM,CAAC;IACtB,WAAW,EAAE,MAAM,CAAC;IACpB,aAAa,CAAC,EAAE,GAAG,CAAC,OAAO,CAAC;IAC5B,aAAa,CAAC,EAAE;QACd,OAAO,CAAC,EAAE,GAAG,CAAC,OAAO,CAAC;QACtB,UAAU,CAAC,EAAE,GAAG,CAAC,OAAO,CAAC;KAC1B,CAAC;IACF,YAAY,EAAE;QACZ,WAAW,EAAE,MAAM,CAAC;KACrB,CAAC;IACF,YAAY,EAAE,OAAO,CAAC,IAAI,CAAC,kBAAkB,CAAC,YAAY,CAAC,EAAE,OAAO,CAAC,CAAC,CAAC;IACvE,qBAAqB,CAAC,EAAE,IAAI,CAC1B,gCAAgC,CAAC,YAAY,CAAC,EAC9C,OAAO,GAAG,eAAe,GAAG,aAAa,CAC1C,GAAG;QAAE,WAAW,CAAC,EAAE,MAAM,CAAC;QAAC,aAAa,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;CACtD,GAAG,IAAI,CAAC,cAAc,CAAC,KAAK,CAAC,EAAE,OAAO,CAAC,eA+FvC;AAED,wBAAgB,uBAAuB,CAAC,EACtC,QAAQ,EACR,QAAQ,GACT,EAAE;IACD,QAAQ,EAAE,MAAM,MAAM,EAAE,CAAC;IACzB,QAAQ,EAAE,iBAAiB,CAAC;CAC7B,eAOA"}
1
+ {"version":3,"file":"TanstackTable.d.ts","sourceRoot":"","sources":["../../src/components/TanstackTable.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAQ,MAAM,EAAO,KAAK,EAAE,MAAM,sBAAsB,CAAC;AAErE,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,QAAQ,CAAC;AAEhD,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,oBAAoB,CAAC;AAK9C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAI/D,OAAO,EAEL,KAAK,gCAAgC,EACtC,MAAM,kCAAkC,CAAC;AAoE1C,UAAU,kBAAkB,CAAC,YAAY;IACvC,KAAK,EAAE,KAAK,CAAC,YAAY,CAAC,CAAC;IAC3B,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC,KAAK,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC,YAAY,EAAE,OAAO,CAAC,CAAA;KAAE,KAAK,GAAG,CAAC,OAAO,CAAC,CAAC;IAC5F,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,cAAc,CAAC,EAAE,GAAG,CAAC,OAAO,CAAC;IAC7B,UAAU,CAAC,EAAE,GAAG,CAAC,OAAO,CAAC;IACzB,SAAS,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC,cAAc,CAAC,GAAG,IAAI,CAAC;CACpD;AAID;;;;;;;;;;GAUG;AACH,wBAAgB,aAAa,CAAC,YAAY,EAAE,EAC1C,KAAK,EACL,KAAK,EACL,OAA4B,EAC5B,SAAc,EACd,cAAsC,EACtC,UAA8B,EAC9B,SAAS,GACV,EAAE,kBAAkB,CAAC,YAAY,CAAC,eA0WlC;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,iBAAiB,CAAC,YAAY,EAAE,EAC9C,KAAK,EACL,KAAK,EACL,aAAa,EACb,WAAW,EACX,aAAa,EACb,aAAa,EACb,YAAY,EACZ,YAAY,EACZ,qBAAqB,EACrB,SAAS,EACT,GAAG,QAAQ,EACZ,EAAE;IACD,KAAK,EAAE,KAAK,CAAC,YAAY,CAAC,CAAC;IAC3B,KAAK,EAAE,MAAM,CAAC;IACd,aAAa,EAAE,MAAM,CAAC;IACtB,WAAW,EAAE,MAAM,CAAC;IACpB,aAAa,CAAC,EAAE,GAAG,CAAC,OAAO,CAAC;IAC5B,aAAa,CAAC,EAAE;QACd,OAAO,CAAC,EAAE,GAAG,CAAC,OAAO,CAAC;QACtB,UAAU,CAAC,EAAE,GAAG,CAAC,OAAO,CAAC;KAC1B,CAAC;IACF,YAAY,EAAE;QACZ,WAAW,EAAE,MAAM,CAAC;KACrB,CAAC;IACF,YAAY,EAAE,OAAO,CAAC,IAAI,CAAC,kBAAkB,CAAC,YAAY,CAAC,EAAE,OAAO,CAAC,CAAC,CAAC;IACvE,qBAAqB,CAAC,EAAE,IAAI,CAC1B,gCAAgC,CAAC,YAAY,CAAC,EAC9C,OAAO,GAAG,eAAe,GAAG,aAAa,CAC1C,GAAG;QAAE,WAAW,CAAC,EAAE,MAAM,CAAC;QAAC,aAAa,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;CACtD,GAAG,IAAI,CAAC,cAAc,CAAC,KAAK,CAAC,EAAE,OAAO,CAAC,eA8FvC;AAED,wBAAgB,uBAAuB,CAAC,EACtC,QAAQ,EACR,QAAQ,GACT,EAAE;IACD,QAAQ,EAAE,MAAM,MAAM,EAAE,CAAC;IACzB,QAAQ,EAAE,iBAAiB,CAAC;CAC7B,eAOA"}
@@ -262,9 +262,7 @@ export function TanstackTableCard({ table, title, singularLabel, pluralLabel, he
262
262
  const displayedCount = table.getRowModel().rows.length;
263
263
  const totalCount = table.getCoreRowModel().rows.length;
264
264
  return (_jsxs("div", { class: clsx('card d-flex flex-column', className), ...divProps, children: [_jsx("div", { class: "card-header bg-primary text-white", children: _jsxs("div", { class: "d-flex align-items-center justify-content-between gap-2", children: [_jsx("div", { children: title }), _jsxs("div", { class: "d-flex gap-2", children: [headerButtons, downloadButtonOptions && (_jsx(TanstackTableDownloadButton, { table: table, pluralLabel: pluralLabel, singularLabel: singularLabel, ...downloadButtonOptions }))] })] }) }), _jsxs("div", { class: "card-body d-flex flex-row flex-wrap flex-grow-0 align-items-center gap-2", children: [_jsxs("div", { class: "position-relative w-100", style: { maxWidth: 'min(400px, 100%)' }, children: [_jsx("input", { ref: searchInputRef, type: "text", class: "form-control pl-ui-tanstack-table-search-input pl-ui-tanstack-table-focusable-shadow", "aria-label": globalFilter.placeholder, placeholder: globalFilter.placeholder, value: inputValue, autoComplete: "off", onInput: (e) => {
265
- if (!(e.target instanceof HTMLInputElement))
266
- return;
267
- const value = e.target.value;
265
+ const value = e.currentTarget.value;
268
266
  setInputValue(value);
269
267
  debouncedSetFilter(value);
270
268
  } }), inputValue && (_jsx(OverlayTrigger, { overlay: _jsx(Tooltip, { children: "Clear search" }), children: _jsx("button", { type: "button", class: "btn btn-floating-icon", "aria-label": "Clear search", onClick: () => {
@@ -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, useState } from 'preact/hooks';\nimport type { JSX } from 'preact/jsx-runtime';\nimport OverlayTrigger from 'react-bootstrap/OverlayTrigger';\nimport Tooltip from 'react-bootstrap/Tooltip';\nimport { useDebouncedCallback } from 'use-debounce';\n\nimport type { ComponentProps } from '@prairielearn/preact-cjs';\nimport { run } from '@prairielearn/run';\n\nimport { ColumnManager } from './ColumnManager.js';\nimport {\n TanstackTableDownloadButton,\n type TanstackTableDownloadButtonProps,\n} from './TanstackTableDownloadButton.js';\nimport { TanstackTableHeaderCell } from './TanstackTableHeaderCell.js';\nimport { useAutoSizeColumns } from './useAutoSizeColumns.js';\n\nfunction TableCell<RowDataModel>({\n cell,\n rowIdx,\n colIdx,\n canSort,\n canFilter,\n wrapText,\n handleGridKeyDown,\n}: {\n cell: Cell<RowDataModel, unknown>;\n rowIdx: number;\n colIdx: number;\n canSort: boolean;\n canFilter: boolean;\n wrapText: boolean;\n handleGridKeyDown: (e: KeyboardEvent, rowIdx: number, colIdx: number) => void;\n}) {\n return (\n <td\n key={cell.id}\n tabIndex={0}\n data-grid-cell-row={rowIdx}\n data-grid-cell-col={colIdx}\n class={clsx(!canSort && !canFilter && 'text-center')}\n style={{\n display: 'flex',\n width: cell.column.getSize(),\n minWidth: 0,\n maxWidth: cell.column.getSize(),\n flexShrink: 0,\n position: cell.column.getIsPinned() === 'left' ? 'sticky' : undefined,\n left: cell.column.getIsPinned() === 'left' ? cell.column.getStart() : undefined,\n verticalAlign: 'middle',\n }}\n onKeyDown={(e) => handleGridKeyDown(e, rowIdx, colIdx)}\n >\n <div\n style={{\n display: 'block',\n minWidth: 0,\n maxWidth: '100%',\n overflow: wrapText ? 'visible' : 'hidden',\n textOverflow: wrapText ? undefined : 'ellipsis',\n whiteSpace: wrapText ? 'normal' : 'nowrap',\n flex: '1 1 0%',\n width: 0, // Allow flex to control width, but start from 0\n }}\n >\n {flexRender(cell.column.columnDef.cell, cell.getContext())}\n </div>\n </td>\n );\n}\n\nconst DefaultNoResultsState = (\n <TanstackTableEmptyState iconName=\"bi-search\">\n No results found matching your search criteria.\n </TanstackTableEmptyState>\n);\n\nconst DefaultEmptyState = (\n <TanstackTableEmptyState iconName=\"bi-eye-slash\">No results found.</TanstackTableEmptyState>\n);\n\ninterface TanstackTableProps<RowDataModel> {\n table: Table<RowDataModel>;\n title: string;\n filters?: Record<string, (props: { header: Header<RowDataModel, unknown> }) => JSX.Element>;\n rowHeight?: number;\n noResultsState?: JSX.Element;\n emptyState?: JSX.Element;\n scrollRef?: React.RefObject<HTMLDivElement> | null;\n}\n\nconst DEFAULT_FILTER_MAP = {};\n\n/**\n * A generic component that renders a full-width, resizeable Tanstack Table.\n * @param params\n * @param params.table - The table model\n * @param params.title - The title of the table\n * @param params.filters - The filters for the table\n * @param params.rowHeight - The height of the rows in the table\n * @param params.noResultsState - The no results state for the table\n * @param params.emptyState - The empty state for the table\n * @param params.scrollRef - Optional ref that will be attached to the scroll container element.\n */\nexport function TanstackTable<RowDataModel>({\n table,\n title,\n filters = DEFAULT_FILTER_MAP,\n rowHeight = 42,\n noResultsState = DefaultNoResultsState,\n emptyState = DefaultEmptyState,\n scrollRef,\n}: TanstackTableProps<RowDataModel>) {\n const parentRef = useRef<HTMLDivElement>(null);\n const tableRef = useRef<HTMLDivElement>(null);\n const scrollContainerRef = scrollRef ?? parentRef;\n\n const rows = [...table.getTopRows(), ...table.getCenterRows()];\n const rowVirtualizer = useVirtualizer({\n count: rows.length,\n getScrollElement: () => scrollContainerRef.current,\n estimateSize: () => rowHeight,\n overscan: 10,\n measureElement: (el) => el?.getBoundingClientRect().height ?? rowHeight,\n });\n\n const visibleColumns = table.getVisibleLeafColumns();\n const centerColumns = visibleColumns.filter((col) => !col.getIsPinned());\n\n const columnVirtualizer = useVirtualizer({\n count: centerColumns.length,\n estimateSize: (index) => centerColumns[index]?.getSize(),\n // `useAutoSizeColumns` solves a different problem (happens once when the column set changes)\n // and we don't need to measure the cells themselves, so we can use the default estimateSize.\n getScrollElement: () => scrollContainerRef.current,\n horizontal: true,\n overscan: 3,\n });\n\n const virtualColumns = columnVirtualizer.getVirtualItems();\n\n const virtualPaddingLeft = run(() => {\n if (columnVirtualizer && virtualColumns?.length > 0) {\n return virtualColumns[0]?.start ?? 0;\n }\n return null;\n });\n\n const virtualPaddingRight = run(() => {\n if (columnVirtualizer && virtualColumns?.length > 0) {\n return (\n columnVirtualizer.getTotalSize() - (virtualColumns[virtualColumns.length - 1]?.end ?? 0)\n );\n }\n return null;\n });\n\n // Check if any column has wrapping enabled\n const hasWrappedColumns = table.getAllLeafColumns().some((col) => col.columnDef.meta?.wrapText);\n\n // Create callback for remeasuring after resize\n const handleResizeEnd = useMemo(() => {\n if (!hasWrappedColumns) return undefined;\n return () => rowVirtualizer.measure();\n }, [hasWrappedColumns, rowVirtualizer]);\n\n const getVisibleCells = (row: Row<RowDataModel>) => [\n ...row.getLeftVisibleCells(),\n ...row.getCenterVisibleCells(),\n ];\n\n const handleGridKeyDown = (e: KeyboardEvent, rowIdx: number, colIdx: number) => {\n const rowLength = getVisibleCells(rows[rowIdx]).length;\n const adjacentCells: Record<KeyboardEvent['key'], { row: number; col: number }> = {\n ArrowDown: {\n row: Math.min(rows.length - 1, rowIdx + 1),\n col: colIdx,\n },\n ArrowUp: {\n row: Math.max(0, rowIdx - 1),\n col: colIdx,\n },\n ArrowRight: {\n row: rowIdx,\n col: Math.min(rowLength - 1, colIdx + 1),\n },\n ArrowLeft: {\n row: rowIdx,\n col: Math.max(0, colIdx - 1),\n },\n };\n\n const next = adjacentCells[e.key];\n\n if (!next) {\n return;\n }\n\n // Only handle arrow keys if we're in the cell itself, not in an interactive element\n const target = e.target as HTMLElement;\n if (target.tagName === 'TD') {\n // If we are on the leftmost column, we should allow left scrolling.\n if (colIdx === 0 && e.key === 'ArrowLeft') {\n return;\n }\n\n // If we are on the top row, we should allow up scrolling.\n if (rowIdx === 0 && e.key === 'ArrowUp') {\n return;\n }\n\n // If we are on the rightmost column, we should allow right scrolling.\n if (colIdx === rowLength - 1 && e.key === 'ArrowRight') {\n return;\n }\n\n e.preventDefault();\n const selector = `[data-grid-cell-row=\"${next.row}\"][data-grid-cell-col=\"${next.col}\"]`;\n const nextCell = tableRef.current?.querySelector(selector) as HTMLElement | null;\n nextCell?.focus();\n }\n };\n\n const virtualRows = rowVirtualizer.getVirtualItems();\n\n const headerGroups = table.getHeaderGroups();\n\n const leafHeaderGroup = headerGroups[headerGroups.length - 1];\n\n const leftPinnedHeaders = leafHeaderGroup.headers.filter(\n (header) => header.column.getIsPinned() === 'left',\n );\n const centerHeaders = leafHeaderGroup.headers.filter((header) => !header.column.getIsPinned());\n\n const isTableResizing = leafHeaderGroup.headers.some((header) => header.column.getIsResizing());\n\n // We toggle this here instead of in the parent since this component logically manages all UI for the table.\n useEffect(() => {\n document.body.classList.toggle('pl-ui-no-user-select', isTableResizing);\n }, [isTableResizing]);\n\n const hasAutoSized = useAutoSizeColumns(table, tableRef, filters);\n\n // Re-measure the virtualizer when auto-sizing completes\n useEffect(() => {\n if (hasAutoSized) {\n columnVirtualizer.measure();\n }\n }, [columnVirtualizer, hasAutoSized]);\n\n const displayedCount = table.getRowModel().rows.length;\n const totalCount = table.getCoreRowModel().rows.length;\n\n return (\n <div style={{ position: 'relative' }} class=\"d-flex flex-column h-100\">\n <div\n ref={scrollContainerRef}\n style={{\n position: 'absolute',\n top: 0,\n left: 0,\n right: 0,\n bottom: 0,\n overflow: 'auto',\n overflowAnchor: 'none',\n }}\n >\n <div\n ref={tableRef}\n style={{\n position: 'relative',\n width: `max(${table.getTotalSize()}px, 100%)`,\n }}\n >\n <table\n class=\"table table-hover mb-0\"\n style={{ display: 'grid', tableLayout: 'fixed' }}\n aria-label={title}\n role=\"grid\"\n >\n <thead\n class=\"position-sticky top-0 w-100 border-top\"\n style={{\n display: 'grid',\n zIndex: 1,\n borderBottom: 'var(--bs-border-width) solid black',\n }}\n >\n <tr\n key={leafHeaderGroup.id}\n class=\"d-flex w-100\"\n style={{ minWidth: `${table.getTotalSize()}px` }}\n >\n {/* Left pinned columns */}\n {leftPinnedHeaders.map((header) => {\n return (\n <TanstackTableHeaderCell\n key={header.id}\n header={header}\n filters={filters}\n table={table}\n handleResizeEnd={handleResizeEnd}\n isPinned=\"left\"\n />\n );\n })}\n\n {/* Virtual padding for left side of center columns */}\n {virtualPaddingLeft ? (\n <th style={{ display: 'flex', width: virtualPaddingLeft }} />\n ) : null}\n\n {/* Virtualized center columns */}\n {virtualColumns.map((virtualColumn) => {\n const header = centerHeaders[virtualColumn.index];\n if (!header) return null;\n\n return (\n <TanstackTableHeaderCell\n key={header.id}\n header={header}\n filters={filters}\n table={table}\n handleResizeEnd={handleResizeEnd}\n isPinned={false}\n />\n );\n })}\n\n {/* Virtual padding for right side of center columns */}\n {virtualPaddingRight ? (\n <th style={{ display: 'flex', width: virtualPaddingRight }} />\n ) : null}\n\n {/* Filler to span remaining width */}\n <th\n tabIndex={-1}\n class=\"d-flex flex-grow-1 p-0\"\n style={{ minWidth: 0 }}\n aria-hidden=\"true\"\n />\n </tr>\n </thead>\n <tbody\n class=\"position-relative w-100\"\n style={{\n display: 'grid',\n height: `${rowVirtualizer.getTotalSize()}px`,\n }}\n >\n {virtualRows.map((virtualRow) => {\n const row = rows[virtualRow.index];\n const rowIdx = virtualRow.index;\n const leftPinnedCells = row.getLeftVisibleCells();\n const centerCells = row.getCenterVisibleCells();\n\n let currentColIdx = 0;\n\n return (\n <tr\n key={row.id}\n ref={(node) => rowVirtualizer.measureElement(node)}\n data-index={virtualRow.index}\n class=\"d-flex position-absolute w-100\"\n style={{\n transform: `translateY(${virtualRow.start}px)`,\n minWidth: `${table.getTotalSize()}px`,\n }}\n >\n {leftPinnedCells.map((cell) => {\n const colIdx = currentColIdx++;\n const canSort = cell.column.getCanSort();\n const canFilter = cell.column.getCanFilter();\n const wrapText = cell.column.columnDef.meta?.wrapText ?? false;\n\n return (\n <TableCell\n key={cell.id}\n cell={cell}\n rowIdx={rowIdx}\n colIdx={colIdx}\n canSort={canSort}\n canFilter={canFilter}\n wrapText={wrapText}\n handleGridKeyDown={handleGridKeyDown}\n />\n );\n })}\n\n {virtualPaddingLeft ? (\n <td style={{ display: 'flex', width: virtualPaddingLeft }} />\n ) : null}\n\n {virtualColumns.map((virtualColumn) => {\n const cell = centerCells[virtualColumn.index];\n if (!cell) return null;\n\n const colIdx = currentColIdx++;\n const canSort = cell.column.getCanSort();\n const canFilter = cell.column.getCanFilter();\n const wrapText = cell.column.columnDef.meta?.wrapText ?? false;\n\n return (\n <TableCell\n key={cell.id}\n cell={cell}\n rowIdx={rowIdx}\n colIdx={colIdx}\n canSort={canSort}\n canFilter={canFilter}\n wrapText={wrapText}\n handleGridKeyDown={handleGridKeyDown}\n />\n );\n })}\n\n {virtualPaddingRight ? (\n <td style={{ display: 'flex', width: virtualPaddingRight }} />\n ) : null}\n\n {/* Filler to span remaining width */}\n <td\n tabIndex={-1}\n class=\"d-flex flex-grow-1 p-0\"\n style={{ minWidth: 0 }}\n aria-hidden=\"true\"\n />\n </tr>\n );\n })}\n </tbody>\n </table>\n </div>\n </div>\n {table.getVisibleLeafColumns().length === 0 || displayedCount === 0 ? (\n <div>\n <div\n class=\"d-flex flex-column justify-content-center align-items-center p-4\"\n style={{\n position: 'absolute',\n top: 0,\n left: 0,\n right: 0,\n bottom: 0,\n // Allow pointer events (e.g. scrolling) to reach the underlying table.\n pointerEvents: 'none',\n }}\n role=\"status\"\n aria-live=\"polite\"\n >\n <div\n class=\"col-lg-6\"\n style={{\n // Allow selecting and interacting with the empty state content.\n pointerEvents: 'auto',\n }}\n >\n {table.getVisibleLeafColumns().length === 0 ? (\n <TanstackTableEmptyState iconName=\"bi-eye-slash\">\n No columns selected. Use the View menu to show columns.\n </TanstackTableEmptyState>\n ) : displayedCount === 0 ? (\n totalCount > 0 ? (\n noResultsState\n ) : (\n emptyState\n )\n ) : null}\n </div>\n </div>\n </div>\n ) : null}\n </div>\n );\n}\n\n/**\n * A generic component that wraps the TanstackTable component in a card.\n * @param params\n * @param params.table - The table model\n * @param params.title - The title of the card\n * @param params.className - The class name to apply to the card\n * @param params.style - The style to apply to the card\n * @param params.singularLabel - The singular label for a single row in the table, e.g. \"student\"\n * @param params.pluralLabel - The plural label for multiple rows in the table, e.g. \"students\"\n * @param params.headerButtons - The buttons to display in the header\n * @param params.columnManager - Optional configuration for the column manager. See {@link ColumnManager} for more details.\n * @param params.columnManager.buttons - The buttons to display next to the column manager (View button)\n * @param params.columnManager.topContent - Optional content to display at the top of the column manager (View) dropdown menu\n * @param params.globalFilter - Configuration for the global filter\n * @param params.globalFilter.placeholder - Placeholder text for the search input\n * @param params.tableOptions - Specific options for the table. See {@link TanstackTableProps} for more details.\n * @param params.downloadButtonOptions - Specific options for the download button. See {@link TanstackTableDownloadButtonProps} for more details.\n */\nexport function TanstackTableCard<RowDataModel>({\n table,\n title,\n singularLabel,\n pluralLabel,\n headerButtons,\n columnManager,\n globalFilter,\n tableOptions,\n downloadButtonOptions,\n className,\n ...divProps\n}: {\n table: Table<RowDataModel>;\n title: string;\n singularLabel: string;\n pluralLabel: string;\n headerButtons?: JSX.Element;\n columnManager?: {\n buttons?: JSX.Element;\n topContent?: JSX.Element;\n };\n globalFilter: {\n placeholder: string;\n };\n tableOptions: Partial<Omit<TanstackTableProps<RowDataModel>, 'table'>>;\n downloadButtonOptions?: Omit<\n TanstackTableDownloadButtonProps<RowDataModel>,\n 'table' | 'singularLabel' | 'pluralLabel'\n > & { pluralLabel?: string; singularLabel?: string };\n} & Omit<ComponentProps<'div'>, 'class'>) {\n const searchInputRef = useRef<HTMLInputElement>(null);\n\n const [inputValue, setInputValue] = useState(\n () => (table.getState().globalFilter as string) ?? '',\n );\n\n // Debounce the filter update\n const debouncedSetFilter = useDebouncedCallback((value: string) => {\n table.setGlobalFilter(value);\n }, 150);\n\n // Focus the search input when Ctrl+F is pressed\n useEffect(() => {\n function onKeyDown(event: KeyboardEvent) {\n if ((event.ctrlKey || event.metaKey) && event.key.toLowerCase() === 'f') {\n if (searchInputRef.current && searchInputRef.current !== document.activeElement) {\n searchInputRef.current.focus();\n event.preventDefault();\n }\n }\n }\n document.addEventListener('keydown', onKeyDown);\n return () => document.removeEventListener('keydown', onKeyDown);\n }, []);\n\n const displayedCount = table.getRowModel().rows.length;\n const totalCount = table.getCoreRowModel().rows.length;\n\n return (\n <div class={clsx('card d-flex flex-column', className)} {...divProps}>\n <div class=\"card-header bg-primary text-white\">\n <div class=\"d-flex align-items-center justify-content-between gap-2\">\n <div>{title}</div>\n <div class=\"d-flex gap-2\">\n {headerButtons}\n\n {downloadButtonOptions && (\n <TanstackTableDownloadButton\n table={table}\n pluralLabel={pluralLabel}\n singularLabel={singularLabel}\n {...downloadButtonOptions}\n />\n )}\n </div>\n </div>\n </div>\n <div class=\"card-body d-flex flex-row flex-wrap flex-grow-0 align-items-center gap-2\">\n <div class=\"position-relative w-100\" style={{ maxWidth: 'min(400px, 100%)' }}>\n <input\n ref={searchInputRef}\n type=\"text\"\n class=\"form-control pl-ui-tanstack-table-search-input pl-ui-tanstack-table-focusable-shadow\"\n aria-label={globalFilter.placeholder}\n placeholder={globalFilter.placeholder}\n value={inputValue}\n autoComplete=\"off\"\n onInput={(e) => {\n if (!(e.target instanceof HTMLInputElement)) return;\n const value = e.target.value;\n setInputValue(value);\n debouncedSetFilter(value);\n }}\n />\n {inputValue && (\n <OverlayTrigger overlay={<Tooltip>Clear search</Tooltip>}>\n <button\n type=\"button\"\n class=\"btn btn-floating-icon\"\n aria-label=\"Clear search\"\n onClick={() => {\n setInputValue('');\n debouncedSetFilter.cancel();\n table.setGlobalFilter('');\n }}\n >\n <i class=\"bi bi-x-circle-fill\" aria-hidden=\"true\" />\n </button>\n </OverlayTrigger>\n )}\n </div>\n <div class=\"d-flex flex-wrap flex-row align-items-center gap-2\">\n <ColumnManager table={table} topContent={columnManager?.topContent} />\n {columnManager?.buttons}\n </div>\n <div class=\"ms-auto text-muted text-nowrap\">\n Showing {displayedCount} of {totalCount} {totalCount === 1 ? singularLabel : pluralLabel}\n </div>\n </div>\n <div class=\"flex-grow-1\">\n <TanstackTable table={table} title={title} {...tableOptions} />\n </div>\n </div>\n );\n}\n\nexport function TanstackTableEmptyState({\n iconName,\n children,\n}: {\n iconName: `bi-${string}`;\n children: ComponentChildren;\n}) {\n return (\n <div class=\"d-flex flex-column justify-content-center align-items-center text-muted\">\n <i class={clsx('bi', iconName, 'display-4 mb-2')} aria-hidden=\"true\" />\n <div>{children}</div>\n </div>\n );\n}\n"]}
1
+ {"version":3,"file":"TanstackTable.js","sourceRoot":"","sources":["../../src/components/TanstackTable.tsx"],"names":[],"mappings":"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","sourcesContent":["import { flexRender } from '@tanstack/react-table';\nimport { useVirtualizer } from '@tanstack/react-virtual';\nimport type { Cell, Header, Row, Table } from '@tanstack/table-core';\nimport clsx from 'clsx';\nimport type { ComponentChildren } from 'preact';\nimport { useEffect, useMemo, useRef, useState } from 'preact/hooks';\nimport type { JSX } from 'preact/jsx-runtime';\nimport OverlayTrigger from 'react-bootstrap/OverlayTrigger';\nimport Tooltip from 'react-bootstrap/Tooltip';\nimport { useDebouncedCallback } from 'use-debounce';\n\nimport type { ComponentProps } from '@prairielearn/preact-cjs';\nimport { run } from '@prairielearn/run';\n\nimport { ColumnManager } from './ColumnManager.js';\nimport {\n TanstackTableDownloadButton,\n type TanstackTableDownloadButtonProps,\n} from './TanstackTableDownloadButton.js';\nimport { TanstackTableHeaderCell } from './TanstackTableHeaderCell.js';\nimport { useAutoSizeColumns } from './useAutoSizeColumns.js';\n\nfunction TableCell<RowDataModel>({\n cell,\n rowIdx,\n colIdx,\n canSort,\n canFilter,\n wrapText,\n handleGridKeyDown,\n}: {\n cell: Cell<RowDataModel, unknown>;\n rowIdx: number;\n colIdx: number;\n canSort: boolean;\n canFilter: boolean;\n wrapText: boolean;\n handleGridKeyDown: (e: KeyboardEvent, rowIdx: number, colIdx: number) => void;\n}) {\n return (\n <td\n key={cell.id}\n tabIndex={0}\n data-grid-cell-row={rowIdx}\n data-grid-cell-col={colIdx}\n class={clsx(!canSort && !canFilter && 'text-center')}\n style={{\n display: 'flex',\n width: cell.column.getSize(),\n minWidth: 0,\n maxWidth: cell.column.getSize(),\n flexShrink: 0,\n position: cell.column.getIsPinned() === 'left' ? 'sticky' : undefined,\n left: cell.column.getIsPinned() === 'left' ? cell.column.getStart() : undefined,\n verticalAlign: 'middle',\n }}\n onKeyDown={(e) => handleGridKeyDown(e, rowIdx, colIdx)}\n >\n <div\n style={{\n display: 'block',\n minWidth: 0,\n maxWidth: '100%',\n overflow: wrapText ? 'visible' : 'hidden',\n textOverflow: wrapText ? undefined : 'ellipsis',\n whiteSpace: wrapText ? 'normal' : 'nowrap',\n flex: '1 1 0%',\n width: 0, // Allow flex to control width, but start from 0\n }}\n >\n {flexRender(cell.column.columnDef.cell, cell.getContext())}\n </div>\n </td>\n );\n}\n\nconst DefaultNoResultsState = (\n <TanstackTableEmptyState iconName=\"bi-search\">\n No results found matching your search criteria.\n </TanstackTableEmptyState>\n);\n\nconst DefaultEmptyState = (\n <TanstackTableEmptyState iconName=\"bi-eye-slash\">No results found.</TanstackTableEmptyState>\n);\n\ninterface TanstackTableProps<RowDataModel> {\n table: Table<RowDataModel>;\n title: string;\n filters?: Record<string, (props: { header: Header<RowDataModel, unknown> }) => JSX.Element>;\n rowHeight?: number;\n noResultsState?: JSX.Element;\n emptyState?: JSX.Element;\n scrollRef?: React.RefObject<HTMLDivElement> | null;\n}\n\nconst DEFAULT_FILTER_MAP = {};\n\n/**\n * A generic component that renders a full-width, resizeable Tanstack Table.\n * @param params\n * @param params.table - The table model\n * @param params.title - The title of the table\n * @param params.filters - The filters for the table\n * @param params.rowHeight - The height of the rows in the table\n * @param params.noResultsState - The no results state for the table\n * @param params.emptyState - The empty state for the table\n * @param params.scrollRef - Optional ref that will be attached to the scroll container element.\n */\nexport function TanstackTable<RowDataModel>({\n table,\n title,\n filters = DEFAULT_FILTER_MAP,\n rowHeight = 42,\n noResultsState = DefaultNoResultsState,\n emptyState = DefaultEmptyState,\n scrollRef,\n}: TanstackTableProps<RowDataModel>) {\n const parentRef = useRef<HTMLDivElement>(null);\n const tableRef = useRef<HTMLDivElement>(null);\n const scrollContainerRef = scrollRef ?? parentRef;\n\n const rows = [...table.getTopRows(), ...table.getCenterRows()];\n const rowVirtualizer = useVirtualizer({\n count: rows.length,\n getScrollElement: () => scrollContainerRef.current,\n estimateSize: () => rowHeight,\n overscan: 10,\n measureElement: (el) => el?.getBoundingClientRect().height ?? rowHeight,\n });\n\n const visibleColumns = table.getVisibleLeafColumns();\n const centerColumns = visibleColumns.filter((col) => !col.getIsPinned());\n\n const columnVirtualizer = useVirtualizer({\n count: centerColumns.length,\n estimateSize: (index) => centerColumns[index]?.getSize(),\n // `useAutoSizeColumns` solves a different problem (happens once when the column set changes)\n // and we don't need to measure the cells themselves, so we can use the default estimateSize.\n getScrollElement: () => scrollContainerRef.current,\n horizontal: true,\n overscan: 3,\n });\n\n const virtualColumns = columnVirtualizer.getVirtualItems();\n\n const virtualPaddingLeft = run(() => {\n if (columnVirtualizer && virtualColumns?.length > 0) {\n return virtualColumns[0]?.start ?? 0;\n }\n return null;\n });\n\n const virtualPaddingRight = run(() => {\n if (columnVirtualizer && virtualColumns?.length > 0) {\n return (\n columnVirtualizer.getTotalSize() - (virtualColumns[virtualColumns.length - 1]?.end ?? 0)\n );\n }\n return null;\n });\n\n // Check if any column has wrapping enabled\n const hasWrappedColumns = table.getAllLeafColumns().some((col) => col.columnDef.meta?.wrapText);\n\n // Create callback for remeasuring after resize\n const handleResizeEnd = useMemo(() => {\n if (!hasWrappedColumns) return undefined;\n return () => rowVirtualizer.measure();\n }, [hasWrappedColumns, rowVirtualizer]);\n\n const getVisibleCells = (row: Row<RowDataModel>) => [\n ...row.getLeftVisibleCells(),\n ...row.getCenterVisibleCells(),\n ];\n\n const handleGridKeyDown = (e: KeyboardEvent, rowIdx: number, colIdx: number) => {\n const rowLength = getVisibleCells(rows[rowIdx]).length;\n const adjacentCells: Record<KeyboardEvent['key'], { row: number; col: number }> = {\n ArrowDown: {\n row: Math.min(rows.length - 1, rowIdx + 1),\n col: colIdx,\n },\n ArrowUp: {\n row: Math.max(0, rowIdx - 1),\n col: colIdx,\n },\n ArrowRight: {\n row: rowIdx,\n col: Math.min(rowLength - 1, colIdx + 1),\n },\n ArrowLeft: {\n row: rowIdx,\n col: Math.max(0, colIdx - 1),\n },\n };\n\n const next = adjacentCells[e.key];\n\n if (!next) {\n return;\n }\n\n // Only handle arrow keys if we're in the cell itself, not in an interactive element\n const target = e.target as HTMLElement;\n if (target.tagName === 'TD') {\n // If we are on the leftmost column, we should allow left scrolling.\n if (colIdx === 0 && e.key === 'ArrowLeft') {\n return;\n }\n\n // If we are on the top row, we should allow up scrolling.\n if (rowIdx === 0 && e.key === 'ArrowUp') {\n return;\n }\n\n // If we are on the rightmost column, we should allow right scrolling.\n if (colIdx === rowLength - 1 && e.key === 'ArrowRight') {\n return;\n }\n\n e.preventDefault();\n const selector = `[data-grid-cell-row=\"${next.row}\"][data-grid-cell-col=\"${next.col}\"]`;\n const nextCell = tableRef.current?.querySelector(selector) as HTMLElement | null;\n nextCell?.focus();\n }\n };\n\n const virtualRows = rowVirtualizer.getVirtualItems();\n\n const headerGroups = table.getHeaderGroups();\n\n const leafHeaderGroup = headerGroups[headerGroups.length - 1];\n\n const leftPinnedHeaders = leafHeaderGroup.headers.filter(\n (header) => header.column.getIsPinned() === 'left',\n );\n const centerHeaders = leafHeaderGroup.headers.filter((header) => !header.column.getIsPinned());\n\n const isTableResizing = leafHeaderGroup.headers.some((header) => header.column.getIsResizing());\n\n // We toggle this here instead of in the parent since this component logically manages all UI for the table.\n useEffect(() => {\n document.body.classList.toggle('pl-ui-no-user-select', isTableResizing);\n }, [isTableResizing]);\n\n const hasAutoSized = useAutoSizeColumns(table, tableRef, filters);\n\n // Re-measure the virtualizer when auto-sizing completes\n useEffect(() => {\n if (hasAutoSized) {\n columnVirtualizer.measure();\n }\n }, [columnVirtualizer, hasAutoSized]);\n\n const displayedCount = table.getRowModel().rows.length;\n const totalCount = table.getCoreRowModel().rows.length;\n\n return (\n <div style={{ position: 'relative' }} class=\"d-flex flex-column h-100\">\n <div\n ref={scrollContainerRef}\n style={{\n position: 'absolute',\n top: 0,\n left: 0,\n right: 0,\n bottom: 0,\n overflow: 'auto',\n overflowAnchor: 'none',\n }}\n >\n <div\n ref={tableRef}\n style={{\n position: 'relative',\n width: `max(${table.getTotalSize()}px, 100%)`,\n }}\n >\n <table\n class=\"table table-hover mb-0\"\n style={{ display: 'grid', tableLayout: 'fixed' }}\n aria-label={title}\n role=\"grid\"\n >\n <thead\n class=\"position-sticky top-0 w-100 border-top\"\n style={{\n display: 'grid',\n zIndex: 1,\n borderBottom: 'var(--bs-border-width) solid black',\n }}\n >\n <tr\n key={leafHeaderGroup.id}\n class=\"d-flex w-100\"\n style={{ minWidth: `${table.getTotalSize()}px` }}\n >\n {/* Left pinned columns */}\n {leftPinnedHeaders.map((header) => {\n return (\n <TanstackTableHeaderCell\n key={header.id}\n header={header}\n filters={filters}\n table={table}\n handleResizeEnd={handleResizeEnd}\n isPinned=\"left\"\n />\n );\n })}\n\n {/* Virtual padding for left side of center columns */}\n {virtualPaddingLeft ? (\n <th style={{ display: 'flex', width: virtualPaddingLeft }} />\n ) : null}\n\n {/* Virtualized center columns */}\n {virtualColumns.map((virtualColumn) => {\n const header = centerHeaders[virtualColumn.index];\n if (!header) return null;\n\n return (\n <TanstackTableHeaderCell\n key={header.id}\n header={header}\n filters={filters}\n table={table}\n handleResizeEnd={handleResizeEnd}\n isPinned={false}\n />\n );\n })}\n\n {/* Virtual padding for right side of center columns */}\n {virtualPaddingRight ? (\n <th style={{ display: 'flex', width: virtualPaddingRight }} />\n ) : null}\n\n {/* Filler to span remaining width */}\n <th\n tabIndex={-1}\n class=\"d-flex flex-grow-1 p-0\"\n style={{ minWidth: 0 }}\n aria-hidden=\"true\"\n />\n </tr>\n </thead>\n <tbody\n class=\"position-relative w-100\"\n style={{\n display: 'grid',\n height: `${rowVirtualizer.getTotalSize()}px`,\n }}\n >\n {virtualRows.map((virtualRow) => {\n const row = rows[virtualRow.index];\n const rowIdx = virtualRow.index;\n const leftPinnedCells = row.getLeftVisibleCells();\n const centerCells = row.getCenterVisibleCells();\n\n let currentColIdx = 0;\n\n return (\n <tr\n key={row.id}\n ref={(node) => rowVirtualizer.measureElement(node)}\n data-index={virtualRow.index}\n class=\"d-flex position-absolute w-100\"\n style={{\n transform: `translateY(${virtualRow.start}px)`,\n minWidth: `${table.getTotalSize()}px`,\n }}\n >\n {leftPinnedCells.map((cell) => {\n const colIdx = currentColIdx++;\n const canSort = cell.column.getCanSort();\n const canFilter = cell.column.getCanFilter();\n const wrapText = cell.column.columnDef.meta?.wrapText ?? false;\n\n return (\n <TableCell\n key={cell.id}\n cell={cell}\n rowIdx={rowIdx}\n colIdx={colIdx}\n canSort={canSort}\n canFilter={canFilter}\n wrapText={wrapText}\n handleGridKeyDown={handleGridKeyDown}\n />\n );\n })}\n\n {virtualPaddingLeft ? (\n <td style={{ display: 'flex', width: virtualPaddingLeft }} />\n ) : null}\n\n {virtualColumns.map((virtualColumn) => {\n const cell = centerCells[virtualColumn.index];\n if (!cell) return null;\n\n const colIdx = currentColIdx++;\n const canSort = cell.column.getCanSort();\n const canFilter = cell.column.getCanFilter();\n const wrapText = cell.column.columnDef.meta?.wrapText ?? false;\n\n return (\n <TableCell\n key={cell.id}\n cell={cell}\n rowIdx={rowIdx}\n colIdx={colIdx}\n canSort={canSort}\n canFilter={canFilter}\n wrapText={wrapText}\n handleGridKeyDown={handleGridKeyDown}\n />\n );\n })}\n\n {virtualPaddingRight ? (\n <td style={{ display: 'flex', width: virtualPaddingRight }} />\n ) : null}\n\n {/* Filler to span remaining width */}\n <td\n tabIndex={-1}\n class=\"d-flex flex-grow-1 p-0\"\n style={{ minWidth: 0 }}\n aria-hidden=\"true\"\n />\n </tr>\n );\n })}\n </tbody>\n </table>\n </div>\n </div>\n {table.getVisibleLeafColumns().length === 0 || displayedCount === 0 ? (\n <div>\n <div\n class=\"d-flex flex-column justify-content-center align-items-center p-4\"\n style={{\n position: 'absolute',\n top: 0,\n left: 0,\n right: 0,\n bottom: 0,\n // Allow pointer events (e.g. scrolling) to reach the underlying table.\n pointerEvents: 'none',\n }}\n role=\"status\"\n aria-live=\"polite\"\n >\n <div\n class=\"col-lg-6\"\n style={{\n // Allow selecting and interacting with the empty state content.\n pointerEvents: 'auto',\n }}\n >\n {table.getVisibleLeafColumns().length === 0 ? (\n <TanstackTableEmptyState iconName=\"bi-eye-slash\">\n No columns selected. Use the View menu to show columns.\n </TanstackTableEmptyState>\n ) : displayedCount === 0 ? (\n totalCount > 0 ? (\n noResultsState\n ) : (\n emptyState\n )\n ) : null}\n </div>\n </div>\n </div>\n ) : null}\n </div>\n );\n}\n\n/**\n * A generic component that wraps the TanstackTable component in a card.\n * @param params\n * @param params.table - The table model\n * @param params.title - The title of the card\n * @param params.className - The class name to apply to the card\n * @param params.style - The style to apply to the card\n * @param params.singularLabel - The singular label for a single row in the table, e.g. \"student\"\n * @param params.pluralLabel - The plural label for multiple rows in the table, e.g. \"students\"\n * @param params.headerButtons - The buttons to display in the header\n * @param params.columnManager - Optional configuration for the column manager. See {@link ColumnManager} for more details.\n * @param params.columnManager.buttons - The buttons to display next to the column manager (View button)\n * @param params.columnManager.topContent - Optional content to display at the top of the column manager (View) dropdown menu\n * @param params.globalFilter - Configuration for the global filter\n * @param params.globalFilter.placeholder - Placeholder text for the search input\n * @param params.tableOptions - Specific options for the table. See {@link TanstackTableProps} for more details.\n * @param params.downloadButtonOptions - Specific options for the download button. See {@link TanstackTableDownloadButtonProps} for more details.\n */\nexport function TanstackTableCard<RowDataModel>({\n table,\n title,\n singularLabel,\n pluralLabel,\n headerButtons,\n columnManager,\n globalFilter,\n tableOptions,\n downloadButtonOptions,\n className,\n ...divProps\n}: {\n table: Table<RowDataModel>;\n title: string;\n singularLabel: string;\n pluralLabel: string;\n headerButtons?: JSX.Element;\n columnManager?: {\n buttons?: JSX.Element;\n topContent?: JSX.Element;\n };\n globalFilter: {\n placeholder: string;\n };\n tableOptions: Partial<Omit<TanstackTableProps<RowDataModel>, 'table'>>;\n downloadButtonOptions?: Omit<\n TanstackTableDownloadButtonProps<RowDataModel>,\n 'table' | 'singularLabel' | 'pluralLabel'\n > & { pluralLabel?: string; singularLabel?: string };\n} & Omit<ComponentProps<'div'>, 'class'>) {\n const searchInputRef = useRef<HTMLInputElement>(null);\n\n const [inputValue, setInputValue] = useState(\n () => (table.getState().globalFilter as string) ?? '',\n );\n\n // Debounce the filter update\n const debouncedSetFilter = useDebouncedCallback((value: string) => {\n table.setGlobalFilter(value);\n }, 150);\n\n // Focus the search input when Ctrl+F is pressed\n useEffect(() => {\n function onKeyDown(event: KeyboardEvent) {\n if ((event.ctrlKey || event.metaKey) && event.key.toLowerCase() === 'f') {\n if (searchInputRef.current && searchInputRef.current !== document.activeElement) {\n searchInputRef.current.focus();\n event.preventDefault();\n }\n }\n }\n document.addEventListener('keydown', onKeyDown);\n return () => document.removeEventListener('keydown', onKeyDown);\n }, []);\n\n const displayedCount = table.getRowModel().rows.length;\n const totalCount = table.getCoreRowModel().rows.length;\n\n return (\n <div class={clsx('card d-flex flex-column', className)} {...divProps}>\n <div class=\"card-header bg-primary text-white\">\n <div class=\"d-flex align-items-center justify-content-between gap-2\">\n <div>{title}</div>\n <div class=\"d-flex gap-2\">\n {headerButtons}\n\n {downloadButtonOptions && (\n <TanstackTableDownloadButton\n table={table}\n pluralLabel={pluralLabel}\n singularLabel={singularLabel}\n {...downloadButtonOptions}\n />\n )}\n </div>\n </div>\n </div>\n <div class=\"card-body d-flex flex-row flex-wrap flex-grow-0 align-items-center gap-2\">\n <div class=\"position-relative w-100\" style={{ maxWidth: 'min(400px, 100%)' }}>\n <input\n ref={searchInputRef}\n type=\"text\"\n class=\"form-control pl-ui-tanstack-table-search-input pl-ui-tanstack-table-focusable-shadow\"\n aria-label={globalFilter.placeholder}\n placeholder={globalFilter.placeholder}\n value={inputValue}\n autoComplete=\"off\"\n onInput={(e) => {\n const value = e.currentTarget.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"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@prairielearn/ui",
3
- "version": "1.7.1",
3
+ "version": "1.7.2",
4
4
  "type": "module",
5
5
  "repository": {
6
6
  "type": "git",
@@ -94,12 +94,10 @@ export function NumericInputColumnFilter<TData, TValue>({
94
94
  disabled={emptyOnly}
95
95
  aria-describedby={`${columnId}-filter-description`}
96
96
  onInput={(e) => {
97
- if (e.target instanceof HTMLInputElement) {
98
- column.setFilterValue({
99
- filterValue: e.target.value,
100
- emptyOnly: false,
101
- });
102
- }
97
+ column.setFilterValue({
98
+ filterValue: e.currentTarget.value,
99
+ emptyOnly: false,
100
+ });
103
101
  }}
104
102
  onClick={(e) => e.stopPropagation()}
105
103
  />
@@ -121,13 +119,11 @@ export function NumericInputColumnFilter<TData, TValue>({
121
119
  checked={emptyOnly}
122
120
  id={`${columnId}-empty-filter`}
123
121
  onChange={(e) => {
124
- if (e.target instanceof HTMLInputElement) {
125
- column.setFilterValue(
126
- e.target.checked
127
- ? { filterValue: '', emptyOnly: true }
128
- : { filterValue: '', emptyOnly: false },
129
- );
130
- }
122
+ column.setFilterValue(
123
+ e.currentTarget.checked
124
+ ? { filterValue: '', emptyOnly: true }
125
+ : { filterValue: '', emptyOnly: false },
126
+ );
131
127
  }}
132
128
  />
133
129
  <label class="form-check-label" for={`${columnId}-empty-filter`}>
@@ -586,8 +586,7 @@ export function TanstackTableCard<RowDataModel>({
586
586
  value={inputValue}
587
587
  autoComplete="off"
588
588
  onInput={(e) => {
589
- if (!(e.target instanceof HTMLInputElement)) return;
590
- const value = e.target.value;
589
+ const value = e.currentTarget.value;
591
590
  setInputValue(value);
592
591
  debouncedSetFilter(value);
593
592
  }}