@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,1110 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
"use client";
|
|
3
|
+
|
|
4
|
+
// src/data-table/DataRowWindow.tsx
|
|
5
|
+
import { useEffect as useEffect3, useState as useState6 } from "react";
|
|
6
|
+
|
|
7
|
+
// src/dialog.tsx
|
|
8
|
+
import * as DialogPrimitive from "@radix-ui/react-dialog";
|
|
9
|
+
import * as VisuallyHidden from "@radix-ui/react-visually-hidden";
|
|
10
|
+
import { treeContainsComponent } from "@ai-matrx/kit/react-tree";
|
|
11
|
+
import * as React4 from "react";
|
|
12
|
+
|
|
13
|
+
// src/cn.ts
|
|
14
|
+
import { clsx } from "clsx";
|
|
15
|
+
import { twMerge } from "tailwind-merge";
|
|
16
|
+
function cn(...inputs) {
|
|
17
|
+
return twMerge(clsx(inputs));
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// src/motion.ts
|
|
21
|
+
var MOTION_OVERLAY = "matrx-motion-overlay";
|
|
22
|
+
var MOTION_DIALOG = "matrx-motion-dialog";
|
|
23
|
+
var MOTION_BOTTOM_SHEET = "matrx-motion-bottom-sheet";
|
|
24
|
+
var MOTION_POPPER = "matrx-motion-popper";
|
|
25
|
+
var MOTION_SHEET = {
|
|
26
|
+
top: "matrx-motion-sheet-top",
|
|
27
|
+
bottom: "matrx-motion-sheet-bottom",
|
|
28
|
+
left: "matrx-motion-sheet-left",
|
|
29
|
+
right: "matrx-motion-sheet-right",
|
|
30
|
+
center: "matrx-motion-sheet-center"
|
|
31
|
+
};
|
|
32
|
+
var MOTION_PULSE = "matrx-pulse";
|
|
33
|
+
var MOTION_SPIN = "matrx-spin";
|
|
34
|
+
var ALL_MOTION_CLASSES = [
|
|
35
|
+
MOTION_OVERLAY,
|
|
36
|
+
MOTION_DIALOG,
|
|
37
|
+
MOTION_BOTTOM_SHEET,
|
|
38
|
+
MOTION_POPPER,
|
|
39
|
+
...Object.values(MOTION_SHEET),
|
|
40
|
+
MOTION_PULSE,
|
|
41
|
+
MOTION_SPIN
|
|
42
|
+
];
|
|
43
|
+
|
|
44
|
+
// src/icons.tsx
|
|
45
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
46
|
+
function lucideProps(props) {
|
|
47
|
+
return {
|
|
48
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
49
|
+
viewBox: "0 0 24 24",
|
|
50
|
+
fill: "none",
|
|
51
|
+
stroke: "currentColor",
|
|
52
|
+
strokeWidth: 2,
|
|
53
|
+
strokeLinecap: "round",
|
|
54
|
+
strokeLinejoin: "round",
|
|
55
|
+
"aria-hidden": true,
|
|
56
|
+
...props
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
function XIcon(props) {
|
|
60
|
+
return /* @__PURE__ */ jsxs("svg", { ...lucideProps(props), children: [
|
|
61
|
+
/* @__PURE__ */ jsx("path", { d: "M18 6 6 18" }),
|
|
62
|
+
/* @__PURE__ */ jsx("path", { d: "m6 6 12 12" })
|
|
63
|
+
] });
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// src/portal-container.tsx
|
|
67
|
+
import * as React from "react";
|
|
68
|
+
import { jsx as jsx2 } from "react/jsx-runtime";
|
|
69
|
+
var CONTEXT_SLOT = /* @__PURE__ */ Symbol.for("ai-matrx.design-system.portal-container-context");
|
|
70
|
+
var contextRegistry = globalThis;
|
|
71
|
+
var PortalContainerContext = contextRegistry[CONTEXT_SLOT] ??= React.createContext(void 0);
|
|
72
|
+
function PortalContainerProvider({
|
|
73
|
+
container,
|
|
74
|
+
children
|
|
75
|
+
}) {
|
|
76
|
+
return /* @__PURE__ */ jsx2(PortalContainerContext.Provider, { value: container ?? void 0, children });
|
|
77
|
+
}
|
|
78
|
+
function usePortalContainer(explicit) {
|
|
79
|
+
const injected = React.useContext(PortalContainerContext);
|
|
80
|
+
if (explicit !== void 0) {
|
|
81
|
+
return explicit ?? void 0;
|
|
82
|
+
}
|
|
83
|
+
return injected ?? void 0;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// src/radix-dialog-modal-context.tsx
|
|
87
|
+
import * as React2 from "react";
|
|
88
|
+
import { jsx as jsx3 } from "react/jsx-runtime";
|
|
89
|
+
var CONTEXT_SLOT2 = /* @__PURE__ */ Symbol.for("ai-matrx.design-system.dialog-modal-context");
|
|
90
|
+
var contextRegistry2 = globalThis;
|
|
91
|
+
var RadixDialogModalContext = contextRegistry2[CONTEXT_SLOT2] ??= React2.createContext(true);
|
|
92
|
+
function RadixDialogModalProvider({
|
|
93
|
+
children,
|
|
94
|
+
modal
|
|
95
|
+
}) {
|
|
96
|
+
return /* @__PURE__ */ jsx3(RadixDialogModalContext.Provider, { value: modal, children });
|
|
97
|
+
}
|
|
98
|
+
function useRadixDialogModal() {
|
|
99
|
+
return React2.useContext(RadixDialogModalContext);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// src/use-is-mobile.ts
|
|
103
|
+
import * as React3 from "react";
|
|
104
|
+
var MOBILE_BREAKPOINT = 768;
|
|
105
|
+
function useIsMobile() {
|
|
106
|
+
const [isMobile, setIsMobile] = React3.useState(false);
|
|
107
|
+
const [hasMounted, setHasMounted] = React3.useState(false);
|
|
108
|
+
React3.useEffect(() => {
|
|
109
|
+
setHasMounted(true);
|
|
110
|
+
const onChange = () => {
|
|
111
|
+
setIsMobile(window.innerWidth < MOBILE_BREAKPOINT);
|
|
112
|
+
};
|
|
113
|
+
onChange();
|
|
114
|
+
if (typeof window.matchMedia !== "function") {
|
|
115
|
+
return void 0;
|
|
116
|
+
}
|
|
117
|
+
const mql = window.matchMedia(`(max-width: ${MOBILE_BREAKPOINT - 1}px)`);
|
|
118
|
+
mql.addEventListener("change", onChange);
|
|
119
|
+
return () => mql.removeEventListener("change", onChange);
|
|
120
|
+
}, []);
|
|
121
|
+
if (!hasMounted) {
|
|
122
|
+
return false;
|
|
123
|
+
}
|
|
124
|
+
return isMobile;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// src/dialog.tsx
|
|
128
|
+
import { jsx as jsx4, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
129
|
+
var Dialog = ({
|
|
130
|
+
children,
|
|
131
|
+
...props
|
|
132
|
+
}) => {
|
|
133
|
+
const modal = props.modal ?? true;
|
|
134
|
+
return /* @__PURE__ */ jsx4(RadixDialogModalProvider, { modal, children: /* @__PURE__ */ jsx4(DialogPrimitive.Root, { ...props, modal, children }) });
|
|
135
|
+
};
|
|
136
|
+
Dialog.displayName = "Dialog";
|
|
137
|
+
var DialogPortal = ({
|
|
138
|
+
container,
|
|
139
|
+
...props
|
|
140
|
+
}) => {
|
|
141
|
+
const resolved = usePortalContainer(container);
|
|
142
|
+
return /* @__PURE__ */ jsx4(DialogPrimitive.Portal, { container: resolved ?? null, ...props });
|
|
143
|
+
};
|
|
144
|
+
DialogPortal.displayName = "DialogPortal";
|
|
145
|
+
var CONTEXT_SLOT3 = /* @__PURE__ */ Symbol.for("ai-matrx.design-system.dialog-container-context");
|
|
146
|
+
var contextRegistry3 = globalThis;
|
|
147
|
+
var DialogContainerContext = contextRegistry3[CONTEXT_SLOT3] ??= React4.createContext(null);
|
|
148
|
+
var DialogOverlay = React4.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx4(
|
|
149
|
+
DialogPrimitive.Overlay,
|
|
150
|
+
{
|
|
151
|
+
ref,
|
|
152
|
+
"data-slot": "dialog-overlay",
|
|
153
|
+
className: cn(
|
|
154
|
+
// Page context behind the modal stays readable; separation comes from a
|
|
155
|
+
// light token scrim, not a blur.
|
|
156
|
+
"fixed inset-0 z-[10000] bg-[var(--matrx-overlay-scrim-soft)]",
|
|
157
|
+
MOTION_OVERLAY,
|
|
158
|
+
className
|
|
159
|
+
),
|
|
160
|
+
...props
|
|
161
|
+
}
|
|
162
|
+
));
|
|
163
|
+
DialogOverlay.displayName = DialogPrimitive.Overlay.displayName;
|
|
164
|
+
var DialogContentPrimitive = React4.forwardRef((props, ref) => {
|
|
165
|
+
const isModal = useRadixDialogModal();
|
|
166
|
+
return /* @__PURE__ */ jsx4(
|
|
167
|
+
DialogPrimitive.Content,
|
|
168
|
+
{
|
|
169
|
+
...props,
|
|
170
|
+
ref,
|
|
171
|
+
"aria-modal": isModal || void 0
|
|
172
|
+
}
|
|
173
|
+
);
|
|
174
|
+
});
|
|
175
|
+
DialogContentPrimitive.displayName = "DialogContentPrimitive";
|
|
176
|
+
var DIALOG_DESKTOP_CLASSES = "matrx-dialog-card fixed inset-0 z-[10000] m-auto grid h-fit min-w-0 w-full max-w-lg gap-4 max-h-[85dvh] overflow-y-auto overscroll-contain [--dialog-pad:1.5rem] overflow-x-clip border bg-background p-6 shadow-lg [overflow-wrap:anywhere] [&>*]:min-w-0 [&>*]:max-w-full sm:rounded-lg";
|
|
177
|
+
var DIALOG_MOBILE_SHEET_CLASSES = "matrx-mobile-sheet matrx-dialog-card fixed inset-x-0 bottom-0 left-0 right-0 top-auto z-[10000] flex min-w-0 flex-col w-full max-w-full max-h-[90dvh] gap-4 [--dialog-pad:1rem] overflow-x-clip border-t bg-background p-4 pb-safe shadow-lg [overflow-wrap:anywhere] [&>*]:min-w-0 [&>*]:max-w-full rounded-t-2xl rounded-b-none overflow-y-auto overscroll-contain";
|
|
178
|
+
var DIALOG_MOBILE_SHEET_OVERRIDE = "inset-x-0 bottom-0 left-0 right-0 top-auto translate-none w-full max-w-full max-h-[90dvh] rounded-b-none rounded-t-2xl overflow-y-auto";
|
|
179
|
+
var DialogContent = React4.forwardRef(
|
|
180
|
+
({
|
|
181
|
+
className,
|
|
182
|
+
children,
|
|
183
|
+
mobileSheet = true,
|
|
184
|
+
animated = true,
|
|
185
|
+
showCloseButton = true,
|
|
186
|
+
container,
|
|
187
|
+
...props
|
|
188
|
+
}, ref) => {
|
|
189
|
+
const isMobile = useIsMobile();
|
|
190
|
+
const isModal = useRadixDialogModal();
|
|
191
|
+
const injectedContainer = usePortalContainer(container);
|
|
192
|
+
const asSheet = mobileSheet && isMobile;
|
|
193
|
+
const [containerEl, setContainerEl] = React4.useState(
|
|
194
|
+
null
|
|
195
|
+
);
|
|
196
|
+
const mergedRef = React4.useCallback(
|
|
197
|
+
(node) => {
|
|
198
|
+
setContainerEl(node);
|
|
199
|
+
if (typeof ref === "function") ref(node);
|
|
200
|
+
else if (ref)
|
|
201
|
+
ref.current = node;
|
|
202
|
+
},
|
|
203
|
+
[ref]
|
|
204
|
+
);
|
|
205
|
+
const hasTitle = treeContainsComponent(children, DialogTitle) || treeContainsComponent(children, DialogPrimitive.Title);
|
|
206
|
+
const hasDescription = treeContainsComponent(children, DialogDescription) || treeContainsComponent(children, DialogPrimitive.Description);
|
|
207
|
+
return /* @__PURE__ */ jsxs2(DialogPortal, { container, children: [
|
|
208
|
+
isModal ? /* @__PURE__ */ jsx4(DialogOverlay, {}) : null,
|
|
209
|
+
/* @__PURE__ */ jsxs2(
|
|
210
|
+
DialogContentPrimitive,
|
|
211
|
+
{
|
|
212
|
+
ref: mergedRef,
|
|
213
|
+
"data-slot": "dialog-content",
|
|
214
|
+
className: cn(
|
|
215
|
+
asSheet ? DIALOG_MOBILE_SHEET_CLASSES : DIALOG_DESKTOP_CLASSES,
|
|
216
|
+
animated && (asSheet ? MOTION_BOTTOM_SHEET : MOTION_DIALOG),
|
|
217
|
+
className,
|
|
218
|
+
asSheet && DIALOG_MOBILE_SHEET_OVERRIDE,
|
|
219
|
+
// A non-modal dialog is the coexistence contract for content that
|
|
220
|
+
// can sit beside a host's floating window manager (which starts at
|
|
221
|
+
// z=1000); staying below that boundary makes a newly focused window
|
|
222
|
+
// usable without coupling the two systems.
|
|
223
|
+
!isModal && "z-[900]"
|
|
224
|
+
),
|
|
225
|
+
...hasDescription ? {} : { "aria-describedby": void 0 },
|
|
226
|
+
...props,
|
|
227
|
+
children: [
|
|
228
|
+
hasTitle ? null : /* @__PURE__ */ jsx4(VisuallyHidden.Root, { children: /* @__PURE__ */ jsx4(DialogPrimitive.Title, { children: "Dialog" }) }),
|
|
229
|
+
/* @__PURE__ */ jsx4(DialogContainerContext.Provider, { value: containerEl, children: /* @__PURE__ */ jsx4(
|
|
230
|
+
PortalContainerProvider,
|
|
231
|
+
{
|
|
232
|
+
container: containerEl ?? injectedContainer ?? null,
|
|
233
|
+
children
|
|
234
|
+
}
|
|
235
|
+
) }),
|
|
236
|
+
showCloseButton ? /* @__PURE__ */ jsxs2(
|
|
237
|
+
DialogPrimitive.Close,
|
|
238
|
+
{
|
|
239
|
+
"data-slot": "dialog-close",
|
|
240
|
+
"aria-label": "Close",
|
|
241
|
+
className: "absolute right-2 top-4 flex h-11 w-11 items-center justify-center rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-accent data-[state=open]:text-muted-foreground sm:right-4 lg:h-10 lg:w-10",
|
|
242
|
+
children: [
|
|
243
|
+
/* @__PURE__ */ jsx4(XIcon, { className: "h-4 w-4" }),
|
|
244
|
+
/* @__PURE__ */ jsx4("span", { className: "sr-only", children: "Close" })
|
|
245
|
+
]
|
|
246
|
+
}
|
|
247
|
+
) : null
|
|
248
|
+
]
|
|
249
|
+
}
|
|
250
|
+
)
|
|
251
|
+
] });
|
|
252
|
+
}
|
|
253
|
+
);
|
|
254
|
+
DialogContent.displayName = DialogPrimitive.Content.displayName;
|
|
255
|
+
var DialogHeader = ({
|
|
256
|
+
className,
|
|
257
|
+
...props
|
|
258
|
+
}) => /* @__PURE__ */ jsx4(
|
|
259
|
+
"div",
|
|
260
|
+
{
|
|
261
|
+
"data-slot": "dialog-header",
|
|
262
|
+
className: cn(
|
|
263
|
+
// DialogContent owns an absolute close control. Reserve its hit area in
|
|
264
|
+
// every header so trailing actions never render underneath it.
|
|
265
|
+
"flex flex-col space-y-1.5 pr-12 text-center sm:text-left",
|
|
266
|
+
className
|
|
267
|
+
),
|
|
268
|
+
...props
|
|
269
|
+
}
|
|
270
|
+
);
|
|
271
|
+
DialogHeader.displayName = "DialogHeader";
|
|
272
|
+
var DialogFooter = ({
|
|
273
|
+
className,
|
|
274
|
+
...props
|
|
275
|
+
}) => /* @__PURE__ */ jsx4(
|
|
276
|
+
"div",
|
|
277
|
+
{
|
|
278
|
+
"data-slot": "dialog-footer",
|
|
279
|
+
className: cn(
|
|
280
|
+
"flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2",
|
|
281
|
+
// Ruling 3/4 — sticky to the card edges so a scrolled body never shows
|
|
282
|
+
// through under the actions, and safe-area padded on a phone.
|
|
283
|
+
//
|
|
284
|
+
// 🚨 THERE IS NO `mb-[…]` HERE, AND THERE MUST NEVER BE ONE AGAIN
|
|
285
|
+
// (0.11.3, feedback bf8fb00b). The bleed over the card's bottom padding
|
|
286
|
+
// used to be a NEGATIVE BLOCK MARGIN, which makes this box taller than
|
|
287
|
+
// the grid area the card allocates it — and `position: sticky` CLAMPS a
|
|
288
|
+
// box to its containing block, so the browser yanked the footer UP by
|
|
289
|
+
// `--dialog-pad` against the card's `gap-4` and painted 8px of opaque
|
|
290
|
+
// footer over the last line of the consequence, at every length. The
|
|
291
|
+
// card now RESERVES this footer's block size instead (`matrx-dialog-card`
|
|
292
|
+
// in styles.css hands over its `padding-bottom`), so the bleed costs no
|
|
293
|
+
// margin at all. Guard: `sticky-footer-geometry.test.tsx`.
|
|
294
|
+
//
|
|
295
|
+
// These stay UTILITIES rather than a shipped rule so a caller's
|
|
296
|
+
// `className` still wins through tailwind-merge — a shipped rule would
|
|
297
|
+
// have to be unlayered to survive Tailwind preflight (§ THE PREFLIGHT
|
|
298
|
+
// RULE in styles.css) and would then override every caller's padding.
|
|
299
|
+
"sticky bottom-0 z-10 bg-background pt-3",
|
|
300
|
+
"mx-[calc(var(--dialog-pad,0px)*-1)] px-[var(--dialog-pad,0px)] pb-[var(--dialog-pad,0px)]",
|
|
301
|
+
className
|
|
302
|
+
),
|
|
303
|
+
...props
|
|
304
|
+
}
|
|
305
|
+
);
|
|
306
|
+
DialogFooter.displayName = "DialogFooter";
|
|
307
|
+
var DialogTitle = React4.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx4(
|
|
308
|
+
DialogPrimitive.Title,
|
|
309
|
+
{
|
|
310
|
+
ref,
|
|
311
|
+
"data-slot": "dialog-title",
|
|
312
|
+
className: cn("text-lg font-semibold leading-none tracking-tight", className),
|
|
313
|
+
...props
|
|
314
|
+
}
|
|
315
|
+
));
|
|
316
|
+
DialogTitle.displayName = DialogPrimitive.Title.displayName;
|
|
317
|
+
var DialogDescription = React4.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx4(
|
|
318
|
+
DialogPrimitive.Description,
|
|
319
|
+
{
|
|
320
|
+
ref,
|
|
321
|
+
"data-slot": "dialog-description",
|
|
322
|
+
className: cn("text-sm text-muted-foreground", className),
|
|
323
|
+
...props
|
|
324
|
+
}
|
|
325
|
+
));
|
|
326
|
+
DialogDescription.displayName = DialogPrimitive.Description.displayName;
|
|
327
|
+
|
|
328
|
+
// src/data-table/default-host.tsx
|
|
329
|
+
import { jsx as jsx5, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
330
|
+
function DefaultTableWindow({ title, onClose, width = 720, height = 560, actionsRight, sidebar, children }) {
|
|
331
|
+
return /* @__PURE__ */ jsx5(Dialog, { open: true, onOpenChange: (open) => {
|
|
332
|
+
if (!open) onClose();
|
|
333
|
+
}, children: /* @__PURE__ */ jsxs3(DialogContent, { className: "flex flex-col overflow-hidden", style: { width: `min(${width}px, calc(100vw - 2rem))`, height: `min(${height}px, 85dvh)` }, children: [
|
|
334
|
+
/* @__PURE__ */ jsxs3(DialogHeader, { children: [
|
|
335
|
+
/* @__PURE__ */ jsx5(DialogTitle, { children: title }),
|
|
336
|
+
actionsRight
|
|
337
|
+
] }),
|
|
338
|
+
/* @__PURE__ */ jsxs3("div", { className: "flex min-h-0 flex-1", children: [
|
|
339
|
+
sidebar ? /* @__PURE__ */ jsx5("aside", { className: "shrink-0 border-r pr-2", children: sidebar }) : null,
|
|
340
|
+
/* @__PURE__ */ jsx5("div", { className: "min-h-0 min-w-0 flex-1 overflow-auto", children })
|
|
341
|
+
] })
|
|
342
|
+
] }) });
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
// src/data-table/icons.tsx
|
|
346
|
+
import { jsx as jsx6, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
347
|
+
function Icon({ children, ...props }) {
|
|
348
|
+
return /* @__PURE__ */ jsx6("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", "aria-hidden": "true", ...props, children });
|
|
349
|
+
}
|
|
350
|
+
var Check = (p) => /* @__PURE__ */ jsx6(Icon, { ...p, children: /* @__PURE__ */ jsx6("path", { d: "m5 12 4 4L19 6" }) });
|
|
351
|
+
var SlidersHorizontal = (p) => /* @__PURE__ */ jsx6(Icon, { ...p, children: /* @__PURE__ */ jsx6("path", { d: "M4 7h16M4 17h16M9 7v0M15 17v0" }) });
|
|
352
|
+
var Pencil = (p) => /* @__PURE__ */ jsx6(Icon, { ...p, children: /* @__PURE__ */ jsx6("path", { d: "m4 20 4-1 11-11-3-3L5 16l-1 4z" }) });
|
|
353
|
+
var Eye = (p) => /* @__PURE__ */ jsxs4(Icon, { ...p, children: [
|
|
354
|
+
/* @__PURE__ */ jsx6("path", { d: "M2 12s3-6 10-6 10 6 10 6-3 6-10 6S2 12 2 12z" }),
|
|
355
|
+
/* @__PURE__ */ jsx6("circle", { cx: "12", cy: "12", r: "2" })
|
|
356
|
+
] });
|
|
357
|
+
var PanelRight = (p) => /* @__PURE__ */ jsxs4(Icon, { ...p, children: [
|
|
358
|
+
/* @__PURE__ */ jsx6("rect", { x: "3", y: "4", width: "18", height: "16", rx: "2" }),
|
|
359
|
+
/* @__PURE__ */ jsx6("path", { d: "M15 4v16" })
|
|
360
|
+
] });
|
|
361
|
+
var ExternalLink = (p) => /* @__PURE__ */ jsx6(Icon, { ...p, children: /* @__PURE__ */ jsx6("path", { d: "M14 4h6v6M20 4l-9 9M19 13v6H5V5h6" }) });
|
|
362
|
+
var Copy = (p) => /* @__PURE__ */ jsxs4(Icon, { ...p, children: [
|
|
363
|
+
/* @__PURE__ */ jsx6("rect", { x: "9", y: "9", width: "11", height: "11", rx: "2" }),
|
|
364
|
+
/* @__PURE__ */ jsx6("path", { d: "M15 9V6a2 2 0 0 0-2-2H6a2 2 0 0 0-2 2v7a2 2 0 0 0 2 2h3" })
|
|
365
|
+
] });
|
|
366
|
+
var Lightbulb = (p) => /* @__PURE__ */ jsx6(Icon, { ...p, children: /* @__PURE__ */ jsx6("path", { d: "M9 18h6M10 22h4M8 14a6 6 0 1 1 8 0c-1 1-1 2-1 3H9c0-1 0-2-1-3z" }) });
|
|
367
|
+
var ShieldOff = (p) => /* @__PURE__ */ jsx6(Icon, { ...p, children: /* @__PURE__ */ jsx6("path", { d: "m3 3 18 18M12 3l7 3v5c0 4-3 7-7 9-4-2-7-5-7-9V6l4-2" }) });
|
|
368
|
+
|
|
369
|
+
// src/data-table/host.tsx
|
|
370
|
+
import { createContext as createContext4, useContext as useContext4 } from "react";
|
|
371
|
+
import { jsx as jsx7 } from "react/jsx-runtime";
|
|
372
|
+
var HOST_SLOT = /* @__PURE__ */ Symbol.for("ai-matrx.design-system.table-host-context");
|
|
373
|
+
var hostRegistry = globalThis;
|
|
374
|
+
var HostContext = hostRegistry[HOST_SLOT] ??= createContext4({});
|
|
375
|
+
function useMatrxDataTableHost() {
|
|
376
|
+
return useContext4(HostContext);
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
// src/data-table/copy.tsx
|
|
380
|
+
import { useState as useState4 } from "react";
|
|
381
|
+
import { useClipboard } from "@ai-matrx/kit/clipboard";
|
|
382
|
+
|
|
383
|
+
// src/button.tsx
|
|
384
|
+
import { Slot } from "@radix-ui/react-slot";
|
|
385
|
+
import { cva } from "class-variance-authority";
|
|
386
|
+
import * as React5 from "react";
|
|
387
|
+
import { jsx as jsx8 } from "react/jsx-runtime";
|
|
388
|
+
var buttonVariants = cva(
|
|
389
|
+
"inline-flex cursor-pointer items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-all duration-200 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-0 disabled:pointer-events-none disabled:opacity-50 active:scale-[0.98] [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0",
|
|
390
|
+
{
|
|
391
|
+
variants: {
|
|
392
|
+
variant: {
|
|
393
|
+
default: "bg-primary text-primary-foreground shadow-sm hover:bg-primary/90",
|
|
394
|
+
/**
|
|
395
|
+
* An explicit spelling of `default`, identical by construction. It is
|
|
396
|
+
* not decoration: matrx-frontend's `ButtonMine` fork carried it, ~17
|
|
397
|
+
* call sites say `variant="primary"`, and the alternative to keeping
|
|
398
|
+
* the word was a host shim translating a prop — the massage C22 bans.
|
|
399
|
+
*/
|
|
400
|
+
primary: "bg-primary text-primary-foreground shadow-sm hover:bg-primary/90",
|
|
401
|
+
destructive: "bg-destructive text-destructive-foreground shadow-sm hover:bg-destructive/90",
|
|
402
|
+
/** The affirmative twin of `destructive` — confirm, approve, publish. */
|
|
403
|
+
success: "bg-success text-success-foreground shadow-sm hover:bg-success/90",
|
|
404
|
+
outline: "border border-border bg-card shadow-sm hover:bg-accent hover:text-accent-foreground",
|
|
405
|
+
secondary: "bg-secondary text-secondary-foreground shadow-sm hover:bg-secondary/80",
|
|
406
|
+
ghost: "hover:bg-accent hover:text-accent-foreground",
|
|
407
|
+
link: "text-primary underline-offset-4 hover:underline",
|
|
408
|
+
subtle: "border border-transparent bg-muted/50 text-foreground hover:border-border hover:bg-muted"
|
|
409
|
+
},
|
|
410
|
+
/**
|
|
411
|
+
* ONE size scale, xs → 3xl, plus the two icon boxes. The top and bottom
|
|
412
|
+
* of the scale came in from matrx-frontend's `ButtonMine` fork (2026-09-07):
|
|
413
|
+
* a dense toolbar needs `xs`, and a marketing/hero call to action needs
|
|
414
|
+
* `xl`+. A host that has to reach outside this scale forks the button,
|
|
415
|
+
* which is exactly how `ButtonMine` was born.
|
|
416
|
+
*/
|
|
417
|
+
size: {
|
|
418
|
+
xs: "h-6 rounded-md px-1 text-xs",
|
|
419
|
+
sm: "h-8 rounded-md px-3 text-xs",
|
|
420
|
+
default: "h-9 px-4 py-2",
|
|
421
|
+
/** An explicit spelling of `default` (the fork's middle rung). */
|
|
422
|
+
md: "h-9 px-4 py-2",
|
|
423
|
+
lg: "h-10 rounded-md px-6",
|
|
424
|
+
xl: "h-12 px-8 py-3 text-base",
|
|
425
|
+
"2xl": "h-14 px-10 py-3 text-base",
|
|
426
|
+
"3xl": "h-16 px-12 py-3 text-base",
|
|
427
|
+
icon: "h-9 w-9",
|
|
428
|
+
"icon-sm": "h-7 w-7",
|
|
429
|
+
/** A circular icon box — an avatar-shaped control, not a square one. */
|
|
430
|
+
roundIcon: "h-8 w-8 rounded-full p-0"
|
|
431
|
+
}
|
|
432
|
+
},
|
|
433
|
+
defaultVariants: {
|
|
434
|
+
variant: "default",
|
|
435
|
+
size: "default"
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
);
|
|
439
|
+
var Button = React5.forwardRef(
|
|
440
|
+
({ className, variant, size, asChild = false, ...props }, ref) => {
|
|
441
|
+
const Component = asChild ? Slot : "button";
|
|
442
|
+
return /* @__PURE__ */ jsx8(
|
|
443
|
+
Component,
|
|
444
|
+
{
|
|
445
|
+
className: cn(buttonVariants({ variant, size, className })),
|
|
446
|
+
ref,
|
|
447
|
+
...props
|
|
448
|
+
}
|
|
449
|
+
);
|
|
450
|
+
}
|
|
451
|
+
);
|
|
452
|
+
Button.displayName = "Button";
|
|
453
|
+
|
|
454
|
+
// src/popover.tsx
|
|
455
|
+
import * as PopoverPrimitive from "@radix-ui/react-popover";
|
|
456
|
+
import * as React6 from "react";
|
|
457
|
+
import { jsx as jsx9 } from "react/jsx-runtime";
|
|
458
|
+
var Popover = PopoverPrimitive.Root;
|
|
459
|
+
var PopoverTrigger = PopoverPrimitive.Trigger;
|
|
460
|
+
var PopoverContent = React6.forwardRef(
|
|
461
|
+
({
|
|
462
|
+
className,
|
|
463
|
+
align = "center",
|
|
464
|
+
sideOffset = 4,
|
|
465
|
+
container,
|
|
466
|
+
animated = true,
|
|
467
|
+
...props
|
|
468
|
+
}, ref) => {
|
|
469
|
+
const portalContainer = usePortalContainer(container);
|
|
470
|
+
return /* @__PURE__ */ jsx9(PopoverPrimitive.Portal, { container: portalContainer, children: /* @__PURE__ */ jsx9(
|
|
471
|
+
PopoverPrimitive.Content,
|
|
472
|
+
{
|
|
473
|
+
ref,
|
|
474
|
+
align,
|
|
475
|
+
sideOffset,
|
|
476
|
+
className: cn(
|
|
477
|
+
// Cap to the viewport space Radix measured and scroll — a popover taller
|
|
478
|
+
// than the screen must never trap the user with unreachable content.
|
|
479
|
+
// z must EQUAL the dialog layer (z-[10000]), never exceed it: with equal
|
|
480
|
+
// z, DOM portal order decides, so a dialog opened FROM a popover stacks
|
|
481
|
+
// above it, and a popover opened from a dialog still stacks above the
|
|
482
|
+
// dialog. z-[10001] buried dialogs behind fullscreen popovers.
|
|
483
|
+
"z-[10000] w-72 max-h-[var(--radix-popover-content-available-height)] overflow-y-auto overscroll-contain rounded-md border bg-popover p-4 text-popover-foreground shadow-md outline-none",
|
|
484
|
+
animated && MOTION_POPPER,
|
|
485
|
+
className
|
|
486
|
+
),
|
|
487
|
+
...props
|
|
488
|
+
}
|
|
489
|
+
) });
|
|
490
|
+
}
|
|
491
|
+
);
|
|
492
|
+
PopoverContent.displayName = PopoverPrimitive.Content.displayName;
|
|
493
|
+
|
|
494
|
+
// src/data-table/useTableNotice.tsx
|
|
495
|
+
import { useState as useState3 } from "react";
|
|
496
|
+
import { jsx as jsx10, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
497
|
+
function useTableNotice() {
|
|
498
|
+
const host = useMatrxDataTableHost();
|
|
499
|
+
const [message, setMessage] = useState3(null);
|
|
500
|
+
const notify = {
|
|
501
|
+
success(text) {
|
|
502
|
+
if (host.notify) host.notify.success(text);
|
|
503
|
+
else setMessage({ text, error: false });
|
|
504
|
+
},
|
|
505
|
+
error(text) {
|
|
506
|
+
if (host.notify) host.notify.error(text);
|
|
507
|
+
else setMessage({ text, error: true });
|
|
508
|
+
}
|
|
509
|
+
};
|
|
510
|
+
const notice = message ? /* @__PURE__ */ jsxs5("span", { role: message.error ? "alert" : "status", className: message.error ? "text-xs text-destructive" : "text-xs text-muted-foreground", children: [
|
|
511
|
+
message.text,
|
|
512
|
+
/* @__PURE__ */ jsx10("button", { type: "button", "aria-label": "Dismiss notification", className: "ml-2 underline", onClick: () => setMessage(null), children: "Dismiss" })
|
|
513
|
+
] }) : null;
|
|
514
|
+
return { notify, notice };
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
// src/data-table/copy-payload.ts
|
|
518
|
+
function tableAgentText(value) {
|
|
519
|
+
if (typeof value === "string") return value;
|
|
520
|
+
const attributes = Object.entries(value.attributes ?? {}).filter(([, entry]) => entry !== null && entry !== void 0 && entry !== "").map(([key, entry]) => ` ${key}="${String(entry)}"`).join("");
|
|
521
|
+
const context = Object.entries(value.context ?? {}).filter(([, entry]) => entry !== null && entry !== void 0 && entry !== "").map(([key, entry]) => `<${key}>${String(entry)}</${key}>`).join("\n");
|
|
522
|
+
const url = typeof window === "undefined" ? "" : window.location.href;
|
|
523
|
+
const route = typeof window === "undefined" ? "" : window.location.pathname;
|
|
524
|
+
return `<${value.kind}${attributes}>
|
|
525
|
+
<context>
|
|
526
|
+
<location>${value.location}</location>
|
|
527
|
+
${url ? `<url>${url}</url>` : ""}
|
|
528
|
+
${route ? `<route>${route}</route>` : ""}
|
|
529
|
+
<copied>${value.description}</copied>
|
|
530
|
+
<copied-at>${(/* @__PURE__ */ new Date()).toISOString()}</copied-at>
|
|
531
|
+
${context}
|
|
532
|
+
</context>
|
|
533
|
+
${value.summary ? `<summary>
|
|
534
|
+
${value.summary}
|
|
535
|
+
</summary>
|
|
536
|
+
` : ""}<data format="json">
|
|
537
|
+
\`\`\`json
|
|
538
|
+
${JSON.stringify(value.data, null, 2)}
|
|
539
|
+
\`\`\`
|
|
540
|
+
</data>
|
|
541
|
+
</${value.kind}>`;
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
// src/data-table/copy.tsx
|
|
545
|
+
import { jsx as jsx11, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
546
|
+
function resolve(value) {
|
|
547
|
+
return typeof value === "function" ? value() : value;
|
|
548
|
+
}
|
|
549
|
+
function download(item) {
|
|
550
|
+
if (!item.build) throw new Error(`No export action is available for ${item.label}.`);
|
|
551
|
+
const file = item.build();
|
|
552
|
+
const url = URL.createObjectURL(new Blob([file.content], { type: file.mime }));
|
|
553
|
+
try {
|
|
554
|
+
const link = document.createElement("a");
|
|
555
|
+
link.href = url;
|
|
556
|
+
link.download = `${item.label.replace(/[^a-z0-9]+/gi, "-")}.${file.extension}`;
|
|
557
|
+
document.body.append(link);
|
|
558
|
+
link.click();
|
|
559
|
+
link.remove();
|
|
560
|
+
} finally {
|
|
561
|
+
URL.revokeObjectURL(url);
|
|
562
|
+
}
|
|
563
|
+
}
|
|
564
|
+
function CopyButtons(props) {
|
|
565
|
+
const { CopyControls } = useMatrxDataTableHost();
|
|
566
|
+
if (CopyControls) return /* @__PURE__ */ jsx11(CopyControls, { ...props });
|
|
567
|
+
return /* @__PURE__ */ jsx11(DefaultCopyButtons, { ...props });
|
|
568
|
+
}
|
|
569
|
+
function DefaultCopyButtons({ human, agent, json, label, size = "icon", appearance = "segmented", disabled = false, stopPropagation = true, className, aiVariants = [], agentVariant, aiCustom, export: exportConfig, hide }) {
|
|
570
|
+
const { notify, notice } = useTableNotice();
|
|
571
|
+
const clipboard = useClipboard({ notify: (message, kind) => notify[kind](message) });
|
|
572
|
+
const [busy, setBusy] = useState4(false);
|
|
573
|
+
const [modal, setModal] = useState4(null);
|
|
574
|
+
const run = async (operation) => {
|
|
575
|
+
if (busy) return;
|
|
576
|
+
setBusy(true);
|
|
577
|
+
try {
|
|
578
|
+
await operation();
|
|
579
|
+
} catch (error) {
|
|
580
|
+
notify.error(error instanceof Error ? error.message : String(error));
|
|
581
|
+
} finally {
|
|
582
|
+
setBusy(false);
|
|
583
|
+
}
|
|
584
|
+
};
|
|
585
|
+
const faithfulAgent = agent === void 0 ? [] : [{
|
|
586
|
+
id: agentVariant?.id ?? "copy-for-ai",
|
|
587
|
+
label: agentVariant?.label ?? "Copy for AI",
|
|
588
|
+
...agentVariant?.hint === void 0 ? {} : { hint: agentVariant.hint },
|
|
589
|
+
section: "ai",
|
|
590
|
+
build: () => resolve(agent)
|
|
591
|
+
}];
|
|
592
|
+
const orderedAgent = agentVariant?.position === "last" ? [...aiVariants, ...faithfulAgent] : [...faithfulAgent, ...aiVariants];
|
|
593
|
+
const variants = [
|
|
594
|
+
...json === void 0 ? [] : [{ id: "json", label: "Copy JSON", section: "copy", build: () => JSON.stringify(resolve(json), null, 2) }],
|
|
595
|
+
...orderedAgent
|
|
596
|
+
].filter((variant) => !hide?.includes(variant.section ?? "ai"));
|
|
597
|
+
const exports = hide?.includes("export") ? void 0 : exportConfig;
|
|
598
|
+
const showCustom = aiCustom && !hide?.includes("ai");
|
|
599
|
+
const buttonClass = `${size === "xs" ? "h-8 w-8 lg:h-5 lg:w-8" : ""} ${appearance === "bare" ? "border-0 bg-transparent shadow-none" : ""}`;
|
|
600
|
+
return /* @__PURE__ */ jsxs6("span", { className: `inline-flex flex-wrap items-center gap-0.5 ${className ?? ""}`, onClick: stopPropagation ? (event) => event.stopPropagation() : void 0, children: [
|
|
601
|
+
human !== void 0 && !hide?.includes("copy") ? /* @__PURE__ */ jsx11(Button, { type: "button", variant: "ghost", size: "icon", className: buttonClass, disabled: disabled || busy, "aria-label": `Copy ${label}`, onClick: () => void run(() => clipboard.copyText(resolve(human), `${label} copied`)), children: /* @__PURE__ */ jsx11(Copy, { className: "h-3.5 w-3.5" }) }) : null,
|
|
602
|
+
variants.length > 0 || showCustom || exports ? /* @__PURE__ */ jsxs6(Popover, { children: [
|
|
603
|
+
/* @__PURE__ */ jsx11(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsx11(Button, { type: "button", variant: "ghost", size: "icon", className: buttonClass, disabled: disabled || busy, "aria-label": `Copy ${label} for AI or export`, children: /* @__PURE__ */ jsx11(SlidersHorizontal, { className: "h-3.5 w-3.5" }) }) }),
|
|
604
|
+
/* @__PURE__ */ jsxs6(PopoverContent, { align: "end", className: "w-64 p-1", children: [
|
|
605
|
+
variants.map((variant) => /* @__PURE__ */ jsx11(Button, { type: "button", variant: "ghost", size: "sm", className: "w-full justify-start", disabled: busy, onClick: () => void run(async () => {
|
|
606
|
+
if (variant.onSelect) variant.onSelect();
|
|
607
|
+
else if (variant.modal) setModal(variant.id);
|
|
608
|
+
else if (variant.build) await clipboard.copyText(tableAgentText(await variant.build()), variant.successMessage ?? `${label} copied`);
|
|
609
|
+
else throw new Error(`No copy action is available for ${variant.label}.`);
|
|
610
|
+
}), children: variant.label }, variant.id)),
|
|
611
|
+
showCustom ? /* @__PURE__ */ jsx11(Button, { type: "button", variant: "ghost", size: "sm", className: "w-full justify-start", onClick: () => setModal("custom"), children: aiCustom.label ?? "Custom copy\u2026" }) : null,
|
|
612
|
+
exports?.items.map((item) => /* @__PURE__ */ jsx11(Button, { type: "button", variant: "ghost", size: "sm", className: "w-full justify-start", disabled: busy, onClick: () => void run(async () => {
|
|
613
|
+
if (item.onSelect) await item.onSelect();
|
|
614
|
+
else download(item);
|
|
615
|
+
}), children: item.label }, item.id)),
|
|
616
|
+
exports?.sheetRows ? /* @__PURE__ */ jsx11(Button, { type: "button", variant: "ghost", size: "sm", className: "w-full justify-start", onClick: () => void run(async () => {
|
|
617
|
+
const rows = exports.sheetRows?.();
|
|
618
|
+
if (!rows) return;
|
|
619
|
+
const keys = [...new Set(rows.flatMap((row) => Object.keys(row)))];
|
|
620
|
+
const value = (input) => String(input ?? "").replace(/[\t\r\n]+/g, " ");
|
|
621
|
+
await clipboard.copyText([keys.map(value).join(" "), ...rows.map((row) => keys.map((key) => value(row[key])).join(" "))].join("\n"), "Copied for pasting into Google Sheets");
|
|
622
|
+
}), children: "Copy for Google Sheets" }) : null
|
|
623
|
+
] })
|
|
624
|
+
] }) : null,
|
|
625
|
+
variants.map((variant) => variant.modal && modal === variant.id ? /* @__PURE__ */ jsx11("span", { children: variant.modal({ open: true, onOpenChange: (open) => setModal(open ? variant.id : null) }) }, variant.id) : null),
|
|
626
|
+
modal === "custom" && aiCustom ? /* @__PURE__ */ jsx11(CustomCopy, { source: aiCustom, onClose: () => setModal(null) }) : null,
|
|
627
|
+
notice
|
|
628
|
+
] });
|
|
629
|
+
}
|
|
630
|
+
function CustomCopy({ source, onClose }) {
|
|
631
|
+
const [values, setValues] = useState4(() => Object.fromEntries(source.options.map((option) => [option.key, option.default])));
|
|
632
|
+
const { notify, notice } = useTableNotice();
|
|
633
|
+
const clipboard = useClipboard({ notify: (message, kind) => notify[kind](message) });
|
|
634
|
+
let text = "";
|
|
635
|
+
let error = null;
|
|
636
|
+
try {
|
|
637
|
+
const built = source.build(values);
|
|
638
|
+
text = tableAgentText(source.wrap(built.text, values, built.meta));
|
|
639
|
+
} catch (raw) {
|
|
640
|
+
error = raw instanceof Error ? raw.message : String(raw);
|
|
641
|
+
}
|
|
642
|
+
return /* @__PURE__ */ jsx11(Dialog, { open: true, onOpenChange: (open) => {
|
|
643
|
+
if (!open) onClose();
|
|
644
|
+
}, children: /* @__PURE__ */ jsxs6(DialogContent, { children: [
|
|
645
|
+
/* @__PURE__ */ jsx11(DialogHeader, { children: /* @__PURE__ */ jsx11(DialogTitle, { children: source.dialogTitle ?? "Custom copy" }) }),
|
|
646
|
+
source.dialogDescription ? /* @__PURE__ */ jsx11("p", { className: "text-sm", children: source.dialogDescription }) : null,
|
|
647
|
+
source.options.map((option) => /* @__PURE__ */ jsxs6("label", { className: "flex items-center justify-between gap-3 text-sm", children: [
|
|
648
|
+
option.label,
|
|
649
|
+
option.kind === "toggle" ? /* @__PURE__ */ jsx11("input", { type: "checkbox", checked: values[option.key] === true, onChange: (e) => setValues({ ...values, [option.key]: e.target.checked }) }) : option.kind === "preset" ? /* @__PURE__ */ jsx11("select", { "aria-label": option.label, value: Number(values[option.key]), onChange: (e) => setValues({ ...values, [option.key]: Number(e.target.value) }), children: option.options.map((choice) => /* @__PURE__ */ jsx11("option", { value: choice.value, children: choice.label }, choice.value)) }) : option.kind === "slider" ? /* @__PURE__ */ jsx11("input", { "aria-label": option.label, type: "range", min: 0, max: option.stops.length - 1, step: 1, value: Math.max(0, option.stops.indexOf(Number(values[option.key]))), onChange: (e) => setValues({ ...values, [option.key]: option.stops[Number(e.target.value)] ?? option.default }) }) : /* @__PURE__ */ jsxs6("span", { className: "flex gap-1", children: [
|
|
650
|
+
option.presets?.map((choice) => /* @__PURE__ */ jsx11(Button, { type: "button", size: "sm", variant: "outline", onClick: () => setValues({ ...values, [option.key]: choice.value }), children: choice.label }, choice.value)),
|
|
651
|
+
/* @__PURE__ */ jsx11("input", { "aria-label": option.label, type: "number", value: Number(values[option.key]), min: option.min, step: option.step, onChange: (e) => {
|
|
652
|
+
if (Number.isFinite(e.target.valueAsNumber)) setValues({ ...values, [option.key]: e.target.valueAsNumber });
|
|
653
|
+
} })
|
|
654
|
+
] })
|
|
655
|
+
] }, option.key)),
|
|
656
|
+
error ? /* @__PURE__ */ jsx11("p", { role: "alert", className: "text-destructive", children: error }) : /* @__PURE__ */ jsx11("pre", { className: "max-h-48 overflow-auto whitespace-pre-wrap text-xs", children: text }),
|
|
657
|
+
notice,
|
|
658
|
+
/* @__PURE__ */ jsxs6(DialogFooter, { children: [
|
|
659
|
+
/* @__PURE__ */ jsx11(Button, { type: "button", variant: "outline", onClick: onClose, children: "Close" }),
|
|
660
|
+
/* @__PURE__ */ jsx11(Button, { type: "button", disabled: error !== null, onClick: () => void clipboard.copyText(text, "Custom copy copied"), children: "Copy" })
|
|
661
|
+
] })
|
|
662
|
+
] }) });
|
|
663
|
+
}
|
|
664
|
+
|
|
665
|
+
// src/data-table/MatrxUuidCell.tsx
|
|
666
|
+
import {
|
|
667
|
+
useEffect as useEffect2,
|
|
668
|
+
useRef,
|
|
669
|
+
useState as useState5
|
|
670
|
+
} from "react";
|
|
671
|
+
|
|
672
|
+
// src/tooltip.tsx
|
|
673
|
+
import * as TooltipPrimitive from "@radix-ui/react-tooltip";
|
|
674
|
+
import * as React7 from "react";
|
|
675
|
+
import { jsx as jsx12 } from "react/jsx-runtime";
|
|
676
|
+
var TooltipProvider = TooltipPrimitive.Provider;
|
|
677
|
+
var Tooltip = TooltipPrimitive.Root;
|
|
678
|
+
var TooltipTrigger = TooltipPrimitive.Trigger;
|
|
679
|
+
var TooltipContent = React7.forwardRef(({ className, sideOffset = 4, container, animated = true, ...props }, ref) => {
|
|
680
|
+
const resolved = usePortalContainer(container);
|
|
681
|
+
return /* @__PURE__ */ jsx12(TooltipPrimitive.Portal, { container: resolved ?? null, children: /* @__PURE__ */ jsx12(
|
|
682
|
+
TooltipPrimitive.Content,
|
|
683
|
+
{
|
|
684
|
+
ref,
|
|
685
|
+
"data-slot": "tooltip-content",
|
|
686
|
+
sideOffset,
|
|
687
|
+
className: cn(
|
|
688
|
+
"z-[10001] overflow-hidden rounded-md border border-border bg-popover px-3 py-1.5 text-xs text-popover-foreground shadow-md",
|
|
689
|
+
animated && MOTION_POPPER,
|
|
690
|
+
className
|
|
691
|
+
),
|
|
692
|
+
...props
|
|
693
|
+
}
|
|
694
|
+
) });
|
|
695
|
+
});
|
|
696
|
+
TooltipContent.displayName = TooltipPrimitive.Content.displayName;
|
|
697
|
+
|
|
698
|
+
// src/data-table/MatrxUuidCell.tsx
|
|
699
|
+
import { jsx as jsx13, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
700
|
+
var SHORT_LEN = 8;
|
|
701
|
+
function shortUuid(id, len = SHORT_LEN) {
|
|
702
|
+
return id.slice(0, len);
|
|
703
|
+
}
|
|
704
|
+
async function copyText(text) {
|
|
705
|
+
try {
|
|
706
|
+
if (navigator.clipboard?.writeText) {
|
|
707
|
+
await navigator.clipboard.writeText(text);
|
|
708
|
+
return true;
|
|
709
|
+
}
|
|
710
|
+
} catch {
|
|
711
|
+
}
|
|
712
|
+
try {
|
|
713
|
+
const ta = document.createElement("textarea");
|
|
714
|
+
ta.value = text;
|
|
715
|
+
ta.style.position = "fixed";
|
|
716
|
+
ta.style.opacity = "0";
|
|
717
|
+
document.body.appendChild(ta);
|
|
718
|
+
ta.select();
|
|
719
|
+
const ok = document.execCommand("copy");
|
|
720
|
+
document.body.removeChild(ta);
|
|
721
|
+
return ok;
|
|
722
|
+
} catch {
|
|
723
|
+
return false;
|
|
724
|
+
}
|
|
725
|
+
}
|
|
726
|
+
function MatrxUuidCell({
|
|
727
|
+
value,
|
|
728
|
+
label,
|
|
729
|
+
onOpen,
|
|
730
|
+
href,
|
|
731
|
+
token,
|
|
732
|
+
forbidden = false,
|
|
733
|
+
className,
|
|
734
|
+
trailing
|
|
735
|
+
}) {
|
|
736
|
+
const { resolveEntityDoors, ResourcePeek } = useMatrxDataTableHost();
|
|
737
|
+
const { notify, notice } = useTableNotice();
|
|
738
|
+
const [copied, setCopied] = useState5(false);
|
|
739
|
+
const [opening, setOpening] = useState5(false);
|
|
740
|
+
const [peekOpen, setPeekOpen] = useState5(false);
|
|
741
|
+
const timer = useRef(null);
|
|
742
|
+
useEffect2(
|
|
743
|
+
() => () => {
|
|
744
|
+
if (timer.current) clearTimeout(timer.current);
|
|
745
|
+
},
|
|
746
|
+
[]
|
|
747
|
+
);
|
|
748
|
+
if (!value) {
|
|
749
|
+
return /* @__PURE__ */ jsx13("span", { className: "text-muted-foreground/40", children: "\u2014" });
|
|
750
|
+
}
|
|
751
|
+
const doors = token ? resolveEntityDoors?.(token, value, href) ?? null : null;
|
|
752
|
+
const resolvedHref = doors?.href ?? href ?? null;
|
|
753
|
+
const canPeek = Boolean(doors?.canPeek) && !forbidden;
|
|
754
|
+
const display = shortUuid(value);
|
|
755
|
+
const tip = label ? `${label}: ${value}` : value;
|
|
756
|
+
const stop = (e) => {
|
|
757
|
+
e.stopPropagation();
|
|
758
|
+
};
|
|
759
|
+
const onCopy = (e) => {
|
|
760
|
+
stop(e);
|
|
761
|
+
e.preventDefault();
|
|
762
|
+
void copyText(value).then((ok) => {
|
|
763
|
+
if (!ok) {
|
|
764
|
+
notify?.error("Couldn't copy ID");
|
|
765
|
+
return;
|
|
766
|
+
}
|
|
767
|
+
setCopied(true);
|
|
768
|
+
if (timer.current) clearTimeout(timer.current);
|
|
769
|
+
timer.current = setTimeout(() => setCopied(false), 1500);
|
|
770
|
+
});
|
|
771
|
+
};
|
|
772
|
+
const handleOpen = async (e) => {
|
|
773
|
+
stop(e);
|
|
774
|
+
e.preventDefault();
|
|
775
|
+
if (!onOpen || forbidden || opening) return;
|
|
776
|
+
setOpening(true);
|
|
777
|
+
try {
|
|
778
|
+
const result = await onOpen(value);
|
|
779
|
+
if (result === "forbidden") {
|
|
780
|
+
notify?.error("You don't have access to open this record");
|
|
781
|
+
}
|
|
782
|
+
} catch (err) {
|
|
783
|
+
notify?.error(
|
|
784
|
+
`Couldn't open: ${err instanceof Error ? err.message : String(err)}`
|
|
785
|
+
);
|
|
786
|
+
} finally {
|
|
787
|
+
setOpening(false);
|
|
788
|
+
}
|
|
789
|
+
};
|
|
790
|
+
const idNode = (() => {
|
|
791
|
+
if (forbidden) {
|
|
792
|
+
return /* @__PURE__ */ jsxs7("span", { className: "inline-flex min-w-0 items-center gap-1 font-mono text-xs text-muted-foreground/70 tabular-nums", children: [
|
|
793
|
+
/* @__PURE__ */ jsx13("span", { className: "min-w-0 truncate", children: display }),
|
|
794
|
+
/* @__PURE__ */ jsx13(ShieldOff, { className: "h-3 w-3 shrink-0 opacity-60", "aria-hidden": true })
|
|
795
|
+
] });
|
|
796
|
+
}
|
|
797
|
+
if (onOpen) {
|
|
798
|
+
return /* @__PURE__ */ jsxs7(
|
|
799
|
+
"button",
|
|
800
|
+
{
|
|
801
|
+
type: "button",
|
|
802
|
+
"aria-label": `Open ${label ?? "record"} ${display}`,
|
|
803
|
+
onClick: (e) => void handleOpen(e),
|
|
804
|
+
disabled: opening,
|
|
805
|
+
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",
|
|
806
|
+
children: [
|
|
807
|
+
/* @__PURE__ */ jsx13("span", { className: "min-w-0 truncate tabular-nums", children: display }),
|
|
808
|
+
/* @__PURE__ */ jsx13(PanelRight, { className: "h-3 w-3 shrink-0 opacity-50" })
|
|
809
|
+
]
|
|
810
|
+
}
|
|
811
|
+
);
|
|
812
|
+
}
|
|
813
|
+
if (resolvedHref) {
|
|
814
|
+
return /* @__PURE__ */ jsxs7(
|
|
815
|
+
"a",
|
|
816
|
+
{
|
|
817
|
+
href: resolvedHref,
|
|
818
|
+
target: "_blank",
|
|
819
|
+
rel: "noopener noreferrer",
|
|
820
|
+
"aria-label": `Open ${label ?? "record"} ${display} in a new tab`,
|
|
821
|
+
onClick: stop,
|
|
822
|
+
className: "inline-flex min-w-0 items-center gap-1 font-mono text-xs text-sky-600 hover:underline dark:text-sky-400",
|
|
823
|
+
children: [
|
|
824
|
+
/* @__PURE__ */ jsx13("span", { className: "min-w-0 truncate tabular-nums", children: display }),
|
|
825
|
+
/* @__PURE__ */ jsx13(ExternalLink, { className: "h-3 w-3 shrink-0 opacity-50" })
|
|
826
|
+
]
|
|
827
|
+
}
|
|
828
|
+
);
|
|
829
|
+
}
|
|
830
|
+
return /* @__PURE__ */ jsx13("span", { className: "min-w-0 truncate font-mono text-xs text-muted-foreground tabular-nums select-all", children: display });
|
|
831
|
+
})();
|
|
832
|
+
return /* @__PURE__ */ jsxs7(
|
|
833
|
+
"span",
|
|
834
|
+
{
|
|
835
|
+
"data-matrx-cell-control": true,
|
|
836
|
+
className: cn(
|
|
837
|
+
"inline-flex min-w-0 max-w-full items-center gap-0.5 whitespace-nowrap",
|
|
838
|
+
className
|
|
839
|
+
),
|
|
840
|
+
onClick: stop,
|
|
841
|
+
children: [
|
|
842
|
+
/* @__PURE__ */ jsx13(TooltipProvider, { delayDuration: 200, children: /* @__PURE__ */ jsxs7(Tooltip, { children: [
|
|
843
|
+
/* @__PURE__ */ jsx13(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsx13("span", { className: "inline-flex min-w-0 max-w-full items-center", children: idNode }) }),
|
|
844
|
+
/* @__PURE__ */ jsx13(TooltipContent, { side: "top", className: "max-w-sm break-all font-mono", children: forbidden ? `No access \xB7 ${tip}` : tip })
|
|
845
|
+
] }) }),
|
|
846
|
+
/* @__PURE__ */ jsx13(
|
|
847
|
+
"button",
|
|
848
|
+
{
|
|
849
|
+
type: "button",
|
|
850
|
+
onClick: onCopy,
|
|
851
|
+
"aria-label": copied ? "Copied" : `Copy ${label ?? "id"}`,
|
|
852
|
+
title: `Copy ${label ?? "id"}: ${value}`,
|
|
853
|
+
className: cn(
|
|
854
|
+
"inline-flex h-4 w-4 shrink-0 items-center justify-center rounded text-muted-foreground transition-colors hover:text-foreground focus:outline-none",
|
|
855
|
+
copied && "text-primary"
|
|
856
|
+
),
|
|
857
|
+
children: copied ? /* @__PURE__ */ jsx13(Check, { className: "h-3 w-3" }) : /* @__PURE__ */ jsx13(Copy, { className: "h-3 w-3" })
|
|
858
|
+
}
|
|
859
|
+
),
|
|
860
|
+
canPeek ? /* @__PURE__ */ jsx13(
|
|
861
|
+
"button",
|
|
862
|
+
{
|
|
863
|
+
type: "button",
|
|
864
|
+
onClick: (e) => {
|
|
865
|
+
stop(e);
|
|
866
|
+
setPeekOpen(true);
|
|
867
|
+
},
|
|
868
|
+
"aria-label": `Quick look at ${label ?? "record"}`,
|
|
869
|
+
title: `Quick look at ${label ?? "record"}`,
|
|
870
|
+
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",
|
|
871
|
+
children: /* @__PURE__ */ jsx13(Lightbulb, { className: "h-3 w-3" })
|
|
872
|
+
}
|
|
873
|
+
) : null,
|
|
874
|
+
onOpen && resolvedHref && !forbidden ? /* @__PURE__ */ jsx13(
|
|
875
|
+
"a",
|
|
876
|
+
{
|
|
877
|
+
href: resolvedHref,
|
|
878
|
+
target: "_blank",
|
|
879
|
+
rel: "noopener noreferrer",
|
|
880
|
+
onClick: stop,
|
|
881
|
+
"aria-label": `Open ${label ?? "record"} in a new tab`,
|
|
882
|
+
title: `Open ${label ?? "record"} in a new tab`,
|
|
883
|
+
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",
|
|
884
|
+
children: /* @__PURE__ */ jsx13(ExternalLink, { className: "h-3 w-3" })
|
|
885
|
+
}
|
|
886
|
+
) : null,
|
|
887
|
+
trailing,
|
|
888
|
+
notice,
|
|
889
|
+
canPeek && peekOpen && ResourcePeek ? /* @__PURE__ */ jsx13(
|
|
890
|
+
ResourcePeek,
|
|
891
|
+
{
|
|
892
|
+
kind: doors?.peekKind ?? token ?? "record",
|
|
893
|
+
id: value,
|
|
894
|
+
onClose: () => setPeekOpen(false),
|
|
895
|
+
...href == null ? {} : { href }
|
|
896
|
+
}
|
|
897
|
+
) : null
|
|
898
|
+
]
|
|
899
|
+
}
|
|
900
|
+
);
|
|
901
|
+
}
|
|
902
|
+
|
|
903
|
+
// src/data-table/entity.ts
|
|
904
|
+
function isUuidValue(value) {
|
|
905
|
+
return typeof value === "string" && /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(value);
|
|
906
|
+
}
|
|
907
|
+
|
|
908
|
+
// src/data-table/DataRowInspector.tsx
|
|
909
|
+
import { jsx as jsx14, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
910
|
+
var noFieldToken = () => null;
|
|
911
|
+
function formatValue(value) {
|
|
912
|
+
if (value == null) return "\u2014";
|
|
913
|
+
if (typeof value === "string") return value || "\u2014";
|
|
914
|
+
if (typeof value === "number" || typeof value === "boolean") {
|
|
915
|
+
return String(value);
|
|
916
|
+
}
|
|
917
|
+
if (value instanceof Date) return value.toISOString();
|
|
918
|
+
try {
|
|
919
|
+
return JSON.stringify(value, null, 2);
|
|
920
|
+
} catch {
|
|
921
|
+
return String(value);
|
|
922
|
+
}
|
|
923
|
+
}
|
|
924
|
+
function isPlainObject(value) {
|
|
925
|
+
return typeof value === "object" && value !== null && !Array.isArray(value) && !(value instanceof Date);
|
|
926
|
+
}
|
|
927
|
+
function DataRowInspector({
|
|
928
|
+
row,
|
|
929
|
+
title,
|
|
930
|
+
className,
|
|
931
|
+
fieldCopy = true,
|
|
932
|
+
recordKind = "record",
|
|
933
|
+
recordLabel,
|
|
934
|
+
location,
|
|
935
|
+
tokenForField = noFieldToken
|
|
936
|
+
}) {
|
|
937
|
+
const entries = isPlainObject(row) ? Object.entries(row) : [["value", row]];
|
|
938
|
+
const fieldButtons = (key, value) => fieldCopy ? /* @__PURE__ */ jsx14("div", { className: "absolute right-1 top-1 opacity-0 transition-opacity focus-within:opacity-100 group-hover/field:opacity-100", children: /* @__PURE__ */ jsx14(
|
|
939
|
+
CopyButtons,
|
|
940
|
+
{
|
|
941
|
+
size: "xs",
|
|
942
|
+
label: recordLabel ? `${recordLabel} \xB7 ${key}` : key,
|
|
943
|
+
human: () => formatValue(value),
|
|
944
|
+
agent: () => ({
|
|
945
|
+
kind: `${recordKind}-field`,
|
|
946
|
+
location: location ?? "Data row inspector",
|
|
947
|
+
description: `The "${key}" field of one ${recordLabel ?? recordKind}.`,
|
|
948
|
+
data: { field: key, value, record: row },
|
|
949
|
+
attributes: { field: key }
|
|
950
|
+
})
|
|
951
|
+
}
|
|
952
|
+
) }) : null;
|
|
953
|
+
return /* @__PURE__ */ jsxs8(
|
|
954
|
+
"div",
|
|
955
|
+
{
|
|
956
|
+
className: cn("flex h-full min-h-0 flex-col overflow-hidden", className),
|
|
957
|
+
children: [
|
|
958
|
+
title ? /* @__PURE__ */ jsx14("div", { className: "shrink-0 border-b border-border px-3 py-2 text-sm font-medium", children: title }) : null,
|
|
959
|
+
/* @__PURE__ */ jsx14("div", { className: "min-h-0 flex-1 overflow-auto p-3", children: /* @__PURE__ */ jsx14("dl", { className: "space-y-2", children: entries.map(([key, value]) => {
|
|
960
|
+
if (isUuidValue(value)) {
|
|
961
|
+
return /* @__PURE__ */ jsxs8(
|
|
962
|
+
"div",
|
|
963
|
+
{
|
|
964
|
+
className: "group/field relative grid grid-cols-[minmax(7rem,9rem)_1fr] gap-2 rounded-md border border-border/60 bg-muted/20 px-2.5 py-2",
|
|
965
|
+
children: [
|
|
966
|
+
/* @__PURE__ */ jsx14("dt", { className: "truncate text-[11px] font-medium uppercase tracking-wide text-muted-foreground", children: key }),
|
|
967
|
+
/* @__PURE__ */ jsx14("dd", { className: "min-w-0", children: /* @__PURE__ */ jsx14(
|
|
968
|
+
MatrxUuidCell,
|
|
969
|
+
{
|
|
970
|
+
value,
|
|
971
|
+
label: key,
|
|
972
|
+
token: tokenForField(key, row)
|
|
973
|
+
}
|
|
974
|
+
) })
|
|
975
|
+
]
|
|
976
|
+
},
|
|
977
|
+
key
|
|
978
|
+
);
|
|
979
|
+
}
|
|
980
|
+
const formatted = formatValue(value);
|
|
981
|
+
const multiline = formatted.includes("\n") || formatted.length > 80;
|
|
982
|
+
return /* @__PURE__ */ jsxs8(
|
|
983
|
+
"div",
|
|
984
|
+
{
|
|
985
|
+
className: "group/field relative grid grid-cols-[minmax(7rem,9rem)_1fr] gap-2 rounded-md border border-border/60 bg-muted/20 py-2 pl-2.5 pr-12",
|
|
986
|
+
children: [
|
|
987
|
+
/* @__PURE__ */ jsx14("dt", { className: "truncate text-[11px] font-medium uppercase tracking-wide text-muted-foreground", children: key }),
|
|
988
|
+
/* @__PURE__ */ jsx14(
|
|
989
|
+
"dd",
|
|
990
|
+
{
|
|
991
|
+
className: cn(
|
|
992
|
+
"min-w-0 text-sm text-foreground break-words",
|
|
993
|
+
multiline && "whitespace-pre-wrap font-mono text-xs leading-relaxed"
|
|
994
|
+
),
|
|
995
|
+
children: formatted
|
|
996
|
+
}
|
|
997
|
+
),
|
|
998
|
+
fieldButtons(key, value)
|
|
999
|
+
]
|
|
1000
|
+
},
|
|
1001
|
+
key
|
|
1002
|
+
);
|
|
1003
|
+
}) }) })
|
|
1004
|
+
]
|
|
1005
|
+
}
|
|
1006
|
+
);
|
|
1007
|
+
}
|
|
1008
|
+
|
|
1009
|
+
// src/data-table/DataRowWindow.tsx
|
|
1010
|
+
import { jsx as jsx15, jsxs as jsxs9 } from "react/jsx-runtime";
|
|
1011
|
+
function DataRowWindow({
|
|
1012
|
+
isOpen,
|
|
1013
|
+
onClose,
|
|
1014
|
+
title = "Row details",
|
|
1015
|
+
row,
|
|
1016
|
+
children,
|
|
1017
|
+
viewContent,
|
|
1018
|
+
editContent,
|
|
1019
|
+
defaultTab,
|
|
1020
|
+
headerActions,
|
|
1021
|
+
width = 720,
|
|
1022
|
+
height = 560,
|
|
1023
|
+
windowId = "matrx-data-row-window"
|
|
1024
|
+
}) {
|
|
1025
|
+
const host = useMatrxDataTableHost();
|
|
1026
|
+
const WindowPanel = host.WindowPanel ?? DefaultTableWindow;
|
|
1027
|
+
const hasEdit = editContent != null;
|
|
1028
|
+
const hasTabs = hasEdit && children == null;
|
|
1029
|
+
const initialTab = defaultTab ?? (hasEdit ? "edit" : "view");
|
|
1030
|
+
const [tab, setTab] = useState6(initialTab);
|
|
1031
|
+
useEffect3(() => {
|
|
1032
|
+
if (!isOpen) return;
|
|
1033
|
+
setTab(defaultTab ?? (hasEdit ? "edit" : "view"));
|
|
1034
|
+
}, [isOpen, windowId, hasEdit, defaultTab]);
|
|
1035
|
+
if (!isOpen) return null;
|
|
1036
|
+
const viewBody = viewContent ?? /* @__PURE__ */ jsx15(DataRowInspector, { row });
|
|
1037
|
+
const body = children ?? /* @__PURE__ */ jsx15("div", { className: "flex h-full min-h-0 flex-1 flex-col overflow-hidden", children: hasTabs ? tab === "edit" ? /* @__PURE__ */ jsx15("div", { className: "min-h-0 flex-1 overflow-y-auto", children: editContent }) : /* @__PURE__ */ jsx15("div", { className: "min-h-0 flex-1 overflow-y-auto", children: viewBody }) : /* @__PURE__ */ jsx15("div", { className: "min-h-0 flex-1 overflow-y-auto", children: viewBody }) });
|
|
1038
|
+
return /* @__PURE__ */ jsx15(
|
|
1039
|
+
WindowPanel,
|
|
1040
|
+
{
|
|
1041
|
+
id: windowId,
|
|
1042
|
+
title,
|
|
1043
|
+
onClose,
|
|
1044
|
+
width,
|
|
1045
|
+
height,
|
|
1046
|
+
minWidth: 420,
|
|
1047
|
+
minHeight: 320,
|
|
1048
|
+
actionsRight: headerActions,
|
|
1049
|
+
bodyClassName: "flex min-h-0 flex-1 flex-col overflow-hidden p-0",
|
|
1050
|
+
sidebarDefaultSize: 140,
|
|
1051
|
+
sidebarMinSize: 110,
|
|
1052
|
+
sidebar: hasTabs ? /* @__PURE__ */ jsxs9(
|
|
1053
|
+
"nav",
|
|
1054
|
+
{
|
|
1055
|
+
"aria-label": "Row window tabs",
|
|
1056
|
+
className: "flex h-full flex-col gap-0.5 overflow-y-auto p-1.5",
|
|
1057
|
+
children: [
|
|
1058
|
+
/* @__PURE__ */ jsx15(
|
|
1059
|
+
TabButton,
|
|
1060
|
+
{
|
|
1061
|
+
active: tab === "view",
|
|
1062
|
+
icon: /* @__PURE__ */ jsx15(Eye, { className: "h-4 w-4 shrink-0" }),
|
|
1063
|
+
label: "View",
|
|
1064
|
+
onClick: () => setTab("view")
|
|
1065
|
+
}
|
|
1066
|
+
),
|
|
1067
|
+
/* @__PURE__ */ jsx15(
|
|
1068
|
+
TabButton,
|
|
1069
|
+
{
|
|
1070
|
+
active: tab === "edit",
|
|
1071
|
+
icon: /* @__PURE__ */ jsx15(Pencil, { className: "h-4 w-4 shrink-0" }),
|
|
1072
|
+
label: "Edit",
|
|
1073
|
+
onClick: () => setTab("edit")
|
|
1074
|
+
}
|
|
1075
|
+
)
|
|
1076
|
+
]
|
|
1077
|
+
}
|
|
1078
|
+
) : void 0,
|
|
1079
|
+
children: body
|
|
1080
|
+
}
|
|
1081
|
+
);
|
|
1082
|
+
}
|
|
1083
|
+
function TabButton({
|
|
1084
|
+
active,
|
|
1085
|
+
icon,
|
|
1086
|
+
label,
|
|
1087
|
+
onClick
|
|
1088
|
+
}) {
|
|
1089
|
+
return /* @__PURE__ */ jsxs9(
|
|
1090
|
+
"button",
|
|
1091
|
+
{
|
|
1092
|
+
type: "button",
|
|
1093
|
+
role: "tab",
|
|
1094
|
+
"aria-selected": active,
|
|
1095
|
+
onClick,
|
|
1096
|
+
className: cn(
|
|
1097
|
+
"flex items-center gap-2 rounded-md px-2.5 py-2 text-left text-sm transition-colors",
|
|
1098
|
+
active ? "bg-accent font-medium text-foreground" : "text-muted-foreground hover:bg-muted/60 hover:text-foreground"
|
|
1099
|
+
),
|
|
1100
|
+
children: [
|
|
1101
|
+
icon,
|
|
1102
|
+
/* @__PURE__ */ jsx15("span", { className: "min-w-0 flex-1 truncate", children: label })
|
|
1103
|
+
]
|
|
1104
|
+
}
|
|
1105
|
+
);
|
|
1106
|
+
}
|
|
1107
|
+
export {
|
|
1108
|
+
DataRowWindow
|
|
1109
|
+
};
|
|
1110
|
+
//# sourceMappingURL=data-row-window.js.map
|