@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
@@ -0,0 +1,145 @@
1
+ "use client";
2
+ "use strict";
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
7
+ var __export = (target, all) => {
8
+ for (var name in all)
9
+ __defProp(target, name, { get: all[name], enumerable: true });
10
+ };
11
+ var __copyProps = (to, from, except, desc) => {
12
+ if (from && typeof from === "object" || typeof from === "function") {
13
+ for (let key of __getOwnPropNames(from))
14
+ if (!__hasOwnProp.call(to, key) && key !== except)
15
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
16
+ }
17
+ return to;
18
+ };
19
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
20
+
21
+ // src/data-table/date-filter.ts
22
+ var date_filter_exports = {};
23
+ __export(date_filter_exports, {
24
+ TIME_RANGE_PRESETS: () => TIME_RANGE_PRESETS,
25
+ TIME_RANGE_PRESET_VALUES: () => TIME_RANGE_PRESET_VALUES,
26
+ dateFilterBounds: () => dateFilterBounds
27
+ });
28
+ module.exports = __toCommonJS(date_filter_exports);
29
+ var TIME_RANGE_PRESET_VALUES = [
30
+ "all",
31
+ "1m",
32
+ "15m",
33
+ "30m",
34
+ "1h",
35
+ "2h",
36
+ "6h",
37
+ "12h",
38
+ "today",
39
+ "yesterday",
40
+ "this_week",
41
+ "this_month",
42
+ "this_year",
43
+ "older_1m",
44
+ "older_15m",
45
+ "older_30m",
46
+ "older_1h",
47
+ "older_2h",
48
+ "older_6h",
49
+ "older_12h",
50
+ "older_today",
51
+ "older_this_week",
52
+ "older_this_month",
53
+ "older_this_year"
54
+ ];
55
+ var TIME_RANGE_PRESETS = [
56
+ { value: "all", label: "All time", group: "all" },
57
+ { value: "1m", label: "Last minute", group: "recent" },
58
+ { value: "15m", label: "Last 15 minutes", group: "recent" },
59
+ { value: "30m", label: "Last 30 minutes", group: "recent" },
60
+ { value: "1h", label: "Last hour", group: "recent" },
61
+ { value: "2h", label: "Last 2 hours", group: "recent" },
62
+ { value: "6h", label: "Last 6 hours", group: "recent" },
63
+ { value: "12h", label: "Last 12 hours", group: "recent" },
64
+ { value: "today", label: "Today", group: "calendar" },
65
+ { value: "yesterday", label: "Yesterday", group: "calendar" },
66
+ { value: "this_week", label: "This week", group: "calendar" },
67
+ { value: "this_month", label: "This month", group: "calendar" },
68
+ { value: "this_year", label: "This year", group: "calendar" },
69
+ { value: "older_1m", label: "Older than 1 minute", group: "older" },
70
+ { value: "older_15m", label: "Older than 15 minutes", group: "older" },
71
+ { value: "older_30m", label: "Older than 30 minutes", group: "older" },
72
+ { value: "older_1h", label: "Older than 1 hour", group: "older" },
73
+ { value: "older_2h", label: "Older than 2 hours", group: "older" },
74
+ { value: "older_6h", label: "Older than 6 hours", group: "older" },
75
+ { value: "older_12h", label: "Older than 12 hours", group: "older" },
76
+ { value: "older_today", label: "Before today", group: "older" },
77
+ { value: "older_this_week", label: "Before this week", group: "older" },
78
+ { value: "older_this_month", label: "Before this month", group: "older" },
79
+ { value: "older_this_year", label: "Before this year", group: "older" }
80
+ ];
81
+ function startOfDay(date) {
82
+ const next = new Date(date);
83
+ next.setHours(0, 0, 0, 0);
84
+ return next;
85
+ }
86
+ function startOfWeek(date) {
87
+ const next = startOfDay(date);
88
+ next.setDate(next.getDate() - (next.getDay() === 0 ? 6 : next.getDay() - 1));
89
+ return next;
90
+ }
91
+ function startOfMonth(date) {
92
+ return new Date(date.getFullYear(), date.getMonth(), 1);
93
+ }
94
+ function startOfYear(date) {
95
+ return new Date(date.getFullYear(), 0, 1);
96
+ }
97
+ function validDate(value) {
98
+ if (value === void 0) return void 0;
99
+ const date = value instanceof Date ? new Date(value) : new Date(value);
100
+ return Number.isNaN(date.valueOf()) ? void 0 : date;
101
+ }
102
+ function dateFilterBounds(filter, now = /* @__PURE__ */ new Date()) {
103
+ if (!filter.preset) {
104
+ return {
105
+ ...filter.since === void 0 ? {} : { since: filter.since },
106
+ ...filter.until === void 0 ? {} : { until: filter.until }
107
+ };
108
+ }
109
+ if (filter.preset === "all") return {};
110
+ const anchor = validDate(filter.anchor) ?? validDate(now) ?? /* @__PURE__ */ new Date();
111
+ const ago = (milliseconds) => new Date(anchor.valueOf() - milliseconds).toISOString();
112
+ const recent = {
113
+ "1m": 6e4,
114
+ "15m": 15 * 6e4,
115
+ "30m": 30 * 6e4,
116
+ "1h": 60 * 6e4,
117
+ "2h": 2 * 60 * 6e4,
118
+ "6h": 6 * 60 * 6e4,
119
+ "12h": 12 * 60 * 6e4
120
+ };
121
+ const duration = recent[filter.preset.replace("older_", "")];
122
+ if (duration !== void 0) {
123
+ return filter.preset.startsWith("older_") ? { until: ago(duration) } : { since: ago(duration) };
124
+ }
125
+ const starts = {
126
+ today: startOfDay(anchor),
127
+ this_week: startOfWeek(anchor),
128
+ this_month: startOfMonth(anchor),
129
+ this_year: startOfYear(anchor),
130
+ older_today: startOfDay(anchor),
131
+ older_this_week: startOfWeek(anchor),
132
+ older_this_month: startOfMonth(anchor),
133
+ older_this_year: startOfYear(anchor)
134
+ };
135
+ if (filter.preset === "yesterday") {
136
+ const until = startOfDay(anchor);
137
+ const since = new Date(until);
138
+ since.setDate(since.getDate() - 1);
139
+ return { since: since.toISOString(), until: until.toISOString() };
140
+ }
141
+ const start = starts[filter.preset];
142
+ if (!start) return {};
143
+ return filter.preset.startsWith("older_") ? { until: start.toISOString() } : { since: start.toISOString() };
144
+ }
145
+ //# sourceMappingURL=date-filter.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/data-table/date-filter.ts"],"sourcesContent":["/** Relative time ranges shared by every canonical table date column. */\nexport const TIME_RANGE_PRESET_VALUES = [\n \"all\", \"1m\", \"15m\", \"30m\", \"1h\", \"2h\", \"6h\", \"12h\",\n \"today\", \"yesterday\", \"this_week\", \"this_month\", \"this_year\",\n \"older_1m\", \"older_15m\", \"older_30m\", \"older_1h\", \"older_2h\", \"older_6h\", \"older_12h\",\n \"older_today\", \"older_this_week\", \"older_this_month\", \"older_this_year\",\n] as const;\n\nexport type TimeRangePreset = (typeof TIME_RANGE_PRESET_VALUES)[number];\n\nexport interface TimeRangePresetOption {\n value: TimeRangePreset;\n label: string;\n group: \"all\" | \"recent\" | \"older\" | \"calendar\";\n}\n\n/** A contiguous range: `since` is inclusive and `until` is exclusive. */\nexport interface DateFilterBounds {\n since?: string;\n until?: string;\n}\n\nexport interface DateColumnFilterValue extends DateFilterBounds {\n kind: \"date\";\n /** Date ranges are contiguous only; complement queries need a source contract first. */\n negated?: never;\n /** A relative range. Omit for explicit custom bounds. */\n preset?: TimeRangePreset;\n /** ISO instant captured when a relative preset was chosen. */\n anchor?: string;\n}\n\nexport const TIME_RANGE_PRESETS: readonly TimeRangePresetOption[] = [\n { value: \"all\", label: \"All time\", group: \"all\" },\n { value: \"1m\", label: \"Last minute\", group: \"recent\" },\n { value: \"15m\", label: \"Last 15 minutes\", group: \"recent\" },\n { value: \"30m\", label: \"Last 30 minutes\", group: \"recent\" },\n { value: \"1h\", label: \"Last hour\", group: \"recent\" },\n { value: \"2h\", label: \"Last 2 hours\", group: \"recent\" },\n { value: \"6h\", label: \"Last 6 hours\", group: \"recent\" },\n { value: \"12h\", label: \"Last 12 hours\", group: \"recent\" },\n { value: \"today\", label: \"Today\", group: \"calendar\" },\n { value: \"yesterday\", label: \"Yesterday\", group: \"calendar\" },\n { value: \"this_week\", label: \"This week\", group: \"calendar\" },\n { value: \"this_month\", label: \"This month\", group: \"calendar\" },\n { value: \"this_year\", label: \"This year\", group: \"calendar\" },\n { value: \"older_1m\", label: \"Older than 1 minute\", group: \"older\" },\n { value: \"older_15m\", label: \"Older than 15 minutes\", group: \"older\" },\n { value: \"older_30m\", label: \"Older than 30 minutes\", group: \"older\" },\n { value: \"older_1h\", label: \"Older than 1 hour\", group: \"older\" },\n { value: \"older_2h\", label: \"Older than 2 hours\", group: \"older\" },\n { value: \"older_6h\", label: \"Older than 6 hours\", group: \"older\" },\n { value: \"older_12h\", label: \"Older than 12 hours\", group: \"older\" },\n { value: \"older_today\", label: \"Before today\", group: \"older\" },\n { value: \"older_this_week\", label: \"Before this week\", group: \"older\" },\n { value: \"older_this_month\", label: \"Before this month\", group: \"older\" },\n { value: \"older_this_year\", label: \"Before this year\", group: \"older\" },\n];\n\nfunction startOfDay(date: Date): Date {\n const next = new Date(date);\n next.setHours(0, 0, 0, 0);\n return next;\n}\n\nfunction startOfWeek(date: Date): Date {\n const next = startOfDay(date);\n next.setDate(next.getDate() - (next.getDay() === 0 ? 6 : next.getDay() - 1));\n return next;\n}\n\nfunction startOfMonth(date: Date): Date {\n return new Date(date.getFullYear(), date.getMonth(), 1);\n}\n\nfunction startOfYear(date: Date): Date {\n return new Date(date.getFullYear(), 0, 1);\n}\n\nfunction validDate(value: Date | string | undefined): Date | undefined {\n if (value === undefined) return undefined;\n const date = value instanceof Date ? new Date(value) : new Date(value);\n return Number.isNaN(date.valueOf()) ? undefined : date;\n}\n\n/**\n * Resolve a date filter without reading the clock implicitly. Relative presets\n * use their captured `anchor`; callers may provide `now` only for legacy or\n * programmatic values without one. `until` remains exclusive throughout.\n */\nexport function dateFilterBounds(\n filter: DateColumnFilterValue,\n now: Date | string = new Date(),\n): DateFilterBounds {\n if (!filter.preset) {\n return {\n ...(filter.since === undefined ? {} : { since: filter.since }),\n ...(filter.until === undefined ? {} : { until: filter.until }),\n };\n }\n if (filter.preset === \"all\") return {};\n\n const anchor = validDate(filter.anchor) ?? validDate(now) ?? new Date();\n const ago = (milliseconds: number) =>\n new Date(anchor.valueOf() - milliseconds).toISOString();\n const recent: Partial<Record<TimeRangePreset, number>> = {\n \"1m\": 60_000,\n \"15m\": 15 * 60_000,\n \"30m\": 30 * 60_000,\n \"1h\": 60 * 60_000,\n \"2h\": 2 * 60 * 60_000,\n \"6h\": 6 * 60 * 60_000,\n \"12h\": 12 * 60 * 60_000,\n };\n const duration = recent[filter.preset.replace(\"older_\", \"\") as TimeRangePreset];\n if (duration !== undefined) {\n return filter.preset.startsWith(\"older_\") ? { until: ago(duration) } : { since: ago(duration) };\n }\n\n const starts: Partial<Record<TimeRangePreset, Date>> = {\n today: startOfDay(anchor),\n this_week: startOfWeek(anchor),\n this_month: startOfMonth(anchor),\n this_year: startOfYear(anchor),\n older_today: startOfDay(anchor),\n older_this_week: startOfWeek(anchor),\n older_this_month: startOfMonth(anchor),\n older_this_year: startOfYear(anchor),\n };\n if (filter.preset === \"yesterday\") {\n const until = startOfDay(anchor);\n const since = new Date(until);\n since.setDate(since.getDate() - 1);\n return { since: since.toISOString(), until: until.toISOString() };\n }\n const start = starts[filter.preset];\n if (!start) return {};\n return filter.preset.startsWith(\"older_\")\n ? { until: start.toISOString() }\n : { since: start.toISOString() };\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,31 @@
1
+ /** Relative time ranges shared by every canonical table date column. */
2
+ declare const TIME_RANGE_PRESET_VALUES: readonly ["all", "1m", "15m", "30m", "1h", "2h", "6h", "12h", "today", "yesterday", "this_week", "this_month", "this_year", "older_1m", "older_15m", "older_30m", "older_1h", "older_2h", "older_6h", "older_12h", "older_today", "older_this_week", "older_this_month", "older_this_year"];
3
+ type TimeRangePreset = (typeof TIME_RANGE_PRESET_VALUES)[number];
4
+ interface TimeRangePresetOption {
5
+ value: TimeRangePreset;
6
+ label: string;
7
+ group: "all" | "recent" | "older" | "calendar";
8
+ }
9
+ /** A contiguous range: `since` is inclusive and `until` is exclusive. */
10
+ interface DateFilterBounds {
11
+ since?: string;
12
+ until?: string;
13
+ }
14
+ interface DateColumnFilterValue extends DateFilterBounds {
15
+ kind: "date";
16
+ /** Date ranges are contiguous only; complement queries need a source contract first. */
17
+ negated?: never;
18
+ /** A relative range. Omit for explicit custom bounds. */
19
+ preset?: TimeRangePreset;
20
+ /** ISO instant captured when a relative preset was chosen. */
21
+ anchor?: string;
22
+ }
23
+ declare const TIME_RANGE_PRESETS: readonly TimeRangePresetOption[];
24
+ /**
25
+ * Resolve a date filter without reading the clock implicitly. Relative presets
26
+ * use their captured `anchor`; callers may provide `now` only for legacy or
27
+ * programmatic values without one. `until` remains exclusive throughout.
28
+ */
29
+ declare function dateFilterBounds(filter: DateColumnFilterValue, now?: Date | string): DateFilterBounds;
30
+
31
+ export { type DateColumnFilterValue, type DateFilterBounds, TIME_RANGE_PRESETS, TIME_RANGE_PRESET_VALUES, type TimeRangePreset, type TimeRangePresetOption, dateFilterBounds };
@@ -0,0 +1,31 @@
1
+ /** Relative time ranges shared by every canonical table date column. */
2
+ declare const TIME_RANGE_PRESET_VALUES: readonly ["all", "1m", "15m", "30m", "1h", "2h", "6h", "12h", "today", "yesterday", "this_week", "this_month", "this_year", "older_1m", "older_15m", "older_30m", "older_1h", "older_2h", "older_6h", "older_12h", "older_today", "older_this_week", "older_this_month", "older_this_year"];
3
+ type TimeRangePreset = (typeof TIME_RANGE_PRESET_VALUES)[number];
4
+ interface TimeRangePresetOption {
5
+ value: TimeRangePreset;
6
+ label: string;
7
+ group: "all" | "recent" | "older" | "calendar";
8
+ }
9
+ /** A contiguous range: `since` is inclusive and `until` is exclusive. */
10
+ interface DateFilterBounds {
11
+ since?: string;
12
+ until?: string;
13
+ }
14
+ interface DateColumnFilterValue extends DateFilterBounds {
15
+ kind: "date";
16
+ /** Date ranges are contiguous only; complement queries need a source contract first. */
17
+ negated?: never;
18
+ /** A relative range. Omit for explicit custom bounds. */
19
+ preset?: TimeRangePreset;
20
+ /** ISO instant captured when a relative preset was chosen. */
21
+ anchor?: string;
22
+ }
23
+ declare const TIME_RANGE_PRESETS: readonly TimeRangePresetOption[];
24
+ /**
25
+ * Resolve a date filter without reading the clock implicitly. Relative presets
26
+ * use their captured `anchor`; callers may provide `now` only for legacy or
27
+ * programmatic values without one. `until` remains exclusive throughout.
28
+ */
29
+ declare function dateFilterBounds(filter: DateColumnFilterValue, now?: Date | string): DateFilterBounds;
30
+
31
+ export { type DateColumnFilterValue, type DateFilterBounds, TIME_RANGE_PRESETS, TIME_RANGE_PRESET_VALUES, type TimeRangePreset, type TimeRangePresetOption, dateFilterBounds };
@@ -0,0 +1,125 @@
1
+ "use client";
2
+
3
+ // src/data-table/date-filter.ts
4
+ var TIME_RANGE_PRESET_VALUES = [
5
+ "all",
6
+ "1m",
7
+ "15m",
8
+ "30m",
9
+ "1h",
10
+ "2h",
11
+ "6h",
12
+ "12h",
13
+ "today",
14
+ "yesterday",
15
+ "this_week",
16
+ "this_month",
17
+ "this_year",
18
+ "older_1m",
19
+ "older_15m",
20
+ "older_30m",
21
+ "older_1h",
22
+ "older_2h",
23
+ "older_6h",
24
+ "older_12h",
25
+ "older_today",
26
+ "older_this_week",
27
+ "older_this_month",
28
+ "older_this_year"
29
+ ];
30
+ var TIME_RANGE_PRESETS = [
31
+ { value: "all", label: "All time", group: "all" },
32
+ { value: "1m", label: "Last minute", group: "recent" },
33
+ { value: "15m", label: "Last 15 minutes", group: "recent" },
34
+ { value: "30m", label: "Last 30 minutes", group: "recent" },
35
+ { value: "1h", label: "Last hour", group: "recent" },
36
+ { value: "2h", label: "Last 2 hours", group: "recent" },
37
+ { value: "6h", label: "Last 6 hours", group: "recent" },
38
+ { value: "12h", label: "Last 12 hours", group: "recent" },
39
+ { value: "today", label: "Today", group: "calendar" },
40
+ { value: "yesterday", label: "Yesterday", group: "calendar" },
41
+ { value: "this_week", label: "This week", group: "calendar" },
42
+ { value: "this_month", label: "This month", group: "calendar" },
43
+ { value: "this_year", label: "This year", group: "calendar" },
44
+ { value: "older_1m", label: "Older than 1 minute", group: "older" },
45
+ { value: "older_15m", label: "Older than 15 minutes", group: "older" },
46
+ { value: "older_30m", label: "Older than 30 minutes", group: "older" },
47
+ { value: "older_1h", label: "Older than 1 hour", group: "older" },
48
+ { value: "older_2h", label: "Older than 2 hours", group: "older" },
49
+ { value: "older_6h", label: "Older than 6 hours", group: "older" },
50
+ { value: "older_12h", label: "Older than 12 hours", group: "older" },
51
+ { value: "older_today", label: "Before today", group: "older" },
52
+ { value: "older_this_week", label: "Before this week", group: "older" },
53
+ { value: "older_this_month", label: "Before this month", group: "older" },
54
+ { value: "older_this_year", label: "Before this year", group: "older" }
55
+ ];
56
+ function startOfDay(date) {
57
+ const next = new Date(date);
58
+ next.setHours(0, 0, 0, 0);
59
+ return next;
60
+ }
61
+ function startOfWeek(date) {
62
+ const next = startOfDay(date);
63
+ next.setDate(next.getDate() - (next.getDay() === 0 ? 6 : next.getDay() - 1));
64
+ return next;
65
+ }
66
+ function startOfMonth(date) {
67
+ return new Date(date.getFullYear(), date.getMonth(), 1);
68
+ }
69
+ function startOfYear(date) {
70
+ return new Date(date.getFullYear(), 0, 1);
71
+ }
72
+ function validDate(value) {
73
+ if (value === void 0) return void 0;
74
+ const date = value instanceof Date ? new Date(value) : new Date(value);
75
+ return Number.isNaN(date.valueOf()) ? void 0 : date;
76
+ }
77
+ function dateFilterBounds(filter, now = /* @__PURE__ */ new Date()) {
78
+ if (!filter.preset) {
79
+ return {
80
+ ...filter.since === void 0 ? {} : { since: filter.since },
81
+ ...filter.until === void 0 ? {} : { until: filter.until }
82
+ };
83
+ }
84
+ if (filter.preset === "all") return {};
85
+ const anchor = validDate(filter.anchor) ?? validDate(now) ?? /* @__PURE__ */ new Date();
86
+ const ago = (milliseconds) => new Date(anchor.valueOf() - milliseconds).toISOString();
87
+ const recent = {
88
+ "1m": 6e4,
89
+ "15m": 15 * 6e4,
90
+ "30m": 30 * 6e4,
91
+ "1h": 60 * 6e4,
92
+ "2h": 2 * 60 * 6e4,
93
+ "6h": 6 * 60 * 6e4,
94
+ "12h": 12 * 60 * 6e4
95
+ };
96
+ const duration = recent[filter.preset.replace("older_", "")];
97
+ if (duration !== void 0) {
98
+ return filter.preset.startsWith("older_") ? { until: ago(duration) } : { since: ago(duration) };
99
+ }
100
+ const starts = {
101
+ today: startOfDay(anchor),
102
+ this_week: startOfWeek(anchor),
103
+ this_month: startOfMonth(anchor),
104
+ this_year: startOfYear(anchor),
105
+ older_today: startOfDay(anchor),
106
+ older_this_week: startOfWeek(anchor),
107
+ older_this_month: startOfMonth(anchor),
108
+ older_this_year: startOfYear(anchor)
109
+ };
110
+ if (filter.preset === "yesterday") {
111
+ const until = startOfDay(anchor);
112
+ const since = new Date(until);
113
+ since.setDate(since.getDate() - 1);
114
+ return { since: since.toISOString(), until: until.toISOString() };
115
+ }
116
+ const start = starts[filter.preset];
117
+ if (!start) return {};
118
+ return filter.preset.startsWith("older_") ? { until: start.toISOString() } : { since: start.toISOString() };
119
+ }
120
+ export {
121
+ TIME_RANGE_PRESETS,
122
+ TIME_RANGE_PRESET_VALUES,
123
+ dateFilterBounds
124
+ };
125
+ //# sourceMappingURL=date-filter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/data-table/date-filter.ts"],"sourcesContent":["/** Relative time ranges shared by every canonical table date column. */\nexport const TIME_RANGE_PRESET_VALUES = [\n \"all\", \"1m\", \"15m\", \"30m\", \"1h\", \"2h\", \"6h\", \"12h\",\n \"today\", \"yesterday\", \"this_week\", \"this_month\", \"this_year\",\n \"older_1m\", \"older_15m\", \"older_30m\", \"older_1h\", \"older_2h\", \"older_6h\", \"older_12h\",\n \"older_today\", \"older_this_week\", \"older_this_month\", \"older_this_year\",\n] as const;\n\nexport type TimeRangePreset = (typeof TIME_RANGE_PRESET_VALUES)[number];\n\nexport interface TimeRangePresetOption {\n value: TimeRangePreset;\n label: string;\n group: \"all\" | \"recent\" | \"older\" | \"calendar\";\n}\n\n/** A contiguous range: `since` is inclusive and `until` is exclusive. */\nexport interface DateFilterBounds {\n since?: string;\n until?: string;\n}\n\nexport interface DateColumnFilterValue extends DateFilterBounds {\n kind: \"date\";\n /** Date ranges are contiguous only; complement queries need a source contract first. */\n negated?: never;\n /** A relative range. Omit for explicit custom bounds. */\n preset?: TimeRangePreset;\n /** ISO instant captured when a relative preset was chosen. */\n anchor?: string;\n}\n\nexport const TIME_RANGE_PRESETS: readonly TimeRangePresetOption[] = [\n { value: \"all\", label: \"All time\", group: \"all\" },\n { value: \"1m\", label: \"Last minute\", group: \"recent\" },\n { value: \"15m\", label: \"Last 15 minutes\", group: \"recent\" },\n { value: \"30m\", label: \"Last 30 minutes\", group: \"recent\" },\n { value: \"1h\", label: \"Last hour\", group: \"recent\" },\n { value: \"2h\", label: \"Last 2 hours\", group: \"recent\" },\n { value: \"6h\", label: \"Last 6 hours\", group: \"recent\" },\n { value: \"12h\", label: \"Last 12 hours\", group: \"recent\" },\n { value: \"today\", label: \"Today\", group: \"calendar\" },\n { value: \"yesterday\", label: \"Yesterday\", group: \"calendar\" },\n { value: \"this_week\", label: \"This week\", group: \"calendar\" },\n { value: \"this_month\", label: \"This month\", group: \"calendar\" },\n { value: \"this_year\", label: \"This year\", group: \"calendar\" },\n { value: \"older_1m\", label: \"Older than 1 minute\", group: \"older\" },\n { value: \"older_15m\", label: \"Older than 15 minutes\", group: \"older\" },\n { value: \"older_30m\", label: \"Older than 30 minutes\", group: \"older\" },\n { value: \"older_1h\", label: \"Older than 1 hour\", group: \"older\" },\n { value: \"older_2h\", label: \"Older than 2 hours\", group: \"older\" },\n { value: \"older_6h\", label: \"Older than 6 hours\", group: \"older\" },\n { value: \"older_12h\", label: \"Older than 12 hours\", group: \"older\" },\n { value: \"older_today\", label: \"Before today\", group: \"older\" },\n { value: \"older_this_week\", label: \"Before this week\", group: \"older\" },\n { value: \"older_this_month\", label: \"Before this month\", group: \"older\" },\n { value: \"older_this_year\", label: \"Before this year\", group: \"older\" },\n];\n\nfunction startOfDay(date: Date): Date {\n const next = new Date(date);\n next.setHours(0, 0, 0, 0);\n return next;\n}\n\nfunction startOfWeek(date: Date): Date {\n const next = startOfDay(date);\n next.setDate(next.getDate() - (next.getDay() === 0 ? 6 : next.getDay() - 1));\n return next;\n}\n\nfunction startOfMonth(date: Date): Date {\n return new Date(date.getFullYear(), date.getMonth(), 1);\n}\n\nfunction startOfYear(date: Date): Date {\n return new Date(date.getFullYear(), 0, 1);\n}\n\nfunction validDate(value: Date | string | undefined): Date | undefined {\n if (value === undefined) return undefined;\n const date = value instanceof Date ? new Date(value) : new Date(value);\n return Number.isNaN(date.valueOf()) ? undefined : date;\n}\n\n/**\n * Resolve a date filter without reading the clock implicitly. Relative presets\n * use their captured `anchor`; callers may provide `now` only for legacy or\n * programmatic values without one. `until` remains exclusive throughout.\n */\nexport function dateFilterBounds(\n filter: DateColumnFilterValue,\n now: Date | string = new Date(),\n): DateFilterBounds {\n if (!filter.preset) {\n return {\n ...(filter.since === undefined ? {} : { since: filter.since }),\n ...(filter.until === undefined ? {} : { until: filter.until }),\n };\n }\n if (filter.preset === \"all\") return {};\n\n const anchor = validDate(filter.anchor) ?? validDate(now) ?? new Date();\n const ago = (milliseconds: number) =>\n new Date(anchor.valueOf() - milliseconds).toISOString();\n const recent: Partial<Record<TimeRangePreset, number>> = {\n \"1m\": 60_000,\n \"15m\": 15 * 60_000,\n \"30m\": 30 * 60_000,\n \"1h\": 60 * 60_000,\n \"2h\": 2 * 60 * 60_000,\n \"6h\": 6 * 60 * 60_000,\n \"12h\": 12 * 60 * 60_000,\n };\n const duration = recent[filter.preset.replace(\"older_\", \"\") as TimeRangePreset];\n if (duration !== undefined) {\n return filter.preset.startsWith(\"older_\") ? { until: ago(duration) } : { since: ago(duration) };\n }\n\n const starts: Partial<Record<TimeRangePreset, Date>> = {\n today: startOfDay(anchor),\n this_week: startOfWeek(anchor),\n this_month: startOfMonth(anchor),\n this_year: startOfYear(anchor),\n older_today: startOfDay(anchor),\n older_this_week: startOfWeek(anchor),\n older_this_month: startOfMonth(anchor),\n older_this_year: startOfYear(anchor),\n };\n if (filter.preset === \"yesterday\") {\n const until = startOfDay(anchor);\n const since = new Date(until);\n since.setDate(since.getDate() - 1);\n return { since: since.toISOString(), until: until.toISOString() };\n }\n const start = starts[filter.preset];\n if (!start) return {};\n return filter.preset.startsWith(\"older_\")\n ? { until: start.toISOString() }\n : { since: start.toISOString() };\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,118 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/data-table/facets.ts
21
+ var facets_exports = {};
22
+ __export(facets_exports, {
23
+ FACETS_LOADING_NOTICE: () => FACETS_LOADING_NOTICE,
24
+ MAX_LISTABLE_FACET_LENGTH: () => MAX_LISTABLE_FACET_LENGTH,
25
+ computeColumnFacets: () => computeColumnFacets,
26
+ describeFacetFailure: () => describeFacetFailure,
27
+ describeFacetSource: () => describeFacetSource,
28
+ localFacetsAreComplete: () => localFacetsAreComplete
29
+ });
30
+ module.exports = __toCommonJS(facets_exports);
31
+ var MAX_LISTABLE_FACET_LENGTH = 300;
32
+ var DEFAULT_FACET_LIMIT = 200;
33
+ var MAX_FACET_LIMIT = 500;
34
+ function facetText(raw) {
35
+ if (raw === null || raw === void 0) return "";
36
+ if (typeof raw === "object") {
37
+ try {
38
+ return JSON.stringify(raw);
39
+ } catch {
40
+ return String(raw);
41
+ }
42
+ }
43
+ return String(raw);
44
+ }
45
+ function computeColumnFacets(args) {
46
+ const { columnId, rows, readValue } = args;
47
+ const limit = Math.min(Math.max(args.limit ?? DEFAULT_FACET_LIMIT, 1), MAX_FACET_LIMIT);
48
+ const counts = /* @__PURE__ */ new Map();
49
+ const longValues = /* @__PURE__ */ new Set();
50
+ let blank = 0;
51
+ let maxLength = 0;
52
+ for (const row of rows) {
53
+ let raw;
54
+ try {
55
+ raw = readValue(row, columnId);
56
+ } catch {
57
+ raw = void 0;
58
+ }
59
+ const trimmed = facetText(raw).trim();
60
+ if (trimmed === "") {
61
+ blank += 1;
62
+ continue;
63
+ }
64
+ if (trimmed.length > maxLength) maxLength = trimmed.length;
65
+ if (trimmed.length > MAX_LISTABLE_FACET_LENGTH) {
66
+ longValues.add(trimmed);
67
+ continue;
68
+ }
69
+ counts.set(trimmed, (counts.get(trimmed) ?? 0) + 1);
70
+ }
71
+ const values = [...counts.entries()].map(([value, count]) => ({ value, count })).sort((a, b) => b.count - a.count || a.value.localeCompare(b.value)).slice(0, limit);
72
+ return {
73
+ columnId,
74
+ totalRows: rows.length,
75
+ filled: rows.length - blank,
76
+ blank,
77
+ distinctCount: counts.size + longValues.size,
78
+ maxLength,
79
+ unlistable: longValues.size,
80
+ limit,
81
+ truncated: counts.size > limit,
82
+ values,
83
+ answeredBy: "local",
84
+ complete: args.complete ?? true
85
+ };
86
+ }
87
+ function localFacetsAreComplete(loadedRowCount, total) {
88
+ if (total === void 0 || !Number.isFinite(total) || total < 0) return false;
89
+ return loadedRowCount >= total;
90
+ }
91
+ var NUMBERS = new Intl.NumberFormat("en-US");
92
+ function n(value) {
93
+ try {
94
+ return NUMBERS.format(value);
95
+ } catch {
96
+ return String(value);
97
+ }
98
+ }
99
+ function describeFacetSource(facets, noun = "row") {
100
+ if (!facets) return "";
101
+ const plural = `${noun}s`;
102
+ if (!facets.complete) {
103
+ return `These counts cover the ${n(facets.totalRows)} ${plural} loaded here, not the whole table.`;
104
+ }
105
+ if (facets.truncated) {
106
+ return `Showing the ${n(facets.values.length)} most common of ${n(facets.distinctCount)} values.`;
107
+ }
108
+ if (facets.unlistable > 0) {
109
+ 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"}.`;
110
+ }
111
+ return "";
112
+ }
113
+ var FACETS_LOADING_NOTICE = "Counting values\u2026";
114
+ function describeFacetFailure(error) {
115
+ const detail = error instanceof Error ? error.message : String(error ?? "");
116
+ 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.";
117
+ }
118
+ //# sourceMappingURL=facets.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/data-table/facets.ts"],"sourcesContent":["/**\n * @ai-matrx/design-system/data-table — COLUMN FACETS: the values a column\n * actually holds, and how many rows carry each one.\n *\n * A filter box that says \"Contains…\" asks the reader to already know what is in\n * the column. A checklist of the real values, each with its count, tells them —\n * and the values that surprise you (the one-off typo, the stray casing, the\n * blank nobody filled in) are exactly the ones a text box hides.\n *\n * THE TWO LAWS this module exists to keep:\n *\n * 1. **Counts are computed locally ONLY when the browser holds every row the\n * counts describe.** `localFacetsAreComplete` is the test, and it compares\n * the loaded row count against the total AFTER the active search, because\n * that is the population the counts claim to describe. One page of rows\n * would produce counts that look authoritative and are not. When the browser\n * does not hold them all, the consumer's `source` answers from the database.\n *\n * 2. **A partial answer says so, in a sentence.** `describeFacetSource` writes\n * the one line the popover shows. There is no silent fallback: if the\n * database could not be asked, or answered only part of the truth, the\n * reader is told before they act on the list.\n *\n * Pure: no React, no DOM, no network. Nothing here throws on any input.\n */\n\n/** One distinct value of a column and how many rows carry it. */\nexport interface ColumnFacetValue {\n value: string;\n count: number;\n}\n\n/**\n * What one column holds. `values` is the top slice by frequency; the counts\n * that describe the WHOLE column — `totalRows`, `filled`, `blank`,\n * `distinctCount` — are always about every row considered, never about the\n * slice, which is what makes `truncated` meaningful rather than decorative.\n */\nexport interface ColumnFacets {\n columnId: string;\n /** Rows considered (after the active search, if any). */\n totalRows: number;\n /** Rows whose cell is non-empty. */\n filled: number;\n /** Rows whose cell is null or whitespace-only — a real filter target. */\n blank: number;\n /** Distinct non-empty values across EVERY row, not just `values`. */\n distinctCount: number;\n /** Longest value; a caller refuses a picker on a column of long prose. */\n maxLength: number;\n /** Distinct values too long to offer as options. */\n unlistable: number;\n limit: number;\n /** True when `values` does not carry every listable distinct value. */\n truncated: boolean;\n /** Top values by frequency, descending; ties broken by the value itself. */\n values: ColumnFacetValue[];\n /** Who counted. \"source\" is the database; \"local\" is the rows in the browser. */\n answeredBy: \"source\" | \"local\";\n /**\n * True when the counts describe every row of the column. False means the\n * reader is looking at a partial answer and the popover must say so.\n */\n complete: boolean;\n}\n\n/** A value longer than this is real data, not a pickable option. */\nexport const MAX_LISTABLE_FACET_LENGTH = 300;\n\nconst DEFAULT_FACET_LIMIT = 200;\nconst MAX_FACET_LIMIT = 500;\n\nfunction facetText(raw: unknown): string {\n if (raw === null || raw === undefined) return \"\";\n if (typeof raw === \"object\") {\n try {\n return JSON.stringify(raw);\n } catch {\n return String(raw);\n }\n }\n return String(raw);\n}\n\n/**\n * Compute a column's facets from rows already in memory.\n *\n * `rows` MUST be every row the facets are meant to describe — the caller\n * decides that with `localFacetsAreComplete`. `complete` carries the caller's\n * verdict through to the popover so a partial answer can never be printed as a\n * whole one.\n */\nexport function computeColumnFacets(args: {\n columnId: string;\n rows: readonly unknown[];\n readValue: (row: unknown, columnId: string) => unknown;\n limit?: number | undefined;\n complete?: boolean | undefined;\n}): ColumnFacets {\n const { columnId, rows, readValue } = args;\n const limit = Math.min(Math.max(args.limit ?? DEFAULT_FACET_LIMIT, 1), MAX_FACET_LIMIT);\n\n const counts = new Map<string, number>();\n const longValues = new Set<string>();\n let blank = 0;\n let maxLength = 0;\n\n for (const row of rows) {\n let raw: unknown;\n try {\n raw = readValue(row, columnId);\n } catch {\n raw = undefined;\n }\n const trimmed = facetText(raw).trim();\n if (trimmed === \"\") {\n blank += 1;\n continue;\n }\n if (trimmed.length > maxLength) maxLength = trimmed.length;\n if (trimmed.length > MAX_LISTABLE_FACET_LENGTH) {\n longValues.add(trimmed);\n continue;\n }\n counts.set(trimmed, (counts.get(trimmed) ?? 0) + 1);\n }\n\n const values: ColumnFacetValue[] = [...counts.entries()]\n .map(([value, count]) => ({ value, count }))\n .sort((a, b) => b.count - a.count || a.value.localeCompare(b.value))\n .slice(0, limit);\n\n return {\n columnId,\n totalRows: rows.length,\n filled: rows.length - blank,\n blank,\n distinctCount: counts.size + longValues.size,\n maxLength,\n unlistable: longValues.size,\n limit,\n truncated: counts.size > limit,\n values,\n answeredBy: \"local\",\n complete: args.complete ?? true,\n };\n}\n\n/**\n * May facets be computed from these rows, or must the source be asked?\n *\n * The ONLY safe condition is holding every row the facets describe. `total` is\n * the row count AFTER the active search, which is exactly what the loaded rows\n * also reflect — so comparing counts is a true completeness test, not a guess.\n * An unknown total is never treated as \"small enough\".\n */\nexport function localFacetsAreComplete(\n loadedRowCount: number,\n total: number | undefined,\n): boolean {\n if (total === undefined || !Number.isFinite(total) || total < 0) return false;\n return loadedRowCount >= total;\n}\n\nconst NUMBERS = new Intl.NumberFormat(\"en-US\");\n\nfunction n(value: number): string {\n try {\n return NUMBERS.format(value);\n } catch {\n return String(value);\n }\n}\n\n/**\n * The one sentence under the value list. Empty when the answer is whole and\n * nothing needs saying — the popover never nags a reader whose counts are\n * exact.\n */\nexport function describeFacetSource(\n facets: ColumnFacets | null | undefined,\n noun = \"row\",\n): string {\n if (!facets) return \"\";\n const plural = `${noun}s`;\n if (!facets.complete) {\n return `These counts cover the ${n(facets.totalRows)} ${plural} loaded here, not the whole table.`;\n }\n if (facets.truncated) {\n return `Showing the ${n(facets.values.length)} most common of ${n(facets.distinctCount)} values.`;\n }\n if (facets.unlistable > 0) {\n 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\"}.`;\n }\n return \"\";\n}\n\n/** The sentence shown while the source is being asked, and when it refuses. */\nexport const FACETS_LOADING_NOTICE = \"Counting values…\";\nexport function describeFacetFailure(error: unknown): string {\n const detail = error instanceof Error ? error.message : String(error ?? \"\");\n return detail\n ? `The values in this column could not be counted: ${detail} Match text instead.`\n : \"The values in this column could not be counted. Match text instead.\";\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,100 @@
1
+ /**
2
+ * @ai-matrx/design-system/data-table — COLUMN FACETS: the values a column
3
+ * actually holds, and how many rows carry each one.
4
+ *
5
+ * A filter box that says "Contains…" asks the reader to already know what is in
6
+ * the column. A checklist of the real values, each with its count, tells them —
7
+ * and the values that surprise you (the one-off typo, the stray casing, the
8
+ * blank nobody filled in) are exactly the ones a text box hides.
9
+ *
10
+ * THE TWO LAWS this module exists to keep:
11
+ *
12
+ * 1. **Counts are computed locally ONLY when the browser holds every row the
13
+ * counts describe.** `localFacetsAreComplete` is the test, and it compares
14
+ * the loaded row count against the total AFTER the active search, because
15
+ * that is the population the counts claim to describe. One page of rows
16
+ * would produce counts that look authoritative and are not. When the browser
17
+ * does not hold them all, the consumer's `source` answers from the database.
18
+ *
19
+ * 2. **A partial answer says so, in a sentence.** `describeFacetSource` writes
20
+ * the one line the popover shows. There is no silent fallback: if the
21
+ * database could not be asked, or answered only part of the truth, the
22
+ * reader is told before they act on the list.
23
+ *
24
+ * Pure: no React, no DOM, no network. Nothing here throws on any input.
25
+ */
26
+ /** One distinct value of a column and how many rows carry it. */
27
+ interface ColumnFacetValue {
28
+ value: string;
29
+ count: number;
30
+ }
31
+ /**
32
+ * What one column holds. `values` is the top slice by frequency; the counts
33
+ * that describe the WHOLE column — `totalRows`, `filled`, `blank`,
34
+ * `distinctCount` — are always about every row considered, never about the
35
+ * slice, which is what makes `truncated` meaningful rather than decorative.
36
+ */
37
+ interface ColumnFacets {
38
+ columnId: string;
39
+ /** Rows considered (after the active search, if any). */
40
+ totalRows: number;
41
+ /** Rows whose cell is non-empty. */
42
+ filled: number;
43
+ /** Rows whose cell is null or whitespace-only — a real filter target. */
44
+ blank: number;
45
+ /** Distinct non-empty values across EVERY row, not just `values`. */
46
+ distinctCount: number;
47
+ /** Longest value; a caller refuses a picker on a column of long prose. */
48
+ maxLength: number;
49
+ /** Distinct values too long to offer as options. */
50
+ unlistable: number;
51
+ limit: number;
52
+ /** True when `values` does not carry every listable distinct value. */
53
+ truncated: boolean;
54
+ /** Top values by frequency, descending; ties broken by the value itself. */
55
+ values: ColumnFacetValue[];
56
+ /** Who counted. "source" is the database; "local" is the rows in the browser. */
57
+ answeredBy: "source" | "local";
58
+ /**
59
+ * True when the counts describe every row of the column. False means the
60
+ * reader is looking at a partial answer and the popover must say so.
61
+ */
62
+ complete: boolean;
63
+ }
64
+ /** A value longer than this is real data, not a pickable option. */
65
+ declare const MAX_LISTABLE_FACET_LENGTH = 300;
66
+ /**
67
+ * Compute a column's facets from rows already in memory.
68
+ *
69
+ * `rows` MUST be every row the facets are meant to describe — the caller
70
+ * decides that with `localFacetsAreComplete`. `complete` carries the caller's
71
+ * verdict through to the popover so a partial answer can never be printed as a
72
+ * whole one.
73
+ */
74
+ declare function computeColumnFacets(args: {
75
+ columnId: string;
76
+ rows: readonly unknown[];
77
+ readValue: (row: unknown, columnId: string) => unknown;
78
+ limit?: number | undefined;
79
+ complete?: boolean | undefined;
80
+ }): ColumnFacets;
81
+ /**
82
+ * May facets be computed from these rows, or must the source be asked?
83
+ *
84
+ * The ONLY safe condition is holding every row the facets describe. `total` is
85
+ * the row count AFTER the active search, which is exactly what the loaded rows
86
+ * also reflect — so comparing counts is a true completeness test, not a guess.
87
+ * An unknown total is never treated as "small enough".
88
+ */
89
+ declare function localFacetsAreComplete(loadedRowCount: number, total: number | undefined): boolean;
90
+ /**
91
+ * The one sentence under the value list. Empty when the answer is whole and
92
+ * nothing needs saying — the popover never nags a reader whose counts are
93
+ * exact.
94
+ */
95
+ declare function describeFacetSource(facets: ColumnFacets | null | undefined, noun?: string): string;
96
+ /** The sentence shown while the source is being asked, and when it refuses. */
97
+ declare const FACETS_LOADING_NOTICE = "Counting values\u2026";
98
+ declare function describeFacetFailure(error: unknown): string;
99
+
100
+ export { type ColumnFacetValue, type ColumnFacets, FACETS_LOADING_NOTICE, MAX_LISTABLE_FACET_LENGTH, computeColumnFacets, describeFacetFailure, describeFacetSource, localFacetsAreComplete };