@ai-matrx/design-system 0.22.0 → 0.26.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (115) hide show
  1. package/CHANGELOG.md +227 -0
  2. package/dist/data-table/cell-undo.cjs +98 -0
  3. package/dist/data-table/cell-undo.cjs.map +1 -0
  4. package/dist/data-table/cell-undo.d.cts +85 -0
  5. package/dist/data-table/cell-undo.d.ts +85 -0
  6. package/dist/data-table/cell-undo.js +78 -0
  7. package/dist/data-table/cell-undo.js.map +1 -0
  8. package/dist/data-table/copy-helpers.d.cts +7 -2
  9. package/dist/data-table/copy-helpers.d.ts +7 -2
  10. package/dist/data-table/coverage.cjs +116 -0
  11. package/dist/data-table/coverage.cjs.map +1 -0
  12. package/dist/data-table/coverage.d.cts +65 -0
  13. package/dist/data-table/coverage.d.ts +65 -0
  14. package/dist/data-table/coverage.js +96 -0
  15. package/dist/data-table/coverage.js.map +1 -0
  16. package/dist/data-table/data-row-inspector.cjs.map +1 -1
  17. package/dist/data-table/data-row-inspector.js.map +1 -1
  18. package/dist/data-table/data-row-window.cjs.map +1 -1
  19. package/dist/data-table/data-row-window.js.map +1 -1
  20. package/dist/data-table/facets.cjs +118 -0
  21. package/dist/data-table/facets.cjs.map +1 -0
  22. package/dist/data-table/facets.d.cts +100 -0
  23. package/dist/data-table/facets.d.ts +100 -0
  24. package/dist/data-table/facets.js +98 -0
  25. package/dist/data-table/facets.js.map +1 -0
  26. package/dist/data-table/fill.cjs +299 -0
  27. package/dist/data-table/fill.cjs.map +1 -0
  28. package/dist/data-table/fill.d.cts +129 -0
  29. package/dist/data-table/fill.d.ts +129 -0
  30. package/dist/data-table/fill.js +277 -0
  31. package/dist/data-table/fill.js.map +1 -0
  32. package/dist/data-table/filter-engine.d.cts +7 -2
  33. package/dist/data-table/filter-engine.d.ts +7 -2
  34. package/dist/data-table/grid-clipboard.cjs +165 -0
  35. package/dist/data-table/grid-clipboard.cjs.map +1 -0
  36. package/dist/data-table/grid-clipboard.d.cts +85 -0
  37. package/dist/data-table/grid-clipboard.d.ts +85 -0
  38. package/dist/data-table/grid-clipboard.js +145 -0
  39. package/dist/data-table/grid-clipboard.js.map +1 -0
  40. package/dist/data-table/grid-selection.cjs +258 -0
  41. package/dist/data-table/grid-selection.cjs.map +1 -0
  42. package/dist/data-table/grid-selection.d.cts +166 -0
  43. package/dist/data-table/grid-selection.d.ts +166 -0
  44. package/dist/data-table/grid-selection.js +238 -0
  45. package/dist/data-table/grid-selection.js.map +1 -0
  46. package/dist/data-table/grouping.cjs +302 -0
  47. package/dist/data-table/grouping.cjs.map +1 -0
  48. package/dist/data-table/grouping.d.cts +119 -0
  49. package/dist/data-table/grouping.d.ts +119 -0
  50. package/dist/data-table/grouping.js +282 -0
  51. package/dist/data-table/grouping.js.map +1 -0
  52. package/dist/data-table/host.cjs.map +1 -1
  53. package/dist/data-table/host.d.cts +18 -4
  54. package/dist/data-table/host.d.ts +18 -4
  55. package/dist/data-table/host.js.map +1 -1
  56. package/dist/data-table/index.cjs +3173 -451
  57. package/dist/data-table/index.cjs.map +1 -1
  58. package/dist/data-table/index.d.cts +13 -3
  59. package/dist/data-table/index.d.ts +13 -3
  60. package/dist/data-table/index.js +3174 -451
  61. package/dist/data-table/index.js.map +1 -1
  62. package/dist/data-table/infer-filter.d.cts +7 -2
  63. package/dist/data-table/infer-filter.d.ts +7 -2
  64. package/dist/data-table/layered-filters.d.cts +7 -2
  65. package/dist/data-table/layered-filters.d.ts +7 -2
  66. package/dist/data-table/menu-targets.cjs +77 -0
  67. package/dist/data-table/menu-targets.cjs.map +1 -0
  68. package/dist/data-table/menu-targets.d.cts +108 -0
  69. package/dist/data-table/menu-targets.d.ts +108 -0
  70. package/dist/data-table/menu-targets.js +57 -0
  71. package/dist/data-table/menu-targets.js.map +1 -0
  72. package/dist/data-table/pagination.cjs.map +1 -1
  73. package/dist/data-table/pagination.js.map +1 -1
  74. package/dist/data-table/query-control.d.cts +7 -2
  75. package/dist/data-table/query-control.d.ts +7 -2
  76. package/dist/data-table/spreadsheet.cjs +1062 -0
  77. package/dist/data-table/spreadsheet.cjs.map +1 -0
  78. package/dist/data-table/spreadsheet.d.cts +153 -0
  79. package/dist/data-table/spreadsheet.d.ts +153 -0
  80. package/dist/data-table/spreadsheet.js +1042 -0
  81. package/dist/data-table/spreadsheet.js.map +1 -0
  82. package/dist/data-table/types.cjs.map +1 -1
  83. package/dist/data-table/types.d.cts +7 -2
  84. package/dist/data-table/types.d.ts +7 -2
  85. package/dist/data-table/url-state.d.cts +7 -2
  86. package/dist/data-table/url-state.d.ts +7 -2
  87. package/dist/data-table/uuid-cell.cjs.map +1 -1
  88. package/dist/data-table/uuid-cell.js.map +1 -1
  89. package/dist/data-table/validation.cjs +229 -0
  90. package/dist/data-table/validation.cjs.map +1 -0
  91. package/dist/data-table/validation.d.cts +125 -0
  92. package/dist/data-table/validation.d.ts +125 -0
  93. package/dist/data-table/validation.js +209 -0
  94. package/dist/data-table/validation.js.map +1 -0
  95. package/dist/data-table/virtual-window.cjs +203 -0
  96. package/dist/data-table/virtual-window.cjs.map +1 -0
  97. package/dist/data-table/virtual-window.d.cts +84 -0
  98. package/dist/data-table/virtual-window.d.ts +84 -0
  99. package/dist/data-table/virtual-window.js +183 -0
  100. package/dist/data-table/virtual-window.js.map +1 -0
  101. package/dist/data-table/xlsx.d.cts +7 -2
  102. package/dist/data-table/xlsx.d.ts +7 -2
  103. package/dist/field-formats/index.d.cts +2 -2
  104. package/dist/field-formats/index.d.ts +2 -2
  105. package/dist/formulas/index.cjs +1041 -0
  106. package/dist/formulas/index.cjs.map +1 -0
  107. package/dist/formulas/index.d.cts +389 -0
  108. package/dist/formulas/index.d.ts +389 -0
  109. package/dist/formulas/index.js +1019 -0
  110. package/dist/formulas/index.js.map +1 -0
  111. package/dist/{types-BdbvlF9-.d.cts → types-CJWU1Gae.d.cts} +1 -1
  112. package/dist/{types-BdbvlF9-.d.ts → types-CJWU1Gae.d.ts} +1 -1
  113. package/dist/{types-D-flTpK0.d.ts → types-Cu5_eUG1.d.ts} +268 -4
  114. package/dist/{types-Sn249eoy.d.cts → types-DrJXAAoD.d.cts} +268 -4
  115. package/package.json +129 -2
@@ -0,0 +1,116 @@
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/coverage.ts
21
+ var coverage_exports = {};
22
+ __export(coverage_exports, {
23
+ JARGON_WORDS: () => JARGON_WORDS,
24
+ PARTIAL_BADGE: () => PARTIAL_BADGE,
25
+ UNKNOWN_TOTAL_BADGE: () => UNKNOWN_TOTAL_BADGE,
26
+ describeCoverage: () => describeCoverage
27
+ });
28
+ module.exports = __toCommonJS(coverage_exports);
29
+ var PARTIAL_BADGE = "Partial";
30
+ var UNKNOWN_TOTAL_BADGE = "Unknown total";
31
+ var NUMBERS = new Intl.NumberFormat("en-US");
32
+ function formatNumber(value) {
33
+ try {
34
+ return NUMBERS.format(value);
35
+ } catch {
36
+ return String(value);
37
+ }
38
+ }
39
+ function pluralize(noun) {
40
+ const word = noun.trim();
41
+ if (!word) return "rows";
42
+ const lower = word.toLowerCase();
43
+ if (/[^aeiou]y$/.test(lower)) return `${word.slice(0, -1)}ies`;
44
+ if (/(s|x|z|ch|sh)$/.test(lower)) return `${word}es`;
45
+ return `${word}s`;
46
+ }
47
+ function quantity(value, noun) {
48
+ return `${formatNumber(value)} ${value === 1 ? noun.trim() || "row" : pluralize(noun)}`;
49
+ }
50
+ function toWhole(value) {
51
+ return typeof value === "number" && Number.isFinite(value) && value > 0 ? Math.floor(value) : 0;
52
+ }
53
+ function toOptionalWhole(value) {
54
+ if (typeof value !== "number" || !Number.isFinite(value) || value < 0) return void 0;
55
+ return Math.floor(value);
56
+ }
57
+ function describeCoverage(input) {
58
+ const safe = input && typeof input === "object" ? input : { loaded: 0, answeredBy: "client" };
59
+ const loaded = toWhole(safe.loaded);
60
+ const matched = toOptionalWhole(safe.matched);
61
+ const total = toOptionalWhole(safe.total);
62
+ const cap = toOptionalWhole(safe.cap);
63
+ const answeredBy = safe.answeredBy === "source" ? "source" : "client";
64
+ const noun = typeof safe.noun === "string" && safe.noun.trim() ? safe.noun.trim() : "row";
65
+ const reference = matched ?? total;
66
+ const capReached = cap !== void 0 && cap > 0 && loaded >= cap;
67
+ if (reference !== void 0 && loaded >= reference) {
68
+ return {
69
+ scope: "all",
70
+ complete: true,
71
+ count: { value: matched ?? reference, exact: true },
72
+ notice: "",
73
+ badge: ""
74
+ };
75
+ }
76
+ if (answeredBy === "source" && matched !== void 0 && !capReached) {
77
+ return { scope: "all", complete: true, count: { value: matched, exact: true }, notice: "", badge: "" };
78
+ }
79
+ if (capReached && cap !== void 0) {
80
+ const rest = reference !== void 0 ? `${quantity(reference, noun)} in this table; the rest were not looked at.` : "Anything past that was not looked at.";
81
+ return {
82
+ scope: "capped",
83
+ complete: false,
84
+ count: { value: loaded, exact: false },
85
+ notice: `This filter searched the first ${quantity(cap, noun)} only. ${rest}`,
86
+ badge: PARTIAL_BADGE
87
+ };
88
+ }
89
+ if (answeredBy === "client" && reference !== void 0) {
90
+ return {
91
+ scope: "loaded",
92
+ complete: false,
93
+ count: { value: loaded, exact: false },
94
+ notice: `Filtering the ${quantity(loaded, noun)} loaded so far, not all ${quantity(reference, noun)} in this table.`,
95
+ badge: PARTIAL_BADGE
96
+ };
97
+ }
98
+ if (answeredBy === "source" && total !== void 0) {
99
+ return {
100
+ scope: "all",
101
+ complete: false,
102
+ count: { value: loaded, exact: false },
103
+ notice: `Showing the ${quantity(loaded, noun)} loaded so far out of ${quantity(total, noun)} in this table.`,
104
+ badge: PARTIAL_BADGE
105
+ };
106
+ }
107
+ return {
108
+ scope: answeredBy === "source" ? "all" : "loaded",
109
+ complete: false,
110
+ count: { value: loaded, exact: false },
111
+ notice: `Showing the ${quantity(loaded, noun)} loaded so far. The total number of ${pluralize(noun)} in this table is not known.`,
112
+ badge: UNKNOWN_TOTAL_BADGE
113
+ };
114
+ }
115
+ var JARGON_WORDS = ["cap", "limit", "fetch", "query", "client", "source"];
116
+ //# sourceMappingURL=coverage.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/data-table/coverage.ts"],"sourcesContent":["/**\n * Honest coverage for the canonical data table.\n *\n * A table that filters over the rows it happens to hold and then prints a count\n * beside them is telling the reader that the count is the answer. When the rows\n * it holds are only part of the table, that is a lie on screen. This module is\n * the model that decides which of the three things is true — the answer covers\n * everything, the answer covers only what is loaded, or a ceiling cut the\n * answer short — and writes the one plain sentence that says so.\n *\n * Pure: no React, no DOM, no formatting beyond `Intl.NumberFormat`. Nothing here\n * throws on any input.\n */\n\nexport type CoverageScope = \"all\" | \"loaded\" | \"capped\";\n\nexport interface CoverageInput {\n /** Rows the table currently holds. */\n loaded: number;\n /** Total rows the source says match the query, when it can say. */\n matched?: number | undefined;\n /** Total rows in the table irrespective of the query, when known. */\n total?: number | undefined;\n /** Hard ceiling the source imposed on this answer, if any (e.g. a 5,000-row fetch cap). */\n cap?: number | undefined;\n /** Whether the SOURCE answered the filters/sort, or the client did over loaded rows. */\n answeredBy: \"source\" | \"client\";\n /** What a row is called, for the sentence. Default \"row\". */\n noun?: string;\n}\n\nexport interface CoverageResult {\n scope: CoverageScope;\n /** True only when the answer provably covers every matching row AND the count is exact. */\n complete: boolean;\n /** The count to show beside the table, and whether it is exact. */\n count: { value: number; exact: boolean };\n /** One plain-English sentence. Empty string when scope is \"all\" and there is nothing to warn about. */\n notice: string;\n /** Short label for a badge, e.g. \"Partial\". Empty when complete. */\n badge: string;\n}\n\n/** Badge for an answer that covers only part of the data. */\nexport const PARTIAL_BADGE = \"Partial\";\n/** Badge for an answer whose own size cannot be stated. */\nexport const UNKNOWN_TOTAL_BADGE = \"Unknown total\";\n\n/* ------------------------------------------------------------------ */\n/* words and numbers */\n/* ------------------------------------------------------------------ */\n\nconst NUMBERS = new Intl.NumberFormat(\"en-US\");\n\nfunction formatNumber(value: number): string {\n try {\n return NUMBERS.format(value);\n } catch {\n return String(value);\n }\n}\n\nfunction pluralize(noun: string): string {\n const word = noun.trim();\n if (!word) return \"rows\";\n const lower = word.toLowerCase();\n if (/[^aeiou]y$/.test(lower)) return `${word.slice(0, -1)}ies`;\n if (/(s|x|z|ch|sh)$/.test(lower)) return `${word}es`;\n return `${word}s`;\n}\n\n/** \"1 row\" / \"84,312 rows\". */\nfunction quantity(value: number, noun: string): string {\n return `${formatNumber(value)} ${value === 1 ? noun.trim() || \"row\" : pluralize(noun)}`;\n}\n\n/* ------------------------------------------------------------------ */\n/* clamping — nothing here throws */\n/* ------------------------------------------------------------------ */\n\nfunction toWhole(value: unknown): number {\n return typeof value === \"number\" && Number.isFinite(value) && value > 0 ? Math.floor(value) : 0;\n}\n\nfunction toOptionalWhole(value: unknown): number | undefined {\n if (typeof value !== \"number\" || !Number.isFinite(value) || value < 0) return undefined;\n return Math.floor(value);\n}\n\n/* ------------------------------------------------------------------ */\n\n/**\n * Decide what the table is allowed to claim about the answer it is showing.\n *\n * Order of reasoning:\n * 1. If a known figure says everything is already here, the answer is complete\n * — even when a ceiling exists, because the ceiling was never reached.\n * 2. A source that answered the filters and can state how many rows match is\n * complete: source-side paging does not make the count a guess.\n * 3. A ceiling the loaded count has reached means part of the data was never\n * looked at. This is the sentence that replaces the silent lie.\n * 4. A client filtering fewer rows than exist is answering over part of them.\n * 5. When the size of the data is not known at all, say so rather than letting\n * the loaded count pose as the total.\n */\nexport function describeCoverage(input: CoverageInput): CoverageResult {\n const safe: CoverageInput = input && typeof input === \"object\" ? input : { loaded: 0, answeredBy: \"client\" };\n const loaded = toWhole(safe.loaded);\n const matched = toOptionalWhole(safe.matched);\n const total = toOptionalWhole(safe.total);\n const cap = toOptionalWhole(safe.cap);\n const answeredBy = safe.answeredBy === \"source\" ? \"source\" : \"client\";\n const noun = typeof safe.noun === \"string\" && safe.noun.trim() ? safe.noun.trim() : \"row\";\n\n const reference = matched ?? total;\n const capReached = cap !== undefined && cap > 0 && loaded >= cap;\n\n // 1. Everything that exists is already here.\n if (reference !== undefined && loaded >= reference) {\n return {\n scope: \"all\",\n complete: true,\n count: { value: matched ?? reference, exact: true },\n notice: \"\",\n badge: \"\",\n };\n }\n\n // 2. The source answered the filters and stated the size of the answer.\n if (answeredBy === \"source\" && matched !== undefined && !capReached) {\n return { scope: \"all\", complete: true, count: { value: matched, exact: true }, notice: \"\", badge: \"\" };\n }\n\n // 3. A ceiling cut the answer short.\n if (capReached && cap !== undefined) {\n const rest =\n reference !== undefined\n ? `${quantity(reference, noun)} in this table; the rest were not looked at.`\n : \"Anything past that was not looked at.\";\n return {\n scope: \"capped\",\n complete: false,\n count: { value: loaded, exact: false },\n notice: `This filter searched the first ${quantity(cap, noun)} only. ${rest}`,\n badge: PARTIAL_BADGE,\n };\n }\n\n // 4. The client is filtering over fewer rows than exist.\n if (answeredBy === \"client\" && reference !== undefined) {\n return {\n scope: \"loaded\",\n complete: false,\n count: { value: loaded, exact: false },\n notice: `Filtering the ${quantity(loaded, noun)} loaded so far, not all ${quantity(reference, noun)} in this table.`,\n badge: PARTIAL_BADGE,\n };\n }\n\n // 5a. The source answered everything but cannot say how big the answer is.\n if (answeredBy === \"source\" && total !== undefined) {\n return {\n scope: \"all\",\n complete: false,\n count: { value: loaded, exact: false },\n notice: `Showing the ${quantity(loaded, noun)} loaded so far out of ${quantity(total, noun)} in this table.`,\n badge: PARTIAL_BADGE,\n };\n }\n\n // 5b. Nothing is known beyond what is on screen.\n return {\n scope: answeredBy === \"source\" ? \"all\" : \"loaded\",\n complete: false,\n count: { value: loaded, exact: false },\n notice: `Showing the ${quantity(loaded, noun)} loaded so far. The total number of ${pluralize(noun)} in this table is not known.`,\n badge: UNKNOWN_TOTAL_BADGE,\n };\n}\n\n/** Words a sentence for a non-technical reader must never contain. */\nexport const JARGON_WORDS = [\"cap\", \"limit\", \"fetch\", \"query\", \"client\", \"source\"] as const;\n"],"mappings":"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","names":[]}
@@ -0,0 +1,65 @@
1
+ /**
2
+ * Honest coverage for the canonical data table.
3
+ *
4
+ * A table that filters over the rows it happens to hold and then prints a count
5
+ * beside them is telling the reader that the count is the answer. When the rows
6
+ * it holds are only part of the table, that is a lie on screen. This module is
7
+ * the model that decides which of the three things is true — the answer covers
8
+ * everything, the answer covers only what is loaded, or a ceiling cut the
9
+ * answer short — and writes the one plain sentence that says so.
10
+ *
11
+ * Pure: no React, no DOM, no formatting beyond `Intl.NumberFormat`. Nothing here
12
+ * throws on any input.
13
+ */
14
+ type CoverageScope = "all" | "loaded" | "capped";
15
+ interface CoverageInput {
16
+ /** Rows the table currently holds. */
17
+ loaded: number;
18
+ /** Total rows the source says match the query, when it can say. */
19
+ matched?: number | undefined;
20
+ /** Total rows in the table irrespective of the query, when known. */
21
+ total?: number | undefined;
22
+ /** Hard ceiling the source imposed on this answer, if any (e.g. a 5,000-row fetch cap). */
23
+ cap?: number | undefined;
24
+ /** Whether the SOURCE answered the filters/sort, or the client did over loaded rows. */
25
+ answeredBy: "source" | "client";
26
+ /** What a row is called, for the sentence. Default "row". */
27
+ noun?: string;
28
+ }
29
+ interface CoverageResult {
30
+ scope: CoverageScope;
31
+ /** True only when the answer provably covers every matching row AND the count is exact. */
32
+ complete: boolean;
33
+ /** The count to show beside the table, and whether it is exact. */
34
+ count: {
35
+ value: number;
36
+ exact: boolean;
37
+ };
38
+ /** One plain-English sentence. Empty string when scope is "all" and there is nothing to warn about. */
39
+ notice: string;
40
+ /** Short label for a badge, e.g. "Partial". Empty when complete. */
41
+ badge: string;
42
+ }
43
+ /** Badge for an answer that covers only part of the data. */
44
+ declare const PARTIAL_BADGE = "Partial";
45
+ /** Badge for an answer whose own size cannot be stated. */
46
+ declare const UNKNOWN_TOTAL_BADGE = "Unknown total";
47
+ /**
48
+ * Decide what the table is allowed to claim about the answer it is showing.
49
+ *
50
+ * Order of reasoning:
51
+ * 1. If a known figure says everything is already here, the answer is complete
52
+ * — even when a ceiling exists, because the ceiling was never reached.
53
+ * 2. A source that answered the filters and can state how many rows match is
54
+ * complete: source-side paging does not make the count a guess.
55
+ * 3. A ceiling the loaded count has reached means part of the data was never
56
+ * looked at. This is the sentence that replaces the silent lie.
57
+ * 4. A client filtering fewer rows than exist is answering over part of them.
58
+ * 5. When the size of the data is not known at all, say so rather than letting
59
+ * the loaded count pose as the total.
60
+ */
61
+ declare function describeCoverage(input: CoverageInput): CoverageResult;
62
+ /** Words a sentence for a non-technical reader must never contain. */
63
+ declare const JARGON_WORDS: readonly ["cap", "limit", "fetch", "query", "client", "source"];
64
+
65
+ export { type CoverageInput, type CoverageResult, type CoverageScope, JARGON_WORDS, PARTIAL_BADGE, UNKNOWN_TOTAL_BADGE, describeCoverage };
@@ -0,0 +1,65 @@
1
+ /**
2
+ * Honest coverage for the canonical data table.
3
+ *
4
+ * A table that filters over the rows it happens to hold and then prints a count
5
+ * beside them is telling the reader that the count is the answer. When the rows
6
+ * it holds are only part of the table, that is a lie on screen. This module is
7
+ * the model that decides which of the three things is true — the answer covers
8
+ * everything, the answer covers only what is loaded, or a ceiling cut the
9
+ * answer short — and writes the one plain sentence that says so.
10
+ *
11
+ * Pure: no React, no DOM, no formatting beyond `Intl.NumberFormat`. Nothing here
12
+ * throws on any input.
13
+ */
14
+ type CoverageScope = "all" | "loaded" | "capped";
15
+ interface CoverageInput {
16
+ /** Rows the table currently holds. */
17
+ loaded: number;
18
+ /** Total rows the source says match the query, when it can say. */
19
+ matched?: number | undefined;
20
+ /** Total rows in the table irrespective of the query, when known. */
21
+ total?: number | undefined;
22
+ /** Hard ceiling the source imposed on this answer, if any (e.g. a 5,000-row fetch cap). */
23
+ cap?: number | undefined;
24
+ /** Whether the SOURCE answered the filters/sort, or the client did over loaded rows. */
25
+ answeredBy: "source" | "client";
26
+ /** What a row is called, for the sentence. Default "row". */
27
+ noun?: string;
28
+ }
29
+ interface CoverageResult {
30
+ scope: CoverageScope;
31
+ /** True only when the answer provably covers every matching row AND the count is exact. */
32
+ complete: boolean;
33
+ /** The count to show beside the table, and whether it is exact. */
34
+ count: {
35
+ value: number;
36
+ exact: boolean;
37
+ };
38
+ /** One plain-English sentence. Empty string when scope is "all" and there is nothing to warn about. */
39
+ notice: string;
40
+ /** Short label for a badge, e.g. "Partial". Empty when complete. */
41
+ badge: string;
42
+ }
43
+ /** Badge for an answer that covers only part of the data. */
44
+ declare const PARTIAL_BADGE = "Partial";
45
+ /** Badge for an answer whose own size cannot be stated. */
46
+ declare const UNKNOWN_TOTAL_BADGE = "Unknown total";
47
+ /**
48
+ * Decide what the table is allowed to claim about the answer it is showing.
49
+ *
50
+ * Order of reasoning:
51
+ * 1. If a known figure says everything is already here, the answer is complete
52
+ * — even when a ceiling exists, because the ceiling was never reached.
53
+ * 2. A source that answered the filters and can state how many rows match is
54
+ * complete: source-side paging does not make the count a guess.
55
+ * 3. A ceiling the loaded count has reached means part of the data was never
56
+ * looked at. This is the sentence that replaces the silent lie.
57
+ * 4. A client filtering fewer rows than exist is answering over part of them.
58
+ * 5. When the size of the data is not known at all, say so rather than letting
59
+ * the loaded count pose as the total.
60
+ */
61
+ declare function describeCoverage(input: CoverageInput): CoverageResult;
62
+ /** Words a sentence for a non-technical reader must never contain. */
63
+ declare const JARGON_WORDS: readonly ["cap", "limit", "fetch", "query", "client", "source"];
64
+
65
+ export { type CoverageInput, type CoverageResult, type CoverageScope, JARGON_WORDS, PARTIAL_BADGE, UNKNOWN_TOTAL_BADGE, describeCoverage };
@@ -0,0 +1,96 @@
1
+
2
+ // src/data-table/coverage.ts
3
+ var PARTIAL_BADGE = "Partial";
4
+ var UNKNOWN_TOTAL_BADGE = "Unknown total";
5
+ var NUMBERS = new Intl.NumberFormat("en-US");
6
+ function formatNumber(value) {
7
+ try {
8
+ return NUMBERS.format(value);
9
+ } catch {
10
+ return String(value);
11
+ }
12
+ }
13
+ function pluralize(noun) {
14
+ const word = noun.trim();
15
+ if (!word) return "rows";
16
+ const lower = word.toLowerCase();
17
+ if (/[^aeiou]y$/.test(lower)) return `${word.slice(0, -1)}ies`;
18
+ if (/(s|x|z|ch|sh)$/.test(lower)) return `${word}es`;
19
+ return `${word}s`;
20
+ }
21
+ function quantity(value, noun) {
22
+ return `${formatNumber(value)} ${value === 1 ? noun.trim() || "row" : pluralize(noun)}`;
23
+ }
24
+ function toWhole(value) {
25
+ return typeof value === "number" && Number.isFinite(value) && value > 0 ? Math.floor(value) : 0;
26
+ }
27
+ function toOptionalWhole(value) {
28
+ if (typeof value !== "number" || !Number.isFinite(value) || value < 0) return void 0;
29
+ return Math.floor(value);
30
+ }
31
+ function describeCoverage(input) {
32
+ const safe = input && typeof input === "object" ? input : { loaded: 0, answeredBy: "client" };
33
+ const loaded = toWhole(safe.loaded);
34
+ const matched = toOptionalWhole(safe.matched);
35
+ const total = toOptionalWhole(safe.total);
36
+ const cap = toOptionalWhole(safe.cap);
37
+ const answeredBy = safe.answeredBy === "source" ? "source" : "client";
38
+ const noun = typeof safe.noun === "string" && safe.noun.trim() ? safe.noun.trim() : "row";
39
+ const reference = matched ?? total;
40
+ const capReached = cap !== void 0 && cap > 0 && loaded >= cap;
41
+ if (reference !== void 0 && loaded >= reference) {
42
+ return {
43
+ scope: "all",
44
+ complete: true,
45
+ count: { value: matched ?? reference, exact: true },
46
+ notice: "",
47
+ badge: ""
48
+ };
49
+ }
50
+ if (answeredBy === "source" && matched !== void 0 && !capReached) {
51
+ return { scope: "all", complete: true, count: { value: matched, exact: true }, notice: "", badge: "" };
52
+ }
53
+ if (capReached && cap !== void 0) {
54
+ const rest = reference !== void 0 ? `${quantity(reference, noun)} in this table; the rest were not looked at.` : "Anything past that was not looked at.";
55
+ return {
56
+ scope: "capped",
57
+ complete: false,
58
+ count: { value: loaded, exact: false },
59
+ notice: `This filter searched the first ${quantity(cap, noun)} only. ${rest}`,
60
+ badge: PARTIAL_BADGE
61
+ };
62
+ }
63
+ if (answeredBy === "client" && reference !== void 0) {
64
+ return {
65
+ scope: "loaded",
66
+ complete: false,
67
+ count: { value: loaded, exact: false },
68
+ notice: `Filtering the ${quantity(loaded, noun)} loaded so far, not all ${quantity(reference, noun)} in this table.`,
69
+ badge: PARTIAL_BADGE
70
+ };
71
+ }
72
+ if (answeredBy === "source" && total !== void 0) {
73
+ return {
74
+ scope: "all",
75
+ complete: false,
76
+ count: { value: loaded, exact: false },
77
+ notice: `Showing the ${quantity(loaded, noun)} loaded so far out of ${quantity(total, noun)} in this table.`,
78
+ badge: PARTIAL_BADGE
79
+ };
80
+ }
81
+ return {
82
+ scope: answeredBy === "source" ? "all" : "loaded",
83
+ complete: false,
84
+ count: { value: loaded, exact: false },
85
+ notice: `Showing the ${quantity(loaded, noun)} loaded so far. The total number of ${pluralize(noun)} in this table is not known.`,
86
+ badge: UNKNOWN_TOTAL_BADGE
87
+ };
88
+ }
89
+ var JARGON_WORDS = ["cap", "limit", "fetch", "query", "client", "source"];
90
+ export {
91
+ JARGON_WORDS,
92
+ PARTIAL_BADGE,
93
+ UNKNOWN_TOTAL_BADGE,
94
+ describeCoverage
95
+ };
96
+ //# sourceMappingURL=coverage.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/data-table/coverage.ts"],"sourcesContent":["/**\n * Honest coverage for the canonical data table.\n *\n * A table that filters over the rows it happens to hold and then prints a count\n * beside them is telling the reader that the count is the answer. When the rows\n * it holds are only part of the table, that is a lie on screen. This module is\n * the model that decides which of the three things is true — the answer covers\n * everything, the answer covers only what is loaded, or a ceiling cut the\n * answer short — and writes the one plain sentence that says so.\n *\n * Pure: no React, no DOM, no formatting beyond `Intl.NumberFormat`. Nothing here\n * throws on any input.\n */\n\nexport type CoverageScope = \"all\" | \"loaded\" | \"capped\";\n\nexport interface CoverageInput {\n /** Rows the table currently holds. */\n loaded: number;\n /** Total rows the source says match the query, when it can say. */\n matched?: number | undefined;\n /** Total rows in the table irrespective of the query, when known. */\n total?: number | undefined;\n /** Hard ceiling the source imposed on this answer, if any (e.g. a 5,000-row fetch cap). */\n cap?: number | undefined;\n /** Whether the SOURCE answered the filters/sort, or the client did over loaded rows. */\n answeredBy: \"source\" | \"client\";\n /** What a row is called, for the sentence. Default \"row\". */\n noun?: string;\n}\n\nexport interface CoverageResult {\n scope: CoverageScope;\n /** True only when the answer provably covers every matching row AND the count is exact. */\n complete: boolean;\n /** The count to show beside the table, and whether it is exact. */\n count: { value: number; exact: boolean };\n /** One plain-English sentence. Empty string when scope is \"all\" and there is nothing to warn about. */\n notice: string;\n /** Short label for a badge, e.g. \"Partial\". Empty when complete. */\n badge: string;\n}\n\n/** Badge for an answer that covers only part of the data. */\nexport const PARTIAL_BADGE = \"Partial\";\n/** Badge for an answer whose own size cannot be stated. */\nexport const UNKNOWN_TOTAL_BADGE = \"Unknown total\";\n\n/* ------------------------------------------------------------------ */\n/* words and numbers */\n/* ------------------------------------------------------------------ */\n\nconst NUMBERS = new Intl.NumberFormat(\"en-US\");\n\nfunction formatNumber(value: number): string {\n try {\n return NUMBERS.format(value);\n } catch {\n return String(value);\n }\n}\n\nfunction pluralize(noun: string): string {\n const word = noun.trim();\n if (!word) return \"rows\";\n const lower = word.toLowerCase();\n if (/[^aeiou]y$/.test(lower)) return `${word.slice(0, -1)}ies`;\n if (/(s|x|z|ch|sh)$/.test(lower)) return `${word}es`;\n return `${word}s`;\n}\n\n/** \"1 row\" / \"84,312 rows\". */\nfunction quantity(value: number, noun: string): string {\n return `${formatNumber(value)} ${value === 1 ? noun.trim() || \"row\" : pluralize(noun)}`;\n}\n\n/* ------------------------------------------------------------------ */\n/* clamping — nothing here throws */\n/* ------------------------------------------------------------------ */\n\nfunction toWhole(value: unknown): number {\n return typeof value === \"number\" && Number.isFinite(value) && value > 0 ? Math.floor(value) : 0;\n}\n\nfunction toOptionalWhole(value: unknown): number | undefined {\n if (typeof value !== \"number\" || !Number.isFinite(value) || value < 0) return undefined;\n return Math.floor(value);\n}\n\n/* ------------------------------------------------------------------ */\n\n/**\n * Decide what the table is allowed to claim about the answer it is showing.\n *\n * Order of reasoning:\n * 1. If a known figure says everything is already here, the answer is complete\n * — even when a ceiling exists, because the ceiling was never reached.\n * 2. A source that answered the filters and can state how many rows match is\n * complete: source-side paging does not make the count a guess.\n * 3. A ceiling the loaded count has reached means part of the data was never\n * looked at. This is the sentence that replaces the silent lie.\n * 4. A client filtering fewer rows than exist is answering over part of them.\n * 5. When the size of the data is not known at all, say so rather than letting\n * the loaded count pose as the total.\n */\nexport function describeCoverage(input: CoverageInput): CoverageResult {\n const safe: CoverageInput = input && typeof input === \"object\" ? input : { loaded: 0, answeredBy: \"client\" };\n const loaded = toWhole(safe.loaded);\n const matched = toOptionalWhole(safe.matched);\n const total = toOptionalWhole(safe.total);\n const cap = toOptionalWhole(safe.cap);\n const answeredBy = safe.answeredBy === \"source\" ? \"source\" : \"client\";\n const noun = typeof safe.noun === \"string\" && safe.noun.trim() ? safe.noun.trim() : \"row\";\n\n const reference = matched ?? total;\n const capReached = cap !== undefined && cap > 0 && loaded >= cap;\n\n // 1. Everything that exists is already here.\n if (reference !== undefined && loaded >= reference) {\n return {\n scope: \"all\",\n complete: true,\n count: { value: matched ?? reference, exact: true },\n notice: \"\",\n badge: \"\",\n };\n }\n\n // 2. The source answered the filters and stated the size of the answer.\n if (answeredBy === \"source\" && matched !== undefined && !capReached) {\n return { scope: \"all\", complete: true, count: { value: matched, exact: true }, notice: \"\", badge: \"\" };\n }\n\n // 3. A ceiling cut the answer short.\n if (capReached && cap !== undefined) {\n const rest =\n reference !== undefined\n ? `${quantity(reference, noun)} in this table; the rest were not looked at.`\n : \"Anything past that was not looked at.\";\n return {\n scope: \"capped\",\n complete: false,\n count: { value: loaded, exact: false },\n notice: `This filter searched the first ${quantity(cap, noun)} only. ${rest}`,\n badge: PARTIAL_BADGE,\n };\n }\n\n // 4. The client is filtering over fewer rows than exist.\n if (answeredBy === \"client\" && reference !== undefined) {\n return {\n scope: \"loaded\",\n complete: false,\n count: { value: loaded, exact: false },\n notice: `Filtering the ${quantity(loaded, noun)} loaded so far, not all ${quantity(reference, noun)} in this table.`,\n badge: PARTIAL_BADGE,\n };\n }\n\n // 5a. The source answered everything but cannot say how big the answer is.\n if (answeredBy === \"source\" && total !== undefined) {\n return {\n scope: \"all\",\n complete: false,\n count: { value: loaded, exact: false },\n notice: `Showing the ${quantity(loaded, noun)} loaded so far out of ${quantity(total, noun)} in this table.`,\n badge: PARTIAL_BADGE,\n };\n }\n\n // 5b. Nothing is known beyond what is on screen.\n return {\n scope: answeredBy === \"source\" ? \"all\" : \"loaded\",\n complete: false,\n count: { value: loaded, exact: false },\n notice: `Showing the ${quantity(loaded, noun)} loaded so far. The total number of ${pluralize(noun)} in this table is not known.`,\n badge: UNKNOWN_TOTAL_BADGE,\n };\n}\n\n/** Words a sentence for a non-technical reader must never contain. */\nexport const JARGON_WORDS = [\"cap\", \"limit\", \"fetch\", \"query\", \"client\", \"source\"] as const;\n"],"mappings":"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","names":[]}