@ai-matrx/design-system 0.14.2 → 0.15.1
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.
- package/CHANGELOG.md +49 -0
- package/README.md +26 -0
- package/dist/data-table/copy-helpers.cjs +149 -0
- package/dist/data-table/copy-helpers.cjs.map +1 -0
- package/dist/data-table/copy-helpers.d.cts +31 -0
- package/dist/data-table/copy-helpers.d.ts +31 -0
- package/dist/data-table/copy-helpers.js +127 -0
- package/dist/data-table/copy-helpers.js.map +1 -0
- package/dist/data-table/copy-types.cjs +19 -0
- package/dist/data-table/copy-types.cjs.map +1 -0
- package/dist/data-table/copy-types.d.cts +137 -0
- package/dist/data-table/copy-types.d.ts +137 -0
- package/dist/data-table/copy-types.js +1 -0
- package/dist/data-table/copy-types.js.map +1 -0
- package/dist/data-table/data-row-inspector.cjs +1016 -0
- package/dist/data-table/data-row-inspector.cjs.map +1 -0
- package/dist/data-table/data-row-inspector.d.cts +34 -0
- package/dist/data-table/data-row-inspector.d.ts +34 -0
- package/dist/data-table/data-row-inspector.js +988 -0
- package/dist/data-table/data-row-inspector.js.map +1 -0
- package/dist/data-table/data-row-window.cjs +1136 -0
- package/dist/data-table/data-row-window.cjs.map +1 -0
- package/dist/data-table/data-row-window.d.cts +29 -0
- package/dist/data-table/data-row-window.d.ts +29 -0
- package/dist/data-table/data-row-window.js +1110 -0
- package/dist/data-table/data-row-window.js.map +1 -0
- package/dist/data-table/filter-engine.cjs +314 -0
- package/dist/data-table/filter-engine.cjs.map +1 -0
- package/dist/data-table/filter-engine.d.cts +35 -0
- package/dist/data-table/filter-engine.d.ts +35 -0
- package/dist/data-table/filter-engine.js +292 -0
- package/dist/data-table/filter-engine.js.map +1 -0
- package/dist/data-table/host.cjs +40 -0
- package/dist/data-table/host.cjs.map +1 -0
- package/dist/data-table/host.d.cts +73 -0
- package/dist/data-table/host.d.ts +73 -0
- package/dist/data-table/host.js +20 -0
- package/dist/data-table/host.js.map +1 -0
- package/dist/data-table/index.cjs +6865 -0
- package/dist/data-table/index.cjs.map +1 -0
- package/dist/data-table/index.d.cts +27 -0
- package/dist/data-table/index.d.ts +27 -0
- package/dist/data-table/index.js +6869 -0
- package/dist/data-table/index.js.map +1 -0
- package/dist/data-table/infer-filter.cjs +114 -0
- package/dist/data-table/infer-filter.cjs.map +1 -0
- package/dist/data-table/infer-filter.d.cts +17 -0
- package/dist/data-table/infer-filter.d.ts +17 -0
- package/dist/data-table/infer-filter.js +92 -0
- package/dist/data-table/infer-filter.js.map +1 -0
- package/dist/data-table/layered-filters.cjs +249 -0
- package/dist/data-table/layered-filters.cjs.map +1 -0
- package/dist/data-table/layered-filters.d.cts +3 -0
- package/dist/data-table/layered-filters.d.ts +3 -0
- package/dist/data-table/layered-filters.js +229 -0
- package/dist/data-table/layered-filters.js.map +1 -0
- package/dist/data-table/pagination.cjs +595 -0
- package/dist/data-table/pagination.cjs.map +1 -0
- package/dist/data-table/pagination.d.cts +34 -0
- package/dist/data-table/pagination.d.ts +34 -0
- package/dist/data-table/pagination.js +563 -0
- package/dist/data-table/pagination.js.map +1 -0
- package/dist/data-table/query-control.cjs +129 -0
- package/dist/data-table/query-control.cjs.map +1 -0
- package/dist/data-table/query-control.d.cts +32 -0
- package/dist/data-table/query-control.d.ts +32 -0
- package/dist/data-table/query-control.js +107 -0
- package/dist/data-table/query-control.js.map +1 -0
- package/dist/data-table/scroll-pagination.cjs +222 -0
- package/dist/data-table/scroll-pagination.cjs.map +1 -0
- package/dist/data-table/scroll-pagination.d.cts +37 -0
- package/dist/data-table/scroll-pagination.d.ts +37 -0
- package/dist/data-table/scroll-pagination.js +202 -0
- package/dist/data-table/scroll-pagination.js.map +1 -0
- package/dist/data-table/types.cjs +19 -0
- package/dist/data-table/types.cjs.map +1 -0
- package/dist/data-table/types.d.cts +3 -0
- package/dist/data-table/types.d.ts +3 -0
- package/dist/data-table/types.js +1 -0
- package/dist/data-table/types.js.map +1 -0
- package/dist/data-table/url-state.cjs +201 -0
- package/dist/data-table/url-state.cjs.map +1 -0
- package/dist/data-table/url-state.d.cts +33 -0
- package/dist/data-table/url-state.d.ts +33 -0
- package/dist/data-table/url-state.js +184 -0
- package/dist/data-table/url-state.js.map +1 -0
- package/dist/data-table/uuid-cell.cjs +371 -0
- package/dist/data-table/uuid-cell.cjs.map +1 -0
- package/dist/data-table/uuid-cell.d.cts +31 -0
- package/dist/data-table/uuid-cell.d.ts +31 -0
- package/dist/data-table/uuid-cell.js +345 -0
- package/dist/data-table/uuid-cell.js.map +1 -0
- package/dist/data-table/xlsx.cjs +100 -0
- package/dist/data-table/xlsx.cjs.map +1 -0
- package/dist/data-table/xlsx.d.cts +16 -0
- package/dist/data-table/xlsx.d.ts +16 -0
- package/dist/data-table/xlsx.js +70 -0
- package/dist/data-table/xlsx.js.map +1 -0
- package/dist/index.cjs +10 -6
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +3 -4
- package/dist/index.d.ts +3 -4
- package/dist/index.js +10 -6
- package/dist/index.js.map +1 -1
- package/dist/styles.css +22 -0
- package/dist/types-ClaxTaXq.d.ts +798 -0
- package/dist/types-XXbVB1sh.d.cts +798 -0
- package/package.json +164 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,54 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.15.1 — 2026-09-11
|
|
4
|
+
|
|
5
|
+
The unpublished 0.15.0 candidate caught a clean-checkout test-order defect.
|
|
6
|
+
Source tests now run before any build; the fresh tarball gate owns artifact
|
|
7
|
+
existence, entrypoint, context, and boundary checks.
|
|
8
|
+
|
|
9
|
+
The canonical MatrxDataTable now ships at `./data-table`, including local and
|
|
10
|
+
controlled queries, URL state, filters, sorting, selection, editing, hierarchy,
|
|
11
|
+
copy/export, inspection, row windows, mobile cards, and pagination. Dedicated
|
|
12
|
+
subpaths keep host binding and utility imports separate from the table body.
|
|
13
|
+
|
|
14
|
+
`controlled-append` accepts a source-owned pagination receipt and requests one
|
|
15
|
+
next page after a fresh user gesture plus actual downward scrolling near the
|
|
16
|
+
bottom. Mounting, layout changes, and programmatic scrolling alone do not fetch.
|
|
17
|
+
It preserves loaded rows, shows a two-row loading footer, and offers explicit
|
|
18
|
+
Load more/Retry/Reload controls. Manual mode disables automatic fetching.
|
|
19
|
+
|
|
20
|
+
Dashboard parity includes column visibility/order, deferred and negated filters,
|
|
21
|
+
null/empty distinctions, footer rows, row highlighting, distinct sort/filter
|
|
22
|
+
accessors, and source-owned filter/sort with local loaded-row search/paging.
|
|
23
|
+
|
|
24
|
+
Host, portal, and dialog contexts share identity across public entrypoints and
|
|
25
|
+
ESM/CJS loaders; a packed-artifact provider test prevents silent lost bindings.
|
|
26
|
+
|
|
27
|
+
### Consumer action
|
|
28
|
+
|
|
29
|
+
Replace local MatrxDataTable imports with `@ai-matrx/design-system/data-table`
|
|
30
|
+
and utility imports with their dedicated subpaths. Bind framework/navigation,
|
|
31
|
+
copy, entity doors, notifications, and window identities through the lightweight
|
|
32
|
+
`./data-table/host` provider. Defaults work without a provider. Keep the existing
|
|
33
|
+
stylesheet setup (`styles.css`, tokens/theme and Tailwind source registration).
|
|
34
|
+
|
|
35
|
+
Existing local and exact-page consumers retain their modes. Opt paginated
|
|
36
|
+
sources into `controlled-append`; the table cannot infer an endpoint or cursor.
|
|
37
|
+
Use `@ai-matrx/data/react` `usePaginatedData` or an equivalent receipt, key every
|
|
38
|
+
source-affecting input, and pass the application-resolved scrolling preference.
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
## 0.14.3 — 2026-09-11
|
|
42
|
+
|
|
43
|
+
`ConfirmDialog` now renders its accessible description through Radix's
|
|
44
|
+
`asChild` slot on a semantic `<div>`. Callers can pass block-level consequence
|
|
45
|
+
content without nesting it inside the default paragraph element, while the
|
|
46
|
+
dialog keeps its `aria-describedby` relationship intact.
|
|
47
|
+
|
|
48
|
+
### Consumer action
|
|
49
|
+
|
|
50
|
+
Update the package. Existing `description` callers need no changes.
|
|
51
|
+
|
|
3
52
|
## 0.14.2 — 2026-09-11
|
|
4
53
|
|
|
5
54
|
`DialogContent` now centres its desktop card with `inset-0 m-auto h-fit`,
|
package/README.md
CHANGED
|
@@ -136,3 +136,29 @@ pnpm --filter @ai-matrx/design-system check:package
|
|
|
136
136
|
```
|
|
137
137
|
|
|
138
138
|
The gate validates declarations and exports against the packed tarball, installs it into an empty project, and imports the public entry point.
|
|
139
|
+
|
|
140
|
+
## Data tables
|
|
141
|
+
|
|
142
|
+
```tsx
|
|
143
|
+
import { MatrxDataTable } from "@ai-matrx/design-system/data-table";
|
|
144
|
+
import { usePaginatedData } from "@ai-matrx/data/react";
|
|
145
|
+
|
|
146
|
+
// The caller owns loadPage, source identity, and query state.
|
|
147
|
+
const pagination = usePaginatedData({ queryKey, initialCursor: 0, loadPage, getRowId });
|
|
148
|
+
<MatrxDataTable data={pagination.rows} columns={columns} getRowId={getRowId}
|
|
149
|
+
query={{ mode: "controlled-append", state, onStateChange, pagination,
|
|
150
|
+
scroll: { mode: "scroll", thresholdPx: 96, intentTimeoutMs: 1200 } }} />;
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
Use application-resolved preferences for `scroll`; `manual` always retains Load
|
|
154
|
+
more. Numbered exact-page sources use `controlled` with `totalItems`; fully loaded
|
|
155
|
+
client arrays use local mode or `controlled-local`. Partial server results must
|
|
156
|
+
not claim client filtering searched the entire source. Query identity must cover
|
|
157
|
+
authentication, organization, filters, sort, and page size. A page loader receives
|
|
158
|
+
an AbortSignal and returns rows, a next cursor (null when complete), and an
|
|
159
|
+
optional exact total. Repeated cursors fail visibly instead of looping.
|
|
160
|
+
|
|
161
|
+
Framework-specific identity ports are optional via
|
|
162
|
+
`@ai-matrx/design-system/data-table/host`. Utilities and types have dedicated
|
|
163
|
+
subpaths listed in the export map. The package retains copy/subset/export, row
|
|
164
|
+
windows, hierarchy, selection, inline editing, and accessible manual pagination.
|
|
@@ -0,0 +1,149 @@
|
|
|
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/tableCopy.ts
|
|
21
|
+
var tableCopy_exports = {};
|
|
22
|
+
__export(tableCopy_exports, {
|
|
23
|
+
buildRowAgentInput: () => buildRowAgentInput,
|
|
24
|
+
buildViewAgentInput: () => buildViewAgentInput,
|
|
25
|
+
buildViewHuman: () => buildViewHuman,
|
|
26
|
+
rowsToCsvFromColumns: () => rowsToCsvFromColumns,
|
|
27
|
+
rowsToMarkdownTable: () => rowsToMarkdownTable,
|
|
28
|
+
rowsToRecordsFromColumns: () => rowsToRecordsFromColumns
|
|
29
|
+
});
|
|
30
|
+
module.exports = __toCommonJS(tableCopy_exports);
|
|
31
|
+
|
|
32
|
+
// src/data-table/filter-engine.ts
|
|
33
|
+
function columnId(col) {
|
|
34
|
+
if (col.id) return col.id;
|
|
35
|
+
if (col.accessorKey) return String(col.accessorKey);
|
|
36
|
+
throw new Error("MatrxDataTable column requires `id` or `accessorKey`");
|
|
37
|
+
}
|
|
38
|
+
function getCellValue(row, col) {
|
|
39
|
+
if (col.accessorFn) return col.accessorFn(row);
|
|
40
|
+
if (col.accessorKey) {
|
|
41
|
+
return row[col.accessorKey];
|
|
42
|
+
}
|
|
43
|
+
return void 0;
|
|
44
|
+
}
|
|
45
|
+
function stringifyCellValue(value) {
|
|
46
|
+
if (value == null) return "";
|
|
47
|
+
if (typeof value === "string") return value;
|
|
48
|
+
if (typeof value === "number" || typeof value === "boolean") {
|
|
49
|
+
return String(value);
|
|
50
|
+
}
|
|
51
|
+
if (value instanceof Date) return value.toISOString();
|
|
52
|
+
try {
|
|
53
|
+
return JSON.stringify(value);
|
|
54
|
+
} catch {
|
|
55
|
+
return String(value);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// src/data-table/tableCopy.ts
|
|
60
|
+
function rowsToMarkdownTable(rows, columns) {
|
|
61
|
+
const cols = columns.filter((c) => c.filter !== false || c.accessorKey);
|
|
62
|
+
if (cols.length === 0) return "";
|
|
63
|
+
const headers = cols.map((c) => {
|
|
64
|
+
if (typeof c.header === "string") return c.header;
|
|
65
|
+
return columnId(c);
|
|
66
|
+
});
|
|
67
|
+
const lines = [
|
|
68
|
+
`| ${headers.join(" | ")} |`,
|
|
69
|
+
`| ${headers.map(() => "---").join(" | ")} |`
|
|
70
|
+
];
|
|
71
|
+
for (const row of rows) {
|
|
72
|
+
const cells = cols.map(
|
|
73
|
+
(c) => stringifyCellValue(getCellValue(row, c)).replace(/\|/g, "\\|")
|
|
74
|
+
);
|
|
75
|
+
lines.push(`| ${cells.join(" | ")} |`);
|
|
76
|
+
}
|
|
77
|
+
return lines.join("\n");
|
|
78
|
+
}
|
|
79
|
+
function buildRowAgentInput(config, row) {
|
|
80
|
+
return {
|
|
81
|
+
kind: config.rowKind,
|
|
82
|
+
location: config.location,
|
|
83
|
+
description: config.rowDescription ?? `One ${config.label} row.`,
|
|
84
|
+
data: config.agentRow ? config.agentRow(row) : row,
|
|
85
|
+
summary: config.humanRow(row),
|
|
86
|
+
...config.rowAttributes ? { attributes: config.rowAttributes(row) } : {}
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
function buildViewAgentInput(config, visible, all, meta) {
|
|
90
|
+
return {
|
|
91
|
+
kind: config.listKind,
|
|
92
|
+
location: config.location,
|
|
93
|
+
description: meta?.scope === "selected" ? `${visible.length} selected ${visible.length === 1 ? config.label : config.listLabel ?? `${config.label} rows`}.` : config.listDescription ?? `${config.listLabel ?? config.label} \u2014 current filtered/sorted view.`,
|
|
94
|
+
data: visible.map((r) => config.agentRow ? config.agentRow(r) : r),
|
|
95
|
+
summary: visible.map((r) => config.humanRow(r)).join("\n---\n"),
|
|
96
|
+
attributes: {
|
|
97
|
+
visible_count: visible.length,
|
|
98
|
+
total_count: all.length,
|
|
99
|
+
search: meta?.search || void 0,
|
|
100
|
+
search_match: meta?.search ? meta.searchMatchMode : void 0,
|
|
101
|
+
any_of: meta?.anyOf || void 0,
|
|
102
|
+
active_filters: meta?.filterCount ?? 0,
|
|
103
|
+
sort: meta?.sort || void 0,
|
|
104
|
+
scope: meta?.scope ?? "view",
|
|
105
|
+
...config.listAttributes?.(visible, all)
|
|
106
|
+
},
|
|
107
|
+
...config.listContext ? { context: config.listContext(visible, all) } : {}
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
function rowsToCsvFromColumns(rows, columns) {
|
|
111
|
+
const cols = columns.filter((c) => c.filter !== false || c.accessorKey);
|
|
112
|
+
const escape = (value) => /[",\n\r]/.test(value) ? `"${value.replace(/"/g, '""')}"` : value;
|
|
113
|
+
const lines = [
|
|
114
|
+
cols.map((c) => escape(typeof c.header === "string" ? c.header : columnId(c))).join(",")
|
|
115
|
+
];
|
|
116
|
+
for (const row of rows) {
|
|
117
|
+
lines.push(
|
|
118
|
+
cols.map((c) => escape(stringifyCellValue(getCellValue(row, c)))).join(",")
|
|
119
|
+
);
|
|
120
|
+
}
|
|
121
|
+
return lines.join("\n");
|
|
122
|
+
}
|
|
123
|
+
function rowsToRecordsFromColumns(rows, columns) {
|
|
124
|
+
const cols = columns.filter((c) => c.filter !== false || c.accessorKey);
|
|
125
|
+
const headers = cols.map(
|
|
126
|
+
(c) => typeof c.header === "string" ? c.header : columnId(c)
|
|
127
|
+
);
|
|
128
|
+
return rows.map((row) => {
|
|
129
|
+
const record = {};
|
|
130
|
+
cols.forEach((c, index) => {
|
|
131
|
+
record[headers[index] ?? columnId(c)] = stringifyCellValue(getCellValue(row, c));
|
|
132
|
+
});
|
|
133
|
+
return record;
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
function buildViewHuman(config, visible, columns) {
|
|
137
|
+
const md = rowsToMarkdownTable(visible, columns);
|
|
138
|
+
const summaries = visible.map((r) => config.humanRow(r)).join("\n\n");
|
|
139
|
+
return [
|
|
140
|
+
`${config.listLabel ?? config.label} (${visible.length} rows)`,
|
|
141
|
+
"",
|
|
142
|
+
md,
|
|
143
|
+
"",
|
|
144
|
+
"---",
|
|
145
|
+
"",
|
|
146
|
+
summaries
|
|
147
|
+
].join("\n");
|
|
148
|
+
}
|
|
149
|
+
//# sourceMappingURL=copy-helpers.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/data-table/tableCopy.ts","../../src/data-table/filter-engine.ts"],"sourcesContent":["import type { AgentPayloadInput } from \"./copy\";\nimport type { MatrxColumnDef, MatrxDataTableCopyConfig } from \"./types\";\nimport { columnId, getCellValue, stringifyCellValue } from \"./filter-engine\";\n\n/** Human markdown table of the current view (visible columns × rows). */\nexport function rowsToMarkdownTable<T>(\n rows: T[],\n columns: MatrxColumnDef<T>[],\n): string {\n const cols = columns.filter((c) => c.filter !== false || c.accessorKey);\n if (cols.length === 0) return \"\";\n const headers = cols.map((c) => {\n if (typeof c.header === \"string\") return c.header;\n return columnId(c);\n });\n const lines = [\n `| ${headers.join(\" | \")} |`,\n `| ${headers.map(() => \"---\").join(\" | \")} |`,\n ];\n for (const row of rows) {\n const cells = cols.map((c) =>\n stringifyCellValue(getCellValue(row, c)).replace(/\\|/g, \"\\\\|\"),\n );\n lines.push(`| ${cells.join(\" | \")} |`);\n }\n return lines.join(\"\\n\");\n}\n\nexport function buildRowAgentInput<T>(\n config: MatrxDataTableCopyConfig<T>,\n row: T,\n): AgentPayloadInput {\n return {\n kind: config.rowKind,\n location: config.location,\n description: config.rowDescription ?? `One ${config.label} row.`,\n data: config.agentRow ? config.agentRow(row) : row,\n summary: config.humanRow(row),\n ...(config.rowAttributes ? { attributes: config.rowAttributes(row) } : {}),\n };\n}\n\nexport function buildViewAgentInput<T>(\n config: MatrxDataTableCopyConfig<T>,\n visible: T[],\n all: T[],\n meta?: {\n search?: string;\n searchMatchMode?: string;\n anyOf?: string;\n filterCount?: number;\n sort?: string | null;\n /** `custom` = shaped in the copy-subset window (filter & sort before copying). */\n scope?: \"view\" | \"selected\" | \"custom\";\n },\n): AgentPayloadInput {\n return {\n kind: config.listKind,\n location: config.location,\n description:\n meta?.scope === \"selected\"\n ? `${visible.length} selected ${visible.length === 1 ? config.label : (config.listLabel ?? `${config.label} rows`)}.`\n : (config.listDescription ??\n `${config.listLabel ?? config.label} — current filtered/sorted view.`),\n data: visible.map((r) => (config.agentRow ? config.agentRow(r) : r)),\n summary: visible.map((r) => config.humanRow(r)).join(\"\\n---\\n\"),\n attributes: {\n visible_count: visible.length,\n total_count: all.length,\n search: meta?.search || undefined,\n search_match: meta?.search ? meta.searchMatchMode : undefined,\n any_of: meta?.anyOf || undefined,\n active_filters: meta?.filterCount ?? 0,\n sort: meta?.sort || undefined,\n scope: meta?.scope ?? \"view\",\n ...config.listAttributes?.(visible, all),\n },\n ...(config.listContext ? { context: config.listContext(visible, all) } : {}),\n };\n}\n\n/** CSV of the current view (visible columns × rows), for ExportMenu. */\nexport function rowsToCsvFromColumns<T>(\n rows: T[],\n columns: MatrxColumnDef<T>[],\n): string {\n const cols = columns.filter((c) => c.filter !== false || c.accessorKey);\n const escape = (value: string) =>\n /[\",\\n\\r]/.test(value) ? `\"${value.replace(/\"/g, '\"\"')}\"` : value;\n const lines = [\n cols\n .map((c) => escape(typeof c.header === \"string\" ? c.header : columnId(c)))\n .join(\",\"),\n ];\n for (const row of rows) {\n lines.push(\n cols\n .map((c) => escape(stringifyCellValue(getCellValue(row, c))))\n .join(\",\"),\n );\n }\n return lines.join(\"\\n\");\n}\n\n/**\n * The same rows the CSV export produces, as records — the shape a Google Sheet\n * (or any other row destination) takes.\n *\n * It shares `rowsToCsvFromColumns`'s column selection and cell stringification\n * ON PURPOSE: a user who exports the view as CSV and a user who sends it to a\n * Sheet must get the SAME columns, in the same order, with the same values.\n * Two independent row builders is how those two answers start disagreeing.\n */\nexport function rowsToRecordsFromColumns<T>(\n rows: T[],\n columns: MatrxColumnDef<T>[],\n): Array<Record<string, unknown>> {\n const cols = columns.filter((c) => c.filter !== false || c.accessorKey);\n const headers = cols.map((c) =>\n typeof c.header === \"string\" ? c.header : columnId(c),\n );\n return rows.map((row) => {\n const record: Record<string, unknown> = {};\n cols.forEach((c, index) => {\n record[headers[index] ?? columnId(c)] = stringifyCellValue(getCellValue(row, c));\n });\n return record;\n });\n}\n\nexport function buildViewHuman<T>(\n config: MatrxDataTableCopyConfig<T>,\n visible: T[],\n columns: MatrxColumnDef<T>[],\n): string {\n const md = rowsToMarkdownTable(visible, columns);\n const summaries = visible.map((r) => config.humanRow(r)).join(\"\\n\\n\");\n return [\n `${config.listLabel ?? config.label} (${visible.length} rows)`,\n \"\",\n md,\n \"\",\n \"---\",\n \"\",\n summaries,\n ].join(\"\\n\");\n}\n","import type {\n ColumnFilterValue,\n ColumnFiltersState,\n MatrxColumnDef,\n SortState,\n TableSearchMatchMode,\n} from \"./types\";\nimport {\n completeLayeredFilterRules,\n layeredFilterMatchesValue,\n type LayeredFilterRule,\n} from \"./layered-filters\";\n\n/**\n * Sentinel select-filter values for \"no value\" / \"any value\" — every select\n * filter offers them automatically when the column has empty cells, so any\n * table can isolate rows missing a value (or having one) without extra config.\n */\nexport const SELECT_EMPTY_VALUE = \"__empty__\";\nexport const SELECT_NOT_EMPTY_VALUE = \"__not_empty__\";\n\nexport function columnId<T>(col: MatrxColumnDef<T>): string {\n if (col.id) return col.id;\n if (col.accessorKey) return String(col.accessorKey);\n throw new Error(\"MatrxDataTable column requires `id` or `accessorKey`\");\n}\n\nexport function getCellValue<T>(row: T, col: MatrxColumnDef<T>): unknown {\n if (col.accessorFn) return col.accessorFn(row);\n if (col.accessorKey) {\n return (row as Record<string, unknown>)[col.accessorKey];\n }\n return undefined;\n}\n\nexport function getFilterValue<T>(row: T, col: MatrxColumnDef<T>): unknown {\n return col.filterValue ? col.filterValue(row) : getCellValue(row, col);\n}\n\nexport function getSortValue<T>(row: T, col: MatrxColumnDef<T>): unknown {\n return col.sortValue ? col.sortValue(row) : getCellValue(row, col);\n}\n\nexport function stringifyCellValue(value: unknown): string {\n if (value == null) return \"\";\n if (typeof value === \"string\") return value;\n if (typeof value === \"number\" || typeof value === \"boolean\") {\n return String(value);\n }\n if (value instanceof Date) return value.toISOString();\n try {\n return JSON.stringify(value);\n } catch {\n return String(value);\n }\n}\n\n/** Unicode-aware tokenization shared by whole-word table search. */\nexport function tableSearchWords(value: string): string[] {\n return value\n .toLocaleLowerCase()\n .split(/[^\\p{L}\\p{N}]+/u)\n .filter(Boolean);\n}\n\n/**\n * Match the primary table search against one row's searchable text. In\n * whole-word mode every entered word must exist as a complete token, so\n * `are` matches `we are ready` but never `hardware`.\n */\nexport function matchesTableSearch(\n searchableText: string,\n query: string,\n mode: TableSearchMatchMode = \"contains\",\n): boolean {\n const normalizedQuery = query.trim().toLocaleLowerCase();\n if (!normalizedQuery) return true;\n if (mode === \"contains\") {\n return searchableText.toLocaleLowerCase().includes(normalizedQuery);\n }\n const actualWords = new Set(tableSearchWords(searchableText));\n const expectedWords = tableSearchWords(normalizedQuery);\n return (\n expectedWords.length > 0 &&\n expectedWords.every((word) => actualWords.has(word))\n );\n}\n\nfunction compareValues(a: unknown, b: unknown): number {\n if (a == null && b == null) return 0;\n if (a == null) return -1;\n if (b == null) return 1;\n if (typeof a === \"number\" && typeof b === \"number\") return a - b;\n if (typeof a === \"boolean\" && typeof b === \"boolean\") {\n return Number(a) - Number(b);\n }\n return stringifyCellValue(a).localeCompare(stringifyCellValue(b), undefined, {\n numeric: true,\n sensitivity: \"base\",\n });\n}\n\nexport function passesColumnFilter(\n value: unknown,\n filter: ColumnFilterValue,\n): boolean {\n const matches = (() => {\n switch (filter.kind) {\n case \"text\": {\n const s = stringifyCellValue(value);\n if (filter.mode === \"null\") return value == null;\n if (filter.mode === \"not_null\") return value != null;\n if (filter.mode === \"empty\") return s.trim() === \"\";\n if (filter.mode === \"not_empty\") return s.trim() !== \"\";\n const q = filter.value.trim().toLowerCase();\n if (!q) return true;\n return s.toLowerCase().includes(q);\n }\n case \"select\": {\n const selected =\n filter.values ??\n (filter.value && filter.value !== \"__all__\" ? [filter.value] : []);\n if (selected.length === 0) return true;\n const s = stringifyCellValue(value);\n // OR semantics across the set; sentinels compose (\"A or (empty)\").\n return selected.some((v) =>\n v === SELECT_EMPTY_VALUE\n ? s === \"\"\n : v === SELECT_NOT_EMPTY_VALUE\n ? s !== \"\"\n : s === v,\n );\n }\n case \"boolean\": {\n return Boolean(value) === filter.value;\n }\n case \"number\": {\n const n =\n typeof value === \"number\"\n ? value\n : Number(stringifyCellValue(value).replace(/[^0-9.-]/g, \"\"));\n if (Number.isNaN(n)) return false;\n if (filter.min !== undefined && n < filter.min) return false;\n if (filter.max !== undefined && n > filter.max) return false;\n return true;\n }\n }\n })();\n return filter.negated ? !matches : matches;\n}\n\nexport function filterAndSortRows<T>(\n rows: T[],\n columns: MatrxColumnDef<T>[],\n columnFilters: ColumnFiltersState,\n sort: SortState | null,\n globalSearch: string,\n anyOf?: { columnIds: string[]; query: string },\n layeredFilters?: readonly LayeredFilterRule[],\n globalSearchMode: TableSearchMatchMode = \"contains\",\n searchText?: (row: T) => string,\n): T[] {\n const colById = new Map(columns.map((c) => [columnId(c), c]));\n const q = globalSearch.trim().toLowerCase();\n const anyQ = anyOf?.query.trim().toLowerCase() ?? \"\";\n const activeLayeredFilters = completeLayeredFilterRules(layeredFilters);\n\n let result = rows.filter((row) => {\n for (const [id, filter] of Object.entries(columnFilters)) {\n if (!filter) continue;\n const col = colById.get(id);\n if (!col || col.filter === false) continue;\n if (!passesColumnFilter(getFilterValue(row, col), filter)) return false;\n }\n\n for (const rule of activeLayeredFilters) {\n const col = colById.get(rule.field);\n if (!col || !layeredFilterMatchesValue(getFilterValue(row, col), rule)) {\n return false;\n }\n }\n\n if (anyQ && anyOf) {\n const hit = anyOf.columnIds.some((id) => {\n const col = colById.get(id);\n if (!col) return false;\n return stringifyCellValue(getFilterValue(row, col))\n .toLowerCase()\n .includes(anyQ);\n });\n if (!hit) return false;\n }\n\n if (!q) return true;\n const searchableValues = columns\n .filter(\n (col) =>\n col.filter !== false || Boolean(col.accessorKey || col.accessorFn),\n )\n .map((col) => stringifyCellValue(getFilterValue(row, col)));\n const additionalSearchText = searchText?.(row);\n if (additionalSearchText) searchableValues.push(additionalSearchText);\n if (globalSearchMode === \"contains\") {\n return searchableValues.some((value) =>\n matchesTableSearch(value, q, \"contains\"),\n );\n }\n return matchesTableSearch(searchableValues.join(\" \"), q, \"whole_words\");\n });\n\n if (sort) {\n const col = colById.get(sort.id);\n if (col && col.sortable !== false) {\n const dir = sort.direction === \"asc\" ? 1 : -1;\n result = [...result].sort((a, b) => {\n const va = getSortValue(a, col);\n const vb = getSortValue(b, col);\n // Empty cells (null / \"\") sort LAST in both directions — sorting a\n // column should surface its values, never a wall of blanks.\n const ea = va == null || stringifyCellValue(va) === \"\";\n const eb = vb == null || stringifyCellValue(vb) === \"\";\n if (ea !== eb) return ea ? 1 : -1;\n return compareValues(va, vb) * dir;\n });\n }\n }\n\n return result;\n}\n\nexport function countActiveColumnFilters(filters: ColumnFiltersState): number {\n let n = 0;\n for (const f of Object.values(filters)) {\n if (!f) continue;\n switch (f.kind) {\n case \"text\":\n if (f.value.trim() || (f.mode && f.mode !== \"contains\")) n += 1;\n break;\n case \"select\":\n if (f.values ? f.values.length > 0 : f.value && f.value !== \"__all__\")\n n += 1;\n break;\n case \"boolean\":\n n += 1;\n break;\n case \"number\":\n if (f.min !== undefined || f.max !== undefined) n += 1;\n break;\n }\n }\n return n;\n}\n\nexport function isColumnFilterActive(\n filter: ColumnFilterValue | undefined,\n): boolean {\n if (!filter) return false;\n switch (filter.kind) {\n case \"text\":\n return (\n filter.value.trim().length > 0 ||\n (filter.mode !== undefined && filter.mode !== \"contains\")\n );\n case \"select\":\n return filter.values\n ? filter.values.length > 0\n : Boolean(filter.value) && filter.value !== \"__all__\";\n case \"boolean\":\n return true;\n case \"number\":\n return filter.min !== undefined || filter.max !== undefined;\n }\n}\n\n/** Apply draft edits onto a row for display (shallow field merge). */\nexport function applyRowEdits<T>(\n row: T,\n edits: Record<string, unknown> | undefined,\n): T {\n if (!edits || Object.keys(edits).length === 0) return row;\n return { ...row, ...edits };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACqBO,SAAS,SAAY,KAAgC;AAC1D,MAAI,IAAI,GAAI,QAAO,IAAI;AACvB,MAAI,IAAI,YAAa,QAAO,OAAO,IAAI,WAAW;AAClD,QAAM,IAAI,MAAM,sDAAsD;AACxE;AAEO,SAAS,aAAgB,KAAQ,KAAiC;AACvE,MAAI,IAAI,WAAY,QAAO,IAAI,WAAW,GAAG;AAC7C,MAAI,IAAI,aAAa;AACnB,WAAQ,IAAgC,IAAI,WAAW;AAAA,EACzD;AACA,SAAO;AACT;AAUO,SAAS,mBAAmB,OAAwB;AACzD,MAAI,SAAS,KAAM,QAAO;AAC1B,MAAI,OAAO,UAAU,SAAU,QAAO;AACtC,MAAI,OAAO,UAAU,YAAY,OAAO,UAAU,WAAW;AAC3D,WAAO,OAAO,KAAK;AAAA,EACrB;AACA,MAAI,iBAAiB,KAAM,QAAO,MAAM,YAAY;AACpD,MAAI;AACF,WAAO,KAAK,UAAU,KAAK;AAAA,EAC7B,QAAQ;AACN,WAAO,OAAO,KAAK;AAAA,EACrB;AACF;;;ADlDO,SAAS,oBACd,MACA,SACQ;AACR,QAAM,OAAO,QAAQ,OAAO,CAAC,MAAM,EAAE,WAAW,SAAS,EAAE,WAAW;AACtE,MAAI,KAAK,WAAW,EAAG,QAAO;AAC9B,QAAM,UAAU,KAAK,IAAI,CAAC,MAAM;AAC9B,QAAI,OAAO,EAAE,WAAW,SAAU,QAAO,EAAE;AAC3C,WAAO,SAAS,CAAC;AAAA,EACnB,CAAC;AACD,QAAM,QAAQ;AAAA,IACZ,KAAK,QAAQ,KAAK,KAAK,CAAC;AAAA,IACxB,KAAK,QAAQ,IAAI,MAAM,KAAK,EAAE,KAAK,KAAK,CAAC;AAAA,EAC3C;AACA,aAAW,OAAO,MAAM;AACtB,UAAM,QAAQ,KAAK;AAAA,MAAI,CAAC,MACtB,mBAAmB,aAAa,KAAK,CAAC,CAAC,EAAE,QAAQ,OAAO,KAAK;AAAA,IAC/D;AACA,UAAM,KAAK,KAAK,MAAM,KAAK,KAAK,CAAC,IAAI;AAAA,EACvC;AACA,SAAO,MAAM,KAAK,IAAI;AACxB;AAEO,SAAS,mBACd,QACA,KACmB;AACnB,SAAO;AAAA,IACL,MAAM,OAAO;AAAA,IACb,UAAU,OAAO;AAAA,IACjB,aAAa,OAAO,kBAAkB,OAAO,OAAO,KAAK;AAAA,IACzD,MAAM,OAAO,WAAW,OAAO,SAAS,GAAG,IAAI;AAAA,IAC/C,SAAS,OAAO,SAAS,GAAG;AAAA,IAC5B,GAAI,OAAO,gBAAgB,EAAE,YAAY,OAAO,cAAc,GAAG,EAAE,IAAI,CAAC;AAAA,EAC1E;AACF;AAEO,SAAS,oBACd,QACA,SACA,KACA,MASmB;AACnB,SAAO;AAAA,IACL,MAAM,OAAO;AAAA,IACb,UAAU,OAAO;AAAA,IACjB,aACE,MAAM,UAAU,aACZ,GAAG,QAAQ,MAAM,aAAa,QAAQ,WAAW,IAAI,OAAO,QAAS,OAAO,aAAa,GAAG,OAAO,KAAK,OAAQ,MAC/G,OAAO,mBACR,GAAG,OAAO,aAAa,OAAO,KAAK;AAAA,IACzC,MAAM,QAAQ,IAAI,CAAC,MAAO,OAAO,WAAW,OAAO,SAAS,CAAC,IAAI,CAAE;AAAA,IACnE,SAAS,QAAQ,IAAI,CAAC,MAAM,OAAO,SAAS,CAAC,CAAC,EAAE,KAAK,SAAS;AAAA,IAC9D,YAAY;AAAA,MACV,eAAe,QAAQ;AAAA,MACvB,aAAa,IAAI;AAAA,MACjB,QAAQ,MAAM,UAAU;AAAA,MACxB,cAAc,MAAM,SAAS,KAAK,kBAAkB;AAAA,MACpD,QAAQ,MAAM,SAAS;AAAA,MACvB,gBAAgB,MAAM,eAAe;AAAA,MACrC,MAAM,MAAM,QAAQ;AAAA,MACpB,OAAO,MAAM,SAAS;AAAA,MACtB,GAAG,OAAO,iBAAiB,SAAS,GAAG;AAAA,IACzC;AAAA,IACA,GAAI,OAAO,cAAc,EAAE,SAAS,OAAO,YAAY,SAAS,GAAG,EAAE,IAAI,CAAC;AAAA,EAC5E;AACF;AAGO,SAAS,qBACd,MACA,SACQ;AACR,QAAM,OAAO,QAAQ,OAAO,CAAC,MAAM,EAAE,WAAW,SAAS,EAAE,WAAW;AACtE,QAAM,SAAS,CAAC,UACd,WAAW,KAAK,KAAK,IAAI,IAAI,MAAM,QAAQ,MAAM,IAAI,CAAC,MAAM;AAC9D,QAAM,QAAQ;AAAA,IACZ,KACG,IAAI,CAAC,MAAM,OAAO,OAAO,EAAE,WAAW,WAAW,EAAE,SAAS,SAAS,CAAC,CAAC,CAAC,EACxE,KAAK,GAAG;AAAA,EACb;AACA,aAAW,OAAO,MAAM;AACtB,UAAM;AAAA,MACJ,KACG,IAAI,CAAC,MAAM,OAAO,mBAAmB,aAAa,KAAK,CAAC,CAAC,CAAC,CAAC,EAC3D,KAAK,GAAG;AAAA,IACb;AAAA,EACF;AACA,SAAO,MAAM,KAAK,IAAI;AACxB;AAWO,SAAS,yBACd,MACA,SACgC;AAChC,QAAM,OAAO,QAAQ,OAAO,CAAC,MAAM,EAAE,WAAW,SAAS,EAAE,WAAW;AACtE,QAAM,UAAU,KAAK;AAAA,IAAI,CAAC,MACxB,OAAO,EAAE,WAAW,WAAW,EAAE,SAAS,SAAS,CAAC;AAAA,EACtD;AACA,SAAO,KAAK,IAAI,CAAC,QAAQ;AACvB,UAAM,SAAkC,CAAC;AACzC,SAAK,QAAQ,CAAC,GAAG,UAAU;AACzB,aAAO,QAAQ,KAAK,KAAK,SAAS,CAAC,CAAC,IAAI,mBAAmB,aAAa,KAAK,CAAC,CAAC;AAAA,IACjF,CAAC;AACD,WAAO;AAAA,EACT,CAAC;AACH;AAEO,SAAS,eACd,QACA,SACA,SACQ;AACR,QAAM,KAAK,oBAAoB,SAAS,OAAO;AAC/C,QAAM,YAAY,QAAQ,IAAI,CAAC,MAAM,OAAO,SAAS,CAAC,CAAC,EAAE,KAAK,MAAM;AACpE,SAAO;AAAA,IACL,GAAG,OAAO,aAAa,OAAO,KAAK,KAAK,QAAQ,MAAM;AAAA,IACtD;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF,EAAE,KAAK,IAAI;AACb;","names":[]}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { AgentPayloadInput } from './copy-types.cjs';
|
|
2
|
+
import { f as MatrxDataTableCopyConfig, M as MatrxColumnDef } from '../types-XXbVB1sh.cjs';
|
|
3
|
+
import 'react';
|
|
4
|
+
|
|
5
|
+
/** Human markdown table of the current view (visible columns × rows). */
|
|
6
|
+
declare function rowsToMarkdownTable<T>(rows: T[], columns: MatrxColumnDef<T>[]): string;
|
|
7
|
+
declare function buildRowAgentInput<T>(config: MatrxDataTableCopyConfig<T>, row: T): AgentPayloadInput;
|
|
8
|
+
declare function buildViewAgentInput<T>(config: MatrxDataTableCopyConfig<T>, visible: T[], all: T[], meta?: {
|
|
9
|
+
search?: string;
|
|
10
|
+
searchMatchMode?: string;
|
|
11
|
+
anyOf?: string;
|
|
12
|
+
filterCount?: number;
|
|
13
|
+
sort?: string | null;
|
|
14
|
+
/** `custom` = shaped in the copy-subset window (filter & sort before copying). */
|
|
15
|
+
scope?: "view" | "selected" | "custom";
|
|
16
|
+
}): AgentPayloadInput;
|
|
17
|
+
/** CSV of the current view (visible columns × rows), for ExportMenu. */
|
|
18
|
+
declare function rowsToCsvFromColumns<T>(rows: T[], columns: MatrxColumnDef<T>[]): string;
|
|
19
|
+
/**
|
|
20
|
+
* The same rows the CSV export produces, as records — the shape a Google Sheet
|
|
21
|
+
* (or any other row destination) takes.
|
|
22
|
+
*
|
|
23
|
+
* It shares `rowsToCsvFromColumns`'s column selection and cell stringification
|
|
24
|
+
* ON PURPOSE: a user who exports the view as CSV and a user who sends it to a
|
|
25
|
+
* Sheet must get the SAME columns, in the same order, with the same values.
|
|
26
|
+
* Two independent row builders is how those two answers start disagreeing.
|
|
27
|
+
*/
|
|
28
|
+
declare function rowsToRecordsFromColumns<T>(rows: T[], columns: MatrxColumnDef<T>[]): Array<Record<string, unknown>>;
|
|
29
|
+
declare function buildViewHuman<T>(config: MatrxDataTableCopyConfig<T>, visible: T[], columns: MatrxColumnDef<T>[]): string;
|
|
30
|
+
|
|
31
|
+
export { buildRowAgentInput, buildViewAgentInput, buildViewHuman, rowsToCsvFromColumns, rowsToMarkdownTable, rowsToRecordsFromColumns };
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { AgentPayloadInput } from './copy-types.js';
|
|
2
|
+
import { f as MatrxDataTableCopyConfig, M as MatrxColumnDef } from '../types-ClaxTaXq.js';
|
|
3
|
+
import 'react';
|
|
4
|
+
|
|
5
|
+
/** Human markdown table of the current view (visible columns × rows). */
|
|
6
|
+
declare function rowsToMarkdownTable<T>(rows: T[], columns: MatrxColumnDef<T>[]): string;
|
|
7
|
+
declare function buildRowAgentInput<T>(config: MatrxDataTableCopyConfig<T>, row: T): AgentPayloadInput;
|
|
8
|
+
declare function buildViewAgentInput<T>(config: MatrxDataTableCopyConfig<T>, visible: T[], all: T[], meta?: {
|
|
9
|
+
search?: string;
|
|
10
|
+
searchMatchMode?: string;
|
|
11
|
+
anyOf?: string;
|
|
12
|
+
filterCount?: number;
|
|
13
|
+
sort?: string | null;
|
|
14
|
+
/** `custom` = shaped in the copy-subset window (filter & sort before copying). */
|
|
15
|
+
scope?: "view" | "selected" | "custom";
|
|
16
|
+
}): AgentPayloadInput;
|
|
17
|
+
/** CSV of the current view (visible columns × rows), for ExportMenu. */
|
|
18
|
+
declare function rowsToCsvFromColumns<T>(rows: T[], columns: MatrxColumnDef<T>[]): string;
|
|
19
|
+
/**
|
|
20
|
+
* The same rows the CSV export produces, as records — the shape a Google Sheet
|
|
21
|
+
* (or any other row destination) takes.
|
|
22
|
+
*
|
|
23
|
+
* It shares `rowsToCsvFromColumns`'s column selection and cell stringification
|
|
24
|
+
* ON PURPOSE: a user who exports the view as CSV and a user who sends it to a
|
|
25
|
+
* Sheet must get the SAME columns, in the same order, with the same values.
|
|
26
|
+
* Two independent row builders is how those two answers start disagreeing.
|
|
27
|
+
*/
|
|
28
|
+
declare function rowsToRecordsFromColumns<T>(rows: T[], columns: MatrxColumnDef<T>[]): Array<Record<string, unknown>>;
|
|
29
|
+
declare function buildViewHuman<T>(config: MatrxDataTableCopyConfig<T>, visible: T[], columns: MatrxColumnDef<T>[]): string;
|
|
30
|
+
|
|
31
|
+
export { buildRowAgentInput, buildViewAgentInput, buildViewHuman, rowsToCsvFromColumns, rowsToMarkdownTable, rowsToRecordsFromColumns };
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
|
|
2
|
+
// src/data-table/filter-engine.ts
|
|
3
|
+
function columnId(col) {
|
|
4
|
+
if (col.id) return col.id;
|
|
5
|
+
if (col.accessorKey) return String(col.accessorKey);
|
|
6
|
+
throw new Error("MatrxDataTable column requires `id` or `accessorKey`");
|
|
7
|
+
}
|
|
8
|
+
function getCellValue(row, col) {
|
|
9
|
+
if (col.accessorFn) return col.accessorFn(row);
|
|
10
|
+
if (col.accessorKey) {
|
|
11
|
+
return row[col.accessorKey];
|
|
12
|
+
}
|
|
13
|
+
return void 0;
|
|
14
|
+
}
|
|
15
|
+
function stringifyCellValue(value) {
|
|
16
|
+
if (value == null) return "";
|
|
17
|
+
if (typeof value === "string") return value;
|
|
18
|
+
if (typeof value === "number" || typeof value === "boolean") {
|
|
19
|
+
return String(value);
|
|
20
|
+
}
|
|
21
|
+
if (value instanceof Date) return value.toISOString();
|
|
22
|
+
try {
|
|
23
|
+
return JSON.stringify(value);
|
|
24
|
+
} catch {
|
|
25
|
+
return String(value);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// src/data-table/tableCopy.ts
|
|
30
|
+
function rowsToMarkdownTable(rows, columns) {
|
|
31
|
+
const cols = columns.filter((c) => c.filter !== false || c.accessorKey);
|
|
32
|
+
if (cols.length === 0) return "";
|
|
33
|
+
const headers = cols.map((c) => {
|
|
34
|
+
if (typeof c.header === "string") return c.header;
|
|
35
|
+
return columnId(c);
|
|
36
|
+
});
|
|
37
|
+
const lines = [
|
|
38
|
+
`| ${headers.join(" | ")} |`,
|
|
39
|
+
`| ${headers.map(() => "---").join(" | ")} |`
|
|
40
|
+
];
|
|
41
|
+
for (const row of rows) {
|
|
42
|
+
const cells = cols.map(
|
|
43
|
+
(c) => stringifyCellValue(getCellValue(row, c)).replace(/\|/g, "\\|")
|
|
44
|
+
);
|
|
45
|
+
lines.push(`| ${cells.join(" | ")} |`);
|
|
46
|
+
}
|
|
47
|
+
return lines.join("\n");
|
|
48
|
+
}
|
|
49
|
+
function buildRowAgentInput(config, row) {
|
|
50
|
+
return {
|
|
51
|
+
kind: config.rowKind,
|
|
52
|
+
location: config.location,
|
|
53
|
+
description: config.rowDescription ?? `One ${config.label} row.`,
|
|
54
|
+
data: config.agentRow ? config.agentRow(row) : row,
|
|
55
|
+
summary: config.humanRow(row),
|
|
56
|
+
...config.rowAttributes ? { attributes: config.rowAttributes(row) } : {}
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
function buildViewAgentInput(config, visible, all, meta) {
|
|
60
|
+
return {
|
|
61
|
+
kind: config.listKind,
|
|
62
|
+
location: config.location,
|
|
63
|
+
description: meta?.scope === "selected" ? `${visible.length} selected ${visible.length === 1 ? config.label : config.listLabel ?? `${config.label} rows`}.` : config.listDescription ?? `${config.listLabel ?? config.label} \u2014 current filtered/sorted view.`,
|
|
64
|
+
data: visible.map((r) => config.agentRow ? config.agentRow(r) : r),
|
|
65
|
+
summary: visible.map((r) => config.humanRow(r)).join("\n---\n"),
|
|
66
|
+
attributes: {
|
|
67
|
+
visible_count: visible.length,
|
|
68
|
+
total_count: all.length,
|
|
69
|
+
search: meta?.search || void 0,
|
|
70
|
+
search_match: meta?.search ? meta.searchMatchMode : void 0,
|
|
71
|
+
any_of: meta?.anyOf || void 0,
|
|
72
|
+
active_filters: meta?.filterCount ?? 0,
|
|
73
|
+
sort: meta?.sort || void 0,
|
|
74
|
+
scope: meta?.scope ?? "view",
|
|
75
|
+
...config.listAttributes?.(visible, all)
|
|
76
|
+
},
|
|
77
|
+
...config.listContext ? { context: config.listContext(visible, all) } : {}
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
function rowsToCsvFromColumns(rows, columns) {
|
|
81
|
+
const cols = columns.filter((c) => c.filter !== false || c.accessorKey);
|
|
82
|
+
const escape = (value) => /[",\n\r]/.test(value) ? `"${value.replace(/"/g, '""')}"` : value;
|
|
83
|
+
const lines = [
|
|
84
|
+
cols.map((c) => escape(typeof c.header === "string" ? c.header : columnId(c))).join(",")
|
|
85
|
+
];
|
|
86
|
+
for (const row of rows) {
|
|
87
|
+
lines.push(
|
|
88
|
+
cols.map((c) => escape(stringifyCellValue(getCellValue(row, c)))).join(",")
|
|
89
|
+
);
|
|
90
|
+
}
|
|
91
|
+
return lines.join("\n");
|
|
92
|
+
}
|
|
93
|
+
function rowsToRecordsFromColumns(rows, columns) {
|
|
94
|
+
const cols = columns.filter((c) => c.filter !== false || c.accessorKey);
|
|
95
|
+
const headers = cols.map(
|
|
96
|
+
(c) => typeof c.header === "string" ? c.header : columnId(c)
|
|
97
|
+
);
|
|
98
|
+
return rows.map((row) => {
|
|
99
|
+
const record = {};
|
|
100
|
+
cols.forEach((c, index) => {
|
|
101
|
+
record[headers[index] ?? columnId(c)] = stringifyCellValue(getCellValue(row, c));
|
|
102
|
+
});
|
|
103
|
+
return record;
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
function buildViewHuman(config, visible, columns) {
|
|
107
|
+
const md = rowsToMarkdownTable(visible, columns);
|
|
108
|
+
const summaries = visible.map((r) => config.humanRow(r)).join("\n\n");
|
|
109
|
+
return [
|
|
110
|
+
`${config.listLabel ?? config.label} (${visible.length} rows)`,
|
|
111
|
+
"",
|
|
112
|
+
md,
|
|
113
|
+
"",
|
|
114
|
+
"---",
|
|
115
|
+
"",
|
|
116
|
+
summaries
|
|
117
|
+
].join("\n");
|
|
118
|
+
}
|
|
119
|
+
export {
|
|
120
|
+
buildRowAgentInput,
|
|
121
|
+
buildViewAgentInput,
|
|
122
|
+
buildViewHuman,
|
|
123
|
+
rowsToCsvFromColumns,
|
|
124
|
+
rowsToMarkdownTable,
|
|
125
|
+
rowsToRecordsFromColumns
|
|
126
|
+
};
|
|
127
|
+
//# sourceMappingURL=copy-helpers.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/data-table/filter-engine.ts","../../src/data-table/tableCopy.ts"],"sourcesContent":["import type {\n ColumnFilterValue,\n ColumnFiltersState,\n MatrxColumnDef,\n SortState,\n TableSearchMatchMode,\n} from \"./types\";\nimport {\n completeLayeredFilterRules,\n layeredFilterMatchesValue,\n type LayeredFilterRule,\n} from \"./layered-filters\";\n\n/**\n * Sentinel select-filter values for \"no value\" / \"any value\" — every select\n * filter offers them automatically when the column has empty cells, so any\n * table can isolate rows missing a value (or having one) without extra config.\n */\nexport const SELECT_EMPTY_VALUE = \"__empty__\";\nexport const SELECT_NOT_EMPTY_VALUE = \"__not_empty__\";\n\nexport function columnId<T>(col: MatrxColumnDef<T>): string {\n if (col.id) return col.id;\n if (col.accessorKey) return String(col.accessorKey);\n throw new Error(\"MatrxDataTable column requires `id` or `accessorKey`\");\n}\n\nexport function getCellValue<T>(row: T, col: MatrxColumnDef<T>): unknown {\n if (col.accessorFn) return col.accessorFn(row);\n if (col.accessorKey) {\n return (row as Record<string, unknown>)[col.accessorKey];\n }\n return undefined;\n}\n\nexport function getFilterValue<T>(row: T, col: MatrxColumnDef<T>): unknown {\n return col.filterValue ? col.filterValue(row) : getCellValue(row, col);\n}\n\nexport function getSortValue<T>(row: T, col: MatrxColumnDef<T>): unknown {\n return col.sortValue ? col.sortValue(row) : getCellValue(row, col);\n}\n\nexport function stringifyCellValue(value: unknown): string {\n if (value == null) return \"\";\n if (typeof value === \"string\") return value;\n if (typeof value === \"number\" || typeof value === \"boolean\") {\n return String(value);\n }\n if (value instanceof Date) return value.toISOString();\n try {\n return JSON.stringify(value);\n } catch {\n return String(value);\n }\n}\n\n/** Unicode-aware tokenization shared by whole-word table search. */\nexport function tableSearchWords(value: string): string[] {\n return value\n .toLocaleLowerCase()\n .split(/[^\\p{L}\\p{N}]+/u)\n .filter(Boolean);\n}\n\n/**\n * Match the primary table search against one row's searchable text. In\n * whole-word mode every entered word must exist as a complete token, so\n * `are` matches `we are ready` but never `hardware`.\n */\nexport function matchesTableSearch(\n searchableText: string,\n query: string,\n mode: TableSearchMatchMode = \"contains\",\n): boolean {\n const normalizedQuery = query.trim().toLocaleLowerCase();\n if (!normalizedQuery) return true;\n if (mode === \"contains\") {\n return searchableText.toLocaleLowerCase().includes(normalizedQuery);\n }\n const actualWords = new Set(tableSearchWords(searchableText));\n const expectedWords = tableSearchWords(normalizedQuery);\n return (\n expectedWords.length > 0 &&\n expectedWords.every((word) => actualWords.has(word))\n );\n}\n\nfunction compareValues(a: unknown, b: unknown): number {\n if (a == null && b == null) return 0;\n if (a == null) return -1;\n if (b == null) return 1;\n if (typeof a === \"number\" && typeof b === \"number\") return a - b;\n if (typeof a === \"boolean\" && typeof b === \"boolean\") {\n return Number(a) - Number(b);\n }\n return stringifyCellValue(a).localeCompare(stringifyCellValue(b), undefined, {\n numeric: true,\n sensitivity: \"base\",\n });\n}\n\nexport function passesColumnFilter(\n value: unknown,\n filter: ColumnFilterValue,\n): boolean {\n const matches = (() => {\n switch (filter.kind) {\n case \"text\": {\n const s = stringifyCellValue(value);\n if (filter.mode === \"null\") return value == null;\n if (filter.mode === \"not_null\") return value != null;\n if (filter.mode === \"empty\") return s.trim() === \"\";\n if (filter.mode === \"not_empty\") return s.trim() !== \"\";\n const q = filter.value.trim().toLowerCase();\n if (!q) return true;\n return s.toLowerCase().includes(q);\n }\n case \"select\": {\n const selected =\n filter.values ??\n (filter.value && filter.value !== \"__all__\" ? [filter.value] : []);\n if (selected.length === 0) return true;\n const s = stringifyCellValue(value);\n // OR semantics across the set; sentinels compose (\"A or (empty)\").\n return selected.some((v) =>\n v === SELECT_EMPTY_VALUE\n ? s === \"\"\n : v === SELECT_NOT_EMPTY_VALUE\n ? s !== \"\"\n : s === v,\n );\n }\n case \"boolean\": {\n return Boolean(value) === filter.value;\n }\n case \"number\": {\n const n =\n typeof value === \"number\"\n ? value\n : Number(stringifyCellValue(value).replace(/[^0-9.-]/g, \"\"));\n if (Number.isNaN(n)) return false;\n if (filter.min !== undefined && n < filter.min) return false;\n if (filter.max !== undefined && n > filter.max) return false;\n return true;\n }\n }\n })();\n return filter.negated ? !matches : matches;\n}\n\nexport function filterAndSortRows<T>(\n rows: T[],\n columns: MatrxColumnDef<T>[],\n columnFilters: ColumnFiltersState,\n sort: SortState | null,\n globalSearch: string,\n anyOf?: { columnIds: string[]; query: string },\n layeredFilters?: readonly LayeredFilterRule[],\n globalSearchMode: TableSearchMatchMode = \"contains\",\n searchText?: (row: T) => string,\n): T[] {\n const colById = new Map(columns.map((c) => [columnId(c), c]));\n const q = globalSearch.trim().toLowerCase();\n const anyQ = anyOf?.query.trim().toLowerCase() ?? \"\";\n const activeLayeredFilters = completeLayeredFilterRules(layeredFilters);\n\n let result = rows.filter((row) => {\n for (const [id, filter] of Object.entries(columnFilters)) {\n if (!filter) continue;\n const col = colById.get(id);\n if (!col || col.filter === false) continue;\n if (!passesColumnFilter(getFilterValue(row, col), filter)) return false;\n }\n\n for (const rule of activeLayeredFilters) {\n const col = colById.get(rule.field);\n if (!col || !layeredFilterMatchesValue(getFilterValue(row, col), rule)) {\n return false;\n }\n }\n\n if (anyQ && anyOf) {\n const hit = anyOf.columnIds.some((id) => {\n const col = colById.get(id);\n if (!col) return false;\n return stringifyCellValue(getFilterValue(row, col))\n .toLowerCase()\n .includes(anyQ);\n });\n if (!hit) return false;\n }\n\n if (!q) return true;\n const searchableValues = columns\n .filter(\n (col) =>\n col.filter !== false || Boolean(col.accessorKey || col.accessorFn),\n )\n .map((col) => stringifyCellValue(getFilterValue(row, col)));\n const additionalSearchText = searchText?.(row);\n if (additionalSearchText) searchableValues.push(additionalSearchText);\n if (globalSearchMode === \"contains\") {\n return searchableValues.some((value) =>\n matchesTableSearch(value, q, \"contains\"),\n );\n }\n return matchesTableSearch(searchableValues.join(\" \"), q, \"whole_words\");\n });\n\n if (sort) {\n const col = colById.get(sort.id);\n if (col && col.sortable !== false) {\n const dir = sort.direction === \"asc\" ? 1 : -1;\n result = [...result].sort((a, b) => {\n const va = getSortValue(a, col);\n const vb = getSortValue(b, col);\n // Empty cells (null / \"\") sort LAST in both directions — sorting a\n // column should surface its values, never a wall of blanks.\n const ea = va == null || stringifyCellValue(va) === \"\";\n const eb = vb == null || stringifyCellValue(vb) === \"\";\n if (ea !== eb) return ea ? 1 : -1;\n return compareValues(va, vb) * dir;\n });\n }\n }\n\n return result;\n}\n\nexport function countActiveColumnFilters(filters: ColumnFiltersState): number {\n let n = 0;\n for (const f of Object.values(filters)) {\n if (!f) continue;\n switch (f.kind) {\n case \"text\":\n if (f.value.trim() || (f.mode && f.mode !== \"contains\")) n += 1;\n break;\n case \"select\":\n if (f.values ? f.values.length > 0 : f.value && f.value !== \"__all__\")\n n += 1;\n break;\n case \"boolean\":\n n += 1;\n break;\n case \"number\":\n if (f.min !== undefined || f.max !== undefined) n += 1;\n break;\n }\n }\n return n;\n}\n\nexport function isColumnFilterActive(\n filter: ColumnFilterValue | undefined,\n): boolean {\n if (!filter) return false;\n switch (filter.kind) {\n case \"text\":\n return (\n filter.value.trim().length > 0 ||\n (filter.mode !== undefined && filter.mode !== \"contains\")\n );\n case \"select\":\n return filter.values\n ? filter.values.length > 0\n : Boolean(filter.value) && filter.value !== \"__all__\";\n case \"boolean\":\n return true;\n case \"number\":\n return filter.min !== undefined || filter.max !== undefined;\n }\n}\n\n/** Apply draft edits onto a row for display (shallow field merge). */\nexport function applyRowEdits<T>(\n row: T,\n edits: Record<string, unknown> | undefined,\n): T {\n if (!edits || Object.keys(edits).length === 0) return row;\n return { ...row, ...edits };\n}\n","import type { AgentPayloadInput } from \"./copy\";\nimport type { MatrxColumnDef, MatrxDataTableCopyConfig } from \"./types\";\nimport { columnId, getCellValue, stringifyCellValue } from \"./filter-engine\";\n\n/** Human markdown table of the current view (visible columns × rows). */\nexport function rowsToMarkdownTable<T>(\n rows: T[],\n columns: MatrxColumnDef<T>[],\n): string {\n const cols = columns.filter((c) => c.filter !== false || c.accessorKey);\n if (cols.length === 0) return \"\";\n const headers = cols.map((c) => {\n if (typeof c.header === \"string\") return c.header;\n return columnId(c);\n });\n const lines = [\n `| ${headers.join(\" | \")} |`,\n `| ${headers.map(() => \"---\").join(\" | \")} |`,\n ];\n for (const row of rows) {\n const cells = cols.map((c) =>\n stringifyCellValue(getCellValue(row, c)).replace(/\\|/g, \"\\\\|\"),\n );\n lines.push(`| ${cells.join(\" | \")} |`);\n }\n return lines.join(\"\\n\");\n}\n\nexport function buildRowAgentInput<T>(\n config: MatrxDataTableCopyConfig<T>,\n row: T,\n): AgentPayloadInput {\n return {\n kind: config.rowKind,\n location: config.location,\n description: config.rowDescription ?? `One ${config.label} row.`,\n data: config.agentRow ? config.agentRow(row) : row,\n summary: config.humanRow(row),\n ...(config.rowAttributes ? { attributes: config.rowAttributes(row) } : {}),\n };\n}\n\nexport function buildViewAgentInput<T>(\n config: MatrxDataTableCopyConfig<T>,\n visible: T[],\n all: T[],\n meta?: {\n search?: string;\n searchMatchMode?: string;\n anyOf?: string;\n filterCount?: number;\n sort?: string | null;\n /** `custom` = shaped in the copy-subset window (filter & sort before copying). */\n scope?: \"view\" | \"selected\" | \"custom\";\n },\n): AgentPayloadInput {\n return {\n kind: config.listKind,\n location: config.location,\n description:\n meta?.scope === \"selected\"\n ? `${visible.length} selected ${visible.length === 1 ? config.label : (config.listLabel ?? `${config.label} rows`)}.`\n : (config.listDescription ??\n `${config.listLabel ?? config.label} — current filtered/sorted view.`),\n data: visible.map((r) => (config.agentRow ? config.agentRow(r) : r)),\n summary: visible.map((r) => config.humanRow(r)).join(\"\\n---\\n\"),\n attributes: {\n visible_count: visible.length,\n total_count: all.length,\n search: meta?.search || undefined,\n search_match: meta?.search ? meta.searchMatchMode : undefined,\n any_of: meta?.anyOf || undefined,\n active_filters: meta?.filterCount ?? 0,\n sort: meta?.sort || undefined,\n scope: meta?.scope ?? \"view\",\n ...config.listAttributes?.(visible, all),\n },\n ...(config.listContext ? { context: config.listContext(visible, all) } : {}),\n };\n}\n\n/** CSV of the current view (visible columns × rows), for ExportMenu. */\nexport function rowsToCsvFromColumns<T>(\n rows: T[],\n columns: MatrxColumnDef<T>[],\n): string {\n const cols = columns.filter((c) => c.filter !== false || c.accessorKey);\n const escape = (value: string) =>\n /[\",\\n\\r]/.test(value) ? `\"${value.replace(/\"/g, '\"\"')}\"` : value;\n const lines = [\n cols\n .map((c) => escape(typeof c.header === \"string\" ? c.header : columnId(c)))\n .join(\",\"),\n ];\n for (const row of rows) {\n lines.push(\n cols\n .map((c) => escape(stringifyCellValue(getCellValue(row, c))))\n .join(\",\"),\n );\n }\n return lines.join(\"\\n\");\n}\n\n/**\n * The same rows the CSV export produces, as records — the shape a Google Sheet\n * (or any other row destination) takes.\n *\n * It shares `rowsToCsvFromColumns`'s column selection and cell stringification\n * ON PURPOSE: a user who exports the view as CSV and a user who sends it to a\n * Sheet must get the SAME columns, in the same order, with the same values.\n * Two independent row builders is how those two answers start disagreeing.\n */\nexport function rowsToRecordsFromColumns<T>(\n rows: T[],\n columns: MatrxColumnDef<T>[],\n): Array<Record<string, unknown>> {\n const cols = columns.filter((c) => c.filter !== false || c.accessorKey);\n const headers = cols.map((c) =>\n typeof c.header === \"string\" ? c.header : columnId(c),\n );\n return rows.map((row) => {\n const record: Record<string, unknown> = {};\n cols.forEach((c, index) => {\n record[headers[index] ?? columnId(c)] = stringifyCellValue(getCellValue(row, c));\n });\n return record;\n });\n}\n\nexport function buildViewHuman<T>(\n config: MatrxDataTableCopyConfig<T>,\n visible: T[],\n columns: MatrxColumnDef<T>[],\n): string {\n const md = rowsToMarkdownTable(visible, columns);\n const summaries = visible.map((r) => config.humanRow(r)).join(\"\\n\\n\");\n return [\n `${config.listLabel ?? config.label} (${visible.length} rows)`,\n \"\",\n md,\n \"\",\n \"---\",\n \"\",\n summaries,\n ].join(\"\\n\");\n}\n"],"mappings":";;AAqBO,SAAS,SAAY,KAAgC;AAC1D,MAAI,IAAI,GAAI,QAAO,IAAI;AACvB,MAAI,IAAI,YAAa,QAAO,OAAO,IAAI,WAAW;AAClD,QAAM,IAAI,MAAM,sDAAsD;AACxE;AAEO,SAAS,aAAgB,KAAQ,KAAiC;AACvE,MAAI,IAAI,WAAY,QAAO,IAAI,WAAW,GAAG;AAC7C,MAAI,IAAI,aAAa;AACnB,WAAQ,IAAgC,IAAI,WAAW;AAAA,EACzD;AACA,SAAO;AACT;AAUO,SAAS,mBAAmB,OAAwB;AACzD,MAAI,SAAS,KAAM,QAAO;AAC1B,MAAI,OAAO,UAAU,SAAU,QAAO;AACtC,MAAI,OAAO,UAAU,YAAY,OAAO,UAAU,WAAW;AAC3D,WAAO,OAAO,KAAK;AAAA,EACrB;AACA,MAAI,iBAAiB,KAAM,QAAO,MAAM,YAAY;AACpD,MAAI;AACF,WAAO,KAAK,UAAU,KAAK;AAAA,EAC7B,QAAQ;AACN,WAAO,OAAO,KAAK;AAAA,EACrB;AACF;;;AClDO,SAAS,oBACd,MACA,SACQ;AACR,QAAM,OAAO,QAAQ,OAAO,CAAC,MAAM,EAAE,WAAW,SAAS,EAAE,WAAW;AACtE,MAAI,KAAK,WAAW,EAAG,QAAO;AAC9B,QAAM,UAAU,KAAK,IAAI,CAAC,MAAM;AAC9B,QAAI,OAAO,EAAE,WAAW,SAAU,QAAO,EAAE;AAC3C,WAAO,SAAS,CAAC;AAAA,EACnB,CAAC;AACD,QAAM,QAAQ;AAAA,IACZ,KAAK,QAAQ,KAAK,KAAK,CAAC;AAAA,IACxB,KAAK,QAAQ,IAAI,MAAM,KAAK,EAAE,KAAK,KAAK,CAAC;AAAA,EAC3C;AACA,aAAW,OAAO,MAAM;AACtB,UAAM,QAAQ,KAAK;AAAA,MAAI,CAAC,MACtB,mBAAmB,aAAa,KAAK,CAAC,CAAC,EAAE,QAAQ,OAAO,KAAK;AAAA,IAC/D;AACA,UAAM,KAAK,KAAK,MAAM,KAAK,KAAK,CAAC,IAAI;AAAA,EACvC;AACA,SAAO,MAAM,KAAK,IAAI;AACxB;AAEO,SAAS,mBACd,QACA,KACmB;AACnB,SAAO;AAAA,IACL,MAAM,OAAO;AAAA,IACb,UAAU,OAAO;AAAA,IACjB,aAAa,OAAO,kBAAkB,OAAO,OAAO,KAAK;AAAA,IACzD,MAAM,OAAO,WAAW,OAAO,SAAS,GAAG,IAAI;AAAA,IAC/C,SAAS,OAAO,SAAS,GAAG;AAAA,IAC5B,GAAI,OAAO,gBAAgB,EAAE,YAAY,OAAO,cAAc,GAAG,EAAE,IAAI,CAAC;AAAA,EAC1E;AACF;AAEO,SAAS,oBACd,QACA,SACA,KACA,MASmB;AACnB,SAAO;AAAA,IACL,MAAM,OAAO;AAAA,IACb,UAAU,OAAO;AAAA,IACjB,aACE,MAAM,UAAU,aACZ,GAAG,QAAQ,MAAM,aAAa,QAAQ,WAAW,IAAI,OAAO,QAAS,OAAO,aAAa,GAAG,OAAO,KAAK,OAAQ,MAC/G,OAAO,mBACR,GAAG,OAAO,aAAa,OAAO,KAAK;AAAA,IACzC,MAAM,QAAQ,IAAI,CAAC,MAAO,OAAO,WAAW,OAAO,SAAS,CAAC,IAAI,CAAE;AAAA,IACnE,SAAS,QAAQ,IAAI,CAAC,MAAM,OAAO,SAAS,CAAC,CAAC,EAAE,KAAK,SAAS;AAAA,IAC9D,YAAY;AAAA,MACV,eAAe,QAAQ;AAAA,MACvB,aAAa,IAAI;AAAA,MACjB,QAAQ,MAAM,UAAU;AAAA,MACxB,cAAc,MAAM,SAAS,KAAK,kBAAkB;AAAA,MACpD,QAAQ,MAAM,SAAS;AAAA,MACvB,gBAAgB,MAAM,eAAe;AAAA,MACrC,MAAM,MAAM,QAAQ;AAAA,MACpB,OAAO,MAAM,SAAS;AAAA,MACtB,GAAG,OAAO,iBAAiB,SAAS,GAAG;AAAA,IACzC;AAAA,IACA,GAAI,OAAO,cAAc,EAAE,SAAS,OAAO,YAAY,SAAS,GAAG,EAAE,IAAI,CAAC;AAAA,EAC5E;AACF;AAGO,SAAS,qBACd,MACA,SACQ;AACR,QAAM,OAAO,QAAQ,OAAO,CAAC,MAAM,EAAE,WAAW,SAAS,EAAE,WAAW;AACtE,QAAM,SAAS,CAAC,UACd,WAAW,KAAK,KAAK,IAAI,IAAI,MAAM,QAAQ,MAAM,IAAI,CAAC,MAAM;AAC9D,QAAM,QAAQ;AAAA,IACZ,KACG,IAAI,CAAC,MAAM,OAAO,OAAO,EAAE,WAAW,WAAW,EAAE,SAAS,SAAS,CAAC,CAAC,CAAC,EACxE,KAAK,GAAG;AAAA,EACb;AACA,aAAW,OAAO,MAAM;AACtB,UAAM;AAAA,MACJ,KACG,IAAI,CAAC,MAAM,OAAO,mBAAmB,aAAa,KAAK,CAAC,CAAC,CAAC,CAAC,EAC3D,KAAK,GAAG;AAAA,IACb;AAAA,EACF;AACA,SAAO,MAAM,KAAK,IAAI;AACxB;AAWO,SAAS,yBACd,MACA,SACgC;AAChC,QAAM,OAAO,QAAQ,OAAO,CAAC,MAAM,EAAE,WAAW,SAAS,EAAE,WAAW;AACtE,QAAM,UAAU,KAAK;AAAA,IAAI,CAAC,MACxB,OAAO,EAAE,WAAW,WAAW,EAAE,SAAS,SAAS,CAAC;AAAA,EACtD;AACA,SAAO,KAAK,IAAI,CAAC,QAAQ;AACvB,UAAM,SAAkC,CAAC;AACzC,SAAK,QAAQ,CAAC,GAAG,UAAU;AACzB,aAAO,QAAQ,KAAK,KAAK,SAAS,CAAC,CAAC,IAAI,mBAAmB,aAAa,KAAK,CAAC,CAAC;AAAA,IACjF,CAAC;AACD,WAAO;AAAA,EACT,CAAC;AACH;AAEO,SAAS,eACd,QACA,SACA,SACQ;AACR,QAAM,KAAK,oBAAoB,SAAS,OAAO;AAC/C,QAAM,YAAY,QAAQ,IAAI,CAAC,MAAM,OAAO,SAAS,CAAC,CAAC,EAAE,KAAK,MAAM;AACpE,SAAO;AAAA,IACL,GAAG,OAAO,aAAa,OAAO,KAAK,KAAK,QAAQ,MAAM;AAAA,IACtD;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF,EAAE,KAAK,IAAI;AACb;","names":[]}
|
|
@@ -0,0 +1,19 @@
|
|
|
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 __copyProps = (to, from, except, desc) => {
|
|
7
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
8
|
+
for (let key of __getOwnPropNames(from))
|
|
9
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
10
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
11
|
+
}
|
|
12
|
+
return to;
|
|
13
|
+
};
|
|
14
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
15
|
+
|
|
16
|
+
// src/data-table/copy-types.ts
|
|
17
|
+
var copy_types_exports = {};
|
|
18
|
+
module.exports = __toCommonJS(copy_types_exports);
|
|
19
|
+
//# sourceMappingURL=copy-types.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/data-table/copy-types.ts"],"sourcesContent":["import type { ComponentType, ReactNode } from \"react\";\n\nexport type CopyResolvable<T> = T | (() => T);\nexport type CopyScalar = string | number | boolean | null | undefined;\nexport interface AgentPayloadInput { kind: string; location: string; description: string; data: unknown; summary?: string; context?: Record<string, CopyScalar>; attributes?: Record<string, CopyScalar>; }\nexport type AiIcon = ComponentType<{ className?: string }>;\nexport interface AiVariant { id: string; label: string; hint?: string; icon?: AiIcon; section?: \"copy\" | \"ai\"; ariaLabel?: string; successMessage?: string; build?: () => AgentPayloadInput | string | Promise<AgentPayloadInput | string>; onSelect?: () => void; modal?: (props: { open: boolean; onOpenChange: (open: boolean) => void }) => ReactNode; }\nexport type AiCustomOption =\n | { kind: \"toggle\"; key: string; label: string; hint?: string; default: boolean }\n | { kind: \"preset\"; key: string; label: string; options: { label: string; value: number }[]; default: number }\n | { kind: \"slider\"; key: string; label: string; hint?: string; stops: number[]; default: number; formatValue: (value: number) => string }\n | { kind: \"number\"; key: string; label: string; hint?: string; min?: number; step?: number; presets?: { label: string; value: number }[]; default: number };\nexport type AiOptionValues = Record<string, boolean | number>;\nexport interface AiCustomSource { label?: string; hint?: string; options: AiCustomOption[]; build: (options: AiOptionValues) => { text: string; meta?: Record<string, string | number | boolean | undefined> }; wrap: (text: string, options: AiOptionValues, meta?: Record<string, string | number | boolean | undefined>) => AgentPayloadInput; dialogTitle?: string; dialogDescription?: string; }\nexport interface ExportItem { id: string; label: string; build?: () => { content: BlobPart; extension: string; mime: string }; onSelect?: () => void | Promise<void>; }\nexport interface CopyExportConfig { items: ExportItem[]; sheetRows?: () => Array<Record<string, unknown>>; }\n/** Exact portable table-copy API. Host adapters import this, never recreate it. */\nexport interface CopyControlProps {\n human?: CopyResolvable<string>;\n agent?: CopyResolvable<AgentPayloadInput | string>;\n json?: CopyResolvable<unknown>;\n label: string;\n size?: \"xs\" | \"icon\" | \"sm\";\n /** Accepted for direct compatibility; package chrome has no application groomer engine. */\n appearance?: \"segmented\" | \"bare\";\n disabled?: boolean;\n stopPropagation?: boolean;\n className?: string;\n /** Legacy source compatibility; controls are already grouped by default. */\n grouped?: boolean;\n aiVariants?: AiVariant[];\n agentVariant?: { id?: string; label?: string; hint?: string; position?: \"first\" | \"last\" };\n aiCustom?: AiCustomSource;\n export?: CopyExportConfig;\n hide?: Array<\"copy\" | \"ai\" | \"export\">;\n}\nexport interface CopySubsetColumn<T> { id: string; header: string; accessorKey?: keyof T & string; accessorFn?: (row: T) => unknown; hidden?: boolean; }\nexport interface CopySubsetSource<T> { label: string; location: string; kind: string; rows: T[]; columns: CopySubsetColumn<T>[]; getRowId: (row: T) => string; serializer?: (rows: T[]) => AgentPayloadInput; }\n"],"mappings":";;;;;;;;;;;;;;;;AAAA;AAAA;","names":[]}
|