@ai-matrx/design-system 0.14.3 → 0.15.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.
Files changed (106) hide show
  1. package/CHANGELOG.md +54 -0
  2. package/README.md +32 -0
  3. package/dist/data-table/copy-helpers.cjs +149 -0
  4. package/dist/data-table/copy-helpers.cjs.map +1 -0
  5. package/dist/data-table/copy-helpers.d.cts +31 -0
  6. package/dist/data-table/copy-helpers.d.ts +31 -0
  7. package/dist/data-table/copy-helpers.js +127 -0
  8. package/dist/data-table/copy-helpers.js.map +1 -0
  9. package/dist/data-table/copy-types.cjs +19 -0
  10. package/dist/data-table/copy-types.cjs.map +1 -0
  11. package/dist/data-table/copy-types.d.cts +137 -0
  12. package/dist/data-table/copy-types.d.ts +137 -0
  13. package/dist/data-table/copy-types.js +1 -0
  14. package/dist/data-table/copy-types.js.map +1 -0
  15. package/dist/data-table/data-row-inspector.cjs +1016 -0
  16. package/dist/data-table/data-row-inspector.cjs.map +1 -0
  17. package/dist/data-table/data-row-inspector.d.cts +34 -0
  18. package/dist/data-table/data-row-inspector.d.ts +34 -0
  19. package/dist/data-table/data-row-inspector.js +988 -0
  20. package/dist/data-table/data-row-inspector.js.map +1 -0
  21. package/dist/data-table/data-row-window.cjs +1136 -0
  22. package/dist/data-table/data-row-window.cjs.map +1 -0
  23. package/dist/data-table/data-row-window.d.cts +29 -0
  24. package/dist/data-table/data-row-window.d.ts +29 -0
  25. package/dist/data-table/data-row-window.js +1110 -0
  26. package/dist/data-table/data-row-window.js.map +1 -0
  27. package/dist/data-table/filter-engine.cjs +314 -0
  28. package/dist/data-table/filter-engine.cjs.map +1 -0
  29. package/dist/data-table/filter-engine.d.cts +35 -0
  30. package/dist/data-table/filter-engine.d.ts +35 -0
  31. package/dist/data-table/filter-engine.js +292 -0
  32. package/dist/data-table/filter-engine.js.map +1 -0
  33. package/dist/data-table/host.cjs +40 -0
  34. package/dist/data-table/host.cjs.map +1 -0
  35. package/dist/data-table/host.d.cts +73 -0
  36. package/dist/data-table/host.d.ts +73 -0
  37. package/dist/data-table/host.js +20 -0
  38. package/dist/data-table/host.js.map +1 -0
  39. package/dist/data-table/index.cjs +7198 -0
  40. package/dist/data-table/index.cjs.map +1 -0
  41. package/dist/data-table/index.d.cts +27 -0
  42. package/dist/data-table/index.d.ts +27 -0
  43. package/dist/data-table/index.js +7202 -0
  44. package/dist/data-table/index.js.map +1 -0
  45. package/dist/data-table/infer-filter.cjs +111 -0
  46. package/dist/data-table/infer-filter.cjs.map +1 -0
  47. package/dist/data-table/infer-filter.d.cts +17 -0
  48. package/dist/data-table/infer-filter.d.ts +17 -0
  49. package/dist/data-table/infer-filter.js +89 -0
  50. package/dist/data-table/infer-filter.js.map +1 -0
  51. package/dist/data-table/layered-filters.cjs +249 -0
  52. package/dist/data-table/layered-filters.cjs.map +1 -0
  53. package/dist/data-table/layered-filters.d.cts +3 -0
  54. package/dist/data-table/layered-filters.d.ts +3 -0
  55. package/dist/data-table/layered-filters.js +229 -0
  56. package/dist/data-table/layered-filters.js.map +1 -0
  57. package/dist/data-table/pagination.cjs +595 -0
  58. package/dist/data-table/pagination.cjs.map +1 -0
  59. package/dist/data-table/pagination.d.cts +34 -0
  60. package/dist/data-table/pagination.d.ts +34 -0
  61. package/dist/data-table/pagination.js +563 -0
  62. package/dist/data-table/pagination.js.map +1 -0
  63. package/dist/data-table/query-control.cjs +126 -0
  64. package/dist/data-table/query-control.cjs.map +1 -0
  65. package/dist/data-table/query-control.d.cts +32 -0
  66. package/dist/data-table/query-control.d.ts +32 -0
  67. package/dist/data-table/query-control.js +104 -0
  68. package/dist/data-table/query-control.js.map +1 -0
  69. package/dist/data-table/scroll-pagination.cjs +222 -0
  70. package/dist/data-table/scroll-pagination.cjs.map +1 -0
  71. package/dist/data-table/scroll-pagination.d.cts +37 -0
  72. package/dist/data-table/scroll-pagination.d.ts +37 -0
  73. package/dist/data-table/scroll-pagination.js +202 -0
  74. package/dist/data-table/scroll-pagination.js.map +1 -0
  75. package/dist/data-table/types.cjs +19 -0
  76. package/dist/data-table/types.cjs.map +1 -0
  77. package/dist/data-table/types.d.cts +3 -0
  78. package/dist/data-table/types.d.ts +3 -0
  79. package/dist/data-table/types.js +1 -0
  80. package/dist/data-table/types.js.map +1 -0
  81. package/dist/data-table/url-state.cjs +201 -0
  82. package/dist/data-table/url-state.cjs.map +1 -0
  83. package/dist/data-table/url-state.d.cts +33 -0
  84. package/dist/data-table/url-state.d.ts +33 -0
  85. package/dist/data-table/url-state.js +184 -0
  86. package/dist/data-table/url-state.js.map +1 -0
  87. package/dist/data-table/uuid-cell.cjs +371 -0
  88. package/dist/data-table/uuid-cell.cjs.map +1 -0
  89. package/dist/data-table/uuid-cell.d.cts +31 -0
  90. package/dist/data-table/uuid-cell.d.ts +31 -0
  91. package/dist/data-table/uuid-cell.js +345 -0
  92. package/dist/data-table/uuid-cell.js.map +1 -0
  93. package/dist/data-table/xlsx.cjs +100 -0
  94. package/dist/data-table/xlsx.cjs.map +1 -0
  95. package/dist/data-table/xlsx.d.cts +16 -0
  96. package/dist/data-table/xlsx.d.ts +16 -0
  97. package/dist/data-table/xlsx.js +70 -0
  98. package/dist/data-table/xlsx.js.map +1 -0
  99. package/dist/index.cjs +9 -5
  100. package/dist/index.cjs.map +1 -1
  101. package/dist/index.js +9 -5
  102. package/dist/index.js.map +1 -1
  103. package/dist/styles.css +22 -0
  104. package/dist/types-ClaxTaXq.d.ts +798 -0
  105. package/dist/types-XXbVB1sh.d.cts +798 -0
  106. package/package.json +164 -1
@@ -0,0 +1,111 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/data-table/infer-filter.ts
21
+ var infer_filter_exports = {};
22
+ __export(infer_filter_exports, {
23
+ collectSelectOptions: () => collectSelectOptions,
24
+ filterKindLabel: () => filterKindLabel,
25
+ resolveFilterKind: () => resolveFilterKind
26
+ });
27
+ module.exports = __toCommonJS(infer_filter_exports);
28
+
29
+ // src/data-table/filter-engine.ts
30
+ var SELECT_EMPTY_VALUE = "__empty__";
31
+ var SELECT_NOT_EMPTY_VALUE = "__not_empty__";
32
+ function getCellValue(row, col) {
33
+ if (col.accessorFn) return col.accessorFn(row);
34
+ if (col.accessorKey) {
35
+ return row[col.accessorKey];
36
+ }
37
+ return void 0;
38
+ }
39
+ function stringifyCellValue(value) {
40
+ if (value == null) return "";
41
+ if (typeof value === "string") return value;
42
+ if (typeof value === "number" || typeof value === "boolean") {
43
+ return String(value);
44
+ }
45
+ if (value instanceof Date) return value.toISOString();
46
+ try {
47
+ return JSON.stringify(value);
48
+ } catch {
49
+ return String(value);
50
+ }
51
+ }
52
+
53
+ // src/data-table/infer-filter.ts
54
+ var SELECT_CARDINALITY_CAP = 24;
55
+ function resolveFilterKind(col, sampleRows) {
56
+ if (col.filter === false) return null;
57
+ if (col.filter && col.filter !== "auto") return col.filter;
58
+ const values = sampleRows.slice(0, 200).map((row) => getCellValue(row, col)).filter((v) => v != null);
59
+ if (values.length === 0) return "text";
60
+ if (values.every((v) => typeof v === "boolean")) return "boolean";
61
+ if (values.every((v) => typeof v === "number" && Number.isFinite(v))) {
62
+ return "number";
63
+ }
64
+ const strings = values.map(stringifyCellValue);
65
+ const unique = new Set(strings);
66
+ if (unique.size >= 1 && unique.size <= SELECT_CARDINALITY_CAP && unique.size < strings.length * 0.6) {
67
+ return "select";
68
+ }
69
+ return "text";
70
+ }
71
+ function collectSelectOptions(col, rows) {
72
+ const seen = /* @__PURE__ */ new Set();
73
+ const options = [];
74
+ let hasEmpty = false;
75
+ for (const row of rows) {
76
+ const raw = getCellValue(row, col);
77
+ const value = raw == null ? "" : stringifyCellValue(raw);
78
+ if (!value) {
79
+ hasEmpty = true;
80
+ continue;
81
+ }
82
+ if (col.filterOptions?.length || seen.has(value)) continue;
83
+ seen.add(value);
84
+ options.push({ value, label: value });
85
+ }
86
+ const base = col.filterOptions?.length ? [...col.filterOptions] : options.sort((a, b) => a.label.localeCompare(b.label));
87
+ if (hasEmpty) {
88
+ base.unshift(
89
+ { value: SELECT_EMPTY_VALUE, label: "(empty)" },
90
+ { value: SELECT_NOT_EMPTY_VALUE, label: "(not empty)" }
91
+ );
92
+ }
93
+ return base;
94
+ }
95
+ function filterKindLabel(kind) {
96
+ switch (kind) {
97
+ case "text":
98
+ return "Text";
99
+ case "select":
100
+ return "Select";
101
+ case "boolean":
102
+ return "Boolean";
103
+ case "number":
104
+ return "Number";
105
+ case "auto":
106
+ return "Auto";
107
+ case false:
108
+ return "Off";
109
+ }
110
+ }
111
+ //# sourceMappingURL=infer-filter.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/data-table/infer-filter.ts","../../src/data-table/filter-engine.ts"],"sourcesContent":["import type { ColumnFilterKind, MatrxColumnDef } from \"./types\";\nimport {\n SELECT_EMPTY_VALUE,\n SELECT_NOT_EMPTY_VALUE,\n getCellValue,\n stringifyCellValue,\n} from \"./filter-engine\";\n\n/** Auto-inference switches to text above this many distinct values… */\nconst SELECT_CARDINALITY_CAP = 24;\n\nexport type ResolvedFilterKind = \"text\" | \"select\" | \"boolean\" | \"number\";\n\n/**\n * Infer a filter UI from sample row values. Prefer select for low-cardinality\n * string/enum columns, boolean for booleans, number for numeric, else text.\n */\nexport function resolveFilterKind<T>(\n col: MatrxColumnDef<T>,\n sampleRows: T[],\n): ResolvedFilterKind | null {\n if (col.filter === false) return null;\n if (col.filter && col.filter !== \"auto\") return col.filter;\n\n const values = sampleRows\n .slice(0, 200)\n .map((row) => getCellValue(row, col))\n .filter((v) => v != null);\n\n if (values.length === 0) return \"text\";\n\n if (values.every((v) => typeof v === \"boolean\")) return \"boolean\";\n if (values.every((v) => typeof v === \"number\" && Number.isFinite(v))) {\n return \"number\";\n }\n\n const strings = values.map(stringifyCellValue);\n const unique = new Set(strings);\n if (\n unique.size >= 1 &&\n unique.size <= SELECT_CARDINALITY_CAP &&\n unique.size < strings.length * 0.6\n ) {\n return \"select\";\n }\n\n return \"text\";\n}\n\nexport function collectSelectOptions<T>(\n col: MatrxColumnDef<T>,\n rows: T[],\n): Array<{ value: string; label: string }> {\n const seen = new Set<string>();\n const options: Array<{ value: string; label: string }> = [];\n let hasEmpty = false;\n for (const row of rows) {\n const raw = getCellValue(row, col);\n const value = raw == null ? \"\" : stringifyCellValue(raw);\n if (!value) {\n hasEmpty = true;\n continue;\n }\n if (col.filterOptions?.length || seen.has(value)) continue;\n seen.add(value);\n options.push({ value, label: value });\n }\n const base = col.filterOptions?.length\n ? [...col.filterOptions]\n : options.sort((a, b) => a.label.localeCompare(b.label));\n // Emptiness sentinels: whenever the column has blank cells, every select\n // filter can isolate them — or exclude them — with zero per-table config.\n if (hasEmpty) {\n base.unshift(\n { value: SELECT_EMPTY_VALUE, label: \"(empty)\" },\n { value: SELECT_NOT_EMPTY_VALUE, label: \"(not empty)\" },\n );\n }\n return base;\n}\n\nexport function filterKindLabel(\n kind: ColumnFilterKind | ResolvedFilterKind,\n): string {\n switch (kind) {\n case \"text\":\n return \"Text\";\n case \"select\":\n return \"Select\";\n case \"boolean\":\n return \"Boolean\";\n case \"number\":\n return \"Number\";\n case \"auto\":\n return \"Auto\";\n case false:\n return \"Off\";\n }\n}\n","import type {\n ColumnFilterValue,\n ColumnFiltersState,\n MatrxColumnDef,\n SortState,\n TableSearchMatchMode,\n} from \"./types\";\nimport {\n completeLayeredFilterRules,\n layeredFilterMatchesValue,\n type LayeredFilterRule,\n} from \"./layered-filters\";\n\n/**\n * Sentinel select-filter values for \"no value\" / \"any value\" — every select\n * filter offers them automatically when the column has empty cells, so any\n * table can isolate rows missing a value (or having one) without extra config.\n */\nexport const SELECT_EMPTY_VALUE = \"__empty__\";\nexport const SELECT_NOT_EMPTY_VALUE = \"__not_empty__\";\n\nexport function columnId<T>(col: MatrxColumnDef<T>): string {\n if (col.id) return col.id;\n if (col.accessorKey) return String(col.accessorKey);\n throw new Error(\"MatrxDataTable column requires `id` or `accessorKey`\");\n}\n\nexport function getCellValue<T>(row: T, col: MatrxColumnDef<T>): unknown {\n if (col.accessorFn) return col.accessorFn(row);\n if (col.accessorKey) {\n return (row as Record<string, unknown>)[col.accessorKey];\n }\n return undefined;\n}\n\nexport function getFilterValue<T>(row: T, col: MatrxColumnDef<T>): unknown {\n return col.filterValue ? col.filterValue(row) : getCellValue(row, col);\n}\n\nexport function getSortValue<T>(row: T, col: MatrxColumnDef<T>): unknown {\n return col.sortValue ? col.sortValue(row) : getCellValue(row, col);\n}\n\nexport function stringifyCellValue(value: unknown): string {\n if (value == null) return \"\";\n if (typeof value === \"string\") return value;\n if (typeof value === \"number\" || typeof value === \"boolean\") {\n return String(value);\n }\n if (value instanceof Date) return value.toISOString();\n try {\n return JSON.stringify(value);\n } catch {\n return String(value);\n }\n}\n\n/** Unicode-aware tokenization shared by whole-word table search. */\nexport function tableSearchWords(value: string): string[] {\n return value\n .toLocaleLowerCase()\n .split(/[^\\p{L}\\p{N}]+/u)\n .filter(Boolean);\n}\n\n/**\n * Match the primary table search against one row's searchable text. In\n * whole-word mode every entered word must exist as a complete token, so\n * `are` matches `we are ready` but never `hardware`.\n */\nexport function matchesTableSearch(\n searchableText: string,\n query: string,\n mode: TableSearchMatchMode = \"contains\",\n): boolean {\n const normalizedQuery = query.trim().toLocaleLowerCase();\n if (!normalizedQuery) return true;\n if (mode === \"contains\") {\n return searchableText.toLocaleLowerCase().includes(normalizedQuery);\n }\n const actualWords = new Set(tableSearchWords(searchableText));\n const expectedWords = tableSearchWords(normalizedQuery);\n return (\n expectedWords.length > 0 &&\n expectedWords.every((word) => actualWords.has(word))\n );\n}\n\nfunction compareValues(a: unknown, b: unknown): number {\n if (a == null && b == null) return 0;\n if (a == null) return -1;\n if (b == null) return 1;\n if (typeof a === \"number\" && typeof b === \"number\") return a - b;\n if (typeof a === \"boolean\" && typeof b === \"boolean\") {\n return Number(a) - Number(b);\n }\n return stringifyCellValue(a).localeCompare(stringifyCellValue(b), undefined, {\n numeric: true,\n sensitivity: \"base\",\n });\n}\n\nexport function passesColumnFilter(\n value: unknown,\n filter: ColumnFilterValue,\n): boolean {\n const matches = (() => {\n switch (filter.kind) {\n case \"text\": {\n const s = stringifyCellValue(value);\n if (filter.mode === \"null\") return value == null;\n if (filter.mode === \"not_null\") return value != null;\n if (filter.mode === \"empty\") return s.trim() === \"\";\n if (filter.mode === \"not_empty\") return s.trim() !== \"\";\n const q = filter.value.trim().toLowerCase();\n if (!q) return true;\n return s.toLowerCase().includes(q);\n }\n case \"select\": {\n const selected =\n filter.values ??\n (filter.value && filter.value !== \"__all__\" ? [filter.value] : []);\n if (selected.length === 0) return true;\n const s = stringifyCellValue(value);\n // OR semantics across the set; sentinels compose (\"A or (empty)\").\n return selected.some((v) =>\n v === SELECT_EMPTY_VALUE\n ? s === \"\"\n : v === SELECT_NOT_EMPTY_VALUE\n ? s !== \"\"\n : s === v,\n );\n }\n case \"boolean\": {\n return Boolean(value) === filter.value;\n }\n case \"number\": {\n const n =\n typeof value === \"number\"\n ? value\n : Number(stringifyCellValue(value).replace(/[^0-9.-]/g, \"\"));\n if (Number.isNaN(n)) return false;\n if (filter.min !== undefined && n < filter.min) return false;\n if (filter.max !== undefined && n > filter.max) return false;\n return true;\n }\n }\n })();\n return filter.negated ? !matches : matches;\n}\n\nexport function filterAndSortRows<T>(\n rows: T[],\n columns: MatrxColumnDef<T>[],\n columnFilters: ColumnFiltersState,\n sort: SortState | null,\n globalSearch: string,\n anyOf?: { columnIds: string[]; query: string },\n layeredFilters?: readonly LayeredFilterRule[],\n globalSearchMode: TableSearchMatchMode = \"contains\",\n searchText?: (row: T) => string,\n): T[] {\n const colById = new Map(columns.map((c) => [columnId(c), c]));\n const q = globalSearch.trim().toLowerCase();\n const anyQ = anyOf?.query.trim().toLowerCase() ?? \"\";\n const activeLayeredFilters = completeLayeredFilterRules(layeredFilters);\n\n let result = rows.filter((row) => {\n for (const [id, filter] of Object.entries(columnFilters)) {\n if (!filter) continue;\n const col = colById.get(id);\n if (!col || col.filter === false) continue;\n if (!passesColumnFilter(getFilterValue(row, col), filter)) return false;\n }\n\n for (const rule of activeLayeredFilters) {\n const col = colById.get(rule.field);\n if (!col || !layeredFilterMatchesValue(getFilterValue(row, col), rule)) {\n return false;\n }\n }\n\n if (anyQ && anyOf) {\n const hit = anyOf.columnIds.some((id) => {\n const col = colById.get(id);\n if (!col) return false;\n return stringifyCellValue(getFilterValue(row, col))\n .toLowerCase()\n .includes(anyQ);\n });\n if (!hit) return false;\n }\n\n if (!q) return true;\n const searchableValues = columns\n .filter(\n (col) =>\n col.filter !== false || Boolean(col.accessorKey || col.accessorFn),\n )\n .map((col) => stringifyCellValue(getFilterValue(row, col)));\n const additionalSearchText = searchText?.(row);\n if (additionalSearchText) searchableValues.push(additionalSearchText);\n if (globalSearchMode === \"contains\") {\n return searchableValues.some((value) =>\n matchesTableSearch(value, q, \"contains\"),\n );\n }\n return matchesTableSearch(searchableValues.join(\" \"), q, \"whole_words\");\n });\n\n if (sort) {\n const col = colById.get(sort.id);\n if (col && col.sortable !== false) {\n const dir = sort.direction === \"asc\" ? 1 : -1;\n result = [...result].sort((a, b) => {\n const va = getSortValue(a, col);\n const vb = getSortValue(b, col);\n // Empty cells (null / \"\") sort LAST in both directions — sorting a\n // column should surface its values, never a wall of blanks.\n const ea = va == null || stringifyCellValue(va) === \"\";\n const eb = vb == null || stringifyCellValue(vb) === \"\";\n if (ea !== eb) return ea ? 1 : -1;\n return compareValues(va, vb) * dir;\n });\n }\n }\n\n return result;\n}\n\nexport function countActiveColumnFilters(filters: ColumnFiltersState): number {\n let n = 0;\n for (const f of Object.values(filters)) {\n if (!f) continue;\n switch (f.kind) {\n case \"text\":\n if (f.value.trim() || (f.mode && f.mode !== \"contains\")) n += 1;\n break;\n case \"select\":\n if (f.values ? f.values.length > 0 : f.value && f.value !== \"__all__\")\n n += 1;\n break;\n case \"boolean\":\n n += 1;\n break;\n case \"number\":\n if (f.min !== undefined || f.max !== undefined) n += 1;\n break;\n }\n }\n return n;\n}\n\nexport function isColumnFilterActive(\n filter: ColumnFilterValue | undefined,\n): boolean {\n if (!filter) return false;\n switch (filter.kind) {\n case \"text\":\n return (\n filter.value.trim().length > 0 ||\n (filter.mode !== undefined && filter.mode !== \"contains\")\n );\n case \"select\":\n return filter.values\n ? filter.values.length > 0\n : Boolean(filter.value) && filter.value !== \"__all__\";\n case \"boolean\":\n return true;\n case \"number\":\n return filter.min !== undefined || filter.max !== undefined;\n }\n}\n\n/** Apply draft edits onto a row for display (shallow field merge). */\nexport function applyRowEdits<T>(\n row: T,\n edits: Record<string, unknown> | undefined,\n): T {\n if (!edits || Object.keys(edits).length === 0) return row;\n return { ...row, ...edits };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACkBO,IAAM,qBAAqB;AAC3B,IAAM,yBAAyB;AAQ/B,SAAS,aAAgB,KAAQ,KAAiC;AACvE,MAAI,IAAI,WAAY,QAAO,IAAI,WAAW,GAAG;AAC7C,MAAI,IAAI,aAAa;AACnB,WAAQ,IAAgC,IAAI,WAAW;AAAA,EACzD;AACA,SAAO;AACT;AAUO,SAAS,mBAAmB,OAAwB;AACzD,MAAI,SAAS,KAAM,QAAO;AAC1B,MAAI,OAAO,UAAU,SAAU,QAAO;AACtC,MAAI,OAAO,UAAU,YAAY,OAAO,UAAU,WAAW;AAC3D,WAAO,OAAO,KAAK;AAAA,EACrB;AACA,MAAI,iBAAiB,KAAM,QAAO,MAAM,YAAY;AACpD,MAAI;AACF,WAAO,KAAK,UAAU,KAAK;AAAA,EAC7B,QAAQ;AACN,WAAO,OAAO,KAAK;AAAA,EACrB;AACF;;;AD9CA,IAAM,yBAAyB;AAQxB,SAAS,kBACd,KACA,YAC2B;AAC3B,MAAI,IAAI,WAAW,MAAO,QAAO;AACjC,MAAI,IAAI,UAAU,IAAI,WAAW,OAAQ,QAAO,IAAI;AAEpD,QAAM,SAAS,WACZ,MAAM,GAAG,GAAG,EACZ,IAAI,CAAC,QAAQ,aAAa,KAAK,GAAG,CAAC,EACnC,OAAO,CAAC,MAAM,KAAK,IAAI;AAE1B,MAAI,OAAO,WAAW,EAAG,QAAO;AAEhC,MAAI,OAAO,MAAM,CAAC,MAAM,OAAO,MAAM,SAAS,EAAG,QAAO;AACxD,MAAI,OAAO,MAAM,CAAC,MAAM,OAAO,MAAM,YAAY,OAAO,SAAS,CAAC,CAAC,GAAG;AACpE,WAAO;AAAA,EACT;AAEA,QAAM,UAAU,OAAO,IAAI,kBAAkB;AAC7C,QAAM,SAAS,IAAI,IAAI,OAAO;AAC9B,MACE,OAAO,QAAQ,KACf,OAAO,QAAQ,0BACf,OAAO,OAAO,QAAQ,SAAS,KAC/B;AACA,WAAO;AAAA,EACT;AAEA,SAAO;AACT;AAEO,SAAS,qBACd,KACA,MACyC;AACzC,QAAM,OAAO,oBAAI,IAAY;AAC7B,QAAM,UAAmD,CAAC;AAC1D,MAAI,WAAW;AACf,aAAW,OAAO,MAAM;AACtB,UAAM,MAAM,aAAa,KAAK,GAAG;AACjC,UAAM,QAAQ,OAAO,OAAO,KAAK,mBAAmB,GAAG;AACvD,QAAI,CAAC,OAAO;AACV,iBAAW;AACX;AAAA,IACF;AACA,QAAI,IAAI,eAAe,UAAU,KAAK,IAAI,KAAK,EAAG;AAClD,SAAK,IAAI,KAAK;AACd,YAAQ,KAAK,EAAE,OAAO,OAAO,MAAM,CAAC;AAAA,EACtC;AACA,QAAM,OAAO,IAAI,eAAe,SAC5B,CAAC,GAAG,IAAI,aAAa,IACrB,QAAQ,KAAK,CAAC,GAAG,MAAM,EAAE,MAAM,cAAc,EAAE,KAAK,CAAC;AAGzD,MAAI,UAAU;AACZ,SAAK;AAAA,MACH,EAAE,OAAO,oBAAoB,OAAO,UAAU;AAAA,MAC9C,EAAE,OAAO,wBAAwB,OAAO,cAAc;AAAA,IACxD;AAAA,EACF;AACA,SAAO;AACT;AAEO,SAAS,gBACd,MACQ;AACR,UAAQ,MAAM;AAAA,IACZ,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,EACX;AACF;","names":[]}
@@ -0,0 +1,17 @@
1
+ import { M as MatrxColumnDef, e as ColumnFilterKind } from '../types-XXbVB1sh.cjs';
2
+ import 'react';
3
+ import './copy-types.cjs';
4
+
5
+ type ResolvedFilterKind = "text" | "select" | "boolean" | "number";
6
+ /**
7
+ * Infer a filter UI from sample row values. Prefer select for low-cardinality
8
+ * string/enum columns, boolean for booleans, number for numeric, else text.
9
+ */
10
+ declare function resolveFilterKind<T>(col: MatrxColumnDef<T>, sampleRows: T[]): ResolvedFilterKind | null;
11
+ declare function collectSelectOptions<T>(col: MatrxColumnDef<T>, rows: T[]): Array<{
12
+ value: string;
13
+ label: string;
14
+ }>;
15
+ declare function filterKindLabel(kind: ColumnFilterKind | ResolvedFilterKind): string;
16
+
17
+ export { type ResolvedFilterKind, collectSelectOptions, filterKindLabel, resolveFilterKind };
@@ -0,0 +1,17 @@
1
+ import { M as MatrxColumnDef, e as ColumnFilterKind } from '../types-ClaxTaXq.js';
2
+ import 'react';
3
+ import './copy-types.js';
4
+
5
+ type ResolvedFilterKind = "text" | "select" | "boolean" | "number";
6
+ /**
7
+ * Infer a filter UI from sample row values. Prefer select for low-cardinality
8
+ * string/enum columns, boolean for booleans, number for numeric, else text.
9
+ */
10
+ declare function resolveFilterKind<T>(col: MatrxColumnDef<T>, sampleRows: T[]): ResolvedFilterKind | null;
11
+ declare function collectSelectOptions<T>(col: MatrxColumnDef<T>, rows: T[]): Array<{
12
+ value: string;
13
+ label: string;
14
+ }>;
15
+ declare function filterKindLabel(kind: ColumnFilterKind | ResolvedFilterKind): string;
16
+
17
+ export { type ResolvedFilterKind, collectSelectOptions, filterKindLabel, resolveFilterKind };
@@ -0,0 +1,89 @@
1
+
2
+ // src/data-table/filter-engine.ts
3
+ var SELECT_EMPTY_VALUE = "__empty__";
4
+ var SELECT_NOT_EMPTY_VALUE = "__not_empty__";
5
+ function getCellValue(row, col) {
6
+ if (col.accessorFn) return col.accessorFn(row);
7
+ if (col.accessorKey) {
8
+ return row[col.accessorKey];
9
+ }
10
+ return void 0;
11
+ }
12
+ function stringifyCellValue(value) {
13
+ if (value == null) return "";
14
+ if (typeof value === "string") return value;
15
+ if (typeof value === "number" || typeof value === "boolean") {
16
+ return String(value);
17
+ }
18
+ if (value instanceof Date) return value.toISOString();
19
+ try {
20
+ return JSON.stringify(value);
21
+ } catch {
22
+ return String(value);
23
+ }
24
+ }
25
+
26
+ // src/data-table/infer-filter.ts
27
+ var SELECT_CARDINALITY_CAP = 24;
28
+ function resolveFilterKind(col, sampleRows) {
29
+ if (col.filter === false) return null;
30
+ if (col.filter && col.filter !== "auto") return col.filter;
31
+ const values = sampleRows.slice(0, 200).map((row) => getCellValue(row, col)).filter((v) => v != null);
32
+ if (values.length === 0) return "text";
33
+ if (values.every((v) => typeof v === "boolean")) return "boolean";
34
+ if (values.every((v) => typeof v === "number" && Number.isFinite(v))) {
35
+ return "number";
36
+ }
37
+ const strings = values.map(stringifyCellValue);
38
+ const unique = new Set(strings);
39
+ if (unique.size >= 1 && unique.size <= SELECT_CARDINALITY_CAP && unique.size < strings.length * 0.6) {
40
+ return "select";
41
+ }
42
+ return "text";
43
+ }
44
+ function collectSelectOptions(col, rows) {
45
+ const seen = /* @__PURE__ */ new Set();
46
+ const options = [];
47
+ let hasEmpty = false;
48
+ for (const row of rows) {
49
+ const raw = getCellValue(row, col);
50
+ const value = raw == null ? "" : stringifyCellValue(raw);
51
+ if (!value) {
52
+ hasEmpty = true;
53
+ continue;
54
+ }
55
+ if (col.filterOptions?.length || seen.has(value)) continue;
56
+ seen.add(value);
57
+ options.push({ value, label: value });
58
+ }
59
+ const base = col.filterOptions?.length ? [...col.filterOptions] : options.sort((a, b) => a.label.localeCompare(b.label));
60
+ if (hasEmpty) {
61
+ base.unshift(
62
+ { value: SELECT_EMPTY_VALUE, label: "(empty)" },
63
+ { value: SELECT_NOT_EMPTY_VALUE, label: "(not empty)" }
64
+ );
65
+ }
66
+ return base;
67
+ }
68
+ function filterKindLabel(kind) {
69
+ switch (kind) {
70
+ case "text":
71
+ return "Text";
72
+ case "select":
73
+ return "Select";
74
+ case "boolean":
75
+ return "Boolean";
76
+ case "number":
77
+ return "Number";
78
+ case "auto":
79
+ return "Auto";
80
+ case false:
81
+ return "Off";
82
+ }
83
+ }
84
+ export {
85
+ collectSelectOptions,
86
+ filterKindLabel,
87
+ resolveFilterKind
88
+ };
89
+ //# sourceMappingURL=infer-filter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/data-table/filter-engine.ts","../../src/data-table/infer-filter.ts"],"sourcesContent":["import type {\n ColumnFilterValue,\n ColumnFiltersState,\n MatrxColumnDef,\n SortState,\n TableSearchMatchMode,\n} from \"./types\";\nimport {\n completeLayeredFilterRules,\n layeredFilterMatchesValue,\n type LayeredFilterRule,\n} from \"./layered-filters\";\n\n/**\n * Sentinel select-filter values for \"no value\" / \"any value\" — every select\n * filter offers them automatically when the column has empty cells, so any\n * table can isolate rows missing a value (or having one) without extra config.\n */\nexport const SELECT_EMPTY_VALUE = \"__empty__\";\nexport const SELECT_NOT_EMPTY_VALUE = \"__not_empty__\";\n\nexport function columnId<T>(col: MatrxColumnDef<T>): string {\n if (col.id) return col.id;\n if (col.accessorKey) return String(col.accessorKey);\n throw new Error(\"MatrxDataTable column requires `id` or `accessorKey`\");\n}\n\nexport function getCellValue<T>(row: T, col: MatrxColumnDef<T>): unknown {\n if (col.accessorFn) return col.accessorFn(row);\n if (col.accessorKey) {\n return (row as Record<string, unknown>)[col.accessorKey];\n }\n return undefined;\n}\n\nexport function getFilterValue<T>(row: T, col: MatrxColumnDef<T>): unknown {\n return col.filterValue ? col.filterValue(row) : getCellValue(row, col);\n}\n\nexport function getSortValue<T>(row: T, col: MatrxColumnDef<T>): unknown {\n return col.sortValue ? col.sortValue(row) : getCellValue(row, col);\n}\n\nexport function stringifyCellValue(value: unknown): string {\n if (value == null) return \"\";\n if (typeof value === \"string\") return value;\n if (typeof value === \"number\" || typeof value === \"boolean\") {\n return String(value);\n }\n if (value instanceof Date) return value.toISOString();\n try {\n return JSON.stringify(value);\n } catch {\n return String(value);\n }\n}\n\n/** Unicode-aware tokenization shared by whole-word table search. */\nexport function tableSearchWords(value: string): string[] {\n return value\n .toLocaleLowerCase()\n .split(/[^\\p{L}\\p{N}]+/u)\n .filter(Boolean);\n}\n\n/**\n * Match the primary table search against one row's searchable text. In\n * whole-word mode every entered word must exist as a complete token, so\n * `are` matches `we are ready` but never `hardware`.\n */\nexport function matchesTableSearch(\n searchableText: string,\n query: string,\n mode: TableSearchMatchMode = \"contains\",\n): boolean {\n const normalizedQuery = query.trim().toLocaleLowerCase();\n if (!normalizedQuery) return true;\n if (mode === \"contains\") {\n return searchableText.toLocaleLowerCase().includes(normalizedQuery);\n }\n const actualWords = new Set(tableSearchWords(searchableText));\n const expectedWords = tableSearchWords(normalizedQuery);\n return (\n expectedWords.length > 0 &&\n expectedWords.every((word) => actualWords.has(word))\n );\n}\n\nfunction compareValues(a: unknown, b: unknown): number {\n if (a == null && b == null) return 0;\n if (a == null) return -1;\n if (b == null) return 1;\n if (typeof a === \"number\" && typeof b === \"number\") return a - b;\n if (typeof a === \"boolean\" && typeof b === \"boolean\") {\n return Number(a) - Number(b);\n }\n return stringifyCellValue(a).localeCompare(stringifyCellValue(b), undefined, {\n numeric: true,\n sensitivity: \"base\",\n });\n}\n\nexport function passesColumnFilter(\n value: unknown,\n filter: ColumnFilterValue,\n): boolean {\n const matches = (() => {\n switch (filter.kind) {\n case \"text\": {\n const s = stringifyCellValue(value);\n if (filter.mode === \"null\") return value == null;\n if (filter.mode === \"not_null\") return value != null;\n if (filter.mode === \"empty\") return s.trim() === \"\";\n if (filter.mode === \"not_empty\") return s.trim() !== \"\";\n const q = filter.value.trim().toLowerCase();\n if (!q) return true;\n return s.toLowerCase().includes(q);\n }\n case \"select\": {\n const selected =\n filter.values ??\n (filter.value && filter.value !== \"__all__\" ? [filter.value] : []);\n if (selected.length === 0) return true;\n const s = stringifyCellValue(value);\n // OR semantics across the set; sentinels compose (\"A or (empty)\").\n return selected.some((v) =>\n v === SELECT_EMPTY_VALUE\n ? s === \"\"\n : v === SELECT_NOT_EMPTY_VALUE\n ? s !== \"\"\n : s === v,\n );\n }\n case \"boolean\": {\n return Boolean(value) === filter.value;\n }\n case \"number\": {\n const n =\n typeof value === \"number\"\n ? value\n : Number(stringifyCellValue(value).replace(/[^0-9.-]/g, \"\"));\n if (Number.isNaN(n)) return false;\n if (filter.min !== undefined && n < filter.min) return false;\n if (filter.max !== undefined && n > filter.max) return false;\n return true;\n }\n }\n })();\n return filter.negated ? !matches : matches;\n}\n\nexport function filterAndSortRows<T>(\n rows: T[],\n columns: MatrxColumnDef<T>[],\n columnFilters: ColumnFiltersState,\n sort: SortState | null,\n globalSearch: string,\n anyOf?: { columnIds: string[]; query: string },\n layeredFilters?: readonly LayeredFilterRule[],\n globalSearchMode: TableSearchMatchMode = \"contains\",\n searchText?: (row: T) => string,\n): T[] {\n const colById = new Map(columns.map((c) => [columnId(c), c]));\n const q = globalSearch.trim().toLowerCase();\n const anyQ = anyOf?.query.trim().toLowerCase() ?? \"\";\n const activeLayeredFilters = completeLayeredFilterRules(layeredFilters);\n\n let result = rows.filter((row) => {\n for (const [id, filter] of Object.entries(columnFilters)) {\n if (!filter) continue;\n const col = colById.get(id);\n if (!col || col.filter === false) continue;\n if (!passesColumnFilter(getFilterValue(row, col), filter)) return false;\n }\n\n for (const rule of activeLayeredFilters) {\n const col = colById.get(rule.field);\n if (!col || !layeredFilterMatchesValue(getFilterValue(row, col), rule)) {\n return false;\n }\n }\n\n if (anyQ && anyOf) {\n const hit = anyOf.columnIds.some((id) => {\n const col = colById.get(id);\n if (!col) return false;\n return stringifyCellValue(getFilterValue(row, col))\n .toLowerCase()\n .includes(anyQ);\n });\n if (!hit) return false;\n }\n\n if (!q) return true;\n const searchableValues = columns\n .filter(\n (col) =>\n col.filter !== false || Boolean(col.accessorKey || col.accessorFn),\n )\n .map((col) => stringifyCellValue(getFilterValue(row, col)));\n const additionalSearchText = searchText?.(row);\n if (additionalSearchText) searchableValues.push(additionalSearchText);\n if (globalSearchMode === \"contains\") {\n return searchableValues.some((value) =>\n matchesTableSearch(value, q, \"contains\"),\n );\n }\n return matchesTableSearch(searchableValues.join(\" \"), q, \"whole_words\");\n });\n\n if (sort) {\n const col = colById.get(sort.id);\n if (col && col.sortable !== false) {\n const dir = sort.direction === \"asc\" ? 1 : -1;\n result = [...result].sort((a, b) => {\n const va = getSortValue(a, col);\n const vb = getSortValue(b, col);\n // Empty cells (null / \"\") sort LAST in both directions — sorting a\n // column should surface its values, never a wall of blanks.\n const ea = va == null || stringifyCellValue(va) === \"\";\n const eb = vb == null || stringifyCellValue(vb) === \"\";\n if (ea !== eb) return ea ? 1 : -1;\n return compareValues(va, vb) * dir;\n });\n }\n }\n\n return result;\n}\n\nexport function countActiveColumnFilters(filters: ColumnFiltersState): number {\n let n = 0;\n for (const f of Object.values(filters)) {\n if (!f) continue;\n switch (f.kind) {\n case \"text\":\n if (f.value.trim() || (f.mode && f.mode !== \"contains\")) n += 1;\n break;\n case \"select\":\n if (f.values ? f.values.length > 0 : f.value && f.value !== \"__all__\")\n n += 1;\n break;\n case \"boolean\":\n n += 1;\n break;\n case \"number\":\n if (f.min !== undefined || f.max !== undefined) n += 1;\n break;\n }\n }\n return n;\n}\n\nexport function isColumnFilterActive(\n filter: ColumnFilterValue | undefined,\n): boolean {\n if (!filter) return false;\n switch (filter.kind) {\n case \"text\":\n return (\n filter.value.trim().length > 0 ||\n (filter.mode !== undefined && filter.mode !== \"contains\")\n );\n case \"select\":\n return filter.values\n ? filter.values.length > 0\n : Boolean(filter.value) && filter.value !== \"__all__\";\n case \"boolean\":\n return true;\n case \"number\":\n return filter.min !== undefined || filter.max !== undefined;\n }\n}\n\n/** Apply draft edits onto a row for display (shallow field merge). */\nexport function applyRowEdits<T>(\n row: T,\n edits: Record<string, unknown> | undefined,\n): T {\n if (!edits || Object.keys(edits).length === 0) return row;\n return { ...row, ...edits };\n}\n","import type { ColumnFilterKind, MatrxColumnDef } from \"./types\";\nimport {\n SELECT_EMPTY_VALUE,\n SELECT_NOT_EMPTY_VALUE,\n getCellValue,\n stringifyCellValue,\n} from \"./filter-engine\";\n\n/** Auto-inference switches to text above this many distinct values… */\nconst SELECT_CARDINALITY_CAP = 24;\n\nexport type ResolvedFilterKind = \"text\" | \"select\" | \"boolean\" | \"number\";\n\n/**\n * Infer a filter UI from sample row values. Prefer select for low-cardinality\n * string/enum columns, boolean for booleans, number for numeric, else text.\n */\nexport function resolveFilterKind<T>(\n col: MatrxColumnDef<T>,\n sampleRows: T[],\n): ResolvedFilterKind | null {\n if (col.filter === false) return null;\n if (col.filter && col.filter !== \"auto\") return col.filter;\n\n const values = sampleRows\n .slice(0, 200)\n .map((row) => getCellValue(row, col))\n .filter((v) => v != null);\n\n if (values.length === 0) return \"text\";\n\n if (values.every((v) => typeof v === \"boolean\")) return \"boolean\";\n if (values.every((v) => typeof v === \"number\" && Number.isFinite(v))) {\n return \"number\";\n }\n\n const strings = values.map(stringifyCellValue);\n const unique = new Set(strings);\n if (\n unique.size >= 1 &&\n unique.size <= SELECT_CARDINALITY_CAP &&\n unique.size < strings.length * 0.6\n ) {\n return \"select\";\n }\n\n return \"text\";\n}\n\nexport function collectSelectOptions<T>(\n col: MatrxColumnDef<T>,\n rows: T[],\n): Array<{ value: string; label: string }> {\n const seen = new Set<string>();\n const options: Array<{ value: string; label: string }> = [];\n let hasEmpty = false;\n for (const row of rows) {\n const raw = getCellValue(row, col);\n const value = raw == null ? \"\" : stringifyCellValue(raw);\n if (!value) {\n hasEmpty = true;\n continue;\n }\n if (col.filterOptions?.length || seen.has(value)) continue;\n seen.add(value);\n options.push({ value, label: value });\n }\n const base = col.filterOptions?.length\n ? [...col.filterOptions]\n : options.sort((a, b) => a.label.localeCompare(b.label));\n // Emptiness sentinels: whenever the column has blank cells, every select\n // filter can isolate them — or exclude them — with zero per-table config.\n if (hasEmpty) {\n base.unshift(\n { value: SELECT_EMPTY_VALUE, label: \"(empty)\" },\n { value: SELECT_NOT_EMPTY_VALUE, label: \"(not empty)\" },\n );\n }\n return base;\n}\n\nexport function filterKindLabel(\n kind: ColumnFilterKind | ResolvedFilterKind,\n): string {\n switch (kind) {\n case \"text\":\n return \"Text\";\n case \"select\":\n return \"Select\";\n case \"boolean\":\n return \"Boolean\";\n case \"number\":\n return \"Number\";\n case \"auto\":\n return \"Auto\";\n case false:\n return \"Off\";\n }\n}\n"],"mappings":";;AAkBO,IAAM,qBAAqB;AAC3B,IAAM,yBAAyB;AAQ/B,SAAS,aAAgB,KAAQ,KAAiC;AACvE,MAAI,IAAI,WAAY,QAAO,IAAI,WAAW,GAAG;AAC7C,MAAI,IAAI,aAAa;AACnB,WAAQ,IAAgC,IAAI,WAAW;AAAA,EACzD;AACA,SAAO;AACT;AAUO,SAAS,mBAAmB,OAAwB;AACzD,MAAI,SAAS,KAAM,QAAO;AAC1B,MAAI,OAAO,UAAU,SAAU,QAAO;AACtC,MAAI,OAAO,UAAU,YAAY,OAAO,UAAU,WAAW;AAC3D,WAAO,OAAO,KAAK;AAAA,EACrB;AACA,MAAI,iBAAiB,KAAM,QAAO,MAAM,YAAY;AACpD,MAAI;AACF,WAAO,KAAK,UAAU,KAAK;AAAA,EAC7B,QAAQ;AACN,WAAO,OAAO,KAAK;AAAA,EACrB;AACF;;;AC9CA,IAAM,yBAAyB;AAQxB,SAAS,kBACd,KACA,YAC2B;AAC3B,MAAI,IAAI,WAAW,MAAO,QAAO;AACjC,MAAI,IAAI,UAAU,IAAI,WAAW,OAAQ,QAAO,IAAI;AAEpD,QAAM,SAAS,WACZ,MAAM,GAAG,GAAG,EACZ,IAAI,CAAC,QAAQ,aAAa,KAAK,GAAG,CAAC,EACnC,OAAO,CAAC,MAAM,KAAK,IAAI;AAE1B,MAAI,OAAO,WAAW,EAAG,QAAO;AAEhC,MAAI,OAAO,MAAM,CAAC,MAAM,OAAO,MAAM,SAAS,EAAG,QAAO;AACxD,MAAI,OAAO,MAAM,CAAC,MAAM,OAAO,MAAM,YAAY,OAAO,SAAS,CAAC,CAAC,GAAG;AACpE,WAAO;AAAA,EACT;AAEA,QAAM,UAAU,OAAO,IAAI,kBAAkB;AAC7C,QAAM,SAAS,IAAI,IAAI,OAAO;AAC9B,MACE,OAAO,QAAQ,KACf,OAAO,QAAQ,0BACf,OAAO,OAAO,QAAQ,SAAS,KAC/B;AACA,WAAO;AAAA,EACT;AAEA,SAAO;AACT;AAEO,SAAS,qBACd,KACA,MACyC;AACzC,QAAM,OAAO,oBAAI,IAAY;AAC7B,QAAM,UAAmD,CAAC;AAC1D,MAAI,WAAW;AACf,aAAW,OAAO,MAAM;AACtB,UAAM,MAAM,aAAa,KAAK,GAAG;AACjC,UAAM,QAAQ,OAAO,OAAO,KAAK,mBAAmB,GAAG;AACvD,QAAI,CAAC,OAAO;AACV,iBAAW;AACX;AAAA,IACF;AACA,QAAI,IAAI,eAAe,UAAU,KAAK,IAAI,KAAK,EAAG;AAClD,SAAK,IAAI,KAAK;AACd,YAAQ,KAAK,EAAE,OAAO,OAAO,MAAM,CAAC;AAAA,EACtC;AACA,QAAM,OAAO,IAAI,eAAe,SAC5B,CAAC,GAAG,IAAI,aAAa,IACrB,QAAQ,KAAK,CAAC,GAAG,MAAM,EAAE,MAAM,cAAc,EAAE,KAAK,CAAC;AAGzD,MAAI,UAAU;AACZ,SAAK;AAAA,MACH,EAAE,OAAO,oBAAoB,OAAO,UAAU;AAAA,MAC9C,EAAE,OAAO,wBAAwB,OAAO,cAAc;AAAA,IACxD;AAAA,EACF;AACA,SAAO;AACT;AAEO,SAAS,gBACd,MACQ;AACR,UAAQ,MAAM;AAAA,IACZ,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,EACX;AACF;","names":[]}
@@ -0,0 +1,249 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/data-table/layered-filters.ts
21
+ var layered_filters_exports = {};
22
+ __export(layered_filters_exports, {
23
+ LAYERED_FILTER_OPERATORS: () => LAYERED_FILTER_OPERATORS,
24
+ LAYERED_FILTER_OPERATOR_LABELS: () => LAYERED_FILTER_OPERATOR_LABELS,
25
+ columnFiltersToLayeredRules: () => columnFiltersToLayeredRules,
26
+ completeLayeredFilterRules: () => completeLayeredFilterRules,
27
+ decodeLayeredFilterRules: () => decodeLayeredFilterRules,
28
+ encodeLayeredFilterRules: () => encodeLayeredFilterRules,
29
+ isCompleteLayeredFilterRule: () => isCompleteLayeredFilterRule,
30
+ isLayeredFilterOperator: () => isLayeredFilterOperator,
31
+ layeredFilterMatchesValue: () => layeredFilterMatchesValue,
32
+ layeredFilterNeedsValue: () => layeredFilterNeedsValue,
33
+ layeredFilterRuleSummary: () => layeredFilterRuleSummary,
34
+ operatorsForLayeredField: () => operatorsForLayeredField
35
+ });
36
+ module.exports = __toCommonJS(layered_filters_exports);
37
+ var import_zod = require("zod");
38
+ var LAYERED_FILTER_OPERATORS = [
39
+ "contains",
40
+ "not_contains",
41
+ "equals",
42
+ "not_equals",
43
+ "starts_with",
44
+ "ends_with",
45
+ "word",
46
+ "not_word",
47
+ "is_empty",
48
+ "is_not_empty",
49
+ "greater_than",
50
+ "greater_or_equal",
51
+ "less_than",
52
+ "less_or_equal",
53
+ "between"
54
+ ];
55
+ var layeredFilterRuleSchema = import_zod.z.object({
56
+ id: import_zod.z.string().min(1),
57
+ field: import_zod.z.string().min(1),
58
+ operator: import_zod.z.enum(LAYERED_FILTER_OPERATORS),
59
+ value: import_zod.z.string(),
60
+ valueTo: import_zod.z.string().optional()
61
+ });
62
+ var layeredFilterRulesSchema = import_zod.z.array(layeredFilterRuleSchema).max(20);
63
+ var TEXT_OPERATORS = [
64
+ "contains",
65
+ "not_contains",
66
+ "equals",
67
+ "not_equals",
68
+ "starts_with",
69
+ "ends_with",
70
+ "word",
71
+ "not_word",
72
+ "is_empty",
73
+ "is_not_empty"
74
+ ];
75
+ var NUMBER_OPERATORS = [
76
+ "equals",
77
+ "not_equals",
78
+ "greater_than",
79
+ "greater_or_equal",
80
+ "less_than",
81
+ "less_or_equal",
82
+ "between",
83
+ "is_empty",
84
+ "is_not_empty"
85
+ ];
86
+ var SELECT_OPERATORS = [
87
+ "equals",
88
+ "not_equals",
89
+ "is_empty",
90
+ "is_not_empty"
91
+ ];
92
+ var LAYERED_FILTER_OPERATOR_LABELS = {
93
+ contains: "contains",
94
+ not_contains: "doesn't contain",
95
+ equals: "is",
96
+ not_equals: "is not",
97
+ starts_with: "starts with",
98
+ ends_with: "ends with",
99
+ word: "has the word",
100
+ not_word: "doesn't have the word",
101
+ is_empty: "is empty",
102
+ is_not_empty: "is not empty",
103
+ greater_than: "is greater than",
104
+ greater_or_equal: "is at least",
105
+ less_than: "is less than",
106
+ less_or_equal: "is at most",
107
+ between: "is between"
108
+ };
109
+ function operatorsForLayeredField(field) {
110
+ if (field.operators) return field.operators;
111
+ if (field.kind === "number") return NUMBER_OPERATORS;
112
+ if (field.kind === "select") return SELECT_OPERATORS;
113
+ return TEXT_OPERATORS;
114
+ }
115
+ function layeredFilterNeedsValue(operator) {
116
+ return operator !== "is_empty" && operator !== "is_not_empty";
117
+ }
118
+ function isCompleteLayeredFilterRule(rule) {
119
+ if (!layeredFilterNeedsValue(rule.operator)) return true;
120
+ if (rule.value.trim() === "") return false;
121
+ return rule.operator !== "between" || Boolean(rule.valueTo?.trim());
122
+ }
123
+ function completeLayeredFilterRules(rules) {
124
+ return (rules ?? []).filter(isCompleteLayeredFilterRule);
125
+ }
126
+ function isLayeredFilterOperator(value) {
127
+ return LAYERED_FILTER_OPERATORS.some((operator) => operator === value);
128
+ }
129
+ function decodeLayeredFilterRules(raw) {
130
+ if (!raw) return [];
131
+ try {
132
+ const parsed = JSON.parse(raw);
133
+ const result = layeredFilterRulesSchema.safeParse(parsed);
134
+ return result.success ? result.data : [];
135
+ } catch {
136
+ return [];
137
+ }
138
+ }
139
+ function encodeLayeredFilterRules(rules) {
140
+ return rules?.length ? JSON.stringify(rules) : null;
141
+ }
142
+ function columnFiltersToLayeredRules(filters, fieldIds) {
143
+ const rules = [];
144
+ for (const field of fieldIds) {
145
+ const filter = filters[field];
146
+ if (!filter) continue;
147
+ if (filter.kind === "text") {
148
+ rules.push({
149
+ id: `column-${field}`,
150
+ field,
151
+ operator: filter.mode === "empty" ? "is_empty" : filter.mode === "not_empty" ? "is_not_empty" : "contains",
152
+ value: filter.value
153
+ });
154
+ continue;
155
+ }
156
+ if (filter.kind === "number") {
157
+ if (filter.min !== void 0 && filter.max !== void 0) {
158
+ rules.push({
159
+ id: `column-${field}`,
160
+ field,
161
+ operator: "between",
162
+ value: String(filter.min),
163
+ valueTo: String(filter.max)
164
+ });
165
+ } else if (filter.min !== void 0) {
166
+ rules.push({
167
+ id: `column-${field}`,
168
+ field,
169
+ operator: "greater_or_equal",
170
+ value: String(filter.min)
171
+ });
172
+ } else if (filter.max !== void 0) {
173
+ rules.push({
174
+ id: `column-${field}`,
175
+ field,
176
+ operator: "less_or_equal",
177
+ value: String(filter.max)
178
+ });
179
+ }
180
+ continue;
181
+ }
182
+ if (filter.kind === "boolean") {
183
+ rules.push({
184
+ id: `column-${field}`,
185
+ field,
186
+ operator: "equals",
187
+ value: String(filter.value)
188
+ });
189
+ }
190
+ }
191
+ return rules;
192
+ }
193
+ function normalizedText(value) {
194
+ if (value === null || value === void 0) return "";
195
+ return String(value).trim().toLocaleLowerCase();
196
+ }
197
+ function words(value) {
198
+ return value.split(/[^\p{L}\p{N}]+/u).filter(Boolean);
199
+ }
200
+ function layeredFilterMatchesValue(value, rule) {
201
+ const actual = normalizedText(value);
202
+ const expected = rule.value.trim().toLocaleLowerCase();
203
+ const expectedTo = rule.valueTo?.trim().toLocaleLowerCase() ?? "";
204
+ switch (rule.operator) {
205
+ case "contains":
206
+ return actual.includes(expected);
207
+ case "not_contains":
208
+ return !actual.includes(expected);
209
+ case "equals":
210
+ return actual === expected;
211
+ case "not_equals":
212
+ return actual !== expected;
213
+ case "starts_with":
214
+ return actual.startsWith(expected);
215
+ case "ends_with":
216
+ return actual.endsWith(expected);
217
+ case "word":
218
+ return words(actual).includes(expected);
219
+ case "not_word":
220
+ return !words(actual).includes(expected);
221
+ case "is_empty":
222
+ return actual === "";
223
+ case "is_not_empty":
224
+ return actual !== "";
225
+ case "greater_than":
226
+ return Number(actual) > Number(expected);
227
+ case "greater_or_equal":
228
+ return Number(actual) >= Number(expected);
229
+ case "less_than":
230
+ return Number(actual) < Number(expected);
231
+ case "less_or_equal":
232
+ return Number(actual) <= Number(expected);
233
+ case "between": {
234
+ const numeric = Number(actual);
235
+ return numeric >= Number(expected) && numeric <= Number(expectedTo);
236
+ }
237
+ }
238
+ }
239
+ function layeredFilterRuleSummary(rule, fields) {
240
+ const field = fields.find((candidate) => candidate.id === rule.field);
241
+ const fieldLabel = field?.label ?? rule.field;
242
+ const operator = LAYERED_FILTER_OPERATOR_LABELS[rule.operator];
243
+ if (!layeredFilterNeedsValue(rule.operator))
244
+ return `${fieldLabel} ${operator}`;
245
+ const selectedLabel = field?.kind === "select" ? field.options.find((option) => option.value === rule.value)?.label : void 0;
246
+ const value = selectedLabel ?? rule.value;
247
+ return rule.operator === "between" ? `${fieldLabel} ${operator} ${value} and ${rule.valueTo ?? ""}` : `${fieldLabel} ${operator} ${value}`;
248
+ }
249
+ //# sourceMappingURL=layered-filters.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/data-table/layered-filters.ts"],"sourcesContent":["/**\n * Shared vocabulary for ordered, AND-combined table filters. The same rule\n * shape is URL-safe, can be evaluated by local tables, and can be handed to a\n * controlled table's server query without a surface inventing another query\n * builder.\n */\n\nimport { z } from \"zod\";\nimport type { ColumnFiltersState } from \"./types\";\n\nexport const LAYERED_FILTER_OPERATORS = [\n \"contains\",\n \"not_contains\",\n \"equals\",\n \"not_equals\",\n \"starts_with\",\n \"ends_with\",\n \"word\",\n \"not_word\",\n \"is_empty\",\n \"is_not_empty\",\n \"greater_than\",\n \"greater_or_equal\",\n \"less_than\",\n \"less_or_equal\",\n \"between\",\n] as const;\n\nexport type LayeredFilterOperator = (typeof LAYERED_FILTER_OPERATORS)[number];\n\nexport interface LayeredFilterRule {\n id: string;\n field: string;\n operator: LayeredFilterOperator;\n value: string;\n valueTo?: string | undefined;\n}\n\nexport type LayeredFilterField =\n | {\n id: string;\n label: string;\n kind: \"text\";\n operators?: LayeredFilterOperator[];\n }\n | {\n id: string;\n label: string;\n kind: \"number\";\n operators?: LayeredFilterOperator[];\n placeholder?: string;\n }\n | {\n id: string;\n label: string;\n kind: \"select\";\n operators?: LayeredFilterOperator[];\n options: Array<{ value: string; label: string }>;\n };\n\nconst layeredFilterRuleSchema = z.object({\n id: z.string().min(1),\n field: z.string().min(1),\n operator: z.enum(LAYERED_FILTER_OPERATORS),\n value: z.string(),\n valueTo: z.string().optional(),\n});\n\nconst layeredFilterRulesSchema = z.array(layeredFilterRuleSchema).max(20);\n\nconst TEXT_OPERATORS: LayeredFilterOperator[] = [\n \"contains\",\n \"not_contains\",\n \"equals\",\n \"not_equals\",\n \"starts_with\",\n \"ends_with\",\n \"word\",\n \"not_word\",\n \"is_empty\",\n \"is_not_empty\",\n];\n\nconst NUMBER_OPERATORS: LayeredFilterOperator[] = [\n \"equals\",\n \"not_equals\",\n \"greater_than\",\n \"greater_or_equal\",\n \"less_than\",\n \"less_or_equal\",\n \"between\",\n \"is_empty\",\n \"is_not_empty\",\n];\n\nconst SELECT_OPERATORS: LayeredFilterOperator[] = [\n \"equals\",\n \"not_equals\",\n \"is_empty\",\n \"is_not_empty\",\n];\n\nexport const LAYERED_FILTER_OPERATOR_LABELS: Record<\n LayeredFilterOperator,\n string\n> = {\n contains: \"contains\",\n not_contains: \"doesn't contain\",\n equals: \"is\",\n not_equals: \"is not\",\n starts_with: \"starts with\",\n ends_with: \"ends with\",\n word: \"has the word\",\n not_word: \"doesn't have the word\",\n is_empty: \"is empty\",\n is_not_empty: \"is not empty\",\n greater_than: \"is greater than\",\n greater_or_equal: \"is at least\",\n less_than: \"is less than\",\n less_or_equal: \"is at most\",\n between: \"is between\",\n};\n\nexport function operatorsForLayeredField(\n field: LayeredFilterField,\n): LayeredFilterOperator[] {\n if (field.operators) return field.operators;\n if (field.kind === \"number\") return NUMBER_OPERATORS;\n if (field.kind === \"select\") return SELECT_OPERATORS;\n return TEXT_OPERATORS;\n}\n\nexport function layeredFilterNeedsValue(\n operator: LayeredFilterOperator,\n): boolean {\n return operator !== \"is_empty\" && operator !== \"is_not_empty\";\n}\n\nexport function isCompleteLayeredFilterRule(rule: LayeredFilterRule): boolean {\n if (!layeredFilterNeedsValue(rule.operator)) return true;\n if (rule.value.trim() === \"\") return false;\n return rule.operator !== \"between\" || Boolean(rule.valueTo?.trim());\n}\n\nexport function completeLayeredFilterRules(\n rules: readonly LayeredFilterRule[] | undefined,\n): LayeredFilterRule[] {\n return (rules ?? []).filter(isCompleteLayeredFilterRule);\n}\n\nexport function isLayeredFilterOperator(\n value: string,\n): value is LayeredFilterOperator {\n return LAYERED_FILTER_OPERATORS.some((operator) => operator === value);\n}\n\n/** Decode URL-owned rules without trusting a hand-edited link. */\nexport function decodeLayeredFilterRules(\n raw: string | null,\n): LayeredFilterRule[] {\n if (!raw) return [];\n try {\n const parsed: unknown = JSON.parse(raw);\n const result = layeredFilterRulesSchema.safeParse(parsed);\n return result.success ? result.data : [];\n } catch {\n return [];\n }\n}\n\n/** Stable compact URL/wire form; rule order is preserved. */\nexport function encodeLayeredFilterRules(\n rules: readonly LayeredFilterRule[] | undefined,\n): string | null {\n return rules?.length ? JSON.stringify(rules) : null;\n}\n\n/**\n * Translate ordinary per-column controls into the same ordered rule language\n * used by the advanced builder. Controlled tables can send these rules to one\n * server evaluator instead of rebuilding numeric/text filter semantics.\n * Select filters are intentionally omitted because their multi-select control\n * is OR-combined and should stay in its native query parameter.\n */\nexport function columnFiltersToLayeredRules(\n filters: ColumnFiltersState,\n fieldIds: readonly string[],\n): LayeredFilterRule[] {\n const rules: LayeredFilterRule[] = [];\n for (const field of fieldIds) {\n const filter = filters[field];\n if (!filter) continue;\n\n if (filter.kind === \"text\") {\n rules.push({\n id: `column-${field}`,\n field,\n operator:\n filter.mode === \"empty\"\n ? \"is_empty\"\n : filter.mode === \"not_empty\"\n ? \"is_not_empty\"\n : \"contains\",\n value: filter.value,\n });\n continue;\n }\n\n if (filter.kind === \"number\") {\n if (filter.min !== undefined && filter.max !== undefined) {\n rules.push({\n id: `column-${field}`,\n field,\n operator: \"between\",\n value: String(filter.min),\n valueTo: String(filter.max),\n });\n } else if (filter.min !== undefined) {\n rules.push({\n id: `column-${field}`,\n field,\n operator: \"greater_or_equal\",\n value: String(filter.min),\n });\n } else if (filter.max !== undefined) {\n rules.push({\n id: `column-${field}`,\n field,\n operator: \"less_or_equal\",\n value: String(filter.max),\n });\n }\n continue;\n }\n\n if (filter.kind === \"boolean\") {\n rules.push({\n id: `column-${field}`,\n field,\n operator: \"equals\",\n value: String(filter.value),\n });\n }\n }\n return rules;\n}\n\nfunction normalizedText(value: unknown): string {\n if (value === null || value === undefined) return \"\";\n return String(value).trim().toLocaleLowerCase();\n}\n\nfunction words(value: string): string[] {\n return value.split(/[^\\p{L}\\p{N}]+/u).filter(Boolean);\n}\n\n/** Local-table evaluator. Controlled tables send the same rule shape server-side. */\nexport function layeredFilterMatchesValue(\n value: unknown,\n rule: LayeredFilterRule,\n): boolean {\n const actual = normalizedText(value);\n const expected = rule.value.trim().toLocaleLowerCase();\n const expectedTo = rule.valueTo?.trim().toLocaleLowerCase() ?? \"\";\n\n switch (rule.operator) {\n case \"contains\":\n return actual.includes(expected);\n case \"not_contains\":\n return !actual.includes(expected);\n case \"equals\":\n return actual === expected;\n case \"not_equals\":\n return actual !== expected;\n case \"starts_with\":\n return actual.startsWith(expected);\n case \"ends_with\":\n return actual.endsWith(expected);\n case \"word\":\n return words(actual).includes(expected);\n case \"not_word\":\n return !words(actual).includes(expected);\n case \"is_empty\":\n return actual === \"\";\n case \"is_not_empty\":\n return actual !== \"\";\n case \"greater_than\":\n return Number(actual) > Number(expected);\n case \"greater_or_equal\":\n return Number(actual) >= Number(expected);\n case \"less_than\":\n return Number(actual) < Number(expected);\n case \"less_or_equal\":\n return Number(actual) <= Number(expected);\n case \"between\": {\n const numeric = Number(actual);\n return numeric >= Number(expected) && numeric <= Number(expectedTo);\n }\n }\n}\n\nexport function layeredFilterRuleSummary(\n rule: LayeredFilterRule,\n fields: readonly LayeredFilterField[],\n): string {\n const field = fields.find((candidate) => candidate.id === rule.field);\n const fieldLabel = field?.label ?? rule.field;\n const operator = LAYERED_FILTER_OPERATOR_LABELS[rule.operator];\n if (!layeredFilterNeedsValue(rule.operator))\n return `${fieldLabel} ${operator}`;\n const selectedLabel =\n field?.kind === \"select\"\n ? field.options.find((option) => option.value === rule.value)?.label\n : undefined;\n const value = selectedLabel ?? rule.value;\n return rule.operator === \"between\"\n ? `${fieldLabel} ${operator} ${value} and ${rule.valueTo ?? \"\"}`\n : `${fieldLabel} ${operator} ${value}`;\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,3 @@
1
+ export { z as LAYERED_FILTER_OPERATORS, B as LAYERED_FILTER_OPERATOR_LABELS, D as LayeredFilterField, E as LayeredFilterOperator, L as LayeredFilterRule, F as columnFiltersToLayeredRules, G as completeLayeredFilterRules, H as decodeLayeredFilterRules, I as encodeLayeredFilterRules, J as isCompleteLayeredFilterRule, K as isLayeredFilterOperator, N as layeredFilterMatchesValue, O as layeredFilterNeedsValue, P as layeredFilterRuleSummary, Q as operatorsForLayeredField } from '../types-XXbVB1sh.cjs';
2
+ import 'react';
3
+ import './copy-types.cjs';
@@ -0,0 +1,3 @@
1
+ export { z as LAYERED_FILTER_OPERATORS, B as LAYERED_FILTER_OPERATOR_LABELS, D as LayeredFilterField, E as LayeredFilterOperator, L as LayeredFilterRule, F as columnFiltersToLayeredRules, G as completeLayeredFilterRules, H as decodeLayeredFilterRules, I as encodeLayeredFilterRules, J as isCompleteLayeredFilterRule, K as isLayeredFilterOperator, N as layeredFilterMatchesValue, O as layeredFilterNeedsValue, P as layeredFilterRuleSummary, Q as operatorsForLayeredField } from '../types-ClaxTaXq.js';
2
+ import 'react';
3
+ import './copy-types.js';