@ai-matrx/design-system 0.60.0 → 0.60.6
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 +55 -0
- package/dist/data-table/index.cjs +13 -2
- package/dist/data-table/index.cjs.map +1 -1
- package/dist/data-table/index.js +13 -2
- package/dist/data-table/index.js.map +1 -1
- package/dist/desktop-lint.mjs +218 -0
- package/dist/desktop.cjs +3565 -0
- package/dist/desktop.cjs.map +1 -0
- package/dist/desktop.css +234 -0
- package/dist/desktop.d.cts +484 -0
- package/dist/desktop.d.ts +484 -0
- package/dist/desktop.js +3533 -0
- package/dist/desktop.js.map +1 -0
- package/dist/index.d.cts +14 -876
- package/dist/index.d.ts +14 -876
- package/dist/organization-picker-mLqIcesx.d.cts +875 -0
- package/dist/organization-picker-mLqIcesx.d.ts +875 -0
- package/package.json +13 -1
package/dist/desktop.cjs
ADDED
|
@@ -0,0 +1,3565 @@
|
|
|
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/desktop/index.ts
|
|
32
|
+
var desktop_exports = {};
|
|
33
|
+
__export(desktop_exports, {
|
|
34
|
+
AppRail: () => AppRail,
|
|
35
|
+
AppRailAvatar: () => AppRailAvatar,
|
|
36
|
+
AppRailGroup: () => AppRailGroup,
|
|
37
|
+
AppRailItem: () => AppRailItem,
|
|
38
|
+
AppRailSpacer: () => AppRailSpacer,
|
|
39
|
+
Banner: () => Banner,
|
|
40
|
+
Button: () => Button,
|
|
41
|
+
CanvasLayout: () => CanvasLayout,
|
|
42
|
+
Checkbox: () => Checkbox,
|
|
43
|
+
CodeBlock: () => CodeBlock,
|
|
44
|
+
Collapsible: () => Collapsible,
|
|
45
|
+
CollapsibleContent: () => CollapsibleContent2,
|
|
46
|
+
CollapsibleTrigger: () => CollapsibleTrigger2,
|
|
47
|
+
ComingSoon: () => ComingSoon,
|
|
48
|
+
ConfirmDialog: () => ConfirmDialog,
|
|
49
|
+
ConfirmDialogHost: () => ConfirmDialogHost,
|
|
50
|
+
ContextMenu: () => ContextMenu,
|
|
51
|
+
ContextMenuContent: () => ContextMenuContent,
|
|
52
|
+
ContextMenuItem: () => ContextMenuItem,
|
|
53
|
+
ContextMenuSeparator: () => ContextMenuSeparator,
|
|
54
|
+
ContextMenuShortcut: () => ContextMenuShortcut,
|
|
55
|
+
ContextMenuTrigger: () => ContextMenuTrigger,
|
|
56
|
+
DataTable: () => DataTable,
|
|
57
|
+
Dialog: () => Dialog,
|
|
58
|
+
DialogClose: () => DialogClose,
|
|
59
|
+
DialogContent: () => DialogContent,
|
|
60
|
+
DialogDescription: () => DialogDescription,
|
|
61
|
+
DialogFooter: () => DialogFooter,
|
|
62
|
+
DialogHeader: () => DialogHeader,
|
|
63
|
+
DialogTitle: () => DialogTitle,
|
|
64
|
+
DialogTrigger: () => DialogTrigger,
|
|
65
|
+
DropdownMenu: () => DropdownMenu,
|
|
66
|
+
DropdownMenuCheckboxItem: () => DropdownMenuCheckboxItem,
|
|
67
|
+
DropdownMenuContent: () => DropdownMenuContent,
|
|
68
|
+
DropdownMenuGroup: () => DropdownMenuGroup,
|
|
69
|
+
DropdownMenuItem: () => DropdownMenuItem,
|
|
70
|
+
DropdownMenuLabel: () => DropdownMenuLabel,
|
|
71
|
+
DropdownMenuRadioGroup: () => DropdownMenuRadioGroup,
|
|
72
|
+
DropdownMenuRadioItem: () => DropdownMenuRadioItem,
|
|
73
|
+
DropdownMenuSeparator: () => DropdownMenuSeparator,
|
|
74
|
+
DropdownMenuShortcut: () => DropdownMenuShortcut,
|
|
75
|
+
DropdownMenuSub: () => DropdownMenuSub,
|
|
76
|
+
DropdownMenuSubContent: () => DropdownMenuSubContent,
|
|
77
|
+
DropdownMenuSubTrigger: () => DropdownMenuSubTrigger,
|
|
78
|
+
DropdownMenuTrigger: () => DropdownMenuTrigger,
|
|
79
|
+
ErrorBanner: () => ErrorBanner,
|
|
80
|
+
FIELD: () => FIELD,
|
|
81
|
+
FieldRow: () => FieldRow,
|
|
82
|
+
IconButton: () => IconButton,
|
|
83
|
+
Kbd: () => Kbd,
|
|
84
|
+
METER_THRESHOLDS: () => METER_THRESHOLDS,
|
|
85
|
+
Meter: () => Meter,
|
|
86
|
+
ModeSwitch: () => ModeSwitch,
|
|
87
|
+
OrganizationMark: () => OrganizationMark,
|
|
88
|
+
Page: () => Page,
|
|
89
|
+
PageHeader: () => PageHeader,
|
|
90
|
+
Popover: () => Popover,
|
|
91
|
+
PopoverAnchor: () => PopoverAnchor,
|
|
92
|
+
PopoverContent: () => PopoverContent,
|
|
93
|
+
PopoverTrigger: () => PopoverTrigger,
|
|
94
|
+
Resource: () => Resource,
|
|
95
|
+
Row: () => Row,
|
|
96
|
+
SearchField: () => SearchField,
|
|
97
|
+
Section: () => Section,
|
|
98
|
+
SectionContent: () => SectionContent,
|
|
99
|
+
Segmented: () => Segmented,
|
|
100
|
+
Select: () => Select,
|
|
101
|
+
Skeleton: () => Skeleton,
|
|
102
|
+
SourceList: () => SourceList,
|
|
103
|
+
SourceListItem: () => SourceListItem,
|
|
104
|
+
SourceListMore: () => SourceListMore,
|
|
105
|
+
SourceListSection: () => SourceListSection,
|
|
106
|
+
Spinner: () => Spinner,
|
|
107
|
+
StateView: () => StateView,
|
|
108
|
+
StatusDot: () => StatusDot,
|
|
109
|
+
StatusLight: () => StatusLight,
|
|
110
|
+
Switch: () => Switch,
|
|
111
|
+
TEXT_TONE: () => TEXT_TONE,
|
|
112
|
+
Tag: () => Tag,
|
|
113
|
+
Text: () => Text,
|
|
114
|
+
TextArea: () => TextArea,
|
|
115
|
+
TextField: () => TextField,
|
|
116
|
+
Toolbar: () => Toolbar,
|
|
117
|
+
ToolbarDivider: () => ToolbarDivider,
|
|
118
|
+
ToolbarSpacer: () => ToolbarSpacer,
|
|
119
|
+
Tooltip: () => Tooltip,
|
|
120
|
+
TooltipContent: () => TooltipContent,
|
|
121
|
+
TooltipProvider: () => TooltipProvider,
|
|
122
|
+
TooltipTrigger: () => TooltipTrigger,
|
|
123
|
+
buttonVariants: () => buttonVariants,
|
|
124
|
+
cn: () => cn,
|
|
125
|
+
errorMessage: () => errorMessage,
|
|
126
|
+
formatAge: () => formatAge,
|
|
127
|
+
formatBytes: () => formatBytes,
|
|
128
|
+
formatClockTime: () => formatClockTime,
|
|
129
|
+
formatDuration: () => formatDuration,
|
|
130
|
+
formatRelativeTime: () => formatRelativeTime,
|
|
131
|
+
formatUsage: () => formatUsage,
|
|
132
|
+
meterTone: () => meterTone,
|
|
133
|
+
organizationColor: () => organizationColor
|
|
134
|
+
});
|
|
135
|
+
module.exports = __toCommonJS(desktop_exports);
|
|
136
|
+
|
|
137
|
+
// src/desktop/cn.ts
|
|
138
|
+
var import_clsx = require("clsx");
|
|
139
|
+
var import_tailwind_merge = require("tailwind-merge");
|
|
140
|
+
var merge = (0, import_tailwind_merge.extendTailwindMerge)({
|
|
141
|
+
extend: {
|
|
142
|
+
theme: {
|
|
143
|
+
spacing: [
|
|
144
|
+
"control-sm",
|
|
145
|
+
"control",
|
|
146
|
+
"control-lg",
|
|
147
|
+
"row-compact",
|
|
148
|
+
"sidebar-row",
|
|
149
|
+
"row",
|
|
150
|
+
"row-tall",
|
|
151
|
+
"toolbar",
|
|
152
|
+
"icon-sm",
|
|
153
|
+
"icon",
|
|
154
|
+
"icon-lg",
|
|
155
|
+
"icon-xl"
|
|
156
|
+
],
|
|
157
|
+
radius: ["control", "group", "panel"],
|
|
158
|
+
container: ["form", "page", "wide"],
|
|
159
|
+
text: ["title-large", "title-1", "title-2", "title-3", "headline", "body", "callout", "caption"],
|
|
160
|
+
shadow: ["control", "raised", "popover"]
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
});
|
|
164
|
+
function cn(...inputs) {
|
|
165
|
+
return merge((0, import_clsx.clsx)(inputs));
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// src/desktop/text.tsx
|
|
169
|
+
var React = __toESM(require("react"), 1);
|
|
170
|
+
var import_jsx_runtime = require("react/jsx-runtime");
|
|
171
|
+
var VARIANT = {
|
|
172
|
+
"title-large": "text-title-large",
|
|
173
|
+
"title-1": "text-title-1",
|
|
174
|
+
"title-2": "text-title-2",
|
|
175
|
+
"title-3": "text-title-3",
|
|
176
|
+
headline: "text-headline",
|
|
177
|
+
body: "text-body",
|
|
178
|
+
callout: "text-callout",
|
|
179
|
+
caption: "text-caption"
|
|
180
|
+
};
|
|
181
|
+
var TEXT_TONE = {
|
|
182
|
+
primary: "text-label",
|
|
183
|
+
secondary: "text-label-secondary",
|
|
184
|
+
tertiary: "text-label-tertiary",
|
|
185
|
+
tint: "text-tint",
|
|
186
|
+
danger: "text-system-red",
|
|
187
|
+
warning: "text-system-orange",
|
|
188
|
+
success: "text-system-green"
|
|
189
|
+
};
|
|
190
|
+
var Text = React.forwardRef(function Text2({
|
|
191
|
+
variant = "body",
|
|
192
|
+
tone = "primary",
|
|
193
|
+
emphasized,
|
|
194
|
+
mono,
|
|
195
|
+
truncate,
|
|
196
|
+
tabular,
|
|
197
|
+
selectable,
|
|
198
|
+
as: Tag2 = "span",
|
|
199
|
+
className,
|
|
200
|
+
...props
|
|
201
|
+
}, ref) {
|
|
202
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
203
|
+
Tag2,
|
|
204
|
+
{
|
|
205
|
+
ref,
|
|
206
|
+
"data-slot": "text",
|
|
207
|
+
className: cn(
|
|
208
|
+
VARIANT[variant],
|
|
209
|
+
TEXT_TONE[tone],
|
|
210
|
+
emphasized && "font-medium",
|
|
211
|
+
mono && "font-mono",
|
|
212
|
+
truncate && "min-w-0 truncate",
|
|
213
|
+
tabular && "tabular-nums",
|
|
214
|
+
selectable && "select-text",
|
|
215
|
+
className
|
|
216
|
+
),
|
|
217
|
+
...props
|
|
218
|
+
}
|
|
219
|
+
);
|
|
220
|
+
});
|
|
221
|
+
|
|
222
|
+
// src/desktop/spinner.tsx
|
|
223
|
+
var import_jsx_runtime2 = require("react/jsx-runtime");
|
|
224
|
+
var SPOKES = Array.from({ length: 12 }, (_, i) => i);
|
|
225
|
+
var SIZE = { sm: "size-icon-sm", md: "size-icon", lg: "size-icon-lg", xl: "size-icon-xl" };
|
|
226
|
+
function Spinner({ size = "md", label, className, ...props }) {
|
|
227
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
228
|
+
"svg",
|
|
229
|
+
{
|
|
230
|
+
viewBox: "0 0 24 24",
|
|
231
|
+
role: label ? "status" : void 0,
|
|
232
|
+
"aria-label": label,
|
|
233
|
+
"aria-hidden": label ? void 0 : true,
|
|
234
|
+
"data-slot": "spinner",
|
|
235
|
+
className: cn("mx-spinner shrink-0 text-label-secondary", SIZE[size], className),
|
|
236
|
+
...props,
|
|
237
|
+
children: SPOKES.map((i) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
238
|
+
"rect",
|
|
239
|
+
{
|
|
240
|
+
x: "11",
|
|
241
|
+
y: "2",
|
|
242
|
+
width: "2",
|
|
243
|
+
height: "6",
|
|
244
|
+
rx: "1",
|
|
245
|
+
fill: "currentColor",
|
|
246
|
+
opacity: 0.18 + 0.82 * (i + 1) / 12,
|
|
247
|
+
transform: `rotate(${i * 30} 12 12)`
|
|
248
|
+
},
|
|
249
|
+
i
|
|
250
|
+
))
|
|
251
|
+
}
|
|
252
|
+
);
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
// src/desktop/button.tsx
|
|
256
|
+
var import_react_slot = require("@radix-ui/react-slot");
|
|
257
|
+
var import_class_variance_authority = require("class-variance-authority");
|
|
258
|
+
var React4 = __toESM(require("react"), 1);
|
|
259
|
+
|
|
260
|
+
// src/tooltip.tsx
|
|
261
|
+
var TooltipPrimitive = __toESM(require("@radix-ui/react-tooltip"), 1);
|
|
262
|
+
var React3 = __toESM(require("react"), 1);
|
|
263
|
+
|
|
264
|
+
// src/cn.ts
|
|
265
|
+
var import_clsx2 = require("clsx");
|
|
266
|
+
var import_tailwind_merge2 = require("tailwind-merge");
|
|
267
|
+
function cn2(...inputs) {
|
|
268
|
+
return (0, import_tailwind_merge2.twMerge)((0, import_clsx2.clsx)(inputs));
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
// src/motion.ts
|
|
272
|
+
var MOTION_OVERLAY = "matrx-motion-overlay";
|
|
273
|
+
var MOTION_DIALOG = "matrx-motion-dialog";
|
|
274
|
+
var CENTERED_DIALOG = "matrx-dialog-centered";
|
|
275
|
+
var POPPER_OFFSET = "matrx-popper-offset";
|
|
276
|
+
var MOTION_BOTTOM_SHEET = "matrx-motion-bottom-sheet";
|
|
277
|
+
var MOTION_POPPER = "matrx-motion-popper";
|
|
278
|
+
var MOTION_SHEET = {
|
|
279
|
+
top: "matrx-motion-sheet-top",
|
|
280
|
+
bottom: "matrx-motion-sheet-bottom",
|
|
281
|
+
left: "matrx-motion-sheet-left",
|
|
282
|
+
right: "matrx-motion-sheet-right",
|
|
283
|
+
center: "matrx-motion-sheet-center"
|
|
284
|
+
};
|
|
285
|
+
var MOTION_PULSE = "matrx-pulse";
|
|
286
|
+
var MOTION_SPIN = "matrx-spin";
|
|
287
|
+
var ALL_MOTION_CLASSES = [
|
|
288
|
+
MOTION_OVERLAY,
|
|
289
|
+
MOTION_DIALOG,
|
|
290
|
+
MOTION_BOTTOM_SHEET,
|
|
291
|
+
MOTION_POPPER,
|
|
292
|
+
...Object.values(MOTION_SHEET),
|
|
293
|
+
MOTION_PULSE,
|
|
294
|
+
MOTION_SPIN
|
|
295
|
+
];
|
|
296
|
+
|
|
297
|
+
// src/portal-container.tsx
|
|
298
|
+
var React2 = __toESM(require("react"), 1);
|
|
299
|
+
var import_jsx_runtime3 = require("react/jsx-runtime");
|
|
300
|
+
var CONTEXT_SLOT = /* @__PURE__ */ Symbol.for("ai-matrx.design-system.portal-container-context");
|
|
301
|
+
var contextRegistry = globalThis;
|
|
302
|
+
var PortalContainerContext = contextRegistry[CONTEXT_SLOT] ??= React2.createContext(void 0);
|
|
303
|
+
function PortalContainerProvider({
|
|
304
|
+
container,
|
|
305
|
+
children
|
|
306
|
+
}) {
|
|
307
|
+
return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(PortalContainerContext.Provider, { value: container ?? void 0, children });
|
|
308
|
+
}
|
|
309
|
+
function usePortalContainer(explicit) {
|
|
310
|
+
const injected = React2.useContext(PortalContainerContext);
|
|
311
|
+
if (explicit !== void 0) {
|
|
312
|
+
return explicit ?? void 0;
|
|
313
|
+
}
|
|
314
|
+
return injected ?? void 0;
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
// src/title-tooltips.ts
|
|
318
|
+
var TITLE_TOOLTIP_ATTR = "data-matrx-title";
|
|
319
|
+
var TOOLTIP_TRIGGER_ATTR = "data-matrx-tooltip-trigger";
|
|
320
|
+
var NATIVE_TITLE_OPT_OUT_ATTR = "data-native-title";
|
|
321
|
+
var OPEN_DELAY_MS = 350;
|
|
322
|
+
var SKIP_DELAY_MS = 500;
|
|
323
|
+
var GAP_PX = 6;
|
|
324
|
+
var EDGE_PX = 8;
|
|
325
|
+
var SURFACE_CLASS = "matrx-tooltip matrx-title-tooltip matrx-motion-popper z-[10001] rounded-md border border-border bg-popover px-2.5 py-1 text-xs font-medium text-popover-foreground";
|
|
326
|
+
var INSTALLATION_KEY = /* @__PURE__ */ Symbol.for("@ai-matrx/design-system/title-tooltips");
|
|
327
|
+
var installations = {
|
|
328
|
+
get: (doc) => doc[INSTALLATION_KEY],
|
|
329
|
+
set: (doc, value) => {
|
|
330
|
+
doc[INSTALLATION_KEY] = value;
|
|
331
|
+
},
|
|
332
|
+
delete: (doc) => {
|
|
333
|
+
delete doc[INSTALLATION_KEY];
|
|
334
|
+
}
|
|
335
|
+
};
|
|
336
|
+
function acquireTitleTooltips(doc = typeof document === "undefined" ? void 0 : document) {
|
|
337
|
+
if (!doc) return () => {
|
|
338
|
+
};
|
|
339
|
+
const existing = installations.get(doc);
|
|
340
|
+
if (existing) {
|
|
341
|
+
existing.count += 1;
|
|
342
|
+
} else {
|
|
343
|
+
installations.set(doc, { count: 1, uninstall: install(doc) });
|
|
344
|
+
}
|
|
345
|
+
let released = false;
|
|
346
|
+
return () => {
|
|
347
|
+
if (released) return;
|
|
348
|
+
released = true;
|
|
349
|
+
const current = installations.get(doc);
|
|
350
|
+
if (!current) return;
|
|
351
|
+
current.count -= 1;
|
|
352
|
+
if (current.count <= 0) {
|
|
353
|
+
current.uninstall();
|
|
354
|
+
installations.delete(doc);
|
|
355
|
+
}
|
|
356
|
+
};
|
|
357
|
+
}
|
|
358
|
+
function install(doc) {
|
|
359
|
+
const win = doc.defaultView;
|
|
360
|
+
let active = null;
|
|
361
|
+
let surface = null;
|
|
362
|
+
let openTimer;
|
|
363
|
+
let lastClosedAt = 0;
|
|
364
|
+
const HTMLElementCtor = win?.HTMLElement;
|
|
365
|
+
const titledAncestor = (node) => {
|
|
366
|
+
if (!HTMLElementCtor || !(node instanceof win.Node)) return null;
|
|
367
|
+
const start = node instanceof HTMLElementCtor ? node : node.parentElement;
|
|
368
|
+
if (!start) return null;
|
|
369
|
+
const el = start.closest(`[title], [${TITLE_TOOLTIP_ATTR}]`);
|
|
370
|
+
if (!el || !(el instanceof HTMLElementCtor)) return null;
|
|
371
|
+
if (el.closest(`[${NATIVE_TITLE_OPT_OUT_ATTR}]`)) return null;
|
|
372
|
+
return el;
|
|
373
|
+
};
|
|
374
|
+
const textOf = (el) => (el.getAttribute(TITLE_TOOLTIP_ATTR) ?? el.getAttribute("title") ?? "").trim();
|
|
375
|
+
const lift = (el) => {
|
|
376
|
+
const title = el.getAttribute("title");
|
|
377
|
+
if (title === null) return;
|
|
378
|
+
el.setAttribute(TITLE_TOOLTIP_ATTR, title);
|
|
379
|
+
el.removeAttribute("title");
|
|
380
|
+
};
|
|
381
|
+
const restore = (el) => {
|
|
382
|
+
const held = el.getAttribute(TITLE_TOOLTIP_ATTR);
|
|
383
|
+
if (held === null) return;
|
|
384
|
+
if (!el.hasAttribute("title")) el.setAttribute("title", held);
|
|
385
|
+
el.removeAttribute(TITLE_TOOLTIP_ATTR);
|
|
386
|
+
};
|
|
387
|
+
const ensureSurface = () => {
|
|
388
|
+
if (surface && surface.isConnected) return surface;
|
|
389
|
+
surface = doc.createElement("div");
|
|
390
|
+
surface.setAttribute("role", "tooltip");
|
|
391
|
+
surface.setAttribute("data-slot", "title-tooltip");
|
|
392
|
+
surface.className = SURFACE_CLASS;
|
|
393
|
+
surface.style.visibility = "hidden";
|
|
394
|
+
doc.body.appendChild(surface);
|
|
395
|
+
return surface;
|
|
396
|
+
};
|
|
397
|
+
const preferredSide = (el) => {
|
|
398
|
+
const hint = el.closest("[data-title-side]")?.getAttribute("data-title-side");
|
|
399
|
+
return hint === "bottom" || hint === "left" || hint === "right" ? hint : "top";
|
|
400
|
+
};
|
|
401
|
+
const place = (el, node) => {
|
|
402
|
+
const vw = win?.innerWidth ?? 0;
|
|
403
|
+
const vh = win?.innerHeight ?? 0;
|
|
404
|
+
const anchor = el.getBoundingClientRect();
|
|
405
|
+
const box = node.getBoundingClientRect();
|
|
406
|
+
const fits = {
|
|
407
|
+
top: anchor.top - GAP_PX - box.height >= EDGE_PX,
|
|
408
|
+
bottom: anchor.bottom + GAP_PX + box.height <= vh - EDGE_PX,
|
|
409
|
+
left: anchor.left - GAP_PX - box.width >= EDGE_PX,
|
|
410
|
+
right: anchor.right + GAP_PX + box.width <= vw - EDGE_PX
|
|
411
|
+
};
|
|
412
|
+
const opposite = { top: "bottom", bottom: "top", left: "right", right: "left" };
|
|
413
|
+
const wanted = preferredSide(el);
|
|
414
|
+
const side = fits[wanted] ? wanted : fits[opposite[wanted]] ? opposite[wanted] : wanted;
|
|
415
|
+
let x;
|
|
416
|
+
let y;
|
|
417
|
+
if (side === "top" || side === "bottom") {
|
|
418
|
+
x = anchor.left + anchor.width / 2 - box.width / 2;
|
|
419
|
+
y = side === "top" ? anchor.top - GAP_PX - box.height : anchor.bottom + GAP_PX;
|
|
420
|
+
} else {
|
|
421
|
+
y = anchor.top + anchor.height / 2 - box.height / 2;
|
|
422
|
+
x = side === "left" ? anchor.left - GAP_PX - box.width : anchor.right + GAP_PX;
|
|
423
|
+
}
|
|
424
|
+
x = Math.min(Math.max(x, EDGE_PX), Math.max(EDGE_PX, vw - EDGE_PX - box.width));
|
|
425
|
+
y = Math.min(Math.max(y, EDGE_PX), Math.max(EDGE_PX, vh - EDGE_PX - box.height));
|
|
426
|
+
node.style.transform = `translate(${Math.round(x)}px, ${Math.round(y)}px)`;
|
|
427
|
+
node.setAttribute("data-side", side);
|
|
428
|
+
};
|
|
429
|
+
const isShown = (node, root) => {
|
|
430
|
+
const rect = node.getBoundingClientRect();
|
|
431
|
+
if (rect.width <= 0 || rect.height <= 0) return false;
|
|
432
|
+
for (let cur = node; cur; cur = cur === root ? null : cur.parentElement) {
|
|
433
|
+
const cs = win?.getComputedStyle(cur);
|
|
434
|
+
if (!cs) return false;
|
|
435
|
+
if (cs.display === "none" || cs.visibility === "hidden" || Number(cs.opacity) === 0) return false;
|
|
436
|
+
}
|
|
437
|
+
return true;
|
|
438
|
+
};
|
|
439
|
+
const repeatsVisibleText = (el, text) => {
|
|
440
|
+
const norm = (t) => t.replace(/\s+/g, " ").trim().toLowerCase();
|
|
441
|
+
const want = norm(text);
|
|
442
|
+
if (!want || !win) return false;
|
|
443
|
+
const walker = doc.createTreeWalker(el, win.NodeFilter.SHOW_TEXT);
|
|
444
|
+
for (let node = walker.nextNode(); node; node = walker.nextNode()) {
|
|
445
|
+
if (norm(node.textContent ?? "") !== want) continue;
|
|
446
|
+
const host = node.parentElement;
|
|
447
|
+
if (!host || !isShown(host, el)) continue;
|
|
448
|
+
if (host.scrollWidth > host.clientWidth + 1) continue;
|
|
449
|
+
return true;
|
|
450
|
+
}
|
|
451
|
+
return false;
|
|
452
|
+
};
|
|
453
|
+
const show = (el, instant) => {
|
|
454
|
+
if (active !== el) return;
|
|
455
|
+
if (el.closest(`[${TOOLTIP_TRIGGER_ATTR}]`)) return;
|
|
456
|
+
const text = textOf(el);
|
|
457
|
+
if (!text || !el.isConnected) return;
|
|
458
|
+
if (repeatsVisibleText(el, text)) return;
|
|
459
|
+
const node = ensureSurface();
|
|
460
|
+
node.textContent = text;
|
|
461
|
+
node.style.visibility = "hidden";
|
|
462
|
+
node.setAttribute("data-state", instant ? "instant-open" : "delayed-open");
|
|
463
|
+
place(el, node);
|
|
464
|
+
node.style.visibility = "visible";
|
|
465
|
+
if (el.id) node.id = `${el.id}-title-tooltip`;
|
|
466
|
+
};
|
|
467
|
+
const hide = () => {
|
|
468
|
+
if (openTimer !== void 0) clearTimeout(openTimer);
|
|
469
|
+
openTimer = void 0;
|
|
470
|
+
if (surface && surface.style.visibility === "visible") lastClosedAt = Date.now();
|
|
471
|
+
if (surface) {
|
|
472
|
+
surface.style.visibility = "hidden";
|
|
473
|
+
surface.removeAttribute("data-state");
|
|
474
|
+
}
|
|
475
|
+
};
|
|
476
|
+
const activate = (el) => {
|
|
477
|
+
if (active === el) return;
|
|
478
|
+
deactivate();
|
|
479
|
+
active = el;
|
|
480
|
+
lift(el);
|
|
481
|
+
const instant = Date.now() - lastClosedAt < SKIP_DELAY_MS;
|
|
482
|
+
if (instant) show(el, true);
|
|
483
|
+
else openTimer = setTimeout(() => show(el, false), OPEN_DELAY_MS);
|
|
484
|
+
};
|
|
485
|
+
const deactivate = () => {
|
|
486
|
+
hide();
|
|
487
|
+
if (active) restore(active);
|
|
488
|
+
active = null;
|
|
489
|
+
};
|
|
490
|
+
const onPointerOver = (e) => {
|
|
491
|
+
if (e.pointerType === "touch") return;
|
|
492
|
+
const el = titledAncestor(e.target);
|
|
493
|
+
if (el) activate(el);
|
|
494
|
+
else if (active) deactivate();
|
|
495
|
+
};
|
|
496
|
+
const onPointerOut = (e) => {
|
|
497
|
+
if (!active) return;
|
|
498
|
+
const next = e.relatedTarget;
|
|
499
|
+
if (next instanceof win.Node && active.contains(next)) return;
|
|
500
|
+
deactivate();
|
|
501
|
+
};
|
|
502
|
+
const onFocusIn = (e) => {
|
|
503
|
+
const el = titledAncestor(e.target);
|
|
504
|
+
if (!el) return;
|
|
505
|
+
let visible = true;
|
|
506
|
+
try {
|
|
507
|
+
visible = e.target.matches(":focus-visible");
|
|
508
|
+
} catch {
|
|
509
|
+
visible = true;
|
|
510
|
+
}
|
|
511
|
+
if (visible) activate(el);
|
|
512
|
+
};
|
|
513
|
+
const onFocusOut = (e) => {
|
|
514
|
+
if (!active) return;
|
|
515
|
+
const next = e.relatedTarget;
|
|
516
|
+
if (next instanceof win.Node && active.contains(next)) return;
|
|
517
|
+
deactivate();
|
|
518
|
+
};
|
|
519
|
+
const onDismiss = () => deactivate();
|
|
520
|
+
const onKeyDown = (e) => {
|
|
521
|
+
if (e.key === "Escape") deactivate();
|
|
522
|
+
};
|
|
523
|
+
doc.addEventListener("pointerover", onPointerOver, true);
|
|
524
|
+
doc.addEventListener("pointerout", onPointerOut, true);
|
|
525
|
+
doc.addEventListener("focusin", onFocusIn, true);
|
|
526
|
+
doc.addEventListener("focusout", onFocusOut, true);
|
|
527
|
+
doc.addEventListener("pointerdown", onDismiss, true);
|
|
528
|
+
doc.addEventListener("keydown", onKeyDown, true);
|
|
529
|
+
doc.addEventListener("scroll", onDismiss, true);
|
|
530
|
+
win?.addEventListener("blur", onDismiss);
|
|
531
|
+
return () => {
|
|
532
|
+
deactivate();
|
|
533
|
+
doc.removeEventListener("pointerover", onPointerOver, true);
|
|
534
|
+
doc.removeEventListener("pointerout", onPointerOut, true);
|
|
535
|
+
doc.removeEventListener("focusin", onFocusIn, true);
|
|
536
|
+
doc.removeEventListener("focusout", onFocusOut, true);
|
|
537
|
+
doc.removeEventListener("pointerdown", onDismiss, true);
|
|
538
|
+
doc.removeEventListener("keydown", onKeyDown, true);
|
|
539
|
+
doc.removeEventListener("scroll", onDismiss, true);
|
|
540
|
+
win?.removeEventListener("blur", onDismiss);
|
|
541
|
+
surface?.remove();
|
|
542
|
+
surface = null;
|
|
543
|
+
};
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
// src/tooltip.tsx
|
|
547
|
+
var import_jsx_runtime4 = require("react/jsx-runtime");
|
|
548
|
+
function TooltipProvider({ titleTooltips = true, ...props }) {
|
|
549
|
+
React3.useEffect(() => {
|
|
550
|
+
if (!titleTooltips) return;
|
|
551
|
+
return acquireTitleTooltips();
|
|
552
|
+
}, [titleTooltips]);
|
|
553
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(TooltipPrimitive.Provider, { ...props });
|
|
554
|
+
}
|
|
555
|
+
var Tooltip = TooltipPrimitive.Root;
|
|
556
|
+
var TooltipTrigger = React3.forwardRef((props, ref) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(TooltipPrimitive.Trigger, { ref, ...{ [TOOLTIP_TRIGGER_ATTR]: "" }, ...props }));
|
|
557
|
+
TooltipTrigger.displayName = TooltipPrimitive.Trigger.displayName;
|
|
558
|
+
var TooltipContent = React3.forwardRef(({ className, sideOffset = 4, container, animated = true, ...props }, ref) => {
|
|
559
|
+
const resolved = usePortalContainer(container);
|
|
560
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(TooltipPrimitive.Portal, { container: resolved ?? null, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
561
|
+
TooltipPrimitive.Content,
|
|
562
|
+
{
|
|
563
|
+
ref,
|
|
564
|
+
"data-slot": "tooltip-content",
|
|
565
|
+
sideOffset,
|
|
566
|
+
className: cn2(
|
|
567
|
+
"matrx-tooltip z-[10001] overflow-hidden rounded-md border border-border bg-popover px-2.5 py-1 text-xs font-medium text-popover-foreground",
|
|
568
|
+
animated && MOTION_POPPER,
|
|
569
|
+
className
|
|
570
|
+
),
|
|
571
|
+
...props
|
|
572
|
+
}
|
|
573
|
+
) });
|
|
574
|
+
});
|
|
575
|
+
TooltipContent.displayName = TooltipPrimitive.Content.displayName;
|
|
576
|
+
|
|
577
|
+
// src/desktop/button.tsx
|
|
578
|
+
var import_jsx_runtime5 = require("react/jsx-runtime");
|
|
579
|
+
var buttonVariants = (0, import_class_variance_authority.cva)(
|
|
580
|
+
"focus-ring inline-flex shrink-0 select-none items-center justify-center gap-1.5 whitespace-nowrap rounded-control font-medium transition-colors duration-100 disabled:pointer-events-none disabled:opacity-40 [&_svg]:pointer-events-none [&_svg]:shrink-0",
|
|
581
|
+
{
|
|
582
|
+
variants: {
|
|
583
|
+
variant: {
|
|
584
|
+
primary: "bg-tint text-tint-label shadow-control hover:brightness-110 active:brightness-95 disabled:bg-fill-secondary disabled:text-label-tertiary disabled:opacity-100 disabled:shadow-none",
|
|
585
|
+
secondary: "bg-control text-label shadow-control hover:bg-fill-secondary active:bg-fill-tertiary",
|
|
586
|
+
plain: "bg-transparent text-label hover:bg-fill-secondary active:bg-fill-tertiary",
|
|
587
|
+
destructive: "bg-control text-system-red shadow-control hover:bg-fill-secondary active:bg-fill-tertiary"
|
|
588
|
+
},
|
|
589
|
+
size: {
|
|
590
|
+
sm: "h-control-sm px-2 text-callout [&_svg]:size-icon-sm",
|
|
591
|
+
md: "h-control px-3 text-body [&_svg]:size-icon",
|
|
592
|
+
lg: "h-control-lg px-4 text-body [&_svg]:size-icon"
|
|
593
|
+
}
|
|
594
|
+
},
|
|
595
|
+
defaultVariants: { variant: "secondary", size: "md" }
|
|
596
|
+
}
|
|
597
|
+
);
|
|
598
|
+
var Button = React4.forwardRef(function Button2({ variant, size, icon, busy, asChild, disabled, className, children, type = "button", ...props }, ref) {
|
|
599
|
+
const Comp = asChild ? import_react_slot.Slot : "button";
|
|
600
|
+
return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
601
|
+
Comp,
|
|
602
|
+
{
|
|
603
|
+
ref,
|
|
604
|
+
type: asChild ? void 0 : type,
|
|
605
|
+
"data-slot": "button",
|
|
606
|
+
disabled: disabled || busy,
|
|
607
|
+
"aria-busy": busy || void 0,
|
|
608
|
+
className: cn(buttonVariants({ variant, size }), className),
|
|
609
|
+
...props,
|
|
610
|
+
children: asChild ? children : /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_jsx_runtime5.Fragment, { children: [
|
|
611
|
+
busy ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(Spinner, { size: size === "sm" ? "sm" : "md" }) : icon,
|
|
612
|
+
children
|
|
613
|
+
] })
|
|
614
|
+
}
|
|
615
|
+
);
|
|
616
|
+
});
|
|
617
|
+
var iconButtonVariants = (0, import_class_variance_authority.cva)(
|
|
618
|
+
"focus-ring inline-flex shrink-0 select-none items-center justify-center rounded-control text-label-secondary transition-colors duration-100 hover:bg-fill-secondary hover:text-label active:bg-fill-tertiary disabled:pointer-events-none disabled:opacity-40 data-[active=true]:bg-fill-secondary data-[active=true]:text-label [&_svg]:pointer-events-none [&_svg]:shrink-0",
|
|
619
|
+
{
|
|
620
|
+
variants: {
|
|
621
|
+
size: {
|
|
622
|
+
sm: "size-control-sm [&_svg]:size-icon-sm",
|
|
623
|
+
md: "size-control [&_svg]:size-icon"
|
|
624
|
+
}
|
|
625
|
+
},
|
|
626
|
+
defaultVariants: { size: "md" }
|
|
627
|
+
}
|
|
628
|
+
);
|
|
629
|
+
var IconButton = React4.forwardRef(function IconButton2({ icon, label, shortcut, active, size, className, type = "button", ...props }, ref) {
|
|
630
|
+
return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(Tooltip, { children: [
|
|
631
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
632
|
+
"button",
|
|
633
|
+
{
|
|
634
|
+
ref,
|
|
635
|
+
type,
|
|
636
|
+
"data-slot": "icon-button",
|
|
637
|
+
"aria-label": label,
|
|
638
|
+
"aria-pressed": active === void 0 ? void 0 : active,
|
|
639
|
+
"data-active": active || void 0,
|
|
640
|
+
className: cn(iconButtonVariants({ size }), className),
|
|
641
|
+
...props,
|
|
642
|
+
children: icon
|
|
643
|
+
}
|
|
644
|
+
) }),
|
|
645
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(TooltipContent, { children: [
|
|
646
|
+
label,
|
|
647
|
+
shortcut ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "ml-2 opacity-60", children: shortcut }) : null
|
|
648
|
+
] })
|
|
649
|
+
] });
|
|
650
|
+
});
|
|
651
|
+
|
|
652
|
+
// src/desktop/field.tsx
|
|
653
|
+
var React5 = __toESM(require("react"), 1);
|
|
654
|
+
|
|
655
|
+
// src/icons.tsx
|
|
656
|
+
var import_jsx_runtime6 = require("react/jsx-runtime");
|
|
657
|
+
function lucideProps(props) {
|
|
658
|
+
return {
|
|
659
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
660
|
+
viewBox: "0 0 24 24",
|
|
661
|
+
fill: "none",
|
|
662
|
+
stroke: "currentColor",
|
|
663
|
+
strokeWidth: 2,
|
|
664
|
+
strokeLinecap: "round",
|
|
665
|
+
strokeLinejoin: "round",
|
|
666
|
+
"aria-hidden": true,
|
|
667
|
+
...props
|
|
668
|
+
};
|
|
669
|
+
}
|
|
670
|
+
function Loader2Icon(props) {
|
|
671
|
+
return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("svg", { ...lucideProps(props), children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("path", { d: "M21 12a9 9 0 1 1-6.219-8.56" }) });
|
|
672
|
+
}
|
|
673
|
+
function ChevronRightIcon(props) {
|
|
674
|
+
return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("svg", { ...lucideProps(props), children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("path", { d: "m9 18 6-6-6-6" }) });
|
|
675
|
+
}
|
|
676
|
+
function ChevronDownIcon(props) {
|
|
677
|
+
return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("svg", { ...lucideProps(props), children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("path", { d: "m6 9 6 6 6-6" }) });
|
|
678
|
+
}
|
|
679
|
+
function MoreHorizontalIcon(props) {
|
|
680
|
+
return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("svg", { ...lucideProps(props), children: [
|
|
681
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("circle", { cx: "12", cy: "12", r: "1" }),
|
|
682
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("circle", { cx: "19", cy: "12", r: "1" }),
|
|
683
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("circle", { cx: "5", cy: "12", r: "1" })
|
|
684
|
+
] });
|
|
685
|
+
}
|
|
686
|
+
function ChevronsUpDownIcon(props) {
|
|
687
|
+
return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("svg", { ...lucideProps(props), children: [
|
|
688
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("path", { d: "m7 15 5 5 5-5" }),
|
|
689
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("path", { d: "m7 9 5-5 5 5" })
|
|
690
|
+
] });
|
|
691
|
+
}
|
|
692
|
+
function XIcon(props) {
|
|
693
|
+
return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("svg", { ...lucideProps(props), children: [
|
|
694
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("path", { d: "M18 6 6 18" }),
|
|
695
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("path", { d: "m6 6 12 12" })
|
|
696
|
+
] });
|
|
697
|
+
}
|
|
698
|
+
function radixProps(props) {
|
|
699
|
+
return {
|
|
700
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
701
|
+
viewBox: "0 0 15 15",
|
|
702
|
+
width: 15,
|
|
703
|
+
height: 15,
|
|
704
|
+
fill: "none",
|
|
705
|
+
"aria-hidden": true,
|
|
706
|
+
...props
|
|
707
|
+
};
|
|
708
|
+
}
|
|
709
|
+
function MagnifyingGlassIcon(props) {
|
|
710
|
+
return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("svg", { ...radixProps(props), children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
711
|
+
"path",
|
|
712
|
+
{
|
|
713
|
+
d: "M10 6.5C10 8.433 8.433 10 6.5 10C4.567 10 3 8.433 3 6.5C3 4.567 4.567 3 6.5 3C8.433 3 10 4.567 10 6.5ZM9.30884 10.0159C8.53901 10.6318 7.56251 11 6.5 11C4.01472 11 2 8.98528 2 6.5C2 4.01472 4.01472 2 6.5 2C8.98528 2 11 4.01472 11 6.5C11 7.56251 10.6318 8.53901 10.0159 9.30884L12.8536 12.1464C13.0488 12.3417 13.0488 12.6583 12.8536 12.8536C12.6583 13.0488 12.3417 13.0488 12.1464 12.8536L9.30884 10.0159Z",
|
|
714
|
+
fill: "currentColor",
|
|
715
|
+
fillRule: "evenodd",
|
|
716
|
+
clipRule: "evenodd"
|
|
717
|
+
}
|
|
718
|
+
) });
|
|
719
|
+
}
|
|
720
|
+
function CheckIcon(props) {
|
|
721
|
+
return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("svg", { ...radixProps(props), children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
722
|
+
"path",
|
|
723
|
+
{
|
|
724
|
+
d: "M11.4669 3.72684C11.7558 3.91574 11.8369 4.30308 11.648 4.59198L7.39799 11.092C7.29783 11.2452 7.13556 11.3467 6.95402 11.3699C6.77247 11.3931 6.58989 11.3355 6.45446 11.2124L3.70446 8.71241C3.44905 8.48022 3.43023 8.08494 3.66242 7.82953C3.89461 7.57412 4.28989 7.55529 4.5453 7.78749L6.75292 9.79441L10.6018 3.90792C10.7907 3.61902 11.178 3.53795 11.4669 3.72684Z",
|
|
725
|
+
fill: "currentColor",
|
|
726
|
+
fillRule: "evenodd",
|
|
727
|
+
clipRule: "evenodd"
|
|
728
|
+
}
|
|
729
|
+
) });
|
|
730
|
+
}
|
|
731
|
+
function DividerHorizontalIcon(props) {
|
|
732
|
+
return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("svg", { ...radixProps(props), children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
733
|
+
"path",
|
|
734
|
+
{
|
|
735
|
+
d: "M2 7.5C2 7.22386 2.22386 7 2.5 7H12.5C12.7761 7 13 7.22386 13 7.5C13 7.77614 12.7761 8 12.5 8H2.5C2.22386 8 2 7.77614 2 7.5Z",
|
|
736
|
+
fill: "currentColor",
|
|
737
|
+
fillRule: "evenodd",
|
|
738
|
+
clipRule: "evenodd"
|
|
739
|
+
}
|
|
740
|
+
) });
|
|
741
|
+
}
|
|
742
|
+
function DotFilledIcon(props) {
|
|
743
|
+
return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("svg", { ...radixProps(props), children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
744
|
+
"path",
|
|
745
|
+
{
|
|
746
|
+
d: "M9.875 7.5C9.875 8.81168 8.81168 9.875 7.5 9.875C6.18832 9.875 5.125 8.81168 5.125 7.5C5.125 6.18832 6.18832 5.125 7.5 5.125C8.81168 5.125 9.875 6.18832 9.875 7.5Z",
|
|
747
|
+
fill: "currentColor"
|
|
748
|
+
}
|
|
749
|
+
) });
|
|
750
|
+
}
|
|
751
|
+
function RadixChevronRightIcon(props) {
|
|
752
|
+
return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("svg", { ...radixProps(props), children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
753
|
+
"path",
|
|
754
|
+
{
|
|
755
|
+
d: "M6.1584 3.13508C6.35985 2.94621 6.67627 2.95642 6.86514 3.15788L10.6151 7.15788C10.7954 7.3502 10.7954 7.64949 10.6151 7.84182L6.86514 11.8418C6.67627 12.0433 6.35985 12.0535 6.1584 11.8646C5.95694 11.6757 5.94673 11.3593 6.1356 11.1579L9.565 7.49985L6.1356 3.84182C5.94673 3.64036 5.95694 3.32394 6.1584 3.13508Z",
|
|
756
|
+
fill: "currentColor",
|
|
757
|
+
fillRule: "evenodd",
|
|
758
|
+
clipRule: "evenodd"
|
|
759
|
+
}
|
|
760
|
+
) });
|
|
761
|
+
}
|
|
762
|
+
function RadixChevronDownIcon(props) {
|
|
763
|
+
return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("svg", { ...radixProps(props), children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
764
|
+
"path",
|
|
765
|
+
{
|
|
766
|
+
d: "M3.13523 6.15803C3.3241 5.95657 3.64052 5.94637 3.84197 6.13523L7.5 9.56464L11.158 6.13523C11.3595 5.94637 11.6759 5.95657 11.8648 6.15803C12.0536 6.35949 12.0434 6.67591 11.842 6.86477L7.84197 10.6148C7.64964 10.7951 7.35036 10.7951 7.15803 10.6148L3.15803 6.86477C2.95657 6.67591 2.94637 6.35949 3.13523 6.15803Z",
|
|
767
|
+
fill: "currentColor",
|
|
768
|
+
fillRule: "evenodd",
|
|
769
|
+
clipRule: "evenodd"
|
|
770
|
+
}
|
|
771
|
+
) });
|
|
772
|
+
}
|
|
773
|
+
function RadixChevronUpIcon(props) {
|
|
774
|
+
return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("svg", { ...radixProps(props), children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
775
|
+
"path",
|
|
776
|
+
{
|
|
777
|
+
d: "M3.13523 8.84197C3.3241 9.04343 3.64052 9.05363 3.84197 8.86477L7.5 5.43536L11.158 8.86477C11.3595 9.05363 11.6759 9.04343 11.8648 8.84197C12.0536 8.64051 12.0434 8.32409 11.842 8.13523L7.84197 4.38523C7.64964 4.20492 7.35036 4.20492 7.15803 4.38523L3.15803 8.13523C2.95657 8.32409 2.94637 8.64051 3.13523 8.84197Z",
|
|
778
|
+
fill: "currentColor",
|
|
779
|
+
fillRule: "evenodd",
|
|
780
|
+
clipRule: "evenodd"
|
|
781
|
+
}
|
|
782
|
+
) });
|
|
783
|
+
}
|
|
784
|
+
function DragHandleDots2Icon(props) {
|
|
785
|
+
return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("svg", { ...radixProps(props), children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
786
|
+
"path",
|
|
787
|
+
{
|
|
788
|
+
d: "M4.5 2.5C5.05228 2.5 5.5 2.05228 5.5 1.5C5.5 0.947715 5.05228 0.5 4.5 0.5C3.94772 0.5 3.5 0.947715 3.5 1.5C3.5 2.05228 3.94772 2.5 4.5 2.5ZM4.5 6.5C5.05228 6.5 5.5 6.05229 5.5 5.5C5.5 4.94771 5.05228 4.5 4.5 4.5C3.94772 4.5 3.5 4.94771 3.5 5.5C3.5 6.05229 3.94772 6.5 4.5 6.5ZM5.5 9.5C5.5 10.0523 5.05228 10.5 4.5 10.5C3.94772 10.5 3.5 10.0523 3.5 9.5C3.5 8.94771 3.94772 8.5 4.5 8.5C5.05228 8.5 5.5 8.94771 5.5 9.5ZM4.5 14.5C5.05228 14.5 5.5 14.0523 5.5 13.5C5.5 12.9477 5.05228 12.5 4.5 12.5C3.94772 12.5 3.5 12.9477 3.5 13.5C3.5 14.0523 3.94772 14.5 4.5 14.5ZM11.5 1.5C11.5 2.05228 11.0523 2.5 10.5 2.5C9.94771 2.5 9.5 2.05228 9.5 1.5C9.5 0.947715 9.94771 0.5 10.5 0.5C11.0523 0.5 11.5 0.947715 11.5 1.5ZM10.5 6.5C11.0523 6.5 11.5 6.05229 11.5 5.5C11.5 4.94771 11.0523 4.5 10.5 4.5C9.94771 4.5 9.5 4.94771 9.5 5.5C9.5 6.05229 9.94771 6.5 10.5 6.5ZM11.5 9.5C11.5 10.0523 11.0523 10.5 10.5 10.5C9.94771 10.5 9.5 10.0523 9.5 9.5C9.5 8.94771 9.94771 8.5 10.5 8.5C11.0523 8.5 11.5 8.94771 11.5 9.5ZM10.5 14.5C11.0523 14.5 11.5 14.0523 11.5 13.5C11.5 12.9477 11.0523 12.5 10.5 12.5C9.94771 12.5 9.5 12.9477 9.5 13.5C9.5 14.0523 9.94771 14.5 10.5 14.5Z",
|
|
789
|
+
fill: "currentColor",
|
|
790
|
+
fillRule: "evenodd",
|
|
791
|
+
clipRule: "evenodd"
|
|
792
|
+
}
|
|
793
|
+
) });
|
|
794
|
+
}
|
|
795
|
+
|
|
796
|
+
// src/desktop/field.tsx
|
|
797
|
+
var import_jsx_runtime7 = require("react/jsx-runtime");
|
|
798
|
+
var FIELD = "focus-ring w-full min-w-0 rounded-control bg-control text-body text-label shadow-control placeholder:text-label-tertiary disabled:opacity-40 read-only:bg-fill";
|
|
799
|
+
var TextField = React5.forwardRef(function TextField2({ size = "md", invalid, className, ...props }, ref) {
|
|
800
|
+
return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
801
|
+
"input",
|
|
802
|
+
{
|
|
803
|
+
ref,
|
|
804
|
+
"data-slot": "text-field",
|
|
805
|
+
"aria-invalid": invalid || void 0,
|
|
806
|
+
className: cn(
|
|
807
|
+
FIELD,
|
|
808
|
+
"select-text px-2",
|
|
809
|
+
size === "sm" ? "h-control-sm text-callout" : "h-control",
|
|
810
|
+
invalid && "outline outline-2 outline-system-red/60",
|
|
811
|
+
className
|
|
812
|
+
),
|
|
813
|
+
...props
|
|
814
|
+
}
|
|
815
|
+
);
|
|
816
|
+
});
|
|
817
|
+
var TextArea = React5.forwardRef(function TextArea2({ invalid, className, rows = 4, ...props }, ref) {
|
|
818
|
+
return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
819
|
+
"textarea",
|
|
820
|
+
{
|
|
821
|
+
ref,
|
|
822
|
+
rows,
|
|
823
|
+
"data-slot": "text-area",
|
|
824
|
+
"aria-invalid": invalid || void 0,
|
|
825
|
+
className: cn(FIELD, "select-text resize-none px-2 py-1.5", invalid && "outline outline-2 outline-system-red/60", className),
|
|
826
|
+
...props
|
|
827
|
+
}
|
|
828
|
+
);
|
|
829
|
+
});
|
|
830
|
+
var SearchField = React5.forwardRef(function SearchField2({ value, onValueChange, size = "md", placeholder = "Search", className, onKeyDown, ...props }, ref) {
|
|
831
|
+
return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
|
|
832
|
+
"div",
|
|
833
|
+
{
|
|
834
|
+
"data-slot": "search-field",
|
|
835
|
+
className: cn("relative flex items-center", size === "sm" ? "h-control-sm" : "h-control", className),
|
|
836
|
+
children: [
|
|
837
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(MagnifyingGlassIcon, { className: "pointer-events-none absolute left-2 size-icon-sm text-label-tertiary" }),
|
|
838
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
839
|
+
"input",
|
|
840
|
+
{
|
|
841
|
+
ref,
|
|
842
|
+
type: "search",
|
|
843
|
+
value,
|
|
844
|
+
placeholder,
|
|
845
|
+
onChange: (event) => onValueChange(event.target.value),
|
|
846
|
+
onKeyDown: (event) => {
|
|
847
|
+
if (event.key === "Escape" && value) {
|
|
848
|
+
event.preventDefault();
|
|
849
|
+
onValueChange("");
|
|
850
|
+
}
|
|
851
|
+
onKeyDown?.(event);
|
|
852
|
+
},
|
|
853
|
+
className: cn(
|
|
854
|
+
FIELD,
|
|
855
|
+
"h-full select-text bg-fill pl-7 pr-7 shadow-none [&::-webkit-search-cancel-button]:hidden",
|
|
856
|
+
size === "sm" && "text-callout"
|
|
857
|
+
),
|
|
858
|
+
...props
|
|
859
|
+
}
|
|
860
|
+
),
|
|
861
|
+
value ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
862
|
+
"button",
|
|
863
|
+
{
|
|
864
|
+
type: "button",
|
|
865
|
+
"aria-label": "Clear search",
|
|
866
|
+
onClick: () => onValueChange(""),
|
|
867
|
+
className: "focus-ring absolute right-1.5 flex size-icon items-center justify-center rounded-full bg-label-tertiary text-content hover:bg-label-secondary",
|
|
868
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(XIcon, { className: "size-2.5", strokeWidth: 3 })
|
|
869
|
+
}
|
|
870
|
+
) : null
|
|
871
|
+
]
|
|
872
|
+
}
|
|
873
|
+
);
|
|
874
|
+
});
|
|
875
|
+
|
|
876
|
+
// src/desktop/select.tsx
|
|
877
|
+
var SelectPrimitive2 = __toESM(require("@radix-ui/react-select"), 1);
|
|
878
|
+
|
|
879
|
+
// src/select.tsx
|
|
880
|
+
var SelectPrimitive = __toESM(require("@radix-ui/react-select"), 1);
|
|
881
|
+
var import_class_variance_authority2 = require("class-variance-authority");
|
|
882
|
+
var React6 = __toESM(require("react"), 1);
|
|
883
|
+
|
|
884
|
+
// src/field-surface.ts
|
|
885
|
+
var FIELD_SURFACE_CLASS = "border border-border bg-card";
|
|
886
|
+
|
|
887
|
+
// src/select.tsx
|
|
888
|
+
var import_jsx_runtime8 = require("react/jsx-runtime");
|
|
889
|
+
var selectTriggerVariants = (0, import_class_variance_authority2.cva)(
|
|
890
|
+
`flex w-full items-center justify-between gap-2 whitespace-nowrap rounded-md ${FIELD_SURFACE_CLASS} text-sm shadow-sm ring-offset-background placeholder:text-muted-foreground transition-colors hover:bg-accent hover:border-accent-foreground/20 focus:outline-none focus:ring-1 focus:ring-primary/30 focus:border-primary/40 disabled:cursor-not-allowed disabled:opacity-50 [&>span]:min-w-0 [&>span]:line-clamp-1`,
|
|
891
|
+
{
|
|
892
|
+
variants: {
|
|
893
|
+
size: {
|
|
894
|
+
sm: "h-7 px-2 py-1 text-xs",
|
|
895
|
+
default: "h-9 px-3 py-1",
|
|
896
|
+
lg: "h-10 px-3 py-2"
|
|
897
|
+
}
|
|
898
|
+
},
|
|
899
|
+
defaultVariants: {
|
|
900
|
+
size: "default"
|
|
901
|
+
}
|
|
902
|
+
}
|
|
903
|
+
);
|
|
904
|
+
function SelectChevron({ className, size = "default" }) {
|
|
905
|
+
return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
|
|
906
|
+
RadixChevronDownIcon,
|
|
907
|
+
{
|
|
908
|
+
"aria-hidden": "true",
|
|
909
|
+
"data-select-chevron": "",
|
|
910
|
+
className: cn2(size === "sm" ? "h-3.5 w-3.5" : "h-4 w-4", "shrink-0 opacity-50", className)
|
|
911
|
+
}
|
|
912
|
+
);
|
|
913
|
+
}
|
|
914
|
+
var SelectTrigger = React6.forwardRef(({ className, children, hideArrow = false, size, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
|
|
915
|
+
SelectPrimitive.Trigger,
|
|
916
|
+
{
|
|
917
|
+
ref,
|
|
918
|
+
className: cn2(selectTriggerVariants({ size, className })),
|
|
919
|
+
...props,
|
|
920
|
+
children: [
|
|
921
|
+
children,
|
|
922
|
+
!hideArrow && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(SelectPrimitive.Icon, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(SelectChevron, { size: size === "sm" ? "sm" : "default" }) })
|
|
923
|
+
]
|
|
924
|
+
}
|
|
925
|
+
));
|
|
926
|
+
SelectTrigger.displayName = SelectPrimitive.Trigger.displayName;
|
|
927
|
+
var SelectScrollUpButton = React6.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
|
|
928
|
+
SelectPrimitive.ScrollUpButton,
|
|
929
|
+
{
|
|
930
|
+
ref,
|
|
931
|
+
className: cn2(
|
|
932
|
+
"flex cursor-default items-center justify-center py-1",
|
|
933
|
+
className
|
|
934
|
+
),
|
|
935
|
+
...props,
|
|
936
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(RadixChevronUpIcon, {})
|
|
937
|
+
}
|
|
938
|
+
));
|
|
939
|
+
SelectScrollUpButton.displayName = SelectPrimitive.ScrollUpButton.displayName;
|
|
940
|
+
var SelectScrollDownButton = React6.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
|
|
941
|
+
SelectPrimitive.ScrollDownButton,
|
|
942
|
+
{
|
|
943
|
+
ref,
|
|
944
|
+
className: cn2(
|
|
945
|
+
"flex cursor-default items-center justify-center py-1",
|
|
946
|
+
className
|
|
947
|
+
),
|
|
948
|
+
...props,
|
|
949
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(RadixChevronDownIcon, {})
|
|
950
|
+
}
|
|
951
|
+
));
|
|
952
|
+
SelectScrollDownButton.displayName = SelectPrimitive.ScrollDownButton.displayName;
|
|
953
|
+
var SelectContent = React6.forwardRef(
|
|
954
|
+
({
|
|
955
|
+
className,
|
|
956
|
+
children,
|
|
957
|
+
position = "popper",
|
|
958
|
+
container,
|
|
959
|
+
animated = true,
|
|
960
|
+
...props
|
|
961
|
+
}, ref) => {
|
|
962
|
+
const portalContainer = usePortalContainer(container);
|
|
963
|
+
return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(SelectPrimitive.Portal, { container: portalContainer, children: /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
|
|
964
|
+
SelectPrimitive.Content,
|
|
965
|
+
{
|
|
966
|
+
ref,
|
|
967
|
+
className: cn2(
|
|
968
|
+
"relative z-[10001] max-h-96 min-w-[8rem] max-w-[calc(100vw-2rem)] overflow-hidden rounded-md border bg-popover text-popover-foreground shadow-md",
|
|
969
|
+
animated && MOTION_POPPER,
|
|
970
|
+
// The resting gap from the trigger. It is the `translate` property
|
|
971
|
+
// (POPPER_OFFSET), never Tailwind translate utilities: those compose
|
|
972
|
+
// with `transform` on a v4 host and are REPLACED by it on a v3 one,
|
|
973
|
+
// so on v3 the animation ate the gap and the surface snapped 4px at
|
|
974
|
+
// the end. Same law as the centred dialog card — see motion.ts.
|
|
975
|
+
position === "popper" && POPPER_OFFSET,
|
|
976
|
+
className
|
|
977
|
+
),
|
|
978
|
+
position,
|
|
979
|
+
...props,
|
|
980
|
+
children: [
|
|
981
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(SelectScrollUpButton, {}),
|
|
982
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
|
|
983
|
+
SelectPrimitive.Viewport,
|
|
984
|
+
{
|
|
985
|
+
className: cn2(
|
|
986
|
+
"max-w-full overflow-y-auto p-0.5",
|
|
987
|
+
position === "popper" && "w-full min-w-[var(--radix-select-trigger-width)] max-h-[var(--radix-select-content-available-height)]"
|
|
988
|
+
),
|
|
989
|
+
children
|
|
990
|
+
}
|
|
991
|
+
),
|
|
992
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(SelectScrollDownButton, {})
|
|
993
|
+
]
|
|
994
|
+
}
|
|
995
|
+
) });
|
|
996
|
+
}
|
|
997
|
+
);
|
|
998
|
+
SelectContent.displayName = SelectPrimitive.Content.displayName;
|
|
999
|
+
var SelectLabel = React6.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
|
|
1000
|
+
SelectPrimitive.Label,
|
|
1001
|
+
{
|
|
1002
|
+
ref,
|
|
1003
|
+
className: cn2("px-2 py-1.5 text-sm font-semibold", className),
|
|
1004
|
+
...props
|
|
1005
|
+
}
|
|
1006
|
+
));
|
|
1007
|
+
SelectLabel.displayName = SelectPrimitive.Label.displayName;
|
|
1008
|
+
var SelectItem = React6.forwardRef(({ className, children, description, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
|
|
1009
|
+
SelectPrimitive.Item,
|
|
1010
|
+
{
|
|
1011
|
+
ref,
|
|
1012
|
+
className: cn2(
|
|
1013
|
+
"relative flex w-full min-w-0 select-none rounded-sm py-1 pl-2 pr-8 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[disabled]:opacity-50",
|
|
1014
|
+
description ? "flex-col items-start gap-0.5 py-1.5" : "items-center",
|
|
1015
|
+
className
|
|
1016
|
+
),
|
|
1017
|
+
...props,
|
|
1018
|
+
children: [
|
|
1019
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: "absolute right-2 flex h-3.5 w-3.5 items-center justify-center", children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(SelectPrimitive.ItemIndicator, { children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(CheckIcon, { className: "h-4 w-4" }) }) }),
|
|
1020
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: "min-w-0 whitespace-normal break-words [overflow-wrap:anywhere]", children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(SelectPrimitive.ItemText, { children }) }),
|
|
1021
|
+
description ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: "block min-w-0 break-words text-xs leading-snug text-muted-foreground [overflow-wrap:anywhere]", children: description }) : null
|
|
1022
|
+
]
|
|
1023
|
+
}
|
|
1024
|
+
));
|
|
1025
|
+
SelectItem.displayName = SelectPrimitive.Item.displayName;
|
|
1026
|
+
var SelectSeparator = React6.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
|
|
1027
|
+
SelectPrimitive.Separator,
|
|
1028
|
+
{
|
|
1029
|
+
ref,
|
|
1030
|
+
className: cn2("-mx-1 my-1 h-px bg-muted", className),
|
|
1031
|
+
...props
|
|
1032
|
+
}
|
|
1033
|
+
));
|
|
1034
|
+
SelectSeparator.displayName = SelectPrimitive.Separator.displayName;
|
|
1035
|
+
|
|
1036
|
+
// src/desktop/select.tsx
|
|
1037
|
+
var import_jsx_runtime9 = require("react/jsx-runtime");
|
|
1038
|
+
function Select({
|
|
1039
|
+
value,
|
|
1040
|
+
onValueChange,
|
|
1041
|
+
options,
|
|
1042
|
+
placeholder,
|
|
1043
|
+
size = "md",
|
|
1044
|
+
disabled,
|
|
1045
|
+
id,
|
|
1046
|
+
className,
|
|
1047
|
+
"aria-label": ariaLabel,
|
|
1048
|
+
...data
|
|
1049
|
+
}) {
|
|
1050
|
+
return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(SelectPrimitive2.Root, { ...value === void 0 ? {} : { value }, onValueChange, disabled: disabled ?? false, children: [
|
|
1051
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(
|
|
1052
|
+
SelectPrimitive2.Trigger,
|
|
1053
|
+
{
|
|
1054
|
+
...data,
|
|
1055
|
+
id,
|
|
1056
|
+
"aria-label": ariaLabel,
|
|
1057
|
+
"data-slot": "select",
|
|
1058
|
+
className: cn(
|
|
1059
|
+
"focus-ring inline-flex min-w-0 items-center justify-between gap-2 rounded-control bg-control pl-2.5 pr-1.5 text-label shadow-control hover:bg-fill-secondary disabled:opacity-40 data-[placeholder]:text-label-tertiary",
|
|
1060
|
+
size === "sm" ? "h-control-sm text-callout" : "h-control text-body",
|
|
1061
|
+
className
|
|
1062
|
+
),
|
|
1063
|
+
children: [
|
|
1064
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "min-w-0 truncate", children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(SelectPrimitive2.Value, { placeholder }) }),
|
|
1065
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(SelectPrimitive2.Icon, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(ChevronsUpDownIcon, { className: "size-icon-sm shrink-0 text-label-secondary" }) })
|
|
1066
|
+
]
|
|
1067
|
+
}
|
|
1068
|
+
),
|
|
1069
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(SelectContent, { children: options.map(
|
|
1070
|
+
(option, index) => option === "-" ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(SelectSeparator, {}, `sep-${index}`) : /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(SelectItem, { value: option.value, disabled: option.disabled ?? false, children: option.label }, option.value)
|
|
1071
|
+
) })
|
|
1072
|
+
] });
|
|
1073
|
+
}
|
|
1074
|
+
|
|
1075
|
+
// src/desktop/segmented.tsx
|
|
1076
|
+
var ToggleGroup = __toESM(require("@radix-ui/react-toggle-group"), 1);
|
|
1077
|
+
var import_jsx_runtime10 = require("react/jsx-runtime");
|
|
1078
|
+
function Segmented({
|
|
1079
|
+
value,
|
|
1080
|
+
onValueChange,
|
|
1081
|
+
segments,
|
|
1082
|
+
size = "md",
|
|
1083
|
+
className,
|
|
1084
|
+
"aria-label": ariaLabel,
|
|
1085
|
+
...data
|
|
1086
|
+
}) {
|
|
1087
|
+
return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
1088
|
+
ToggleGroup.Root,
|
|
1089
|
+
{
|
|
1090
|
+
...data,
|
|
1091
|
+
type: "single",
|
|
1092
|
+
value,
|
|
1093
|
+
"aria-label": ariaLabel,
|
|
1094
|
+
onValueChange: (next) => {
|
|
1095
|
+
if (next) onValueChange(next);
|
|
1096
|
+
},
|
|
1097
|
+
"data-slot": "segmented",
|
|
1098
|
+
className: cn(
|
|
1099
|
+
"inline-flex items-stretch gap-0.5 rounded-control bg-fill-secondary p-0.5",
|
|
1100
|
+
size === "sm" ? "h-control-sm text-callout" : "h-control text-body",
|
|
1101
|
+
className
|
|
1102
|
+
),
|
|
1103
|
+
children: segments.map((segment) => /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
1104
|
+
ToggleGroup.Item,
|
|
1105
|
+
{
|
|
1106
|
+
value: segment.value,
|
|
1107
|
+
"aria-label": segment.ariaLabel,
|
|
1108
|
+
disabled: segment.disabled,
|
|
1109
|
+
className: "focus-ring inline-flex min-w-0 flex-1 items-center justify-center gap-1 rounded-control px-2.5 text-label-secondary transition-colors duration-100 hover:text-label disabled:opacity-40 data-[state=on]:bg-control data-[state=on]:text-label data-[state=on]:shadow-control [&_svg]:size-icon-sm",
|
|
1110
|
+
children: segment.label
|
|
1111
|
+
},
|
|
1112
|
+
segment.value
|
|
1113
|
+
))
|
|
1114
|
+
}
|
|
1115
|
+
);
|
|
1116
|
+
}
|
|
1117
|
+
|
|
1118
|
+
// src/switch.tsx
|
|
1119
|
+
var SwitchPrimitives = __toESM(require("@radix-ui/react-switch"), 1);
|
|
1120
|
+
var React7 = __toESM(require("react"), 1);
|
|
1121
|
+
var import_jsx_runtime11 = require("react/jsx-runtime");
|
|
1122
|
+
var SWITCH_SIZES = {
|
|
1123
|
+
sm: {
|
|
1124
|
+
root: "h-4 w-9 border-border data-[state=unchecked]:bg-input",
|
|
1125
|
+
thumb: "h-4 w-4 border border-primary data-[state=checked]:translate-x-4 data-[state=unchecked]:translate-x-0"
|
|
1126
|
+
},
|
|
1127
|
+
md: {
|
|
1128
|
+
root: "h-5 w-9 border-transparent data-[state=unchecked]:bg-input",
|
|
1129
|
+
thumb: "h-4 w-4 data-[state=checked]:translate-x-4 data-[state=unchecked]:translate-x-0"
|
|
1130
|
+
}
|
|
1131
|
+
};
|
|
1132
|
+
var Switch = React7.forwardRef(({ className, size = "md", ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
|
|
1133
|
+
SwitchPrimitives.Root,
|
|
1134
|
+
{
|
|
1135
|
+
ref,
|
|
1136
|
+
"data-slot": "switch",
|
|
1137
|
+
className: cn2(
|
|
1138
|
+
"peer inline-flex shrink-0 items-center rounded-full border-2 shadow-sm transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-primary",
|
|
1139
|
+
SWITCH_SIZES[size].root,
|
|
1140
|
+
className
|
|
1141
|
+
),
|
|
1142
|
+
...props,
|
|
1143
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
|
|
1144
|
+
SwitchPrimitives.Thumb,
|
|
1145
|
+
{
|
|
1146
|
+
"data-slot": "switch-thumb",
|
|
1147
|
+
className: cn2(
|
|
1148
|
+
"pointer-events-none block rounded-full bg-background shadow-lg ring-0 transition-transform",
|
|
1149
|
+
SWITCH_SIZES[size].thumb
|
|
1150
|
+
)
|
|
1151
|
+
}
|
|
1152
|
+
)
|
|
1153
|
+
}
|
|
1154
|
+
));
|
|
1155
|
+
Switch.displayName = SwitchPrimitives.Root.displayName;
|
|
1156
|
+
|
|
1157
|
+
// src/checkbox.tsx
|
|
1158
|
+
var CheckboxPrimitive = __toESM(require("@radix-ui/react-checkbox"), 1);
|
|
1159
|
+
var React8 = __toESM(require("react"), 1);
|
|
1160
|
+
var import_jsx_runtime12 = require("react/jsx-runtime");
|
|
1161
|
+
var CHECKBOX_SIZES = {
|
|
1162
|
+
sm: { root: "h-3.5 w-3.5 rounded-xs", glyph: "h-3 w-3" },
|
|
1163
|
+
md: { root: "h-4 w-4 rounded-sm", glyph: "h-3.5 w-3.5" }
|
|
1164
|
+
};
|
|
1165
|
+
var Checkbox = React8.forwardRef(({ className, size = "md", ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
|
|
1166
|
+
CheckboxPrimitive.Root,
|
|
1167
|
+
{
|
|
1168
|
+
ref,
|
|
1169
|
+
"data-slot": "checkbox",
|
|
1170
|
+
className: cn2(
|
|
1171
|
+
"peer shrink-0 border border-primary shadow focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground data-[state=indeterminate]:bg-primary data-[state=indeterminate]:text-primary-foreground",
|
|
1172
|
+
CHECKBOX_SIZES[size].root,
|
|
1173
|
+
className
|
|
1174
|
+
),
|
|
1175
|
+
...props,
|
|
1176
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
|
|
1177
|
+
CheckboxPrimitive.Indicator,
|
|
1178
|
+
{
|
|
1179
|
+
"data-slot": "checkbox-indicator",
|
|
1180
|
+
className: "flex items-center justify-center text-current",
|
|
1181
|
+
children: props.checked === "indeterminate" ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(DividerHorizontalIcon, { className: CHECKBOX_SIZES[size].glyph }) : /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(CheckIcon, { className: CHECKBOX_SIZES[size].glyph })
|
|
1182
|
+
}
|
|
1183
|
+
)
|
|
1184
|
+
}
|
|
1185
|
+
));
|
|
1186
|
+
Checkbox.displayName = CheckboxPrimitive.Root.displayName;
|
|
1187
|
+
|
|
1188
|
+
// src/desktop/layout.tsx
|
|
1189
|
+
var React9 = __toESM(require("react"), 1);
|
|
1190
|
+
var import_jsx_runtime13 = require("react/jsx-runtime");
|
|
1191
|
+
var WIDTH = {
|
|
1192
|
+
form: "max-w-form",
|
|
1193
|
+
wide: "max-w-wide",
|
|
1194
|
+
full: "max-w-none"
|
|
1195
|
+
};
|
|
1196
|
+
var Page = React9.forwardRef(function Page2({ width = "wide", header, className, children, ...props }, ref) {
|
|
1197
|
+
return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { ref, "data-slot": "page", className: cn("h-full overflow-y-auto bg-content", className), ...props, children: /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: cn("mx-auto flex w-full flex-col gap-6 px-8 pb-10 pt-6", width === "full" ? WIDTH.full : WIDTH.wide), children: [
|
|
1198
|
+
header,
|
|
1199
|
+
width === "form" ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { className: cn("flex w-full flex-col gap-6", WIDTH.form), children }) : children
|
|
1200
|
+
] }) });
|
|
1201
|
+
});
|
|
1202
|
+
function PageHeader({ title, subtitle, icon, actions, className, ...props }) {
|
|
1203
|
+
return /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("header", { "data-slot": "page-header", className: cn("flex min-h-control-lg items-center gap-3", className), ...props, children: [
|
|
1204
|
+
icon ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "flex size-icon-xl shrink-0 items-center justify-center text-label-secondary [&_svg]:size-icon-lg", children: icon }) : null,
|
|
1205
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "flex min-w-0 flex-1 flex-col", children: [
|
|
1206
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)(Text, { as: "h1", variant: "title-2", truncate: true, children: title }),
|
|
1207
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)(Text, { variant: "callout", tone: "secondary", truncate: true, "aria-hidden": subtitle ? void 0 : true, children: subtitle ?? "\xA0" })
|
|
1208
|
+
] }),
|
|
1209
|
+
actions ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { className: "flex shrink-0 items-center gap-2", children: actions }) : null
|
|
1210
|
+
] });
|
|
1211
|
+
}
|
|
1212
|
+
function Toolbar({ draggable, className, ...props }) {
|
|
1213
|
+
return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
1214
|
+
"div",
|
|
1215
|
+
{
|
|
1216
|
+
role: "toolbar",
|
|
1217
|
+
"data-slot": "toolbar",
|
|
1218
|
+
className: cn(
|
|
1219
|
+
"flex h-toolbar shrink-0 items-center gap-2 px-3 [&_button]:window-no-drag [&_input]:window-no-drag",
|
|
1220
|
+
draggable && "window-drag",
|
|
1221
|
+
className
|
|
1222
|
+
),
|
|
1223
|
+
...props
|
|
1224
|
+
}
|
|
1225
|
+
);
|
|
1226
|
+
}
|
|
1227
|
+
function ToolbarSpacer() {
|
|
1228
|
+
return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { "aria-hidden": true, className: "flex-1" });
|
|
1229
|
+
}
|
|
1230
|
+
function ToolbarDivider() {
|
|
1231
|
+
return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { "aria-hidden": true, className: "mx-1 h-icon w-px bg-separator" });
|
|
1232
|
+
}
|
|
1233
|
+
|
|
1234
|
+
// src/desktop/group.tsx
|
|
1235
|
+
var React10 = __toESM(require("react"), 1);
|
|
1236
|
+
var import_jsx_runtime14 = require("react/jsx-runtime");
|
|
1237
|
+
function Section({ title, actions, footer, appearance = "grouped", className, children, ...props }) {
|
|
1238
|
+
return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("section", { "data-slot": "section", className: cn("flex flex-col gap-2", appearance === "grouped" && "max-w-form", className), ...props, children: [
|
|
1239
|
+
title || actions ? /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: "flex min-h-control items-end gap-2", children: [
|
|
1240
|
+
title ? /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(Text, { as: "h2", variant: "headline", className: "flex-1", truncate: true, children: title }) : /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { className: "flex-1" }),
|
|
1241
|
+
actions ? /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("div", { className: "flex items-center gap-2", children: actions }) : null
|
|
1242
|
+
] }) : null,
|
|
1243
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
1244
|
+
"div",
|
|
1245
|
+
{
|
|
1246
|
+
"data-appearance": appearance,
|
|
1247
|
+
className: cn(
|
|
1248
|
+
"flex flex-col [&>[data-slot=row]+[data-slot=row]]:before:block",
|
|
1249
|
+
appearance === "grouped" && "max-w-form overflow-hidden rounded-group bg-group shadow-raised"
|
|
1250
|
+
),
|
|
1251
|
+
children
|
|
1252
|
+
}
|
|
1253
|
+
),
|
|
1254
|
+
footer ? /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(Text, { as: "p", variant: "caption", tone: "secondary", children: footer }) : null
|
|
1255
|
+
] });
|
|
1256
|
+
}
|
|
1257
|
+
function SectionContent({ className, ...props }) {
|
|
1258
|
+
return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("div", { "data-slot": "row", className: cn("relative px-3 py-3", className), ...props });
|
|
1259
|
+
}
|
|
1260
|
+
var Row = React10.forwardRef(function Row2({ icon, title, subtitle, value, accessory, chevron, onSelect, selected, disabled, destructive, mono, className, style, ...props }, ref) {
|
|
1261
|
+
const interactive = Boolean(onSelect) && !disabled;
|
|
1262
|
+
const body = /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(import_jsx_runtime14.Fragment, { children: [
|
|
1263
|
+
icon ? /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { className: "flex size-icon-lg shrink-0 items-center justify-center text-label-secondary [&_svg]:size-icon", children: icon }) : null,
|
|
1264
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("span", { className: "flex min-w-0 flex-1 flex-col", children: [
|
|
1265
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)(Text, { tone: destructive ? "danger" : "primary", mono, truncate: true, className: cn(selected && "text-inherit"), children: title }),
|
|
1266
|
+
subtitle ? /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(Text, { variant: "callout", tone: "secondary", truncate: true, className: cn(selected && "text-inherit opacity-80"), children: subtitle }) : null
|
|
1267
|
+
] }),
|
|
1268
|
+
value !== void 0 && value !== null ? /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(Text, { tone: "secondary", tabular: true, truncate: true, className: cn("max-w-1/2 shrink-0 text-right", selected && "text-inherit opacity-80"), children: value }) : null,
|
|
1269
|
+
accessory ? /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
1270
|
+
"span",
|
|
1271
|
+
{
|
|
1272
|
+
className: "flex shrink-0 items-center gap-2",
|
|
1273
|
+
onClick: (event) => event.stopPropagation(),
|
|
1274
|
+
onKeyDown: (event) => event.stopPropagation(),
|
|
1275
|
+
children: accessory
|
|
1276
|
+
}
|
|
1277
|
+
) : null,
|
|
1278
|
+
chevron ? /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(ChevronRightIcon, { "aria-hidden": true, className: "size-icon-sm shrink-0 text-label-tertiary" }) : null
|
|
1279
|
+
] });
|
|
1280
|
+
const rowClass = cn(
|
|
1281
|
+
"relative flex w-full items-center gap-3 px-3 text-left before:absolute before:right-0 before:top-0 before:hidden before:h-px before:bg-separator",
|
|
1282
|
+
icon ? "before:left-11" : "before:left-3",
|
|
1283
|
+
subtitle ? "min-h-row-tall py-1.5" : "min-h-row py-1",
|
|
1284
|
+
disabled && "opacity-40",
|
|
1285
|
+
selected && "bg-selection text-selection-label",
|
|
1286
|
+
className
|
|
1287
|
+
);
|
|
1288
|
+
if (interactive) {
|
|
1289
|
+
return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
1290
|
+
"button",
|
|
1291
|
+
{
|
|
1292
|
+
ref,
|
|
1293
|
+
type: "button",
|
|
1294
|
+
"data-slot": "row",
|
|
1295
|
+
"aria-current": selected || void 0,
|
|
1296
|
+
onClick: onSelect,
|
|
1297
|
+
style,
|
|
1298
|
+
className: cn(rowClass, "focus-ring -outline-offset-3 transition-colors duration-100", !selected && "hover:bg-fill active:bg-fill-secondary"),
|
|
1299
|
+
...props,
|
|
1300
|
+
children: body
|
|
1301
|
+
}
|
|
1302
|
+
);
|
|
1303
|
+
}
|
|
1304
|
+
return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("div", { ref, "data-slot": "row", style, className: rowClass, ...props, children: body });
|
|
1305
|
+
});
|
|
1306
|
+
function FieldRow({ label, field, action, onSubmit, stacked, className, ...props }) {
|
|
1307
|
+
return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(
|
|
1308
|
+
"form",
|
|
1309
|
+
{
|
|
1310
|
+
"data-slot": "row",
|
|
1311
|
+
noValidate: true,
|
|
1312
|
+
onSubmit: (event) => {
|
|
1313
|
+
event.preventDefault();
|
|
1314
|
+
onSubmit?.();
|
|
1315
|
+
},
|
|
1316
|
+
className: cn(
|
|
1317
|
+
"relative flex min-h-row w-full gap-3 px-3 py-1.5 before:absolute before:left-3 before:right-0 before:top-0 before:hidden before:h-px before:bg-separator",
|
|
1318
|
+
stacked ? "flex-col py-3" : "items-center",
|
|
1319
|
+
className
|
|
1320
|
+
),
|
|
1321
|
+
...props,
|
|
1322
|
+
children: [
|
|
1323
|
+
label ? /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(Text, { variant: "body", className: "w-32 shrink-0", truncate: true, children: label }) : null,
|
|
1324
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)("div", { className: "min-w-0 flex-1", children: field }),
|
|
1325
|
+
action ? /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("div", { className: cn("flex shrink-0 items-center gap-2", stacked && "self-end"), children: action }) : null
|
|
1326
|
+
]
|
|
1327
|
+
}
|
|
1328
|
+
);
|
|
1329
|
+
}
|
|
1330
|
+
|
|
1331
|
+
// src/desktop/code-block.tsx
|
|
1332
|
+
var React11 = __toESM(require("react"), 1);
|
|
1333
|
+
var import_jsx_runtime15 = require("react/jsx-runtime");
|
|
1334
|
+
function CodeBlock({ scroll = true, follow = false, className, children, ...props }) {
|
|
1335
|
+
const ref = React11.useRef(null);
|
|
1336
|
+
const pinned = React11.useRef(true);
|
|
1337
|
+
React11.useLayoutEffect(() => {
|
|
1338
|
+
const el = ref.current;
|
|
1339
|
+
if (follow && el && pinned.current) el.scrollTop = el.scrollHeight;
|
|
1340
|
+
}, [follow, children]);
|
|
1341
|
+
return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
|
|
1342
|
+
"pre",
|
|
1343
|
+
{
|
|
1344
|
+
ref,
|
|
1345
|
+
onScroll: (event) => {
|
|
1346
|
+
const el = event.currentTarget;
|
|
1347
|
+
pinned.current = el.scrollHeight - el.scrollTop - el.clientHeight < 8;
|
|
1348
|
+
},
|
|
1349
|
+
"data-slot": "code-block",
|
|
1350
|
+
className: cn(
|
|
1351
|
+
"m-0 select-text whitespace-pre-wrap break-words rounded-control bg-fill px-3 py-2 font-mono text-callout text-label",
|
|
1352
|
+
scroll && "max-h-80 overflow-y-auto",
|
|
1353
|
+
className
|
|
1354
|
+
),
|
|
1355
|
+
...props,
|
|
1356
|
+
children
|
|
1357
|
+
}
|
|
1358
|
+
);
|
|
1359
|
+
}
|
|
1360
|
+
|
|
1361
|
+
// src/desktop/table.tsx
|
|
1362
|
+
var React18 = __toESM(require("react"), 1);
|
|
1363
|
+
|
|
1364
|
+
// src/alert-dialog.tsx
|
|
1365
|
+
var AlertDialogPrimitive = __toESM(require("@radix-ui/react-alert-dialog"), 1);
|
|
1366
|
+
var import_react_tree = require("@ai-matrx/kit/react-tree");
|
|
1367
|
+
var React14 = __toESM(require("react"), 1);
|
|
1368
|
+
|
|
1369
|
+
// src/button.tsx
|
|
1370
|
+
var import_react_slot2 = require("@radix-ui/react-slot");
|
|
1371
|
+
var import_class_variance_authority3 = require("class-variance-authority");
|
|
1372
|
+
var React12 = __toESM(require("react"), 1);
|
|
1373
|
+
var import_jsx_runtime16 = require("react/jsx-runtime");
|
|
1374
|
+
var buttonVariants2 = (0, import_class_variance_authority3.cva)(
|
|
1375
|
+
"inline-flex 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:opacity-50 active:scale-[0.98] [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0",
|
|
1376
|
+
{
|
|
1377
|
+
variants: {
|
|
1378
|
+
variant: {
|
|
1379
|
+
default: "bg-primary text-primary-foreground shadow-sm hover:bg-primary/90",
|
|
1380
|
+
/**
|
|
1381
|
+
* An explicit spelling of `default`, identical by construction. It is
|
|
1382
|
+
* not decoration: matrx-frontend's `ButtonMine` fork carried it, ~17
|
|
1383
|
+
* call sites say `variant="primary"`, and the alternative to keeping
|
|
1384
|
+
* the word was a host shim translating a prop — the massage C22 bans.
|
|
1385
|
+
*/
|
|
1386
|
+
primary: "bg-primary text-primary-foreground shadow-sm hover:bg-primary/90",
|
|
1387
|
+
destructive: "bg-destructive text-destructive-foreground shadow-sm hover:bg-destructive/90",
|
|
1388
|
+
/** The affirmative twin of `destructive` — confirm, approve, publish. */
|
|
1389
|
+
success: "bg-success text-success-foreground shadow-sm hover:bg-success/90",
|
|
1390
|
+
outline: "border border-border bg-card shadow-sm hover:bg-accent hover:text-accent-foreground",
|
|
1391
|
+
secondary: "bg-secondary text-secondary-foreground shadow-sm hover:bg-secondary/80",
|
|
1392
|
+
ghost: "hover:bg-accent hover:text-accent-foreground",
|
|
1393
|
+
link: "text-primary underline-offset-4 hover:underline",
|
|
1394
|
+
subtle: "border border-transparent bg-muted/50 text-foreground hover:border-border hover:bg-muted"
|
|
1395
|
+
},
|
|
1396
|
+
/**
|
|
1397
|
+
* ONE size scale, xs → 3xl, plus the two icon boxes. The top and bottom
|
|
1398
|
+
* of the scale came in from matrx-frontend's `ButtonMine` fork (2026-09-07):
|
|
1399
|
+
* a dense toolbar needs `xs`, and a marketing/hero call to action needs
|
|
1400
|
+
* `xl`+. A host that has to reach outside this scale forks the button,
|
|
1401
|
+
* which is exactly how `ButtonMine` was born.
|
|
1402
|
+
*/
|
|
1403
|
+
size: {
|
|
1404
|
+
xs: "h-6 rounded-md px-1 text-xs",
|
|
1405
|
+
sm: "h-8 rounded-md px-3 text-xs",
|
|
1406
|
+
default: "h-9 px-4 py-2",
|
|
1407
|
+
/** An explicit spelling of `default` (the fork's middle rung). */
|
|
1408
|
+
md: "h-9 px-4 py-2",
|
|
1409
|
+
lg: "h-10 rounded-md px-6",
|
|
1410
|
+
xl: "h-12 px-8 py-3 text-base",
|
|
1411
|
+
"2xl": "h-14 px-10 py-3 text-base",
|
|
1412
|
+
"3xl": "h-16 px-12 py-3 text-base",
|
|
1413
|
+
icon: "h-9 w-9",
|
|
1414
|
+
"icon-sm": "h-7 w-7",
|
|
1415
|
+
/** A circular icon box — an avatar-shaped control, not a square one. */
|
|
1416
|
+
roundIcon: "h-8 w-8 rounded-full p-0"
|
|
1417
|
+
}
|
|
1418
|
+
},
|
|
1419
|
+
defaultVariants: {
|
|
1420
|
+
variant: "default",
|
|
1421
|
+
size: "default"
|
|
1422
|
+
}
|
|
1423
|
+
}
|
|
1424
|
+
);
|
|
1425
|
+
var TOUCH_FLOOR_SIZES = /* @__PURE__ */ new Set([
|
|
1426
|
+
"xs",
|
|
1427
|
+
"sm",
|
|
1428
|
+
"default",
|
|
1429
|
+
"md",
|
|
1430
|
+
"lg",
|
|
1431
|
+
"icon",
|
|
1432
|
+
"icon-sm",
|
|
1433
|
+
"roundIcon"
|
|
1434
|
+
]);
|
|
1435
|
+
var SQUARE_SIZES = /* @__PURE__ */ new Set(["icon", "icon-sm", "roundIcon"]);
|
|
1436
|
+
function swallowClick(event) {
|
|
1437
|
+
event.preventDefault();
|
|
1438
|
+
event.stopPropagation();
|
|
1439
|
+
}
|
|
1440
|
+
var Button3 = React12.forwardRef(
|
|
1441
|
+
({ className, variant, size, asChild = false, onClick, ...props }, ref) => {
|
|
1442
|
+
const Component = asChild ? import_react_slot2.Slot : "button";
|
|
1443
|
+
const resolvedSize = size ?? "default";
|
|
1444
|
+
const disabledChild = asChild && Boolean(props.disabled);
|
|
1445
|
+
const ariaDisabled = props["aria-disabled"];
|
|
1446
|
+
const inert = disabledChild || ariaDisabled === true || ariaDisabled === "true";
|
|
1447
|
+
return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
|
|
1448
|
+
Component,
|
|
1449
|
+
{
|
|
1450
|
+
className: cn2(buttonVariants2({ variant, size, className })),
|
|
1451
|
+
"data-matrx-button": "",
|
|
1452
|
+
"data-tap-floor": TOUCH_FLOOR_SIZES.has(resolvedSize) ? "" : void 0,
|
|
1453
|
+
"data-tap-floor-square": SQUARE_SIZES.has(resolvedSize) ? "" : void 0,
|
|
1454
|
+
ref,
|
|
1455
|
+
...props,
|
|
1456
|
+
"aria-disabled": disabledChild ? true : ariaDisabled,
|
|
1457
|
+
tabIndex: disabledChild ? -1 : props.tabIndex,
|
|
1458
|
+
onClick: inert ? swallowClick : onClick
|
|
1459
|
+
}
|
|
1460
|
+
);
|
|
1461
|
+
}
|
|
1462
|
+
);
|
|
1463
|
+
Button3.displayName = "Button";
|
|
1464
|
+
|
|
1465
|
+
// src/floating-layer.tsx
|
|
1466
|
+
var import_react_focus_scope = require("@radix-ui/react-focus-scope");
|
|
1467
|
+
var import_react_slot3 = require("@radix-ui/react-slot");
|
|
1468
|
+
var React13 = __toESM(require("react"), 1);
|
|
1469
|
+
var import_jsx_runtime17 = require("react/jsx-runtime");
|
|
1470
|
+
var FLOATING_LAYER_ATTR = "data-matrx-floating-layer";
|
|
1471
|
+
var REGISTRY_SLOT = /* @__PURE__ */ Symbol.for("ai-matrx.design-system.modal-layer-registry");
|
|
1472
|
+
var registryHost = globalThis;
|
|
1473
|
+
var registry = registryHost[REGISTRY_SLOT] ??= {
|
|
1474
|
+
count: 0,
|
|
1475
|
+
listeners: /* @__PURE__ */ new Set()
|
|
1476
|
+
};
|
|
1477
|
+
function emit() {
|
|
1478
|
+
for (const listener of registry.listeners) listener();
|
|
1479
|
+
}
|
|
1480
|
+
var SCROLL_EVENTS = ["wheel", "touchmove"];
|
|
1481
|
+
function stopAtBody(event) {
|
|
1482
|
+
if (isInsideFloatingLayer(event.target)) event.stopPropagation();
|
|
1483
|
+
}
|
|
1484
|
+
function setScrollPassthrough(on) {
|
|
1485
|
+
if (typeof document === "undefined" || !document.body) return;
|
|
1486
|
+
for (const type of SCROLL_EVENTS) {
|
|
1487
|
+
if (on) document.body.addEventListener(type, stopAtBody, { passive: true });
|
|
1488
|
+
else document.body.removeEventListener(type, stopAtBody);
|
|
1489
|
+
}
|
|
1490
|
+
}
|
|
1491
|
+
function registerModalLayer() {
|
|
1492
|
+
registry.count += 1;
|
|
1493
|
+
if (registry.count === 1) setScrollPassthrough(true);
|
|
1494
|
+
emit();
|
|
1495
|
+
let released = false;
|
|
1496
|
+
return () => {
|
|
1497
|
+
if (released) return;
|
|
1498
|
+
released = true;
|
|
1499
|
+
registry.count = Math.max(0, registry.count - 1);
|
|
1500
|
+
if (registry.count === 0) setScrollPassthrough(false);
|
|
1501
|
+
emit();
|
|
1502
|
+
};
|
|
1503
|
+
}
|
|
1504
|
+
function isModalLayerOpen() {
|
|
1505
|
+
return registry.count > 0;
|
|
1506
|
+
}
|
|
1507
|
+
function useModalLayer(active) {
|
|
1508
|
+
React13.useLayoutEffect(() => active ? registerModalLayer() : void 0, [active]);
|
|
1509
|
+
}
|
|
1510
|
+
var POPPER_WRAPPER = "[data-radix-popper-content-wrapper]";
|
|
1511
|
+
function isInsideFloatingLayer(target) {
|
|
1512
|
+
if (!(target instanceof Element)) return false;
|
|
1513
|
+
if (target.closest(`[${FLOATING_LAYER_ATTR}]`)) return true;
|
|
1514
|
+
return target.closest(POPPER_WRAPPER) !== null && target.ownerDocument.querySelector(`[${FLOATING_LAYER_ATTR}]`) !== null;
|
|
1515
|
+
}
|
|
1516
|
+
function keepFloatingLayersInside(handler) {
|
|
1517
|
+
return (event) => {
|
|
1518
|
+
handler?.(event);
|
|
1519
|
+
if (isInsideFloatingLayer(event.target)) event.preventDefault();
|
|
1520
|
+
};
|
|
1521
|
+
}
|
|
1522
|
+
var preventAutoFocus = (event) => event.preventDefault();
|
|
1523
|
+
var FloatingLayer = React13.forwardRef(({ children }, ref) => {
|
|
1524
|
+
const [opener] = React13.useState(
|
|
1525
|
+
() => typeof document === "undefined" ? null : document.activeElement
|
|
1526
|
+
);
|
|
1527
|
+
const returnFocus = (event) => {
|
|
1528
|
+
event.preventDefault();
|
|
1529
|
+
const active = document.activeElement;
|
|
1530
|
+
const lost = !active || active === document.body || !active.isConnected;
|
|
1531
|
+
if (lost && isModalLayerOpen() && opener instanceof HTMLElement && opener.isConnected) {
|
|
1532
|
+
opener.focus({ preventScroll: true });
|
|
1533
|
+
}
|
|
1534
|
+
};
|
|
1535
|
+
return /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
|
|
1536
|
+
import_react_focus_scope.FocusScope,
|
|
1537
|
+
{
|
|
1538
|
+
asChild: true,
|
|
1539
|
+
trapped: false,
|
|
1540
|
+
loop: false,
|
|
1541
|
+
onMountAutoFocus: preventAutoFocus,
|
|
1542
|
+
onUnmountAutoFocus: returnFocus,
|
|
1543
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
|
|
1544
|
+
import_react_slot3.Slot,
|
|
1545
|
+
{
|
|
1546
|
+
ref,
|
|
1547
|
+
...{ [FLOATING_LAYER_ATTR]: "" },
|
|
1548
|
+
style: { pointerEvents: "auto" },
|
|
1549
|
+
children
|
|
1550
|
+
}
|
|
1551
|
+
)
|
|
1552
|
+
}
|
|
1553
|
+
);
|
|
1554
|
+
});
|
|
1555
|
+
FloatingLayer.displayName = "FloatingLayer";
|
|
1556
|
+
|
|
1557
|
+
// src/alert-dialog.tsx
|
|
1558
|
+
var import_jsx_runtime18 = require("react/jsx-runtime");
|
|
1559
|
+
var AlertDialog = AlertDialogPrimitive.Root;
|
|
1560
|
+
var AlertDialogPortal = ({
|
|
1561
|
+
container,
|
|
1562
|
+
...props
|
|
1563
|
+
}) => {
|
|
1564
|
+
const resolved = usePortalContainer(container);
|
|
1565
|
+
return /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(AlertDialogPrimitive.Portal, { container: resolved ?? null, ...props });
|
|
1566
|
+
};
|
|
1567
|
+
AlertDialogPortal.displayName = "AlertDialogPortal";
|
|
1568
|
+
var AlertDialogOverlay = React14.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
|
|
1569
|
+
AlertDialogPrimitive.Overlay,
|
|
1570
|
+
{
|
|
1571
|
+
ref,
|
|
1572
|
+
"data-slot": "alert-dialog-overlay",
|
|
1573
|
+
className: cn2(
|
|
1574
|
+
"fixed inset-0 z-[10000] bg-[var(--matrx-overlay-scrim)]",
|
|
1575
|
+
MOTION_OVERLAY,
|
|
1576
|
+
className
|
|
1577
|
+
),
|
|
1578
|
+
...props
|
|
1579
|
+
}
|
|
1580
|
+
));
|
|
1581
|
+
AlertDialogOverlay.displayName = AlertDialogPrimitive.Overlay.displayName;
|
|
1582
|
+
var AlertDialogContentPrimitive = React14.forwardRef((props, ref) => {
|
|
1583
|
+
useModalLayer(true);
|
|
1584
|
+
return /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(AlertDialogPrimitive.Content, { ...props, ref, "aria-modal": "true" });
|
|
1585
|
+
});
|
|
1586
|
+
AlertDialogContentPrimitive.displayName = "AlertDialogContentPrimitive";
|
|
1587
|
+
var AlertDialogDescription = React14.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
|
|
1588
|
+
AlertDialogPrimitive.Description,
|
|
1589
|
+
{
|
|
1590
|
+
ref,
|
|
1591
|
+
"data-slot": "alert-dialog-description",
|
|
1592
|
+
className: cn2("text-sm text-muted-foreground", className),
|
|
1593
|
+
...props
|
|
1594
|
+
}
|
|
1595
|
+
));
|
|
1596
|
+
AlertDialogDescription.displayName = AlertDialogPrimitive.Description.displayName;
|
|
1597
|
+
var AlertDialogContent = React14.forwardRef(({ className, children, container, animated = true, ...props }, ref) => {
|
|
1598
|
+
const hasDescription = (0, import_react_tree.treeContainsComponent)(children, AlertDialogDescription) || (0, import_react_tree.treeContainsComponent)(children, AlertDialogPrimitive.Description);
|
|
1599
|
+
return /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(AlertDialogPortal, { container, children: [
|
|
1600
|
+
/* @__PURE__ */ (0, import_jsx_runtime18.jsx)(AlertDialogOverlay, {}),
|
|
1601
|
+
/* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(
|
|
1602
|
+
AlertDialogContentPrimitive,
|
|
1603
|
+
{
|
|
1604
|
+
ref,
|
|
1605
|
+
"data-slot": "alert-dialog-content",
|
|
1606
|
+
className: cn2(
|
|
1607
|
+
`matrx-dialog-card fixed left-[50%] top-[50%] z-[10000] grid min-w-0 w-full max-w-lg ${CENTERED_DIALOG} gap-4 border bg-background p-6 shadow-lg`,
|
|
1608
|
+
// Ruling 3 — never taller than the viewport, always scrollable, and
|
|
1609
|
+
// the footer stays reachable via --dialog-pad like Dialog's.
|
|
1610
|
+
// Ruling 5 (0.11.3) — `matrx-dialog-card` hands the bottom padding to
|
|
1611
|
+
// a sticky footer when there is one, so the footer's box matches the
|
|
1612
|
+
// space the grid gave it and sticky never clamps it up over the
|
|
1613
|
+
// consequence copy.
|
|
1614
|
+
"max-h-[85dvh] overflow-y-auto overscroll-contain [--dialog-pad:1.5rem] overflow-x-clip [overflow-wrap:anywhere] [&>*]:min-w-0 [&>*]:max-w-full",
|
|
1615
|
+
"sm:rounded-lg",
|
|
1616
|
+
animated && MOTION_DIALOG,
|
|
1617
|
+
className
|
|
1618
|
+
),
|
|
1619
|
+
...props,
|
|
1620
|
+
children: [
|
|
1621
|
+
hasDescription ? null : /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(AlertDialogPrimitive.Description, { className: "sr-only", children: "Please confirm the action described in this dialog." }),
|
|
1622
|
+
children
|
|
1623
|
+
]
|
|
1624
|
+
}
|
|
1625
|
+
)
|
|
1626
|
+
] });
|
|
1627
|
+
});
|
|
1628
|
+
AlertDialogContent.displayName = AlertDialogPrimitive.Content.displayName;
|
|
1629
|
+
var AlertDialogHeader = ({
|
|
1630
|
+
className,
|
|
1631
|
+
...props
|
|
1632
|
+
}) => /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
|
|
1633
|
+
"div",
|
|
1634
|
+
{
|
|
1635
|
+
"data-slot": "alert-dialog-header",
|
|
1636
|
+
className: cn2("flex flex-col space-y-2 text-center sm:text-left", className),
|
|
1637
|
+
...props
|
|
1638
|
+
}
|
|
1639
|
+
);
|
|
1640
|
+
AlertDialogHeader.displayName = "AlertDialogHeader";
|
|
1641
|
+
var AlertDialogFooter = ({
|
|
1642
|
+
className,
|
|
1643
|
+
...props
|
|
1644
|
+
}) => /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
|
|
1645
|
+
"div",
|
|
1646
|
+
{
|
|
1647
|
+
"data-slot": "alert-dialog-footer",
|
|
1648
|
+
className: cn2(
|
|
1649
|
+
"flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2",
|
|
1650
|
+
// Ruling 3/4 — sticky to the card edges so a scrolled body never shows
|
|
1651
|
+
// through under the actions, and safe-area padded on a phone.
|
|
1652
|
+
//
|
|
1653
|
+
// 🚨 THERE IS NO `mb-[…]` HERE, AND THERE MUST NEVER BE ONE AGAIN
|
|
1654
|
+
// (0.11.3, feedback bf8fb00b). The bleed over the card's bottom padding
|
|
1655
|
+
// used to be a NEGATIVE BLOCK MARGIN, which makes this box taller than
|
|
1656
|
+
// the grid area the card allocates it — and `position: sticky` CLAMPS a
|
|
1657
|
+
// box to its containing block, so the browser yanked the footer UP by
|
|
1658
|
+
// `--dialog-pad` against the card's `gap-4` and painted 8px of opaque
|
|
1659
|
+
// footer over the last line of the consequence, at every length. The
|
|
1660
|
+
// card now RESERVES this footer's block size instead (`matrx-dialog-card`
|
|
1661
|
+
// in styles.css hands over its `padding-bottom`), so the bleed costs no
|
|
1662
|
+
// margin at all. Guard: `sticky-footer-geometry.test.tsx`.
|
|
1663
|
+
//
|
|
1664
|
+
// These stay UTILITIES rather than a shipped rule so a caller's
|
|
1665
|
+
// `className` still wins through tailwind-merge — a shipped rule would
|
|
1666
|
+
// have to be unlayered to survive Tailwind preflight (§ THE PREFLIGHT
|
|
1667
|
+
// RULE in styles.css) and would then override every caller's padding.
|
|
1668
|
+
"sticky bottom-0 z-10 bg-background pt-3",
|
|
1669
|
+
"mx-[calc(var(--dialog-pad,0px)*-1)] px-[var(--dialog-pad,0px)] pb-[var(--dialog-pad,0px)]",
|
|
1670
|
+
className
|
|
1671
|
+
),
|
|
1672
|
+
...props
|
|
1673
|
+
}
|
|
1674
|
+
);
|
|
1675
|
+
AlertDialogFooter.displayName = "AlertDialogFooter";
|
|
1676
|
+
var AlertDialogTitle = React14.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
|
|
1677
|
+
AlertDialogPrimitive.Title,
|
|
1678
|
+
{
|
|
1679
|
+
ref,
|
|
1680
|
+
"data-slot": "alert-dialog-title",
|
|
1681
|
+
className: cn2("text-lg font-semibold", className),
|
|
1682
|
+
...props
|
|
1683
|
+
}
|
|
1684
|
+
));
|
|
1685
|
+
AlertDialogTitle.displayName = AlertDialogPrimitive.Title.displayName;
|
|
1686
|
+
var AlertDialogAction = React14.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
|
|
1687
|
+
AlertDialogPrimitive.Action,
|
|
1688
|
+
{
|
|
1689
|
+
ref,
|
|
1690
|
+
"data-slot": "alert-dialog-action",
|
|
1691
|
+
className: cn2(buttonVariants2(), className),
|
|
1692
|
+
...props
|
|
1693
|
+
}
|
|
1694
|
+
));
|
|
1695
|
+
AlertDialogAction.displayName = AlertDialogPrimitive.Action.displayName;
|
|
1696
|
+
var AlertDialogCancel = React14.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
|
|
1697
|
+
AlertDialogPrimitive.Cancel,
|
|
1698
|
+
{
|
|
1699
|
+
ref,
|
|
1700
|
+
"data-slot": "alert-dialog-cancel",
|
|
1701
|
+
className: cn2(
|
|
1702
|
+
buttonVariants2({ variant: "outline" }),
|
|
1703
|
+
"mt-2 sm:mt-0",
|
|
1704
|
+
className
|
|
1705
|
+
),
|
|
1706
|
+
...props
|
|
1707
|
+
}
|
|
1708
|
+
));
|
|
1709
|
+
AlertDialogCancel.displayName = AlertDialogPrimitive.Cancel.displayName;
|
|
1710
|
+
|
|
1711
|
+
// src/confirm-dialog.tsx
|
|
1712
|
+
var import_jsx_runtime19 = require("react/jsx-runtime");
|
|
1713
|
+
function ConfirmDialog({
|
|
1714
|
+
open,
|
|
1715
|
+
onOpenChange,
|
|
1716
|
+
title,
|
|
1717
|
+
description,
|
|
1718
|
+
content,
|
|
1719
|
+
contentClassName,
|
|
1720
|
+
confirmLabel = "Confirm",
|
|
1721
|
+
cancelLabel = "Cancel",
|
|
1722
|
+
variant = "default",
|
|
1723
|
+
busy = false,
|
|
1724
|
+
confirmDisabled = false,
|
|
1725
|
+
confirmHeld = false,
|
|
1726
|
+
portalContainer,
|
|
1727
|
+
onConfirm
|
|
1728
|
+
}) {
|
|
1729
|
+
return /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(AlertDialog, { open, onOpenChange, children: /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(
|
|
1730
|
+
AlertDialogContent,
|
|
1731
|
+
{
|
|
1732
|
+
className: contentClassName,
|
|
1733
|
+
...portalContainer === void 0 ? {} : { container: portalContainer },
|
|
1734
|
+
children: [
|
|
1735
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(AlertDialogHeader, { children: [
|
|
1736
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsx)(AlertDialogTitle, { children: title }),
|
|
1737
|
+
description ? /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(AlertDialogDescription, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("div", { children: description }) }) : null
|
|
1738
|
+
] }),
|
|
1739
|
+
content ?? null,
|
|
1740
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(AlertDialogFooter, { children: [
|
|
1741
|
+
cancelLabel === null ? null : /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(AlertDialogCancel, { className: "max-lg:min-h-11", disabled: busy, children: cancelLabel }),
|
|
1742
|
+
confirmHeld ? null : /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(
|
|
1743
|
+
AlertDialogAction,
|
|
1744
|
+
{
|
|
1745
|
+
disabled: busy || confirmDisabled,
|
|
1746
|
+
onClick: (event) => {
|
|
1747
|
+
event.preventDefault();
|
|
1748
|
+
void onConfirm();
|
|
1749
|
+
},
|
|
1750
|
+
className: cn2(
|
|
1751
|
+
"max-lg:min-h-11",
|
|
1752
|
+
// One source of button appearance: the destructive look comes
|
|
1753
|
+
// from `buttonVariants`, not a second hand-written copy of it.
|
|
1754
|
+
// tailwind-merge makes it WIN over the default variant classes.
|
|
1755
|
+
variant === "destructive" && buttonVariants2({ variant: "destructive" })
|
|
1756
|
+
),
|
|
1757
|
+
children: [
|
|
1758
|
+
busy ? /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(Loader2Icon, { className: cn2("mr-2 h-4 w-4", MOTION_SPIN) }) : null,
|
|
1759
|
+
confirmLabel
|
|
1760
|
+
]
|
|
1761
|
+
}
|
|
1762
|
+
)
|
|
1763
|
+
] })
|
|
1764
|
+
]
|
|
1765
|
+
}
|
|
1766
|
+
) });
|
|
1767
|
+
}
|
|
1768
|
+
|
|
1769
|
+
// src/dropdown-menu.tsx
|
|
1770
|
+
var DropdownMenuPrimitive = __toESM(require("@radix-ui/react-dropdown-menu"), 1);
|
|
1771
|
+
var React17 = __toESM(require("react"), 1);
|
|
1772
|
+
|
|
1773
|
+
// src/menu-scroll-edges.tsx
|
|
1774
|
+
var React15 = __toESM(require("react"), 1);
|
|
1775
|
+
|
|
1776
|
+
// src/use-scroll-fade.ts
|
|
1777
|
+
var import_react = require("react");
|
|
1778
|
+
var EPSILON = 2;
|
|
1779
|
+
function useScrollFade() {
|
|
1780
|
+
const [state, setState] = (0, import_react.useState)({
|
|
1781
|
+
top: false,
|
|
1782
|
+
bottom: false
|
|
1783
|
+
});
|
|
1784
|
+
const nodeRef = (0, import_react.useRef)(null);
|
|
1785
|
+
const cleanupRef = (0, import_react.useRef)(null);
|
|
1786
|
+
const measure = (0, import_react.useCallback)(() => {
|
|
1787
|
+
const el = nodeRef.current;
|
|
1788
|
+
if (!el) return;
|
|
1789
|
+
const overflowing = el.scrollHeight - el.clientHeight > EPSILON;
|
|
1790
|
+
const next = {
|
|
1791
|
+
top: overflowing && el.scrollTop > EPSILON,
|
|
1792
|
+
bottom: overflowing && el.scrollTop + el.clientHeight < el.scrollHeight - EPSILON
|
|
1793
|
+
};
|
|
1794
|
+
setState(
|
|
1795
|
+
(prev) => prev.top === next.top && prev.bottom === next.bottom ? prev : next
|
|
1796
|
+
);
|
|
1797
|
+
}, []);
|
|
1798
|
+
const ref = (0, import_react.useCallback)(
|
|
1799
|
+
(node) => {
|
|
1800
|
+
cleanupRef.current?.();
|
|
1801
|
+
cleanupRef.current = null;
|
|
1802
|
+
nodeRef.current = node;
|
|
1803
|
+
if (!node) return;
|
|
1804
|
+
node.addEventListener("scroll", measure, { passive: true });
|
|
1805
|
+
const ro = typeof ResizeObserver === "undefined" ? null : new ResizeObserver(measure);
|
|
1806
|
+
ro?.observe(node);
|
|
1807
|
+
const mo = new MutationObserver(measure);
|
|
1808
|
+
mo.observe(node, { childList: true, subtree: true });
|
|
1809
|
+
cleanupRef.current = () => {
|
|
1810
|
+
node.removeEventListener("scroll", measure);
|
|
1811
|
+
ro?.disconnect();
|
|
1812
|
+
mo.disconnect();
|
|
1813
|
+
};
|
|
1814
|
+
requestAnimationFrame(measure);
|
|
1815
|
+
},
|
|
1816
|
+
[measure]
|
|
1817
|
+
);
|
|
1818
|
+
(0, import_react.useEffect)(() => () => cleanupRef.current?.(), []);
|
|
1819
|
+
return {
|
|
1820
|
+
ref,
|
|
1821
|
+
state,
|
|
1822
|
+
fadeProps: {
|
|
1823
|
+
"data-fade-top": state.top ? "" : void 0,
|
|
1824
|
+
"data-fade-bottom": state.bottom ? "" : void 0,
|
|
1825
|
+
className: "matrx-scroll-fade"
|
|
1826
|
+
}
|
|
1827
|
+
};
|
|
1828
|
+
}
|
|
1829
|
+
|
|
1830
|
+
// src/menu-scroll-edges.tsx
|
|
1831
|
+
var import_jsx_runtime20 = require("react/jsx-runtime");
|
|
1832
|
+
var HOVER_SPEED = 0.6;
|
|
1833
|
+
var PAGE_OVERLAP = 48;
|
|
1834
|
+
function composeRefs(...refs) {
|
|
1835
|
+
return (node) => {
|
|
1836
|
+
for (const ref of refs) {
|
|
1837
|
+
if (typeof ref === "function") ref(node);
|
|
1838
|
+
else if (ref) ref.current = node;
|
|
1839
|
+
}
|
|
1840
|
+
};
|
|
1841
|
+
}
|
|
1842
|
+
function MenuScrollEdge({ side, scroller }) {
|
|
1843
|
+
const frame = React15.useRef(null);
|
|
1844
|
+
const dir = side === "top" ? -1 : 1;
|
|
1845
|
+
const stop = React15.useCallback(() => {
|
|
1846
|
+
if (frame.current !== null) cancelAnimationFrame(frame.current);
|
|
1847
|
+
frame.current = null;
|
|
1848
|
+
}, []);
|
|
1849
|
+
const glide = React15.useCallback(() => {
|
|
1850
|
+
stop();
|
|
1851
|
+
let last = performance.now();
|
|
1852
|
+
const step = (now) => {
|
|
1853
|
+
const el = scroller.current;
|
|
1854
|
+
if (!el) return stop();
|
|
1855
|
+
el.scrollTop += dir * HOVER_SPEED * (now - last);
|
|
1856
|
+
last = now;
|
|
1857
|
+
frame.current = requestAnimationFrame(step);
|
|
1858
|
+
};
|
|
1859
|
+
frame.current = requestAnimationFrame(step);
|
|
1860
|
+
}, [dir, scroller, stop]);
|
|
1861
|
+
React15.useEffect(() => stop, [stop]);
|
|
1862
|
+
return /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("div", { "aria-hidden": true, "data-menu-scroll-edge": side, className: "matrx-menu-scroll-edge", children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("div", { className: "matrx-menu-scroll-fade", children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
|
|
1863
|
+
"div",
|
|
1864
|
+
{
|
|
1865
|
+
"data-menu-scroll-button": "",
|
|
1866
|
+
className: "matrx-menu-scroll-button",
|
|
1867
|
+
onPointerEnter: (e) => {
|
|
1868
|
+
if (e.pointerType !== "touch") glide();
|
|
1869
|
+
},
|
|
1870
|
+
onPointerLeave: stop,
|
|
1871
|
+
onPointerDown: (e) => {
|
|
1872
|
+
e.preventDefault();
|
|
1873
|
+
const el = scroller.current;
|
|
1874
|
+
if (!el) return;
|
|
1875
|
+
el.scrollTop += dir * Math.max(el.clientHeight - PAGE_OVERLAP, PAGE_OVERLAP);
|
|
1876
|
+
},
|
|
1877
|
+
children: side === "top" ? /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(RadixChevronUpIcon, {}) : /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(RadixChevronDownIcon, {})
|
|
1878
|
+
}
|
|
1879
|
+
) }) });
|
|
1880
|
+
}
|
|
1881
|
+
function useMenuScrollEdges(...forwarded) {
|
|
1882
|
+
const fade = useScrollFade();
|
|
1883
|
+
const scroller = React15.useRef(null);
|
|
1884
|
+
const ref = React15.useCallback(composeRefs(scroller, fade.ref, ...forwarded), [
|
|
1885
|
+
fade.ref,
|
|
1886
|
+
...forwarded
|
|
1887
|
+
]);
|
|
1888
|
+
const wrap = (children) => /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(import_jsx_runtime20.Fragment, { children: [
|
|
1889
|
+
fade.state.top ? /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(MenuScrollEdge, { side: "top", scroller }) : null,
|
|
1890
|
+
children,
|
|
1891
|
+
fade.state.bottom ? /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(MenuScrollEdge, { side: "bottom", scroller }) : null
|
|
1892
|
+
] });
|
|
1893
|
+
return { ref, wrap, surfaceClassName: "matrx-menu-surface" };
|
|
1894
|
+
}
|
|
1895
|
+
|
|
1896
|
+
// src/submenu-focus.ts
|
|
1897
|
+
var React16 = __toESM(require("react"), 1);
|
|
1898
|
+
function useSubmenuSurvivesParentPanelFocus(forwardedRef, onFocusOutside) {
|
|
1899
|
+
const content = React16.useRef(null);
|
|
1900
|
+
const ref = React16.useCallback(
|
|
1901
|
+
(node) => {
|
|
1902
|
+
content.current = node;
|
|
1903
|
+
if (typeof forwardedRef === "function") forwardedRef(node);
|
|
1904
|
+
else if (forwardedRef) forwardedRef.current = node;
|
|
1905
|
+
},
|
|
1906
|
+
[forwardedRef]
|
|
1907
|
+
);
|
|
1908
|
+
const handle = (event) => {
|
|
1909
|
+
onFocusOutside?.(event);
|
|
1910
|
+
if (event.defaultPrevented) return;
|
|
1911
|
+
const to = event.target;
|
|
1912
|
+
const id = content.current?.id;
|
|
1913
|
+
if (!(to instanceof Element) || !id || to.getAttribute("role") !== "menu") return;
|
|
1914
|
+
const holdsTrigger = Array.from(to.querySelectorAll("[aria-controls]")).some((el) => el.getAttribute("aria-controls") === id);
|
|
1915
|
+
if (holdsTrigger) event.preventDefault();
|
|
1916
|
+
};
|
|
1917
|
+
return { ref, onFocusOutside: handle };
|
|
1918
|
+
}
|
|
1919
|
+
|
|
1920
|
+
// src/dropdown-menu.tsx
|
|
1921
|
+
var import_jsx_runtime21 = require("react/jsx-runtime");
|
|
1922
|
+
var DropdownMenu = DropdownMenuPrimitive.Root;
|
|
1923
|
+
var DropdownMenuTrigger = DropdownMenuPrimitive.Trigger;
|
|
1924
|
+
var DropdownMenuGroup = DropdownMenuPrimitive.Group;
|
|
1925
|
+
var DropdownMenuSub = DropdownMenuPrimitive.Sub;
|
|
1926
|
+
var DropdownMenuRadioGroup = DropdownMenuPrimitive.RadioGroup;
|
|
1927
|
+
var MENU_SURFACE_CLASS = "z-[10001] min-w-[8rem] max-h-[var(--radix-dropdown-menu-content-available-height)] overflow-y-auto overflow-x-hidden overscroll-contain rounded-md border bg-popover p-1 text-popover-foreground";
|
|
1928
|
+
var MENU_ITEM_CLASS = "relative flex select-none items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:opacity-50";
|
|
1929
|
+
var MENU_INDICATOR_ITEM_CLASS = "relative flex select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:opacity-50";
|
|
1930
|
+
var DropdownMenuSubTrigger = React17.forwardRef(({ className, inset, children, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(
|
|
1931
|
+
DropdownMenuPrimitive.SubTrigger,
|
|
1932
|
+
{
|
|
1933
|
+
ref,
|
|
1934
|
+
"data-slot": "dropdown-menu-sub-trigger",
|
|
1935
|
+
className: cn2(
|
|
1936
|
+
"flex select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none focus:bg-accent data-[state=open]:bg-accent",
|
|
1937
|
+
inset && "pl-8",
|
|
1938
|
+
className
|
|
1939
|
+
),
|
|
1940
|
+
...props,
|
|
1941
|
+
children: [
|
|
1942
|
+
children,
|
|
1943
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)(RadixChevronRightIcon, { className: "ml-auto h-4 w-4" })
|
|
1944
|
+
]
|
|
1945
|
+
}
|
|
1946
|
+
));
|
|
1947
|
+
DropdownMenuSubTrigger.displayName = DropdownMenuPrimitive.SubTrigger.displayName;
|
|
1948
|
+
var DropdownMenuSubContent = React17.forwardRef(({ className, container, sideOffset = 2, alignOffset = -4, collisionPadding = 8, onFocusOutside, children, ...props }, ref) => {
|
|
1949
|
+
const portalContainer = usePortalContainer(container);
|
|
1950
|
+
const keep = useSubmenuSurvivesParentPanelFocus(ref, onFocusOutside);
|
|
1951
|
+
const edges = useMenuScrollEdges(keep.ref);
|
|
1952
|
+
return /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(DropdownMenuPrimitive.Portal, { container: portalContainer, children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
|
|
1953
|
+
DropdownMenuPrimitive.SubContent,
|
|
1954
|
+
{
|
|
1955
|
+
ref: edges.ref,
|
|
1956
|
+
onFocusOutside: keep.onFocusOutside,
|
|
1957
|
+
"data-slot": "dropdown-menu-sub-content",
|
|
1958
|
+
sideOffset,
|
|
1959
|
+
alignOffset,
|
|
1960
|
+
collisionPadding,
|
|
1961
|
+
className: cn2(MENU_SURFACE_CLASS, edges.surfaceClassName, "shadow-lg", MOTION_POPPER, className),
|
|
1962
|
+
...props,
|
|
1963
|
+
children: edges.wrap(children)
|
|
1964
|
+
}
|
|
1965
|
+
) });
|
|
1966
|
+
});
|
|
1967
|
+
DropdownMenuSubContent.displayName = DropdownMenuPrimitive.SubContent.displayName;
|
|
1968
|
+
var DropdownMenuContent = React17.forwardRef(({ className, sideOffset = 4, container, children, ...props }, ref) => {
|
|
1969
|
+
const portalContainer = usePortalContainer(container);
|
|
1970
|
+
const edges = useMenuScrollEdges(ref);
|
|
1971
|
+
return /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(DropdownMenuPrimitive.Portal, { container: portalContainer, children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
|
|
1972
|
+
DropdownMenuPrimitive.Content,
|
|
1973
|
+
{
|
|
1974
|
+
ref: edges.ref,
|
|
1975
|
+
"data-slot": "dropdown-menu-content",
|
|
1976
|
+
sideOffset,
|
|
1977
|
+
className: cn2(
|
|
1978
|
+
MENU_SURFACE_CLASS,
|
|
1979
|
+
edges.surfaceClassName,
|
|
1980
|
+
"shadow-md",
|
|
1981
|
+
MOTION_POPPER,
|
|
1982
|
+
className
|
|
1983
|
+
),
|
|
1984
|
+
...props,
|
|
1985
|
+
children: edges.wrap(children)
|
|
1986
|
+
}
|
|
1987
|
+
) });
|
|
1988
|
+
});
|
|
1989
|
+
DropdownMenuContent.displayName = DropdownMenuPrimitive.Content.displayName;
|
|
1990
|
+
var DropdownMenuItem = React17.forwardRef(({ className, inset, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
|
|
1991
|
+
DropdownMenuPrimitive.Item,
|
|
1992
|
+
{
|
|
1993
|
+
ref,
|
|
1994
|
+
"data-slot": "dropdown-menu-item",
|
|
1995
|
+
className: cn2(MENU_ITEM_CLASS, inset && "pl-8", className),
|
|
1996
|
+
...props
|
|
1997
|
+
}
|
|
1998
|
+
));
|
|
1999
|
+
DropdownMenuItem.displayName = DropdownMenuPrimitive.Item.displayName;
|
|
2000
|
+
var DropdownMenuCheckboxItem = React17.forwardRef(({ className, children, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(
|
|
2001
|
+
DropdownMenuPrimitive.CheckboxItem,
|
|
2002
|
+
{
|
|
2003
|
+
ref,
|
|
2004
|
+
"data-slot": "dropdown-menu-checkbox-item",
|
|
2005
|
+
className: cn2(MENU_INDICATOR_ITEM_CLASS, className),
|
|
2006
|
+
...props,
|
|
2007
|
+
children: [
|
|
2008
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { className: "absolute left-2 flex h-3.5 w-3.5 items-center justify-center", children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(DropdownMenuPrimitive.ItemIndicator, { children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(CheckIcon, { className: "h-4 w-4" }) }) }),
|
|
2009
|
+
children
|
|
2010
|
+
]
|
|
2011
|
+
}
|
|
2012
|
+
));
|
|
2013
|
+
DropdownMenuCheckboxItem.displayName = DropdownMenuPrimitive.CheckboxItem.displayName;
|
|
2014
|
+
var DropdownMenuRadioItem = React17.forwardRef(({ className, children, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(
|
|
2015
|
+
DropdownMenuPrimitive.RadioItem,
|
|
2016
|
+
{
|
|
2017
|
+
ref,
|
|
2018
|
+
"data-slot": "dropdown-menu-radio-item",
|
|
2019
|
+
className: cn2(MENU_INDICATOR_ITEM_CLASS, className),
|
|
2020
|
+
...props,
|
|
2021
|
+
children: [
|
|
2022
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { className: "absolute left-2 flex h-3.5 w-3.5 items-center justify-center", children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(DropdownMenuPrimitive.ItemIndicator, { children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(DotFilledIcon, { className: "h-4 w-4 fill-current" }) }) }),
|
|
2023
|
+
children
|
|
2024
|
+
]
|
|
2025
|
+
}
|
|
2026
|
+
));
|
|
2027
|
+
DropdownMenuRadioItem.displayName = DropdownMenuPrimitive.RadioItem.displayName;
|
|
2028
|
+
var DropdownMenuLabel = React17.forwardRef(({ className, inset, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
|
|
2029
|
+
DropdownMenuPrimitive.Label,
|
|
2030
|
+
{
|
|
2031
|
+
ref,
|
|
2032
|
+
"data-slot": "dropdown-menu-label",
|
|
2033
|
+
className: cn2("px-2 py-1.5 text-sm font-semibold", inset && "pl-8", className),
|
|
2034
|
+
...props
|
|
2035
|
+
}
|
|
2036
|
+
));
|
|
2037
|
+
DropdownMenuLabel.displayName = DropdownMenuPrimitive.Label.displayName;
|
|
2038
|
+
var DropdownMenuSeparator = React17.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
|
|
2039
|
+
DropdownMenuPrimitive.Separator,
|
|
2040
|
+
{
|
|
2041
|
+
ref,
|
|
2042
|
+
"data-slot": "dropdown-menu-separator",
|
|
2043
|
+
className: cn2("-mx-1 my-1 h-px bg-muted", className),
|
|
2044
|
+
...props
|
|
2045
|
+
}
|
|
2046
|
+
));
|
|
2047
|
+
DropdownMenuSeparator.displayName = DropdownMenuPrimitive.Separator.displayName;
|
|
2048
|
+
var DropdownMenuShortcut = ({
|
|
2049
|
+
className,
|
|
2050
|
+
...props
|
|
2051
|
+
}) => /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
|
|
2052
|
+
"span",
|
|
2053
|
+
{
|
|
2054
|
+
"data-slot": "dropdown-menu-shortcut",
|
|
2055
|
+
className: cn2("ml-auto text-xs tracking-widest opacity-60", className),
|
|
2056
|
+
...props
|
|
2057
|
+
}
|
|
2058
|
+
);
|
|
2059
|
+
DropdownMenuShortcut.displayName = "DropdownMenuShortcut";
|
|
2060
|
+
|
|
2061
|
+
// src/desktop/table.tsx
|
|
2062
|
+
var import_jsx_runtime22 = require("react/jsx-runtime");
|
|
2063
|
+
var WIDTH2 = { auto: "", fit: "w-px whitespace-nowrap", xs: "w-16", sm: "w-24", md: "w-36", lg: "w-56" };
|
|
2064
|
+
var ALIGN = { start: "text-left", end: "text-right", center: "text-center" };
|
|
2065
|
+
var CONTROL_TAGS = /* @__PURE__ */ new Set(["BUTTON", "A", "INPUT", "SELECT", "TEXTAREA"]);
|
|
2066
|
+
function insideControl(target, row) {
|
|
2067
|
+
for (let el = target; el && el !== row; el = el.parentElement) {
|
|
2068
|
+
if (CONTROL_TAGS.has(el.tagName) || el.getAttribute("role") === "menuitem") return true;
|
|
2069
|
+
}
|
|
2070
|
+
return false;
|
|
2071
|
+
}
|
|
2072
|
+
function DataTable({
|
|
2073
|
+
columns,
|
|
2074
|
+
rows,
|
|
2075
|
+
rowKey,
|
|
2076
|
+
onRowSelect,
|
|
2077
|
+
selectedKey,
|
|
2078
|
+
empty,
|
|
2079
|
+
striped = true,
|
|
2080
|
+
detail,
|
|
2081
|
+
isDisabled,
|
|
2082
|
+
rowAction,
|
|
2083
|
+
rowMenu,
|
|
2084
|
+
defaultSort,
|
|
2085
|
+
rowData,
|
|
2086
|
+
className,
|
|
2087
|
+
"aria-label": ariaLabel,
|
|
2088
|
+
...rest
|
|
2089
|
+
}) {
|
|
2090
|
+
const bodyRef = React18.useRef(null);
|
|
2091
|
+
const [expanded, setExpanded] = React18.useState(() => /* @__PURE__ */ new Set());
|
|
2092
|
+
const toggle = (key, open) => setExpanded((prev) => {
|
|
2093
|
+
const next = new Set(prev);
|
|
2094
|
+
if (open ?? !next.has(key)) next.add(key);
|
|
2095
|
+
else next.delete(key);
|
|
2096
|
+
return next;
|
|
2097
|
+
});
|
|
2098
|
+
const [sort, setSort] = React18.useState(defaultSort ?? null);
|
|
2099
|
+
const sortColumn = sort ? columns.find((c) => c.key === sort.key && c.sort) : void 0;
|
|
2100
|
+
const ordered = React18.useMemo(() => {
|
|
2101
|
+
if (!sort || !sortColumn?.sort) return rows;
|
|
2102
|
+
const by = sortColumn.sort;
|
|
2103
|
+
const sign = sort.direction === "ascending" ? 1 : -1;
|
|
2104
|
+
return [...rows].sort((a, b) => sign * by(a, b));
|
|
2105
|
+
}, [rows, sort, sortColumn]);
|
|
2106
|
+
const toggleSort = (key) => setSort((prev) => prev?.key === key ? { key, direction: prev.direction === "ascending" ? "descending" : "ascending" } : { key, direction: "descending" });
|
|
2107
|
+
const hasActions = Boolean(rowAction || rowMenu);
|
|
2108
|
+
const span = columns.length + (detail ? 1 : 0) + (hasActions ? 1 : 0);
|
|
2109
|
+
const moveFocus = (event, delta) => {
|
|
2110
|
+
const rowsEls = Array.from(bodyRef.current?.rows ?? []).filter((tr) => tr.tabIndex >= 0);
|
|
2111
|
+
const next = rowsEls[rowsEls.indexOf(event.currentTarget) + delta];
|
|
2112
|
+
if (next) {
|
|
2113
|
+
event.preventDefault();
|
|
2114
|
+
next.focus();
|
|
2115
|
+
}
|
|
2116
|
+
};
|
|
2117
|
+
return /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("div", { "data-slot": "data-table", className: cn("overflow-hidden rounded-group bg-content shadow-raised", className), ...rest, children: /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("table", { "aria-label": ariaLabel, className: "w-full table-auto border-collapse text-body", children: [
|
|
2118
|
+
/* @__PURE__ */ (0, import_jsx_runtime22.jsx)("thead", { children: /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("tr", { className: "h-row-compact border-b border-separator", children: [
|
|
2119
|
+
detail ? /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("th", { "aria-hidden": true, className: "w-6" }) : null,
|
|
2120
|
+
columns.map((column) => {
|
|
2121
|
+
const sorted = sort?.key === column.key ? sort.direction : void 0;
|
|
2122
|
+
return /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
|
|
2123
|
+
"th",
|
|
2124
|
+
{
|
|
2125
|
+
scope: "col",
|
|
2126
|
+
"aria-sort": column.sort ? sorted ?? "none" : void 0,
|
|
2127
|
+
className: cn(
|
|
2128
|
+
"px-3 text-caption font-medium text-label-secondary",
|
|
2129
|
+
WIDTH2[column.width ?? "auto"],
|
|
2130
|
+
ALIGN[column.align ?? (column.numeric ? "end" : "start")]
|
|
2131
|
+
),
|
|
2132
|
+
children: column.sort ? /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(
|
|
2133
|
+
"button",
|
|
2134
|
+
{
|
|
2135
|
+
type: "button",
|
|
2136
|
+
onClick: () => toggleSort(column.key),
|
|
2137
|
+
className: cn(
|
|
2138
|
+
"focus-ring inline-flex items-center gap-1 rounded-control hover:text-label",
|
|
2139
|
+
sorted && "text-label",
|
|
2140
|
+
(column.align ?? (column.numeric ? "end" : "start")) === "end" && "flex-row-reverse"
|
|
2141
|
+
),
|
|
2142
|
+
children: [
|
|
2143
|
+
column.header,
|
|
2144
|
+
/* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
|
|
2145
|
+
ChevronDownIcon,
|
|
2146
|
+
{
|
|
2147
|
+
"aria-hidden": true,
|
|
2148
|
+
className: cn("size-2.5 transition-transform duration-100", !sorted && "invisible", sorted === "ascending" && "rotate-180")
|
|
2149
|
+
}
|
|
2150
|
+
)
|
|
2151
|
+
]
|
|
2152
|
+
}
|
|
2153
|
+
) : column.header
|
|
2154
|
+
},
|
|
2155
|
+
column.key
|
|
2156
|
+
);
|
|
2157
|
+
}),
|
|
2158
|
+
hasActions ? /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("th", { "aria-label": "Actions", className: "w-px" }) : null
|
|
2159
|
+
] }) }),
|
|
2160
|
+
rows.length === 0 && empty ? /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("tbody", { children: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("tr", { children: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("td", { colSpan: span, children: empty }) }) }) : /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("tbody", { ref: bodyRef, children: ordered.map((row, index) => {
|
|
2161
|
+
const key = rowKey(row);
|
|
2162
|
+
const selected = selectedKey === key;
|
|
2163
|
+
const disabled = isDisabled?.(row) ?? false;
|
|
2164
|
+
const content = detail && !disabled ? detail(row) : null;
|
|
2165
|
+
const open = content !== null && expanded.has(key);
|
|
2166
|
+
const activate = onRowSelect ? () => onRowSelect(row) : content !== null ? () => toggle(key) : void 0;
|
|
2167
|
+
const focusable = Boolean(activate) && !disabled;
|
|
2168
|
+
return /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(React18.Fragment, { children: [
|
|
2169
|
+
/* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(
|
|
2170
|
+
"tr",
|
|
2171
|
+
{
|
|
2172
|
+
...rowData?.(row),
|
|
2173
|
+
tabIndex: focusable ? 0 : void 0,
|
|
2174
|
+
"aria-selected": onRowSelect ? selected : void 0,
|
|
2175
|
+
"aria-expanded": content !== null ? open : void 0,
|
|
2176
|
+
"aria-disabled": disabled || void 0,
|
|
2177
|
+
"data-row-key": key,
|
|
2178
|
+
onClick: focusable ? (event) => {
|
|
2179
|
+
if (insideControl(event.target, event.currentTarget)) return;
|
|
2180
|
+
activate?.();
|
|
2181
|
+
} : void 0,
|
|
2182
|
+
onKeyDown: focusable ? (event) => {
|
|
2183
|
+
if (event.key === "Enter" || event.key === " ") {
|
|
2184
|
+
event.preventDefault();
|
|
2185
|
+
activate?.();
|
|
2186
|
+
} else if (event.key === "ArrowDown") moveFocus(event, 1);
|
|
2187
|
+
else if (event.key === "ArrowUp") moveFocus(event, -1);
|
|
2188
|
+
else if (event.key === "ArrowRight" && content !== null) toggle(key, true);
|
|
2189
|
+
else if (event.key === "ArrowLeft" && content !== null) toggle(key, false);
|
|
2190
|
+
} : void 0,
|
|
2191
|
+
className: cn(
|
|
2192
|
+
"group/row h-row-compact",
|
|
2193
|
+
striped && index % 2 === 1 && "bg-fill",
|
|
2194
|
+
focusable && "focus-ring -outline-offset-3",
|
|
2195
|
+
disabled && "text-label-tertiary",
|
|
2196
|
+
selected && "bg-selection text-selection-label"
|
|
2197
|
+
),
|
|
2198
|
+
children: [
|
|
2199
|
+
detail ? /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("td", { className: "w-6 pl-2", children: content !== null ? /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
|
|
2200
|
+
ChevronRightIcon,
|
|
2201
|
+
{
|
|
2202
|
+
"aria-hidden": true,
|
|
2203
|
+
className: cn("size-icon-sm text-label-secondary transition-transform duration-100", open && "rotate-90")
|
|
2204
|
+
}
|
|
2205
|
+
) : null }) : null,
|
|
2206
|
+
columns.map((column) => /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
|
|
2207
|
+
"td",
|
|
2208
|
+
{
|
|
2209
|
+
className: cn(
|
|
2210
|
+
"max-w-0 truncate px-3",
|
|
2211
|
+
WIDTH2[column.width ?? "auto"],
|
|
2212
|
+
ALIGN[column.align ?? (column.numeric ? "end" : "start")],
|
|
2213
|
+
column.numeric && "tabular-nums",
|
|
2214
|
+
column.mono && "font-mono text-callout",
|
|
2215
|
+
column.width === "fit" && "max-w-none"
|
|
2216
|
+
),
|
|
2217
|
+
children: column.cell(row)
|
|
2218
|
+
},
|
|
2219
|
+
column.key
|
|
2220
|
+
)),
|
|
2221
|
+
hasActions ? /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
|
|
2222
|
+
"td",
|
|
2223
|
+
{
|
|
2224
|
+
className: cn(
|
|
2225
|
+
"w-px whitespace-nowrap px-2",
|
|
2226
|
+
// Finder: a row's buttons appear on hover, keyboard focus or selection only.
|
|
2227
|
+
!selected && "opacity-0 group-hover/row:opacity-100 group-focus-within/row:opacity-100 has-[[data-state=open]]:opacity-100"
|
|
2228
|
+
),
|
|
2229
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(RowActions, { action: rowAction?.(row) ?? null, menu: rowMenu?.(row) ?? null })
|
|
2230
|
+
}
|
|
2231
|
+
) : null
|
|
2232
|
+
]
|
|
2233
|
+
}
|
|
2234
|
+
),
|
|
2235
|
+
open ? /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("tr", { className: cn(striped && index % 2 === 1 && "bg-fill"), children: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("td", { colSpan: span, className: "px-3 pb-2 pl-9", children: content }) }) : null
|
|
2236
|
+
] }, key);
|
|
2237
|
+
}) })
|
|
2238
|
+
] }) });
|
|
2239
|
+
}
|
|
2240
|
+
function RowActions({ action, menu }) {
|
|
2241
|
+
const [asking, setAsking] = React18.useState(null);
|
|
2242
|
+
return /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("span", { className: "flex items-center justify-end gap-1", children: [
|
|
2243
|
+
action ? /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(Button, { size: "sm", disabled: action.disabled ?? false, onClick: action.onSelect, children: action.label }) : null,
|
|
2244
|
+
menu && menu.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(DropdownMenu, { children: [
|
|
2245
|
+
/* @__PURE__ */ (0, import_jsx_runtime22.jsx)(DropdownMenuTrigger, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(IconButton, { size: "sm", label: "More", icon: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(MoreHorizontalIcon, {}) }) }),
|
|
2246
|
+
/* @__PURE__ */ (0, import_jsx_runtime22.jsx)(DropdownMenuContent, { align: "end", children: menu.map((item) => /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
|
|
2247
|
+
DropdownMenuItem,
|
|
2248
|
+
{
|
|
2249
|
+
disabled: item.disabled ?? false,
|
|
2250
|
+
onSelect: () => item.destructive && item.confirm ? setAsking(item) : item.onSelect(),
|
|
2251
|
+
className: item.destructive ? "text-system-red focus:text-system-red" : void 0,
|
|
2252
|
+
children: item.label
|
|
2253
|
+
},
|
|
2254
|
+
item.label
|
|
2255
|
+
)) })
|
|
2256
|
+
] }) : null,
|
|
2257
|
+
/* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
|
|
2258
|
+
ConfirmDialog,
|
|
2259
|
+
{
|
|
2260
|
+
open: asking !== null,
|
|
2261
|
+
onOpenChange: (open) => open ? void 0 : setAsking(null),
|
|
2262
|
+
title: asking?.confirm ?? "",
|
|
2263
|
+
confirmLabel: asking?.label,
|
|
2264
|
+
variant: "destructive",
|
|
2265
|
+
onConfirm: () => {
|
|
2266
|
+
asking?.onSelect();
|
|
2267
|
+
setAsking(null);
|
|
2268
|
+
}
|
|
2269
|
+
}
|
|
2270
|
+
)
|
|
2271
|
+
] });
|
|
2272
|
+
}
|
|
2273
|
+
|
|
2274
|
+
// src/desktop/source-list.tsx
|
|
2275
|
+
var React19 = __toESM(require("react"), 1);
|
|
2276
|
+
var import_jsx_runtime23 = require("react/jsx-runtime");
|
|
2277
|
+
function SourceList({ className, onKeyDown, ...props }) {
|
|
2278
|
+
return /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
|
|
2279
|
+
"nav",
|
|
2280
|
+
{
|
|
2281
|
+
"data-slot": "source-list",
|
|
2282
|
+
className: cn("flex min-h-0 flex-col gap-3 px-2", className),
|
|
2283
|
+
onKeyDown: (event) => {
|
|
2284
|
+
onKeyDown?.(event);
|
|
2285
|
+
if (event.defaultPrevented) return;
|
|
2286
|
+
const keys = ["ArrowDown", "ArrowUp", "Home", "End"];
|
|
2287
|
+
if (!keys.includes(event.key)) return;
|
|
2288
|
+
const items = Array.from(event.currentTarget.querySelectorAll("[data-slot=source-list-item]:not(:disabled)"));
|
|
2289
|
+
const at = items.indexOf(document.activeElement);
|
|
2290
|
+
if (at === -1) return;
|
|
2291
|
+
const next = event.key === "Home" ? 0 : event.key === "End" ? items.length - 1 : at + (event.key === "ArrowDown" ? 1 : -1);
|
|
2292
|
+
const target = items[Math.max(0, Math.min(items.length - 1, next))];
|
|
2293
|
+
if (target) {
|
|
2294
|
+
event.preventDefault();
|
|
2295
|
+
target.focus();
|
|
2296
|
+
}
|
|
2297
|
+
},
|
|
2298
|
+
...props
|
|
2299
|
+
}
|
|
2300
|
+
);
|
|
2301
|
+
}
|
|
2302
|
+
function SourceListSection({ title, action, className, children, ...props }) {
|
|
2303
|
+
const id = React19.useId();
|
|
2304
|
+
return /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("div", { role: "group", "aria-labelledby": title ? id : void 0, "data-slot": "source-list-section", className: cn("flex flex-col", className), ...props, children: [
|
|
2305
|
+
title || action ? /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("div", { className: "flex h-row-compact items-center gap-2 px-2", children: [
|
|
2306
|
+
/* @__PURE__ */ (0, import_jsx_runtime23.jsx)(Text, { id, variant: "caption", tone: "tertiary", emphasized: true, className: "flex-1", truncate: true, children: title }),
|
|
2307
|
+
action
|
|
2308
|
+
] }) : null,
|
|
2309
|
+
children
|
|
2310
|
+
] });
|
|
2311
|
+
}
|
|
2312
|
+
var SourceListItem = React19.forwardRef(function SourceListItem2({ icon, label, hint, badge, current, className, type = "button", ...props }, ref) {
|
|
2313
|
+
return /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(
|
|
2314
|
+
"button",
|
|
2315
|
+
{
|
|
2316
|
+
ref,
|
|
2317
|
+
type,
|
|
2318
|
+
"data-slot": "source-list-item",
|
|
2319
|
+
"data-current": current || void 0,
|
|
2320
|
+
"aria-current": current ? "page" : void 0,
|
|
2321
|
+
className: cn(
|
|
2322
|
+
"focus-ring group/item flex w-full items-center gap-2 rounded-control px-2 text-left text-body text-label transition-colors duration-100 hover:bg-fill disabled:opacity-40",
|
|
2323
|
+
hint ? "min-h-row py-1" : "h-sidebar-row",
|
|
2324
|
+
current && "bg-selection-muted font-medium hover:bg-selection-muted [[data-window-active=false]_&]:bg-fill",
|
|
2325
|
+
className
|
|
2326
|
+
),
|
|
2327
|
+
...props,
|
|
2328
|
+
children: [
|
|
2329
|
+
icon ? /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("span", { className: cn("flex size-icon shrink-0 items-center justify-center text-label-secondary [&_svg]:size-icon", hint && "self-start mt-px"), children: icon }) : null,
|
|
2330
|
+
/* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("span", { className: "flex min-w-0 flex-1 flex-col", children: [
|
|
2331
|
+
/* @__PURE__ */ (0, import_jsx_runtime23.jsx)("span", { className: "truncate", children: label }),
|
|
2332
|
+
hint ? /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("span", { className: "truncate text-callout font-normal text-label-secondary", children: hint }) : null
|
|
2333
|
+
] }),
|
|
2334
|
+
badge ? /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("span", { className: "flex shrink-0 items-center text-callout tabular-nums text-label-tertiary", children: badge }) : null
|
|
2335
|
+
]
|
|
2336
|
+
}
|
|
2337
|
+
);
|
|
2338
|
+
});
|
|
2339
|
+
function SourceListMore({ expanded, onToggle, className, ...props }) {
|
|
2340
|
+
return /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
|
|
2341
|
+
"button",
|
|
2342
|
+
{
|
|
2343
|
+
...props,
|
|
2344
|
+
type: "button",
|
|
2345
|
+
"data-slot": "source-list-more",
|
|
2346
|
+
"aria-expanded": expanded,
|
|
2347
|
+
onClick: onToggle,
|
|
2348
|
+
className: cn("focus-ring flex h-sidebar-row w-full items-center rounded-control px-2 text-left text-callout text-label-tertiary hover:text-label-secondary", className),
|
|
2349
|
+
children: expanded ? "Show less" : "Show more"
|
|
2350
|
+
}
|
|
2351
|
+
);
|
|
2352
|
+
}
|
|
2353
|
+
|
|
2354
|
+
// src/desktop/state.tsx
|
|
2355
|
+
var import_jsx_runtime24 = require("react/jsx-runtime");
|
|
2356
|
+
function StateView({ kind, icon, title, message, actions, size = "inline", className, ...props }) {
|
|
2357
|
+
return /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)(
|
|
2358
|
+
"div",
|
|
2359
|
+
{
|
|
2360
|
+
role: kind === "error" ? "alert" : kind === "loading" ? "status" : void 0,
|
|
2361
|
+
"aria-busy": kind === "loading" || void 0,
|
|
2362
|
+
"data-slot": "state-view",
|
|
2363
|
+
"data-kind": kind,
|
|
2364
|
+
className: cn(
|
|
2365
|
+
"flex flex-col items-center justify-center gap-2 px-6 text-center",
|
|
2366
|
+
size === "page" ? "h-full min-h-60 py-16" : "py-10",
|
|
2367
|
+
className
|
|
2368
|
+
),
|
|
2369
|
+
...props,
|
|
2370
|
+
children: [
|
|
2371
|
+
kind === "loading" ? /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(Spinner, { size: "lg" }) : icon ? /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
|
|
2372
|
+
"span",
|
|
2373
|
+
{
|
|
2374
|
+
className: cn(
|
|
2375
|
+
"mb-1 flex items-center justify-center [&_svg]:size-icon-xl",
|
|
2376
|
+
kind === "error" ? "text-system-red" : "text-label-tertiary"
|
|
2377
|
+
),
|
|
2378
|
+
children: icon
|
|
2379
|
+
}
|
|
2380
|
+
) : null,
|
|
2381
|
+
/* @__PURE__ */ (0, import_jsx_runtime24.jsx)(Text, { variant: size === "page" ? "title-3" : "headline", tone: kind === "loading" ? "secondary" : "primary", children: title }),
|
|
2382
|
+
message ? /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(Text, { as: "p", variant: "callout", tone: "secondary", selectable: kind === "error", className: "max-w-form", children: message }) : null,
|
|
2383
|
+
actions ? /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("div", { className: "mt-2 flex items-center gap-2", children: actions }) : null
|
|
2384
|
+
]
|
|
2385
|
+
}
|
|
2386
|
+
);
|
|
2387
|
+
}
|
|
2388
|
+
var BANNER_TONE = {
|
|
2389
|
+
neutral: { box: "bg-fill", icon: "text-label-secondary" },
|
|
2390
|
+
info: { box: "bg-system-blue/10", icon: "text-system-blue" },
|
|
2391
|
+
warning: { box: "bg-system-orange/12", icon: "text-system-orange" },
|
|
2392
|
+
danger: { box: "bg-system-red/10", icon: "text-system-red" },
|
|
2393
|
+
success: { box: "bg-system-green/10", icon: "text-system-green" }
|
|
2394
|
+
};
|
|
2395
|
+
function Banner({ tone, icon, title, message, actions, className, ...props }) {
|
|
2396
|
+
return /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)(
|
|
2397
|
+
"div",
|
|
2398
|
+
{
|
|
2399
|
+
role: tone === "danger" || tone === "warning" ? "alert" : "status",
|
|
2400
|
+
"data-slot": "banner",
|
|
2401
|
+
"data-tone": tone,
|
|
2402
|
+
className: cn("flex gap-2.5 rounded-group px-3 py-2", message ? "items-start" : "items-center", BANNER_TONE[tone].box, className),
|
|
2403
|
+
...props,
|
|
2404
|
+
children: [
|
|
2405
|
+
icon ? /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("span", { className: cn("flex shrink-0 [&_svg]:size-icon", message && "mt-px", BANNER_TONE[tone].icon), children: icon }) : null,
|
|
2406
|
+
/* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("div", { className: "flex min-w-0 flex-1 flex-col gap-0.5", children: [
|
|
2407
|
+
/* @__PURE__ */ (0, import_jsx_runtime24.jsx)(Text, { emphasized: true, children: title }),
|
|
2408
|
+
message ? /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(Text, { as: "p", variant: "callout", tone: "secondary", selectable: true, children: message }) : null
|
|
2409
|
+
] }),
|
|
2410
|
+
actions ? /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("div", { className: "flex shrink-0 items-center gap-2", children: actions }) : null
|
|
2411
|
+
]
|
|
2412
|
+
}
|
|
2413
|
+
);
|
|
2414
|
+
}
|
|
2415
|
+
function AlertGlyph() {
|
|
2416
|
+
return /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: 1.75, strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": true, children: [
|
|
2417
|
+
/* @__PURE__ */ (0, import_jsx_runtime24.jsx)("path", { d: "m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3" }),
|
|
2418
|
+
/* @__PURE__ */ (0, import_jsx_runtime24.jsx)("path", { d: "M12 9v4" }),
|
|
2419
|
+
/* @__PURE__ */ (0, import_jsx_runtime24.jsx)("path", { d: "M12 17h.01" })
|
|
2420
|
+
] });
|
|
2421
|
+
}
|
|
2422
|
+
function ErrorBanner({ error, onDismiss, title = "That didn't work", ...props }) {
|
|
2423
|
+
if (!error) return null;
|
|
2424
|
+
return /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
|
|
2425
|
+
Banner,
|
|
2426
|
+
{
|
|
2427
|
+
tone: "danger",
|
|
2428
|
+
icon: /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(AlertGlyph, {}),
|
|
2429
|
+
title,
|
|
2430
|
+
message: error.message,
|
|
2431
|
+
actions: /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(Button, { size: "sm", onClick: onDismiss, children: "Dismiss" }),
|
|
2432
|
+
...props
|
|
2433
|
+
}
|
|
2434
|
+
);
|
|
2435
|
+
}
|
|
2436
|
+
|
|
2437
|
+
// src/desktop/resource.tsx
|
|
2438
|
+
var import_jsx_runtime25 = require("react/jsx-runtime");
|
|
2439
|
+
function Resource({ state, loading, failed, isEmpty, empty, size = "inline", children }) {
|
|
2440
|
+
if (state.data === void 0) {
|
|
2441
|
+
if (state.error) {
|
|
2442
|
+
return /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
|
|
2443
|
+
StateView,
|
|
2444
|
+
{
|
|
2445
|
+
kind: "error",
|
|
2446
|
+
size,
|
|
2447
|
+
title: failed,
|
|
2448
|
+
message: state.error.message,
|
|
2449
|
+
actions: state.refetch ? /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(Button, { onClick: () => void state.refetch?.(), children: "Try again" }) : void 0
|
|
2450
|
+
}
|
|
2451
|
+
);
|
|
2452
|
+
}
|
|
2453
|
+
return /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(StateView, { kind: "loading", size, title: loading });
|
|
2454
|
+
}
|
|
2455
|
+
const blank = isEmpty ? isEmpty(state.data) : Array.isArray(state.data) && state.data.length === 0;
|
|
2456
|
+
if (blank && empty) return /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(import_jsx_runtime25.Fragment, { children: empty });
|
|
2457
|
+
return /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(import_jsx_runtime25.Fragment, { children: children(state.data) });
|
|
2458
|
+
}
|
|
2459
|
+
|
|
2460
|
+
// src/desktop/tag.tsx
|
|
2461
|
+
var import_jsx_runtime26 = require("react/jsx-runtime");
|
|
2462
|
+
var TONE = {
|
|
2463
|
+
neutral: "bg-fill-secondary text-label-secondary",
|
|
2464
|
+
tint: "bg-tint/12 text-tint",
|
|
2465
|
+
success: "bg-system-green/14 text-system-green",
|
|
2466
|
+
warning: "bg-system-orange/14 text-system-orange",
|
|
2467
|
+
danger: "bg-system-red/12 text-system-red"
|
|
2468
|
+
};
|
|
2469
|
+
function Tag({ tone = "neutral", className, ...props }) {
|
|
2470
|
+
return /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
|
|
2471
|
+
"span",
|
|
2472
|
+
{
|
|
2473
|
+
"data-slot": "tag",
|
|
2474
|
+
"data-tone": tone,
|
|
2475
|
+
className: cn(
|
|
2476
|
+
"inline-flex h-4.5 shrink-0 items-center gap-1 whitespace-nowrap rounded-full px-1.5 text-caption font-medium [&_svg]:size-2.5",
|
|
2477
|
+
TONE[tone],
|
|
2478
|
+
className
|
|
2479
|
+
),
|
|
2480
|
+
...props
|
|
2481
|
+
}
|
|
2482
|
+
);
|
|
2483
|
+
}
|
|
2484
|
+
var DOT = {
|
|
2485
|
+
neutral: "bg-system-gray",
|
|
2486
|
+
success: "bg-system-green",
|
|
2487
|
+
warning: "bg-system-orange",
|
|
2488
|
+
danger: "bg-system-red",
|
|
2489
|
+
tint: "bg-tint"
|
|
2490
|
+
};
|
|
2491
|
+
function StatusLight({ tone, label, className, ...props }) {
|
|
2492
|
+
return /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
|
|
2493
|
+
"span",
|
|
2494
|
+
{
|
|
2495
|
+
role: "img",
|
|
2496
|
+
"aria-label": label,
|
|
2497
|
+
"data-slot": "status-light",
|
|
2498
|
+
className: cn("inline-block size-2 shrink-0 rounded-full", DOT[tone], className),
|
|
2499
|
+
...props
|
|
2500
|
+
}
|
|
2501
|
+
);
|
|
2502
|
+
}
|
|
2503
|
+
function Kbd({ className, ...props }) {
|
|
2504
|
+
return /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
|
|
2505
|
+
"kbd",
|
|
2506
|
+
{
|
|
2507
|
+
"data-slot": "kbd",
|
|
2508
|
+
className: cn("inline-flex h-4.5 items-center rounded-control bg-fill-secondary px-1 font-sans text-caption text-label-secondary", className),
|
|
2509
|
+
...props
|
|
2510
|
+
}
|
|
2511
|
+
);
|
|
2512
|
+
}
|
|
2513
|
+
|
|
2514
|
+
// src/desktop/coming-soon.tsx
|
|
2515
|
+
var import_jsx_runtime27 = require("react/jsx-runtime");
|
|
2516
|
+
function ComingSoon({ icon, title, summary, includes, className, ...props }) {
|
|
2517
|
+
return /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(Page, { width: "full", "data-coming-soon": title, className, ...props, children: /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("div", { className: "mx-auto flex max-w-form flex-col items-center gap-3 pt-16 text-center", children: [
|
|
2518
|
+
/* @__PURE__ */ (0, import_jsx_runtime27.jsx)("span", { className: "flex size-16 items-center justify-center rounded-panel bg-fill-secondary text-label-secondary [&_svg]:size-icon-xl", children: icon }),
|
|
2519
|
+
/* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("div", { className: "flex flex-col items-center gap-1.5", children: [
|
|
2520
|
+
/* @__PURE__ */ (0, import_jsx_runtime27.jsx)(Tag, { children: "Coming soon" }),
|
|
2521
|
+
/* @__PURE__ */ (0, import_jsx_runtime27.jsx)(Text, { as: "h1", variant: "title-1", children: title }),
|
|
2522
|
+
/* @__PURE__ */ (0, import_jsx_runtime27.jsx)(Text, { as: "p", variant: "body", tone: "secondary", className: "max-w-form", children: summary })
|
|
2523
|
+
] }),
|
|
2524
|
+
includes && includes.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("ul", { className: "mt-3 flex flex-col gap-2 text-left", children: includes.slice(0, 4).map((line) => /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("li", { className: "flex items-start gap-2", children: [
|
|
2525
|
+
/* @__PURE__ */ (0, import_jsx_runtime27.jsx)("span", { "aria-hidden": true, className: "mt-1.5 size-1.5 shrink-0 rounded-full bg-label-tertiary" }),
|
|
2526
|
+
/* @__PURE__ */ (0, import_jsx_runtime27.jsx)(Text, { variant: "callout", tone: "secondary", children: line })
|
|
2527
|
+
] }, line)) }) : null
|
|
2528
|
+
] }) });
|
|
2529
|
+
}
|
|
2530
|
+
|
|
2531
|
+
// src/desktop/meter.tsx
|
|
2532
|
+
var ProgressPrimitive = __toESM(require("@radix-ui/react-progress"), 1);
|
|
2533
|
+
var import_jsx_runtime28 = require("react/jsx-runtime");
|
|
2534
|
+
var METER_THRESHOLDS = { warning: 0.7, critical: 0.9 };
|
|
2535
|
+
function meterTone(fraction) {
|
|
2536
|
+
if (fraction >= METER_THRESHOLDS.critical) return "critical";
|
|
2537
|
+
if (fraction >= METER_THRESHOLDS.warning) return "warning";
|
|
2538
|
+
return "normal";
|
|
2539
|
+
}
|
|
2540
|
+
function Meter({ value, max = 100, kind = "progress", size = "md", className, "aria-label": ariaLabel, ...data }) {
|
|
2541
|
+
const fraction = value === null ? 0 : Math.min(1, Math.max(0, value / max));
|
|
2542
|
+
const tone = kind === "level" ? meterTone(fraction) : "progress";
|
|
2543
|
+
return /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
|
|
2544
|
+
ProgressPrimitive.Root,
|
|
2545
|
+
{
|
|
2546
|
+
...data,
|
|
2547
|
+
value,
|
|
2548
|
+
max,
|
|
2549
|
+
"aria-label": ariaLabel,
|
|
2550
|
+
"data-slot": "meter",
|
|
2551
|
+
className: cn("relative w-full min-w-16 overflow-hidden rounded-full bg-fill-tertiary", size === "sm" ? "h-1" : "h-1.5", className),
|
|
2552
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
|
|
2553
|
+
ProgressPrimitive.Indicator,
|
|
2554
|
+
{
|
|
2555
|
+
className: cn(
|
|
2556
|
+
"h-full rounded-full transition-all duration-200",
|
|
2557
|
+
value === null && "w-1/3 animate-pulse",
|
|
2558
|
+
tone === "progress" && "bg-tint",
|
|
2559
|
+
tone === "normal" && "bg-system-green",
|
|
2560
|
+
tone === "warning" && "bg-system-orange",
|
|
2561
|
+
tone === "critical" && "bg-system-red"
|
|
2562
|
+
),
|
|
2563
|
+
style: value === null ? void 0 : { width: `${fraction * 100}%` }
|
|
2564
|
+
}
|
|
2565
|
+
)
|
|
2566
|
+
}
|
|
2567
|
+
);
|
|
2568
|
+
}
|
|
2569
|
+
|
|
2570
|
+
// src/desktop/format.ts
|
|
2571
|
+
var BYTE_UNITS = ["bytes", "KB", "MB", "GB", "TB", "PB"];
|
|
2572
|
+
function formatBytes(bytes) {
|
|
2573
|
+
if (bytes === null || bytes === void 0 || !Number.isFinite(bytes)) return "\u2014";
|
|
2574
|
+
if (bytes < 1e3) return `${Math.round(bytes)} ${bytes === 1 ? "byte" : "bytes"}`;
|
|
2575
|
+
let value = bytes;
|
|
2576
|
+
let unit = 0;
|
|
2577
|
+
while (value >= 1e3 && unit < BYTE_UNITS.length - 1) {
|
|
2578
|
+
value /= 1e3;
|
|
2579
|
+
unit += 1;
|
|
2580
|
+
}
|
|
2581
|
+
return `${value >= 100 ? Math.round(value) : value.toFixed(1).replace(/\.0$/, "")} ${BYTE_UNITS[unit]}`;
|
|
2582
|
+
}
|
|
2583
|
+
function formatDuration(ms) {
|
|
2584
|
+
if (ms === null || ms === void 0 || !Number.isFinite(ms) || ms < 0) return "\u2014";
|
|
2585
|
+
const s = Math.floor(ms / 1e3);
|
|
2586
|
+
if (s < 60) return `${s} sec`;
|
|
2587
|
+
const m = Math.floor(s / 60);
|
|
2588
|
+
if (m < 60) return `${m} min`;
|
|
2589
|
+
const h = Math.floor(m / 60);
|
|
2590
|
+
if (h < 24) return m % 60 ? `${h} hr ${m % 60} min` : `${h} hr`;
|
|
2591
|
+
const d = Math.floor(h / 24);
|
|
2592
|
+
return h % 24 ? `${d} ${d === 1 ? "day" : "days"} ${h % 24} hr` : `${d} ${d === 1 ? "day" : "days"}`;
|
|
2593
|
+
}
|
|
2594
|
+
var RELATIVE = new Intl.RelativeTimeFormat(void 0, { numeric: "auto" });
|
|
2595
|
+
var SAME_YEAR = new Intl.DateTimeFormat(void 0, { month: "short", day: "numeric", hour: "numeric", minute: "2-digit" });
|
|
2596
|
+
var OTHER_YEAR = new Intl.DateTimeFormat(void 0, { year: "numeric", month: "short", day: "numeric" });
|
|
2597
|
+
function formatRelativeTime(at, now = Date.now()) {
|
|
2598
|
+
if (at === null || at === void 0) return "\u2014";
|
|
2599
|
+
const time = at instanceof Date ? at.getTime() : typeof at === "string" ? Date.parse(at) : at;
|
|
2600
|
+
if (!Number.isFinite(time)) return "\u2014";
|
|
2601
|
+
const seconds = Math.round((time - now) / 1e3);
|
|
2602
|
+
const abs = Math.abs(seconds);
|
|
2603
|
+
if (abs < 45) return "just now";
|
|
2604
|
+
if (abs < 3600) return RELATIVE.format(Math.round(seconds / 60), "minute");
|
|
2605
|
+
if (abs < 86400) return RELATIVE.format(Math.round(seconds / 3600), "hour");
|
|
2606
|
+
if (abs < 86400 * 2) return RELATIVE.format(Math.round(seconds / 86400), "day");
|
|
2607
|
+
return new Date(time).getFullYear() === new Date(now).getFullYear() ? SAME_YEAR.format(time) : OTHER_YEAR.format(time);
|
|
2608
|
+
}
|
|
2609
|
+
function errorMessage(error) {
|
|
2610
|
+
if (error instanceof Error) return error.message || error.name;
|
|
2611
|
+
if (typeof error === "string") return error;
|
|
2612
|
+
if (error && typeof error === "object" && "message" in error && typeof error.message === "string") {
|
|
2613
|
+
return error.message;
|
|
2614
|
+
}
|
|
2615
|
+
return "Something went wrong.";
|
|
2616
|
+
}
|
|
2617
|
+
function formatAge(at, now = Date.now()) {
|
|
2618
|
+
if (at === null || at === void 0) return "\u2014";
|
|
2619
|
+
const time = at instanceof Date ? at.getTime() : typeof at === "string" ? Date.parse(at) : at;
|
|
2620
|
+
if (!Number.isFinite(time)) return "\u2014";
|
|
2621
|
+
const minutes = Math.floor((now - time) / 6e4);
|
|
2622
|
+
if (minutes < 1) return "now";
|
|
2623
|
+
if (minutes < 60) return `${minutes}m`;
|
|
2624
|
+
if (minutes < 60 * 24) return `${Math.floor(minutes / 60)}h`;
|
|
2625
|
+
if (minutes < 60 * 24 * 7) return `${Math.floor(minutes / (60 * 24))}d`;
|
|
2626
|
+
return new Date(time).toLocaleDateString(void 0, { month: "short", day: "numeric" });
|
|
2627
|
+
}
|
|
2628
|
+
var CLOCK = new Intl.DateTimeFormat(void 0, { hour: "numeric", minute: "2-digit" });
|
|
2629
|
+
function formatClockTime(at) {
|
|
2630
|
+
if (at === null || at === void 0) return "\u2014";
|
|
2631
|
+
const time = at instanceof Date ? at.getTime() : typeof at === "string" ? Date.parse(at) : at;
|
|
2632
|
+
return Number.isFinite(time) ? CLOCK.format(time) : "\u2014";
|
|
2633
|
+
}
|
|
2634
|
+
function formatUsage(used, total) {
|
|
2635
|
+
if (used === null || used === void 0 || total === null || total === void 0) return "\u2014";
|
|
2636
|
+
return `${formatBytes(used)} of ${formatBytes(total)} in use`;
|
|
2637
|
+
}
|
|
2638
|
+
|
|
2639
|
+
// src/context-menu.tsx
|
|
2640
|
+
var ContextMenuPrimitive = __toESM(require("@radix-ui/react-context-menu"), 1);
|
|
2641
|
+
var React20 = __toESM(require("react"), 1);
|
|
2642
|
+
var import_jsx_runtime29 = require("react/jsx-runtime");
|
|
2643
|
+
var ContextMenu = ContextMenuPrimitive.Root;
|
|
2644
|
+
var ContextMenuTrigger = ContextMenuPrimitive.Trigger;
|
|
2645
|
+
var SURFACE_CLASS2 = "z-[10001] min-w-[8rem] max-h-[var(--radix-context-menu-content-available-height)] overflow-y-auto overflow-x-hidden overscroll-contain rounded-md border bg-popover p-1 text-popover-foreground";
|
|
2646
|
+
var ITEM_CLASS = "relative flex select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:opacity-50";
|
|
2647
|
+
var INDICATOR_ITEM_CLASS = "relative flex select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:opacity-50";
|
|
2648
|
+
var ContextMenuSubTrigger = React20.forwardRef(({ className, inset, children, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime29.jsxs)(
|
|
2649
|
+
ContextMenuPrimitive.SubTrigger,
|
|
2650
|
+
{
|
|
2651
|
+
ref,
|
|
2652
|
+
"data-slot": "context-menu-sub-trigger",
|
|
2653
|
+
className: cn2(
|
|
2654
|
+
"flex select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[state=open]:bg-accent data-[state=open]:text-accent-foreground",
|
|
2655
|
+
inset && "pl-8",
|
|
2656
|
+
className
|
|
2657
|
+
),
|
|
2658
|
+
...props,
|
|
2659
|
+
children: [
|
|
2660
|
+
children,
|
|
2661
|
+
/* @__PURE__ */ (0, import_jsx_runtime29.jsx)(RadixChevronRightIcon, { className: "ml-auto h-4 w-4" })
|
|
2662
|
+
]
|
|
2663
|
+
}
|
|
2664
|
+
));
|
|
2665
|
+
ContextMenuSubTrigger.displayName = ContextMenuPrimitive.SubTrigger.displayName;
|
|
2666
|
+
var ContextMenuSubContent = React20.forwardRef(({ className, container, onFocusOutside, children, ...props }, ref) => {
|
|
2667
|
+
const portalContainer = usePortalContainer(container);
|
|
2668
|
+
const keep = useSubmenuSurvivesParentPanelFocus(ref, onFocusOutside);
|
|
2669
|
+
const edges = useMenuScrollEdges(keep.ref);
|
|
2670
|
+
return /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(ContextMenuPrimitive.Portal, { container: portalContainer, children: /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(
|
|
2671
|
+
ContextMenuPrimitive.SubContent,
|
|
2672
|
+
{
|
|
2673
|
+
ref: edges.ref,
|
|
2674
|
+
onFocusOutside: keep.onFocusOutside,
|
|
2675
|
+
"data-slot": "context-menu-sub-content",
|
|
2676
|
+
className: cn2(SURFACE_CLASS2, edges.surfaceClassName, "shadow-lg", MOTION_POPPER, className),
|
|
2677
|
+
...props,
|
|
2678
|
+
children: edges.wrap(children)
|
|
2679
|
+
}
|
|
2680
|
+
) });
|
|
2681
|
+
});
|
|
2682
|
+
ContextMenuSubContent.displayName = ContextMenuPrimitive.SubContent.displayName;
|
|
2683
|
+
var ContextMenuContent = React20.forwardRef(({ className, container, children, ...props }, ref) => {
|
|
2684
|
+
const portalContainer = usePortalContainer(container);
|
|
2685
|
+
const edges = useMenuScrollEdges(ref);
|
|
2686
|
+
return /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(ContextMenuPrimitive.Portal, { container: portalContainer, children: /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(
|
|
2687
|
+
ContextMenuPrimitive.Content,
|
|
2688
|
+
{
|
|
2689
|
+
ref: edges.ref,
|
|
2690
|
+
"data-slot": "context-menu-content",
|
|
2691
|
+
className: cn2(SURFACE_CLASS2, edges.surfaceClassName, "shadow-md", MOTION_POPPER, className),
|
|
2692
|
+
...props,
|
|
2693
|
+
children: edges.wrap(children)
|
|
2694
|
+
}
|
|
2695
|
+
) });
|
|
2696
|
+
});
|
|
2697
|
+
ContextMenuContent.displayName = ContextMenuPrimitive.Content.displayName;
|
|
2698
|
+
var ContextMenuItem = React20.forwardRef(({ className, inset, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(
|
|
2699
|
+
ContextMenuPrimitive.Item,
|
|
2700
|
+
{
|
|
2701
|
+
ref,
|
|
2702
|
+
"data-slot": "context-menu-item",
|
|
2703
|
+
className: cn2(
|
|
2704
|
+
ITEM_CLASS,
|
|
2705
|
+
"gap-2 [&>svg]:size-4 [&>svg]:shrink-0",
|
|
2706
|
+
inset && "pl-8",
|
|
2707
|
+
className
|
|
2708
|
+
),
|
|
2709
|
+
...props
|
|
2710
|
+
}
|
|
2711
|
+
));
|
|
2712
|
+
ContextMenuItem.displayName = ContextMenuPrimitive.Item.displayName;
|
|
2713
|
+
var ContextMenuCheckboxItem = React20.forwardRef(({ className, children, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime29.jsxs)(
|
|
2714
|
+
ContextMenuPrimitive.CheckboxItem,
|
|
2715
|
+
{
|
|
2716
|
+
ref,
|
|
2717
|
+
"data-slot": "context-menu-checkbox-item",
|
|
2718
|
+
className: cn2(INDICATOR_ITEM_CLASS, className),
|
|
2719
|
+
...props,
|
|
2720
|
+
children: [
|
|
2721
|
+
/* @__PURE__ */ (0, import_jsx_runtime29.jsx)("span", { className: "absolute left-2 flex h-3.5 w-3.5 items-center justify-center", children: /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(ContextMenuPrimitive.ItemIndicator, { children: /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(CheckIcon, { className: "h-4 w-4" }) }) }),
|
|
2722
|
+
children
|
|
2723
|
+
]
|
|
2724
|
+
}
|
|
2725
|
+
));
|
|
2726
|
+
ContextMenuCheckboxItem.displayName = ContextMenuPrimitive.CheckboxItem.displayName;
|
|
2727
|
+
var ContextMenuRadioItem = React20.forwardRef(({ className, children, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime29.jsxs)(
|
|
2728
|
+
ContextMenuPrimitive.RadioItem,
|
|
2729
|
+
{
|
|
2730
|
+
ref,
|
|
2731
|
+
"data-slot": "context-menu-radio-item",
|
|
2732
|
+
className: cn2(INDICATOR_ITEM_CLASS, className),
|
|
2733
|
+
...props,
|
|
2734
|
+
children: [
|
|
2735
|
+
/* @__PURE__ */ (0, import_jsx_runtime29.jsx)("span", { className: "absolute left-2 flex h-3.5 w-3.5 items-center justify-center", children: /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(ContextMenuPrimitive.ItemIndicator, { children: /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(DotFilledIcon, { className: "h-4 w-4 fill-current" }) }) }),
|
|
2736
|
+
children
|
|
2737
|
+
]
|
|
2738
|
+
}
|
|
2739
|
+
));
|
|
2740
|
+
ContextMenuRadioItem.displayName = ContextMenuPrimitive.RadioItem.displayName;
|
|
2741
|
+
var ContextMenuLabel = React20.forwardRef(({ className, inset, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(
|
|
2742
|
+
ContextMenuPrimitive.Label,
|
|
2743
|
+
{
|
|
2744
|
+
ref,
|
|
2745
|
+
"data-slot": "context-menu-label",
|
|
2746
|
+
className: cn2(
|
|
2747
|
+
"px-2 py-1.5 text-sm font-semibold text-foreground",
|
|
2748
|
+
inset && "pl-8",
|
|
2749
|
+
className
|
|
2750
|
+
),
|
|
2751
|
+
...props
|
|
2752
|
+
}
|
|
2753
|
+
));
|
|
2754
|
+
ContextMenuLabel.displayName = ContextMenuPrimitive.Label.displayName;
|
|
2755
|
+
var ContextMenuSeparator = React20.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(
|
|
2756
|
+
ContextMenuPrimitive.Separator,
|
|
2757
|
+
{
|
|
2758
|
+
ref,
|
|
2759
|
+
"data-slot": "context-menu-separator",
|
|
2760
|
+
className: cn2("-mx-1 my-1 h-px bg-border", className),
|
|
2761
|
+
...props
|
|
2762
|
+
}
|
|
2763
|
+
));
|
|
2764
|
+
ContextMenuSeparator.displayName = ContextMenuPrimitive.Separator.displayName;
|
|
2765
|
+
var ContextMenuShortcut = ({
|
|
2766
|
+
className,
|
|
2767
|
+
...props
|
|
2768
|
+
}) => /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(
|
|
2769
|
+
"span",
|
|
2770
|
+
{
|
|
2771
|
+
"data-slot": "context-menu-shortcut",
|
|
2772
|
+
className: cn2("ml-auto text-xs tracking-widest text-muted-foreground", className),
|
|
2773
|
+
...props
|
|
2774
|
+
}
|
|
2775
|
+
);
|
|
2776
|
+
ContextMenuShortcut.displayName = "ContextMenuShortcut";
|
|
2777
|
+
|
|
2778
|
+
// src/dialog.tsx
|
|
2779
|
+
var DialogPrimitive = __toESM(require("@radix-ui/react-dialog"), 1);
|
|
2780
|
+
var VisuallyHidden = __toESM(require("@radix-ui/react-visually-hidden"), 1);
|
|
2781
|
+
var import_react_tree2 = require("@ai-matrx/kit/react-tree");
|
|
2782
|
+
var React22 = __toESM(require("react"), 1);
|
|
2783
|
+
|
|
2784
|
+
// src/radix-dialog-modal-context.tsx
|
|
2785
|
+
var React21 = __toESM(require("react"), 1);
|
|
2786
|
+
var import_jsx_runtime30 = require("react/jsx-runtime");
|
|
2787
|
+
var CONTEXT_SLOT2 = /* @__PURE__ */ Symbol.for("ai-matrx.design-system.dialog-modal-context");
|
|
2788
|
+
var contextRegistry2 = globalThis;
|
|
2789
|
+
var RadixDialogModalContext = contextRegistry2[CONTEXT_SLOT2] ??= React21.createContext(true);
|
|
2790
|
+
function RadixDialogModalProvider({
|
|
2791
|
+
children,
|
|
2792
|
+
modal
|
|
2793
|
+
}) {
|
|
2794
|
+
return /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(RadixDialogModalContext.Provider, { value: modal, children });
|
|
2795
|
+
}
|
|
2796
|
+
function useRadixDialogModal() {
|
|
2797
|
+
return React21.useContext(RadixDialogModalContext);
|
|
2798
|
+
}
|
|
2799
|
+
|
|
2800
|
+
// src/use-is-mobile.ts
|
|
2801
|
+
var import_media_query = require("@ai-matrx/kit/media-query");
|
|
2802
|
+
|
|
2803
|
+
// src/layer-front.ts
|
|
2804
|
+
var LAYER_FRONT_EVENT = "matrx:layer-front";
|
|
2805
|
+
function announceLayerFront(element) {
|
|
2806
|
+
if (typeof document === "undefined") return;
|
|
2807
|
+
document.dispatchEvent(
|
|
2808
|
+
new CustomEvent(LAYER_FRONT_EVENT, { detail: { element } })
|
|
2809
|
+
);
|
|
2810
|
+
}
|
|
2811
|
+
|
|
2812
|
+
// src/dialog.tsx
|
|
2813
|
+
var import_jsx_runtime31 = require("react/jsx-runtime");
|
|
2814
|
+
var DialogWindowedByDefaultContext = React22.createContext(false);
|
|
2815
|
+
var Dialog = ({
|
|
2816
|
+
children,
|
|
2817
|
+
...props
|
|
2818
|
+
}) => {
|
|
2819
|
+
const isMobile = (0, import_media_query.useIsMobile)();
|
|
2820
|
+
const modal = props.modal ?? isMobile;
|
|
2821
|
+
return /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(DialogWindowedByDefaultContext.Provider, { value: props.modal === void 0, children: /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(RadixDialogModalProvider, { modal, children: /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(DialogPrimitive.Root, { ...props, modal, children }) }) });
|
|
2822
|
+
};
|
|
2823
|
+
Dialog.displayName = "Dialog";
|
|
2824
|
+
var DialogTrigger = DialogPrimitive.Trigger;
|
|
2825
|
+
var DialogClose = DialogPrimitive.Close;
|
|
2826
|
+
var DialogPortal = ({
|
|
2827
|
+
container,
|
|
2828
|
+
...props
|
|
2829
|
+
}) => {
|
|
2830
|
+
const resolved = usePortalContainer(container);
|
|
2831
|
+
return /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(DialogPrimitive.Portal, { container: resolved ?? null, ...props });
|
|
2832
|
+
};
|
|
2833
|
+
DialogPortal.displayName = "DialogPortal";
|
|
2834
|
+
var CONTEXT_SLOT3 = /* @__PURE__ */ Symbol.for("ai-matrx.design-system.dialog-container-context");
|
|
2835
|
+
var contextRegistry3 = globalThis;
|
|
2836
|
+
var DialogContainerContext = contextRegistry3[CONTEXT_SLOT3] ??= React22.createContext(null);
|
|
2837
|
+
var DialogOverlay = React22.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(
|
|
2838
|
+
DialogPrimitive.Overlay,
|
|
2839
|
+
{
|
|
2840
|
+
ref,
|
|
2841
|
+
"data-slot": "dialog-overlay",
|
|
2842
|
+
className: cn2(
|
|
2843
|
+
// Page context behind the modal stays readable; separation comes from a
|
|
2844
|
+
// light token scrim, not a blur.
|
|
2845
|
+
"fixed inset-0 z-[10000] bg-[var(--matrx-overlay-scrim-soft)]",
|
|
2846
|
+
MOTION_OVERLAY,
|
|
2847
|
+
className
|
|
2848
|
+
),
|
|
2849
|
+
...props
|
|
2850
|
+
}
|
|
2851
|
+
));
|
|
2852
|
+
DialogOverlay.displayName = DialogPrimitive.Overlay.displayName;
|
|
2853
|
+
var DialogContentPrimitive = React22.forwardRef(({ onInteractOutside, ...props }, ref) => {
|
|
2854
|
+
const isModal = useRadixDialogModal();
|
|
2855
|
+
useModalLayer(isModal);
|
|
2856
|
+
return /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(
|
|
2857
|
+
DialogPrimitive.Content,
|
|
2858
|
+
{
|
|
2859
|
+
...props,
|
|
2860
|
+
ref,
|
|
2861
|
+
"aria-modal": isModal || void 0,
|
|
2862
|
+
onInteractOutside: keepFloatingLayersInside(onInteractOutside)
|
|
2863
|
+
}
|
|
2864
|
+
);
|
|
2865
|
+
});
|
|
2866
|
+
DialogContentPrimitive.displayName = "DialogContentPrimitive";
|
|
2867
|
+
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";
|
|
2868
|
+
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";
|
|
2869
|
+
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";
|
|
2870
|
+
var DialogWindowHandleContext = React22.createContext(null);
|
|
2871
|
+
var WINDOW_KEEP_VISIBLE = 80;
|
|
2872
|
+
function isInteractiveTarget(target) {
|
|
2873
|
+
return target instanceof Element && Boolean(target.closest("button, a, input, textarea, select, [role='button'], [contenteditable='true']"));
|
|
2874
|
+
}
|
|
2875
|
+
var DialogContent = React22.forwardRef(
|
|
2876
|
+
({
|
|
2877
|
+
className,
|
|
2878
|
+
children,
|
|
2879
|
+
mobileSheet = true,
|
|
2880
|
+
animated = true,
|
|
2881
|
+
showCloseButton = true,
|
|
2882
|
+
container,
|
|
2883
|
+
dismissOnOutsideClick = false,
|
|
2884
|
+
onInteractOutside,
|
|
2885
|
+
onEscapeKeyDown,
|
|
2886
|
+
onPointerDownCapture,
|
|
2887
|
+
onFocusCapture,
|
|
2888
|
+
style,
|
|
2889
|
+
...props
|
|
2890
|
+
}, ref) => {
|
|
2891
|
+
const isMobile = (0, import_media_query.useIsMobile)();
|
|
2892
|
+
const isModal = useRadixDialogModal();
|
|
2893
|
+
const injectedContainer = usePortalContainer(container);
|
|
2894
|
+
const asSheet = mobileSheet && isMobile;
|
|
2895
|
+
const windowed = !isModal && !asSheet;
|
|
2896
|
+
const windowedByDefault = React22.useContext(DialogWindowedByDefaultContext);
|
|
2897
|
+
const [containerEl, setContainerEl] = React22.useState(
|
|
2898
|
+
null
|
|
2899
|
+
);
|
|
2900
|
+
const stacks = windowed && windowedByDefault;
|
|
2901
|
+
const [behind, setBehind] = React22.useState(false);
|
|
2902
|
+
React22.useEffect(() => {
|
|
2903
|
+
if (!stacks || !containerEl) return;
|
|
2904
|
+
const onFront = (event) => {
|
|
2905
|
+
const element = event.detail?.element ?? null;
|
|
2906
|
+
if (element && (element === containerEl || containerEl.contains(element))) return;
|
|
2907
|
+
setBehind(true);
|
|
2908
|
+
};
|
|
2909
|
+
document.addEventListener(LAYER_FRONT_EVENT, onFront);
|
|
2910
|
+
announceLayerFront(containerEl);
|
|
2911
|
+
return () => document.removeEventListener(LAYER_FRONT_EVENT, onFront);
|
|
2912
|
+
}, [stacks, containerEl]);
|
|
2913
|
+
const bringToFront = () => {
|
|
2914
|
+
if (!stacks || !containerEl) return;
|
|
2915
|
+
setBehind(false);
|
|
2916
|
+
announceLayerFront(containerEl);
|
|
2917
|
+
};
|
|
2918
|
+
const [offset, setOffset] = React22.useState({ x: 0, y: 0 });
|
|
2919
|
+
const dragRef = React22.useRef(null);
|
|
2920
|
+
const handle = React22.useMemo(() => {
|
|
2921
|
+
if (!windowed) return null;
|
|
2922
|
+
const clamp = (value, limit) => Math.max(-limit, Math.min(limit, value));
|
|
2923
|
+
return {
|
|
2924
|
+
onPointerDown: (event) => {
|
|
2925
|
+
if (event.button !== 0 || isInteractiveTarget(event.target)) return;
|
|
2926
|
+
dragRef.current = { x: event.clientX, y: event.clientY, baseX: offset.x, baseY: offset.y };
|
|
2927
|
+
event.currentTarget.setPointerCapture?.(event.pointerId);
|
|
2928
|
+
},
|
|
2929
|
+
onPointerMove: (event) => {
|
|
2930
|
+
const start = dragRef.current;
|
|
2931
|
+
if (!start) return;
|
|
2932
|
+
setOffset({
|
|
2933
|
+
x: clamp(start.baseX + event.clientX - start.x, window.innerWidth / 2 - WINDOW_KEEP_VISIBLE / 2),
|
|
2934
|
+
y: clamp(start.baseY + event.clientY - start.y, window.innerHeight / 2 - WINDOW_KEEP_VISIBLE / 2)
|
|
2935
|
+
});
|
|
2936
|
+
},
|
|
2937
|
+
onPointerUp: (event) => {
|
|
2938
|
+
if (!dragRef.current) return;
|
|
2939
|
+
dragRef.current = null;
|
|
2940
|
+
event.currentTarget.releasePointerCapture?.(event.pointerId);
|
|
2941
|
+
},
|
|
2942
|
+
onPointerCancel: () => {
|
|
2943
|
+
dragRef.current = null;
|
|
2944
|
+
},
|
|
2945
|
+
onDoubleClick: () => setOffset({ x: 0, y: 0 })
|
|
2946
|
+
};
|
|
2947
|
+
}, [windowed, offset.x, offset.y]);
|
|
2948
|
+
const mergedRef = React22.useCallback(
|
|
2949
|
+
(node) => {
|
|
2950
|
+
setContainerEl(node);
|
|
2951
|
+
if (typeof ref === "function") ref(node);
|
|
2952
|
+
else if (ref)
|
|
2953
|
+
ref.current = node;
|
|
2954
|
+
},
|
|
2955
|
+
[ref]
|
|
2956
|
+
);
|
|
2957
|
+
const hasTitle = (0, import_react_tree2.treeContainsComponent)(children, DialogTitle) || (0, import_react_tree2.treeContainsComponent)(children, DialogPrimitive.Title);
|
|
2958
|
+
const hasDescription = (0, import_react_tree2.treeContainsComponent)(children, DialogDescription) || (0, import_react_tree2.treeContainsComponent)(children, DialogPrimitive.Description);
|
|
2959
|
+
return /* @__PURE__ */ (0, import_jsx_runtime31.jsxs)(DialogPortal, { container, children: [
|
|
2960
|
+
isModal ? /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(DialogOverlay, {}) : null,
|
|
2961
|
+
/* @__PURE__ */ (0, import_jsx_runtime31.jsxs)(
|
|
2962
|
+
DialogContentPrimitive,
|
|
2963
|
+
{
|
|
2964
|
+
ref: mergedRef,
|
|
2965
|
+
"data-slot": "dialog-content",
|
|
2966
|
+
className: cn2(
|
|
2967
|
+
asSheet ? DIALOG_MOBILE_SHEET_CLASSES : DIALOG_DESKTOP_CLASSES,
|
|
2968
|
+
animated && (asSheet ? MOTION_BOTTOM_SHEET : MOTION_DIALOG),
|
|
2969
|
+
className,
|
|
2970
|
+
asSheet && DIALOG_MOBILE_SHEET_OVERRIDE,
|
|
2971
|
+
// A non-modal dialog is the coexistence contract for content that
|
|
2972
|
+
// can sit beside a host's floating window manager (which starts at
|
|
2973
|
+
// z=1000); staying below that boundary makes a newly focused window
|
|
2974
|
+
// usable without coupling the two systems.
|
|
2975
|
+
!isModal && !windowedByDefault && "z-[900]",
|
|
2976
|
+
// Another layer came to the front after this window (layer-front.ts).
|
|
2977
|
+
stacks && behind && "z-[999]",
|
|
2978
|
+
// Without an overlay the card needs its own separation from the page.
|
|
2979
|
+
windowed && "shadow-2xl ring-1 ring-border/60"
|
|
2980
|
+
),
|
|
2981
|
+
style: windowed && (offset.x !== 0 || offset.y !== 0) ? { ...style, translate: `${offset.x}px ${offset.y}px` } : style,
|
|
2982
|
+
onPointerDownCapture: (event) => {
|
|
2983
|
+
onPointerDownCapture?.(event);
|
|
2984
|
+
if (behind) bringToFront();
|
|
2985
|
+
},
|
|
2986
|
+
onFocusCapture: (event) => {
|
|
2987
|
+
onFocusCapture?.(event);
|
|
2988
|
+
if (behind) bringToFront();
|
|
2989
|
+
},
|
|
2990
|
+
onInteractOutside: (event) => {
|
|
2991
|
+
onInteractOutside?.(event);
|
|
2992
|
+
if (windowed && !dismissOnOutsideClick) event.preventDefault();
|
|
2993
|
+
},
|
|
2994
|
+
onEscapeKeyDown: (event) => {
|
|
2995
|
+
onEscapeKeyDown?.(event);
|
|
2996
|
+
const pressedIn = event.target instanceof Node && event.target !== document.body ? event.target : document.activeElement;
|
|
2997
|
+
if (windowed && containerEl && !(pressedIn && containerEl.contains(pressedIn))) {
|
|
2998
|
+
event.preventDefault();
|
|
2999
|
+
}
|
|
3000
|
+
},
|
|
3001
|
+
...hasDescription ? {} : { "aria-describedby": void 0 },
|
|
3002
|
+
...props,
|
|
3003
|
+
children: [
|
|
3004
|
+
hasTitle ? null : /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(VisuallyHidden.Root, { children: /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(DialogPrimitive.Title, { children: "Dialog" }) }),
|
|
3005
|
+
/* @__PURE__ */ (0, import_jsx_runtime31.jsx)(DialogContainerContext.Provider, { value: containerEl, children: /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(DialogWindowHandleContext.Provider, { value: handle, children: /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(
|
|
3006
|
+
PortalContainerProvider,
|
|
3007
|
+
{
|
|
3008
|
+
container: containerEl ?? injectedContainer ?? null,
|
|
3009
|
+
children
|
|
3010
|
+
}
|
|
3011
|
+
) }) }),
|
|
3012
|
+
showCloseButton ? /* @__PURE__ */ (0, import_jsx_runtime31.jsxs)(
|
|
3013
|
+
DialogPrimitive.Close,
|
|
3014
|
+
{
|
|
3015
|
+
"data-slot": "dialog-close",
|
|
3016
|
+
"aria-label": "Close",
|
|
3017
|
+
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 data-[state=open]:bg-accent data-[state=open]:text-muted-foreground sm:right-4 lg:h-10 lg:w-10",
|
|
3018
|
+
children: [
|
|
3019
|
+
/* @__PURE__ */ (0, import_jsx_runtime31.jsx)(XIcon, { className: "h-4 w-4" }),
|
|
3020
|
+
/* @__PURE__ */ (0, import_jsx_runtime31.jsx)("span", { className: "sr-only", children: "Close" })
|
|
3021
|
+
]
|
|
3022
|
+
}
|
|
3023
|
+
) : null
|
|
3024
|
+
]
|
|
3025
|
+
}
|
|
3026
|
+
)
|
|
3027
|
+
] });
|
|
3028
|
+
}
|
|
3029
|
+
);
|
|
3030
|
+
DialogContent.displayName = DialogPrimitive.Content.displayName;
|
|
3031
|
+
var DialogHeader = ({
|
|
3032
|
+
className,
|
|
3033
|
+
...props
|
|
3034
|
+
}) => {
|
|
3035
|
+
const handle = React22.useContext(DialogWindowHandleContext);
|
|
3036
|
+
return /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(
|
|
3037
|
+
"div",
|
|
3038
|
+
{
|
|
3039
|
+
"data-slot": "dialog-header",
|
|
3040
|
+
...handle ? {
|
|
3041
|
+
...handle,
|
|
3042
|
+
"data-window-handle": "",
|
|
3043
|
+
title: "Drag to move \xB7 double-click to recenter"
|
|
3044
|
+
} : {},
|
|
3045
|
+
className: cn2(
|
|
3046
|
+
// DialogContent owns an absolute close control. Reserve its hit area in
|
|
3047
|
+
// every header so trailing actions never render underneath it.
|
|
3048
|
+
"flex flex-col space-y-1.5 pr-12 text-center sm:text-left",
|
|
3049
|
+
handle && "cursor-grab select-none active:cursor-grabbing",
|
|
3050
|
+
className
|
|
3051
|
+
),
|
|
3052
|
+
...props
|
|
3053
|
+
}
|
|
3054
|
+
);
|
|
3055
|
+
};
|
|
3056
|
+
DialogHeader.displayName = "DialogHeader";
|
|
3057
|
+
var DialogFooter = ({
|
|
3058
|
+
className,
|
|
3059
|
+
...props
|
|
3060
|
+
}) => /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(
|
|
3061
|
+
"div",
|
|
3062
|
+
{
|
|
3063
|
+
"data-slot": "dialog-footer",
|
|
3064
|
+
className: cn2(
|
|
3065
|
+
"flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2",
|
|
3066
|
+
// Ruling 3/4 — sticky to the card edges so a scrolled body never shows
|
|
3067
|
+
// through under the actions, and safe-area padded on a phone.
|
|
3068
|
+
//
|
|
3069
|
+
// 🚨 THERE IS NO `mb-[…]` HERE, AND THERE MUST NEVER BE ONE AGAIN
|
|
3070
|
+
// (0.11.3, feedback bf8fb00b). The bleed over the card's bottom padding
|
|
3071
|
+
// used to be a NEGATIVE BLOCK MARGIN, which makes this box taller than
|
|
3072
|
+
// the grid area the card allocates it — and `position: sticky` CLAMPS a
|
|
3073
|
+
// box to its containing block, so the browser yanked the footer UP by
|
|
3074
|
+
// `--dialog-pad` against the card's `gap-4` and painted 8px of opaque
|
|
3075
|
+
// footer over the last line of the consequence, at every length. The
|
|
3076
|
+
// card now RESERVES this footer's block size instead (`matrx-dialog-card`
|
|
3077
|
+
// in styles.css hands over its `padding-bottom`), so the bleed costs no
|
|
3078
|
+
// margin at all. Guard: `sticky-footer-geometry.test.tsx`.
|
|
3079
|
+
//
|
|
3080
|
+
// These stay UTILITIES rather than a shipped rule so a caller's
|
|
3081
|
+
// `className` still wins through tailwind-merge — a shipped rule would
|
|
3082
|
+
// have to be unlayered to survive Tailwind preflight (§ THE PREFLIGHT
|
|
3083
|
+
// RULE in styles.css) and would then override every caller's padding.
|
|
3084
|
+
"sticky bottom-0 z-10 bg-background pt-3",
|
|
3085
|
+
"mx-[calc(var(--dialog-pad,0px)*-1)] px-[var(--dialog-pad,0px)] pb-[var(--dialog-pad,0px)]",
|
|
3086
|
+
className
|
|
3087
|
+
),
|
|
3088
|
+
...props
|
|
3089
|
+
}
|
|
3090
|
+
);
|
|
3091
|
+
DialogFooter.displayName = "DialogFooter";
|
|
3092
|
+
var DialogTitle = React22.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(
|
|
3093
|
+
DialogPrimitive.Title,
|
|
3094
|
+
{
|
|
3095
|
+
ref,
|
|
3096
|
+
"data-slot": "dialog-title",
|
|
3097
|
+
className: cn2("text-lg font-semibold leading-none tracking-tight", className),
|
|
3098
|
+
...props
|
|
3099
|
+
}
|
|
3100
|
+
));
|
|
3101
|
+
DialogTitle.displayName = DialogPrimitive.Title.displayName;
|
|
3102
|
+
var DialogDescription = React22.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(
|
|
3103
|
+
DialogPrimitive.Description,
|
|
3104
|
+
{
|
|
3105
|
+
ref,
|
|
3106
|
+
"data-slot": "dialog-description",
|
|
3107
|
+
className: cn2("text-sm text-muted-foreground", className),
|
|
3108
|
+
...props
|
|
3109
|
+
}
|
|
3110
|
+
));
|
|
3111
|
+
DialogDescription.displayName = DialogPrimitive.Description.displayName;
|
|
3112
|
+
|
|
3113
|
+
// src/confirm-host.tsx
|
|
3114
|
+
var import_confirm_opener = require("@ai-matrx/kit/confirm-opener");
|
|
3115
|
+
var import_opener_react = require("@ai-matrx/kit/opener-react");
|
|
3116
|
+
var React23 = __toESM(require("react"), 1);
|
|
3117
|
+
var import_jsx_runtime32 = require("react/jsx-runtime");
|
|
3118
|
+
function ConfirmDialogHost() {
|
|
3119
|
+
const { shown, open, settle } = (0, import_opener_react.useOpenerHost)(import_confirm_opener.confirmOpener);
|
|
3120
|
+
const [readyFor, setReadyFor] = React23.useState(null);
|
|
3121
|
+
const ready = shown?.ready;
|
|
3122
|
+
React23.useEffect(() => {
|
|
3123
|
+
if (!shown || !ready) return void 0;
|
|
3124
|
+
let live = true;
|
|
3125
|
+
const release = () => {
|
|
3126
|
+
if (live) setReadyFor(shown);
|
|
3127
|
+
};
|
|
3128
|
+
Promise.resolve(ready).then(release, release);
|
|
3129
|
+
return () => {
|
|
3130
|
+
live = false;
|
|
3131
|
+
};
|
|
3132
|
+
}, [shown, ready]);
|
|
3133
|
+
const held = !!ready && readyFor !== shown;
|
|
3134
|
+
const handleConfirm = React23.useCallback(() => settle(true), [settle]);
|
|
3135
|
+
const handleOpenChange = React23.useCallback(
|
|
3136
|
+
(next) => {
|
|
3137
|
+
if (!next) settle(false);
|
|
3138
|
+
},
|
|
3139
|
+
[settle]
|
|
3140
|
+
);
|
|
3141
|
+
return /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(
|
|
3142
|
+
ConfirmDialog,
|
|
3143
|
+
{
|
|
3144
|
+
open,
|
|
3145
|
+
onOpenChange: handleOpenChange,
|
|
3146
|
+
title: shown?.title ?? "",
|
|
3147
|
+
description: shown?.description,
|
|
3148
|
+
confirmLabel: shown?.confirmLabel,
|
|
3149
|
+
cancelLabel: shown?.cancelLabel,
|
|
3150
|
+
variant: shown?.variant,
|
|
3151
|
+
confirmHeld: held,
|
|
3152
|
+
onConfirm: handleConfirm
|
|
3153
|
+
}
|
|
3154
|
+
);
|
|
3155
|
+
}
|
|
3156
|
+
|
|
3157
|
+
// src/popover.tsx
|
|
3158
|
+
var PopoverPrimitive = __toESM(require("@radix-ui/react-popover"), 1);
|
|
3159
|
+
var React24 = __toESM(require("react"), 1);
|
|
3160
|
+
var import_jsx_runtime33 = require("react/jsx-runtime");
|
|
3161
|
+
var Popover = PopoverPrimitive.Root;
|
|
3162
|
+
var PopoverTrigger = PopoverPrimitive.Trigger;
|
|
3163
|
+
var PopoverAnchor = PopoverPrimitive.Anchor;
|
|
3164
|
+
var PopoverContent = React24.forwardRef(
|
|
3165
|
+
({
|
|
3166
|
+
className,
|
|
3167
|
+
align = "center",
|
|
3168
|
+
sideOffset = 4,
|
|
3169
|
+
container,
|
|
3170
|
+
animated = true,
|
|
3171
|
+
sizing = "fixed",
|
|
3172
|
+
collisionPadding = 16,
|
|
3173
|
+
...props
|
|
3174
|
+
}, ref) => {
|
|
3175
|
+
const portalContainer = usePortalContainer(container);
|
|
3176
|
+
return /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(PopoverPrimitive.Portal, { container: portalContainer, children: /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
|
|
3177
|
+
PopoverPrimitive.Content,
|
|
3178
|
+
{
|
|
3179
|
+
ref,
|
|
3180
|
+
align,
|
|
3181
|
+
sideOffset,
|
|
3182
|
+
collisionPadding,
|
|
3183
|
+
className: cn2(
|
|
3184
|
+
// Cap to the viewport space Radix measured and scroll — a popover taller
|
|
3185
|
+
// than the screen must never trap the user with unreachable content.
|
|
3186
|
+
// z must EQUAL the dialog layer (z-[10000]), never exceed it: with equal
|
|
3187
|
+
// z, DOM portal order decides, so a dialog opened FROM a popover stacks
|
|
3188
|
+
// above it, and a popover opened from a dialog still stacks above the
|
|
3189
|
+
// dialog. z-[10001] buried dialogs behind fullscreen popovers.
|
|
3190
|
+
"z-[10000] 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",
|
|
3191
|
+
sizing === "content" ? "w-auto min-w-[min(max(var(--radix-popover-trigger-width),20rem),calc(100vw-2rem))] max-w-[min(28rem,calc(100vw-2rem))]" : "w-72 max-w-[calc(100vw-2rem)]",
|
|
3192
|
+
animated && MOTION_POPPER,
|
|
3193
|
+
className
|
|
3194
|
+
),
|
|
3195
|
+
...props
|
|
3196
|
+
}
|
|
3197
|
+
) });
|
|
3198
|
+
}
|
|
3199
|
+
);
|
|
3200
|
+
PopoverContent.displayName = PopoverPrimitive.Content.displayName;
|
|
3201
|
+
|
|
3202
|
+
// src/collapsible.tsx
|
|
3203
|
+
var CollapsiblePrimitive = __toESM(require("@radix-ui/react-collapsible"), 1);
|
|
3204
|
+
var React25 = __toESM(require("react"), 1);
|
|
3205
|
+
var import_jsx_runtime34 = require("react/jsx-runtime");
|
|
3206
|
+
var Collapsible = CollapsiblePrimitive.Root;
|
|
3207
|
+
var CollapsibleTrigger2 = CollapsiblePrimitive.CollapsibleTrigger;
|
|
3208
|
+
var CollapsibleContent2 = React25.forwardRef(({ className, animated = true, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(
|
|
3209
|
+
CollapsiblePrimitive.CollapsibleContent,
|
|
3210
|
+
{
|
|
3211
|
+
ref,
|
|
3212
|
+
"data-slot": "collapsible-content",
|
|
3213
|
+
className: cn2(
|
|
3214
|
+
animated && "matrx-collapsible-content overflow-hidden",
|
|
3215
|
+
className
|
|
3216
|
+
),
|
|
3217
|
+
...props
|
|
3218
|
+
}
|
|
3219
|
+
));
|
|
3220
|
+
CollapsibleContent2.displayName = "CollapsibleContent";
|
|
3221
|
+
|
|
3222
|
+
// src/skeleton.tsx
|
|
3223
|
+
var import_jsx_runtime35 = require("react/jsx-runtime");
|
|
3224
|
+
function Skeleton({ className, animated = true, ...props }) {
|
|
3225
|
+
return /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(
|
|
3226
|
+
"div",
|
|
3227
|
+
{
|
|
3228
|
+
"data-slot": "skeleton",
|
|
3229
|
+
className: cn2(
|
|
3230
|
+
"rounded-md bg-primary/10",
|
|
3231
|
+
animated && MOTION_PULSE,
|
|
3232
|
+
className
|
|
3233
|
+
),
|
|
3234
|
+
...props
|
|
3235
|
+
}
|
|
3236
|
+
);
|
|
3237
|
+
}
|
|
3238
|
+
|
|
3239
|
+
// src/app-shell/app-rail.tsx
|
|
3240
|
+
var React26 = __toESM(require("react"), 1);
|
|
3241
|
+
|
|
3242
|
+
// src/app-shell/status-dot.tsx
|
|
3243
|
+
var import_jsx_runtime36 = require("react/jsx-runtime");
|
|
3244
|
+
var FILL = {
|
|
3245
|
+
info: "bg-info",
|
|
3246
|
+
warning: "bg-warning",
|
|
3247
|
+
danger: "bg-destructive",
|
|
3248
|
+
success: "bg-success",
|
|
3249
|
+
neutral: "bg-muted-foreground/60"
|
|
3250
|
+
};
|
|
3251
|
+
var RING = {
|
|
3252
|
+
info: "border-info",
|
|
3253
|
+
warning: "border-warning",
|
|
3254
|
+
danger: "border-destructive",
|
|
3255
|
+
success: "border-success",
|
|
3256
|
+
neutral: "border-muted-foreground/60"
|
|
3257
|
+
};
|
|
3258
|
+
function StatusDot({ tone, variant = "solid", label, className, ...props }) {
|
|
3259
|
+
return /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(
|
|
3260
|
+
"span",
|
|
3261
|
+
{
|
|
3262
|
+
"data-slot": "status-dot",
|
|
3263
|
+
"data-tone": tone,
|
|
3264
|
+
"data-variant": variant,
|
|
3265
|
+
...label ? { role: "img", "aria-label": label } : { "aria-hidden": true },
|
|
3266
|
+
className: cn2(
|
|
3267
|
+
"inline-block shrink-0 rounded-full",
|
|
3268
|
+
variant === "solid" ? cn2("size-1.5", FILL[tone]) : cn2("size-2 border-[1.5px] bg-transparent", RING[tone]),
|
|
3269
|
+
className
|
|
3270
|
+
),
|
|
3271
|
+
...props
|
|
3272
|
+
}
|
|
3273
|
+
);
|
|
3274
|
+
}
|
|
3275
|
+
|
|
3276
|
+
// src/app-shell/app-rail.tsx
|
|
3277
|
+
var import_jsx_runtime37 = require("react/jsx-runtime");
|
|
3278
|
+
function AppRail({ className, style, ...props }) {
|
|
3279
|
+
return /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(
|
|
3280
|
+
"nav",
|
|
3281
|
+
{
|
|
3282
|
+
"data-slot": "app-rail",
|
|
3283
|
+
"aria-label": "Areas",
|
|
3284
|
+
className: cn2("flex h-full w-(--app-rail-width) shrink-0 flex-col items-center gap-1 py-2", className),
|
|
3285
|
+
style: { "--app-rail-width": "48px", ...style },
|
|
3286
|
+
...props
|
|
3287
|
+
}
|
|
3288
|
+
);
|
|
3289
|
+
}
|
|
3290
|
+
function AppRailGroup({ className, ...props }) {
|
|
3291
|
+
return /* @__PURE__ */ (0, import_jsx_runtime37.jsx)("div", { "data-slot": "app-rail-group", className: cn2("flex flex-col items-center gap-1", className), ...props });
|
|
3292
|
+
}
|
|
3293
|
+
function AppRailSpacer() {
|
|
3294
|
+
return /* @__PURE__ */ (0, import_jsx_runtime37.jsx)("div", { "data-slot": "app-rail-spacer", className: "flex-1" });
|
|
3295
|
+
}
|
|
3296
|
+
var AppRailItem = React26.forwardRef(function AppRailItem2({ icon, label, shortcut, active = false, attention, attentionLabel, className, ...props }, ref) {
|
|
3297
|
+
return /* @__PURE__ */ (0, import_jsx_runtime37.jsxs)(Tooltip, { children: [
|
|
3298
|
+
/* @__PURE__ */ (0, import_jsx_runtime37.jsx)(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime37.jsxs)(
|
|
3299
|
+
"button",
|
|
3300
|
+
{
|
|
3301
|
+
ref,
|
|
3302
|
+
type: "button",
|
|
3303
|
+
"data-slot": "app-rail-item",
|
|
3304
|
+
"data-active": active,
|
|
3305
|
+
"aria-label": label,
|
|
3306
|
+
"aria-current": active ? "page" : void 0,
|
|
3307
|
+
className: cn2(
|
|
3308
|
+
"relative flex size-8 items-center justify-center rounded-lg text-foreground/55 outline-none transition-colors",
|
|
3309
|
+
"hover:bg-foreground/[0.06] hover:text-foreground",
|
|
3310
|
+
"data-[active=true]:bg-foreground/[0.08] data-[active=true]:text-foreground",
|
|
3311
|
+
"focus-visible:ring-2 focus-visible:ring-ring",
|
|
3312
|
+
"[&_svg]:size-[18px] [&_svg]:shrink-0",
|
|
3313
|
+
className
|
|
3314
|
+
),
|
|
3315
|
+
...props,
|
|
3316
|
+
children: [
|
|
3317
|
+
icon,
|
|
3318
|
+
attention ? /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(
|
|
3319
|
+
StatusDot,
|
|
3320
|
+
{
|
|
3321
|
+
tone: attention,
|
|
3322
|
+
variant: "ring",
|
|
3323
|
+
...attentionLabel ? { label: attentionLabel } : {},
|
|
3324
|
+
className: "absolute right-0.5 bottom-0.5"
|
|
3325
|
+
}
|
|
3326
|
+
) : null
|
|
3327
|
+
]
|
|
3328
|
+
}
|
|
3329
|
+
) }),
|
|
3330
|
+
/* @__PURE__ */ (0, import_jsx_runtime37.jsx)(TooltipContent, { side: "right", align: "center", children: /* @__PURE__ */ (0, import_jsx_runtime37.jsxs)("span", { className: "flex items-center gap-2", children: [
|
|
3331
|
+
label,
|
|
3332
|
+
shortcut ? /* @__PURE__ */ (0, import_jsx_runtime37.jsx)("span", { className: "opacity-60", children: shortcut }) : null
|
|
3333
|
+
] }) })
|
|
3334
|
+
] });
|
|
3335
|
+
});
|
|
3336
|
+
var AppRailAvatar = React26.forwardRef(function AppRailAvatar2({ initials = "", label, className, children, ...props }, ref) {
|
|
3337
|
+
return /* @__PURE__ */ (0, import_jsx_runtime37.jsxs)(Tooltip, { children: [
|
|
3338
|
+
/* @__PURE__ */ (0, import_jsx_runtime37.jsx)(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(
|
|
3339
|
+
"button",
|
|
3340
|
+
{
|
|
3341
|
+
ref,
|
|
3342
|
+
type: "button",
|
|
3343
|
+
"data-slot": "app-rail-avatar",
|
|
3344
|
+
"aria-label": label,
|
|
3345
|
+
className: cn2(
|
|
3346
|
+
"flex size-6 items-center justify-center rounded-full bg-foreground/[0.12] text-[10px] font-semibold leading-none text-foreground/80 outline-none [&_svg]:size-3.5",
|
|
3347
|
+
"focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background",
|
|
3348
|
+
className
|
|
3349
|
+
),
|
|
3350
|
+
...props,
|
|
3351
|
+
children: children ?? initials.slice(0, 2).toUpperCase()
|
|
3352
|
+
}
|
|
3353
|
+
) }),
|
|
3354
|
+
/* @__PURE__ */ (0, import_jsx_runtime37.jsx)(TooltipContent, { side: "right", align: "center", children: label })
|
|
3355
|
+
] });
|
|
3356
|
+
});
|
|
3357
|
+
|
|
3358
|
+
// src/app-shell/canvas.tsx
|
|
3359
|
+
var React27 = __toESM(require("react"), 1);
|
|
3360
|
+
|
|
3361
|
+
// src/resizable.tsx
|
|
3362
|
+
var import_react_resizable_panels = require("react-resizable-panels");
|
|
3363
|
+
var import_jsx_runtime38 = require("react/jsx-runtime");
|
|
3364
|
+
var HANDLE_SIZES = {
|
|
3365
|
+
xs: "w-0.5 [&[aria-orientation=horizontal]]:h-0.5 [&[aria-orientation=horizontal]]:w-full",
|
|
3366
|
+
sm: "w-1 [&[aria-orientation=horizontal]]:h-1 [&[aria-orientation=horizontal]]:w-full",
|
|
3367
|
+
md: "w-1.5 [&[aria-orientation=horizontal]]:h-1.5 [&[aria-orientation=horizontal]]:w-full",
|
|
3368
|
+
lg: "w-2 [&[aria-orientation=horizontal]]:h-2 [&[aria-orientation=horizontal]]:w-full",
|
|
3369
|
+
xl: "w-3 [&[aria-orientation=horizontal]]:h-3 [&[aria-orientation=horizontal]]:w-full",
|
|
3370
|
+
"2xl": "w-4 [&[aria-orientation=horizontal]]:h-4 [&[aria-orientation=horizontal]]:w-full",
|
|
3371
|
+
"3xl": "w-6 [&[aria-orientation=horizontal]]:h-6 [&[aria-orientation=horizontal]]:w-full",
|
|
3372
|
+
"4xl": "w-8 [&[aria-orientation=horizontal]]:h-8 [&[aria-orientation=horizontal]]:w-full"
|
|
3373
|
+
};
|
|
3374
|
+
var ResizablePanelGroup = ({
|
|
3375
|
+
className,
|
|
3376
|
+
...props
|
|
3377
|
+
}) => /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(
|
|
3378
|
+
import_react_resizable_panels.Group,
|
|
3379
|
+
{
|
|
3380
|
+
"data-slot": "resizable-group",
|
|
3381
|
+
className: cn2("flex h-full w-full", className),
|
|
3382
|
+
...props
|
|
3383
|
+
}
|
|
3384
|
+
);
|
|
3385
|
+
ResizablePanelGroup.displayName = "ResizablePanelGroup";
|
|
3386
|
+
var ResizablePanel = import_react_resizable_panels.Panel;
|
|
3387
|
+
var ResizableHandle = ({
|
|
3388
|
+
withHandle,
|
|
3389
|
+
size = "xs",
|
|
3390
|
+
className,
|
|
3391
|
+
...props
|
|
3392
|
+
}) => /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(
|
|
3393
|
+
import_react_resizable_panels.Separator,
|
|
3394
|
+
{
|
|
3395
|
+
"data-slot": "resizable-handle",
|
|
3396
|
+
className: cn2(
|
|
3397
|
+
"group/handle relative flex shrink-0 items-center justify-center bg-border transition-colors",
|
|
3398
|
+
// Invisible hit target centered on the line; wider still on touch.
|
|
3399
|
+
"after:absolute after:inset-y-0 after:left-1/2 after:w-1.5 after:-translate-x-1/2 after:content-['']",
|
|
3400
|
+
"pointer-coarse:after:w-11",
|
|
3401
|
+
"[&[aria-orientation=horizontal]]:after:inset-x-0 [&[aria-orientation=horizontal]]:after:left-0 [&[aria-orientation=horizontal]]:after:top-1/2 [&[aria-orientation=horizontal]]:after:h-1.5 [&[aria-orientation=horizontal]]:after:w-full [&[aria-orientation=horizontal]]:after:-translate-x-0 [&[aria-orientation=horizontal]]:after:-translate-y-1/2",
|
|
3402
|
+
"pointer-coarse:[&[aria-orientation=horizontal]]:after:h-11",
|
|
3403
|
+
// The drag-outline bug: suppress for ALL focus, restore for keyboard.
|
|
3404
|
+
"focus:outline-none focus-visible:ring-1 focus-visible:ring-ring focus-visible:ring-offset-1",
|
|
3405
|
+
// Library-driven state feedback.
|
|
3406
|
+
"data-[separator=hover]:bg-primary data-[separator=active]:bg-primary",
|
|
3407
|
+
HANDLE_SIZES[size],
|
|
3408
|
+
"cursor-col-resize [&[aria-orientation=horizontal]]:cursor-row-resize",
|
|
3409
|
+
className
|
|
3410
|
+
),
|
|
3411
|
+
...props,
|
|
3412
|
+
children: withHandle ? /* @__PURE__ */ (0, import_jsx_runtime38.jsx)("div", { className: "z-10 flex h-8 w-4 items-center justify-center rounded-sm border bg-border group-aria-[orientation=horizontal]/handle:h-4 group-aria-[orientation=horizontal]/handle:w-8", children: /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(DragHandleDots2Icon, { className: "h-2.5 w-2.5 group-aria-[orientation=horizontal]/handle:rotate-90" }) }) : null
|
|
3413
|
+
}
|
|
3414
|
+
);
|
|
3415
|
+
ResizableHandle.displayName = "ResizableHandle";
|
|
3416
|
+
|
|
3417
|
+
// src/app-shell/canvas.tsx
|
|
3418
|
+
var import_jsx_runtime39 = require("react/jsx-runtime");
|
|
3419
|
+
var GAP = "bg-transparent data-[separator=hover]:bg-foreground/15 data-[separator=active]:bg-foreground/25";
|
|
3420
|
+
function CanvasLayout({ columns, expandedId = null, className }) {
|
|
3421
|
+
const visible = columns.filter((c) => c.panels.length > 0);
|
|
3422
|
+
const expanded = expandedId ? visible.flatMap((c) => c.panels).find((p) => p.id === expandedId) : void 0;
|
|
3423
|
+
if (expanded) {
|
|
3424
|
+
return /* @__PURE__ */ (0, import_jsx_runtime39.jsx)("div", { "data-slot": "canvas-layout", "data-expanded": expanded.id, className: cn2("flex h-full w-full", className), children: expanded.node });
|
|
3425
|
+
}
|
|
3426
|
+
return /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(ResizablePanelGroup, { "data-slot": "canvas-layout", orientation: "horizontal", className: cn2("gap-0", className), children: visible.map((column, ci) => /* @__PURE__ */ (0, import_jsx_runtime39.jsxs)(React27.Fragment, { children: [
|
|
3427
|
+
ci > 0 ? /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(ResizableHandle, { "data-canvas-gap": "column", size: "md", className: GAP }) : null,
|
|
3428
|
+
/* @__PURE__ */ (0, import_jsx_runtime39.jsx)(
|
|
3429
|
+
ResizablePanel,
|
|
3430
|
+
{
|
|
3431
|
+
id: column.id,
|
|
3432
|
+
minSize: column.minSize ?? 220,
|
|
3433
|
+
...column.defaultSize !== void 0 ? { defaultSize: column.defaultSize } : {},
|
|
3434
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(ResizablePanelGroup, { "data-canvas-column": column.id, orientation: "vertical", children: column.panels.map((panel, pi) => /* @__PURE__ */ (0, import_jsx_runtime39.jsxs)(React27.Fragment, { children: [
|
|
3435
|
+
pi > 0 ? /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(ResizableHandle, { "data-canvas-gap": "row", size: "md", className: GAP }) : null,
|
|
3436
|
+
/* @__PURE__ */ (0, import_jsx_runtime39.jsx)(
|
|
3437
|
+
ResizablePanel,
|
|
3438
|
+
{
|
|
3439
|
+
id: panel.id,
|
|
3440
|
+
minSize: panel.minSize ?? 120,
|
|
3441
|
+
...panel.defaultSize !== void 0 ? { defaultSize: panel.defaultSize } : {},
|
|
3442
|
+
children: panel.node
|
|
3443
|
+
}
|
|
3444
|
+
)
|
|
3445
|
+
] }, panel.id)) })
|
|
3446
|
+
}
|
|
3447
|
+
)
|
|
3448
|
+
] }, column.id)) });
|
|
3449
|
+
}
|
|
3450
|
+
|
|
3451
|
+
// src/app-shell/mode-switch.tsx
|
|
3452
|
+
var React28 = __toESM(require("react"), 1);
|
|
3453
|
+
var import_jsx_runtime40 = require("react/jsx-runtime");
|
|
3454
|
+
function ModeSwitch({ value, onValueChange, options, label = "Mode", className }) {
|
|
3455
|
+
const refs = React28.useRef([]);
|
|
3456
|
+
const move = (from, delta) => {
|
|
3457
|
+
const next = (from + delta + options.length) % options.length;
|
|
3458
|
+
const option = options[next];
|
|
3459
|
+
if (!option) return;
|
|
3460
|
+
onValueChange(option.value);
|
|
3461
|
+
refs.current[next]?.focus();
|
|
3462
|
+
};
|
|
3463
|
+
return /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(
|
|
3464
|
+
"div",
|
|
3465
|
+
{
|
|
3466
|
+
role: "radiogroup",
|
|
3467
|
+
"aria-label": label,
|
|
3468
|
+
"data-slot": "mode-switch",
|
|
3469
|
+
className: cn2("inline-flex items-center gap-0.5 rounded-lg bg-foreground/[0.06] p-0.5", className),
|
|
3470
|
+
children: options.map((option, index) => {
|
|
3471
|
+
const checked = option.value === value;
|
|
3472
|
+
return /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)(Tooltip, { children: [
|
|
3473
|
+
/* @__PURE__ */ (0, import_jsx_runtime40.jsx)(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(
|
|
3474
|
+
"button",
|
|
3475
|
+
{
|
|
3476
|
+
ref: (el) => {
|
|
3477
|
+
refs.current[index] = el;
|
|
3478
|
+
},
|
|
3479
|
+
type: "button",
|
|
3480
|
+
role: "radio",
|
|
3481
|
+
"aria-checked": checked,
|
|
3482
|
+
"aria-label": option.label,
|
|
3483
|
+
"data-slot": "mode-switch-item",
|
|
3484
|
+
"data-value": option.value,
|
|
3485
|
+
tabIndex: checked ? 0 : -1,
|
|
3486
|
+
onClick: () => onValueChange(option.value),
|
|
3487
|
+
onKeyDown: (event) => {
|
|
3488
|
+
if (event.key === "ArrowRight" || event.key === "ArrowDown") {
|
|
3489
|
+
event.preventDefault();
|
|
3490
|
+
move(index, 1);
|
|
3491
|
+
} else if (event.key === "ArrowLeft" || event.key === "ArrowUp") {
|
|
3492
|
+
event.preventDefault();
|
|
3493
|
+
move(index, -1);
|
|
3494
|
+
}
|
|
3495
|
+
},
|
|
3496
|
+
className: cn2(
|
|
3497
|
+
"flex h-6 w-7 items-center justify-center rounded-md text-foreground/55 outline-none transition-colors",
|
|
3498
|
+
"hover:text-foreground focus-visible:ring-2 focus-visible:ring-ring",
|
|
3499
|
+
"aria-checked:bg-background aria-checked:text-foreground aria-checked:shadow-[0_0_0_0.5px_hsl(var(--foreground)/0.12),0_1px_2px_hsl(var(--foreground)/0.08)]",
|
|
3500
|
+
"dark:aria-checked:bg-foreground/[0.14] dark:aria-checked:shadow-none",
|
|
3501
|
+
"[&_svg]:size-[15px] [&_svg]:shrink-0"
|
|
3502
|
+
),
|
|
3503
|
+
children: option.icon
|
|
3504
|
+
}
|
|
3505
|
+
) }),
|
|
3506
|
+
/* @__PURE__ */ (0, import_jsx_runtime40.jsx)(TooltipContent, { side: "bottom", align: "center", children: /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)("span", { className: "flex items-center gap-2", children: [
|
|
3507
|
+
option.label,
|
|
3508
|
+
option.shortcut ? /* @__PURE__ */ (0, import_jsx_runtime40.jsx)("span", { className: "opacity-60", children: option.shortcut }) : null
|
|
3509
|
+
] }) })
|
|
3510
|
+
] }, option.value);
|
|
3511
|
+
})
|
|
3512
|
+
}
|
|
3513
|
+
);
|
|
3514
|
+
}
|
|
3515
|
+
|
|
3516
|
+
// src/organization-picker.tsx
|
|
3517
|
+
var React29 = __toESM(require("react"), 1);
|
|
3518
|
+
var import_jsx_runtime41 = require("react/jsx-runtime");
|
|
3519
|
+
var ORGANIZATION_HUES = [217, 262, 330, 12, 32, 152, 189, 290];
|
|
3520
|
+
function organizationColor(seed) {
|
|
3521
|
+
let hash = 0;
|
|
3522
|
+
for (let index = 0; index < seed.length; index += 1) {
|
|
3523
|
+
hash = hash * 31 + seed.charCodeAt(index) >>> 0;
|
|
3524
|
+
}
|
|
3525
|
+
const hue = ORGANIZATION_HUES[hash % ORGANIZATION_HUES.length];
|
|
3526
|
+
return { background: `hsl(${hue} 62% 46%)`, color: "#fff" };
|
|
3527
|
+
}
|
|
3528
|
+
function OrganizationMark({
|
|
3529
|
+
id,
|
|
3530
|
+
name,
|
|
3531
|
+
abbreviation,
|
|
3532
|
+
logoUrl,
|
|
3533
|
+
size = 20,
|
|
3534
|
+
className
|
|
3535
|
+
}) {
|
|
3536
|
+
const box = { width: size, height: size };
|
|
3537
|
+
if (logoUrl) {
|
|
3538
|
+
return /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(
|
|
3539
|
+
"span",
|
|
3540
|
+
{
|
|
3541
|
+
style: box,
|
|
3542
|
+
className: cn2("relative inline-flex shrink-0 overflow-hidden rounded-md bg-muted", className),
|
|
3543
|
+
"data-slot": "organization-mark",
|
|
3544
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("img", { src: logoUrl, alt: name, className: "h-full w-full object-cover" })
|
|
3545
|
+
}
|
|
3546
|
+
);
|
|
3547
|
+
}
|
|
3548
|
+
const letters = (abbreviation && abbreviation.trim() || name.trim().slice(0, 1) || "?").slice(0, 3).toUpperCase();
|
|
3549
|
+
const fontSize = Math.round(size * (letters.length >= 3 ? 0.36 : 0.44));
|
|
3550
|
+
return /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(
|
|
3551
|
+
"span",
|
|
3552
|
+
{
|
|
3553
|
+
style: { ...box, ...organizationColor(id || name), fontSize },
|
|
3554
|
+
className: cn2(
|
|
3555
|
+
"inline-flex shrink-0 items-center justify-center rounded-md font-bold leading-none tracking-tight",
|
|
3556
|
+
className
|
|
3557
|
+
),
|
|
3558
|
+
"aria-label": `Organization abbreviation ${letters}`,
|
|
3559
|
+
title: letters,
|
|
3560
|
+
"data-slot": "organization-mark",
|
|
3561
|
+
children: letters
|
|
3562
|
+
}
|
|
3563
|
+
);
|
|
3564
|
+
}
|
|
3565
|
+
//# sourceMappingURL=desktop.cjs.map
|