@ai-matrx/design-system 0.22.0 → 0.26.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (115) hide show
  1. package/CHANGELOG.md +227 -0
  2. package/dist/data-table/cell-undo.cjs +98 -0
  3. package/dist/data-table/cell-undo.cjs.map +1 -0
  4. package/dist/data-table/cell-undo.d.cts +85 -0
  5. package/dist/data-table/cell-undo.d.ts +85 -0
  6. package/dist/data-table/cell-undo.js +78 -0
  7. package/dist/data-table/cell-undo.js.map +1 -0
  8. package/dist/data-table/copy-helpers.d.cts +7 -2
  9. package/dist/data-table/copy-helpers.d.ts +7 -2
  10. package/dist/data-table/coverage.cjs +116 -0
  11. package/dist/data-table/coverage.cjs.map +1 -0
  12. package/dist/data-table/coverage.d.cts +65 -0
  13. package/dist/data-table/coverage.d.ts +65 -0
  14. package/dist/data-table/coverage.js +96 -0
  15. package/dist/data-table/coverage.js.map +1 -0
  16. package/dist/data-table/data-row-inspector.cjs.map +1 -1
  17. package/dist/data-table/data-row-inspector.js.map +1 -1
  18. package/dist/data-table/data-row-window.cjs.map +1 -1
  19. package/dist/data-table/data-row-window.js.map +1 -1
  20. package/dist/data-table/facets.cjs +118 -0
  21. package/dist/data-table/facets.cjs.map +1 -0
  22. package/dist/data-table/facets.d.cts +100 -0
  23. package/dist/data-table/facets.d.ts +100 -0
  24. package/dist/data-table/facets.js +98 -0
  25. package/dist/data-table/facets.js.map +1 -0
  26. package/dist/data-table/fill.cjs +299 -0
  27. package/dist/data-table/fill.cjs.map +1 -0
  28. package/dist/data-table/fill.d.cts +129 -0
  29. package/dist/data-table/fill.d.ts +129 -0
  30. package/dist/data-table/fill.js +277 -0
  31. package/dist/data-table/fill.js.map +1 -0
  32. package/dist/data-table/filter-engine.d.cts +7 -2
  33. package/dist/data-table/filter-engine.d.ts +7 -2
  34. package/dist/data-table/grid-clipboard.cjs +165 -0
  35. package/dist/data-table/grid-clipboard.cjs.map +1 -0
  36. package/dist/data-table/grid-clipboard.d.cts +85 -0
  37. package/dist/data-table/grid-clipboard.d.ts +85 -0
  38. package/dist/data-table/grid-clipboard.js +145 -0
  39. package/dist/data-table/grid-clipboard.js.map +1 -0
  40. package/dist/data-table/grid-selection.cjs +258 -0
  41. package/dist/data-table/grid-selection.cjs.map +1 -0
  42. package/dist/data-table/grid-selection.d.cts +166 -0
  43. package/dist/data-table/grid-selection.d.ts +166 -0
  44. package/dist/data-table/grid-selection.js +238 -0
  45. package/dist/data-table/grid-selection.js.map +1 -0
  46. package/dist/data-table/grouping.cjs +302 -0
  47. package/dist/data-table/grouping.cjs.map +1 -0
  48. package/dist/data-table/grouping.d.cts +119 -0
  49. package/dist/data-table/grouping.d.ts +119 -0
  50. package/dist/data-table/grouping.js +282 -0
  51. package/dist/data-table/grouping.js.map +1 -0
  52. package/dist/data-table/host.cjs.map +1 -1
  53. package/dist/data-table/host.d.cts +18 -4
  54. package/dist/data-table/host.d.ts +18 -4
  55. package/dist/data-table/host.js.map +1 -1
  56. package/dist/data-table/index.cjs +3173 -451
  57. package/dist/data-table/index.cjs.map +1 -1
  58. package/dist/data-table/index.d.cts +13 -3
  59. package/dist/data-table/index.d.ts +13 -3
  60. package/dist/data-table/index.js +3174 -451
  61. package/dist/data-table/index.js.map +1 -1
  62. package/dist/data-table/infer-filter.d.cts +7 -2
  63. package/dist/data-table/infer-filter.d.ts +7 -2
  64. package/dist/data-table/layered-filters.d.cts +7 -2
  65. package/dist/data-table/layered-filters.d.ts +7 -2
  66. package/dist/data-table/menu-targets.cjs +77 -0
  67. package/dist/data-table/menu-targets.cjs.map +1 -0
  68. package/dist/data-table/menu-targets.d.cts +108 -0
  69. package/dist/data-table/menu-targets.d.ts +108 -0
  70. package/dist/data-table/menu-targets.js +57 -0
  71. package/dist/data-table/menu-targets.js.map +1 -0
  72. package/dist/data-table/pagination.cjs.map +1 -1
  73. package/dist/data-table/pagination.js.map +1 -1
  74. package/dist/data-table/query-control.d.cts +7 -2
  75. package/dist/data-table/query-control.d.ts +7 -2
  76. package/dist/data-table/spreadsheet.cjs +1062 -0
  77. package/dist/data-table/spreadsheet.cjs.map +1 -0
  78. package/dist/data-table/spreadsheet.d.cts +153 -0
  79. package/dist/data-table/spreadsheet.d.ts +153 -0
  80. package/dist/data-table/spreadsheet.js +1042 -0
  81. package/dist/data-table/spreadsheet.js.map +1 -0
  82. package/dist/data-table/types.cjs.map +1 -1
  83. package/dist/data-table/types.d.cts +7 -2
  84. package/dist/data-table/types.d.ts +7 -2
  85. package/dist/data-table/url-state.d.cts +7 -2
  86. package/dist/data-table/url-state.d.ts +7 -2
  87. package/dist/data-table/uuid-cell.cjs.map +1 -1
  88. package/dist/data-table/uuid-cell.js.map +1 -1
  89. package/dist/data-table/validation.cjs +229 -0
  90. package/dist/data-table/validation.cjs.map +1 -0
  91. package/dist/data-table/validation.d.cts +125 -0
  92. package/dist/data-table/validation.d.ts +125 -0
  93. package/dist/data-table/validation.js +209 -0
  94. package/dist/data-table/validation.js.map +1 -0
  95. package/dist/data-table/virtual-window.cjs +203 -0
  96. package/dist/data-table/virtual-window.cjs.map +1 -0
  97. package/dist/data-table/virtual-window.d.cts +84 -0
  98. package/dist/data-table/virtual-window.d.ts +84 -0
  99. package/dist/data-table/virtual-window.js +183 -0
  100. package/dist/data-table/virtual-window.js.map +1 -0
  101. package/dist/data-table/xlsx.d.cts +7 -2
  102. package/dist/data-table/xlsx.d.ts +7 -2
  103. package/dist/field-formats/index.d.cts +2 -2
  104. package/dist/field-formats/index.d.ts +2 -2
  105. package/dist/formulas/index.cjs +1041 -0
  106. package/dist/formulas/index.cjs.map +1 -0
  107. package/dist/formulas/index.d.cts +389 -0
  108. package/dist/formulas/index.d.ts +389 -0
  109. package/dist/formulas/index.js +1019 -0
  110. package/dist/formulas/index.js.map +1 -0
  111. package/dist/{types-BdbvlF9-.d.cts → types-CJWU1Gae.d.cts} +1 -1
  112. package/dist/{types-BdbvlF9-.d.ts → types-CJWU1Gae.d.ts} +1 -1
  113. package/dist/{types-D-flTpK0.d.ts → types-Cu5_eUG1.d.ts} +268 -4
  114. package/dist/{types-Sn249eoy.d.cts → types-DrJXAAoD.d.cts} +268 -4
  115. package/package.json +129 -2
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/formulas/formulas.ts"],"sourcesContent":["/**\n * @ai-matrx/design-system/formulas — THE formula language for computed columns.\n *\n * This module moved here from the app so every table in the product shares\n * one formula language. It stays pure: no React, no DOM, no network.\n *\n * A **formula column** holds nothing. Its cell is always `null` in the\n * database; the value the user sees is computed, on read, from the OTHER\n * columns of the SAME row. Champion: Airtable's formula field.\n *\n * THE THREE RULES THIS FILE OBEYS\n *\n * 1. **No `eval`, no `new Function`, no dynamic code.** A formula is user\n * content; it is tokenized, parsed into an AST, and interpreted. Nothing a\n * user types ever becomes JavaScript.\n * 2. **Nothing throws.** `parseFormula` and `evaluateFormula` both return a\n * discriminated result. A syntax error, a missing column, a division by\n * zero and a type mismatch are all `ok: false` with a plain-English\n * sentence a non-technical person can act on — never an exception, never a\n * silent blank cell (THE FALLBACK LAW, `lib/field-formats/FEATURE.md`).\n * 3. **The engine is pure.** It reads cells through a caller-supplied\n * `resolve(name)`. It never touches Redux, Supabase, or the DOM, which is\n * what makes every rule below testable.\n *\n * THE COERCION RULES (the whole contract, in one place)\n *\n * - **BLANK** is `null`, `undefined`, or `\"\"`. All three are the same value to\n * a formula, because a text cell that was cleared holds `\"\"` and a number\n * cell that was cleared holds `null`, and a user does not distinguish them.\n * - **A missing column is NOT blank.** `resolve()` returns `undefined` for a\n * column that does not exist (as opposed to `null` for one that is empty),\n * and that is an error: `{a} + {Typo}` must say the column is unknown rather\n * than quietly computing with zero.\n * - **Arithmetic treats BLANK as 0.** `{a} + {b}` with `b` empty is `{a}`.\n * That is the tolerant reading every spreadsheet uses, and the alternative\n * (the whole cell erroring because one input is empty) makes a formula\n * column useless on real, partly-filled data.\n * - **Numeric strings coerce.** `\"1,234\"`, `\" 42 \"`, `\"$5\"` and `\"3.5\"` are\n * numbers in arithmetic; `\"n/a\"` is a type error naming the value.\n * - **Booleans are 1 and 0 in arithmetic**, so SUM over a checkbox column\n * counts the ticks.\n * - **Aggregates SKIP blanks.** `SUM`/`MIN`/`MAX`/`AVERAGE` ignore empty\n * inputs entirely — `AVERAGE` divides by the count of the values that were\n * actually there, and returns BLANK when there were none.\n * - **Text conversion**: BLANK becomes `\"\"`, a number becomes its shortest\n * exact decimal, a boolean becomes `\"true\"` / `\"false\"`.\n * - **Comparison** is numeric when both sides read as numbers, otherwise a\n * plain string comparison. BLANK takes the shape of what it is compared\n * against — `0` beside a number, `\"\"` beside text — so `{a} = BLANK()`,\n * `{a} = 0` and `{a} = ''` all hold for an empty cell.\n * - **Truthiness** (for `IF` / `AND` / `OR` / `NOT`): BLANK is false, a\n * boolean is itself, a number is true unless `0`, a string is true unless\n * empty.\n * - **Dates are ISO strings, read and written in UTC.** A date function\n * accepts an ISO string (or a `Date`) and returns an ISO string — date-only\n * (`2026-09-14`) when the input was date-only, full (`2026-09-14T…Z`)\n * otherwise. UTC throughout on purpose: reading `2026-09-14` with local\n * getters turns it into the 13th for half the planet.\n * - **Division (and `%`) by zero is an error**, never `Infinity` and never\n * `NaN`. A cell may not lie about arithmetic that did not happen.\n */\n\n// ─── AST ─────────────────────────────────────────────────────────────────────\n\nexport type FormulaValue = string | number | boolean | null;\n\nexport type BinaryOperator =\n | \"+\"\n | \"-\"\n | \"*\"\n | \"/\"\n | \"%\"\n | \"&\"\n | \"=\"\n | \"!=\"\n | \"<>\"\n | \"<\"\n | \"<=\"\n | \">\"\n | \">=\";\n\nexport type FormulaAst =\n | { kind: \"number\"; value: number; position: number }\n | { kind: \"string\"; value: string; position: number }\n | { kind: \"boolean\"; value: boolean; position: number }\n | { kind: \"reference\"; name: string; position: number }\n | { kind: \"unary\"; operator: \"-\"; operand: FormulaAst; position: number }\n | {\n kind: \"binary\";\n operator: BinaryOperator;\n left: FormulaAst;\n right: FormulaAst;\n position: number;\n }\n | {\n kind: \"call\";\n name: FormulaFunctionName;\n args: FormulaAst[];\n position: number;\n };\n\nexport type ParseResult =\n | { ok: true; ast: FormulaAst; references: string[] }\n | { ok: false; error: string; position: number };\n\nexport type FormulaResult =\n | { ok: true; value: FormulaValue }\n | { ok: false; error: string };\n\nexport type FormulaResultType = \"number\" | \"text\" | \"boolean\" | \"date\" | \"unknown\";\n\n/** What a cell lookup may answer: a value, BLANK, or `undefined` = no such column. */\nexport type ResolveCell = (name: string) => unknown;\n\n/** Best-effort declared type of a column, for static result typing. */\nexport type ResolveColumnType = (\n name: string,\n) => \"number\" | \"text\" | \"boolean\" | \"date\" | undefined;\n\n// ─── the function table ──────────────────────────────────────────────────────\n\nconst FUNCTION_SPECS = {\n SUM: {\n signature: \"SUM(number, …)\",\n description: \"Adds every value, ignoring empty ones.\",\n min: 1,\n max: Infinity,\n result: \"number\",\n },\n MIN: {\n signature: \"MIN(number, …)\",\n description: \"The smallest value, ignoring empty ones.\",\n min: 1,\n max: Infinity,\n result: \"number\",\n },\n MAX: {\n signature: \"MAX(number, …)\",\n description: \"The largest value, ignoring empty ones.\",\n min: 1,\n max: Infinity,\n result: \"number\",\n },\n AVERAGE: {\n signature: \"AVERAGE(number, …)\",\n description:\n \"The average of the values that are filled in. Empty values are not counted.\",\n min: 1,\n max: Infinity,\n result: \"number\",\n },\n ROUND: {\n signature: \"ROUND(number, places?)\",\n description:\n \"Rounds to the given number of decimal places (0 when omitted). Halves round away from zero.\",\n min: 1,\n max: 2,\n result: \"number\",\n },\n ABS: {\n signature: \"ABS(number)\",\n description: \"The value without its minus sign.\",\n min: 1,\n max: 1,\n result: \"number\",\n },\n LEN: {\n signature: \"LEN(text)\",\n description: \"How many characters the text has.\",\n min: 1,\n max: 1,\n result: \"number\",\n },\n UPPER: {\n signature: \"UPPER(text)\",\n description: \"The text in capitals.\",\n min: 1,\n max: 1,\n result: \"text\",\n },\n LOWER: {\n signature: \"LOWER(text)\",\n description: \"The text in lower case.\",\n min: 1,\n max: 1,\n result: \"text\",\n },\n TRIM: {\n signature: \"TRIM(text)\",\n description: \"The text without leading or trailing spaces.\",\n min: 1,\n max: 1,\n result: \"text\",\n },\n CONCATENATE: {\n signature: \"CONCATENATE(text, …)\",\n description: \"Joins every value into one piece of text.\",\n min: 1,\n max: Infinity,\n result: \"text\",\n },\n LEFT: {\n signature: \"LEFT(text, count)\",\n description: \"The first few characters of the text.\",\n min: 2,\n max: 2,\n result: \"text\",\n },\n RIGHT: {\n signature: \"RIGHT(text, count)\",\n description: \"The last few characters of the text.\",\n min: 2,\n max: 2,\n result: \"text\",\n },\n CONTAINS: {\n signature: \"CONTAINS(text, part)\",\n description:\n \"Yes when the text contains that part. Upper and lower case are treated the same.\",\n min: 2,\n max: 2,\n result: \"boolean\",\n },\n IF: {\n signature: \"IF(condition, then, otherwise?)\",\n description:\n \"The second value when the condition holds, otherwise the third (empty when omitted).\",\n min: 2,\n max: 3,\n result: \"unknown\",\n },\n AND: {\n signature: \"AND(condition, …)\",\n description: \"Yes when every condition holds.\",\n min: 1,\n max: Infinity,\n result: \"boolean\",\n },\n OR: {\n signature: \"OR(condition, …)\",\n description: \"Yes when at least one condition holds.\",\n min: 1,\n max: Infinity,\n result: \"boolean\",\n },\n NOT: {\n signature: \"NOT(condition)\",\n description: \"Turns yes into no and no into yes.\",\n min: 1,\n max: 1,\n result: \"boolean\",\n },\n BLANK: {\n signature: \"BLANK()\",\n description: \"An empty value, for comparing against or returning.\",\n min: 0,\n max: 0,\n result: \"unknown\",\n },\n ISBLANK: {\n signature: \"ISBLANK(value)\",\n description: \"Yes when the value is empty.\",\n min: 1,\n max: 1,\n result: \"boolean\",\n },\n TODAY: {\n signature: \"TODAY()\",\n description: \"Today's date.\",\n min: 0,\n max: 0,\n result: \"date\",\n },\n NOW: {\n signature: \"NOW()\",\n description: \"The current date and time.\",\n min: 0,\n max: 0,\n result: \"date\",\n },\n DATEDIFF: {\n signature: \"DATEDIFF(from, to, 'days' | 'hours' | 'minutes')\",\n description:\n \"How far the second date is after the first. Negative when it is earlier.\",\n min: 3,\n max: 3,\n result: \"number\",\n },\n YEAR: {\n signature: \"YEAR(date)\",\n description: \"The four-digit year of the date.\",\n min: 1,\n max: 1,\n result: \"number\",\n },\n MONTH: {\n signature: \"MONTH(date)\",\n description: \"The month of the date, 1 to 12.\",\n min: 1,\n max: 1,\n result: \"number\",\n },\n DAY: {\n signature: \"DAY(date)\",\n description: \"The day of the month, 1 to 31.\",\n min: 1,\n max: 1,\n result: \"number\",\n },\n DATEADD: {\n signature: \"DATEADD(date, count, 'days' | 'months' | 'years')\",\n description:\n \"The date moved forward by that many units. Use a negative count to go back.\",\n min: 3,\n max: 3,\n result: \"date\",\n },\n} as const satisfies Record<\n string,\n {\n signature: string;\n description: string;\n min: number;\n max: number;\n result: FormulaResultType;\n }\n>;\n\nexport type FormulaFunctionName = keyof typeof FUNCTION_SPECS;\n\nconst FUNCTION_NAMES = Object.keys(FUNCTION_SPECS) as FormulaFunctionName[];\n\n/** The help list the formula editor shows. Order is the order it renders in. */\nexport const FORMULA_FUNCTIONS: readonly {\n name: FormulaFunctionName;\n signature: string;\n description: string;\n}[] = Object.freeze(\n FUNCTION_NAMES.map((name) => ({\n name,\n signature: FUNCTION_SPECS[name].signature,\n description: FUNCTION_SPECS[name].description,\n })),\n);\n\n// ─── tokenizer ───────────────────────────────────────────────────────────────\n\ntype TokenType =\n | \"number\"\n | \"string\"\n | \"identifier\"\n | \"reference\"\n | \"operator\"\n | \"paren-open\"\n | \"paren-close\"\n | \"comma\"\n | \"end\";\n\ntype Token = { type: TokenType; text: string; position: number };\n\n/** Thrown only INSIDE this module; every public entry point catches it. */\nclass FormulaError extends Error {\n readonly position: number;\n constructor(message: string, position = 0) {\n super(message);\n this.name = \"FormulaError\";\n this.position = position;\n }\n}\n\nconst TWO_CHAR_OPERATORS = [\"!=\", \"<>\", \"<=\", \">=\"];\nconst ONE_CHAR_OPERATORS = [\"+\", \"-\", \"*\", \"/\", \"%\", \"&\", \"=\", \"<\", \">\"];\n\nfunction isDigit(ch: string): boolean {\n return ch >= \"0\" && ch <= \"9\";\n}\n\nfunction isIdentifierStart(ch: string): boolean {\n return /[A-Za-z_]/.test(ch);\n}\n\nfunction isIdentifierPart(ch: string): boolean {\n return /[A-Za-z0-9_]/.test(ch);\n}\n\nfunction tokenize(source: string): Token[] {\n const tokens: Token[] = [];\n let i = 0;\n\n while (i < source.length) {\n const ch = source[i];\n // `i < source.length` already guarantees a character here; the guard only\n // makes that knowledge explicit for the compiler.\n if (ch === undefined) break;\n\n if (/\\s/.test(ch)) {\n i += 1;\n continue;\n }\n\n if (ch === \"{\") {\n const close = source.indexOf(\"}\", i + 1);\n if (close === -1) {\n throw new FormulaError(\n \"This column reference is missing its closing brace `}`.\",\n i,\n );\n }\n const name = source.slice(i + 1, close).trim();\n if (name === \"\") {\n throw new FormulaError(\"This column reference has no name inside `{}`.\", i);\n }\n tokens.push({ type: \"reference\", text: name, position: i });\n i = close + 1;\n continue;\n }\n\n if (ch === '\"' || ch === \"'\") {\n const quote = ch;\n let j = i + 1;\n let text = \"\";\n let closed = false;\n while (j < source.length) {\n if (source[j] === \"\\\\\" && j + 1 < source.length) {\n text += source[j + 1];\n j += 2;\n continue;\n }\n if (source[j] === quote) {\n closed = true;\n j += 1;\n break;\n }\n text += source[j];\n j += 1;\n }\n if (!closed) {\n throw new FormulaError(\n `This text is missing its closing ${quote === '\"' ? \"double\" : \"single\"} quote.`,\n i,\n );\n }\n tokens.push({ type: \"string\", text, position: i });\n i = j;\n continue;\n }\n\n if (isDigit(ch) || (ch === \".\" && isDigit(source[i + 1] ?? \"\"))) {\n let j = i;\n let seenDot = false;\n while (j < source.length) {\n // The `j < source.length` above guarantees a character; `?? \"\"` is\n // never taken and reads as \"not a digit\", which ends the number.\n if (isDigit(source[j] ?? \"\")) {\n j += 1;\n continue;\n }\n if (source[j] === \".\" && !seenDot) {\n seenDot = true;\n j += 1;\n continue;\n }\n break;\n }\n tokens.push({ type: \"number\", text: source.slice(i, j), position: i });\n i = j;\n continue;\n }\n\n if (isIdentifierStart(ch)) {\n let j = i;\n // Guarded by `j < source.length`; `?? \"\"` is never taken and reads as\n // \"not an identifier character\", which ends the identifier.\n while (j < source.length && isIdentifierPart(source[j] ?? \"\")) j += 1;\n tokens.push({ type: \"identifier\", text: source.slice(i, j), position: i });\n i = j;\n continue;\n }\n\n if (ch === \"(\") {\n tokens.push({ type: \"paren-open\", text: ch, position: i });\n i += 1;\n continue;\n }\n if (ch === \")\") {\n tokens.push({ type: \"paren-close\", text: ch, position: i });\n i += 1;\n continue;\n }\n if (ch === \",\") {\n tokens.push({ type: \"comma\", text: ch, position: i });\n i += 1;\n continue;\n }\n\n const two = source.slice(i, i + 2);\n if (TWO_CHAR_OPERATORS.includes(two)) {\n tokens.push({ type: \"operator\", text: two, position: i });\n i += 2;\n continue;\n }\n if (ONE_CHAR_OPERATORS.includes(ch)) {\n tokens.push({ type: \"operator\", text: ch, position: i });\n i += 1;\n continue;\n }\n\n throw new FormulaError(`\\`${ch}\\` does not mean anything in a formula.`, i);\n }\n\n tokens.push({ type: \"end\", text: \"\", position: source.length });\n return tokens;\n}\n\n// ─── parser ──────────────────────────────────────────────────────────────────\n//\n// Precedence, loosest first:\n// comparison = != <> < <= > >=\n// concat &\n// additive + -\n// multiplicative * / %\n// unary -\n// primary literal · {reference} · FUNC(…) · ( … )\n\nconst COMPARISON_OPERATORS = [\"=\", \"!=\", \"<>\", \"<\", \"<=\", \">\", \">=\"];\n\nclass Parser {\n private readonly tokens: Token[];\n private index = 0;\n readonly references: string[] = [];\n\n /**\n * `tokenize` always terminates the list with an `end` token and the grammar\n * never advances past it, so neither accessor below can actually run off the\n * end. This sentinel is what they would return if one ever did, which keeps\n * both accessors total without an assertion.\n */\n private readonly endToken: Token;\n\n constructor(tokens: Token[]) {\n this.tokens = tokens;\n this.endToken = tokens[tokens.length - 1] ?? {\n type: \"end\",\n text: \"\",\n position: 0,\n };\n }\n\n private peek(): Token {\n return this.tokens[this.index] ?? this.endToken;\n }\n\n private next(): Token {\n return this.tokens[this.index++] ?? this.endToken;\n }\n\n private matchOperator(candidates: readonly string[]): Token | null {\n const token = this.peek();\n if (token.type === \"operator\" && candidates.includes(token.text)) {\n this.index += 1;\n return token;\n }\n return null;\n }\n\n parseProgram(): FormulaAst {\n const expression = this.parseComparison();\n const trailing = this.peek();\n if (trailing.type !== \"end\") {\n throw new FormulaError(\n `Nothing should follow the formula here — remove \\`${trailing.text}\\`.`,\n trailing.position,\n );\n }\n return expression;\n }\n\n private parseComparison(): FormulaAst {\n let left = this.parseConcat();\n let operator = this.matchOperator(COMPARISON_OPERATORS);\n while (operator) {\n const right = this.parseConcat();\n left = {\n kind: \"binary\",\n operator: operator.text as BinaryOperator,\n left,\n right,\n position: operator.position,\n };\n operator = this.matchOperator(COMPARISON_OPERATORS);\n }\n return left;\n }\n\n private parseConcat(): FormulaAst {\n let left = this.parseAdditive();\n let operator = this.matchOperator([\"&\"]);\n while (operator) {\n const right = this.parseAdditive();\n left = {\n kind: \"binary\",\n operator: \"&\",\n left,\n right,\n position: operator.position,\n };\n operator = this.matchOperator([\"&\"]);\n }\n return left;\n }\n\n private parseAdditive(): FormulaAst {\n let left = this.parseMultiplicative();\n let operator = this.matchOperator([\"+\", \"-\"]);\n while (operator) {\n const right = this.parseMultiplicative();\n left = {\n kind: \"binary\",\n operator: operator.text as BinaryOperator,\n left,\n right,\n position: operator.position,\n };\n operator = this.matchOperator([\"+\", \"-\"]);\n }\n return left;\n }\n\n private parseMultiplicative(): FormulaAst {\n let left = this.parseUnary();\n let operator = this.matchOperator([\"*\", \"/\", \"%\"]);\n while (operator) {\n const right = this.parseUnary();\n left = {\n kind: \"binary\",\n operator: operator.text as BinaryOperator,\n left,\n right,\n position: operator.position,\n };\n operator = this.matchOperator([\"*\", \"/\", \"%\"]);\n }\n return left;\n }\n\n private parseUnary(): FormulaAst {\n const minus = this.matchOperator([\"-\"]);\n if (minus) {\n return {\n kind: \"unary\",\n operator: \"-\",\n operand: this.parseUnary(),\n position: minus.position,\n };\n }\n // A leading `+` is harmless and people type it; accept and ignore it.\n const plus = this.matchOperator([\"+\"]);\n if (plus) return this.parseUnary();\n return this.parsePrimary();\n }\n\n private parsePrimary(): FormulaAst {\n const token = this.next();\n\n if (token.type === \"number\") {\n return { kind: \"number\", value: Number(token.text), position: token.position };\n }\n\n if (token.type === \"string\") {\n return { kind: \"string\", value: token.text, position: token.position };\n }\n\n if (token.type === \"reference\") {\n if (!this.references.includes(token.text)) this.references.push(token.text);\n return { kind: \"reference\", name: token.text, position: token.position };\n }\n\n if (token.type === \"paren-open\") {\n const inner = this.parseComparison();\n const close = this.next();\n if (close.type !== \"paren-close\") {\n throw new FormulaError(\n \"This opening bracket `(` never closes.\",\n token.position,\n );\n }\n return inner;\n }\n\n if (token.type === \"identifier\") {\n const upper = token.text.toUpperCase();\n if (upper === \"TRUE\" || upper === \"FALSE\") {\n return {\n kind: \"boolean\",\n value: upper === \"TRUE\",\n position: token.position,\n };\n }\n if (!(FUNCTION_NAMES as string[]).includes(upper)) {\n throw new FormulaError(\n `There is no function called \\`${token.text}\\`. A column goes in braces, like {${token.text}}.`,\n token.position,\n );\n }\n const name = upper as FormulaFunctionName;\n const open = this.next();\n if (open.type !== \"paren-open\") {\n throw new FormulaError(\n `\\`${name}\\` needs brackets after it, like ${FUNCTION_SPECS[name].signature}.`,\n token.position,\n );\n }\n const args: FormulaAst[] = [];\n if (this.peek().type === \"paren-close\") {\n this.index += 1;\n } else {\n for (;;) {\n args.push(this.parseComparison());\n const separator = this.next();\n if (separator.type === \"comma\") continue;\n if (separator.type === \"paren-close\") break;\n throw new FormulaError(\n `\\`${name}\\` is missing a comma or its closing bracket.`,\n separator.position,\n );\n }\n }\n const spec = FUNCTION_SPECS[name];\n if (args.length < spec.min || args.length > spec.max) {\n throw new FormulaError(\n `\\`${name}\\` was given ${args.length} value${args.length === 1 ? \"\" : \"s\"}. Use ${spec.signature}.`,\n token.position,\n );\n }\n return { kind: \"call\", name, args, position: token.position };\n }\n\n if (token.type === \"end\") {\n throw new FormulaError(\"The formula stops before it is finished.\", token.position);\n }\n\n throw new FormulaError(\n `\\`${token.text}\\` cannot start a value here.`,\n token.position,\n );\n }\n}\n\n/**\n * Turn formula text into an AST plus the list of columns it reads.\n *\n * `references` holds every `{…}` name exactly as typed, in first-use order —\n * the caller resolves them (display name OR machine field name) through its own\n * lookup, so this module never needs to know the table.\n */\nexport function parseFormula(source: string): ParseResult {\n try {\n const text = source ?? \"\";\n if (text.trim() === \"\") {\n return { ok: false, error: \"This formula is empty.\", position: 0 };\n }\n const parser = new Parser(tokenize(text));\n const ast = parser.parseProgram();\n return { ok: true, ast, references: parser.references };\n } catch (error) {\n if (error instanceof FormulaError) {\n return { ok: false, error: error.message, position: error.position };\n }\n return {\n ok: false,\n error: \"This formula could not be read.\",\n position: 0,\n };\n }\n}\n\n// ─── value coercion ──────────────────────────────────────────────────────────\n\nconst MISSING = Symbol(\"missing-column\");\n\nfunction isBlank(value: FormulaValue): boolean {\n return value === null || value === \"\";\n}\n\n/** Shortest exact decimal for a number — keeps `0.1 + 0.2` out of the cell. */\nfunction numberToText(n: number): string {\n if (Number.isInteger(n)) return String(n);\n const trimmed = Number(n.toPrecision(15));\n return String(Number.isFinite(trimmed) ? trimmed : n);\n}\n\nfunction toText(value: FormulaValue): string {\n if (value === null) return \"\";\n if (typeof value === \"number\") return numberToText(value);\n if (typeof value === \"boolean\") return value ? \"true\" : \"false\";\n return value;\n}\n\n/** A number, or `null` when the value simply is not one. BLANK reads as 0. */\nfunction looseNumber(value: FormulaValue): number | null {\n if (value === null || value === \"\") return 0;\n if (typeof value === \"number\") return Number.isFinite(value) ? value : null;\n if (typeof value === \"boolean\") return value ? 1 : 0;\n const cleaned = value.replace(/[,\\s$€£¥%]/g, \"\");\n if (cleaned === \"\") return 0;\n const n = Number(cleaned);\n return Number.isFinite(n) ? n : null;\n}\n\nfunction requireNumber(value: FormulaValue, what: string): number {\n const n = looseNumber(value);\n if (n === null) {\n throw new FormulaError(`${what} needs a number, but got \"${toText(value)}\".`);\n }\n return n;\n}\n\nfunction truthy(value: FormulaValue): boolean {\n if (value === null || value === \"\") return false;\n if (typeof value === \"boolean\") return value;\n if (typeof value === \"number\") return value !== 0;\n return true;\n}\n\n/** Both sides numeric? compare as numbers. Otherwise compare as text. */\nfunction compareValues(left: FormulaValue, right: FormulaValue): number {\n const looksNumeric = (v: FormulaValue) =>\n typeof v === \"number\" ||\n (typeof v === \"string\" && v.trim() !== \"\" && looseNumber(v) !== null);\n // BLANK takes the shape of whatever it is compared against: 0 beside a\n // number, \"\" beside text. `{Empty} = 0` and `{Empty} = ''` are both true.\n if (isBlank(left) && isBlank(right)) return 0;\n const blankBesideNumber =\n (isBlank(left) && looksNumeric(right)) ||\n (isBlank(right) && looksNumeric(left));\n if (blankBesideNumber || (looksNumeric(left) && looksNumeric(right))) {\n const a = looseNumber(left) ?? 0;\n const b = looseNumber(right) ?? 0;\n return a < b ? -1 : a > b ? 1 : 0;\n }\n if (typeof left === \"boolean\" || typeof right === \"boolean\") {\n const a = truthy(left);\n const b = truthy(right);\n return a === b ? 0 : a ? 1 : -1;\n }\n const a = toText(left);\n const b = toText(right);\n return a < b ? -1 : a > b ? 1 : 0;\n}\n\n// ─── dates ───────────────────────────────────────────────────────────────────\n\nconst DATE_ONLY_RE = /^\\d{4}-\\d{2}-\\d{2}$/;\n\ntype ParsedDate = { ms: number; dateOnly: boolean };\n\nfunction requireDate(value: FormulaValue, what: string): ParsedDate {\n if (value === null || value === \"\") {\n throw new FormulaError(`${what} needs a date, but that value is empty.`);\n }\n if (typeof value === \"string\") {\n const trimmed = value.trim();\n const ms = Date.parse(trimmed);\n if (!Number.isNaN(ms)) return { ms, dateOnly: DATE_ONLY_RE.test(trimmed) };\n }\n throw new FormulaError(`${what} needs a date, but got \"${toText(value)}\".`);\n}\n\nfunction isoFrom(ms: number, dateOnly: boolean): string {\n const iso = new Date(ms).toISOString();\n return dateOnly ? iso.slice(0, 10) : iso;\n}\n\nconst DATEDIFF_UNITS: Record<string, number> = {\n days: 86_400_000,\n hours: 3_600_000,\n minutes: 60_000,\n};\n\nfunction requireUnit(\n value: FormulaValue,\n allowed: string[],\n fn: string,\n): string {\n const unit = toText(value).trim().toLowerCase();\n if (!allowed.includes(unit)) {\n throw new FormulaError(\n `\\`${fn}\\` needs one of ${allowed.map((u) => `'${u}'`).join(\", \")} — got \"${toText(value)}\".`,\n );\n }\n return unit;\n}\n\n/** Add months/years in UTC, clamping to the end of a shorter month. */\nfunction addMonths(ms: number, count: number): number {\n const d = new Date(ms);\n const day = d.getUTCDate();\n const target = new Date(ms);\n target.setUTCDate(1);\n target.setUTCMonth(target.getUTCMonth() + count);\n const lastDay = new Date(\n Date.UTC(target.getUTCFullYear(), target.getUTCMonth() + 1, 0),\n ).getUTCDate();\n target.setUTCDate(Math.min(day, lastDay));\n return target.getTime();\n}\n\n// ─── evaluator ───────────────────────────────────────────────────────────────\n\n/** SUM-style: flatten args, drop blanks, demand numbers of the rest. */\nfunction numericArgs(values: FormulaValue[], fn: string): number[] {\n const out: number[] = [];\n for (const value of values) {\n if (isBlank(value)) continue;\n out.push(requireNumber(value, `\\`${fn}\\``));\n }\n return out;\n}\n\nfunction roundHalfAway(n: number, places: number): number {\n const factor = 10 ** places;\n const scaled = n * factor;\n // Nudge past the float representation of an exact half (1.005 * 100 = 100.49999…).\n const corrected = Number(scaled.toPrecision(15));\n const rounded =\n corrected < 0 ? -Math.round(-corrected) : Math.round(corrected);\n return rounded / factor;\n}\n\n/**\n * Positional argument of a call. The parser has already checked every call\n * against its `FUNCTION_SPECS` min/max, so each index read below is present;\n * this accessor only makes that knowledge explicit. An index that somehow was\n * not supplied reads as BLANK — the value this language already uses for \"not\n * given\" — so nothing here can throw on a missing argument.\n */\nfunction argAt(args: readonly FormulaValue[], index: number): FormulaValue {\n return args[index] ?? null;\n}\n\nfunction callFunction(\n name: FormulaFunctionName,\n args: FormulaValue[],\n): FormulaValue {\n switch (name) {\n case \"SUM\":\n return numericArgs(args, \"SUM\").reduce((a, b) => a + b, 0);\n case \"MIN\": {\n const nums = numericArgs(args, \"MIN\");\n return nums.length === 0 ? null : Math.min(...nums);\n }\n case \"MAX\": {\n const nums = numericArgs(args, \"MAX\");\n return nums.length === 0 ? null : Math.max(...nums);\n }\n case \"AVERAGE\": {\n const nums = numericArgs(args, \"AVERAGE\");\n if (nums.length === 0) return null;\n return nums.reduce((a, b) => a + b, 0) / nums.length;\n }\n case \"ROUND\": {\n const n = requireNumber(argAt(args, 0), \"`ROUND`\");\n const places =\n args.length > 1 ? Math.trunc(requireNumber(argAt(args, 1), \"`ROUND`\")) : 0;\n return roundHalfAway(n, places);\n }\n case \"ABS\":\n return Math.abs(requireNumber(argAt(args, 0), \"`ABS`\"));\n case \"LEN\":\n return toText(argAt(args, 0)).length;\n case \"UPPER\":\n return toText(argAt(args, 0)).toUpperCase();\n case \"LOWER\":\n return toText(argAt(args, 0)).toLowerCase();\n case \"TRIM\":\n return toText(argAt(args, 0)).trim();\n case \"CONCATENATE\":\n return args.map(toText).join(\"\");\n case \"LEFT\": {\n const count = Math.trunc(requireNumber(argAt(args, 1), \"`LEFT`\"));\n return toText(argAt(args, 0)).slice(0, Math.max(0, count));\n }\n case \"RIGHT\": {\n const count = Math.trunc(requireNumber(argAt(args, 1), \"`RIGHT`\"));\n if (count <= 0) return \"\";\n return toText(argAt(args, 0)).slice(-count);\n }\n case \"CONTAINS\":\n return toText(argAt(args, 0))\n .toLowerCase()\n .includes(toText(argAt(args, 1)).toLowerCase());\n case \"NOT\":\n return !truthy(argAt(args, 0));\n case \"BLANK\":\n return null;\n case \"ISBLANK\":\n return isBlank(argAt(args, 0));\n case \"TODAY\":\n return new Date().toISOString().slice(0, 10);\n case \"NOW\":\n return new Date().toISOString();\n case \"DATEDIFF\": {\n const from = requireDate(argAt(args, 0), \"`DATEDIFF`\");\n const to = requireDate(argAt(args, 1), \"`DATEDIFF`\");\n const unit = requireUnit(argAt(args, 2), Object.keys(DATEDIFF_UNITS), \"DATEDIFF\");\n // `requireUnit` was handed this map's own key list, so it can only return\n // a key that is present; the guard makes that explicit.\n const perUnit = DATEDIFF_UNITS[unit];\n if (perUnit === undefined) return null;\n return Math.trunc((to.ms - from.ms) / perUnit);\n }\n case \"YEAR\":\n return new Date(requireDate(argAt(args, 0), \"`YEAR`\").ms).getUTCFullYear();\n case \"MONTH\":\n return new Date(requireDate(argAt(args, 0), \"`MONTH`\").ms).getUTCMonth() + 1;\n case \"DAY\":\n return new Date(requireDate(argAt(args, 0), \"`DAY`\").ms).getUTCDate();\n case \"DATEADD\": {\n const base = requireDate(argAt(args, 0), \"`DATEADD`\");\n const count = Math.trunc(requireNumber(argAt(args, 1), \"`DATEADD`\"));\n const unit = requireUnit(argAt(args, 2), [\"days\", \"months\", \"years\"], \"DATEADD\");\n const ms =\n unit === \"days\"\n ? base.ms + count * 86_400_000\n : addMonths(base.ms, unit === \"months\" ? count : count * 12);\n return isoFrom(ms, base.dateOnly);\n }\n // IF / AND / OR short-circuit in `evaluateNode` and never reach here.\n case \"IF\":\n case \"AND\":\n case \"OR\":\n throw new FormulaError(`\\`${name}\\` could not be evaluated.`);\n }\n}\n\nfunction applyBinary(\n operator: BinaryOperator,\n left: FormulaValue,\n right: FormulaValue,\n): FormulaValue {\n switch (operator) {\n case \"&\":\n return toText(left) + toText(right);\n case \"+\":\n return (\n requireNumber(left, \"`+`\") + requireNumber(right, \"`+`\")\n );\n case \"-\":\n return requireNumber(left, \"`-`\") - requireNumber(right, \"`-`\");\n case \"*\":\n return requireNumber(left, \"`*`\") * requireNumber(right, \"`*`\");\n case \"/\": {\n const divisor = requireNumber(right, \"`/`\");\n if (divisor === 0) {\n throw new FormulaError(\"This formula divides by zero.\");\n }\n return requireNumber(left, \"`/`\") / divisor;\n }\n case \"%\": {\n const divisor = requireNumber(right, \"`%`\");\n if (divisor === 0) {\n throw new FormulaError(\"This formula divides by zero.\");\n }\n return requireNumber(left, \"`%`\") % divisor;\n }\n case \"=\":\n return compareValues(left, right) === 0;\n case \"!=\":\n case \"<>\":\n return compareValues(left, right) !== 0;\n case \"<\":\n return compareValues(left, right) < 0;\n case \"<=\":\n return compareValues(left, right) <= 0;\n case \">\":\n return compareValues(left, right) > 0;\n case \">=\":\n return compareValues(left, right) >= 0;\n }\n}\n\nfunction normalizeCell(raw: unknown, name: string): FormulaValue {\n if (raw === MISSING) {\n throw new FormulaError(`There is no column called {${name}}.`);\n }\n if (raw === null || raw === undefined) return null;\n if (typeof raw === \"string\" || typeof raw === \"boolean\") return raw;\n if (typeof raw === \"number\") {\n if (!Number.isFinite(raw)) {\n throw new FormulaError(`The value in {${name}} is not a usable number.`);\n }\n return raw;\n }\n if (raw instanceof Date) {\n return Number.isNaN(raw.getTime()) ? null : raw.toISOString();\n }\n // Arrays and objects have no scalar reading; JSON keeps them legible.\n try {\n return JSON.stringify(raw);\n } catch {\n return String(raw);\n }\n}\n\nfunction evaluateNode(node: FormulaAst, resolve: ResolveCell): FormulaValue {\n switch (node.kind) {\n case \"number\":\n case \"string\":\n case \"boolean\":\n return node.value;\n case \"reference\": {\n const raw = resolve(node.name);\n // `undefined` means the column does not exist; a blank cell is null/\"\".\n return normalizeCell(raw === undefined ? MISSING : raw, node.name);\n }\n case \"unary\":\n return -requireNumber(evaluateNode(node.operand, resolve), \"`-`\");\n case \"binary\":\n return applyBinary(\n node.operator,\n evaluateNode(node.left, resolve),\n evaluateNode(node.right, resolve),\n );\n case \"call\": {\n if (node.name === \"IF\") {\n // The parser enforces IF's 2–3 arity, so both of these are present; an\n // absent one would read as BLANK rather than throw.\n const test = node.args[0];\n const whenTrue = node.args[1];\n const condition = truthy(test ? evaluateNode(test, resolve) : null);\n if (condition) return whenTrue ? evaluateNode(whenTrue, resolve) : null;\n const whenFalse = node.args[2];\n return node.args.length > 2 && whenFalse\n ? evaluateNode(whenFalse, resolve)\n : null;\n }\n if (node.name === \"AND\") {\n for (const arg of node.args) {\n if (!truthy(evaluateNode(arg, resolve))) return false;\n }\n return true;\n }\n if (node.name === \"OR\") {\n for (const arg of node.args) {\n if (truthy(evaluateNode(arg, resolve))) return true;\n }\n return false;\n }\n return callFunction(\n node.name,\n node.args.map((arg) => evaluateNode(arg, resolve)),\n );\n }\n }\n}\n\n/**\n * Compute one cell. NEVER throws.\n *\n * `resolve(name)` is given the name exactly as it was typed between the braces\n * and must return the cell's value, or `undefined` when no such column exists —\n * the difference between \"empty\" and \"misspelt\", which is the difference\n * between a working formula and a wrong answer.\n */\nexport function evaluateFormula(\n ast: FormulaAst,\n resolve: ResolveCell,\n): FormulaResult {\n try {\n const value = evaluateNode(ast, resolve);\n if (typeof value === \"number\" && !Number.isFinite(value)) {\n return { ok: false, error: \"This formula produced a number too large to show.\" };\n }\n return { ok: true, value };\n } catch (error) {\n if (error instanceof FormulaError) return { ok: false, error: error.message };\n return { ok: false, error: \"This formula could not be calculated.\" };\n }\n}\n\n// ─── static result type ──────────────────────────────────────────────────────\n\n/**\n * Best-effort: what kind of value will this formula produce? The UI uses it to\n * pick a sensible display format for the computed column. `unknown` is always a\n * legal answer — never guess a format on a shaky reading.\n */\nexport function formulaResultType(\n ast: FormulaAst,\n columnType: ResolveColumnType,\n): FormulaResultType {\n switch (ast.kind) {\n case \"number\":\n return \"number\";\n case \"string\":\n return \"text\";\n case \"boolean\":\n return \"boolean\";\n case \"unary\":\n return \"number\";\n case \"reference\":\n return columnType(ast.name) ?? \"unknown\";\n case \"binary\": {\n if (ast.operator === \"&\") return \"text\";\n if (COMPARISON_OPERATORS.includes(ast.operator)) return \"boolean\";\n return \"number\";\n }\n case \"call\": {\n if (ast.name === \"IF\") {\n // Arity is parser-enforced: IF always carries its \"then\" branch. An\n // absent branch types as \"unknown\", the same neutral this function\n // already returns for anything it cannot pin down.\n const thenArg = ast.args[1];\n const then = thenArg ? formulaResultType(thenArg, columnType) : \"unknown\";\n const otherwiseArg = ast.args[2];\n if (ast.args.length < 3 || !otherwiseArg) return then;\n const otherwise = formulaResultType(otherwiseArg, columnType);\n if (then === otherwise) return then;\n if (then === \"unknown\") return otherwise;\n if (otherwise === \"unknown\") return then;\n return \"unknown\";\n }\n if (ast.name === \"MIN\" || ast.name === \"MAX\") {\n // MIN/MAX over dates is still dates; over anything else, a number.\n const types = ast.args.map((a) => formulaResultType(a, columnType));\n if (types.length > 0 && types.every((t) => t === \"date\")) return \"date\";\n return \"number\";\n }\n return FUNCTION_SPECS[ast.name].result;\n }\n }\n}\n\n// ─── computed columns over rows ─────────────────────────────────────────────\n//\n// THE ONE PLACE a formula column's value is injected into a row. The grid,\n// every copy/export reader (`getCompleteTable` consumers), the client-side\n// sort and the agent scope all call this, so no reader can ever hand out a\n// formula column as the empty cell the database actually stores.\n\n/** The slice of a field row this helper needs to find and evaluate formulas. */\nexport interface ComputedColumnField {\n field_name: string;\n display_name: string;\n data_type: string;\n metadata?: unknown;\n}\n\nexport interface ComputedColumn<F extends ComputedColumnField = ComputedColumnField> {\n field: F;\n parsed: ParseResult;\n}\n\n/**\n * Read the field's declared format WITHOUT importing `lib/field-formats`\n * (which imports nothing from `features/`, and the reverse direction is the\n * one this module keeps clean too). The shape is `metadata.format = { id,\n * options }`, exactly what `readFieldFormatConfig` reads.\n */\nfunction formulaExpressionOf(field: ComputedColumnField): string | null {\n const metadata = field.metadata;\n if (!metadata || typeof metadata !== \"object\") return null;\n const format = (metadata as { format?: unknown }).format;\n if (!format || typeof format !== \"object\") return null;\n if ((format as { id?: unknown }).id !== \"formula\") return null;\n const options = (format as { options?: unknown }).options;\n const formula =\n options && typeof options === \"object\"\n ? (options as { formula?: unknown }).formula\n : undefined;\n const expression =\n formula && typeof formula === \"object\"\n ? (formula as { expression?: unknown }).expression\n : undefined;\n return typeof expression === \"string\" ? expression : \"\";\n}\n\n/** True when the column's declared format is `formula`. */\nexport function isFormulaColumn(field: ComputedColumnField): boolean {\n return formulaExpressionOf(field) !== null;\n}\n\n/** Every formula column among `fields`, each with its expression parsed once. */\nexport function formulaColumnsOf<F extends ComputedColumnField>(\n fields: readonly F[],\n): ComputedColumn<F>[] {\n return fields.flatMap((field) => {\n const expression = formulaExpressionOf(field);\n if (expression === null) return [];\n return [{ field, parsed: parseFormula(expression) }];\n });\n}\n\nexport interface ComputedRowsResult<R> {\n rows: R[];\n /** `${rowId}::${fieldName}` → the plain-English reason that cell shows #ERROR. */\n errors: Map<string, string>;\n /** Machine names of the formula columns, for callers that must skip them. */\n formulaFieldNames: Set<string>;\n}\n\n/**\n * Return `rows` with every formula column's value computed from the row's\n * other cells. Pure; the input rows are never mutated. When `fields` holds no\n * formula column the SAME array is returned, so callers pay nothing.\n *\n * Formula columns are evaluated in `fields` order, and each one is written into\n * the working copy of the row before the next runs — so a later formula may\n * reference an earlier one. References resolve by machine name first, then by\n * display name (case-insensitive), matching the grid.\n */\nexport function withComputedColumns<\n R extends { id: string; data: Record<string, unknown> },\n F extends ComputedColumnField,\n>(rows: readonly R[], fields: readonly F[]): ComputedRowsResult<R> {\n const columns = formulaColumnsOf(fields);\n const formulaFieldNames = new Set(columns.map((c) => c.field.field_name));\n const errors = new Map<string, string>();\n if (columns.length === 0) {\n return { rows: rows as R[], errors, formulaFieldNames };\n }\n // A reference resolves against the table's COLUMNS, never against the keys a\n // particular row happens to carry: a row saved with only some of its cells\n // (every new row, every sparse import) has no key for the rest, and that is\n // BLANK — not \"no such column\", which is the #ERROR the engine reserves for\n // a misspelt reference.\n const fieldNameByReference = new Map<string, string>();\n for (const f of fields) {\n fieldNameByReference.set(f.display_name.toLowerCase(), f.field_name);\n }\n for (const f of fields) {\n // Machine names win over a display name that happens to collide.\n fieldNameByReference.set(f.field_name.toLowerCase(), f.field_name);\n }\n const computed = rows.map((row) => {\n const data: Record<string, unknown> = { ...(row.data ?? {}) };\n const resolve: ResolveCell = (name) => {\n if (name in data) return data[name];\n const fieldName = fieldNameByReference.get(name.toLowerCase());\n if (fieldName === undefined) return undefined; // truly no such column\n return data[fieldName] ?? null;\n };\n for (const { field, parsed } of columns) {\n if (!parsed.ok) {\n data[field.field_name] = null;\n errors.set(`${row.id}::${field.field_name}`, parsed.error);\n continue;\n }\n const result = evaluateFormula(parsed.ast, resolve);\n if (result.ok) data[field.field_name] = result.value;\n else {\n data[field.field_name] = null;\n errors.set(`${row.id}::${field.field_name}`, result.error);\n }\n }\n return { ...row, data };\n });\n return { rows: computed, errors, formulaFieldNames };\n}\n\n// ─── renaming a column a formula refers to ──────────────────────────────────\n\n/**\n * Rewrite every `{from}` reference in `expression` to `{to}` — what keeps a\n * formula working when the column it names is renamed (Airtable and Sheets do\n * this silently; a rename that turned a working column into #ERROR would be\n * the platform punishing the user for tidying up).\n *\n * Walks the source with the tokenizer's own rules for the two things that\n * matter: a `{…}` inside a quoted string is TEXT and is left alone, and `\\`\n * escapes the next character inside a string. The match is on the trimmed\n * name, case-insensitive — the same comparison `withComputedColumns` makes.\n * Returns the SAME string when nothing referenced `from`.\n */\nexport function rewriteFormulaReferences(\n expression: string,\n from: string,\n to: string,\n): string {\n const target = from.trim().toLowerCase();\n if (!target) return expression;\n let out = \"\";\n let i = 0;\n while (i < expression.length) {\n const ch = expression[i];\n if (ch === '\"' || ch === \"'\") {\n let j = i + 1;\n while (j < expression.length) {\n if (expression[j] === \"\\\\\" && j + 1 < expression.length) {\n j += 2;\n continue;\n }\n if (expression[j] === ch) {\n j += 1;\n break;\n }\n j += 1;\n }\n out += expression.slice(i, j);\n i = j;\n continue;\n }\n if (ch === \"{\") {\n const close = expression.indexOf(\"}\", i + 1);\n if (close === -1) {\n out += expression.slice(i);\n break;\n }\n const name = expression.slice(i + 1, close);\n out += name.trim().toLowerCase() === target ? `{${to}}` : expression.slice(i, close + 1);\n i = close + 1;\n continue;\n }\n out += ch;\n i += 1;\n }\n return out;\n}\n"],"mappings":"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","names":["a","b"]}
@@ -201,4 +201,4 @@ type FieldFormatDef = {
201
201
  parse: (raw: unknown, options: FieldFormatOptions) => unknown;
202
202
  };
203
203
 
204
- export type { FieldFormatId as F, StructuredListBinding as S, FieldFormatDef as a, FieldFormatConfig as b, FormatResult as c, FieldBaseType as d, FieldChoice as e, FieldEditorKind as f, FieldFormatOptions as g };
204
+ export type { FieldFormatConfig as F, StructuredListBinding as S, FieldFormatId as a, FieldFormatDef as b, FormatResult as c, FieldBaseType as d, FieldChoice as e, FieldEditorKind as f, FieldFormatOptions as g };
@@ -201,4 +201,4 @@ type FieldFormatDef = {
201
201
  parse: (raw: unknown, options: FieldFormatOptions) => unknown;
202
202
  };
203
203
 
204
- export type { FieldFormatId as F, StructuredListBinding as S, FieldFormatDef as a, FieldFormatConfig as b, FormatResult as c, FieldBaseType as d, FieldChoice as e, FieldEditorKind as f, FieldFormatOptions as g };
204
+ export type { FieldFormatConfig as F, StructuredListBinding as S, FieldFormatId as a, FieldFormatDef as b, FormatResult as c, FieldBaseType as d, FieldChoice as e, FieldEditorKind as f, FieldFormatOptions as g };
@@ -1,7 +1,11 @@
1
1
  import { ContentTransferReference } from './content-transfer.js';
2
2
  import { ReactNode } from 'react';
3
3
  import { AiVariant, AiCustomSource, CopyExportConfig } from './data-table/copy-types.js';
4
- import { b as FieldFormatConfig } from './types-BdbvlF9-.js';
4
+ import { F as FieldFormatConfig } from './types-CJWU1Gae.js';
5
+ import { GroupAggregateSpec, GroupOrder } from './data-table/grouping.js';
6
+ import { CellPatch } from './data-table/fill.js';
7
+ import { ColumnFacets } from './data-table/facets.js';
8
+ import { SpreadsheetIntent } from './data-table/spreadsheet.js';
5
9
 
6
10
  type TableSearchRole = "id" | "name" | "description" | "other";
7
11
  interface TableSearchScope {
@@ -900,9 +904,49 @@ interface MatrxDataTableExpandedDetailConfig<T> {
900
904
  render: (row: T, controls: MatrxDataTableRecordControls) => ReactNode;
901
905
  className?: string;
902
906
  }
903
- /** Opaque host-owned row menu descriptor. The package never interprets it. */
907
+ /**
908
+ * THE FOUR LEVELS of a right-click, as opaque host-owned descriptors.
909
+ *
910
+ * A spreadsheet's right-click menu is four menus, chosen by where the pointer
911
+ * was: the CELL, the ROW it sits in, the COLUMN it sits under, and — when the
912
+ * click lands inside an extended range — the SELECTION. The canonical table
913
+ * used to expose exactly one of them, which is why every surface that wanted
914
+ * the other three forked the table.
915
+ *
916
+ * Each resolver is optional and each returns something the package never
917
+ * interprets; the host builds its menu from it. A level with no resolver here
918
+ * falls back to the host's own `createDefaultMenuContext`, so a table that
919
+ * declares nothing still gets all four through the platform's universal menu.
920
+ */
904
921
  interface MatrxDataTableContextMenuConfig<T> {
905
- resolveRowContext: (row: T, controls: MatrxDataTableRecordControls) => unknown;
922
+ resolveRowContext?: (row: T, controls: MatrxDataTableRecordControls) => unknown;
923
+ resolveCellContext?: (args: {
924
+ row: T;
925
+ rowId: string;
926
+ columnId: string;
927
+ columnLabel: string;
928
+ value: unknown;
929
+ /** The cell's value as text, for the menu's own Copy. */
930
+ text: string;
931
+ controls: MatrxDataTableRecordControls;
932
+ }) => unknown;
933
+ resolveColumnContext?: (args: {
934
+ columnId: string;
935
+ columnLabel: string;
936
+ /** Every rendered row's value for this column, in visual order. */
937
+ values: unknown[];
938
+ }) => unknown;
939
+ resolveSelectionContext?: (args: {
940
+ rowIds: string[];
941
+ columnIds: string[];
942
+ /** The selected rectangle as an Excel/Sheets-compatible TSV block. */
943
+ tsv: string;
944
+ cellCount: number;
945
+ }) => unknown;
946
+ resolveTableContext?: (args: {
947
+ rowCount: number;
948
+ columnIds: string[];
949
+ }) => unknown;
906
950
  }
907
951
  interface MatrxDataTableWindowConfig<T> {
908
952
  /** Window title. */
@@ -959,6 +1003,24 @@ interface MatrxDataTableEditConfig<T> {
959
1003
  autoSave?: boolean;
960
1004
  /** Optional cancel hook (draft already discarded). */
961
1005
  onCancel?: () => void;
1006
+ /**
1007
+ * Judge a value the person MEANT to write, before it enters the draft.
1008
+ *
1009
+ * Return a sentence to REFUSE it: the editor stays open, the value is not
1010
+ * taken, and the sentence is shown IN THE CELL, where the person is standing.
1011
+ * Return undefined to accept. Excel's posture, and the reason is the product —
1012
+ * "Amount must be at least 0" is a thing a person can act on; a silent revert
1013
+ * is not.
1014
+ *
1015
+ * It never judges what is already stored: a value that predates the rule is
1016
+ * legal, is never rewritten, and is the point of declaring a rule at all.
1017
+ */
1018
+ validate?: ((args: {
1019
+ row: T;
1020
+ rowId: string;
1021
+ columnId: string;
1022
+ value: unknown;
1023
+ }) => string | undefined) | undefined;
962
1024
  }
963
1025
  interface MatrxDataTableHierarchyConfig<T> {
964
1026
  /** Complete hierarchy when `data` is only the current controlled page. */
@@ -1064,6 +1126,186 @@ interface MatrxDataTableStyleConfig<T> {
1064
1126
  */
1065
1127
  choiceColorFor?: ChoiceColorLookup;
1066
1128
  }
1129
+ /**
1130
+ * GROUPING — "group these rows by Status" with collapsible sections, a count
1131
+ * and a subtotal on each.
1132
+ *
1133
+ * This is the primitive's answer to the biggest gap against Airtable, Google
1134
+ * Sheets and Notion. It is opt-in, and it is CONTROLLED for the two pieces of
1135
+ * state a person expects to survive: which column groups, and which sections
1136
+ * are shut. Both are ordinary values, so a saved view carries them like any
1137
+ * other reading of the table.
1138
+ *
1139
+ * Grouping runs AFTER local processing and over the rows the table is about to
1140
+ * render. It never re-sorts inside a group: the sort the person chose is the
1141
+ * order they see.
1142
+ */
1143
+ interface MatrxDataTableGroupingConfig<T> {
1144
+ /** Column id to group by. `null` means grouping is available but off. */
1145
+ columnId: string | null;
1146
+ /** Emitted when the person picks a different column (or clears it). */
1147
+ onColumnIdChange?: ((columnId: string | null) => void) | undefined;
1148
+ /**
1149
+ * Columns offered in the group-by control. Defaults to every visible column
1150
+ * that is not an actions column. A column with thousands of distinct values
1151
+ * is a bad grouping and the consumer is the only one who knows that.
1152
+ */
1153
+ groupableColumnIds?: string[] | undefined;
1154
+ /** Per-column subtotal shown on every group header, keyed by column id. */
1155
+ aggregates?: Record<string, GroupAggregateSpec> | undefined;
1156
+ /** Controlled collapsed group keys (`TableGroup.key`). */
1157
+ collapsed?: string[] | undefined;
1158
+ onCollapsedChange?: ((collapsed: string[]) => void) | undefined;
1159
+ /** Section order. Default `"value-asc"`; blanks always sort last. */
1160
+ order?: GroupOrder | undefined;
1161
+ /** Label for the section of rows whose grouping value is blank. */
1162
+ emptyLabel?: string | undefined;
1163
+ /** Singular noun for a group's summary ("job" → "12 jobs"). */
1164
+ rowNoun?: string | undefined;
1165
+ /**
1166
+ * Reads the grouping value for a column. Defaults to the column's own
1167
+ * accessor.
1168
+ *
1169
+ * NOT named `valueOf`: every object in JavaScript already has one, so an
1170
+ * optional config field by that name is always "present" and a `config.valueOf
1171
+ * ? … : …` check silently calls `Object.prototype.valueOf` and groups every
1172
+ * row under the config object itself. It did exactly that here once.
1173
+ */
1174
+ readCell?: ((row: T, columnId: string) => unknown) | undefined;
1175
+ }
1176
+ /**
1177
+ * VIRTUALIZATION — render the rows a person can actually see.
1178
+ *
1179
+ * Off by default, because most tables in the product are short and a windowed
1180
+ * body costs a scroll container contract that a short table does not need.
1181
+ * On, it engages only past `threshold` rows, so a 40-row table behaves exactly
1182
+ * as it always did and a 100,000-row table scrolls.
1183
+ */
1184
+ interface MatrxDataTableVirtualizeConfig {
1185
+ enabled?: boolean | undefined;
1186
+ /** Row height in px used for the window math. Default follows density. */
1187
+ rowHeight?: number | undefined;
1188
+ /** Rows rendered beyond each edge of the viewport. Default 8. */
1189
+ overscan?: number | undefined;
1190
+ /** Row count below which the body renders whole. Default 150. */
1191
+ threshold?: number | undefined;
1192
+ /**
1193
+ * Window the COLUMNS too, for tables hundreds of columns wide. Frozen
1194
+ * columns are always rendered, never windowed out.
1195
+ */
1196
+ columns?: boolean | undefined;
1197
+ /** Column width in px used for the horizontal window. Default 160. */
1198
+ columnWidth?: number | undefined;
1199
+ }
1200
+ /**
1201
+ * THE HONEST COUNT.
1202
+ *
1203
+ * A table that filters over part of its rows and says nothing is a screen that
1204
+ * lies. Declare what the answer actually covers and the table states it, in
1205
+ * plain English, above the rows — and never presents a partial count as exact.
1206
+ *
1207
+ * Omit this entirely and the table says nothing, which is correct for a table
1208
+ * whose rows are all in hand.
1209
+ */
1210
+ interface MatrxDataTableCoverageConfig {
1211
+ /** Rows the source says match the current query, when it can say. */
1212
+ matched?: number | undefined;
1213
+ /** Rows in the table irrespective of the query, when known. */
1214
+ total?: number | undefined;
1215
+ /** A ceiling the source imposed on this answer (a fetch cap). */
1216
+ cap?: number | undefined;
1217
+ /** Who answered the filters and sort. Default `"client"`. */
1218
+ answeredBy?: "source" | "client" | undefined;
1219
+ /** Singular noun for the sentence. Default "row". */
1220
+ noun?: string | undefined;
1221
+ }
1222
+ /**
1223
+ * SPREADSHEET GESTURES — range selection, Excel paste, fill, undo.
1224
+ *
1225
+ * The `/data` grid had all of this and nothing else in the product could reach
1226
+ * it. Opt-in, because a reference table with one editable column does not want
1227
+ * a cell cursor; a table a person works IN does.
1228
+ *
1229
+ * The table owns the gestures and the selection rectangle; the CONSUMER owns
1230
+ * the writes. Every mutation arrives as a list of `CellPatch`, so the caller
1231
+ * applies it through the door it already has and the table never invents a
1232
+ * persistence path of its own. That is also what makes undo honest: the stack
1233
+ * holds before-values, and an undo is just another patch list.
1234
+ */
1235
+ interface MatrxDataTableSpreadsheetConfig<T> {
1236
+ enabled?: boolean | undefined;
1237
+ /**
1238
+ * Apply cell writes. Called for a paste, a fill-down, a fill-handle drag, a
1239
+ * clear, an undo and a redo. The table re-reads from `data`, so a write the
1240
+ * caller refuses simply never appears.
1241
+ */
1242
+ onPatch: (patches: CellPatch[], intent: SpreadsheetIntent) => void | Promise<void>;
1243
+ /** Column ids a patch may target. Defaults to every column marked `editable`. */
1244
+ writableColumnIds?: string[] | undefined;
1245
+ /** Why a column refuses a write, as a sentence a person can act on. */
1246
+ columnRefusal?: ((columnId: string) => string | undefined) | undefined;
1247
+ /** Reads a cell for copy and fill. Defaults to the column's own accessor. */
1248
+ readCell?: ((row: T, columnId: string) => unknown) | undefined;
1249
+ /** The little square at the corner of a selection. Default true. */
1250
+ fillHandle?: boolean | undefined;
1251
+ /** Paste real Excel/Sheets TSV into the selection. Default true. */
1252
+ paste?: boolean | undefined;
1253
+ /** Undo depth. 0 turns undo off. Default 100. */
1254
+ undoDepth?: number | undefined;
1255
+ /** Says what just happened, and why something did not. */
1256
+ notify?: ((message: string) => void) | undefined;
1257
+ /**
1258
+ * Judge ONE value before it is written, for a paste, a fill or a clear.
1259
+ *
1260
+ * Return a sentence to REFUSE it — the cell is left alone and the sentence is
1261
+ * what the person is told. Return undefined to accept. This is the same rule
1262
+ * the inline editor enforces (`edit.validate`), asked in the one other place
1263
+ * a value can enter a cell, because a rule a paste can walk around is not a
1264
+ * rule.
1265
+ */
1266
+ validateCell?: ((args: {
1267
+ rowId: string;
1268
+ columnId: string;
1269
+ value: unknown;
1270
+ }) => string | undefined) | undefined;
1271
+ }
1272
+ /**
1273
+ * THE VALUES A COLUMN HOLDS, with a count beside each one.
1274
+ *
1275
+ * A filter that only offers "Contains…" asks the reader to already know what is
1276
+ * in the column. The checklist tells them, and the counts are what make it
1277
+ * usable: "Blocked 12" is a decision, "Blocked" is a guess.
1278
+ *
1279
+ * THE HONESTY RULE: counts are computed in the browser ONLY when the browser
1280
+ * holds every row they describe. Otherwise `source` is asked — the database
1281
+ * counts over the whole column — and if there is no source, the popover SAYS
1282
+ * the counts cover only the loaded rows. A partial count is never printed as if
1283
+ * it were the whole truth.
1284
+ */
1285
+ interface MatrxDataTableFacetsConfig<T> {
1286
+ enabled?: boolean | undefined;
1287
+ /**
1288
+ * Database-computed values and counts for one column. Called only when the
1289
+ * rows in the browser do not cover `totalRows` — local-data-first is the same
1290
+ * rule for every source. Return null (or throw) and the popover says so and
1291
+ * falls back to matching text.
1292
+ */
1293
+ source?: ((args: {
1294
+ columnId: string;
1295
+ search: string;
1296
+ limit: number;
1297
+ }) => Promise<ColumnFacets | null>) | undefined;
1298
+ /**
1299
+ * Rows the whole table holds after the active search. This is how the table
1300
+ * knows whether the rows it has are all of them. Omit it and the table treats
1301
+ * its own rows as a partial answer and says so.
1302
+ */
1303
+ totalRows?: number | undefined;
1304
+ /** Most values to list. Default 200, hard ceiling 500. */
1305
+ limit?: number | undefined;
1306
+ /** Reads a cell for counting. Defaults to the column's own filter accessor. */
1307
+ readCell?: ((row: T, columnId: string) => unknown) | undefined;
1308
+ }
1067
1309
  interface MatrxDataTableProps<T> {
1068
1310
  data: T[];
1069
1311
  columns: MatrxColumnDef<T>[];
@@ -1193,6 +1435,28 @@ interface MatrxDataTableProps<T> {
1193
1435
  * person edits it.
1194
1436
  */
1195
1437
  tableStyle?: MatrxDataTableStyleConfig<T>;
1438
+ /**
1439
+ * GROUPS — collapsible sections by a column, with a count and per-column
1440
+ * subtotals on each header. Opt-in and controlled; see the config's own doc.
1441
+ */
1442
+ grouping?: MatrxDataTableGroupingConfig<T>;
1443
+ /** Render only the rows in view. Opt-in; engages past its own threshold. */
1444
+ virtualize?: boolean | MatrxDataTableVirtualizeConfig;
1445
+ /**
1446
+ * What the current answer actually covers. Declare it and the table says so
1447
+ * on screen when a filter is answering over part of the table, instead of
1448
+ * presenting a partial count as the whole truth.
1449
+ */
1450
+ coverage?: MatrxDataTableCoverageConfig;
1451
+ /** Range selection, Excel paste, fill handle, fill-down, undo. Opt-in. */
1452
+ spreadsheet?: MatrxDataTableSpreadsheetConfig<T>;
1453
+ /** Real values with their counts in every column filter popover. */
1454
+ facets?: MatrxDataTableFacetsConfig<T>;
1455
+ /**
1456
+ * Explicit body row height in px. Overrides the density default. Use it when
1457
+ * a saved view remembers a row height the person chose.
1458
+ */
1459
+ rowHeight?: number;
1196
1460
  /** Highlight an index in the currently rendered page without selecting it. */
1197
1461
  highlightedIndex?: number | undefined;
1198
1462
  /** Receives the complete visible local view after local processing, before paging. Attaching after absence receives one snapshot; callback identity changes alone do not notify. */
@@ -1258,4 +1522,4 @@ interface MatrxDataTableProps<T> {
1258
1522
  onRowOpen?: (row: T) => void;
1259
1523
  }
1260
1524
 
1261
- export { TABLE_STYLE_METADATA_KEY as $, type AnyOfColumnSearch as A, BOOLEAN_TRUE_COLOR as B, type ColumnFiltersState as C, type MatrxDataTableHierarchyMove as D, EMPTY_TABLE_STYLE as E, type MatrxDataTableMobileCardControls as F, type MatrxDataTableQueryControl as G, type MatrxDataTableSelectionConfig as H, type MatrxDataTableSourceProcessing as I, type MatrxDataTableStyleConfig as J, type MatrxDataTableToolbar as K, type LayeredFilterRule as L, type MatrxColumnDef as M, type MatrxDataTableUrlStateConfig as N, type MatrxDataTableWindowConfig as O, type MatrxTablePagination as P, OPS_WITH_VALUE as Q, ROW_TINT_CLASS as R, type SortState as S, type TableSearchMatchMode as T, STYLE_COLORS as U, STYLE_COLOR_LABELS as V, SWATCH_CLASS as W, type SortDirection as X, type StyleColor as Y, type StylePath as Z, type StyleTarget as _, type ColumnFilterValue as a, TABLE_STYLE_VERSION as a0, type TableRankedSearchOptions as a1, type TableSearchRole as a2, type TableSearchScope as a3, type TableStyle as a4, type TableViewQuery as a5, type TextFilterMode as a6, type ToolbarFacet as a7, applyStylePath as a8, colorForChoice as a9, evaluateRule as aa, inferTableSearchRole as ab, isStyleColor as ac, parseTableStyle as ad, rankTableSearchRows as ae, resolveCellColor as af, resolveRowColor as ag, resolveTableSearchScopes as ah, stylePath as ai, tableStyleFromMetadata as aj, tableStyleIsEmpty as ak, LAYERED_FILTER_OPERATORS as al, LAYERED_FILTER_OPERATOR_LABELS as am, type LayeredFilterField as an, type LayeredFilterOperator as ao, columnFiltersToLayeredRules as ap, completeLayeredFilterRules as aq, decodeLayeredFilterRules as ar, encodeLayeredFilterRules as as, isCompleteLayeredFilterRule as at, isLayeredFilterOperator as au, layeredFilterMatchesValue as av, layeredFilterNeedsValue as aw, layeredFilterRuleSummary as ax, operatorsForLayeredField as ay, type TableSavedViewsProps as b, type MatrxDataTableRecordControls as c, type MatrxDataTableDensity as d, type MatrxDataTableQueryState as e, type ColumnFilterKind as f, type MatrxDataTableCopyConfig as g, type MatrxDataTableProps as h, type TableViewSnapshot as i, type ColumnMarker as j, CELL_TINT_CLASS as k, COLOR_RULE_OPS as l, COLOR_RULE_OP_LABELS as m, type CellEditType as n, type CellEditsMap as o, type ChoiceColorLookup as p, type ColorBy as q, type ColorRule as r, type ColorRuleOp as s, type MatrxDataTableContextMenuConfig as t, type MatrxDataTableDetailConfig as u, type MatrxDataTableEditConfig as v, type MatrxDataTableEmptyState as w, type MatrxDataTableExpandedDetailConfig as x, type MatrxDataTableFilterUi as y, type MatrxDataTableHierarchyConfig as z };
1525
+ export { SWATCH_CLASS as $, type AnyOfColumnSearch as A, BOOLEAN_TRUE_COLOR as B, type ColumnFiltersState as C, type MatrxDataTableFilterUi as D, EMPTY_TABLE_STYLE as E, type MatrxDataTableGroupingConfig as F, type MatrxDataTableHierarchyConfig as G, type MatrxDataTableHierarchyMove as H, type MatrxDataTableMobileCardControls as I, type MatrxDataTableQueryControl as J, type MatrxDataTableSelectionConfig as K, type LayeredFilterRule as L, type MatrxColumnDef as M, type MatrxDataTableSourceProcessing as N, type MatrxDataTableSpreadsheetConfig as O, type MatrxDataTableStyleConfig as P, type MatrxDataTableToolbar as Q, type MatrxDataTableUrlStateConfig as R, type SortState as S, type TableSearchMatchMode as T, type MatrxDataTableVirtualizeConfig as U, type MatrxDataTableWindowConfig as V, type MatrxTablePagination as W, OPS_WITH_VALUE as X, ROW_TINT_CLASS as Y, STYLE_COLORS as Z, STYLE_COLOR_LABELS as _, type ColumnFilterValue as a, type SortDirection as a0, type StyleColor as a1, type StylePath as a2, type StyleTarget as a3, TABLE_STYLE_METADATA_KEY as a4, TABLE_STYLE_VERSION as a5, type TableRankedSearchOptions as a6, type TableSearchRole as a7, type TableSearchScope as a8, type TableStyle as a9, layeredFilterMatchesValue as aA, layeredFilterNeedsValue as aB, layeredFilterRuleSummary as aC, operatorsForLayeredField as aD, type TableViewQuery as aa, type TextFilterMode as ab, type ToolbarFacet as ac, applyStylePath as ad, colorForChoice as ae, evaluateRule as af, inferTableSearchRole as ag, isStyleColor as ah, parseTableStyle as ai, rankTableSearchRows as aj, resolveCellColor as ak, resolveRowColor as al, resolveTableSearchScopes as am, stylePath as an, tableStyleFromMetadata as ao, tableStyleIsEmpty as ap, LAYERED_FILTER_OPERATORS as aq, LAYERED_FILTER_OPERATOR_LABELS as ar, type LayeredFilterField as as, type LayeredFilterOperator as at, columnFiltersToLayeredRules as au, completeLayeredFilterRules as av, decodeLayeredFilterRules as aw, encodeLayeredFilterRules as ax, isCompleteLayeredFilterRule as ay, isLayeredFilterOperator as az, type TableSavedViewsProps as b, type MatrxDataTableDensity as c, type MatrxDataTableRecordControls as d, type MatrxDataTableQueryState as e, type ColumnFilterKind as f, type MatrxDataTableCopyConfig as g, type MatrxDataTableProps as h, type TableViewSnapshot as i, type ColumnMarker as j, CELL_TINT_CLASS as k, COLOR_RULE_OPS as l, COLOR_RULE_OP_LABELS as m, type CellEditType as n, type CellEditsMap as o, type ChoiceColorLookup as p, type ColorBy as q, type ColorRule as r, type ColorRuleOp as s, type MatrxDataTableContextMenuConfig as t, type MatrxDataTableCoverageConfig as u, type MatrxDataTableDetailConfig as v, type MatrxDataTableEditConfig as w, type MatrxDataTableEmptyState as x, type MatrxDataTableExpandedDetailConfig as y, type MatrxDataTableFacetsConfig as z };