@ai-matrx/design-system 0.25.0 → 0.27.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 (81) hide show
  1. package/CHANGELOG.md +94 -0
  2. package/dist/data-table/copy-helpers.cjs.map +1 -1
  3. package/dist/data-table/copy-helpers.d.cts +3 -1
  4. package/dist/data-table/copy-helpers.d.ts +3 -1
  5. package/dist/data-table/copy-helpers.js.map +1 -1
  6. package/dist/data-table/data-row-inspector.cjs.map +1 -1
  7. package/dist/data-table/data-row-inspector.js.map +1 -1
  8. package/dist/data-table/data-row-window.cjs.map +1 -1
  9. package/dist/data-table/data-row-window.js.map +1 -1
  10. package/dist/data-table/date-filter.cjs +145 -0
  11. package/dist/data-table/date-filter.cjs.map +1 -0
  12. package/dist/data-table/date-filter.d.cts +31 -0
  13. package/dist/data-table/date-filter.d.ts +31 -0
  14. package/dist/data-table/date-filter.js +125 -0
  15. package/dist/data-table/date-filter.js.map +1 -0
  16. package/dist/data-table/facets.cjs +118 -0
  17. package/dist/data-table/facets.cjs.map +1 -0
  18. package/dist/data-table/facets.d.cts +100 -0
  19. package/dist/data-table/facets.d.ts +100 -0
  20. package/dist/data-table/facets.js +98 -0
  21. package/dist/data-table/facets.js.map +1 -0
  22. package/dist/data-table/filter-engine.cjs +88 -0
  23. package/dist/data-table/filter-engine.cjs.map +1 -1
  24. package/dist/data-table/filter-engine.d.cts +3 -1
  25. package/dist/data-table/filter-engine.d.ts +3 -1
  26. package/dist/data-table/filter-engine.js +88 -0
  27. package/dist/data-table/filter-engine.js.map +1 -1
  28. package/dist/data-table/host.cjs.map +1 -1
  29. package/dist/data-table/host.d.cts +14 -3
  30. package/dist/data-table/host.d.ts +14 -3
  31. package/dist/data-table/host.js.map +1 -1
  32. package/dist/data-table/index.cjs +918 -152
  33. package/dist/data-table/index.cjs.map +1 -1
  34. package/dist/data-table/index.d.cts +5 -2
  35. package/dist/data-table/index.d.ts +5 -2
  36. package/dist/data-table/index.js +956 -189
  37. package/dist/data-table/index.js.map +1 -1
  38. package/dist/data-table/infer-filter.cjs +2 -0
  39. package/dist/data-table/infer-filter.cjs.map +1 -1
  40. package/dist/data-table/infer-filter.d.cts +4 -2
  41. package/dist/data-table/infer-filter.d.ts +4 -2
  42. package/dist/data-table/infer-filter.js +2 -0
  43. package/dist/data-table/infer-filter.js.map +1 -1
  44. package/dist/data-table/layered-filters.d.cts +3 -1
  45. package/dist/data-table/layered-filters.d.ts +3 -1
  46. package/dist/data-table/menu-targets.cjs +77 -0
  47. package/dist/data-table/menu-targets.cjs.map +1 -0
  48. package/dist/data-table/menu-targets.d.cts +108 -0
  49. package/dist/data-table/menu-targets.d.ts +108 -0
  50. package/dist/data-table/menu-targets.js +57 -0
  51. package/dist/data-table/menu-targets.js.map +1 -0
  52. package/dist/data-table/pagination.cjs.map +1 -1
  53. package/dist/data-table/pagination.js.map +1 -1
  54. package/dist/data-table/query-control.cjs.map +1 -1
  55. package/dist/data-table/query-control.d.cts +3 -1
  56. package/dist/data-table/query-control.d.ts +3 -1
  57. package/dist/data-table/query-control.js.map +1 -1
  58. package/dist/data-table/spreadsheet.cjs +61 -13
  59. package/dist/data-table/spreadsheet.cjs.map +1 -1
  60. package/dist/data-table/spreadsheet.d.cts +36 -1
  61. package/dist/data-table/spreadsheet.d.ts +36 -1
  62. package/dist/data-table/spreadsheet.js +61 -13
  63. package/dist/data-table/spreadsheet.js.map +1 -1
  64. package/dist/data-table/types.cjs.map +1 -1
  65. package/dist/data-table/types.d.cts +3 -1
  66. package/dist/data-table/types.d.ts +3 -1
  67. package/dist/data-table/url-state.d.cts +3 -1
  68. package/dist/data-table/url-state.d.ts +3 -1
  69. package/dist/data-table/uuid-cell.cjs.map +1 -1
  70. package/dist/data-table/uuid-cell.js.map +1 -1
  71. package/dist/data-table/xlsx.cjs.map +1 -1
  72. package/dist/data-table/xlsx.d.cts +3 -1
  73. package/dist/data-table/xlsx.d.ts +3 -1
  74. package/dist/data-table/xlsx.js.map +1 -1
  75. package/dist/field-formats/index.cjs +15 -0
  76. package/dist/field-formats/index.cjs.map +1 -1
  77. package/dist/field-formats/index.js +15 -0
  78. package/dist/field-formats/index.js.map +1 -1
  79. package/dist/{types-nlLCMiqd.d.ts → types-D9JBR6ZU.d.ts} +118 -5
  80. package/dist/{types-CmQrOz-T.d.cts → types-jQddSUnj.d.cts} +118 -5
  81. package/package.json +31 -1
@@ -10,7 +10,7 @@ import {
10
10
  useId as useId2,
11
11
  useLayoutEffect as useLayoutEffect2,
12
12
  useMemo as useMemo8,
13
- useRef as useRef14,
13
+ useRef as useRef15,
14
14
  useState as useState22
15
15
  } from "react";
16
16
  import {
@@ -5043,6 +5043,124 @@ function layeredFilterRuleSummary(rule, fields) {
5043
5043
  return rule.operator === "between" ? `${fieldLabel} ${operator} ${value} and ${rule.valueTo ?? ""}` : `${fieldLabel} ${operator} ${value}`;
5044
5044
  }
5045
5045
 
5046
+ // src/data-table/date-filter.ts
5047
+ var TIME_RANGE_PRESET_VALUES = [
5048
+ "all",
5049
+ "1m",
5050
+ "15m",
5051
+ "30m",
5052
+ "1h",
5053
+ "2h",
5054
+ "6h",
5055
+ "12h",
5056
+ "today",
5057
+ "yesterday",
5058
+ "this_week",
5059
+ "this_month",
5060
+ "this_year",
5061
+ "older_1m",
5062
+ "older_15m",
5063
+ "older_30m",
5064
+ "older_1h",
5065
+ "older_2h",
5066
+ "older_6h",
5067
+ "older_12h",
5068
+ "older_today",
5069
+ "older_this_week",
5070
+ "older_this_month",
5071
+ "older_this_year"
5072
+ ];
5073
+ var TIME_RANGE_PRESETS = [
5074
+ { value: "all", label: "All time", group: "all" },
5075
+ { value: "1m", label: "Last minute", group: "recent" },
5076
+ { value: "15m", label: "Last 15 minutes", group: "recent" },
5077
+ { value: "30m", label: "Last 30 minutes", group: "recent" },
5078
+ { value: "1h", label: "Last hour", group: "recent" },
5079
+ { value: "2h", label: "Last 2 hours", group: "recent" },
5080
+ { value: "6h", label: "Last 6 hours", group: "recent" },
5081
+ { value: "12h", label: "Last 12 hours", group: "recent" },
5082
+ { value: "today", label: "Today", group: "calendar" },
5083
+ { value: "yesterday", label: "Yesterday", group: "calendar" },
5084
+ { value: "this_week", label: "This week", group: "calendar" },
5085
+ { value: "this_month", label: "This month", group: "calendar" },
5086
+ { value: "this_year", label: "This year", group: "calendar" },
5087
+ { value: "older_1m", label: "Older than 1 minute", group: "older" },
5088
+ { value: "older_15m", label: "Older than 15 minutes", group: "older" },
5089
+ { value: "older_30m", label: "Older than 30 minutes", group: "older" },
5090
+ { value: "older_1h", label: "Older than 1 hour", group: "older" },
5091
+ { value: "older_2h", label: "Older than 2 hours", group: "older" },
5092
+ { value: "older_6h", label: "Older than 6 hours", group: "older" },
5093
+ { value: "older_12h", label: "Older than 12 hours", group: "older" },
5094
+ { value: "older_today", label: "Before today", group: "older" },
5095
+ { value: "older_this_week", label: "Before this week", group: "older" },
5096
+ { value: "older_this_month", label: "Before this month", group: "older" },
5097
+ { value: "older_this_year", label: "Before this year", group: "older" }
5098
+ ];
5099
+ function startOfDay(date) {
5100
+ const next = new Date(date);
5101
+ next.setHours(0, 0, 0, 0);
5102
+ return next;
5103
+ }
5104
+ function startOfWeek(date) {
5105
+ const next = startOfDay(date);
5106
+ next.setDate(next.getDate() - (next.getDay() === 0 ? 6 : next.getDay() - 1));
5107
+ return next;
5108
+ }
5109
+ function startOfMonth(date) {
5110
+ return new Date(date.getFullYear(), date.getMonth(), 1);
5111
+ }
5112
+ function startOfYear(date) {
5113
+ return new Date(date.getFullYear(), 0, 1);
5114
+ }
5115
+ function validDate(value) {
5116
+ if (value === void 0) return void 0;
5117
+ const date = value instanceof Date ? new Date(value) : new Date(value);
5118
+ return Number.isNaN(date.valueOf()) ? void 0 : date;
5119
+ }
5120
+ function dateFilterBounds(filter, now = /* @__PURE__ */ new Date()) {
5121
+ if (!filter.preset) {
5122
+ return {
5123
+ ...filter.since === void 0 ? {} : { since: filter.since },
5124
+ ...filter.until === void 0 ? {} : { until: filter.until }
5125
+ };
5126
+ }
5127
+ if (filter.preset === "all") return {};
5128
+ const anchor = validDate(filter.anchor) ?? validDate(now) ?? /* @__PURE__ */ new Date();
5129
+ const ago = (milliseconds) => new Date(anchor.valueOf() - milliseconds).toISOString();
5130
+ const recent = {
5131
+ "1m": 6e4,
5132
+ "15m": 15 * 6e4,
5133
+ "30m": 30 * 6e4,
5134
+ "1h": 60 * 6e4,
5135
+ "2h": 2 * 60 * 6e4,
5136
+ "6h": 6 * 60 * 6e4,
5137
+ "12h": 12 * 60 * 6e4
5138
+ };
5139
+ const duration = recent[filter.preset.replace("older_", "")];
5140
+ if (duration !== void 0) {
5141
+ return filter.preset.startsWith("older_") ? { until: ago(duration) } : { since: ago(duration) };
5142
+ }
5143
+ const starts = {
5144
+ today: startOfDay(anchor),
5145
+ this_week: startOfWeek(anchor),
5146
+ this_month: startOfMonth(anchor),
5147
+ this_year: startOfYear(anchor),
5148
+ older_today: startOfDay(anchor),
5149
+ older_this_week: startOfWeek(anchor),
5150
+ older_this_month: startOfMonth(anchor),
5151
+ older_this_year: startOfYear(anchor)
5152
+ };
5153
+ if (filter.preset === "yesterday") {
5154
+ const until = startOfDay(anchor);
5155
+ const since = new Date(until);
5156
+ since.setDate(since.getDate() - 1);
5157
+ return { since: since.toISOString(), until: until.toISOString() };
5158
+ }
5159
+ const start = starts[filter.preset];
5160
+ if (!start) return {};
5161
+ return filter.preset.startsWith("older_") ? { until: start.toISOString() } : { since: start.toISOString() };
5162
+ }
5163
+
5046
5164
  // src/data-table/filter-engine.ts
5047
5165
  var SELECT_EMPTY_VALUE = "__empty__";
5048
5166
  var SELECT_NOT_EMPTY_VALUE = "__not_empty__";
@@ -5139,10 +5257,25 @@ function passesColumnFilter(value, filter) {
5139
5257
  if (raw === "") return false;
5140
5258
  const normalized = raw.replace(/[^0-9.-]/g, "");
5141
5259
  if (normalized === "") return false;
5142
- const n = typeof value === "number" ? value : Number(normalized);
5143
- if (!Number.isFinite(n)) return false;
5144
- if (filter.min !== void 0 && n < filter.min) return false;
5145
- if (filter.max !== void 0 && n > filter.max) return false;
5260
+ const n2 = typeof value === "number" ? value : Number(normalized);
5261
+ if (!Number.isFinite(n2)) return false;
5262
+ if (filter.min !== void 0 && n2 < filter.min) return false;
5263
+ if (filter.max !== void 0 && n2 > filter.max) return false;
5264
+ return true;
5265
+ }
5266
+ case "date": {
5267
+ const raw = value instanceof Date ? value : new Date(stringifyCellValue(value));
5268
+ if (Number.isNaN(raw.valueOf())) return false;
5269
+ const { since, until } = dateFilterBounds(filter);
5270
+ const timestamp = raw.valueOf();
5271
+ if (since !== void 0) {
5272
+ const sinceTimestamp = new Date(since).valueOf();
5273
+ if (Number.isNaN(sinceTimestamp) || timestamp < sinceTimestamp) return false;
5274
+ }
5275
+ if (until !== void 0) {
5276
+ const untilTimestamp = new Date(until).valueOf();
5277
+ if (Number.isNaN(untilTimestamp) || timestamp >= untilTimestamp) return false;
5278
+ }
5146
5279
  return true;
5147
5280
  }
5148
5281
  }
@@ -5205,26 +5338,29 @@ function filterAndSortRows(rows, columns, columnFilters, sort, globalSearch, any
5205
5338
  return result;
5206
5339
  }
5207
5340
  function countActiveColumnFilters(filters) {
5208
- let n = 0;
5341
+ let n2 = 0;
5209
5342
  for (const f of Object.values(filters)) {
5210
5343
  if (!f) continue;
5211
5344
  switch (f.kind) {
5212
5345
  case "text":
5213
- if (f.value.trim() || f.mode && f.mode !== "contains") n += 1;
5346
+ if (f.value.trim() || f.mode && f.mode !== "contains") n2 += 1;
5214
5347
  break;
5215
5348
  case "select":
5216
5349
  if (f.values !== void 0 || f.value && f.value !== "__all__")
5217
- n += 1;
5350
+ n2 += 1;
5218
5351
  break;
5219
5352
  case "boolean":
5220
- n += 1;
5353
+ n2 += 1;
5221
5354
  break;
5222
5355
  case "number":
5223
- if (f.min !== void 0 || f.max !== void 0) n += 1;
5356
+ if (f.min !== void 0 || f.max !== void 0) n2 += 1;
5357
+ break;
5358
+ case "date":
5359
+ if (isColumnFilterActive(f)) n2 += 1;
5224
5360
  break;
5225
5361
  }
5226
5362
  }
5227
- return n;
5363
+ return n2;
5228
5364
  }
5229
5365
  function isColumnFilterActive(filter) {
5230
5366
  if (!filter) return false;
@@ -5237,6 +5373,10 @@ function isColumnFilterActive(filter) {
5237
5373
  return true;
5238
5374
  case "number":
5239
5375
  return filter.min !== void 0 || filter.max !== void 0;
5376
+ case "date": {
5377
+ const bounds = dateFilterBounds(filter);
5378
+ return bounds.since !== void 0 || bounds.until !== void 0;
5379
+ }
5240
5380
  }
5241
5381
  }
5242
5382
  function applyRowEdits(row, edits) {
@@ -5506,8 +5646,8 @@ function parseLayeredFilters(raw) {
5506
5646
  }
5507
5647
  }
5508
5648
  function positiveInt(raw, fallback) {
5509
- const n = raw ? Number.parseInt(raw, 10) : Number.NaN;
5510
- return Number.isFinite(n) && n > 0 ? n : fallback;
5649
+ const n2 = raw ? Number.parseInt(raw, 10) : Number.NaN;
5650
+ return Number.isFinite(n2) && n2 > 0 ? n2 : fallback;
5511
5651
  }
5512
5652
  function tableUrlParamPrefix(tableId) {
5513
5653
  if (!/^[a-z][a-z0-9-]{0,63}$/.test(tableId)) {
@@ -5708,6 +5848,7 @@ import {
5708
5848
  forwardRef as forwardRef12,
5709
5849
  useEffect as useEffect9,
5710
5850
  useMemo as useMemo5,
5851
+ useRef as useRef10,
5711
5852
  useState as useState14
5712
5853
  } from "react";
5713
5854
  import { useDraggable } from "@dnd-kit/core";
@@ -5855,6 +5996,95 @@ var CommandShortcut = ({
5855
5996
  };
5856
5997
  CommandShortcut.displayName = "CommandShortcut";
5857
5998
 
5999
+ // src/data-table/facets.ts
6000
+ var MAX_LISTABLE_FACET_LENGTH = 300;
6001
+ var DEFAULT_FACET_LIMIT = 200;
6002
+ var MAX_FACET_LIMIT = 500;
6003
+ function facetText(raw) {
6004
+ if (raw === null || raw === void 0) return "";
6005
+ if (typeof raw === "object") {
6006
+ try {
6007
+ return JSON.stringify(raw);
6008
+ } catch {
6009
+ return String(raw);
6010
+ }
6011
+ }
6012
+ return String(raw);
6013
+ }
6014
+ function computeColumnFacets(args) {
6015
+ const { columnId: columnId2, rows, readValue: readValue2 } = args;
6016
+ const limit = Math.min(Math.max(args.limit ?? DEFAULT_FACET_LIMIT, 1), MAX_FACET_LIMIT);
6017
+ const counts = /* @__PURE__ */ new Map();
6018
+ const longValues = /* @__PURE__ */ new Set();
6019
+ let blank = 0;
6020
+ let maxLength = 0;
6021
+ for (const row of rows) {
6022
+ let raw;
6023
+ try {
6024
+ raw = readValue2(row, columnId2);
6025
+ } catch {
6026
+ raw = void 0;
6027
+ }
6028
+ const trimmed = facetText(raw).trim();
6029
+ if (trimmed === "") {
6030
+ blank += 1;
6031
+ continue;
6032
+ }
6033
+ if (trimmed.length > maxLength) maxLength = trimmed.length;
6034
+ if (trimmed.length > MAX_LISTABLE_FACET_LENGTH) {
6035
+ longValues.add(trimmed);
6036
+ continue;
6037
+ }
6038
+ counts.set(trimmed, (counts.get(trimmed) ?? 0) + 1);
6039
+ }
6040
+ const values = [...counts.entries()].map(([value, count]) => ({ value, count })).sort((a, b) => b.count - a.count || a.value.localeCompare(b.value)).slice(0, limit);
6041
+ return {
6042
+ columnId: columnId2,
6043
+ totalRows: rows.length,
6044
+ filled: rows.length - blank,
6045
+ blank,
6046
+ distinctCount: counts.size + longValues.size,
6047
+ maxLength,
6048
+ unlistable: longValues.size,
6049
+ limit,
6050
+ truncated: counts.size > limit,
6051
+ values,
6052
+ answeredBy: "local",
6053
+ complete: args.complete ?? true
6054
+ };
6055
+ }
6056
+ function localFacetsAreComplete(loadedRowCount, total) {
6057
+ if (total === void 0 || !Number.isFinite(total) || total < 0) return false;
6058
+ return loadedRowCount >= total;
6059
+ }
6060
+ var NUMBERS = new Intl.NumberFormat("en-US");
6061
+ function n(value) {
6062
+ try {
6063
+ return NUMBERS.format(value);
6064
+ } catch {
6065
+ return String(value);
6066
+ }
6067
+ }
6068
+ function describeFacetSource(facets, noun = "row") {
6069
+ if (!facets) return "";
6070
+ const plural2 = `${noun}s`;
6071
+ if (!facets.complete) {
6072
+ return `These counts cover the ${n(facets.totalRows)} ${plural2} loaded here, not the whole table.`;
6073
+ }
6074
+ if (facets.truncated) {
6075
+ return `Showing the ${n(facets.values.length)} most common of ${n(facets.distinctCount)} values.`;
6076
+ }
6077
+ if (facets.unlistable > 0) {
6078
+ return `${n(facets.unlistable)} ${facets.unlistable === 1 ? "value is" : "values are"} too long to list. Match text instead to reach ${facets.unlistable === 1 ? "it" : "them"}.`;
6079
+ }
6080
+ return "";
6081
+ }
6082
+ var FACETS_LOADING_NOTICE = "Counting values\u2026";
6083
+ function describeFacetFailure(error) {
6084
+ const detail = error instanceof Error ? error.message : String(error ?? "");
6085
+ return detail ? `The values in this column could not be counted: ${detail} Match text instead.` : "The values in this column could not be counted. Match text instead.";
6086
+ }
6087
+
5858
6088
  // src/data-table/column-marker.ts
5859
6089
  var MARKER_ACCESSORS = {
5860
6090
  favorite: "favorite",
@@ -5922,6 +6152,10 @@ function ColumnHeaderCell({
5922
6152
  filterFooterHint,
5923
6153
  allowNegation = false,
5924
6154
  allowNullModes = false,
6155
+ facets,
6156
+ facetsLoading = false,
6157
+ facetsError,
6158
+ onFilterOpen,
5925
6159
  reorderColumn
5926
6160
  }) {
5927
6161
  const reorderEnabled = reorderColumn !== void 0;
@@ -5956,6 +6190,11 @@ function ColumnHeaderCell({
5956
6190
  setPendingNegation(filterValue?.negated ?? false);
5957
6191
  }
5958
6192
  }, [filterOpen, filterValue]);
6193
+ const notifyOpen = useRef10(onFilterOpen);
6194
+ notifyOpen.current = onFilterOpen;
6195
+ useEffect9(() => {
6196
+ if (filterOpen) notifyOpen.current?.();
6197
+ }, [filterOpen]);
5959
6198
  const applyFilter = () => {
5960
6199
  onFilterChange(draftFilter);
5961
6200
  setFilterOpen(false);
@@ -5967,7 +6206,7 @@ function ColumnHeaderCell({
5967
6206
  const visibleFilter = deferredFilter ? draftFilter : filterValue;
5968
6207
  const setNegated = (negated) => {
5969
6208
  setPendingNegation(negated);
5970
- if (!visibleFilter) return;
6209
+ if (!visibleFilter || visibleFilter.kind === "date") return;
5971
6210
  const next = { ...visibleFilter, ...negated ? { negated: true } : {} };
5972
6211
  if (!negated) delete next.negated;
5973
6212
  if (deferredFilter) setDraftFilter(next);
@@ -5975,7 +6214,7 @@ function ColumnHeaderCell({
5975
6214
  };
5976
6215
  const markerLabels = marker ? markerCopy(marker) : void 0;
5977
6216
  const accessibleLabel = markerLabels?.label ?? labelText;
5978
- const menuLabel = accessibleLabel ? `Sort or filter ${accessibleLabel}` : "Sort or filter column";
6217
+ const menuLabel = filterKind ? accessibleLabel ? `Sort or filter ${accessibleLabel}` : "Sort or filter column" : accessibleLabel ? `Sort ${accessibleLabel}` : "Sort column";
5979
6218
  const menuContent = /* @__PURE__ */ jsxs22(
5980
6219
  PopoverContent,
5981
6220
  {
@@ -6043,13 +6282,16 @@ function ColumnHeaderCell({
6043
6282
  kind: filterKind,
6044
6283
  value: visibleFilter,
6045
6284
  onChange: (next) => {
6046
- const preservingNegation = next && (visibleFilter?.negated ?? pendingNegation) ? { ...next, negated: true } : next;
6285
+ const preservingNegation = next && next.kind !== "date" && (visibleFilter?.negated ?? pendingNegation) ? { ...next, negated: true } : next;
6047
6286
  if (deferredFilter) setDraftFilter(preservingNegation);
6048
6287
  else onFilterChange(preservingNegation);
6049
6288
  },
6050
6289
  selectOptions,
6051
6290
  selectSingle,
6052
6291
  selectAllowCustom,
6292
+ facets: facets ?? null,
6293
+ facetsLoading,
6294
+ ...facetsError === void 0 ? {} : { facetsError },
6053
6295
  ...markerLabels ? {
6054
6296
  booleanLabels: {
6055
6297
  all: "All",
@@ -6060,7 +6302,7 @@ function ColumnHeaderCell({
6060
6302
  allowNullModes
6061
6303
  }
6062
6304
  ) : null,
6063
- filterKind && allowNegation ? /* @__PURE__ */ jsxs22("div", { className: "mt-2 border-t border-border pt-2", children: [
6305
+ filterKind && filterKind !== "date" && allowNegation ? /* @__PURE__ */ jsxs22("div", { className: "mt-2 border-t border-border pt-2", children: [
6064
6306
  /* @__PURE__ */ jsx31("p", { className: "mb-1 text-[11px] font-medium uppercase tracking-wider text-muted-foreground", children: "Match mode" }),
6065
6307
  /* @__PURE__ */ jsxs22("div", { className: "grid grid-cols-2 gap-1 rounded-md border p-0.5", children: [
6066
6308
  /* @__PURE__ */ jsx31(
@@ -6274,8 +6516,43 @@ function FilterBody({
6274
6516
  selectSingle = false,
6275
6517
  selectAllowCustom = false,
6276
6518
  allowNullModes = false,
6277
- booleanLabels
6519
+ booleanLabels,
6520
+ facets = null,
6521
+ facetsLoading = false,
6522
+ facetsError
6278
6523
  }) {
6524
+ const checklist = facetsLoading || facetsError || facets && (facets.values.length > 0 || facets.blank > 0) ? /* @__PURE__ */ jsx31(
6525
+ FacetChecklist,
6526
+ {
6527
+ facets,
6528
+ loading: facetsLoading,
6529
+ ...facetsError === void 0 ? {} : { error: facetsError },
6530
+ value,
6531
+ onChange
6532
+ }
6533
+ ) : null;
6534
+ if (checklist && kind === "select" && facets && facets.values.length > 0) {
6535
+ return /* @__PURE__ */ jsx31("div", { className: "space-y-2", children: checklist });
6536
+ }
6537
+ if (checklist) {
6538
+ return /* @__PURE__ */ jsxs22("div", { className: "space-y-2", children: [
6539
+ checklist,
6540
+ /* @__PURE__ */ jsx31("div", { className: "h-px bg-border" }),
6541
+ /* @__PURE__ */ jsx31(
6542
+ FilterBody,
6543
+ {
6544
+ kind,
6545
+ value,
6546
+ onChange,
6547
+ selectOptions,
6548
+ selectSingle,
6549
+ selectAllowCustom,
6550
+ allowNullModes,
6551
+ ...booleanLabels === void 0 ? {} : { booleanLabels }
6552
+ }
6553
+ )
6554
+ ] });
6555
+ }
6279
6556
  if (kind === "text") {
6280
6557
  const text = value?.kind === "text" ? value.value : "";
6281
6558
  const mode = (value?.kind === "text" ? value.mode : void 0) ?? "contains";
@@ -6384,8 +6661,183 @@ function FilterBody({
6384
6661
  )
6385
6662
  ] });
6386
6663
  }
6664
+ if (kind === "date") {
6665
+ return /* @__PURE__ */ jsx31(DateFilterBody, { value, onChange });
6666
+ }
6387
6667
  return /* @__PURE__ */ jsx31(NumberFilterBody, { value, onChange });
6388
6668
  }
6669
+ function FacetChecklist({
6670
+ facets,
6671
+ loading,
6672
+ error,
6673
+ value,
6674
+ onChange
6675
+ }) {
6676
+ const [query, setQuery] = useState14("");
6677
+ const selected = value?.kind === "select" ? value.values ?? (value.value && value.value !== "__all__" ? [value.value] : []) : [];
6678
+ const selectedSet = new Set(selected);
6679
+ const rows = useMemo5(() => {
6680
+ if (!facets) return [];
6681
+ const listed = facets.values.map((entry) => ({
6682
+ value: entry.value,
6683
+ label: entry.value,
6684
+ count: entry.count
6685
+ }));
6686
+ const withBlank = facets.blank > 0 ? [{ value: "", label: "(empty)", count: facets.blank }, ...listed] : listed;
6687
+ const q = query.trim().toLowerCase();
6688
+ if (!q) return withBlank;
6689
+ return withBlank.filter((row) => row.label.toLowerCase().includes(q));
6690
+ }, [facets, query]);
6691
+ const toggle = (candidate) => {
6692
+ const next = new Set(selectedSet);
6693
+ if (next.has(candidate)) next.delete(candidate);
6694
+ else next.add(candidate);
6695
+ const values = [...next];
6696
+ onChange(
6697
+ values.length === 0 ? void 0 : { kind: "select", value: values[0] ?? "", values }
6698
+ );
6699
+ };
6700
+ const notice = error ?? (loading ? FACETS_LOADING_NOTICE : describeFacetSource(facets));
6701
+ return /* @__PURE__ */ jsxs22("div", { className: "space-y-1.5", children: [
6702
+ /* @__PURE__ */ jsx31("p", { className: "px-1 text-[11px] font-medium uppercase tracking-wider text-muted-foreground", children: "Values" }),
6703
+ facets && facets.values.length + (facets.blank > 0 ? 1 : 0) > 8 ? /* @__PURE__ */ jsx31(
6704
+ Input,
6705
+ {
6706
+ "data-matrx-filter-autofocus": true,
6707
+ value: query,
6708
+ onChange: (event) => setQuery(event.target.value),
6709
+ placeholder: "Find a value\u2026",
6710
+ className: "h-7 text-sm",
6711
+ style: { fontSize: "16px" }
6712
+ }
6713
+ ) : null,
6714
+ rows.length > 0 ? /* @__PURE__ */ jsx31("ul", { className: "max-h-56 space-y-0.5 overflow-y-auto", "data-matrx-facet-list": "true", children: rows.map((row) => {
6715
+ const checked = selectedSet.has(row.value);
6716
+ return /* @__PURE__ */ jsx31("li", { children: /* @__PURE__ */ jsxs22(
6717
+ "button",
6718
+ {
6719
+ type: "button",
6720
+ role: "checkbox",
6721
+ "aria-checked": checked,
6722
+ "data-matrx-facet-value": row.value === "" ? "__blank__" : row.value,
6723
+ onClick: () => toggle(row.value),
6724
+ className: cn(
6725
+ "flex w-full items-center gap-2 rounded px-1.5 py-1 text-left text-xs hover:bg-muted",
6726
+ checked && "bg-primary/10"
6727
+ ),
6728
+ children: [
6729
+ /* @__PURE__ */ jsx31(
6730
+ "span",
6731
+ {
6732
+ "aria-hidden": true,
6733
+ className: cn(
6734
+ "flex h-3.5 w-3.5 shrink-0 items-center justify-center rounded-[3px] border",
6735
+ checked ? "border-primary bg-primary text-primary-foreground" : "border-border"
6736
+ ),
6737
+ children: checked ? /* @__PURE__ */ jsx31(Check, { className: "h-2.5 w-2.5" }) : null
6738
+ }
6739
+ ),
6740
+ /* @__PURE__ */ jsx31("span", { className: cn("min-w-0 flex-1 truncate", row.value === "" && "italic text-muted-foreground"), children: row.label }),
6741
+ /* @__PURE__ */ jsx31(
6742
+ "span",
6743
+ {
6744
+ "data-matrx-facet-count": String(row.count),
6745
+ className: "shrink-0 tabular-nums text-[11px] text-muted-foreground",
6746
+ children: row.count.toLocaleString("en-US")
6747
+ }
6748
+ )
6749
+ ]
6750
+ }
6751
+ ) }, row.value === "" ? "__blank__" : row.value);
6752
+ }) }) : loading ? null : /* @__PURE__ */ jsx31("p", { className: "px-1 text-[11px] text-muted-foreground", children: query.trim() ? "No value matches that." : "This column has no values yet." }),
6753
+ notice ? /* @__PURE__ */ jsx31(
6754
+ "p",
6755
+ {
6756
+ "data-matrx-facet-notice": "true",
6757
+ className: cn(
6758
+ "px-1 text-[11px] leading-snug",
6759
+ error ? "text-amber-700 dark:text-amber-400" : "text-muted-foreground"
6760
+ ),
6761
+ children: notice
6762
+ }
6763
+ ) : null,
6764
+ selected.length > 0 ? /* @__PURE__ */ jsx31(
6765
+ "button",
6766
+ {
6767
+ type: "button",
6768
+ onClick: () => onChange(void 0),
6769
+ className: "px-1 text-[11px] text-muted-foreground underline-offset-2 hover:underline",
6770
+ children: "Clear selection"
6771
+ }
6772
+ ) : null
6773
+ ] });
6774
+ }
6775
+ function DateFilterBody({
6776
+ value,
6777
+ onChange
6778
+ }) {
6779
+ const dateValue = value?.kind === "date" ? value : void 0;
6780
+ const isCustom = dateValue !== void 0 && dateValue.preset === void 0;
6781
+ const selected = dateValue?.preset ?? (isCustom ? "__custom__" : "all");
6782
+ const setBound = (bound, raw) => {
6783
+ const next = raw ? new Date(raw) : void 0;
6784
+ const iso = next && !Number.isNaN(next.valueOf()) ? next.toISOString() : void 0;
6785
+ const current = dateValue && dateValue.preset === void 0 ? dateValue : { kind: "date" };
6786
+ const updated = { ...current, [bound]: iso };
6787
+ if (updated.since === void 0 && updated.until === void 0) onChange(void 0);
6788
+ else onChange(updated);
6789
+ };
6790
+ return /* @__PURE__ */ jsxs22("div", { className: "space-y-2 px-1", children: [
6791
+ /* @__PURE__ */ jsxs22("div", { className: "flex items-center justify-between", children: [
6792
+ /* @__PURE__ */ jsx31("p", { className: "text-[11px] font-medium uppercase tracking-wider text-muted-foreground", children: "Date range" }),
6793
+ dateValue ? /* @__PURE__ */ jsx31("button", { type: "button", className: "text-[11px] text-muted-foreground hover:text-foreground", onClick: () => onChange(void 0), children: "clear" }) : null
6794
+ ] }),
6795
+ /* @__PURE__ */ jsxs22(
6796
+ Select,
6797
+ {
6798
+ value: selected,
6799
+ onValueChange: (next) => {
6800
+ if (next === "all") {
6801
+ onChange(void 0);
6802
+ return;
6803
+ }
6804
+ if (next === "__custom__") {
6805
+ onChange({ kind: "date" });
6806
+ return;
6807
+ }
6808
+ onChange({ kind: "date", preset: next, anchor: (/* @__PURE__ */ new Date()).toISOString() });
6809
+ },
6810
+ children: [
6811
+ /* @__PURE__ */ jsx31(SelectTrigger, { className: "h-8 text-xs", children: /* @__PURE__ */ jsx31(SelectValue, {}) }),
6812
+ /* @__PURE__ */ jsxs22(SelectContent, { children: [
6813
+ ["all", "recent", "calendar", "older"].map((group) => /* @__PURE__ */ jsxs22("div", { children: [
6814
+ group !== "all" ? /* @__PURE__ */ jsx31("p", { className: "px-2 py-1 text-[10px] font-medium uppercase tracking-wider text-muted-foreground", children: group }) : null,
6815
+ TIME_RANGE_PRESETS.filter((option) => option.group === group).map((option) => /* @__PURE__ */ jsx31(SelectItem, { value: option.value, children: option.label }, option.value))
6816
+ ] }, group)),
6817
+ /* @__PURE__ */ jsx31(SelectItem, { value: "__custom__", children: "Custom range" })
6818
+ ] })
6819
+ ]
6820
+ }
6821
+ ),
6822
+ isCustom ? /* @__PURE__ */ jsxs22("div", { className: "grid gap-1.5", children: [
6823
+ /* @__PURE__ */ jsxs22("label", { className: "grid gap-0.5 text-[11px] text-muted-foreground", children: [
6824
+ "On or after",
6825
+ /* @__PURE__ */ jsx31(Input, { type: "datetime-local", value: dateTimeLocal(dateValue?.since), onChange: (event) => setBound("since", event.target.value), className: "h-8 text-xs" })
6826
+ ] }),
6827
+ /* @__PURE__ */ jsxs22("label", { className: "grid gap-0.5 text-[11px] text-muted-foreground", children: [
6828
+ "Before",
6829
+ /* @__PURE__ */ jsx31(Input, { type: "datetime-local", value: dateTimeLocal(dateValue?.until), onChange: (event) => setBound("until", event.target.value), className: "h-8 text-xs" })
6830
+ ] })
6831
+ ] }) : null
6832
+ ] });
6833
+ }
6834
+ function dateTimeLocal(value) {
6835
+ if (!value) return "";
6836
+ const date = new Date(value);
6837
+ if (Number.isNaN(date.valueOf())) return "";
6838
+ const offset = date.getTimezoneOffset() * 6e4;
6839
+ return new Date(date.valueOf() - offset).toISOString().slice(0, 16);
6840
+ }
6389
6841
  function SearchableSelectFilter({
6390
6842
  selected,
6391
6843
  options,
@@ -6572,15 +7024,15 @@ function NumberFilterBody({
6572
7024
  }
6573
7025
  function parseOptionalNumber(raw) {
6574
7026
  if (raw.trim() === "") return void 0;
6575
- const n = Number(raw.replace(/[^0-9.-]/g, ""));
6576
- return Number.isFinite(n) ? n : void 0;
7027
+ const n2 = Number(raw.replace(/[^0-9.-]/g, ""));
7028
+ return Number.isFinite(n2) ? n2 : void 0;
6577
7029
  }
6578
7030
 
6579
7031
  // src/data-table/ColumnResizeHandle.tsx
6580
7032
  import {
6581
7033
  useCallback as useCallback6,
6582
7034
  useEffect as useEffect10,
6583
- useRef as useRef10,
7035
+ useRef as useRef11,
6584
7036
  useState as useState15
6585
7037
  } from "react";
6586
7038
 
@@ -6624,11 +7076,11 @@ function ColumnResizeHandle({
6624
7076
  onResizeCancel,
6625
7077
  onResizeCommit
6626
7078
  }) {
6627
- const handleRef = useRef10(null);
6628
- const activeResizeRef = useRef10(null);
6629
- const previewRafRef = useRef10(null);
6630
- const queuedWidthRef = useRef10(null);
6631
- const keyboardWidthRef = useRef10(null);
7079
+ const handleRef = useRef11(null);
7080
+ const activeResizeRef = useRef11(null);
7081
+ const previewRafRef = useRef11(null);
7082
+ const queuedWidthRef = useRef11(null);
7083
+ const keyboardWidthRef = useRef11(null);
6632
7084
  const [announcedWidth, setAnnouncedWidth] = useState15(null);
6633
7085
  const bounds = resolveColumnResizeBounds(minWidth, maxWidth);
6634
7086
  const cssVar = columnResizeCssVar(columnId2);
@@ -6890,16 +7342,16 @@ function computeAggregate(rows, column) {
6890
7342
  value = total;
6891
7343
  break;
6892
7344
  case "sum":
6893
- value = numbers.length === 0 ? null : numbers.reduce((acc, n) => acc + n, 0);
7345
+ value = numbers.length === 0 ? null : numbers.reduce((acc, n2) => acc + n2, 0);
6894
7346
  break;
6895
7347
  case "average":
6896
- value = numbers.length === 0 ? null : numbers.reduce((acc, n) => acc + n, 0) / numbers.length;
7348
+ value = numbers.length === 0 ? null : numbers.reduce((acc, n2) => acc + n2, 0) / numbers.length;
6897
7349
  break;
6898
7350
  case "min":
6899
- value = numbers.length === 0 ? null : numbers.reduce((acc, n) => n < acc ? n : acc);
7351
+ value = numbers.length === 0 ? null : numbers.reduce((acc, n2) => n2 < acc ? n2 : acc);
6900
7352
  break;
6901
7353
  case "max":
6902
- value = numbers.length === 0 ? null : numbers.reduce((acc, n) => n > acc ? n : acc);
7354
+ value = numbers.length === 0 ? null : numbers.reduce((acc, n2) => n2 > acc ? n2 : acc);
6903
7355
  break;
6904
7356
  case "empty":
6905
7357
  value = total - filled.length;
@@ -7217,7 +7669,7 @@ function scrollOffsetForIndex(input) {
7217
7669
  }
7218
7670
 
7219
7671
  // src/data-table/useSpreadsheetGrid.ts
7220
- import { useCallback as useCallback7, useEffect as useEffect11, useMemo as useMemo6, useRef as useRef11, useState as useState16 } from "react";
7672
+ import { useCallback as useCallback7, useEffect as useEffect11, useMemo as useMemo6, useRef as useRef12, useState as useState16 } from "react";
7221
7673
 
7222
7674
  // src/data-table/grid-selection.ts
7223
7675
  function rangeBounds(range, rowIds, fieldNames) {
@@ -7323,7 +7775,7 @@ function moveSelection(current, move, rowIds, fieldNames) {
7323
7775
  if (r === -1 || c === -1) return first;
7324
7776
  const lastRow = rowIds.length - 1;
7325
7777
  const lastCol = fieldNames.length - 1;
7326
- const clamp = (n, max) => Math.min(Math.max(n, 0), max);
7778
+ const clamp = (n2, max) => Math.min(Math.max(n2, 0), max);
7327
7779
  switch (move) {
7328
7780
  case "up":
7329
7781
  r = clamp(r - 1, lastRow);
@@ -7455,14 +7907,14 @@ function parseClipboardGrid(text) {
7455
7907
  let row = [];
7456
7908
  let cell = "";
7457
7909
  let i = 0;
7458
- const n = text.length;
7459
- while (i < n) {
7910
+ const n2 = text.length;
7911
+ while (i < n2) {
7460
7912
  const ch = text[i];
7461
7913
  if (cell === "" && ch === '"') {
7462
7914
  let j = i + 1;
7463
7915
  let out = "";
7464
7916
  let closed = false;
7465
- while (j < n) {
7917
+ while (j < n2) {
7466
7918
  const c = text[j];
7467
7919
  if (c === '"') {
7468
7920
  if (text[j + 1] === '"') {
@@ -7477,10 +7929,10 @@ function parseClipboardGrid(text) {
7477
7929
  out += c;
7478
7930
  j += 1;
7479
7931
  }
7480
- if (closed && (j >= n || text[j] === " " || text[j] === "\n" || text[j] === "\r")) {
7932
+ if (closed && (j >= n2 || text[j] === " " || text[j] === "\n" || text[j] === "\r")) {
7481
7933
  cell = out;
7482
7934
  i = j;
7483
- if (i >= n) break;
7935
+ if (i >= n2) break;
7484
7936
  const d = text[i];
7485
7937
  if (d === " ") {
7486
7938
  row.push(cell);
@@ -7516,7 +7968,7 @@ function parseClipboardGrid(text) {
7516
7968
  cell += ch;
7517
7969
  i += 1;
7518
7970
  }
7519
- const endedOnBreak = n > 0 && (text[n - 1] === "\n" || text[n - 1] === "\r");
7971
+ const endedOnBreak = n2 > 0 && (text[n2 - 1] === "\n" || text[n2 - 1] === "\r");
7520
7972
  if (!endedOnBreak) {
7521
7973
  row.push(cell);
7522
7974
  rows.push(row);
@@ -7595,11 +8047,11 @@ function directionOf(source, target) {
7595
8047
  return null;
7596
8048
  }
7597
8049
  function tileIndex(i, a, b) {
7598
- const n = b - a + 1;
7599
- if (n <= 0) return a;
8050
+ const n2 = b - a + 1;
8051
+ if (n2 <= 0) return a;
7600
8052
  if (i >= a && i <= b) return i;
7601
- if (i > b) return a + (i - a) % n;
7602
- return b - (a - 1 - i) % n;
8053
+ if (i > b) return a + (i - a) % n2;
8054
+ return b - (a - 1 - i) % n2;
7603
8055
  }
7604
8056
  function isFiniteNumber(v) {
7605
8057
  return typeof v === "number" && Number.isFinite(v);
@@ -7915,8 +8367,8 @@ function useSpreadsheetGrid(options) {
7915
8367
  const [range, setRange] = useState16(null);
7916
8368
  const [fillTarget, setFillTarget] = useState16(null);
7917
8369
  const [stack, setStack] = useState16(() => createUndoStack(options?.undoDepth ?? 100));
7918
- const draggingRef = useRef11(null);
7919
- const optionsRef = useRef11(options);
8370
+ const draggingRef = useRef12(null);
8371
+ const optionsRef = useRef12(options);
7920
8372
  optionsRef.current = options;
7921
8373
  const rowIds = options?.rowIds ?? [];
7922
8374
  const columnIds = useMemo6(
@@ -7944,6 +8396,26 @@ function useSpreadsheetGrid(options) {
7944
8396
  for (let i = 0; i < columnIds.length; i += 1) map.set(columnIds[i], i);
7945
8397
  return map;
7946
8398
  }, [columnIds]);
8399
+ const screenPatches = useCallback7(
8400
+ (patches) => {
8401
+ const validate = optionsRef.current?.validateCell;
8402
+ if (!validate) return { accepted: patches, refusals: [] };
8403
+ const accepted = [];
8404
+ const refusals = [];
8405
+ for (const patch of patches) {
8406
+ let reason;
8407
+ try {
8408
+ reason = validate({ rowId: patch.rowId, columnId: patch.columnId, value: patch.value });
8409
+ } catch {
8410
+ reason = void 0;
8411
+ }
8412
+ if (reason) refusals.push({ columnId: patch.columnId, reason });
8413
+ else accepted.push(patch);
8414
+ }
8415
+ return { accepted, refusals };
8416
+ },
8417
+ []
8418
+ );
7947
8419
  const applyPatches = useCallback7(
7948
8420
  async (patches, intent, label) => {
7949
8421
  const current = optionsRef.current;
@@ -8000,9 +8472,10 @@ function useSpreadsheetGrid(options) {
8000
8472
  ...base,
8001
8473
  range: { top: bounds.r0, left: bounds.c0, bottom: bounds.r1, right: bounds.c1 }
8002
8474
  });
8003
- await applyPatches(result.patches, "fill-down", "Filled down");
8004
- announceSkips(result.skipped, result.summary);
8005
- }, [fillRequestBase, bounds, applyPatches, announceSkips]);
8475
+ const screened = screenPatches(result.patches);
8476
+ await applyPatches(screened.accepted, "fill-down", "Filled down");
8477
+ announceSkips([...result.skipped, ...screened.refusals], result.summary);
8478
+ }, [fillRequestBase, bounds, applyPatches, announceSkips, screenPatches]);
8006
8479
  const doClear = useCallback7(async () => {
8007
8480
  const current = optionsRef.current;
8008
8481
  if (!current || !bounds) return;
@@ -8023,9 +8496,13 @@ function useSpreadsheetGrid(options) {
8023
8496
  patches.push({ rowId, columnId: columnId2, value: null });
8024
8497
  }
8025
8498
  }
8026
- await applyPatches(patches, "clear", "Cleared");
8027
- announceSkips(skipped, `Cleared ${patches.length} cells.`);
8028
- }, [bounds, applyPatches, announceSkips]);
8499
+ const screened = screenPatches(patches);
8500
+ await applyPatches(screened.accepted, "clear", "Cleared");
8501
+ announceSkips(
8502
+ [...skipped, ...screened.refusals],
8503
+ `Cleared ${screened.accepted.length} cells.`
8504
+ );
8505
+ }, [bounds, applyPatches, announceSkips, screenPatches]);
8029
8506
  const doUndo = useCallback7(async () => {
8030
8507
  const taken = popUndo(stack);
8031
8508
  if (!taken.entry) return;
@@ -8110,6 +8587,14 @@ function useSpreadsheetGrid(options) {
8110
8587
  case "escape":
8111
8588
  setRange(null);
8112
8589
  return;
8590
+ case "edit":
8591
+ case "editSeeded": {
8592
+ const focused = range?.focus;
8593
+ if (!focused || !current.onEditRequest) return;
8594
+ const seed = action.kind === "editSeeded" ? action.seed : null;
8595
+ if (current.onEditRequest(focused, seed)) event.preventDefault();
8596
+ return;
8597
+ }
8113
8598
  default:
8114
8599
  return;
8115
8600
  }
@@ -8124,6 +8609,13 @@ function useSpreadsheetGrid(options) {
8124
8609
  );
8125
8610
  return gridToTsv(grid);
8126
8611
  }, [range, rowIds, columnIds]);
8612
+ const selectionAddresses = useCallback7(() => {
8613
+ if (!range || !bounds) return { rowIds: [], columnIds: [] };
8614
+ return {
8615
+ rowIds: rowIds.slice(bounds.r0, bounds.r1 + 1),
8616
+ columnIds: columnIds.slice(bounds.c0, bounds.c1 + 1)
8617
+ };
8618
+ }, [range, bounds, rowIds, columnIds]);
8127
8619
  const onCopy = useCallback7(
8128
8620
  (event) => {
8129
8621
  if (!optionsRef.current || !range || targetsAnEditor(event.target)) return;
@@ -8179,11 +8671,12 @@ function useSpreadsheetGrid(options) {
8179
8671
  patches.push({ rowId, columnId: columnId2, value: line[c] ?? null });
8180
8672
  }
8181
8673
  }
8182
- void applyPatches(patches, "paste", "Pasted");
8674
+ const screened = screenPatches(patches);
8675
+ void applyPatches(screened.accepted, "paste", "Pasted");
8183
8676
  const tail = outside > 0 ? ` ${outside} landed past the edge of the table and were ignored.` : "";
8184
- announceSkips(skipped, `Pasted ${patches.length} cells.${tail}`);
8677
+ announceSkips([...skipped, ...screened.refusals], `Pasted ${screened.accepted.length} cells.${tail}`);
8185
8678
  },
8186
- [range, rowIds, columnIds, applyPatches, announceSkips]
8679
+ [range, rowIds, columnIds, applyPatches, announceSkips, screenPatches]
8187
8680
  );
8188
8681
  const cellProps = useCallback7(
8189
8682
  (rowId, columnId2) => {
@@ -8202,6 +8695,10 @@ function useSpreadsheetGrid(options) {
8202
8695
  onMouseDown: (event) => {
8203
8696
  if (event.button !== 0) return;
8204
8697
  event.preventDefault();
8698
+ const origin = event.currentTarget;
8699
+ if (typeof HTMLElement !== "undefined" && origin instanceof HTMLElement) {
8700
+ origin.closest('[data-matrx-table-spreadsheet="true"]')?.focus({ preventScroll: true });
8701
+ }
8205
8702
  setRange(
8206
8703
  (previous) => event.shiftKey && previous ? { anchor: previous.anchor, focus: address } : { anchor: address, focus: address }
8207
8704
  );
@@ -8242,9 +8739,9 @@ function useSpreadsheetGrid(options) {
8242
8739
  },
8243
8740
  []
8244
8741
  );
8245
- const fillTargetRef = useRef11(null);
8742
+ const fillTargetRef = useRef12(null);
8246
8743
  fillTargetRef.current = fillTarget;
8247
- const boundsRef = useRef11(null);
8744
+ const boundsRef = useRef12(null);
8248
8745
  boundsRef.current = bounds;
8249
8746
  useEffect11(() => {
8250
8747
  if (!options) return;
@@ -8263,12 +8760,13 @@ function useSpreadsheetGrid(options) {
8263
8760
  source: { top: source.r0, left: source.c0, bottom: source.r1, right: source.c1 },
8264
8761
  target
8265
8762
  });
8266
- void applyPatches(result.patches, "fill", "Filled");
8267
- announceSkips(result.skipped, result.summary);
8763
+ const screened = screenPatches(result.patches);
8764
+ void applyPatches(screened.accepted, "fill", "Filled");
8765
+ announceSkips([...result.skipped, ...screened.refusals], result.summary);
8268
8766
  };
8269
8767
  window.addEventListener("mouseup", commit);
8270
8768
  return () => window.removeEventListener("mouseup", commit);
8271
- }, [options, fillRequestBase, applyPatches, announceSkips]);
8769
+ }, [options, fillRequestBase, applyPatches, announceSkips, screenPatches]);
8272
8770
  return {
8273
8771
  range,
8274
8772
  bounds,
@@ -8284,17 +8782,19 @@ function useSpreadsheetGrid(options) {
8284
8782
  isFillHandleCell,
8285
8783
  startFillDrag,
8286
8784
  canUndo: stack.past.length > 0,
8287
- canRedo: stack.future.length > 0
8785
+ canRedo: stack.future.length > 0,
8786
+ selectionTsv: selectionAsTsv,
8787
+ selectionAddresses
8288
8788
  };
8289
8789
  }
8290
8790
 
8291
8791
  // src/data-table/coverage.ts
8292
8792
  var PARTIAL_BADGE = "Partial";
8293
8793
  var UNKNOWN_TOTAL_BADGE = "Unknown total";
8294
- var NUMBERS = new Intl.NumberFormat("en-US");
8794
+ var NUMBERS2 = new Intl.NumberFormat("en-US");
8295
8795
  function formatNumber(value) {
8296
8796
  try {
8297
- return NUMBERS.format(value);
8797
+ return NUMBERS2.format(value);
8298
8798
  } catch {
8299
8799
  return String(value);
8300
8800
  }
@@ -8925,7 +9425,8 @@ var filterSchema = z2.union([
8925
9425
  z2.object({ kind: z2.literal("text"), value: z2.string(), mode: z2.enum(["contains", "empty", "not_empty", "null", "not_null"]).optional(), negated: z2.boolean().optional() }),
8926
9426
  z2.object({ kind: z2.literal("select"), value: z2.string(), values: z2.array(z2.string()).optional(), negated: z2.boolean().optional() }),
8927
9427
  z2.object({ kind: z2.literal("boolean"), value: z2.boolean(), negated: z2.boolean().optional() }),
8928
- z2.object({ kind: z2.literal("number"), min: z2.number().optional(), max: z2.number().optional(), negated: z2.boolean().optional() })
9428
+ z2.object({ kind: z2.literal("number"), min: z2.number().optional(), max: z2.number().optional(), negated: z2.boolean().optional() }),
9429
+ z2.object({ kind: z2.literal("date"), preset: z2.enum(TIME_RANGE_PRESET_VALUES).optional(), since: z2.string().datetime({ offset: true }).optional(), until: z2.string().datetime({ offset: true }).optional(), anchor: z2.string().datetime({ offset: true }).optional() })
8929
9430
  ]);
8930
9431
  var layeredRuleSchema = z2.object({ id: z2.string().min(1), field: z2.string().min(1), operator: z2.enum(LAYERED_FILTER_OPERATORS), value: z2.string(), valueTo: z2.string().optional() });
8931
9432
  var snapshotSchema = z2.object({
@@ -8953,7 +9454,8 @@ function normalizeFilters(raw) {
8953
9454
  if (value.kind === "text") filters[id] = { kind: "text", value: value.value, ...value.mode === void 0 ? {} : { mode: value.mode }, ...value.negated === void 0 ? {} : { negated: value.negated } };
8954
9455
  else if (value.kind === "select") filters[id] = { kind: "select", value: value.value, ...value.values === void 0 ? {} : { values: value.values }, ...value.negated === void 0 ? {} : { negated: value.negated } };
8955
9456
  else if (value.kind === "boolean") filters[id] = { kind: "boolean", value: value.value, ...value.negated === void 0 ? {} : { negated: value.negated } };
8956
- else filters[id] = { kind: "number", ...value.min === void 0 ? {} : { min: value.min }, ...value.max === void 0 ? {} : { max: value.max }, ...value.negated === void 0 ? {} : { negated: value.negated } };
9457
+ else if (value.kind === "number") filters[id] = { kind: "number", ...value.min === void 0 ? {} : { min: value.min }, ...value.max === void 0 ? {} : { max: value.max }, ...value.negated === void 0 ? {} : { negated: value.negated } };
9458
+ else filters[id] = { kind: "date", ...value.preset === void 0 ? {} : { preset: value.preset }, ...value.since === void 0 ? {} : { since: value.since }, ...value.until === void 0 ? {} : { until: value.until }, ...value.anchor === void 0 ? {} : { anchor: value.anchor } };
8957
9459
  }
8958
9460
  return filters;
8959
9461
  }
@@ -8987,7 +9489,7 @@ function parseTableViewSnapshot(raw) {
8987
9489
  // src/data-table/MatrxUuidCell.tsx
8988
9490
  import {
8989
9491
  useEffect as useEffect13,
8990
- useRef as useRef12,
9492
+ useRef as useRef13,
8991
9493
  useState as useState18
8992
9494
  } from "react";
8993
9495
  import { jsx as jsx34, jsxs as jsxs24 } from "react/jsx-runtime";
@@ -9032,7 +9534,7 @@ function MatrxUuidCell({
9032
9534
  const [copied, setCopied] = useState18(false);
9033
9535
  const [opening, setOpening] = useState18(false);
9034
9536
  const [peekOpen, setPeekOpen] = useState18(false);
9035
- const timer = useRef12(null);
9537
+ const timer = useRef13(null);
9036
9538
  useEffect13(
9037
9539
  () => () => {
9038
9540
  if (timer.current) clearTimeout(timer.current);
@@ -9500,8 +10002,8 @@ function toNumber(value) {
9500
10002
  if (typeof value === "string") {
9501
10003
  const cleaned = value.replace(/[,\s$€£¥%]/g, "");
9502
10004
  if (cleaned === "") return null;
9503
- const n = Number(cleaned);
9504
- return Number.isFinite(n) ? n : null;
10005
+ const n2 = Number(cleaned);
10006
+ return Number.isFinite(n2) ? n2 : null;
9505
10007
  }
9506
10008
  return null;
9507
10009
  }
@@ -9511,6 +10013,7 @@ function toText(value) {
9511
10013
  return String(value);
9512
10014
  return null;
9513
10015
  }
10016
+ var BARE_CALENDAR_DATE = /^(\d{4})-(\d{2})-(\d{2})(?:[T ]00:00(?::00(?:\.0+)?)?)?$/;
9514
10017
  function toDate(value) {
9515
10018
  if (value instanceof Date) return Number.isNaN(value.getTime()) ? null : value;
9516
10019
  if (typeof value === "number") {
@@ -9518,6 +10021,20 @@ function toDate(value) {
9518
10021
  return Number.isNaN(d.getTime()) ? null : d;
9519
10022
  }
9520
10023
  if (typeof value === "string" && value.trim()) {
10024
+ const bare = BARE_CALENDAR_DATE.exec(value.trim());
10025
+ if (bare) {
10026
+ const [year, month, day] = [
10027
+ Number(bare[1]),
10028
+ Number(bare[2]),
10029
+ Number(bare[3])
10030
+ ];
10031
+ const d2 = new Date(year, month - 1, day);
10032
+ d2.setFullYear(year);
10033
+ if (d2.getFullYear() !== year || d2.getMonth() !== month - 1 || d2.getDate() !== day) {
10034
+ return null;
10035
+ }
10036
+ return d2;
10037
+ }
9521
10038
  const d = new Date(value);
9522
10039
  return Number.isNaN(d.getTime()) ? null : d;
9523
10040
  }
@@ -9539,14 +10056,14 @@ function toList(value) {
9539
10056
  }
9540
10057
  return null;
9541
10058
  }
9542
- function groupedNumber(n, options) {
10059
+ function groupedNumber(n2, options) {
9543
10060
  return new Intl.NumberFormat(void 0, {
9544
10061
  useGrouping: options.useGrouping !== false,
9545
10062
  ...options.precision != null ? {
9546
10063
  minimumFractionDigits: options.precision,
9547
10064
  maximumFractionDigits: options.precision
9548
10065
  } : { maximumFractionDigits: 10 }
9549
- }).format(n);
10066
+ }).format(n2);
9550
10067
  }
9551
10068
  function affix(text, options) {
9552
10069
  return `${options.prefix ?? ""}${text}${options.suffix ?? ""}`;
@@ -9680,8 +10197,8 @@ var DEFS = [
9680
10197
  numericAlign: true,
9681
10198
  optionKeys: ["useGrouping", "precision", "prefix", "suffix"],
9682
10199
  format: (v, o) => {
9683
- const n = toNumber(v);
9684
- return n === null ? null : affix(groupedNumber(n, o), o);
10200
+ const n2 = toNumber(v);
10201
+ return n2 === null ? null : affix(groupedNumber(n2, o), o);
9685
10202
  },
9686
10203
  parse: (raw) => toNumber(raw)
9687
10204
  },
@@ -9696,9 +10213,9 @@ var DEFS = [
9696
10213
  numericAlign: true,
9697
10214
  optionKeys: ["precision", "useGrouping", "prefix", "suffix"],
9698
10215
  format: (v, o) => {
9699
- const n = toNumber(v);
9700
- if (n === null) return null;
9701
- return affix(groupedNumber(n, { ...o, precision: o.precision ?? 2 }), o);
10216
+ const n2 = toNumber(v);
10217
+ if (n2 === null) return null;
10218
+ return affix(groupedNumber(n2, { ...o, precision: o.precision ?? 2 }), o);
9702
10219
  },
9703
10220
  parse: (raw) => toNumber(raw)
9704
10221
  },
@@ -9713,8 +10230,8 @@ var DEFS = [
9713
10230
  numericAlign: true,
9714
10231
  optionKeys: ["currency", "precision"],
9715
10232
  format: (v, o) => {
9716
- const n = toNumber(v);
9717
- if (n === null) return null;
10233
+ const n2 = toNumber(v);
10234
+ if (n2 === null) return null;
9718
10235
  const currency = (o.currency || "USD").toUpperCase();
9719
10236
  try {
9720
10237
  return new Intl.NumberFormat(void 0, {
@@ -9724,9 +10241,9 @@ var DEFS = [
9724
10241
  minimumFractionDigits: o.precision,
9725
10242
  maximumFractionDigits: o.precision
9726
10243
  } : {}
9727
- }).format(n);
10244
+ }).format(n2);
9728
10245
  } catch {
9729
- return `${groupedNumber(n, { precision: o.precision ?? 2 })} ${currency}`;
10246
+ return `${groupedNumber(n2, { precision: o.precision ?? 2 })} ${currency}`;
9730
10247
  }
9731
10248
  },
9732
10249
  parse: (raw) => toNumber(raw)
@@ -9742,9 +10259,9 @@ var DEFS = [
9742
10259
  numericAlign: true,
9743
10260
  optionKeys: ["percentScale", "precision"],
9744
10261
  format: (v, o) => {
9745
- const n = toNumber(v);
9746
- if (n === null) return null;
9747
- const shown = o.percentScale === "fraction" ? n * 100 : n;
10262
+ const n2 = toNumber(v);
10263
+ if (n2 === null) return null;
10264
+ const shown = o.percentScale === "fraction" ? n2 * 100 : n2;
9748
10265
  return `${groupedNumber(shown, o.precision === void 0 ? {} : { precision: o.precision })}%`;
9749
10266
  },
9750
10267
  parse: (raw) => toNumber(raw)
@@ -9760,9 +10277,9 @@ var DEFS = [
9760
10277
  numericAlign: true,
9761
10278
  optionKeys: ["durationUnit"],
9762
10279
  format: (v, o) => {
9763
- const n = toNumber(v);
9764
- if (n === null) return null;
9765
- return formatSignedDuration(n * DURATION_TO_SECONDS[o.durationUnit ?? "seconds"]);
10280
+ const n2 = toNumber(v);
10281
+ if (n2 === null) return null;
10282
+ return formatSignedDuration(n2 * DURATION_TO_SECONDS[o.durationUnit ?? "seconds"]);
9766
10283
  },
9767
10284
  parse: (raw) => toNumber(raw)
9768
10285
  },
@@ -9777,13 +10294,13 @@ var DEFS = [
9777
10294
  numericAlign: true,
9778
10295
  optionKeys: ["useGrouping", "prefix", "suffix"],
9779
10296
  format: (v, o) => {
9780
- const n = toNumber(v);
9781
- if (n === null) return null;
9782
- return affix(groupedNumber(Math.trunc(n), { ...o, precision: 0 }), o);
10297
+ const n2 = toNumber(v);
10298
+ if (n2 === null) return null;
10299
+ return affix(groupedNumber(Math.trunc(n2), { ...o, precision: 0 }), o);
9783
10300
  },
9784
10301
  parse: (raw) => {
9785
- const n = toNumber(raw);
9786
- return n === null ? null : Math.trunc(n);
10302
+ const n2 = toNumber(raw);
10303
+ return n2 === null ? null : Math.trunc(n2);
9787
10304
  }
9788
10305
  },
9789
10306
  {
@@ -9797,15 +10314,15 @@ var DEFS = [
9797
10314
  rich: true,
9798
10315
  optionKeys: ["ratingMax"],
9799
10316
  format: (v, o) => {
9800
- const n = toNumber(v);
9801
- if (n === null) return null;
10317
+ const n2 = toNumber(v);
10318
+ if (n2 === null) return null;
9802
10319
  const max = o.ratingMax ?? 5;
9803
- if (n < 0 || n > max) return null;
9804
- return `${n} / ${max}`;
10320
+ if (n2 < 0 || n2 > max) return null;
10321
+ return `${n2} / ${max}`;
9805
10322
  },
9806
10323
  parse: (raw) => {
9807
- const n = toNumber(raw);
9808
- return n === null ? null : Math.round(n);
10324
+ const n2 = toNumber(raw);
10325
+ return n2 === null ? null : Math.round(n2);
9809
10326
  }
9810
10327
  },
9811
10328
  {
@@ -9818,12 +10335,12 @@ var DEFS = [
9818
10335
  editor: "number",
9819
10336
  numericAlign: true,
9820
10337
  format: (v) => {
9821
- const n = toNumber(v);
9822
- return n === null ? null : formatFileSize(n);
10338
+ const n2 = toNumber(v);
10339
+ return n2 === null ? null : formatFileSize(n2);
9823
10340
  },
9824
10341
  parse: (raw) => {
9825
- const n = toNumber(raw);
9826
- return n === null ? null : Math.trunc(n);
10342
+ const n2 = toNumber(raw);
10343
+ return n2 === null ? null : Math.trunc(n2);
9827
10344
  }
9828
10345
  },
9829
10346
  // ── Choice ────────────────────────────────────────────────────────────────
@@ -10233,8 +10750,19 @@ function DirtySavePill({
10233
10750
  }
10234
10751
 
10235
10752
  // src/data-table/EditableTableCell.tsx
10236
- import { useEffect as useEffect15, useRef as useRef13, useState as useState20 } from "react";
10753
+ import { useEffect as useEffect15, useRef as useRef14, useState as useState20 } from "react";
10237
10754
  import { jsx as jsx39, jsxs as jsxs29 } from "react/jsx-runtime";
10755
+ function CellRefusal({ reason }) {
10756
+ return /* @__PURE__ */ jsx39(
10757
+ "p",
10758
+ {
10759
+ role: "alert",
10760
+ "data-matrx-cell-refusal": "true",
10761
+ className: "mt-1 text-[11px] leading-snug text-amber-700 dark:text-amber-400",
10762
+ children: reason
10763
+ }
10764
+ );
10765
+ }
10238
10766
  function CellEditShell({
10239
10767
  href,
10240
10768
  display,
@@ -10281,12 +10809,17 @@ function EditableTableCell({
10281
10809
  className,
10282
10810
  href,
10283
10811
  editTrigger = "click",
10284
- editRequest
10812
+ editRequest,
10813
+ editSeed
10285
10814
  }) {
10286
10815
  const [open, setOpen] = useState20(false);
10816
+ const [refusal, setRefusal] = useState20(null);
10287
10817
  useEffect15(() => {
10288
10818
  if (editRequest !== void 0) setOpen(true);
10289
10819
  }, [editRequest]);
10820
+ useEffect15(() => {
10821
+ if (!open) setRefusal(null);
10822
+ }, [open]);
10290
10823
  const usePencil = Boolean(href) || editTrigger === "pencil";
10291
10824
  if (editType === "string") {
10292
10825
  return /* @__PURE__ */ jsx39(
@@ -10299,7 +10832,8 @@ function EditableTableCell({
10299
10832
  ...className === void 0 ? {} : { className },
10300
10833
  ...href === void 0 ? {} : { href },
10301
10834
  usePencil,
10302
- ...editRequest === void 0 ? {} : { editRequest }
10835
+ ...editRequest === void 0 ? {} : { editRequest },
10836
+ ...editSeed === void 0 || editSeed === null ? {} : { editSeed }
10303
10837
  }
10304
10838
  );
10305
10839
  }
@@ -10344,25 +10878,33 @@ function EditableTableCell({
10344
10878
  ) }) }),
10345
10879
  /* @__PURE__ */ jsx39(TooltipContent, { children: "Edit value" })
10346
10880
  ] }),
10347
- /* @__PURE__ */ jsx39(
10881
+ /* @__PURE__ */ jsxs29(
10348
10882
  PopoverContent,
10349
10883
  {
10350
10884
  align: "start",
10351
10885
  className: "w-56 p-2",
10352
10886
  onClick: (e) => e.stopPropagation(),
10353
- children: /* @__PURE__ */ jsx39(
10354
- TypedEditor,
10355
- {
10356
- editType,
10357
- value,
10358
- editOptions,
10359
- onCommit: (next) => {
10360
- onCommit(next);
10361
- setOpen(false);
10362
- },
10363
- onCancel: () => setOpen(false)
10364
- }
10365
- )
10887
+ children: [
10888
+ /* @__PURE__ */ jsx39(
10889
+ TypedEditor,
10890
+ {
10891
+ editType,
10892
+ value,
10893
+ editOptions,
10894
+ onCommit: (next) => {
10895
+ const refused = onCommit(next);
10896
+ if (typeof refused === "string" && refused) {
10897
+ setRefusal(refused);
10898
+ return;
10899
+ }
10900
+ setRefusal(null);
10901
+ setOpen(false);
10902
+ },
10903
+ onCancel: () => setOpen(false)
10904
+ }
10905
+ ),
10906
+ refusal ? /* @__PURE__ */ jsx39(CellRefusal, { reason: refusal }) : null
10907
+ ]
10366
10908
  }
10367
10909
  )
10368
10910
  ] });
@@ -10375,17 +10917,31 @@ function InlineStringEditor({
10375
10917
  className,
10376
10918
  href,
10377
10919
  usePencil,
10378
- editRequest
10920
+ editRequest,
10921
+ editSeed
10379
10922
  }) {
10380
10923
  const [editing, setEditing] = useState20(false);
10381
10924
  const [draft, setDraft] = useState20(value);
10382
- const handledEditRequest = useRef13(void 0);
10925
+ const [refusal, setRefusal] = useState20(null);
10926
+ const handledEditRequest = useRef14(void 0);
10927
+ const seedRef = useRef14(editSeed);
10928
+ seedRef.current = editSeed;
10383
10929
  useEffect15(() => {
10384
10930
  if (editRequest === void 0 || handledEditRequest.current === editRequest) return;
10385
10931
  handledEditRequest.current = editRequest;
10386
- setDraft(value);
10932
+ setDraft(seedRef.current ?? value);
10933
+ setRefusal(null);
10387
10934
  setEditing(true);
10388
10935
  }, [editRequest, value]);
10936
+ const commit = (next) => {
10937
+ const refused = onCommit(next);
10938
+ if (typeof refused === "string" && refused) {
10939
+ setRefusal(refused);
10940
+ return false;
10941
+ }
10942
+ setRefusal(null);
10943
+ return true;
10944
+ };
10389
10945
  if (!editing) {
10390
10946
  if (usePencil) {
10391
10947
  return /* @__PURE__ */ jsx39(
@@ -10436,32 +10992,38 @@ function InlineStringEditor({
10436
10992
  /* @__PURE__ */ jsx39(TooltipContent, { children: "Click to edit" })
10437
10993
  ] });
10438
10994
  }
10439
- return /* @__PURE__ */ jsx39(
10440
- Input,
10441
- {
10442
- autoFocus: true,
10443
- value: draft,
10444
- onClick: (e) => e.stopPropagation(),
10445
- onChange: (e) => setDraft(e.target.value),
10446
- onBlur: () => {
10447
- onCommit(draft);
10448
- setEditing(false);
10449
- },
10450
- onKeyDown: (e) => {
10451
- if (e.key === "Enter") {
10452
- e.preventDefault();
10453
- onCommit(draft);
10454
- setEditing(false);
10455
- }
10456
- if (e.key === "Escape") {
10457
- e.preventDefault();
10458
- setEditing(false);
10459
- }
10460
- },
10461
- className: "h-7 text-sm",
10462
- style: { fontSize: "16px" }
10463
- }
10464
- );
10995
+ return /* @__PURE__ */ jsxs29("span", { className: "block w-full", children: [
10996
+ /* @__PURE__ */ jsx39(
10997
+ Input,
10998
+ {
10999
+ autoFocus: true,
11000
+ value: draft,
11001
+ onClick: (e) => e.stopPropagation(),
11002
+ onChange: (e) => {
11003
+ setDraft(e.target.value);
11004
+ if (refusal) setRefusal(null);
11005
+ },
11006
+ onBlur: () => {
11007
+ if (commit(draft)) setEditing(false);
11008
+ },
11009
+ onKeyDown: (e) => {
11010
+ if (e.key === "Enter") {
11011
+ e.preventDefault();
11012
+ if (commit(draft)) setEditing(false);
11013
+ }
11014
+ if (e.key === "Escape") {
11015
+ e.preventDefault();
11016
+ setRefusal(null);
11017
+ setEditing(false);
11018
+ }
11019
+ },
11020
+ "aria-invalid": refusal ? true : void 0,
11021
+ className: cn("h-7 text-sm", refusal && "border-amber-500 focus-visible:ring-amber-500"),
11022
+ style: { fontSize: "16px" }
11023
+ }
11024
+ ),
11025
+ refusal ? /* @__PURE__ */ jsx39(CellRefusal, { reason: refusal }) : null
11026
+ ] });
10465
11027
  }
10466
11028
  function TypedEditor({
10467
11029
  editType,
@@ -10646,8 +11208,8 @@ function NumberEditor({
10646
11208
  style: { fontSize: "16px" },
10647
11209
  onKeyDown: (e) => {
10648
11210
  if (e.key === "Enter") {
10649
- const n = Number(draft);
10650
- if (Number.isFinite(n)) onCommit(n);
11211
+ const n2 = Number(draft);
11212
+ if (Number.isFinite(n2)) onCommit(n2);
10651
11213
  }
10652
11214
  if (e.key === "Escape") onCancel();
10653
11215
  }
@@ -10674,8 +11236,8 @@ function NumberEditor({
10674
11236
  className: "h-7",
10675
11237
  "aria-label": "Apply value",
10676
11238
  onClick: () => {
10677
- const n = Number(draft);
10678
- if (Number.isFinite(n)) onCommit(n);
11239
+ const n2 = Number(draft);
11240
+ if (Number.isFinite(n2)) onCommit(n2);
10679
11241
  },
10680
11242
  children: /* @__PURE__ */ jsx39(Check, { className: "h-3.5 w-3.5" })
10681
11243
  }
@@ -11195,6 +11757,8 @@ function filterKindLabel(kind) {
11195
11757
  return "Boolean";
11196
11758
  case "number":
11197
11759
  return "Number";
11760
+ case "date":
11761
+ return "Date";
11198
11762
  case "auto":
11199
11763
  return "Auto";
11200
11764
  case false:
@@ -11483,11 +12047,12 @@ function MatrxDataTableCore({
11483
12047
  virtualize,
11484
12048
  coverage,
11485
12049
  spreadsheet,
12050
+ facets,
11486
12051
  rowHeight: explicitRowHeight
11487
12052
  }) {
11488
12053
  const appendQuery = query?.mode === "controlled-append" ? query : null;
11489
12054
  const appendQueryKey = appendQuery?.pagination.queryKey ?? null;
11490
- const committedAppendQueryKey = useRef14(appendQueryKey);
12055
+ const committedAppendQueryKey = useRef15(appendQueryKey);
11491
12056
  const appendSourceChanged = appendQuery !== null && committedAppendQueryKey.current !== appendQueryKey;
11492
12057
  const data = (appendQuery ? appendQuery.pagination.rows : suppliedData) ?? [];
11493
12058
  const isLoading = appendQuery ? appendQuery.pagination.loading : suppliedLoading;
@@ -11502,7 +12067,7 @@ function MatrxDataTableCore({
11502
12067
  const [savedViewError, setSavedViewError] = useState22(null);
11503
12068
  const [toolbarActionError, setToolbarActionError] = useState22(null);
11504
12069
  const [toolbarActionPending, setToolbarActionPending] = useState22({ refresh: false, add: false });
11505
- const toolbarActionPendingRef = useRef14({ refresh: false, add: false });
12070
+ const toolbarActionPendingRef = useRef15({ refresh: false, add: false });
11506
12071
  const CHECKBOX_TAP_AREA = "h-4 w-4 touch-manipulation";
11507
12072
  const mobileCardsClass = mobileCardsBreakpoint === "lg" ? "lg:hidden" : "sm:hidden";
11508
12073
  const tableWithCardsClass = mobileCardsBreakpoint === "lg" ? "max-lg:hidden" : "max-sm:hidden";
@@ -11640,9 +12205,9 @@ function MatrxDataTableCore({
11640
12205
  const [windowRowSnapshot, setWindowRowSnapshot] = useState22(null);
11641
12206
  const editScopeKey = appendQueryKey === null ? "\0local" : `append:${appendQueryKey}`;
11642
12207
  const [editsByScope, setEditsByScope] = useState22({});
11643
- const editRevisionRef = useRef14({});
12208
+ const editRevisionRef = useRef15({});
11644
12209
  const [savingByScope, setSavingByScope] = useState22({});
11645
- const savingScopesRef = useRef14(/* @__PURE__ */ new Set());
12210
+ const savingScopesRef = useRef15(/* @__PURE__ */ new Set());
11646
12211
  const [editRequest, setEditRequest] = useState22(null);
11647
12212
  const [confirmForeignDraftDiscard, setConfirmForeignDraftDiscard] = useState22(false);
11648
12213
  const edits = editsByScope[editScopeKey]?.values ?? {};
@@ -11729,13 +12294,13 @@ function MatrxDataTableCore({
11729
12294
  onWindowRowIdChange,
11730
12295
  selectionConfig
11731
12296
  ]);
11732
- const scrollRef = useRef14(null);
11733
- const tableRef = useRef14(null);
11734
- const tableHeaderRef = useRef14(null);
11735
- const cardsScrollRef = useRef14(null);
12297
+ const scrollRef = useRef15(null);
12298
+ const tableRef = useRef15(null);
12299
+ const tableHeaderRef = useRef15(null);
12300
+ const cardsScrollRef = useRef15(null);
11736
12301
  const [documentStickyHeader, setDocumentStickyHeader] = useState22(false);
11737
- const documentStickyHeaderRef = useRef14(false);
11738
- const previousAppendKeyForScrollReset = useRef14(appendQueryKey);
12302
+ const documentStickyHeaderRef = useRef15(false);
12303
+ const previousAppendKeyForScrollReset = useRef15(appendQueryKey);
11739
12304
  useLayoutEffect2(() => {
11740
12305
  const previous = previousAppendKeyForScrollReset.current;
11741
12306
  previousAppendKeyForScrollReset.current = appendQueryKey;
@@ -11761,7 +12326,7 @@ function MatrxDataTableCore({
11761
12326
  ...appendScrollPolicy?.mode === "scroll" && appendScrollPolicy.thresholdPx !== void 0 ? { thresholdPx: appendScrollPolicy.thresholdPx } : {},
11762
12327
  ...appendScrollPolicy?.mode === "scroll" && appendScrollPolicy.intentTimeoutMs !== void 0 ? { intentTimeoutMs: appendScrollPolicy.intentTimeoutMs } : {}
11763
12328
  });
11764
- const rootRef = useRef14(null);
12329
+ const rootRef = useRef15(null);
11765
12330
  useClippedContentGuard(rootRef, {
11766
12331
  label: typeof copy === "object" && copy.listLabel ? `Table: ${copy.listLabel}` : "MatrxDataTable"
11767
12332
  });
@@ -11863,11 +12428,11 @@ function MatrxDataTableCore({
11863
12428
  () => normalizeColumnWidthsForColumns(columnState?.widths, columns)
11864
12429
  );
11865
12430
  const [fixedTableGeometry, setFixedTableGeometry] = useState22(null);
11866
- const knownColumnIds = useRef14(new Set(columns.map(columnId)));
11867
- const draggedColumnId = useRef14(null);
11868
- const columnDragActive = useRef14(false);
11869
- const columnDragRaf = useRef14(null);
11870
- const columnDragScrollDirection = useRef14(0);
12431
+ const knownColumnIds = useRef15(new Set(columns.map(columnId)));
12432
+ const draggedColumnId = useRef15(null);
12433
+ const columnDragActive = useRef15(false);
12434
+ const columnDragRaf = useRef15(null);
12435
+ const columnDragScrollDirection = useRef15(0);
11871
12436
  const [columnDragPreview, setColumnDragPreview] = useState22(null);
11872
12437
  const stopColumnDragScroll = useCallback8(() => {
11873
12438
  if (columnDragRaf.current !== null) cancelAnimationFrame(columnDragRaf.current);
@@ -11922,7 +12487,7 @@ function MatrxDataTableCore({
11922
12487
  () => normalizeColumnWidthsForColumns(columnState?.widths ?? columnWidths, columns),
11923
12488
  [columns, columnState?.widths, columnWidths]
11924
12489
  );
11925
- const resizeGestureSnapshotRef = useRef14(null);
12490
+ const resizeGestureSnapshotRef = useRef15(null);
11926
12491
  const measureVisibleColumnWidths = useCallback8(() => {
11927
12492
  const measured = {};
11928
12493
  const headers = tableRef.current ? Array.from(tableRef.current.querySelectorAll(
@@ -12228,7 +12793,7 @@ function MatrxDataTableCore({
12228
12793
  intelligentSearchEnabled,
12229
12794
  searchScope
12230
12795
  ]);
12231
- const lastNotifiedViewRows = useRef14(null);
12796
+ const lastNotifiedViewRows = useRef15(null);
12232
12797
  useEffect16(() => {
12233
12798
  if (!onViewChange) {
12234
12799
  lastNotifiedViewRows.current = null;
@@ -12348,7 +12913,7 @@ function MatrxDataTableCore({
12348
12913
  const columnVirtualizeActive = virtualizeConfig !== null && virtualizeConfig.enabled !== false && virtualizeConfig.columns === true && visibleColumns.length - frozenRunLength > 30 && !hierarchy && mobileCards === void 0;
12349
12914
  const [rowWindow, setRowWindow] = useState22(null);
12350
12915
  const [columnWindow, setColumnWindow] = useState22(null);
12351
- const windowInputs = useRef14({
12916
+ const windowInputs = useRef15({
12352
12917
  virtualizeActive: false,
12353
12918
  bodyEntryCount: 0,
12354
12919
  virtualRowHeight: 40,
@@ -12498,7 +13063,7 @@ function MatrxDataTableCore({
12498
13063
  windowed.push({ kind: "spacer", height: effectiveRowWindow.padEnd, place: "end" });
12499
13064
  return windowed;
12500
13065
  }, [groupedRenderRows, collapsedGroups, paginated, effectiveRowWindow]);
12501
- const getRowIdRef = useRef14(getRowId);
13066
+ const getRowIdRef = useRef15(getRowId);
12502
13067
  getRowIdRef.current = getRowId;
12503
13068
  const spreadsheetRowIds = useMemo8(
12504
13069
  () => paginated.map((row) => getRowIdRef.current(row)),
@@ -12540,9 +13105,92 @@ function MatrxDataTableCore({
12540
13105
  ...spreadsheet.undoDepth === void 0 ? {} : { undoDepth: spreadsheet.undoDepth },
12541
13106
  ...spreadsheet.fillHandle === void 0 ? {} : { fillHandle: spreadsheet.fillHandle },
12542
13107
  ...spreadsheet.paste === void 0 ? {} : { paste: spreadsheet.paste },
12543
- notify: spreadsheet.notify ?? ((message) => toast.success(message))
13108
+ notify: spreadsheet.notify ?? ((message) => toast.success(message)),
13109
+ onEditRequest: (address, seed) => requestCellEditRef.current(address, seed),
13110
+ ...spreadsheet.validateCell ? { validateCell: spreadsheet.validateCell } : {}
12544
13111
  } : null
12545
13112
  );
13113
+ const sheetRef = useRef15(sheet);
13114
+ sheetRef.current = sheet;
13115
+ const searchValueRef = useRef15(searchValue);
13116
+ searchValueRef.current = searchValue;
13117
+ const facetsEnabled = Boolean(facets && facets.enabled !== false);
13118
+ const [facetState, setFacetState] = useState22({});
13119
+ const facetReadCell = useCallback8(
13120
+ (row, colId) => {
13121
+ if (facets?.readCell) return facets.readCell(row, colId);
13122
+ const col = visibleColumns.find((candidate) => columnId(candidate) === colId);
13123
+ return col ? getFilterValue(row, col) : void 0;
13124
+ },
13125
+ [facets, visibleColumns]
13126
+ );
13127
+ const facetInputsRef = useRef15({ data, facets, facetReadCell });
13128
+ facetInputsRef.current = { data, facets, facetReadCell };
13129
+ const facetRequestRef = useRef15(/* @__PURE__ */ new Map());
13130
+ const requestFacets = useCallback8((colId) => {
13131
+ const { data: rows, facets: config, facetReadCell: readCell } = facetInputsRef.current;
13132
+ if (!config || config.enabled === false) return;
13133
+ const complete = localFacetsAreComplete(rows.length, config.totalRows);
13134
+ const limit = config.limit;
13135
+ if (complete || !config.source) {
13136
+ setFacetState((current) => ({
13137
+ ...current,
13138
+ [colId]: {
13139
+ loading: false,
13140
+ data: computeColumnFacets({
13141
+ columnId: colId,
13142
+ rows,
13143
+ readValue: readCell,
13144
+ ...limit === void 0 ? {} : { limit },
13145
+ complete
13146
+ })
13147
+ }
13148
+ }));
13149
+ return;
13150
+ }
13151
+ const ticket = (facetRequestRef.current.get(colId) ?? 0) + 1;
13152
+ facetRequestRef.current.set(colId, ticket);
13153
+ setFacetState((current) => ({
13154
+ ...current,
13155
+ [colId]: { loading: true, data: current[colId]?.data ?? null }
13156
+ }));
13157
+ void Promise.resolve().then(
13158
+ () => config.source?.({ columnId: colId, search: searchValueRef.current, limit: limit ?? 200 })
13159
+ ).then((answer) => {
13160
+ if (facetRequestRef.current.get(colId) !== ticket) return;
13161
+ if (!answer) {
13162
+ setFacetState((current) => ({
13163
+ ...current,
13164
+ [colId]: {
13165
+ loading: false,
13166
+ data: computeColumnFacets({
13167
+ columnId: colId,
13168
+ rows: facetInputsRef.current.data,
13169
+ readValue: facetInputsRef.current.facetReadCell,
13170
+ ...limit === void 0 ? {} : { limit },
13171
+ complete: false
13172
+ })
13173
+ }
13174
+ }));
13175
+ return;
13176
+ }
13177
+ setFacetState((current) => ({
13178
+ ...current,
13179
+ [colId]: { loading: false, data: { ...answer, answeredBy: "source", complete: true } }
13180
+ }));
13181
+ }).catch((error) => {
13182
+ if (facetRequestRef.current.get(colId) !== ticket) return;
13183
+ setFacetState((current) => ({
13184
+ ...current,
13185
+ [colId]: { loading: false, data: null, error: describeFacetFailure(error) }
13186
+ }));
13187
+ });
13188
+ }, []);
13189
+ useEffect16(() => {
13190
+ if (!facetsEnabled) return;
13191
+ setFacetState({});
13192
+ facetRequestRef.current = /* @__PURE__ */ new Map();
13193
+ }, [facetsEnabled, data, searchValue]);
12546
13194
  useEffect16(() => {
12547
13195
  if (!controlledQuery || controlledQuery.state.page === safePage) return;
12548
13196
  emitControlledQueryChange({ page: safePage });
@@ -12587,11 +13235,11 @@ function MatrxDataTableCore({
12587
13235
  });
12588
13236
  const hasActiveFilters = activeFilterCount > 0 || Boolean(searchValue.trim()) || intelligentSearchEnabled && searchScope !== "all" || Boolean(anyOfValue.trim()) || completeLayeredFilterRules(layeredFilters).length > 0 || facetActive;
12589
13237
  const changeCount = useMemo8(() => {
12590
- let n = 0;
13238
+ let n2 = 0;
12591
13239
  for (const fields of Object.values(edits)) {
12592
- n += Object.keys(fields).length;
13240
+ n2 += Object.keys(fields).length;
12593
13241
  }
12594
- return n;
13242
+ return n2;
12595
13243
  }, [edits]);
12596
13244
  const showSearch = toolbar?.search !== false;
12597
13245
  const openDetail = (row) => {
@@ -12670,6 +13318,23 @@ function MatrxDataTableCore({
12670
13318
  icon: void 0
12671
13319
  } : emptyState;
12672
13320
  const commitCell = (rowId, field, next) => {
13321
+ if (edit?.validate) {
13322
+ const row = data.find((candidate) => getRowId(candidate) === rowId);
13323
+ if (row) {
13324
+ let refusal;
13325
+ try {
13326
+ refusal = edit.validate({
13327
+ row: applyRowEdits(row, edits[rowId]),
13328
+ rowId,
13329
+ columnId: field,
13330
+ value: next
13331
+ });
13332
+ } catch {
13333
+ refusal = void 0;
13334
+ }
13335
+ if (refusal) return refusal;
13336
+ }
13337
+ }
12673
13338
  const scope = editScopeKey;
12674
13339
  const nextEdits = { [rowId]: { [field]: next } };
12675
13340
  const revision = setCellEdit(rowId, field, next);
@@ -12714,9 +13379,29 @@ function MatrxDataTableCore({
12714
13379
  setEditRequest((current) => ({
12715
13380
  rowId: getRowId(row),
12716
13381
  field,
12717
- nonce: (current?.nonce ?? 0) + 1
13382
+ nonce: (current?.nonce ?? 0) + 1,
13383
+ seed: null
13384
+ }));
13385
+ };
13386
+ const requestCellEdit = (address, seed) => {
13387
+ if (!editEnabled) return false;
13388
+ const row = data.find((candidate) => getRowId(candidate) === address.rowId);
13389
+ if (!row) return false;
13390
+ const column = visibleColumns.find(
13391
+ (candidate) => columnId(candidate) === address.fieldName
13392
+ );
13393
+ if (!column?.editable || !(column.editableIf?.(row) ?? true)) return false;
13394
+ const field = column.accessorKey ? String(column.accessorKey) : columnId(column);
13395
+ setEditRequest((current) => ({
13396
+ rowId: address.rowId,
13397
+ field,
13398
+ nonce: (current?.nonce ?? 0) + 1,
13399
+ seed
12718
13400
  }));
13401
+ return true;
12719
13402
  };
13403
+ const requestCellEditRef = useRef15(requestCellEdit);
13404
+ requestCellEditRef.current = requestCellEdit;
12720
13405
  const buildRecordControls = (row) => {
12721
13406
  const rowId = getRowId(row);
12722
13407
  const pending = edits[rowId];
@@ -12957,17 +13642,78 @@ function MatrxDataTableCore({
12957
13642
  setEdits({});
12958
13643
  edit?.onCancel?.();
12959
13644
  };
12960
- useEffect16(() => {
12961
- if (!host.rowContextRegistry || !contextMenu && !host.createDefaultRowContext) return;
12962
- return host.rowContextRegistry.register(tableContextMenuInstanceId, (rowId) => {
13645
+ const resolveMenuLevel = (target) => {
13646
+ const rowFor = (rowId) => {
12963
13647
  const row = data.find((candidate) => getRowId(candidate) === rowId);
12964
13648
  if (!row) return null;
12965
- const pending = edits[rowId];
12966
- const displayRow = applyRowEdits(row, pending);
12967
- const controls = buildRecordControls(displayRow);
12968
- return contextMenu?.resolveRowContext(displayRow, controls) ?? host.createDefaultRowContext?.(displayRow, controls) ?? null;
12969
- });
12970
- }, [buildRecordControls, contextMenu, data, getRowId, host.createDefaultRowContext, host.rowContextRegistry, tableContextMenuInstanceId]);
13649
+ return applyRowEdits(row, edits[rowId]);
13650
+ };
13651
+ const columnFor = (id) => visibleColumns.find((col) => columnId(col) === id);
13652
+ const labelFor = (col, id) => col?.label ?? (typeof col?.header === "string" ? col.header : id);
13653
+ const fromHost = (payload) => host.createDefaultMenuContext?.(payload) ?? null;
13654
+ switch (target.level) {
13655
+ case "row": {
13656
+ if (!target.rowId) return null;
13657
+ const displayRow = rowFor(target.rowId);
13658
+ if (!displayRow) return null;
13659
+ const controls = buildRecordControls(displayRow);
13660
+ return contextMenu?.resolveRowContext?.(displayRow, controls) ?? fromHost({ level: "row", row: displayRow, rowId: target.rowId, controls });
13661
+ }
13662
+ case "cell": {
13663
+ if (!target.rowId || !target.columnId) return null;
13664
+ const displayRow = rowFor(target.rowId);
13665
+ const col = columnFor(target.columnId);
13666
+ if (!displayRow || !col) return null;
13667
+ const controls = buildRecordControls(displayRow);
13668
+ const value = spreadsheet?.readCell ? spreadsheet.readCell(displayRow, target.columnId) : getCellValue(displayRow, col);
13669
+ const args = {
13670
+ row: displayRow,
13671
+ rowId: target.rowId,
13672
+ columnId: target.columnId,
13673
+ columnLabel: labelFor(col, target.columnId),
13674
+ value,
13675
+ text: stringifyCellValue(value),
13676
+ controls
13677
+ };
13678
+ return contextMenu?.resolveCellContext?.(args) ?? fromHost({ level: "cell", ...args });
13679
+ }
13680
+ case "column": {
13681
+ if (!target.columnId) return null;
13682
+ const col = columnFor(target.columnId);
13683
+ if (!col) return null;
13684
+ const args = {
13685
+ columnId: target.columnId,
13686
+ columnLabel: labelFor(col, target.columnId),
13687
+ values: paginated.map(
13688
+ (row) => getCellValue(applyRowEdits(row, edits[getRowId(row)]), col)
13689
+ )
13690
+ };
13691
+ return contextMenu?.resolveColumnContext?.(args) ?? fromHost({ level: "column", ...args });
13692
+ }
13693
+ case "selection": {
13694
+ const addresses = sheetRef.current.selectionAddresses();
13695
+ const cellCount = addresses.rowIds.length * addresses.columnIds.length;
13696
+ if (cellCount <= 1) {
13697
+ return resolveMenuLevel({ ...target, level: "cell" });
13698
+ }
13699
+ const args = { ...addresses, tsv: sheetRef.current.selectionTsv(), cellCount };
13700
+ return contextMenu?.resolveSelectionContext?.(args) ?? fromHost({ level: "selection", ...args });
13701
+ }
13702
+ case "table": {
13703
+ const args = { rowCount: data.length, columnIds: visibleColumns.map((col) => columnId(col)) };
13704
+ return contextMenu?.resolveTableContext?.(args) ?? fromHost({ level: "table", ...args });
13705
+ }
13706
+ }
13707
+ };
13708
+ const resolveMenuLevelRef = useRef15(resolveMenuLevel);
13709
+ resolveMenuLevelRef.current = resolveMenuLevel;
13710
+ useEffect16(() => {
13711
+ if (!host.rowContextRegistry) return;
13712
+ return host.rowContextRegistry.register(
13713
+ tableContextMenuInstanceId,
13714
+ (target) => resolveMenuLevelRef.current(target)
13715
+ );
13716
+ }, [host.rowContextRegistry, tableContextMenuInstanceId]);
12971
13717
  const showActionsCol = windowEnabled || Boolean(rowActions) || showInlineRowCopy;
12972
13718
  const selectedIdSet = useMemo8(
12973
13719
  () => new Set(selection?.selectedIds ?? []),
@@ -12985,7 +13731,7 @@ function MatrxDataTableCore({
12985
13731
  () => data.filter((row) => selectedIdSet.has(getRowId(row))),
12986
13732
  [data, selectedIdSet, getRowId]
12987
13733
  );
12988
- const lastToggledIndex = useRef14(null);
13734
+ const lastToggledIndex = useRef15(null);
12989
13735
  const setSelectedIds = (next) => selection?.onSelectedIdsChange([...next]);
12990
13736
  const toggleRowSelected = (index, shiftKey) => {
12991
13737
  if (!selection) return;
@@ -13049,8 +13795,19 @@ function MatrxDataTableCore({
13049
13795
  dirty: Boolean(rowEdits && field in rowEdits),
13050
13796
  onCommit: (next) => commitCell(id, field, next),
13051
13797
  ...href === void 0 ? {} : { href },
13052
- ...col.editTrigger === void 0 ? {} : { editTrigger: col.editTrigger },
13053
- ...editRequest?.rowId === id && editRequest.field === field ? { editRequest: editRequest.nonce } : {}
13798
+ ...spreadsheetEnabled ? {
13799
+ // 🚨 THE CLICK LAW, enforced where the two paths meet. In a grid
13800
+ // with spreadsheet gestures a single click SELECTS a cell — it
13801
+ // may never drop a person into a free-text buffer, because the
13802
+ // same click is how they start a range, and an editor that opens
13803
+ // under the pointer eats the drag and the next keystroke. So the
13804
+ // editor opens the way it does in Excel: Enter, F2, the hover
13805
+ // pencil, or simply typing. An explicit `editTrigger: "click"`
13806
+ // does not override this; the law is the table's, not the
13807
+ // column's.
13808
+ editTrigger: "pencil"
13809
+ } : col.editTrigger === void 0 ? {} : { editTrigger: col.editTrigger },
13810
+ ...editRequest?.rowId === id && editRequest.field === field ? { editRequest: editRequest.nonce, editSeed: editRequest.seed } : {}
13054
13811
  }
13055
13812
  );
13056
13813
  }
@@ -13808,6 +14565,7 @@ function MatrxDataTableCore({
13808
14565
  "th",
13809
14566
  {
13810
14567
  "data-matrx-table-column-id": id,
14568
+ "data-matrx-header-column-id": id,
13811
14569
  "data-matrx-column-drag-shadow": isColumnDragShadow ? "true" : void 0,
13812
14570
  "aria-sort": isSorted ? sort?.direction === "asc" ? "ascending" : "descending" : void 0,
13813
14571
  className: cn(
@@ -13844,6 +14602,12 @@ function MatrxDataTableCore({
13844
14602
  {
13845
14603
  label: col.header,
13846
14604
  labelText: headerLabel,
14605
+ ...facetsEnabled ? {
14606
+ facets: facetState[id]?.data ?? null,
14607
+ facetsLoading: facetState[id]?.loading ?? false,
14608
+ ...facetState[id]?.error === void 0 ? {} : { facetsError: facetState[id]?.error },
14609
+ onFilterOpen: () => requestFacets(id)
14610
+ } : {},
13847
14611
  sortable: col.sortable !== false,
13848
14612
  isSorted: Boolean(isSorted),
13849
14613
  sortDirection: sort?.direction ?? defaultSortDirection,
@@ -14170,7 +14934,7 @@ function MatrxDataTableCore({
14170
14934
  "div",
14171
14935
  {
14172
14936
  "data-matrx-cell-content": true,
14173
- className: "min-w-0 max-w-full flex-1 whitespace-normal break-words [overflow-wrap:anywhere] sm:[&_*]:min-w-0 sm:[&_*]:whitespace-normal sm:[&_*]:[overflow-wrap:anywhere] sm:[&_.truncate]:whitespace-nowrap sm:[&_[data-slot=badge]]:whitespace-nowrap sm:[&_[data-slot=badge]]:[overflow-wrap:normal] sm:[&_[data-matrx-cell-control]]:w-max sm:[&_[data-matrx-cell-control]]:min-w-max sm:[&_[data-matrx-cell-control]]:max-w-none sm:[&_[data-matrx-cell-control]]:whitespace-nowrap sm:[&_[data-matrx-cell-control]]:[overflow-wrap:normal] sm:[&_[data-matrx-cell-control]_*]:whitespace-nowrap sm:[&_[data-matrx-cell-control]_*]:[overflow-wrap:normal]",
14937
+ className: "min-w-0 max-w-full flex-1 whitespace-normal break-words [overflow-wrap:anywhere] sm:[&_*]:min-w-0 sm:[&_*]:whitespace-normal sm:[&_*]:[overflow-wrap:anywhere] sm:[&_.truncate]:whitespace-nowrap sm:[&_[data-slot=badge]]:whitespace-nowrap sm:[&_[data-slot=badge]]:[overflow-wrap:normal] sm:[&_[data-matrx-cell-control]]:w-max sm:[&_[data-matrx-cell-control]]:min-w-max sm:[&_[data-matrx-cell-control]]:max-w-none sm:[&_[data-matrx-cell-control]]:whitespace-nowrap sm:[&_[data-matrx-cell-control]]:[overflow-wrap:normal] sm:[&_[data-matrx-cell-control]_*]:whitespace-nowrap sm:[&_[data-matrx-cell-control]_*]:[overflow-wrap:normal] [&_[data-matrx-cell-fill]]:!w-full [&_[data-matrx-cell-fill]]:!min-w-0 [&_[data-matrx-cell-fill]]:!max-w-full sm:[&_[data-matrx-cell-fill]]:!w-full sm:[&_[data-matrx-cell-fill]]:!min-w-0 sm:[&_[data-matrx-cell-fill]]:!max-w-full",
14174
14938
  children: renderRecordCell(row, col, index)
14175
14939
  }
14176
14940
  )
@@ -14518,8 +15282,8 @@ function MatrxDataTableCore({
14518
15282
  itemsPerPage: pageSize === 0 && allRowsAreLoaded ? 0 : effectivePageSize,
14519
15283
  currentPage: safePage,
14520
15284
  onPageChange: setPage,
14521
- onItemsPerPageChange: (n) => {
14522
- setPageSize(n);
15285
+ onItemsPerPageChange: (n2) => {
15286
+ setPageSize(n2);
14523
15287
  if (!controlledQuery) setPage(1);
14524
15288
  },
14525
15289
  pageSizeOptions: pageSizeOptions.filter((size) => size > 0),
@@ -15478,6 +16242,8 @@ export {
15478
16242
  SavedViewsControl,
15479
16243
  TABLE_STYLE_METADATA_KEY,
15480
16244
  TABLE_STYLE_VERSION,
16245
+ TIME_RANGE_PRESETS,
16246
+ TIME_RANGE_PRESET_VALUES,
15481
16247
  TableTitleRow,
15482
16248
  TableViewTabs,
15483
16249
  UNKNOWN_TOTAL_BADGE,
@@ -15501,6 +16267,7 @@ export {
15501
16267
  computeVariableWindow,
15502
16268
  computeWindow,
15503
16269
  createUndoStack,
16270
+ dateFilterBounds,
15504
16271
  describeCoverage,
15505
16272
  detectSeries,
15506
16273
  directClickKinds,