@ai-matrx/design-system 0.21.14 → 0.25.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 (97) hide show
  1. package/CHANGELOG.md +188 -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 +6 -1
  9. package/dist/data-table/copy-helpers.d.ts +6 -1
  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/fill.cjs +299 -0
  17. package/dist/data-table/fill.cjs.map +1 -0
  18. package/dist/data-table/fill.d.cts +129 -0
  19. package/dist/data-table/fill.d.ts +129 -0
  20. package/dist/data-table/fill.js +277 -0
  21. package/dist/data-table/fill.js.map +1 -0
  22. package/dist/data-table/filter-engine.d.cts +6 -1
  23. package/dist/data-table/filter-engine.d.ts +6 -1
  24. package/dist/data-table/grid-clipboard.cjs +165 -0
  25. package/dist/data-table/grid-clipboard.cjs.map +1 -0
  26. package/dist/data-table/grid-clipboard.d.cts +85 -0
  27. package/dist/data-table/grid-clipboard.d.ts +85 -0
  28. package/dist/data-table/grid-clipboard.js +145 -0
  29. package/dist/data-table/grid-clipboard.js.map +1 -0
  30. package/dist/data-table/grid-selection.cjs +258 -0
  31. package/dist/data-table/grid-selection.cjs.map +1 -0
  32. package/dist/data-table/grid-selection.d.cts +166 -0
  33. package/dist/data-table/grid-selection.d.ts +166 -0
  34. package/dist/data-table/grid-selection.js +238 -0
  35. package/dist/data-table/grid-selection.js.map +1 -0
  36. package/dist/data-table/grouping.cjs +302 -0
  37. package/dist/data-table/grouping.cjs.map +1 -0
  38. package/dist/data-table/grouping.d.cts +119 -0
  39. package/dist/data-table/grouping.d.ts +119 -0
  40. package/dist/data-table/grouping.js +282 -0
  41. package/dist/data-table/grouping.js.map +1 -0
  42. package/dist/data-table/host.d.cts +6 -1
  43. package/dist/data-table/host.d.ts +6 -1
  44. package/dist/data-table/index.cjs +4120 -806
  45. package/dist/data-table/index.cjs.map +1 -1
  46. package/dist/data-table/index.d.cts +99 -3
  47. package/dist/data-table/index.d.ts +99 -3
  48. package/dist/data-table/index.js +3971 -653
  49. package/dist/data-table/index.js.map +1 -1
  50. package/dist/data-table/infer-filter.d.cts +6 -1
  51. package/dist/data-table/infer-filter.d.ts +6 -1
  52. package/dist/data-table/layered-filters.d.cts +6 -1
  53. package/dist/data-table/layered-filters.d.ts +6 -1
  54. package/dist/data-table/query-control.d.cts +6 -1
  55. package/dist/data-table/query-control.d.ts +6 -1
  56. package/dist/data-table/spreadsheet.cjs +1014 -0
  57. package/dist/data-table/spreadsheet.cjs.map +1 -0
  58. package/dist/data-table/spreadsheet.d.cts +118 -0
  59. package/dist/data-table/spreadsheet.d.ts +118 -0
  60. package/dist/data-table/spreadsheet.js +994 -0
  61. package/dist/data-table/spreadsheet.js.map +1 -0
  62. package/dist/data-table/types.cjs.map +1 -1
  63. package/dist/data-table/types.d.cts +6 -1
  64. package/dist/data-table/types.d.ts +6 -1
  65. package/dist/data-table/url-state.d.cts +6 -1
  66. package/dist/data-table/url-state.d.ts +6 -1
  67. package/dist/data-table/validation.cjs +229 -0
  68. package/dist/data-table/validation.cjs.map +1 -0
  69. package/dist/data-table/validation.d.cts +125 -0
  70. package/dist/data-table/validation.d.ts +125 -0
  71. package/dist/data-table/validation.js +209 -0
  72. package/dist/data-table/validation.js.map +1 -0
  73. package/dist/data-table/virtual-window.cjs +203 -0
  74. package/dist/data-table/virtual-window.cjs.map +1 -0
  75. package/dist/data-table/virtual-window.d.cts +84 -0
  76. package/dist/data-table/virtual-window.d.ts +84 -0
  77. package/dist/data-table/virtual-window.js +183 -0
  78. package/dist/data-table/virtual-window.js.map +1 -0
  79. package/dist/data-table/xlsx.d.cts +6 -1
  80. package/dist/data-table/xlsx.d.ts +6 -1
  81. package/dist/field-formats/index.cjs +741 -0
  82. package/dist/field-formats/index.cjs.map +1 -0
  83. package/dist/field-formats/index.d.cts +45 -0
  84. package/dist/field-formats/index.d.ts +45 -0
  85. package/dist/field-formats/index.js +723 -0
  86. package/dist/field-formats/index.js.map +1 -0
  87. package/dist/formulas/index.cjs +1041 -0
  88. package/dist/formulas/index.cjs.map +1 -0
  89. package/dist/formulas/index.d.cts +389 -0
  90. package/dist/formulas/index.d.ts +389 -0
  91. package/dist/formulas/index.js +1019 -0
  92. package/dist/formulas/index.js.map +1 -0
  93. package/dist/types-CJWU1Gae.d.cts +204 -0
  94. package/dist/types-CJWU1Gae.d.ts +204 -0
  95. package/dist/{types-BgqIKbhq.d.cts → types-CmQrOz-T.d.cts} +382 -1
  96. package/dist/{types-emLxze_m.d.ts → types-nlLCMiqd.d.ts} +382 -1
  97. package/package.json +119 -2
@@ -1,6 +1,10 @@
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 { 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 { SpreadsheetIntent } from './data-table/spreadsheet.js';
4
8
 
5
9
  type TableSearchRole = "id" | "name" | "description" | "other";
6
10
  interface TableSearchScope {
@@ -80,6 +84,151 @@ declare function columnFiltersToLayeredRules(filters: ColumnFiltersState, fieldI
80
84
  declare function layeredFilterMatchesValue(value: unknown, rule: LayeredFilterRule): boolean;
81
85
  declare function layeredFilterRuleSummary(rule: LayeredFilterRule, fields: readonly LayeredFilterField[]): string;
82
86
 
87
+ /**
88
+ * Table style — colors on a user data table, the Airtable way.
89
+ *
90
+ * WHAT IS ALLOWED, AND WHY ONLY THIS. A typed dataset is not a canvas; the
91
+ * platform already has Workbooks (Univer) for arbitrary per-cell formatting.
92
+ * Here, color carries MEANING and stays out of the data:
93
+ *
94
+ * 1. COLOR BY A COLUMN — a choice column's option colors (or a boolean's
95
+ * true/false) tint the row or that column's cells. Zero configuration:
96
+ * the Status column already knows "Blocked" is red.
97
+ * 2. RULES — "when Amount > 1000, tint the row amber". Evaluated live, so a
98
+ * tint is never stale.
99
+ * 3. MANUAL HIGHLIGHTS — a small palette on a cell, a row, or a column, for
100
+ * the "look at this one" gesture. The same palette the choice chips use,
101
+ * so the table never has two vocabularies of color.
102
+ *
103
+ * WHERE IT LIVES. This module is the MODEL only — pure, no React, no DOM, no
104
+ * network. Each consumer names its own home for the same document and writes it
105
+ * surgically BY PATH (`stylePath.*`), so two editors highlighting different
106
+ * cells never overwrite each other:
107
+ *
108
+ * - user data tables (`/data`): `udt_datasets.metadata.style`, written by
109
+ * `public.udt_set_table_style(p_table_id, p_path, p_value)`.
110
+ * - the record store: the table's saved view / Table settings document,
111
+ * written by `custom.table_style_set(p_table, p_path, p_value)`.
112
+ *
113
+ * Copy, export, agents and the data itself never see the style.
114
+ *
115
+ * PRECEDENCE (most deliberate wins): a manual cell highlight > a cell rule >
116
+ * color-by targeting cells > a manual column highlight, for the CELL; a manual
117
+ * row highlight > a row rule > color-by targeting rows, for the ROW. A cell
118
+ * tint always paints over the row tint underneath it.
119
+ *
120
+ * Pure module: no React, no DOM, no Supabase.
121
+ */
122
+ declare const TABLE_STYLE_VERSION: 1;
123
+ /**
124
+ * The highlight palette.
125
+ *
126
+ * These are the SAME seven names the choice-chip palette uses minus "neutral"
127
+ * (which means "no color"), spelled out here so the model carries no import at
128
+ * all. Names, never raw hex: a name is legible on both grounds because each one
129
+ * ships its own `dark:` class below, and a retired name degrades to "no color"
130
+ * instead of to an unreadable swatch. A consumer that also draws choice chips
131
+ * must keep its chip palette on these names, or the table grows two vocabularies
132
+ * of color.
133
+ */
134
+ declare const STYLE_COLORS: readonly ["slate", "green", "amber", "red", "blue", "violet", "teal"];
135
+ type StyleColor = (typeof STYLE_COLORS)[number];
136
+ declare function isStyleColor(v: unknown): v is StyleColor;
137
+ declare const COLOR_RULE_OPS: readonly ["is", "is_not", "contains", "is_empty", "not_empty", "is_true", "is_false", "gt", "gte", "lt", "lte"];
138
+ type ColorRuleOp = (typeof COLOR_RULE_OPS)[number];
139
+ /** Which ops take a value to compare against. */
140
+ declare const OPS_WITH_VALUE: readonly ColorRuleOp[];
141
+ declare const COLOR_RULE_OP_LABELS: Record<ColorRuleOp, string>;
142
+ type StyleTarget = "row" | "cell";
143
+ type ColorRule = {
144
+ id: string;
145
+ /** Machine field name the condition reads. */
146
+ field: string;
147
+ op: ColorRuleOp;
148
+ /** Compared as text for `is` / `is_not` / `contains`, as a number for gt/lt. */
149
+ value?: string;
150
+ color: StyleColor;
151
+ /** Tint the whole row, or only this column's cell. */
152
+ target: StyleTarget;
153
+ };
154
+ type ColorBy = {
155
+ field: string;
156
+ target: StyleTarget;
157
+ };
158
+ type TableStyle = {
159
+ version: typeof TABLE_STYLE_VERSION;
160
+ colorBy?: ColorBy | null;
161
+ rules?: ColorRule[];
162
+ /** Manual row highlights, by row id. */
163
+ rows?: Record<string, StyleColor>;
164
+ /** Manual cell highlights, by row id then field name. */
165
+ cells?: Record<string, Record<string, StyleColor>>;
166
+ /** Manual column highlights, by field name. */
167
+ columns?: Record<string, StyleColor>;
168
+ };
169
+ declare const EMPTY_TABLE_STYLE: TableStyle;
170
+ /** The conventional key the style document lives under on a table's own metadata. */
171
+ declare const TABLE_STYLE_METADATA_KEY = "style";
172
+ /** True when nothing in the style would paint anything. */
173
+ declare function tableStyleIsEmpty(style: TableStyle): boolean;
174
+ /**
175
+ * Read a style off table metadata. Tolerant by design: an unknown key, a
176
+ * retired color name or a malformed rule is dropped, never thrown, because a
177
+ * table must still render when its style is stale.
178
+ */
179
+ declare function parseTableStyle(raw: unknown): TableStyle;
180
+ /** Style out of a table-metadata blob (or null / undefined). */
181
+ declare function tableStyleFromMetadata(metadata: unknown): TableStyle;
182
+ /** Does `value` satisfy `rule`? Text ops compare case-insensitively; number ops parse both sides. */
183
+ declare function evaluateRule(rule: ColorRule, value: unknown): boolean;
184
+ /**
185
+ * The color a choice / boolean column gives a value, for color-by. The caller
186
+ * supplies the lookup because option colors live on the resolved choice list
187
+ * (which may come from a structured list), not on the style.
188
+ */
189
+ type ChoiceColorLookup = (fieldName: string, value: unknown) => StyleColor | undefined;
190
+ /**
191
+ * The color an option paints with. An option that DECLARED a color uses it; an
192
+ * option without one gets a stable color from its position in the list, so
193
+ * "color rows by Status" always paints something — Airtable assigns every
194
+ * option a color for the same reason. A column whose options were never
195
+ * colored would otherwise look like color-by did nothing at all.
196
+ */
197
+ declare function colorForChoice(choices: readonly {
198
+ value: string;
199
+ color?: string;
200
+ }[] | undefined, value: unknown): StyleColor | undefined;
201
+ /**
202
+ * Boolean columns have no option list; color-by paints true green and leaves
203
+ * false untinted, which is the "done / not done" reading every checklist has.
204
+ */
205
+ declare const BOOLEAN_TRUE_COLOR: StyleColor;
206
+ /** The tint for a whole row, or null for none. */
207
+ declare function resolveRowColor(style: TableStyle, row: {
208
+ id: string;
209
+ data: Record<string, unknown>;
210
+ }, choiceColorFor: ChoiceColorLookup): StyleColor | null;
211
+ /** The tint for one cell (painted over the row tint), or null for none. */
212
+ declare function resolveCellColor(style: TableStyle, row: {
213
+ id: string;
214
+ data: Record<string, unknown>;
215
+ }, fieldName: string, choiceColorFor: ChoiceColorLookup): StyleColor | null;
216
+ declare const ROW_TINT_CLASS: Record<StyleColor, string>;
217
+ declare const CELL_TINT_CLASS: Record<StyleColor, string>;
218
+ /** A solid swatch for palette menus. */
219
+ declare const SWATCH_CLASS: Record<StyleColor, string>;
220
+ declare const STYLE_COLOR_LABELS: Record<StyleColor, string>;
221
+ type StylePath = readonly string[];
222
+ declare const stylePath: {
223
+ colorBy: () => StylePath;
224
+ rules: () => StylePath;
225
+ row: (rowId: string) => StylePath;
226
+ cell: (rowId: string, fieldName: string) => StylePath;
227
+ column: (fieldName: string) => StylePath;
228
+ };
229
+ /** Apply a path write locally, so the grid repaints before the round trip returns. */
230
+ declare function applyStylePath(style: TableStyle, path: StylePath, value: unknown): TableStyle;
231
+
83
232
  /** How a column's filter UI behaves. `auto` infers from sample values. */
84
233
  type ColumnFilterKind = "auto" | "text" | "select" | "boolean" | "number" | false;
85
234
  type SortDirection = "asc" | "desc";
@@ -259,6 +408,36 @@ interface MatrxColumnDef<T> {
259
408
  /** Force non-navigable for this column. */
260
409
  forbidden?: boolean | ((id: string, row: T) => boolean);
261
410
  };
411
+ /**
412
+ * How this column's value READS — money, percent, a date style, decimal
413
+ * places. The vocabulary is the platform's one field-format registry
414
+ * (`@ai-matrx/design-system/field-formats`), the same `{id, options}` a user
415
+ * data table stores on its column and the record store stores on its Field,
416
+ * so `$1,234.56` is the same `$1,234.56` on every table in the product.
417
+ *
418
+ * A declarative format, not another render function, because a `cell`
419
+ * callback is invisible to everything else: with `format` the column also
420
+ * right-aligns itself, and a consumer's own `cell` asks the primitive for the
421
+ * same text through `formatColumnValue(col, value)` rather than growing a
422
+ * second money formatter beside this one.
423
+ *
424
+ * THE FALLBACK LAW travels with it: a value that does not fit the declared
425
+ * format is never blanked and never throws — it renders its base-type text in
426
+ * amber with the reason on hover, so the person SEES the mismatch.
427
+ *
428
+ * `cell` still wins when both are set.
429
+ */
430
+ format?: FieldFormatConfig;
431
+ /**
432
+ * Keep this column in place while the rest of the table scrolls sideways.
433
+ *
434
+ * Only the LEADING RUN freezes, only from `sm:` up, and only when the widths
435
+ * the offsets need are known — `./frozen-columns.ts` says why each of those
436
+ * three is a rule and not a default. Give a frozen column an explicit
437
+ * `width`; without one the table declines to freeze anything rather than pin
438
+ * a column at a guessed offset.
439
+ */
440
+ frozen?: boolean;
262
441
  className?: string;
263
442
  headerClassName?: string;
264
443
  width?: string | number;
@@ -579,6 +758,17 @@ interface TableViewSnapshot {
579
758
  hidden: string[];
580
759
  widths?: Record<string, number>;
581
760
  };
761
+ /**
762
+ * The view's COLORS — color-by-a-column, rules, and manual highlights
763
+ * (`./table-style.ts`). Optional so every view saved before colors existed
764
+ * still parses; absent means "this view paints nothing".
765
+ *
766
+ * Colors belong to the VIEW and not to the data, for the same reason the
767
+ * filters do: two people can read one table two ways, and neither reading is
768
+ * a fact about the records. That is also why this snapshot is the only place
769
+ * a style is carried — copy, export and agents read the rows, never this.
770
+ */
771
+ style?: TableStyle;
582
772
  }
583
773
  /** Host port for durable named views. The host owns actor, tenancy and CRUD. */
584
774
  interface TableSavedViewsProps {
@@ -855,6 +1045,157 @@ interface MatrxDataTableMobileCardControls {
855
1045
  expandedDetail: ReactNode;
856
1046
  }
857
1047
  type MatrxDataTableDensity = "condensed" | "normal" | "spacious";
1048
+ /**
1049
+ * What the table needs in order to paint a `TableStyle` itself.
1050
+ *
1051
+ * `fieldOf` exists because a style names DATA fields ("tint the row when
1052
+ * `status` is Blocked") while the table addresses COLUMNS. In the common case
1053
+ * the column id IS the field name and the default is right; a table whose
1054
+ * column ids are decorated (`records-grid-notes`) maps them here.
1055
+ */
1056
+ interface MatrxDataTableStyleConfig<T> {
1057
+ /** The style document. */
1058
+ style: TableStyle;
1059
+ /** The row's data as the rules read it. Defaults to the row itself. */
1060
+ documentOf?: (row: T) => Record<string, unknown>;
1061
+ /** Column id to the field name a rule would name. Defaults to identity. */
1062
+ fieldOf?: (columnId: string) => string;
1063
+ /**
1064
+ * The color a choice column's option paints with, for color-by. The consumer
1065
+ * supplies it because option colors live on the resolved option list, which
1066
+ * may come from a shared pick list this package cannot reach.
1067
+ */
1068
+ choiceColorFor?: ChoiceColorLookup;
1069
+ }
1070
+ /**
1071
+ * GROUPING — "group these rows by Status" with collapsible sections, a count
1072
+ * and a subtotal on each.
1073
+ *
1074
+ * This is the primitive's answer to the biggest gap against Airtable, Google
1075
+ * Sheets and Notion. It is opt-in, and it is CONTROLLED for the two pieces of
1076
+ * state a person expects to survive: which column groups, and which sections
1077
+ * are shut. Both are ordinary values, so a saved view carries them like any
1078
+ * other reading of the table.
1079
+ *
1080
+ * Grouping runs AFTER local processing and over the rows the table is about to
1081
+ * render. It never re-sorts inside a group: the sort the person chose is the
1082
+ * order they see.
1083
+ */
1084
+ interface MatrxDataTableGroupingConfig<T> {
1085
+ /** Column id to group by. `null` means grouping is available but off. */
1086
+ columnId: string | null;
1087
+ /** Emitted when the person picks a different column (or clears it). */
1088
+ onColumnIdChange?: ((columnId: string | null) => void) | undefined;
1089
+ /**
1090
+ * Columns offered in the group-by control. Defaults to every visible column
1091
+ * that is not an actions column. A column with thousands of distinct values
1092
+ * is a bad grouping and the consumer is the only one who knows that.
1093
+ */
1094
+ groupableColumnIds?: string[] | undefined;
1095
+ /** Per-column subtotal shown on every group header, keyed by column id. */
1096
+ aggregates?: Record<string, GroupAggregateSpec> | undefined;
1097
+ /** Controlled collapsed group keys (`TableGroup.key`). */
1098
+ collapsed?: string[] | undefined;
1099
+ onCollapsedChange?: ((collapsed: string[]) => void) | undefined;
1100
+ /** Section order. Default `"value-asc"`; blanks always sort last. */
1101
+ order?: GroupOrder | undefined;
1102
+ /** Label for the section of rows whose grouping value is blank. */
1103
+ emptyLabel?: string | undefined;
1104
+ /** Singular noun for a group's summary ("job" → "12 jobs"). */
1105
+ rowNoun?: string | undefined;
1106
+ /**
1107
+ * Reads the grouping value for a column. Defaults to the column's own
1108
+ * accessor.
1109
+ *
1110
+ * NOT named `valueOf`: every object in JavaScript already has one, so an
1111
+ * optional config field by that name is always "present" and a `config.valueOf
1112
+ * ? … : …` check silently calls `Object.prototype.valueOf` and groups every
1113
+ * row under the config object itself. It did exactly that here once.
1114
+ */
1115
+ readCell?: ((row: T, columnId: string) => unknown) | undefined;
1116
+ }
1117
+ /**
1118
+ * VIRTUALIZATION — render the rows a person can actually see.
1119
+ *
1120
+ * Off by default, because most tables in the product are short and a windowed
1121
+ * body costs a scroll container contract that a short table does not need.
1122
+ * On, it engages only past `threshold` rows, so a 40-row table behaves exactly
1123
+ * as it always did and a 100,000-row table scrolls.
1124
+ */
1125
+ interface MatrxDataTableVirtualizeConfig {
1126
+ enabled?: boolean | undefined;
1127
+ /** Row height in px used for the window math. Default follows density. */
1128
+ rowHeight?: number | undefined;
1129
+ /** Rows rendered beyond each edge of the viewport. Default 8. */
1130
+ overscan?: number | undefined;
1131
+ /** Row count below which the body renders whole. Default 150. */
1132
+ threshold?: number | undefined;
1133
+ /**
1134
+ * Window the COLUMNS too, for tables hundreds of columns wide. Frozen
1135
+ * columns are always rendered, never windowed out.
1136
+ */
1137
+ columns?: boolean | undefined;
1138
+ /** Column width in px used for the horizontal window. Default 160. */
1139
+ columnWidth?: number | undefined;
1140
+ }
1141
+ /**
1142
+ * THE HONEST COUNT.
1143
+ *
1144
+ * A table that filters over part of its rows and says nothing is a screen that
1145
+ * lies. Declare what the answer actually covers and the table states it, in
1146
+ * plain English, above the rows — and never presents a partial count as exact.
1147
+ *
1148
+ * Omit this entirely and the table says nothing, which is correct for a table
1149
+ * whose rows are all in hand.
1150
+ */
1151
+ interface MatrxDataTableCoverageConfig {
1152
+ /** Rows the source says match the current query, when it can say. */
1153
+ matched?: number | undefined;
1154
+ /** Rows in the table irrespective of the query, when known. */
1155
+ total?: number | undefined;
1156
+ /** A ceiling the source imposed on this answer (a fetch cap). */
1157
+ cap?: number | undefined;
1158
+ /** Who answered the filters and sort. Default `"client"`. */
1159
+ answeredBy?: "source" | "client" | undefined;
1160
+ /** Singular noun for the sentence. Default "row". */
1161
+ noun?: string | undefined;
1162
+ }
1163
+ /**
1164
+ * SPREADSHEET GESTURES — range selection, Excel paste, fill, undo.
1165
+ *
1166
+ * The `/data` grid had all of this and nothing else in the product could reach
1167
+ * it. Opt-in, because a reference table with one editable column does not want
1168
+ * a cell cursor; a table a person works IN does.
1169
+ *
1170
+ * The table owns the gestures and the selection rectangle; the CONSUMER owns
1171
+ * the writes. Every mutation arrives as a list of `CellPatch`, so the caller
1172
+ * applies it through the door it already has and the table never invents a
1173
+ * persistence path of its own. That is also what makes undo honest: the stack
1174
+ * holds before-values, and an undo is just another patch list.
1175
+ */
1176
+ interface MatrxDataTableSpreadsheetConfig<T> {
1177
+ enabled?: boolean | undefined;
1178
+ /**
1179
+ * Apply cell writes. Called for a paste, a fill-down, a fill-handle drag, a
1180
+ * clear, an undo and a redo. The table re-reads from `data`, so a write the
1181
+ * caller refuses simply never appears.
1182
+ */
1183
+ onPatch: (patches: CellPatch[], intent: SpreadsheetIntent) => void | Promise<void>;
1184
+ /** Column ids a patch may target. Defaults to every column marked `editable`. */
1185
+ writableColumnIds?: string[] | undefined;
1186
+ /** Why a column refuses a write, as a sentence a person can act on. */
1187
+ columnRefusal?: ((columnId: string) => string | undefined) | undefined;
1188
+ /** Reads a cell for copy and fill. Defaults to the column's own accessor. */
1189
+ readCell?: ((row: T, columnId: string) => unknown) | undefined;
1190
+ /** The little square at the corner of a selection. Default true. */
1191
+ fillHandle?: boolean | undefined;
1192
+ /** Paste real Excel/Sheets TSV into the selection. Default true. */
1193
+ paste?: boolean | undefined;
1194
+ /** Undo depth. 0 turns undo off. Default 100. */
1195
+ undoDepth?: number | undefined;
1196
+ /** Says what just happened, and why something did not. */
1197
+ notify?: ((message: string) => void) | undefined;
1198
+ }
858
1199
  interface MatrxDataTableProps<T> {
859
1200
  data: T[];
860
1201
  columns: MatrxColumnDef<T>[];
@@ -964,6 +1305,46 @@ interface MatrxDataTableProps<T> {
964
1305
  footerRows?: T[];
965
1306
  /** Per-row presentation class, after canonical selection/highlight state. */
966
1307
  rowClassName?: (row: T, index: number) => string | undefined;
1308
+ /**
1309
+ * Per-CELL presentation class, after the column's own static `className`.
1310
+ *
1311
+ * The primitive had `rowClassName` and a static per-column `className`, and
1312
+ * nothing in between — so every table that needed to say something about ONE
1313
+ * cell had to grow a `cell` render function and paint inside it, where the
1314
+ * table can no longer see it. This is the missing rung, and `tableStyle`
1315
+ * below is the declarative thing built on top of it.
1316
+ */
1317
+ cellClassName?: (row: T, columnId: string, index: number) => string | undefined;
1318
+ /**
1319
+ * COLORS, declaratively — color by a column, rules, manual highlights.
1320
+ *
1321
+ * The model is `./table-style.ts`: one document, evaluated live on every
1322
+ * render so a tint is never stale, with a documented precedence between the
1323
+ * three ways a cell can earn a color. Pass the document and the table paints
1324
+ * itself; the consumer owns only where that document is STORED and how a
1325
+ * person edits it.
1326
+ */
1327
+ tableStyle?: MatrxDataTableStyleConfig<T>;
1328
+ /**
1329
+ * GROUPS — collapsible sections by a column, with a count and per-column
1330
+ * subtotals on each header. Opt-in and controlled; see the config's own doc.
1331
+ */
1332
+ grouping?: MatrxDataTableGroupingConfig<T>;
1333
+ /** Render only the rows in view. Opt-in; engages past its own threshold. */
1334
+ virtualize?: boolean | MatrxDataTableVirtualizeConfig;
1335
+ /**
1336
+ * What the current answer actually covers. Declare it and the table says so
1337
+ * on screen when a filter is answering over part of the table, instead of
1338
+ * presenting a partial count as the whole truth.
1339
+ */
1340
+ coverage?: MatrxDataTableCoverageConfig;
1341
+ /** Range selection, Excel paste, fill handle, fill-down, undo. Opt-in. */
1342
+ spreadsheet?: MatrxDataTableSpreadsheetConfig<T>;
1343
+ /**
1344
+ * Explicit body row height in px. Overrides the density default. Use it when
1345
+ * a saved view remembers a row height the person chose.
1346
+ */
1347
+ rowHeight?: number;
967
1348
  /** Highlight an index in the currently rendered page without selecting it. */
968
1349
  highlightedIndex?: number | undefined;
969
1350
  /** Receives the complete visible local view after local processing, before paging. Attaching after absence receives one snapshot; callback identity changes alone do not notify. */
@@ -1029,4 +1410,4 @@ interface MatrxDataTableProps<T> {
1029
1410
  onRowOpen?: (row: T) => void;
1030
1411
  }
1031
1412
 
1032
- export { isLayeredFilterOperator as $, type AnyOfColumnSearch as A, type MatrxDataTableWindowConfig as B, type ColumnFiltersState as C, type MatrxTablePagination as D, type SortDirection as E, type TableRankedSearchOptions as F, type TableSearchRole as G, type TableSearchScope as H, type TableViewQuery as I, type TextFilterMode as J, type ToolbarFacet as K, type LayeredFilterRule as L, type MatrxColumnDef as M, inferTableSearchRole as N, rankTableSearchRows as O, resolveTableSearchScopes as P, LAYERED_FILTER_OPERATORS as Q, LAYERED_FILTER_OPERATOR_LABELS as R, type SortState as S, type TableSearchMatchMode as T, type LayeredFilterField as U, type LayeredFilterOperator as V, columnFiltersToLayeredRules as W, completeLayeredFilterRules as X, decodeLayeredFilterRules as Y, encodeLayeredFilterRules as Z, isCompleteLayeredFilterRule as _, type ColumnFilterValue as a, layeredFilterMatchesValue as a0, layeredFilterNeedsValue as a1, layeredFilterRuleSummary as a2, operatorsForLayeredField as a3, 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, type CellEditType as k, type CellEditsMap as l, type MatrxDataTableContextMenuConfig as m, type MatrxDataTableDetailConfig as n, type MatrxDataTableEditConfig as o, type MatrxDataTableEmptyState as p, type MatrxDataTableExpandedDetailConfig as q, type MatrxDataTableFilterUi as r, type MatrxDataTableHierarchyConfig as s, type MatrxDataTableHierarchyMove as t, type MatrxDataTableMobileCardControls as u, type MatrxDataTableQueryControl as v, type MatrxDataTableSelectionConfig as w, type MatrxDataTableSourceProcessing as x, type MatrxDataTableToolbar as y, type MatrxDataTableUrlStateConfig as z };
1413
+ export { type SortDirection as $, type AnyOfColumnSearch as A, BOOLEAN_TRUE_COLOR as B, type ColumnFiltersState as C, type MatrxDataTableGroupingConfig as D, EMPTY_TABLE_STYLE as E, type MatrxDataTableHierarchyConfig as F, type MatrxDataTableHierarchyMove as G, type MatrxDataTableMobileCardControls as H, type MatrxDataTableQueryControl as I, type MatrxDataTableSelectionConfig as J, type MatrxDataTableSourceProcessing as K, type LayeredFilterRule as L, type MatrxColumnDef as M, type MatrxDataTableSpreadsheetConfig as N, type MatrxDataTableStyleConfig as O, type MatrxDataTableToolbar as P, type MatrxDataTableUrlStateConfig as Q, type MatrxDataTableVirtualizeConfig as R, type SortState as S, type TableSearchMatchMode as T, type MatrxDataTableWindowConfig as U, type MatrxTablePagination as V, OPS_WITH_VALUE as W, ROW_TINT_CLASS as X, STYLE_COLORS as Y, STYLE_COLOR_LABELS as Z, SWATCH_CLASS as _, type ColumnFilterValue as a, type StyleColor as a0, type StylePath as a1, type StyleTarget as a2, TABLE_STYLE_METADATA_KEY as a3, TABLE_STYLE_VERSION as a4, type TableRankedSearchOptions as a5, type TableSearchRole as a6, type TableSearchScope as a7, type TableStyle as a8, type TableViewQuery as a9, layeredFilterNeedsValue as aA, layeredFilterRuleSummary as aB, operatorsForLayeredField as aC, type TextFilterMode as aa, type ToolbarFacet as ab, applyStylePath as ac, colorForChoice as ad, evaluateRule as ae, inferTableSearchRole as af, isStyleColor as ag, parseTableStyle as ah, rankTableSearchRows as ai, resolveCellColor as aj, resolveRowColor as ak, resolveTableSearchScopes as al, stylePath as am, tableStyleFromMetadata as an, tableStyleIsEmpty as ao, LAYERED_FILTER_OPERATORS as ap, LAYERED_FILTER_OPERATOR_LABELS as aq, type LayeredFilterField as ar, type LayeredFilterOperator as as, columnFiltersToLayeredRules as at, completeLayeredFilterRules as au, decodeLayeredFilterRules as av, encodeLayeredFilterRules as aw, isCompleteLayeredFilterRule as ax, isLayeredFilterOperator as ay, layeredFilterMatchesValue as az, 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 MatrxDataTableCoverageConfig as u, type MatrxDataTableDetailConfig as v, type MatrxDataTableEditConfig as w, type MatrxDataTableEmptyState as x, type MatrxDataTableExpandedDetailConfig as y, type MatrxDataTableFilterUi as z };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ai-matrx/design-system",
3
- "version": "0.21.14",
3
+ "version": "0.25.0",
4
4
  "description": "AI Matrx semantic React primitives shared across Vite, Next.js, desktop, and customer-built applications.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -78,6 +78,26 @@
78
78
  "default": "./dist/data-table/index.cjs"
79
79
  }
80
80
  },
81
+ "./field-formats": {
82
+ "import": {
83
+ "types": "./dist/field-formats/index.d.ts",
84
+ "default": "./dist/field-formats/index.js"
85
+ },
86
+ "require": {
87
+ "types": "./dist/field-formats/index.d.cts",
88
+ "default": "./dist/field-formats/index.cjs"
89
+ }
90
+ },
91
+ "./formulas": {
92
+ "import": {
93
+ "types": "./dist/formulas/index.d.ts",
94
+ "default": "./dist/formulas/index.js"
95
+ },
96
+ "require": {
97
+ "types": "./dist/formulas/index.d.cts",
98
+ "default": "./dist/formulas/index.cjs"
99
+ }
100
+ },
81
101
  "./data-table/host": {
82
102
  "import": {
83
103
  "types": "./dist/data-table/host.d.ts",
@@ -228,6 +248,96 @@
228
248
  "default": "./dist/data-table/copy-types.cjs"
229
249
  }
230
250
  },
251
+ "./data-table/validation": {
252
+ "import": {
253
+ "types": "./dist/data-table/validation.d.ts",
254
+ "default": "./dist/data-table/validation.js"
255
+ },
256
+ "require": {
257
+ "types": "./dist/data-table/validation.d.cts",
258
+ "default": "./dist/data-table/validation.cjs"
259
+ }
260
+ },
261
+ "./data-table/grid-clipboard": {
262
+ "import": {
263
+ "types": "./dist/data-table/grid-clipboard.d.ts",
264
+ "default": "./dist/data-table/grid-clipboard.js"
265
+ },
266
+ "require": {
267
+ "types": "./dist/data-table/grid-clipboard.d.cts",
268
+ "default": "./dist/data-table/grid-clipboard.cjs"
269
+ }
270
+ },
271
+ "./data-table/grid-selection": {
272
+ "import": {
273
+ "types": "./dist/data-table/grid-selection.d.ts",
274
+ "default": "./dist/data-table/grid-selection.js"
275
+ },
276
+ "require": {
277
+ "types": "./dist/data-table/grid-selection.d.cts",
278
+ "default": "./dist/data-table/grid-selection.cjs"
279
+ }
280
+ },
281
+ "./data-table/cell-undo": {
282
+ "import": {
283
+ "types": "./dist/data-table/cell-undo.d.ts",
284
+ "default": "./dist/data-table/cell-undo.js"
285
+ },
286
+ "require": {
287
+ "types": "./dist/data-table/cell-undo.d.cts",
288
+ "default": "./dist/data-table/cell-undo.cjs"
289
+ }
290
+ },
291
+ "./data-table/fill": {
292
+ "import": {
293
+ "types": "./dist/data-table/fill.d.ts",
294
+ "default": "./dist/data-table/fill.js"
295
+ },
296
+ "require": {
297
+ "types": "./dist/data-table/fill.d.cts",
298
+ "default": "./dist/data-table/fill.cjs"
299
+ }
300
+ },
301
+ "./data-table/coverage": {
302
+ "import": {
303
+ "types": "./dist/data-table/coverage.d.ts",
304
+ "default": "./dist/data-table/coverage.js"
305
+ },
306
+ "require": {
307
+ "types": "./dist/data-table/coverage.d.cts",
308
+ "default": "./dist/data-table/coverage.cjs"
309
+ }
310
+ },
311
+ "./data-table/virtual-window": {
312
+ "import": {
313
+ "types": "./dist/data-table/virtual-window.d.ts",
314
+ "default": "./dist/data-table/virtual-window.js"
315
+ },
316
+ "require": {
317
+ "types": "./dist/data-table/virtual-window.d.cts",
318
+ "default": "./dist/data-table/virtual-window.cjs"
319
+ }
320
+ },
321
+ "./data-table/grouping": {
322
+ "import": {
323
+ "types": "./dist/data-table/grouping.d.ts",
324
+ "default": "./dist/data-table/grouping.js"
325
+ },
326
+ "require": {
327
+ "types": "./dist/data-table/grouping.d.cts",
328
+ "default": "./dist/data-table/grouping.cjs"
329
+ }
330
+ },
331
+ "./data-table/spreadsheet": {
332
+ "import": {
333
+ "types": "./dist/data-table/spreadsheet.d.ts",
334
+ "default": "./dist/data-table/spreadsheet.js"
335
+ },
336
+ "require": {
337
+ "types": "./dist/data-table/spreadsheet.d.cts",
338
+ "default": "./dist/data-table/spreadsheet.cjs"
339
+ }
340
+ },
231
341
  "./package.json": "./package.json"
232
342
  },
233
343
  "dependencies": {
@@ -272,16 +382,20 @@
272
382
  },
273
383
  "devDependencies": {
274
384
  "@arethetypeswrong/cli": "^0.18.5",
385
+ "@tailwindcss/vite": "^4.3.3",
275
386
  "@testing-library/react": "^16.3.3",
276
387
  "@types/node": "^26.6.1",
277
388
  "@types/react": "19.3.0",
278
389
  "@types/react-dom": "19.3.0",
390
+ "@vitejs/plugin-react": "^5.1.1",
279
391
  "jsdom": "^30.0.1",
280
392
  "publint": "^0.3.24",
281
393
  "react": "^19.3.0",
282
394
  "react-dom": "^19.3.0",
395
+ "tailwindcss": "^4.3.3",
283
396
  "tsup": "^8.5.1",
284
397
  "typescript": "^5.9.3",
398
+ "vite": "^7.3.1",
285
399
  "vitest": "^5.0.1"
286
400
  },
287
401
  "publishConfig": {
@@ -305,6 +419,9 @@
305
419
  "typecheck": "tsc --noEmit",
306
420
  "test": "vitest run",
307
421
  "verify:tarball": "node ./scripts/verify-tarball.mjs",
308
- "verify:workspace-tarballs": "MATRX_VERIFY_LOCAL_KIT=1 node ./scripts/verify-tarball.mjs"
422
+ "verify:workspace-tarballs": "MATRX_VERIFY_LOCAL_KIT=1 node ./scripts/verify-tarball.mjs",
423
+ "demo": "vite --config demo/vite.config.ts",
424
+ "demo:build": "vite build --config demo/vite.config.ts",
425
+ "demo:preview": "vite preview --config demo/vite.config.ts"
309
426
  }
310
427
  }