@ai-matrx/design-system 0.14.3 → 0.15.2
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 +54 -0
- package/README.md +32 -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 +7198 -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 +7202 -0
- package/dist/data-table/index.js.map +1 -0
- package/dist/data-table/infer-filter.cjs +111 -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 +89 -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 +126 -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 +104 -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 +9 -5
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +9 -5
- 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
|
@@ -0,0 +1,345 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
"use client";
|
|
3
|
+
|
|
4
|
+
// src/data-table/MatrxUuidCell.tsx
|
|
5
|
+
import {
|
|
6
|
+
useEffect,
|
|
7
|
+
useRef,
|
|
8
|
+
useState as useState2
|
|
9
|
+
} from "react";
|
|
10
|
+
|
|
11
|
+
// src/data-table/icons.tsx
|
|
12
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
13
|
+
function Icon({ children, ...props }) {
|
|
14
|
+
return /* @__PURE__ */ jsx("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", "aria-hidden": "true", ...props, children });
|
|
15
|
+
}
|
|
16
|
+
var Check = (p) => /* @__PURE__ */ jsx(Icon, { ...p, children: /* @__PURE__ */ jsx("path", { d: "m5 12 4 4L19 6" }) });
|
|
17
|
+
var PanelRight = (p) => /* @__PURE__ */ jsxs(Icon, { ...p, children: [
|
|
18
|
+
/* @__PURE__ */ jsx("rect", { x: "3", y: "4", width: "18", height: "16", rx: "2" }),
|
|
19
|
+
/* @__PURE__ */ jsx("path", { d: "M15 4v16" })
|
|
20
|
+
] });
|
|
21
|
+
var ExternalLink = (p) => /* @__PURE__ */ jsx(Icon, { ...p, children: /* @__PURE__ */ jsx("path", { d: "M14 4h6v6M20 4l-9 9M19 13v6H5V5h6" }) });
|
|
22
|
+
var Copy = (p) => /* @__PURE__ */ jsxs(Icon, { ...p, children: [
|
|
23
|
+
/* @__PURE__ */ jsx("rect", { x: "9", y: "9", width: "11", height: "11", rx: "2" }),
|
|
24
|
+
/* @__PURE__ */ jsx("path", { d: "M15 9V6a2 2 0 0 0-2-2H6a2 2 0 0 0-2 2v7a2 2 0 0 0 2 2h3" })
|
|
25
|
+
] });
|
|
26
|
+
var Lightbulb = (p) => /* @__PURE__ */ jsx(Icon, { ...p, children: /* @__PURE__ */ jsx("path", { d: "M9 18h6M10 22h4M8 14a6 6 0 1 1 8 0c-1 1-1 2-1 3H9c0-1 0-2-1-3z" }) });
|
|
27
|
+
var ShieldOff = (p) => /* @__PURE__ */ jsx(Icon, { ...p, children: /* @__PURE__ */ jsx("path", { d: "m3 3 18 18M12 3l7 3v5c0 4-3 7-7 9-4-2-7-5-7-9V6l4-2" }) });
|
|
28
|
+
|
|
29
|
+
// src/data-table/useTableNotice.tsx
|
|
30
|
+
import { useState } from "react";
|
|
31
|
+
|
|
32
|
+
// src/data-table/host.tsx
|
|
33
|
+
import { createContext, useContext } from "react";
|
|
34
|
+
import { jsx as jsx2 } from "react/jsx-runtime";
|
|
35
|
+
var HOST_SLOT = /* @__PURE__ */ Symbol.for("ai-matrx.design-system.table-host-context");
|
|
36
|
+
var hostRegistry = globalThis;
|
|
37
|
+
var HostContext = hostRegistry[HOST_SLOT] ??= createContext({});
|
|
38
|
+
function useMatrxDataTableHost() {
|
|
39
|
+
return useContext(HostContext);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// src/data-table/useTableNotice.tsx
|
|
43
|
+
import { jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
44
|
+
function useTableNotice() {
|
|
45
|
+
const host = useMatrxDataTableHost();
|
|
46
|
+
const [message, setMessage] = useState(null);
|
|
47
|
+
const notify = {
|
|
48
|
+
success(text) {
|
|
49
|
+
if (host.notify) host.notify.success(text);
|
|
50
|
+
else setMessage({ text, error: false });
|
|
51
|
+
},
|
|
52
|
+
error(text) {
|
|
53
|
+
if (host.notify) host.notify.error(text);
|
|
54
|
+
else setMessage({ text, error: true });
|
|
55
|
+
}
|
|
56
|
+
};
|
|
57
|
+
const notice = message ? /* @__PURE__ */ jsxs2("span", { role: message.error ? "alert" : "status", className: message.error ? "text-xs text-destructive" : "text-xs text-muted-foreground", children: [
|
|
58
|
+
message.text,
|
|
59
|
+
/* @__PURE__ */ jsx3("button", { type: "button", "aria-label": "Dismiss notification", className: "ml-2 underline", onClick: () => setMessage(null), children: "Dismiss" })
|
|
60
|
+
] }) : null;
|
|
61
|
+
return { notify, notice };
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// src/tooltip.tsx
|
|
65
|
+
import * as TooltipPrimitive from "@radix-ui/react-tooltip";
|
|
66
|
+
import * as React2 from "react";
|
|
67
|
+
|
|
68
|
+
// src/cn.ts
|
|
69
|
+
import { clsx } from "clsx";
|
|
70
|
+
import { twMerge } from "tailwind-merge";
|
|
71
|
+
function cn(...inputs) {
|
|
72
|
+
return twMerge(clsx(inputs));
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// src/motion.ts
|
|
76
|
+
var MOTION_OVERLAY = "matrx-motion-overlay";
|
|
77
|
+
var MOTION_DIALOG = "matrx-motion-dialog";
|
|
78
|
+
var MOTION_BOTTOM_SHEET = "matrx-motion-bottom-sheet";
|
|
79
|
+
var MOTION_POPPER = "matrx-motion-popper";
|
|
80
|
+
var MOTION_SHEET = {
|
|
81
|
+
top: "matrx-motion-sheet-top",
|
|
82
|
+
bottom: "matrx-motion-sheet-bottom",
|
|
83
|
+
left: "matrx-motion-sheet-left",
|
|
84
|
+
right: "matrx-motion-sheet-right",
|
|
85
|
+
center: "matrx-motion-sheet-center"
|
|
86
|
+
};
|
|
87
|
+
var MOTION_PULSE = "matrx-pulse";
|
|
88
|
+
var MOTION_SPIN = "matrx-spin";
|
|
89
|
+
var ALL_MOTION_CLASSES = [
|
|
90
|
+
MOTION_OVERLAY,
|
|
91
|
+
MOTION_DIALOG,
|
|
92
|
+
MOTION_BOTTOM_SHEET,
|
|
93
|
+
MOTION_POPPER,
|
|
94
|
+
...Object.values(MOTION_SHEET),
|
|
95
|
+
MOTION_PULSE,
|
|
96
|
+
MOTION_SPIN
|
|
97
|
+
];
|
|
98
|
+
|
|
99
|
+
// src/portal-container.tsx
|
|
100
|
+
import * as React from "react";
|
|
101
|
+
import { jsx as jsx4 } from "react/jsx-runtime";
|
|
102
|
+
var CONTEXT_SLOT = /* @__PURE__ */ Symbol.for("ai-matrx.design-system.portal-container-context");
|
|
103
|
+
var contextRegistry = globalThis;
|
|
104
|
+
var PortalContainerContext = contextRegistry[CONTEXT_SLOT] ??= React.createContext(void 0);
|
|
105
|
+
function usePortalContainer(explicit) {
|
|
106
|
+
const injected = React.useContext(PortalContainerContext);
|
|
107
|
+
if (explicit !== void 0) {
|
|
108
|
+
return explicit ?? void 0;
|
|
109
|
+
}
|
|
110
|
+
return injected ?? void 0;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// src/tooltip.tsx
|
|
114
|
+
import { jsx as jsx5 } from "react/jsx-runtime";
|
|
115
|
+
var TooltipProvider = TooltipPrimitive.Provider;
|
|
116
|
+
var Tooltip = TooltipPrimitive.Root;
|
|
117
|
+
var TooltipTrigger = TooltipPrimitive.Trigger;
|
|
118
|
+
var TooltipContent = React2.forwardRef(({ className, sideOffset = 4, container, animated = true, ...props }, ref) => {
|
|
119
|
+
const resolved = usePortalContainer(container);
|
|
120
|
+
return /* @__PURE__ */ jsx5(TooltipPrimitive.Portal, { container: resolved ?? null, children: /* @__PURE__ */ jsx5(
|
|
121
|
+
TooltipPrimitive.Content,
|
|
122
|
+
{
|
|
123
|
+
ref,
|
|
124
|
+
"data-slot": "tooltip-content",
|
|
125
|
+
sideOffset,
|
|
126
|
+
className: cn(
|
|
127
|
+
"z-[10001] overflow-hidden rounded-md border border-border bg-popover px-3 py-1.5 text-xs text-popover-foreground shadow-md",
|
|
128
|
+
animated && MOTION_POPPER,
|
|
129
|
+
className
|
|
130
|
+
),
|
|
131
|
+
...props
|
|
132
|
+
}
|
|
133
|
+
) });
|
|
134
|
+
});
|
|
135
|
+
TooltipContent.displayName = TooltipPrimitive.Content.displayName;
|
|
136
|
+
|
|
137
|
+
// src/data-table/MatrxUuidCell.tsx
|
|
138
|
+
import { jsx as jsx6, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
139
|
+
var SHORT_LEN = 8;
|
|
140
|
+
function shortUuid(id, len = SHORT_LEN) {
|
|
141
|
+
return id.slice(0, len);
|
|
142
|
+
}
|
|
143
|
+
async function copyText(text) {
|
|
144
|
+
try {
|
|
145
|
+
if (navigator.clipboard?.writeText) {
|
|
146
|
+
await navigator.clipboard.writeText(text);
|
|
147
|
+
return true;
|
|
148
|
+
}
|
|
149
|
+
} catch {
|
|
150
|
+
}
|
|
151
|
+
try {
|
|
152
|
+
const ta = document.createElement("textarea");
|
|
153
|
+
ta.value = text;
|
|
154
|
+
ta.style.position = "fixed";
|
|
155
|
+
ta.style.opacity = "0";
|
|
156
|
+
document.body.appendChild(ta);
|
|
157
|
+
ta.select();
|
|
158
|
+
const ok = document.execCommand("copy");
|
|
159
|
+
document.body.removeChild(ta);
|
|
160
|
+
return ok;
|
|
161
|
+
} catch {
|
|
162
|
+
return false;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
function MatrxUuidCell({
|
|
166
|
+
value,
|
|
167
|
+
label,
|
|
168
|
+
onOpen,
|
|
169
|
+
href,
|
|
170
|
+
token,
|
|
171
|
+
forbidden = false,
|
|
172
|
+
className,
|
|
173
|
+
trailing
|
|
174
|
+
}) {
|
|
175
|
+
const { resolveEntityDoors, ResourcePeek } = useMatrxDataTableHost();
|
|
176
|
+
const { notify, notice } = useTableNotice();
|
|
177
|
+
const [copied, setCopied] = useState2(false);
|
|
178
|
+
const [opening, setOpening] = useState2(false);
|
|
179
|
+
const [peekOpen, setPeekOpen] = useState2(false);
|
|
180
|
+
const timer = useRef(null);
|
|
181
|
+
useEffect(
|
|
182
|
+
() => () => {
|
|
183
|
+
if (timer.current) clearTimeout(timer.current);
|
|
184
|
+
},
|
|
185
|
+
[]
|
|
186
|
+
);
|
|
187
|
+
if (!value) {
|
|
188
|
+
return /* @__PURE__ */ jsx6("span", { className: "text-muted-foreground/40", children: "\u2014" });
|
|
189
|
+
}
|
|
190
|
+
const doors = token ? resolveEntityDoors?.(token, value, href) ?? null : null;
|
|
191
|
+
const resolvedHref = doors?.href ?? href ?? null;
|
|
192
|
+
const canPeek = Boolean(doors?.canPeek) && !forbidden;
|
|
193
|
+
const display = shortUuid(value);
|
|
194
|
+
const tip = label ? `${label}: ${value}` : value;
|
|
195
|
+
const stop = (e) => {
|
|
196
|
+
e.stopPropagation();
|
|
197
|
+
};
|
|
198
|
+
const onCopy = (e) => {
|
|
199
|
+
stop(e);
|
|
200
|
+
e.preventDefault();
|
|
201
|
+
void copyText(value).then((ok) => {
|
|
202
|
+
if (!ok) {
|
|
203
|
+
notify?.error("Couldn't copy ID");
|
|
204
|
+
return;
|
|
205
|
+
}
|
|
206
|
+
setCopied(true);
|
|
207
|
+
if (timer.current) clearTimeout(timer.current);
|
|
208
|
+
timer.current = setTimeout(() => setCopied(false), 1500);
|
|
209
|
+
});
|
|
210
|
+
};
|
|
211
|
+
const handleOpen = async (e) => {
|
|
212
|
+
stop(e);
|
|
213
|
+
e.preventDefault();
|
|
214
|
+
if (!onOpen || forbidden || opening) return;
|
|
215
|
+
setOpening(true);
|
|
216
|
+
try {
|
|
217
|
+
const result = await onOpen(value);
|
|
218
|
+
if (result === "forbidden") {
|
|
219
|
+
notify?.error("You don't have access to open this record");
|
|
220
|
+
}
|
|
221
|
+
} catch (err) {
|
|
222
|
+
notify?.error(
|
|
223
|
+
`Couldn't open: ${err instanceof Error ? err.message : String(err)}`
|
|
224
|
+
);
|
|
225
|
+
} finally {
|
|
226
|
+
setOpening(false);
|
|
227
|
+
}
|
|
228
|
+
};
|
|
229
|
+
const idNode = (() => {
|
|
230
|
+
if (forbidden) {
|
|
231
|
+
return /* @__PURE__ */ jsxs3("span", { className: "inline-flex min-w-0 items-center gap-1 font-mono text-xs text-muted-foreground/70 tabular-nums", children: [
|
|
232
|
+
/* @__PURE__ */ jsx6("span", { className: "min-w-0 truncate", children: display }),
|
|
233
|
+
/* @__PURE__ */ jsx6(ShieldOff, { className: "h-3 w-3 shrink-0 opacity-60", "aria-hidden": true })
|
|
234
|
+
] });
|
|
235
|
+
}
|
|
236
|
+
if (onOpen) {
|
|
237
|
+
return /* @__PURE__ */ jsxs3(
|
|
238
|
+
"button",
|
|
239
|
+
{
|
|
240
|
+
type: "button",
|
|
241
|
+
"aria-label": `Open ${label ?? "record"} ${display}`,
|
|
242
|
+
onClick: (e) => void handleOpen(e),
|
|
243
|
+
disabled: opening,
|
|
244
|
+
className: "inline-flex min-w-0 items-center gap-1 font-mono text-xs text-sky-600 hover:underline dark:text-sky-400 disabled:opacity-60",
|
|
245
|
+
children: [
|
|
246
|
+
/* @__PURE__ */ jsx6("span", { className: "min-w-0 truncate tabular-nums", children: display }),
|
|
247
|
+
/* @__PURE__ */ jsx6(PanelRight, { className: "h-3 w-3 shrink-0 opacity-50" })
|
|
248
|
+
]
|
|
249
|
+
}
|
|
250
|
+
);
|
|
251
|
+
}
|
|
252
|
+
if (resolvedHref) {
|
|
253
|
+
return /* @__PURE__ */ jsxs3(
|
|
254
|
+
"a",
|
|
255
|
+
{
|
|
256
|
+
href: resolvedHref,
|
|
257
|
+
target: "_blank",
|
|
258
|
+
rel: "noopener noreferrer",
|
|
259
|
+
"aria-label": `Open ${label ?? "record"} ${display} in a new tab`,
|
|
260
|
+
onClick: stop,
|
|
261
|
+
className: "inline-flex min-w-0 items-center gap-1 font-mono text-xs text-sky-600 hover:underline dark:text-sky-400",
|
|
262
|
+
children: [
|
|
263
|
+
/* @__PURE__ */ jsx6("span", { className: "min-w-0 truncate tabular-nums", children: display }),
|
|
264
|
+
/* @__PURE__ */ jsx6(ExternalLink, { className: "h-3 w-3 shrink-0 opacity-50" })
|
|
265
|
+
]
|
|
266
|
+
}
|
|
267
|
+
);
|
|
268
|
+
}
|
|
269
|
+
return /* @__PURE__ */ jsx6("span", { className: "min-w-0 truncate font-mono text-xs text-muted-foreground tabular-nums select-all", children: display });
|
|
270
|
+
})();
|
|
271
|
+
return /* @__PURE__ */ jsxs3(
|
|
272
|
+
"span",
|
|
273
|
+
{
|
|
274
|
+
"data-matrx-cell-control": true,
|
|
275
|
+
className: cn(
|
|
276
|
+
"inline-flex min-w-0 max-w-full items-center gap-0.5 whitespace-nowrap",
|
|
277
|
+
className
|
|
278
|
+
),
|
|
279
|
+
onClick: stop,
|
|
280
|
+
children: [
|
|
281
|
+
/* @__PURE__ */ jsx6(TooltipProvider, { delayDuration: 200, children: /* @__PURE__ */ jsxs3(Tooltip, { children: [
|
|
282
|
+
/* @__PURE__ */ jsx6(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsx6("span", { className: "inline-flex min-w-0 max-w-full items-center", children: idNode }) }),
|
|
283
|
+
/* @__PURE__ */ jsx6(TooltipContent, { side: "top", className: "max-w-sm break-all font-mono", children: forbidden ? `No access \xB7 ${tip}` : tip })
|
|
284
|
+
] }) }),
|
|
285
|
+
/* @__PURE__ */ jsx6(
|
|
286
|
+
"button",
|
|
287
|
+
{
|
|
288
|
+
type: "button",
|
|
289
|
+
onClick: onCopy,
|
|
290
|
+
"aria-label": copied ? "Copied" : `Copy ${label ?? "id"}`,
|
|
291
|
+
title: `Copy ${label ?? "id"}: ${value}`,
|
|
292
|
+
className: cn(
|
|
293
|
+
"inline-flex h-4 w-4 shrink-0 items-center justify-center rounded text-muted-foreground transition-colors hover:text-foreground focus:outline-none",
|
|
294
|
+
copied && "text-primary"
|
|
295
|
+
),
|
|
296
|
+
children: copied ? /* @__PURE__ */ jsx6(Check, { className: "h-3 w-3" }) : /* @__PURE__ */ jsx6(Copy, { className: "h-3 w-3" })
|
|
297
|
+
}
|
|
298
|
+
),
|
|
299
|
+
canPeek ? /* @__PURE__ */ jsx6(
|
|
300
|
+
"button",
|
|
301
|
+
{
|
|
302
|
+
type: "button",
|
|
303
|
+
onClick: (e) => {
|
|
304
|
+
stop(e);
|
|
305
|
+
setPeekOpen(true);
|
|
306
|
+
},
|
|
307
|
+
"aria-label": `Quick look at ${label ?? "record"}`,
|
|
308
|
+
title: `Quick look at ${label ?? "record"}`,
|
|
309
|
+
className: "inline-flex h-4 w-4 shrink-0 items-center justify-center rounded text-muted-foreground transition-colors hover:text-foreground focus:outline-none",
|
|
310
|
+
children: /* @__PURE__ */ jsx6(Lightbulb, { className: "h-3 w-3" })
|
|
311
|
+
}
|
|
312
|
+
) : null,
|
|
313
|
+
onOpen && resolvedHref && !forbidden ? /* @__PURE__ */ jsx6(
|
|
314
|
+
"a",
|
|
315
|
+
{
|
|
316
|
+
href: resolvedHref,
|
|
317
|
+
target: "_blank",
|
|
318
|
+
rel: "noopener noreferrer",
|
|
319
|
+
onClick: stop,
|
|
320
|
+
"aria-label": `Open ${label ?? "record"} in a new tab`,
|
|
321
|
+
title: `Open ${label ?? "record"} in a new tab`,
|
|
322
|
+
className: "inline-flex h-4 w-4 shrink-0 items-center justify-center rounded text-muted-foreground transition-colors hover:text-foreground focus:outline-none",
|
|
323
|
+
children: /* @__PURE__ */ jsx6(ExternalLink, { className: "h-3 w-3" })
|
|
324
|
+
}
|
|
325
|
+
) : null,
|
|
326
|
+
trailing,
|
|
327
|
+
notice,
|
|
328
|
+
canPeek && peekOpen && ResourcePeek ? /* @__PURE__ */ jsx6(
|
|
329
|
+
ResourcePeek,
|
|
330
|
+
{
|
|
331
|
+
kind: doors?.peekKind ?? token ?? "record",
|
|
332
|
+
id: value,
|
|
333
|
+
onClose: () => setPeekOpen(false),
|
|
334
|
+
...href == null ? {} : { href }
|
|
335
|
+
}
|
|
336
|
+
) : null
|
|
337
|
+
]
|
|
338
|
+
}
|
|
339
|
+
);
|
|
340
|
+
}
|
|
341
|
+
export {
|
|
342
|
+
MatrxUuidCell,
|
|
343
|
+
shortUuid
|
|
344
|
+
};
|
|
345
|
+
//# sourceMappingURL=uuid-cell.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/data-table/MatrxUuidCell.tsx","../../src/data-table/icons.tsx","../../src/data-table/useTableNotice.tsx","../../src/data-table/host.tsx","../../src/tooltip.tsx","../../src/cn.ts","../../src/motion.ts","../../src/portal-container.tsx"],"sourcesContent":["\"use client\";\n\n/**\n * MatrxUuidCell — canonical UUID / FK display for dense tables.\n *\n * Port of aidream `UuidDisplay` + AI Models `UuidCell` + tool-call `ShortId`:\n * short prefix (8 chars), full value on hover, always-visible copy of the full\n * id. Optional FK open: in-app WindowPanel (`onOpen`) and/or route (`href`).\n * Forbidden targets stay copyable but are not navigable.\n *\n * THE DOOR LAW: \"never render an id you can't open\". Pass `token` (the canonical\n * entity token this id points at) and the cell resolves its own doors from the\n * registries — route, new tab, and peek — with no per-callsite wiring. Prefer\n * `EntityRef` whenever the record's NAME is available; this is the fallback for\n * a raw FK column where it genuinely isn't.\n */\n\nimport {\n useEffect,\n useRef,\n useState,\n type MouseEvent,\n type ReactNode,\n} from \"react\";\nimport {\n Check,\n Copy,\n ExternalLink,\n Lightbulb,\n PanelRight,\n ShieldOff,\n} from \"./icons\";\nimport { useTableNotice } from \"./useTableNotice\";\nimport { useMatrxDataTableHost } from \"./host\";\nimport {\n Tooltip,\n TooltipContent,\n TooltipProvider,\n TooltipTrigger,\n} from \"../tooltip\";\nimport { cn } from \"../cn\";\n\nconst SHORT_LEN = 8;\n\nexport function shortUuid(id: string, len = SHORT_LEN): string {\n return id.slice(0, len);\n}\n\nasync function copyText(text: string): Promise<boolean> {\n try {\n if (navigator.clipboard?.writeText) {\n await navigator.clipboard.writeText(text);\n return true;\n }\n } catch {\n // fall through\n }\n try {\n const ta = document.createElement(\"textarea\");\n ta.value = text;\n ta.style.position = \"fixed\";\n ta.style.opacity = \"0\";\n document.body.appendChild(ta);\n ta.select();\n const ok = document.execCommand(\"copy\");\n document.body.removeChild(ta);\n return ok;\n } catch {\n return false;\n }\n}\n\nexport type MatrxUuidOpenResult = void | \"forbidden\";\n\nexport interface MatrxUuidCellProps {\n value: string | null | undefined;\n /** Tooltip / copy label prefix (e.g. column name). */\n label?: string | undefined;\n /**\n * Open the referenced record in-app (prefer WindowPanel).\n * Return `\"forbidden\"` to block navigation and show a muted state.\n */\n onOpen?: (id: string) => MatrxUuidOpenResult | Promise<MatrxUuidOpenResult>;\n /** Optional real href (new tab / cmd-click). Used when navigation is allowed. */\n href?: string | null | undefined;\n /**\n * Canonical entity token this id points at (`agent`, `note`, `task`, …).\n * Supplies the route and the peek automatically — THE DOOR LAW, resolved\n * from the registries instead of wired per callsite. An explicit `href` /\n * `onOpen` still wins.\n */\n token?: string | null | undefined;\n /** Force non-navigable (still copyable). */\n forbidden?: boolean;\n className?: string;\n trailing?: ReactNode;\n}\n\nexport function MatrxUuidCell({\n value,\n label,\n onOpen,\n href,\n token,\n forbidden = false,\n className,\n trailing,\n}: MatrxUuidCellProps) {\n const { resolveEntityDoors, ResourcePeek } = useMatrxDataTableHost();\n const { notify, notice } = useTableNotice();\n const [copied, setCopied] = useState(false);\n const [opening, setOpening] = useState(false);\n const [peekOpen, setPeekOpen] = useState(false);\n const timer = useRef<ReturnType<typeof setTimeout> | null>(null);\n\n useEffect(\n () => () => {\n if (timer.current) clearTimeout(timer.current);\n },\n [],\n );\n\n if (!value) {\n return <span className=\"text-muted-foreground/40\">—</span>;\n }\n\n const doors = token ? resolveEntityDoors?.(token, value, href) ?? null : null;\n const resolvedHref = doors?.href ?? href ?? null;\n const canPeek = Boolean(doors?.canPeek) && !forbidden;\n const display = shortUuid(value);\n const tip = label ? `${label}: ${value}` : value;\n\n const stop = (e: MouseEvent) => {\n e.stopPropagation();\n };\n\n const onCopy = (e: MouseEvent) => {\n stop(e);\n e.preventDefault();\n void copyText(value).then((ok) => {\n if (!ok) {\n notify?.error(\"Couldn't copy ID\");\n return;\n }\n setCopied(true);\n if (timer.current) clearTimeout(timer.current);\n timer.current = setTimeout(() => setCopied(false), 1500);\n });\n };\n\n const handleOpen = async (e: MouseEvent) => {\n stop(e);\n e.preventDefault();\n if (!onOpen || forbidden || opening) return;\n setOpening(true);\n try {\n const result = await onOpen(value);\n if (result === \"forbidden\") {\n // access-errors: ok — relays the onOpen contract's explicit verdict: the caller's opener returns \"forbidden\" only after ITS OWN access check; the cell performs no read and never guesses\n notify?.error(\"You don't have access to open this record\");\n }\n } catch (err) {\n notify?.error(\n `Couldn't open: ${err instanceof Error ? err.message : String(err)}`,\n );\n } finally {\n setOpening(false);\n }\n };\n\n const idNode = (() => {\n if (forbidden) {\n return (\n <span className=\"inline-flex min-w-0 items-center gap-1 font-mono text-xs text-muted-foreground/70 tabular-nums\">\n <span className=\"min-w-0 truncate\">{display}</span>\n <ShieldOff className=\"h-3 w-3 shrink-0 opacity-60\" aria-hidden />\n </span>\n );\n }\n\n if (onOpen) {\n return (\n <button\n type=\"button\"\n aria-label={`Open ${label ?? \"record\"} ${display}`}\n onClick={(e) => void handleOpen(e)}\n disabled={opening}\n className=\"inline-flex min-w-0 items-center gap-1 font-mono text-xs text-sky-600 hover:underline dark:text-sky-400 disabled:opacity-60\"\n >\n <span className=\"min-w-0 truncate tabular-nums\">{display}</span>\n <PanelRight className=\"h-3 w-3 shrink-0 opacity-50\" />\n </button>\n );\n }\n\n if (resolvedHref) {\n return (\n <a\n href={resolvedHref}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n aria-label={`Open ${label ?? \"record\"} ${display} in a new tab`}\n onClick={stop}\n className=\"inline-flex min-w-0 items-center gap-1 font-mono text-xs text-sky-600 hover:underline dark:text-sky-400\"\n >\n <span className=\"min-w-0 truncate tabular-nums\">{display}</span>\n <ExternalLink className=\"h-3 w-3 shrink-0 opacity-50\" />\n </a>\n );\n }\n\n return (\n <span className=\"min-w-0 truncate font-mono text-xs text-muted-foreground tabular-nums select-all\">\n {display}\n </span>\n );\n })();\n\n return (\n <span\n data-matrx-cell-control\n className={cn(\n \"inline-flex min-w-0 max-w-full items-center gap-0.5 whitespace-nowrap\",\n className,\n )}\n onClick={stop}\n >\n <TooltipProvider delayDuration={200}>\n <Tooltip>\n <TooltipTrigger asChild>\n <span className=\"inline-flex min-w-0 max-w-full items-center\">\n {idNode}\n </span>\n </TooltipTrigger>\n <TooltipContent side=\"top\" className=\"max-w-sm break-all font-mono\">\n {forbidden ? `No access · ${tip}` : tip}\n </TooltipContent>\n </Tooltip>\n </TooltipProvider>\n <button\n type=\"button\"\n onClick={onCopy}\n aria-label={copied ? \"Copied\" : `Copy ${label ?? \"id\"}`}\n title={`Copy ${label ?? \"id\"}: ${value}`}\n className={cn(\n \"inline-flex h-4 w-4 shrink-0 items-center justify-center rounded text-muted-foreground transition-colors hover:text-foreground focus:outline-none\",\n copied && \"text-primary\",\n )}\n >\n {copied ? <Check className=\"h-3 w-3\" /> : <Copy className=\"h-3 w-3\" />}\n </button>\n {canPeek ? (\n <button\n type=\"button\"\n onClick={(e) => {\n stop(e);\n setPeekOpen(true);\n }}\n aria-label={`Quick look at ${label ?? \"record\"}`}\n title={`Quick look at ${label ?? \"record\"}`}\n className=\"inline-flex h-4 w-4 shrink-0 items-center justify-center rounded text-muted-foreground transition-colors hover:text-foreground focus:outline-none\"\n >\n <Lightbulb className=\"h-3 w-3\" />\n </button>\n ) : null}\n {onOpen && resolvedHref && !forbidden ? (\n <a\n href={resolvedHref}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n onClick={stop}\n aria-label={`Open ${label ?? \"record\"} in a new tab`}\n title={`Open ${label ?? \"record\"} in a new tab`}\n className=\"inline-flex h-4 w-4 shrink-0 items-center justify-center rounded text-muted-foreground transition-colors hover:text-foreground focus:outline-none\"\n >\n <ExternalLink className=\"h-3 w-3\" />\n </a>\n ) : null}\n {trailing}\n {notice}\n {canPeek && peekOpen && ResourcePeek ? (\n <ResourcePeek\n kind={doors?.peekKind ?? token ?? \"record\"}\n id={value}\n onClose={() => setPeekOpen(false)}\n // Same contract as `EntityDoorControls`: forward the CALLER's raw\n // `href` so the peek's \"Open\" cannot send the user somewhere this\n // cell's own link does not go. `doors.ts` names EntityRef and this\n // cell as the two presentations that must not drift — the peek\n // override shipped on one of them first, which is exactly the drift\n // that file warns about.\n {...(href == null ? {} : { href })}\n />\n ) : null}\n </span>\n );\n}\n","import type { SVGProps } from \"react\";\n\ntype IconProps = SVGProps<SVGSVGElement>;\nfunction Icon({ children, ...props }: IconProps) {\n return <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" aria-hidden=\"true\" {...props}>{children}</svg>;\n}\nconst path = <path d=\"M4 12h16M12 4v16\" />;\nexport const ChevronLeft = (p: IconProps) => <Icon {...p}><path d=\"m15 18-6-6 6-6\" /></Icon>;\nexport const ChevronRight = (p: IconProps) => <Icon {...p}><path d=\"m9 18 6-6-6-6\" /></Icon>;\nexport const ChevronDown = (p: IconProps) => <Icon {...p}><path d=\"m6 9 6 6 6-6\" /></Icon>;\nexport const ArrowDown = (p: IconProps) => <Icon {...p}><path d=\"M12 4v16m-6-6 6 6 6-6\" /></Icon>;\nexport const ArrowUp = (p: IconProps) => <Icon {...p}><path d=\"M12 20V4m-6 6 6-6 6 6\" /></Icon>;\nexport const ArrowUpDown = (p: IconProps) => <Icon {...p}><path d=\"M12 4v16m-4-12 4-4 4 4m-8 8 4 4 4-4\" /></Icon>;\nexport const Check = (p: IconProps) => <Icon {...p}><path d=\"m5 12 4 4L19 6\" /></Icon>;\nexport const X = (p: IconProps) => <Icon {...p}><path d=\"M6 6l12 12M18 6 6 18\" /></Icon>;\nexport const Search = (p: IconProps) => <Icon {...p}><circle cx=\"11\" cy=\"11\" r=\"6\" /><path d=\"m16 16 4 4\" /></Icon>;\nexport const Plus = (p: IconProps) => <Icon {...p}>{path}</Icon>;\nexport const Minus = (p: IconProps) => <Icon {...p}><path d=\"M4 12h16\" /></Icon>;\nexport const Eraser = (p: IconProps) => <Icon {...p}><path d=\"m7 19-3-3 9-9 5 5-7 7H7z\" /></Icon>;\nexport const GripVertical = (p: IconProps) => <Icon {...p}><circle cx=\"9\" cy=\"6\" r=\"1\" /><circle cx=\"15\" cy=\"6\" r=\"1\" /><circle cx=\"9\" cy=\"12\" r=\"1\" /><circle cx=\"15\" cy=\"12\" r=\"1\" /><circle cx=\"9\" cy=\"18\" r=\"1\" /><circle cx=\"15\" cy=\"18\" r=\"1\" /></Icon>;\nexport const SlidersHorizontal = (p: IconProps) => <Icon {...p}><path d=\"M4 7h16M4 17h16M9 7v0M15 17v0\" /></Icon>;\nexport const Trash2 = (p: IconProps) => <Icon {...p}><path d=\"M4 7h16M10 11v6M14 11v6M9 7l1-2h4l1 2M6 7l1 13h10l1-13\" /></Icon>;\nexport const Loader2 = (p: IconProps) => <Icon {...p}><path d=\"M20 12a8 8 0 1 1-2.3-5.7\" /></Icon>;\nexport const Save = (p: IconProps) => <Icon {...p}><path d=\"M5 3h12l2 2v16H5zM8 3v6h8V3M8 21v-7h8v7\" /></Icon>;\nexport const Pencil = (p: IconProps) => <Icon {...p}><path d=\"m4 20 4-1 11-11-3-3L5 16l-1 4z\" /></Icon>;\nexport const Eye = (p: IconProps) => <Icon {...p}><path d=\"M2 12s3-6 10-6 10 6 10 6-3 6-10 6S2 12 2 12z\" /><circle cx=\"12\" cy=\"12\" r=\"2\" /></Icon>;\nexport const PanelRight = (p: IconProps) => <Icon {...p}><rect x=\"3\" y=\"4\" width=\"18\" height=\"16\" rx=\"2\" /><path d=\"M15 4v16\" /></Icon>;\nexport const PanelRightOpen = PanelRight;\nexport const WholeWord = (p: IconProps) => <Icon {...p}><path d=\"M4 6l3 12 3-8 3 8 3-12M17 15h4\" /></Icon>;\nexport const ExternalLink = (p: IconProps) => <Icon {...p}><path d=\"M14 4h6v6M20 4l-9 9M19 13v6H5V5h6\" /></Icon>;\nexport const Copy = (p: IconProps) => <Icon {...p}><rect x=\"9\" y=\"9\" width=\"11\" height=\"11\" rx=\"2\" /><path d=\"M15 9V6a2 2 0 0 0-2-2H6a2 2 0 0 0-2 2v7a2 2 0 0 0 2 2h3\" /></Icon>;\nexport const Lightbulb = (p: IconProps) => <Icon {...p}><path d=\"M9 18h6M10 22h4M8 14a6 6 0 1 1 8 0c-1 1-1 2-1 3H9c0-1 0-2-1-3z\" /></Icon>;\nexport const ShieldOff = (p: IconProps) => <Icon {...p}><path d=\"m3 3 18 18M12 3l7 3v5c0 4-3 7-7 9-4-2-7-5-7-9V6l4-2\" /></Icon>;\nexport const ListFilter = SlidersHorizontal;\nexport const ListX = X;\n\nexport const ChevronsLeft = (p: IconProps) => <Icon {...p}><path d=\"m11 18-6-6 6-6m8 12-6-6 6-6\" /></Icon>;\nexport const ChevronsRight = (p: IconProps) => <Icon {...p}><path d=\"m5 18 6-6-6-6m8 12 6-6-6-6\" /></Icon>;\n","\"use client\";\nimport { useState } from \"react\";\nimport { useMatrxDataTableHost } from \"./host\";\n\n/** Every portable async action has visible feedback even without a host toast system. */\nexport function useTableNotice() {\n const host = useMatrxDataTableHost();\n const [message, setMessage] = useState<{ text: string; error: boolean } | null>(null);\n const notify = {\n success(text: string) { if (host.notify) host.notify.success(text); else setMessage({ text, error: false }); },\n error(text: string) { if (host.notify) host.notify.error(text); else setMessage({ text, error: true }); },\n };\n const notice = message ? <span role={message.error ? \"alert\" : \"status\"} className={message.error ? \"text-xs text-destructive\" : \"text-xs text-muted-foreground\"}>{message.text}<button type=\"button\" aria-label=\"Dismiss notification\" className=\"ml-2 underline\" onClick={() => setMessage(null)}>Dismiss</button></span> : null;\n return { notify, notice };\n}\n","\"use client\";\nimport { createContext, useContext, type Context, type ComponentType, type ReactNode, type MouseEventHandler } from \"react\";\nimport type { CopyControlProps } from \"./copy-types\";\n\nexport type {\n AgentPayloadInput,\n AiCustomSource,\n AiVariant,\n CopyControlProps,\n CopyExportConfig,\n} from \"./copy-types\";\n\nexport interface TableLinkProps { href: string; children: ReactNode; className?: string; onClick?: MouseEventHandler<HTMLAnchorElement>; }\nexport interface TableSidePanelProps { title: string; titleNode?: ReactNode; description?: ReactNode; onClose: () => void; defaultWidth?: number; headerActions?: ReactNode; children: ReactNode; }\nexport interface TableWindowPanelProps { id: string; title: string; onClose: () => void; width?: number; height?: number; minWidth?: number; minHeight?: number; actionsRight?: ReactNode; bodyClassName?: string; sidebarDefaultSize?: number; sidebarMinSize?: number; sidebar?: ReactNode; children: ReactNode; }\nexport interface TableEntityRefProps { token: string; id: string; href?: string; children: ReactNode; className?: string; }\nexport interface TablePeekProps { kind: string; id: string; href?: string; onClose: () => void; }\nexport interface TableDoors { href?: string; peekKind?: string; canPeek?: boolean; }\nexport interface MatrxDataTableHost { Link?: ComponentType<TableLinkProps>; CopyControls?: ComponentType<CopyControlProps>; SidePanelSurface?: ComponentType<TableSidePanelProps>; WindowPanel?: ComponentType<TableWindowPanelProps>; EntityRef?: ComponentType<TableEntityRefProps>; resolveEntityDoors?: (token: string, id: string, href?: string | null) => TableDoors | null; ResourcePeek?: ComponentType<TablePeekProps>; notify?: { success(message: string): void; error(message: string): void }; }\n// Public entrypoints are independently bundled in ESM and CJS. The context\n// identity must survive those bundle boundaries; values stay provider-scoped.\nconst HOST_SLOT = Symbol.for(\"ai-matrx.design-system.table-host-context\");\nconst hostRegistry = globalThis as typeof globalThis & { [HOST_SLOT]?: Context<MatrxDataTableHost> };\nconst HostContext = hostRegistry[HOST_SLOT] ??= createContext<MatrxDataTableHost>({});\nexport function MatrxDataTableProvider({ value, children }: { value: MatrxDataTableHost; children: ReactNode }) { return <HostContext.Provider value={value}>{children}</HostContext.Provider>; }\nexport function useMatrxDataTableHost(): MatrxDataTableHost { return useContext(HostContext); }\n","\"use client\";\n\n/**\n * Tooltip — the ONE tooltip in the fleet.\n *\n * THE COLLAPSE (0.6.0). Two implementations existed: matrx-frontend's\n * `components/ui/tooltip.tsx` (182 import sites) and `@ai-matrx/tap-target`'s\n * `src/tooltip.tsx` — the latter an explicit inlined PORT of the former,\n * carrying a header that said so. tap-target shipped it only because its ~60\n * tap buttons need a tooltip and no shared package owned one; four repos then\n * adopted `TooltipProvider` from tap-target. That made tap-target — a\n * button-geometry package — the accidental owner of a design-system primitive,\n * with a second copy living in the biggest host.\n *\n * A tooltip is a design-system surface, so it lives here. tap-target now takes\n * `@ai-matrx/design-system` as an ordinary sibling dependency and imports this\n * file; its own copy is deleted, and its re-export removed. The DAG already\n * ordered design-system before tap-target, so no cycle is introduced.\n *\n * BEHAVIOUR is matrx-frontend's, verbatim, including the ruling both copies\n * carried in prose:\n *\n * 1. THE ROOT RENDERS UNCONDITIONALLY — no hydration mount gate. The gate the\n * host wrapper once carried (\"Radix generates dynamic aria-controls ids that\n * differ between SSR and client\") was false: Radix ids come from React's\n * SSR-stable `useId`. It was actively harmful — a Trigger wraps\n * ALWAYS-VISIBLE content, so `return null` deleted that subtree from SSR and\n * the first client paint.\n *\n * 2. THE SURFACE IS NEUTRAL, NOT BRANDED. Popover tokens, not `bg-primary`:\n * rich tooltip content using `text-muted-foreground` was illegible on a\n * brand-coloured background in one theme or the other.\n *\n * SEAM INVERSION. The host resolved nested portal targets through\n * `useNestedPortalContainer` (an app hook wired to its dialog + window-panel\n * systems); tap-target's port dropped that seam entirely and always portalled\n * to `document.body` — which is why a tooltip inside a popped-out panel landed\n * in the wrong window. Here it is the package's `PortalContainerProvider`\n * seam: an explicit `container` prop wins, then the injected container, then\n * `document.body`. Both prior behaviours are reachable; neither is forked.\n */\n\nimport * as TooltipPrimitive from \"@radix-ui/react-tooltip\";\nimport * as React from \"react\";\n\nimport { cn } from \"./cn\";\nimport { MOTION_POPPER } from \"./motion\";\nimport { usePortalContainer } from \"./portal-container\";\n\n/**\n * Mount once near the app root. Every tooltip in the tree shares its\n * `delayDuration` / `skipDelayDuration` timing.\n */\nconst TooltipProvider = TooltipPrimitive.Provider;\n\nconst Tooltip = TooltipPrimitive.Root;\n\nconst TooltipTrigger = TooltipPrimitive.Trigger;\n\nexport interface TooltipContentProps\n extends React.ComponentPropsWithoutRef<typeof TooltipPrimitive.Content> {\n /**\n * Explicit portal target; wins over the injected `PortalContainerProvider`\n * value. Pass `null` to force `document.body`.\n */\n container?: HTMLElement | null;\n /**\n * Package-owned open/close motion. Default `true`. Pass `false` for a\n * surface that must appear instantly. It is NOT the reduced-motion switch;\n * `styles.css` already honours `prefers-reduced-motion`.\n */\n animated?: boolean;\n}\n\nconst TooltipContent = React.forwardRef<\n React.ComponentRef<typeof TooltipPrimitive.Content>,\n TooltipContentProps\n>(({ className, sideOffset = 4, container, animated = true, ...props }, ref) => {\n const resolved = usePortalContainer(container);\n return (\n <TooltipPrimitive.Portal container={resolved ?? null}>\n <TooltipPrimitive.Content\n ref={ref}\n data-slot=\"tooltip-content\"\n sideOffset={sideOffset}\n className={cn(\n \"z-[10001] overflow-hidden rounded-md border border-border bg-popover px-3 py-1.5 text-xs text-popover-foreground shadow-md\",\n animated && MOTION_POPPER,\n className,\n )}\n {...props}\n />\n </TooltipPrimitive.Portal>\n );\n});\nTooltipContent.displayName = TooltipPrimitive.Content.displayName;\n\nexport { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger };\n","import { clsx, type ClassValue } from \"clsx\";\nimport { twMerge } from \"tailwind-merge\";\n\nexport function cn(...inputs: ClassValue[]): string {\n return twMerge(clsx(inputs));\n}\n","/**\n * THE MOTION CONTRACT (0.10.0) — the class names every animated surface in\n * this package puts on the DOM, in ONE place.\n *\n * Why a module and not ten inline strings: the sweep that produced this file\n * found the same defect ten times over, and it was invisible each time\n * because each primitive spelled its animation out by hand. Ten hand-written\n * copies of `data-[state=open]:animate-in data-[state=closed]:animate-out\n * data-[state=closed]:fade-out-0 …` meant ten chances to reach for a utility\n * nothing in the package defines — which is exactly what happened. A single\n * exported constant per motion SHAPE means the next primitive cannot invent\n * an eleventh spelling, and `styles.test.ts` can prove every one of them\n * resolves to a rule in the shipped stylesheet.\n *\n * Every rule these name lives in `styles.css` under the package's own\n * keyframes. NONE of them is a Tailwind utility, and none requires a host\n * plugin — see that file's header for the measurement that made this\n * necessary.\n */\n\n/** Modal scrims: Dialog, AlertDialog, Sheet, CommandDialog. Fade only. */\nexport const MOTION_OVERLAY = \"matrx-motion-overlay\";\n\n/** The centred dialog card. Fades and scales; centring is not its business. */\nexport const MOTION_DIALOG = \"matrx-motion-dialog\";\n\n/**\n * THE CENTRING OF A CENTRED DIALOG CARD — package-owned, in the `translate`\n * property, and NEVER a Tailwind `translate-x-[-50%] translate-y-[-50%]` pair.\n *\n * Those utilities do not compile to the same thing in every host: Tailwind v3\n * writes them into `transform`, Tailwind v4 writes them into the independent\n * `translate` property, which COMPOSES with `transform` instead of being\n * replaced by it. 0.10.0's keyframes restated the centring inside `transform`,\n * which was harmless on v3 and doubled the centring on v4 — the card flew in\n * from off the top-left of the viewport for the animation's whole duration and\n * snapped into place at the end (fixed in 0.10.1).\n *\n * Owning it here means one spelling that reads the same on both, and it frees\n * `transform` so the keyframes carry ONLY the delta. Put this on every centred\n * surface UNCONDITIONALLY — it is geometry, so unlike {@link MOTION_DIALOG} it\n * must survive `animated={false}`. Its rule ships in `styles.css`.\n */\nexport const CENTERED_DIALOG = \"matrx-dialog-centered\";\n\n/**\n * The resting gap between a `popper`-positioned surface and its trigger, for\n * the primitives that want one (today: `Select`). Same law and same reason as\n * {@link CENTERED_DIALOG} — a position an animated surface holds AT REST is\n * the `translate` property's job, never a Tailwind translate utility, because\n * those land in `transform` on a v3 host and in `translate` on a v4 one while\n * the keyframes animate `transform` either way. Sizes from\n * `--matrx-popper-offset`; keys off Radix's `data-side`.\n */\nexport const POPPER_OFFSET = \"matrx-popper-offset\";\n\n/** The mobile Dialog / CommandDialog bottom sheet. Slides up and fades. */\nexport const MOTION_BOTTOM_SHEET = \"matrx-motion-bottom-sheet\";\n\n/**\n * Radix-popper surfaces: Popover, DropdownMenu, ContextMenu, HoverCard,\n * Select, Tooltip. Reads `--radix-popper-transform-origin` so the surface\n * grows out of its trigger, and `data-side` so it travels from it.\n */\nexport const MOTION_POPPER = \"matrx-motion-popper\";\n\n/** Edge sheets, one class per `sheetVariants` side. */\nexport const MOTION_SHEET = {\n top: \"matrx-motion-sheet-top\",\n bottom: \"matrx-motion-sheet-bottom\",\n left: \"matrx-motion-sheet-left\",\n right: \"matrx-motion-sheet-right\",\n center: \"matrx-motion-sheet-center\",\n} as const;\n\n/** Skeleton's breathing loop. */\nexport const MOTION_PULSE = \"matrx-pulse\";\n\n/** The spinner loop, for every inline busy indicator in the package. */\nexport const MOTION_SPIN = \"matrx-spin\";\n\n/**\n * Every motion class the package ships, for the guards. If a rule for one of\n * these is missing from `styles.css`, a component animates in no consumer and\n * says nothing about it — the C26 failure this list exists to make loud.\n */\nexport const ALL_MOTION_CLASSES = [\n MOTION_OVERLAY,\n MOTION_DIALOG,\n MOTION_BOTTOM_SHEET,\n MOTION_POPPER,\n ...Object.values(MOTION_SHEET),\n MOTION_PULSE,\n MOTION_SPIN,\n] as const;\n\n/**\n * The host-plugin utilities this package is forbidden to emit. They are\n * supplied by `tailwindcss-animate` / `tw-animate-css`, which is a HOST CSS\n * entry concern a package cannot declare a dependency on: three of the four\n * AI Matrx consumers never loaded it, so every surface wearing these appeared\n * with no animation and no error. `styles.test.ts` fails the build if one\n * comes back.\n */\nexport const FORBIDDEN_HOST_MOTION_UTILITIES = [\n \"animate-in\",\n \"animate-out\",\n \"fade-in\",\n \"fade-out\",\n \"zoom-in\",\n \"zoom-out\",\n \"slide-in-from\",\n \"slide-out-to\",\n \"animate-accordion\",\n \"animate-slide-down\",\n \"animate-slide-up\",\n \"animate-pulse\",\n \"animate-spin\",\n] as const;\n","\"use client\";\n\n/**\n * Injected portal-container seam.\n *\n * The matrx-frontend original resolved nested portal targets through\n * `useNestedPortalContainer` (explicit prop > dialog content > popout body >\n * document.body) — a host-shaped hook wired to that app's dialog and\n * window-panel systems. The package inverts the seam: hosts provide the\n * resolved container through `PortalContainerProvider`; an explicit\n * `container` prop on a component still wins, and with neither the portal\n * falls through to `document.body` (Radix default). Priority semantics are\n * unchanged from the original.\n */\n\nimport * as React from \"react\";\n\n// Shared identity across independently bundled public entrypoints/loaders.\nconst CONTEXT_SLOT = Symbol.for(\"ai-matrx.design-system.portal-container-context\");\nconst contextRegistry = globalThis as typeof globalThis & { [CONTEXT_SLOT]?: React.Context<HTMLElement | null | undefined> };\nconst PortalContainerContext = contextRegistry[CONTEXT_SLOT] ??= React.createContext<HTMLElement | null | undefined>(undefined);\n\nexport interface PortalContainerProviderProps {\n /** Where nested Radix portals (Popover, etc.) should mount. `null`/`undefined` → document.body. */\n container: HTMLElement | null | undefined;\n children: React.ReactNode;\n}\n\nexport function PortalContainerProvider({\n container,\n children,\n}: PortalContainerProviderProps) {\n return (\n <PortalContainerContext.Provider value={container ?? undefined}>\n {children}\n </PortalContainerContext.Provider>\n );\n}\n\n/**\n * Resolve a portal target: explicit prop (including an explicit `null`,\n * meaning \"the default body\") beats the injected container.\n */\nexport function usePortalContainer(\n explicit?: HTMLElement | null,\n): HTMLElement | undefined {\n const injected = React.useContext(PortalContainerContext);\n\n if (explicit !== undefined) {\n return explicit ?? undefined;\n }\n\n return injected ?? undefined;\n}\n"],"mappings":"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","names":["useState","jsx","jsx","jsxs","React","jsx","jsx","jsx","jsxs","useState"]}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
"use strict";
|
|
3
|
+
var __create = Object.create;
|
|
4
|
+
var __defProp = Object.defineProperty;
|
|
5
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
6
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
7
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
8
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
9
|
+
var __export = (target, all) => {
|
|
10
|
+
for (var name in all)
|
|
11
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
12
|
+
};
|
|
13
|
+
var __copyProps = (to, from, except, desc) => {
|
|
14
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
15
|
+
for (let key of __getOwnPropNames(from))
|
|
16
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
17
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
18
|
+
}
|
|
19
|
+
return to;
|
|
20
|
+
};
|
|
21
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
22
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
23
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
24
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
25
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
26
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
27
|
+
mod
|
|
28
|
+
));
|
|
29
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
30
|
+
|
|
31
|
+
// src/data-table/tableXlsx.ts
|
|
32
|
+
var tableXlsx_exports = {};
|
|
33
|
+
__export(tableXlsx_exports, {
|
|
34
|
+
downloadTableAsXlsx: () => downloadTableAsXlsx
|
|
35
|
+
});
|
|
36
|
+
module.exports = __toCommonJS(tableXlsx_exports);
|
|
37
|
+
var XLSX = __toESM(require("xlsx"), 1);
|
|
38
|
+
|
|
39
|
+
// src/data-table/filter-engine.ts
|
|
40
|
+
function columnId(col) {
|
|
41
|
+
if (col.id) return col.id;
|
|
42
|
+
if (col.accessorKey) return String(col.accessorKey);
|
|
43
|
+
throw new Error("MatrxDataTable column requires `id` or `accessorKey`");
|
|
44
|
+
}
|
|
45
|
+
function getCellValue(row, col) {
|
|
46
|
+
if (col.accessorFn) return col.accessorFn(row);
|
|
47
|
+
if (col.accessorKey) {
|
|
48
|
+
return row[col.accessorKey];
|
|
49
|
+
}
|
|
50
|
+
return void 0;
|
|
51
|
+
}
|
|
52
|
+
function stringifyCellValue(value) {
|
|
53
|
+
if (value == null) return "";
|
|
54
|
+
if (typeof value === "string") return value;
|
|
55
|
+
if (typeof value === "number" || typeof value === "boolean") {
|
|
56
|
+
return String(value);
|
|
57
|
+
}
|
|
58
|
+
if (value instanceof Date) return value.toISOString();
|
|
59
|
+
try {
|
|
60
|
+
return JSON.stringify(value);
|
|
61
|
+
} catch {
|
|
62
|
+
return String(value);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// src/data-table/tableXlsx.ts
|
|
67
|
+
function downloadTableAsXlsx({
|
|
68
|
+
rows,
|
|
69
|
+
columns,
|
|
70
|
+
label
|
|
71
|
+
}) {
|
|
72
|
+
const visibleColumns = columns.filter(
|
|
73
|
+
(column) => column.filter !== false || column.accessorKey
|
|
74
|
+
);
|
|
75
|
+
const headers = visibleColumns.map(
|
|
76
|
+
(column) => typeof column.header === "string" ? column.header : columnId(column)
|
|
77
|
+
);
|
|
78
|
+
const values = rows.map(
|
|
79
|
+
(row) => visibleColumns.map(
|
|
80
|
+
(column) => stringifyCellValue(getCellValue(row, column))
|
|
81
|
+
)
|
|
82
|
+
);
|
|
83
|
+
const worksheet = XLSX.utils.aoa_to_sheet([headers, ...values]);
|
|
84
|
+
const workbook = XLSX.utils.book_new();
|
|
85
|
+
const sheetName = label.replace(/[\\/?*[\]:]/g, " ").trim().slice(0, 31) || "Data";
|
|
86
|
+
XLSX.utils.book_append_sheet(workbook, worksheet, sheetName);
|
|
87
|
+
const bytes = XLSX.write(workbook, {
|
|
88
|
+
type: "array",
|
|
89
|
+
bookType: "xlsx",
|
|
90
|
+
compression: true
|
|
91
|
+
});
|
|
92
|
+
const blob = new Blob([bytes], { type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" });
|
|
93
|
+
const url = URL.createObjectURL(blob);
|
|
94
|
+
const anchor = document.createElement("a");
|
|
95
|
+
anchor.href = url;
|
|
96
|
+
anchor.download = `${label.toLowerCase().replace(/[^a-z0-9]+/g, "-") || "table"}.xlsx`;
|
|
97
|
+
anchor.click();
|
|
98
|
+
URL.revokeObjectURL(url);
|
|
99
|
+
}
|
|
100
|
+
//# sourceMappingURL=xlsx.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/data-table/tableXlsx.ts","../../src/data-table/filter-engine.ts"],"sourcesContent":["import * as XLSX from \"xlsx\";\n\nimport { columnId, getCellValue, stringifyCellValue } from \"./filter-engine\";\nimport type { MatrxColumnDef } from \"./types\";\n\n/**\n * Download the same visible columns and rendered cell values used by the\n * canonical table's CSV export as a real Excel workbook. This module is loaded\n * only after the user chooses Excel from the unified action menu.\n */\nexport function downloadTableAsXlsx<T>({\n rows,\n columns,\n label,\n}: {\n rows: T[];\n columns: MatrxColumnDef<T>[];\n label: string;\n}) {\n const visibleColumns = columns.filter(\n (column) => column.filter !== false || column.accessorKey,\n );\n const headers = visibleColumns.map((column) =>\n typeof column.header === \"string\" ? column.header : columnId(column),\n );\n const values = rows.map((row) =>\n visibleColumns.map((column) =>\n stringifyCellValue(getCellValue(row, column)),\n ),\n );\n const worksheet = XLSX.utils.aoa_to_sheet([headers, ...values]);\n const workbook = XLSX.utils.book_new();\n const sheetName = label.replace(/[\\\\/?*[\\]:]/g, \" \").trim().slice(0, 31) || \"Data\";\n XLSX.utils.book_append_sheet(workbook, worksheet, sheetName);\n const bytes = XLSX.write(workbook, {\n type: \"array\",\n bookType: \"xlsx\",\n compression: true,\n });\n const blob = new Blob([bytes], { type: \"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet\" });\n const url = URL.createObjectURL(blob);\n const anchor = document.createElement(\"a\");\n anchor.href = url;\n anchor.download = `${label.toLowerCase().replace(/[^a-z0-9]+/g, \"-\") || \"table\"}.xlsx`;\n anchor.click();\n URL.revokeObjectURL(url);\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,WAAsB;;;ACqBf,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;;;AD7CO,SAAS,oBAAuB;AAAA,EACrC;AAAA,EACA;AAAA,EACA;AACF,GAIG;AACD,QAAM,iBAAiB,QAAQ;AAAA,IAC7B,CAAC,WAAW,OAAO,WAAW,SAAS,OAAO;AAAA,EAChD;AACA,QAAM,UAAU,eAAe;AAAA,IAAI,CAAC,WAClC,OAAO,OAAO,WAAW,WAAW,OAAO,SAAS,SAAS,MAAM;AAAA,EACrE;AACA,QAAM,SAAS,KAAK;AAAA,IAAI,CAAC,QACvB,eAAe;AAAA,MAAI,CAAC,WAClB,mBAAmB,aAAa,KAAK,MAAM,CAAC;AAAA,IAC9C;AAAA,EACF;AACA,QAAM,YAAiB,WAAM,aAAa,CAAC,SAAS,GAAG,MAAM,CAAC;AAC9D,QAAM,WAAgB,WAAM,SAAS;AACrC,QAAM,YAAY,MAAM,QAAQ,gBAAgB,GAAG,EAAE,KAAK,EAAE,MAAM,GAAG,EAAE,KAAK;AAC5E,EAAK,WAAM,kBAAkB,UAAU,WAAW,SAAS;AAC3D,QAAM,QAAa,WAAM,UAAU;AAAA,IACjC,MAAM;AAAA,IACN,UAAU;AAAA,IACV,aAAa;AAAA,EACf,CAAC;AACD,QAAM,OAAO,IAAI,KAAK,CAAC,KAAK,GAAG,EAAE,MAAM,oEAAoE,CAAC;AAC5G,QAAM,MAAM,IAAI,gBAAgB,IAAI;AACpC,QAAM,SAAS,SAAS,cAAc,GAAG;AACzC,SAAO,OAAO;AACd,SAAO,WAAW,GAAG,MAAM,YAAY,EAAE,QAAQ,eAAe,GAAG,KAAK,OAAO;AAC/E,SAAO,MAAM;AACb,MAAI,gBAAgB,GAAG;AACzB;","names":[]}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { M as MatrxColumnDef } from '../types-XXbVB1sh.cjs';
|
|
2
|
+
import 'react';
|
|
3
|
+
import './copy-types.cjs';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Download the same visible columns and rendered cell values used by the
|
|
7
|
+
* canonical table's CSV export as a real Excel workbook. This module is loaded
|
|
8
|
+
* only after the user chooses Excel from the unified action menu.
|
|
9
|
+
*/
|
|
10
|
+
declare function downloadTableAsXlsx<T>({ rows, columns, label, }: {
|
|
11
|
+
rows: T[];
|
|
12
|
+
columns: MatrxColumnDef<T>[];
|
|
13
|
+
label: string;
|
|
14
|
+
}): void;
|
|
15
|
+
|
|
16
|
+
export { downloadTableAsXlsx };
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { M as MatrxColumnDef } from '../types-ClaxTaXq.js';
|
|
2
|
+
import 'react';
|
|
3
|
+
import './copy-types.js';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Download the same visible columns and rendered cell values used by the
|
|
7
|
+
* canonical table's CSV export as a real Excel workbook. This module is loaded
|
|
8
|
+
* only after the user chooses Excel from the unified action menu.
|
|
9
|
+
*/
|
|
10
|
+
declare function downloadTableAsXlsx<T>({ rows, columns, label, }: {
|
|
11
|
+
rows: T[];
|
|
12
|
+
columns: MatrxColumnDef<T>[];
|
|
13
|
+
label: string;
|
|
14
|
+
}): void;
|
|
15
|
+
|
|
16
|
+
export { downloadTableAsXlsx };
|