@ai-matrx/design-system 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs ADDED
@@ -0,0 +1,1299 @@
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/index.ts
32
+ var src_exports = {};
33
+ __export(src_exports, {
34
+ Badge: () => Badge,
35
+ BasicInput: () => BasicInput,
36
+ BottomSheet: () => BottomSheet,
37
+ BottomSheetBody: () => BottomSheetBody,
38
+ BottomSheetFooter: () => BottomSheetFooter,
39
+ BottomSheetHeader: () => BottomSheetHeader,
40
+ Button: () => Button,
41
+ DEFAULT_SCORE_THRESHOLDS: () => DEFAULT_SCORE_THRESHOLDS,
42
+ EditableLabel: () => EditableLabel,
43
+ EnterInput: () => EnterInput,
44
+ Input: () => Input,
45
+ InputWithPrefix: () => InputWithPrefix,
46
+ Label: () => Label,
47
+ OverflowToolbar: () => OverflowToolbar,
48
+ Popover: () => Popover,
49
+ PopoverAnchor: () => PopoverAnchor,
50
+ PopoverContent: () => PopoverContent,
51
+ PopoverTrigger: () => PopoverTrigger,
52
+ PortalContainerProvider: () => PortalContainerProvider,
53
+ RadixDialogModalProvider: () => RadixDialogModalProvider,
54
+ ScoreRing: () => ScoreRing,
55
+ SegmentedControl: () => SegmentedControl,
56
+ Separator: () => Separator,
57
+ Sheet: () => Sheet,
58
+ SheetClose: () => SheetClose,
59
+ SheetContent: () => SheetContent,
60
+ SheetDescription: () => SheetDescription,
61
+ SheetFooter: () => SheetFooter,
62
+ SheetHeader: () => SheetHeader,
63
+ SheetOverlay: () => SheetOverlay,
64
+ SheetPortal: () => SheetPortal,
65
+ SheetTitle: () => SheetTitle,
66
+ SheetTrigger: () => SheetTrigger,
67
+ Skeleton: () => Skeleton,
68
+ TabbedBottomSheet: () => TabbedBottomSheet,
69
+ badgeVariants: () => badgeVariants,
70
+ buttonVariants: () => buttonVariants,
71
+ cn: () => cn,
72
+ scoreAccentBgClasses: () => scoreAccentBgClasses,
73
+ scoreRingColorClasses: () => scoreRingColorClasses,
74
+ sheetVariants: () => sheetVariants,
75
+ usePortalContainer: () => usePortalContainer,
76
+ useRadixDialogModal: () => useRadixDialogModal,
77
+ useScrollFade: () => useScrollFade
78
+ });
79
+ module.exports = __toCommonJS(src_exports);
80
+
81
+ // src/badge.tsx
82
+ var import_class_variance_authority = require("class-variance-authority");
83
+
84
+ // src/cn.ts
85
+ var import_clsx = require("clsx");
86
+ var import_tailwind_merge = require("tailwind-merge");
87
+ function cn(...inputs) {
88
+ return (0, import_tailwind_merge.twMerge)((0, import_clsx.clsx)(inputs));
89
+ }
90
+
91
+ // src/badge.tsx
92
+ var import_jsx_runtime = require("react/jsx-runtime");
93
+ var badgeVariants = (0, import_class_variance_authority.cva)(
94
+ "inline-flex items-center rounded-md border px-2 py-0.5 text-[11px] font-medium transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-0",
95
+ {
96
+ variants: {
97
+ variant: {
98
+ default: "border-transparent bg-primary/15 text-primary",
99
+ secondary: "border-transparent bg-secondary text-secondary-foreground",
100
+ destructive: "border-transparent bg-destructive/15 text-destructive",
101
+ outline: "border-border text-foreground",
102
+ success: "border-transparent bg-success/15 text-success",
103
+ warning: "border-transparent bg-warning/15 text-warning",
104
+ info: "border-blue-200 bg-blue-100 text-blue-800",
105
+ error: "border-transparent bg-red-500/15 text-red-700 dark:text-red-400",
106
+ neutral: "border-border bg-muted text-muted-foreground"
107
+ }
108
+ },
109
+ defaultVariants: {
110
+ variant: "default"
111
+ }
112
+ }
113
+ );
114
+ function Badge({ className, variant, ...props }) {
115
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: cn(badgeVariants({ variant }), className), ...props });
116
+ }
117
+
118
+ // src/bottom-sheet.tsx
119
+ var import_react_visually_hidden = require("@radix-ui/react-visually-hidden");
120
+ var React2 = __toESM(require("react"), 1);
121
+ var import_vaul = require("vaul");
122
+
123
+ // src/icons.tsx
124
+ var import_jsx_runtime2 = require("react/jsx-runtime");
125
+ function lucideProps(props) {
126
+ return {
127
+ xmlns: "http://www.w3.org/2000/svg",
128
+ viewBox: "0 0 24 24",
129
+ fill: "none",
130
+ stroke: "currentColor",
131
+ strokeWidth: 2,
132
+ strokeLinecap: "round",
133
+ strokeLinejoin: "round",
134
+ "aria-hidden": true,
135
+ ...props
136
+ };
137
+ }
138
+ function Loader2Icon(props) {
139
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("svg", { ...lucideProps(props), children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M21 12a9 9 0 1 1-6.219-8.56" }) });
140
+ }
141
+ function ChevronLeftIcon(props) {
142
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("svg", { ...lucideProps(props), children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "m15 18-6-6 6-6" }) });
143
+ }
144
+ function ChevronRightIcon(props) {
145
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("svg", { ...lucideProps(props), children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "m9 18 6-6-6-6" }) });
146
+ }
147
+ function MoreHorizontalIcon(props) {
148
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("svg", { ...lucideProps(props), children: [
149
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("circle", { cx: "12", cy: "12", r: "1" }),
150
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("circle", { cx: "19", cy: "12", r: "1" }),
151
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("circle", { cx: "5", cy: "12", r: "1" })
152
+ ] });
153
+ }
154
+ function Cross2Icon(props) {
155
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
156
+ "svg",
157
+ {
158
+ xmlns: "http://www.w3.org/2000/svg",
159
+ viewBox: "0 0 15 15",
160
+ fill: "none",
161
+ "aria-hidden": true,
162
+ ...props,
163
+ children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
164
+ "path",
165
+ {
166
+ d: "M11.7816 4.03157C12.0062 3.80702 12.0062 3.44295 11.7816 3.2184C11.5571 2.99385 11.193 2.99385 10.9685 3.2184L7.50005 6.68682L4.03164 3.2184C3.80708 2.99385 3.44301 2.99385 3.21846 3.2184C2.99391 3.44295 2.99391 3.80702 3.21846 4.03157L6.68688 7.49999L3.21846 10.9684C2.99391 11.193 2.99391 11.557 3.21846 11.7816C3.44301 12.0061 3.80708 12.0061 4.03164 11.7816L7.50005 8.31316L10.9685 11.7816C11.193 12.0061 11.5571 12.0061 11.7816 11.7816C12.0062 11.557 12.0062 11.193 11.7816 10.9684L8.31322 7.49999L11.7816 4.03157Z",
167
+ fill: "currentColor",
168
+ fillRule: "evenodd",
169
+ clipRule: "evenodd"
170
+ }
171
+ )
172
+ }
173
+ );
174
+ }
175
+
176
+ // src/radix-dialog-modal-context.tsx
177
+ var React = __toESM(require("react"), 1);
178
+ var import_jsx_runtime3 = require("react/jsx-runtime");
179
+ var RadixDialogModalContext = React.createContext(true);
180
+ function RadixDialogModalProvider({
181
+ children,
182
+ modal
183
+ }) {
184
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(RadixDialogModalContext.Provider, { value: modal, children });
185
+ }
186
+ function useRadixDialogModal() {
187
+ return React.useContext(RadixDialogModalContext);
188
+ }
189
+
190
+ // src/bottom-sheet.tsx
191
+ var import_jsx_runtime4 = require("react/jsx-runtime");
192
+ var DrawerRoot = ({
193
+ children,
194
+ shouldScaleBackground = true,
195
+ ...props
196
+ }) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(RadixDialogModalProvider, { modal: true, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
197
+ import_vaul.Drawer.Root,
198
+ {
199
+ modal: true,
200
+ shouldScaleBackground,
201
+ ...props,
202
+ children
203
+ }
204
+ ) });
205
+ DrawerRoot.displayName = "DrawerRoot";
206
+ var DrawerContentPrimitive = React2.forwardRef(({ ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_vaul.Drawer.Content, { ...props, ref, "aria-modal": "true" }));
207
+ DrawerContentPrimitive.displayName = "DrawerContentPrimitive";
208
+ function BottomSheet({
209
+ open,
210
+ onOpenChange,
211
+ title = "Bottom Sheet",
212
+ size = "adaptive",
213
+ surface = "glass",
214
+ contentClassName,
215
+ children
216
+ }) {
217
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(DrawerRoot, { open, onOpenChange, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_vaul.Drawer.Portal, { children: [
218
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
219
+ import_vaul.Drawer.Overlay,
220
+ {
221
+ className: cn("fixed inset-0 z-50 bg-black/80", "fixed inset-0 z-50"),
222
+ style: { background: "rgba(0, 0, 0, 0.08)" }
223
+ }
224
+ ),
225
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
226
+ DrawerContentPrimitive,
227
+ {
228
+ className: cn(
229
+ "fixed inset-x-0 bottom-0 z-50 mt-24 flex flex-col rounded-t-2xl overflow-hidden",
230
+ size === "full" ? "h-[92dvh]" : "min-h-[60dvh] max-h-[90dvh]",
231
+ surface === "solid" && "border border-b-0 border-border bg-background shadow-2xl",
232
+ contentClassName
233
+ ),
234
+ style: surface === "glass" ? {
235
+ background: "var(--matrx-glass-bg)",
236
+ backdropFilter: "blur(20px) saturate(180%)",
237
+ WebkitBackdropFilter: "blur(20px) saturate(180%)",
238
+ border: "1px solid var(--matrx-glass-border-color)",
239
+ borderBottom: "none"
240
+ } : void 0,
241
+ children: [
242
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_react_visually_hidden.VisuallyHidden, { children: [
243
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_vaul.Drawer.Title, { children: title }),
244
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_vaul.Drawer.Description, { children: "Bottom sheet panel." })
245
+ ] }),
246
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "mx-auto mt-3 mb-1 h-1.5 w-10 rounded-full bg-muted-foreground/30 flex-shrink-0" }),
247
+ children
248
+ ]
249
+ }
250
+ )
251
+ ] }) });
252
+ }
253
+ function BottomSheetHeader({
254
+ title,
255
+ showBack = false,
256
+ onBack,
257
+ trailing
258
+ }) {
259
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "flex items-center px-2 pt-1 pb-2 flex-shrink-0 min-h-[44px]", children: [
260
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "min-w-[44px] flex items-center justify-start", children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
261
+ "button",
262
+ {
263
+ onClick: onBack,
264
+ className: cn(
265
+ "h-8 w-8 rounded-full matrx-glass-thin-border flex items-center justify-center transition-all active:scale-95",
266
+ showBack ? "opacity-100" : "opacity-0 pointer-events-none"
267
+ ),
268
+ "aria-hidden": !showBack,
269
+ tabIndex: showBack ? 0 : -1,
270
+ children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(ChevronLeftIcon, { className: "h-4 w-4 text-foreground" })
271
+ }
272
+ ) }),
273
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "text-[17px] font-semibold flex-1 text-center truncate", children: title }),
274
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "min-w-[44px] flex items-center justify-end", children: trailing })
275
+ ] });
276
+ }
277
+ function BottomSheetBody({ children, className }) {
278
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
279
+ "div",
280
+ {
281
+ className: cn(
282
+ // min-h-0 is load-bearing: without it a flex child's min-height:auto
283
+ // floors it at content height and the body grows instead of scrolling.
284
+ "min-h-0 flex-1 overflow-y-auto overscroll-contain pb-safe",
285
+ className
286
+ ),
287
+ children
288
+ }
289
+ );
290
+ }
291
+ function BottomSheetFooter({
292
+ children,
293
+ className
294
+ }) {
295
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
296
+ "div",
297
+ {
298
+ className: cn(
299
+ "flex-shrink-0 px-4 py-3 pb-safe border-t border-white/[0.06]",
300
+ className
301
+ ),
302
+ children
303
+ }
304
+ );
305
+ }
306
+
307
+ // src/button.tsx
308
+ var import_react_slot = require("@radix-ui/react-slot");
309
+ var import_class_variance_authority2 = require("class-variance-authority");
310
+ var React3 = __toESM(require("react"), 1);
311
+ var import_jsx_runtime5 = require("react/jsx-runtime");
312
+ var buttonVariants = (0, import_class_variance_authority2.cva)(
313
+ "inline-flex cursor-pointer items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-all duration-200 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-0 disabled:pointer-events-none disabled:opacity-50 active:scale-[0.98] [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0",
314
+ {
315
+ variants: {
316
+ variant: {
317
+ default: "bg-primary text-primary-foreground shadow-sm hover:bg-primary/90",
318
+ destructive: "bg-destructive text-destructive-foreground shadow-sm hover:bg-destructive/90",
319
+ outline: "border border-border bg-card shadow-sm hover:bg-accent hover:text-accent-foreground",
320
+ secondary: "bg-secondary text-secondary-foreground shadow-sm hover:bg-secondary/80",
321
+ ghost: "hover:bg-accent hover:text-accent-foreground",
322
+ link: "text-primary underline-offset-4 hover:underline",
323
+ subtle: "border border-transparent bg-muted/50 text-foreground hover:border-border hover:bg-muted"
324
+ },
325
+ size: {
326
+ default: "h-9 px-4 py-2",
327
+ sm: "h-8 rounded-md px-3 text-xs",
328
+ lg: "h-10 rounded-md px-6",
329
+ icon: "h-9 w-9",
330
+ "icon-sm": "h-7 w-7"
331
+ }
332
+ },
333
+ defaultVariants: {
334
+ variant: "default",
335
+ size: "default"
336
+ }
337
+ }
338
+ );
339
+ var Button = React3.forwardRef(
340
+ ({ className, variant, size, asChild = false, ...props }, ref) => {
341
+ const Component = asChild ? import_react_slot.Slot : "button";
342
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
343
+ Component,
344
+ {
345
+ className: cn(buttonVariants({ variant, size, className })),
346
+ ref,
347
+ ...props
348
+ }
349
+ );
350
+ }
351
+ );
352
+ Button.displayName = "Button";
353
+
354
+ // src/editable-label.tsx
355
+ var import_react = require("react");
356
+ var import_jsx_runtime6 = require("react/jsx-runtime");
357
+ function EditableLabel({
358
+ value,
359
+ onCommit,
360
+ commitMode = "optimistic",
361
+ validate,
362
+ emptyFallback,
363
+ maxLength = 120,
364
+ activation = "click",
365
+ editing: editingProp,
366
+ onEditingChange,
367
+ selectOnEdit = true,
368
+ placeholder,
369
+ ariaLabel = "Name",
370
+ truncate = true,
371
+ className,
372
+ displayClassName,
373
+ inputClassName
374
+ }) {
375
+ const controlled = activation === "controlled";
376
+ const [internalEditing, setInternalEditing] = (0, import_react.useState)(false);
377
+ const editing = controlled ? !!editingProp : internalEditing;
378
+ const [draft, setDraft] = (0, import_react.useState)(value);
379
+ const [error, setError] = (0, import_react.useState)(null);
380
+ const [busy, setBusy] = (0, import_react.useState)(false);
381
+ const inputRef = (0, import_react.useRef)(null);
382
+ (0, import_react.useEffect)(() => {
383
+ if (!editing) setDraft(value);
384
+ }, [value, editing]);
385
+ (0, import_react.useEffect)(() => {
386
+ if (editing && inputRef.current) {
387
+ inputRef.current.focus();
388
+ if (selectOnEdit) inputRef.current.select();
389
+ }
390
+ }, [editing, selectOnEdit]);
391
+ const setEditing = (0, import_react.useCallback)(
392
+ (next) => {
393
+ if (controlled) {
394
+ onEditingChange?.(next);
395
+ } else {
396
+ setInternalEditing(next);
397
+ onEditingChange?.(next);
398
+ }
399
+ },
400
+ [controlled, onEditingChange]
401
+ );
402
+ const startEdit = (0, import_react.useCallback)(() => {
403
+ setDraft(value);
404
+ setError(null);
405
+ setEditing(true);
406
+ }, [value, setEditing]);
407
+ const cancel = (0, import_react.useCallback)(() => {
408
+ setDraft(value);
409
+ setError(null);
410
+ setBusy(false);
411
+ setEditing(false);
412
+ }, [value, setEditing]);
413
+ const commit = (0, import_react.useCallback)(() => {
414
+ if (busy) return;
415
+ const trimmed = draft.trim();
416
+ const next = trimmed || emptyFallback;
417
+ if (next === void 0) {
418
+ cancel();
419
+ return;
420
+ }
421
+ const validationError = validate?.(next) ?? null;
422
+ if (validationError) {
423
+ setError(validationError);
424
+ return;
425
+ }
426
+ if (next === value) {
427
+ setEditing(false);
428
+ setError(null);
429
+ return;
430
+ }
431
+ if (commitMode === "await") {
432
+ const result2 = onCommit(next);
433
+ if (result2 instanceof Promise) {
434
+ setBusy(true);
435
+ result2.then(() => {
436
+ setBusy(false);
437
+ setEditing(false);
438
+ }).catch(() => {
439
+ setBusy(false);
440
+ });
441
+ return;
442
+ }
443
+ setEditing(false);
444
+ return;
445
+ }
446
+ setEditing(false);
447
+ setError(null);
448
+ const result = onCommit(next);
449
+ if (result instanceof Promise) result.catch(() => {
450
+ });
451
+ }, [
452
+ busy,
453
+ draft,
454
+ emptyFallback,
455
+ validate,
456
+ value,
457
+ commitMode,
458
+ onCommit,
459
+ cancel,
460
+ setEditing
461
+ ]);
462
+ if (editing) {
463
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("span", { className: cn("relative flex min-w-0 flex-1 items-center", className), children: [
464
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
465
+ "input",
466
+ {
467
+ ref: inputRef,
468
+ type: "text",
469
+ value: draft,
470
+ disabled: busy,
471
+ placeholder,
472
+ onChange: (e) => {
473
+ setDraft(e.target.value);
474
+ if (error) setError(null);
475
+ },
476
+ onBlur: commit,
477
+ onClick: (e) => e.stopPropagation(),
478
+ onDoubleClick: (e) => e.stopPropagation(),
479
+ onKeyDown: (e) => {
480
+ if (e.key === "Enter") {
481
+ e.preventDefault();
482
+ e.stopPropagation();
483
+ commit();
484
+ } else if (e.key === "Escape") {
485
+ e.preventDefault();
486
+ e.stopPropagation();
487
+ cancel();
488
+ } else if (e.key === " ") {
489
+ e.stopPropagation();
490
+ }
491
+ },
492
+ maxLength,
493
+ "aria-label": ariaLabel,
494
+ "aria-invalid": error ? true : void 0,
495
+ className: cn(
496
+ "w-full min-w-0 rounded-sm bg-transparent px-1 outline-none",
497
+ "text-base md:text-sm",
498
+ "focus:bg-background focus:ring-1 focus:ring-ring",
499
+ busy && "opacity-60",
500
+ inputClassName
501
+ )
502
+ }
503
+ ),
504
+ busy && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Loader2Icon, { className: "absolute right-1 h-3.5 w-3.5 animate-spin text-muted-foreground" }),
505
+ error && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "absolute left-0 top-full mt-0.5 whitespace-nowrap text-xs text-destructive", children: error })
506
+ ] });
507
+ }
508
+ if (controlled) return null;
509
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
510
+ "button",
511
+ {
512
+ type: "button",
513
+ onClick: activation === "click" ? (e) => {
514
+ e.stopPropagation();
515
+ startEdit();
516
+ } : void 0,
517
+ onDoubleClick: activation === "doubleClick" ? (e) => {
518
+ e.stopPropagation();
519
+ startEdit();
520
+ } : void 0,
521
+ onKeyDown: (e) => {
522
+ if (e.key === "Enter" || e.key === " ") {
523
+ e.preventDefault();
524
+ e.stopPropagation();
525
+ startEdit();
526
+ }
527
+ },
528
+ title: activation === "doubleClick" ? "Double-click to rename" : "Click to rename",
529
+ "aria-label": `Rename ${ariaLabel.toLowerCase()}: ${value}`,
530
+ className: cn(
531
+ "min-w-0 max-w-full rounded-sm px-1 text-left transition-colors",
532
+ "hover:bg-accent/40 focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring",
533
+ truncate && "block truncate",
534
+ className,
535
+ displayClassName
536
+ ),
537
+ children: value || placeholder || ""
538
+ }
539
+ );
540
+ }
541
+
542
+ // src/input.tsx
543
+ var React4 = __toESM(require("react"), 1);
544
+ var import_jsx_runtime7 = require("react/jsx-runtime");
545
+ var getVariantStyles = (variant = "default") => {
546
+ const baseStyles = `flex h-10 w-full border border-border bg-background text-black dark:text-white shadow-input rounded-md px-3 py-2 text-sm file:border-0 file:bg-transparent
547
+ file:text-sm file:font-medium placeholder:text-neutral-400 dark:placeholder-text-neutral-600
548
+ focus-visible:outline-none focus-visible:ring-[2px] focus-visible:ring-neutral-400 dark:focus-visible:ring-neutral-600
549
+ disabled:cursor-not-allowed disabled:opacity-50
550
+ dark:shadow-[0px_0px_1px_1px_var(--neutral-700)]
551
+ transition duration-400
552
+ [&:-webkit-autofill]:bg-background [&:-webkit-autofill]:shadow-[0_0_0_1000px_hsl(var(--background))_inset] [&:-webkit-autofill]:[caret-color:currentColor]
553
+ dark:[&:-webkit-autofill]:shadow-[0_0_0_1000px_hsl(var(--background))_inset] dark:[&:-webkit-autofill]:[-webkit-text-fill-color:white]
554
+ [&:-webkit-autofill:hover]:shadow-[0_0_0_1000px_hsl(var(--background))_inset] [&:-webkit-autofill:focus]:shadow-[0_0_0_1000px_hsl(var(--background))_inset]`;
555
+ switch (variant) {
556
+ case "destructive":
557
+ return `${baseStyles} bg-destructive text-destructive-foreground`;
558
+ case "outline":
559
+ return `${baseStyles} border-2`;
560
+ case "secondary":
561
+ return `${baseStyles} bg-secondary text-secondary-foreground`;
562
+ case "ghost":
563
+ return `${baseStyles} bg-transparent shadow-none`;
564
+ case "link":
565
+ return `${baseStyles} bg-transparent underline-offset-4 hover:underline`;
566
+ case "primary":
567
+ return `${baseStyles} bg-primary text-primary-foreground`;
568
+ default:
569
+ return baseStyles;
570
+ }
571
+ };
572
+ var Input = React4.forwardRef(
573
+ ({ className, type, variant = "default", ...props }, ref) => {
574
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
575
+ "input",
576
+ {
577
+ type,
578
+ className: cn(getVariantStyles(variant), className),
579
+ ref,
580
+ ...props
581
+ }
582
+ );
583
+ }
584
+ );
585
+ Input.displayName = "Input";
586
+ var EnterInput = React4.forwardRef(
587
+ ({ onEnter, ...props }, ref) => {
588
+ const handleKeyDown = (e) => {
589
+ if (e.key === "Enter" && onEnter) {
590
+ e.preventDefault();
591
+ onEnter();
592
+ }
593
+ };
594
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
595
+ Input,
596
+ {
597
+ ...props,
598
+ ref,
599
+ onKeyDown: (e) => {
600
+ handleKeyDown(e);
601
+ if (props.onKeyDown) {
602
+ props.onKeyDown(e);
603
+ }
604
+ }
605
+ }
606
+ );
607
+ }
608
+ );
609
+ EnterInput.displayName = "EnterInput";
610
+ var BasicInput = React4.forwardRef(
611
+ // `variant` is accepted (prop-compatible with Input) but intentionally
612
+ // unused — and destructured so it never leaks onto the DOM element.
613
+ ({ className, type, variant: _variant = "default", ...props }, ref) => {
614
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
615
+ "input",
616
+ {
617
+ type,
618
+ className: cn(
619
+ "flex h-9 w-full rounded-md border border-input bg-transparent px-3 py-1 text-sm shadow-sm transition-colors file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50",
620
+ className
621
+ ),
622
+ ref,
623
+ ...props
624
+ }
625
+ );
626
+ }
627
+ );
628
+ BasicInput.displayName = "BasicInput";
629
+ var InputWithPrefix = React4.forwardRef(({ prefix, className, wrapperClassName, ...props }, ref) => {
630
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: cn("relative", wrapperClassName), children: [
631
+ prefix && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "absolute left-3 top-1/2 -translate-y-1/2 text-muted-foreground", children: prefix }),
632
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
633
+ Input,
634
+ {
635
+ ref,
636
+ className: cn(prefix && "pl-10", className),
637
+ ...props
638
+ }
639
+ )
640
+ ] });
641
+ });
642
+ InputWithPrefix.displayName = "InputWithPrefix";
643
+
644
+ // src/label.tsx
645
+ var LabelPrimitive = __toESM(require("@radix-ui/react-label"), 1);
646
+ var React5 = __toESM(require("react"), 1);
647
+ var import_jsx_runtime8 = require("react/jsx-runtime");
648
+ var Label = React5.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
649
+ LabelPrimitive.Root,
650
+ {
651
+ ref,
652
+ className: cn(
653
+ "text-sm font-medium leading-none text-foreground peer-disabled:cursor-not-allowed peer-disabled:opacity-70",
654
+ className
655
+ ),
656
+ ...props
657
+ }
658
+ ));
659
+ Label.displayName = LabelPrimitive.Root.displayName;
660
+
661
+ // src/overflow-toolbar.tsx
662
+ var import_react2 = require("react");
663
+ var import_jsx_runtime9 = require("react/jsx-runtime");
664
+ var GAP_PX = 6;
665
+ var TONE = {
666
+ default: "border border-border bg-background hover:bg-accent text-foreground",
667
+ primary: "bg-primary text-primary-foreground hover:bg-primary/90",
668
+ destructive: "border border-destructive/40 text-destructive hover:bg-destructive/10"
669
+ };
670
+ function ToolbarButton({
671
+ action,
672
+ enableTooltip = false,
673
+ renderTooltip
674
+ }) {
675
+ const tone = action.tone ?? "default";
676
+ const Icon = action.running ? Loader2Icon : action.icon;
677
+ const showLabel = !action.hideLabel || action.running && action.runningLabel;
678
+ const text = action.running && action.runningLabel ? action.runningLabel : action.label;
679
+ const className = cn(
680
+ "inline-flex items-center gap-1 h-7 rounded-md text-[11px] font-medium transition-colors whitespace-nowrap",
681
+ "disabled:opacity-50 disabled:pointer-events-none",
682
+ action.hideLabel && !action.running ? "w-7 justify-center px-0" : "px-2",
683
+ TONE[tone]
684
+ );
685
+ const inner = /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(import_jsx_runtime9.Fragment, { children: [
686
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
687
+ Icon,
688
+ {
689
+ className: cn("w-3.5 h-3.5 shrink-0", action.running && "animate-spin")
690
+ }
691
+ ),
692
+ showLabel && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { children: text })
693
+ ] });
694
+ const control = action.href && !action.disabled ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
695
+ "a",
696
+ {
697
+ href: action.href,
698
+ target: action.target,
699
+ rel: action.target === "_blank" ? "noopener noreferrer" : void 0,
700
+ className,
701
+ "aria-label": action.label,
702
+ children: inner
703
+ }
704
+ ) : /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
705
+ "button",
706
+ {
707
+ type: "button",
708
+ onClick: action.onSelect,
709
+ disabled: action.disabled,
710
+ className,
711
+ "aria-label": action.label,
712
+ children: inner
713
+ }
714
+ );
715
+ if (enableTooltip && action.hideLabel && renderTooltip) {
716
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_jsx_runtime9.Fragment, { children: renderTooltip(control, action.label) });
717
+ }
718
+ return control;
719
+ }
720
+ function OverflowTrigger({ ariaLabel }) {
721
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
722
+ "button",
723
+ {
724
+ type: "button",
725
+ "aria-label": ariaLabel,
726
+ "aria-haspopup": "menu",
727
+ title: "More actions",
728
+ className: cn(
729
+ "inline-flex items-center justify-center h-7 w-7 rounded-md transition-colors",
730
+ "border border-border bg-background hover:bg-accent text-foreground"
731
+ ),
732
+ children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(MoreHorizontalIcon, { className: "w-3.5 h-3.5" })
733
+ }
734
+ );
735
+ }
736
+ function OverflowToolbar({
737
+ actions,
738
+ leading,
739
+ overflowAriaLabel = "More actions",
740
+ className,
741
+ renderOverflowMenu,
742
+ renderTooltip
743
+ }) {
744
+ const visibleActions = (0, import_react2.useMemo)(
745
+ () => actions.filter((a) => !a.hidden),
746
+ [actions]
747
+ );
748
+ const containerRef = (0, import_react2.useRef)(null);
749
+ const ghostRef = (0, import_react2.useRef)(null);
750
+ const leadingRef = (0, import_react2.useRef)(null);
751
+ const [visibleCount, setVisibleCount] = (0, import_react2.useState)(visibleActions.length);
752
+ const signature = visibleActions.map(
753
+ (a) => `${a.id}:${a.hideLabel ? 1 : 0}:${a.running ? 1 : 0}:${a.runningLabel ?? ""}:${a.label}`
754
+ ).join("|");
755
+ (0, import_react2.useLayoutEffect)(() => {
756
+ const container = containerRef.current;
757
+ const ghost = ghostRef.current;
758
+ if (!container || !ghost) return void 0;
759
+ const compute = () => {
760
+ const available = container.clientWidth;
761
+ const n = visibleActions.length;
762
+ const children = Array.from(ghost.children);
763
+ const hasLeading = leading != null;
764
+ const leadingW = hasLeading ? leadingRef.current?.offsetWidth ?? 0 : 0;
765
+ const itemW = (i) => children[i]?.offsetWidth ?? 0;
766
+ const kebabW = children[n]?.offsetWidth ?? 28;
767
+ const base = leadingW + (hasLeading && n > 0 ? GAP_PX : 0);
768
+ let totalAll = base;
769
+ for (let i = 0; i < n; i++) totalAll += itemW(i) + (i > 0 ? GAP_PX : 0);
770
+ if (totalAll <= available) {
771
+ setVisibleCount(n);
772
+ return;
773
+ }
774
+ let used = base;
775
+ let count = 0;
776
+ for (let i = 0; i < n; i++) {
777
+ const add = itemW(i) + (count > 0 ? GAP_PX : 0);
778
+ if (used + add + GAP_PX + kebabW <= available) {
779
+ used += add;
780
+ count += 1;
781
+ } else break;
782
+ }
783
+ setVisibleCount(count);
784
+ };
785
+ compute();
786
+ const ro = new ResizeObserver(compute);
787
+ ro.observe(container);
788
+ return () => ro.disconnect();
789
+ }, [signature, leading != null]);
790
+ const shown = visibleActions.slice(0, visibleCount);
791
+ const hidden = visibleActions.slice(visibleCount);
792
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { ref: containerRef, className: cn("relative min-w-0", className), children: [
793
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(
794
+ "div",
795
+ {
796
+ ref: ghostRef,
797
+ "aria-hidden": true,
798
+ className: "pointer-events-none absolute left-0 top-0 flex items-center gap-1.5 opacity-0",
799
+ children: [
800
+ visibleActions.map((a) => /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(ToolbarButton, { action: a }, a.id)),
801
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(OverflowTrigger, { ariaLabel: overflowAriaLabel })
802
+ ]
803
+ }
804
+ ),
805
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "flex items-center justify-end gap-1.5", children: [
806
+ leading != null && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { ref: leadingRef, className: "shrink-0", children: leading }),
807
+ shown.map((a) => /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
808
+ ToolbarButton,
809
+ {
810
+ action: a,
811
+ enableTooltip: true,
812
+ renderTooltip
813
+ },
814
+ a.id
815
+ )),
816
+ hidden.length > 0 && renderOverflowMenu({
817
+ actions: hidden,
818
+ trigger: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(OverflowTrigger, { ariaLabel: overflowAriaLabel })
819
+ })
820
+ ] })
821
+ ] });
822
+ }
823
+
824
+ // src/popover.tsx
825
+ var PopoverPrimitive = __toESM(require("@radix-ui/react-popover"), 1);
826
+ var React8 = __toESM(require("react"), 1);
827
+
828
+ // src/portal-container.tsx
829
+ var React7 = __toESM(require("react"), 1);
830
+ var import_jsx_runtime10 = require("react/jsx-runtime");
831
+ var PortalContainerContext = React7.createContext(
832
+ void 0
833
+ );
834
+ function PortalContainerProvider({
835
+ container,
836
+ children
837
+ }) {
838
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(PortalContainerContext.Provider, { value: container ?? void 0, children });
839
+ }
840
+ function usePortalContainer(explicit) {
841
+ const injected = React7.useContext(PortalContainerContext);
842
+ if (explicit !== void 0) {
843
+ return explicit ?? void 0;
844
+ }
845
+ return injected ?? void 0;
846
+ }
847
+
848
+ // src/popover.tsx
849
+ var import_jsx_runtime11 = require("react/jsx-runtime");
850
+ var Popover = PopoverPrimitive.Root;
851
+ var PopoverTrigger = PopoverPrimitive.Trigger;
852
+ var PopoverAnchor = PopoverPrimitive.Anchor;
853
+ var PopoverContent = React8.forwardRef(
854
+ ({ className, align = "center", sideOffset = 4, container, ...props }, ref) => {
855
+ const portalContainer = usePortalContainer(container);
856
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(PopoverPrimitive.Portal, { container: portalContainer, children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
857
+ PopoverPrimitive.Content,
858
+ {
859
+ ref,
860
+ align,
861
+ sideOffset,
862
+ className: cn(
863
+ // Cap to the viewport space Radix measured and scroll — a popover taller
864
+ // than the screen must never trap the user with unreachable content.
865
+ // z must EQUAL the dialog layer (z-[10000]), never exceed it: with equal
866
+ // z, DOM portal order decides, so a dialog opened FROM a popover stacks
867
+ // above it, and a popover opened from a dialog still stacks above the
868
+ // dialog. z-[10001] buried dialogs behind fullscreen popovers.
869
+ "z-[10000] w-72 max-h-[var(--radix-popover-content-available-height)] overflow-y-auto overscroll-contain rounded-md border bg-popover p-4 text-popover-foreground shadow-md outline-none data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
870
+ className
871
+ ),
872
+ ...props
873
+ }
874
+ ) });
875
+ }
876
+ );
877
+ PopoverContent.displayName = PopoverPrimitive.Content.displayName;
878
+
879
+ // src/score-ring.tsx
880
+ var import_jsx_runtime12 = require("react/jsx-runtime");
881
+ var DEFAULT_SCORE_THRESHOLDS = {
882
+ good: 75,
883
+ warning: 50
884
+ };
885
+ function scoreRingColorClasses(pct, thresholds = DEFAULT_SCORE_THRESHOLDS) {
886
+ if (pct === null) return "text-muted-foreground";
887
+ if (pct >= thresholds.good) return "text-green-500";
888
+ if (pct >= thresholds.warning) return "text-orange-500";
889
+ return "text-red-500";
890
+ }
891
+ function scoreAccentBgClasses(pct, thresholds = DEFAULT_SCORE_THRESHOLDS) {
892
+ if (pct === null) return "bg-muted-foreground/30";
893
+ if (pct >= thresholds.good) return "bg-green-500";
894
+ if (pct >= thresholds.warning) return "bg-orange-500";
895
+ return "bg-red-500";
896
+ }
897
+ function ScoreRing({
898
+ pct,
899
+ size = 112,
900
+ strokeWidth = 8,
901
+ label,
902
+ valueClassName,
903
+ className,
904
+ thresholds = DEFAULT_SCORE_THRESHOLDS,
905
+ suffix = "%"
906
+ }) {
907
+ const radius = 50 - strokeWidth / 2;
908
+ const circumference = 2 * Math.PI * radius;
909
+ const boundedPct = pct === null ? 0 : Math.max(0, Math.min(100, pct));
910
+ const dashOffset = circumference * (1 - boundedPct / 100);
911
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(
912
+ "div",
913
+ {
914
+ className: cn(
915
+ "relative flex shrink-0 items-center justify-center",
916
+ className
917
+ ),
918
+ style: { width: size, height: size },
919
+ role: "img",
920
+ "aria-label": `${label ?? "Score"}: ${pct === null ? "not available" : `${pct} out of 100`}`,
921
+ children: [
922
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("svg", { viewBox: "0 0 100 100", className: "h-full w-full -rotate-90", children: [
923
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
924
+ "circle",
925
+ {
926
+ cx: "50",
927
+ cy: "50",
928
+ r: radius,
929
+ className: "stroke-muted",
930
+ strokeWidth,
931
+ fill: "none"
932
+ }
933
+ ),
934
+ pct !== null ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
935
+ "circle",
936
+ {
937
+ cx: "50",
938
+ cy: "50",
939
+ r: radius,
940
+ className: cn(
941
+ "transition-[stroke-dashoffset] duration-700 ease-out",
942
+ scoreRingColorClasses(pct, thresholds)
943
+ ),
944
+ stroke: "currentColor",
945
+ strokeWidth,
946
+ strokeLinecap: "round",
947
+ fill: "none",
948
+ strokeDasharray: circumference,
949
+ strokeDashoffset: dashOffset
950
+ }
951
+ ) : null
952
+ ] }),
953
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "absolute flex flex-col items-center justify-center", children: [
954
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
955
+ "span",
956
+ {
957
+ className: cn(
958
+ "font-bold tabular-nums text-foreground",
959
+ valueClassName ?? "text-2xl"
960
+ ),
961
+ children: pct === null ? "\u2014" : `${pct}${suffix}`
962
+ }
963
+ ),
964
+ label ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: "max-w-[74px] truncate text-[9px] font-semibold uppercase tracking-wide text-muted-foreground", children: label }) : null
965
+ ] })
966
+ ]
967
+ }
968
+ );
969
+ }
970
+
971
+ // src/segmented-control.tsx
972
+ var import_jsx_runtime13 = require("react/jsx-runtime");
973
+ function SegmentedControl({
974
+ value,
975
+ onValueChange,
976
+ data,
977
+ name: _name,
978
+ className,
979
+ fullWidth = false,
980
+ size = "md"
981
+ }) {
982
+ const sizeClasses = {
983
+ sm: "h-7 text-xs",
984
+ md: "h-9 text-sm",
985
+ lg: "h-10 text-base"
986
+ };
987
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
988
+ "div",
989
+ {
990
+ className: cn(
991
+ "inline-flex p-0.5 bg-muted rounded-md",
992
+ fullWidth && "w-full",
993
+ className
994
+ ),
995
+ role: "tablist",
996
+ children: data.map((option) => {
997
+ const isActive = value === option.value;
998
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
999
+ "button",
1000
+ {
1001
+ type: "button",
1002
+ role: "tab",
1003
+ "aria-selected": isActive,
1004
+ disabled: option.disabled,
1005
+ onClick: () => onValueChange(option.value),
1006
+ className: cn(
1007
+ "relative flex items-center justify-center rounded-[0.2rem] px-3 py-1.5 transition-all",
1008
+ sizeClasses[size],
1009
+ fullWidth && "flex-1",
1010
+ isActive ? "bg-background text-foreground shadow-sm" : "text-muted-foreground hover:text-foreground hover:bg-background/50",
1011
+ option.disabled && "opacity-50 cursor-not-allowed"
1012
+ ),
1013
+ children: option.label
1014
+ },
1015
+ option.value
1016
+ );
1017
+ })
1018
+ }
1019
+ );
1020
+ }
1021
+
1022
+ // src/separator.tsx
1023
+ var SeparatorPrimitive = __toESM(require("@radix-ui/react-separator"), 1);
1024
+ var React9 = __toESM(require("react"), 1);
1025
+ var import_jsx_runtime14 = require("react/jsx-runtime");
1026
+ var Separator = React9.forwardRef(({ className, orientation = "horizontal", decorative = true, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
1027
+ SeparatorPrimitive.Root,
1028
+ {
1029
+ ref,
1030
+ decorative,
1031
+ orientation,
1032
+ className: cn(
1033
+ "shrink-0 bg-border",
1034
+ orientation === "horizontal" ? "h-px w-full" : "h-full w-px",
1035
+ className
1036
+ ),
1037
+ ...props
1038
+ }
1039
+ ));
1040
+ Separator.displayName = SeparatorPrimitive.Root.displayName;
1041
+
1042
+ // src/sheet.tsx
1043
+ var SheetPrimitive = __toESM(require("@radix-ui/react-dialog"), 1);
1044
+ var import_react_tree = require("@ai-matrx/kit/react-tree");
1045
+ var import_class_variance_authority3 = require("class-variance-authority");
1046
+ var React10 = __toESM(require("react"), 1);
1047
+ var import_jsx_runtime15 = require("react/jsx-runtime");
1048
+ var SheetContentBase = React10.forwardRef(({ ...props }, ref) => {
1049
+ const isModal = useRadixDialogModal();
1050
+ return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
1051
+ SheetPrimitive.Content,
1052
+ {
1053
+ ...props,
1054
+ ref,
1055
+ "aria-modal": isModal || void 0
1056
+ }
1057
+ );
1058
+ });
1059
+ SheetContentBase.displayName = "SheetContentBase";
1060
+ var Sheet = React10.forwardRef(({ children, ...props }, _ref) => {
1061
+ const modal = props.modal ?? true;
1062
+ return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(RadixDialogModalProvider, { modal, children: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(SheetPrimitive.Root, { ...props, modal, children }) });
1063
+ });
1064
+ Sheet.displayName = "Sheet";
1065
+ var SheetTrigger = SheetPrimitive.Trigger;
1066
+ var SheetClose = SheetPrimitive.Close;
1067
+ var SheetPortal = SheetPrimitive.Portal;
1068
+ var SheetOverlay = React10.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
1069
+ SheetPrimitive.Overlay,
1070
+ {
1071
+ className: cn(
1072
+ "fixed inset-0 z-50 bg-black/80 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0",
1073
+ className
1074
+ ),
1075
+ ...props,
1076
+ ref
1077
+ }
1078
+ ));
1079
+ SheetOverlay.displayName = SheetPrimitive.Overlay.displayName;
1080
+ var sheetVariants = (0, import_class_variance_authority3.cva)(
1081
+ "fixed z-50 gap-4 bg-background p-6 shadow-lg transition ease-in-out data-[state=closed]:duration-300 data-[state=open]:duration-500 data-[state=open]:animate-in data-[state=closed]:animate-out",
1082
+ {
1083
+ variants: {
1084
+ side: {
1085
+ top: "inset-x-0 top-0 border-b data-[state=closed]:slide-out-to-top data-[state=open]:slide-in-from-top",
1086
+ bottom: "inset-x-0 bottom-0 border-t data-[state=closed]:slide-out-to-bottom data-[state=open]:slide-in-from-bottom",
1087
+ left: "inset-y-0 left-0 h-full w-3/4 border-r data-[state=closed]:slide-out-to-left data-[state=open]:slide-in-from-left sm:max-w-sm",
1088
+ right: "inset-y-0 right-0 h-full w-3/4 border-l data-[state=closed]:slide-out-to-right data-[state=open]:slide-in-from-right sm:max-w-sm",
1089
+ center: "inset-0 m-auto max-h-full max-w-full border rounded-lg data-[state=closed]:fade-out data-[state=open]:fade-in"
1090
+ }
1091
+ },
1092
+ defaultVariants: {
1093
+ side: "right"
1094
+ }
1095
+ }
1096
+ );
1097
+ var SheetDescription = React10.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
1098
+ SheetPrimitive.Description,
1099
+ {
1100
+ ref,
1101
+ className: cn("text-sm text-muted-foreground", className),
1102
+ ...props
1103
+ }
1104
+ ));
1105
+ SheetDescription.displayName = SheetPrimitive.Description.displayName;
1106
+ var SheetContent = React10.forwardRef(
1107
+ ({
1108
+ side = "right",
1109
+ className,
1110
+ children,
1111
+ hideCloseButton = false,
1112
+ hideOverlay = false,
1113
+ overlayClassName,
1114
+ ...props
1115
+ }, ref) => {
1116
+ const hasDescription = (0, import_react_tree.treeContainsComponent)(children, SheetDescription) || (0, import_react_tree.treeContainsComponent)(children, SheetPrimitive.Description);
1117
+ return /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(SheetPortal, { children: [
1118
+ !hideOverlay && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(SheetOverlay, { className: overlayClassName }),
1119
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(
1120
+ SheetContentBase,
1121
+ {
1122
+ ref,
1123
+ className: cn(sheetVariants({ side }), className),
1124
+ ...hasDescription ? {} : { "aria-describedby": void 0 },
1125
+ ...props,
1126
+ children: [
1127
+ !hideCloseButton && /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(SheetPrimitive.Close, { className: "absolute right-4 top-4 rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-secondary", children: [
1128
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(Cross2Icon, { className: "h-4 w-4" }),
1129
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: "sr-only", children: "Close" })
1130
+ ] }),
1131
+ children
1132
+ ]
1133
+ }
1134
+ )
1135
+ ] });
1136
+ }
1137
+ );
1138
+ SheetContent.displayName = SheetPrimitive.Content.displayName;
1139
+ var SheetHeader = ({
1140
+ className,
1141
+ ...props
1142
+ }) => /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
1143
+ "div",
1144
+ {
1145
+ className: cn(
1146
+ "flex flex-col space-y-2 text-center sm:text-left",
1147
+ className
1148
+ ),
1149
+ ...props
1150
+ }
1151
+ );
1152
+ SheetHeader.displayName = "SheetHeader";
1153
+ var SheetFooter = ({
1154
+ className,
1155
+ ...props
1156
+ }) => /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
1157
+ "div",
1158
+ {
1159
+ className: cn(
1160
+ "flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2",
1161
+ className
1162
+ ),
1163
+ ...props
1164
+ }
1165
+ );
1166
+ SheetFooter.displayName = "SheetFooter";
1167
+ var SheetTitle = React10.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
1168
+ SheetPrimitive.Title,
1169
+ {
1170
+ ref,
1171
+ className: cn("text-lg font-semibold text-foreground", className),
1172
+ ...props
1173
+ }
1174
+ ));
1175
+ SheetTitle.displayName = SheetPrimitive.Title.displayName;
1176
+
1177
+ // src/skeleton.tsx
1178
+ var import_jsx_runtime16 = require("react/jsx-runtime");
1179
+ function Skeleton({
1180
+ className,
1181
+ ...props
1182
+ }) {
1183
+ return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
1184
+ "div",
1185
+ {
1186
+ className: cn("animate-pulse rounded-md bg-primary/10", className),
1187
+ ...props
1188
+ }
1189
+ );
1190
+ }
1191
+
1192
+ // src/tabbed-bottom-sheet.tsx
1193
+ var import_react3 = require("react");
1194
+ var import_jsx_runtime17 = require("react/jsx-runtime");
1195
+ function TabbedBottomSheet({
1196
+ open,
1197
+ onOpenChange,
1198
+ title,
1199
+ tabs
1200
+ }) {
1201
+ const [selectedTabId, setSelectedTabId] = (0, import_react3.useState)(null);
1202
+ const [wasOpen, setWasOpen] = (0, import_react3.useState)(open);
1203
+ if (open !== wasOpen) {
1204
+ setWasOpen(open);
1205
+ if (open) setSelectedTabId(null);
1206
+ }
1207
+ const selectedTab = selectedTabId ? tabs.find((tab) => tab.id === selectedTabId) : null;
1208
+ return /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
1209
+ BottomSheet,
1210
+ {
1211
+ open,
1212
+ onOpenChange,
1213
+ title,
1214
+ size: "full",
1215
+ children: /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { className: "matrx-mobile-sheet flex min-h-0 flex-1 flex-col", children: [
1216
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
1217
+ BottomSheetHeader,
1218
+ {
1219
+ title: selectedTab ? selectedTab.label : title,
1220
+ showBack: !!selectedTab,
1221
+ onBack: () => setSelectedTabId(null)
1222
+ }
1223
+ ),
1224
+ selectedTab ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("div", { className: "flex min-h-0 flex-1 flex-col overflow-hidden pb-safe", children: selectedTab.content }) : /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(BottomSheetBody, { children: /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("ul", { className: "divide-y divide-border", children: tabs.map((tab) => {
1225
+ const Icon = tab.icon;
1226
+ return /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("li", { children: /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(
1227
+ "button",
1228
+ {
1229
+ type: "button",
1230
+ onClick: () => setSelectedTabId(tab.id),
1231
+ className: "flex w-full items-center gap-3 px-4 py-3.5 text-left transition-colors hover:bg-muted/60 active:bg-muted",
1232
+ children: [
1233
+ Icon ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(Icon, { className: "h-5 w-5 shrink-0 text-muted-foreground" }) : null,
1234
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("span", { className: "min-w-0 flex-1 text-base text-foreground", children: tab.label }),
1235
+ tab.trailing,
1236
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(ChevronRightIcon, { className: "h-5 w-5 shrink-0 text-muted-foreground" })
1237
+ ]
1238
+ }
1239
+ ) }, tab.id);
1240
+ }) }) })
1241
+ ] })
1242
+ }
1243
+ );
1244
+ }
1245
+
1246
+ // src/use-scroll-fade.ts
1247
+ var import_react4 = require("react");
1248
+ var EPSILON = 2;
1249
+ function useScrollFade() {
1250
+ const [state, setState] = (0, import_react4.useState)({
1251
+ top: false,
1252
+ bottom: false
1253
+ });
1254
+ const nodeRef = (0, import_react4.useRef)(null);
1255
+ const cleanupRef = (0, import_react4.useRef)(null);
1256
+ const measure = (0, import_react4.useCallback)(() => {
1257
+ const el = nodeRef.current;
1258
+ if (!el) return;
1259
+ const overflowing = el.scrollHeight - el.clientHeight > EPSILON;
1260
+ const next = {
1261
+ top: overflowing && el.scrollTop > EPSILON,
1262
+ bottom: overflowing && el.scrollTop + el.clientHeight < el.scrollHeight - EPSILON
1263
+ };
1264
+ setState(
1265
+ (prev) => prev.top === next.top && prev.bottom === next.bottom ? prev : next
1266
+ );
1267
+ }, []);
1268
+ const ref = (0, import_react4.useCallback)(
1269
+ (node) => {
1270
+ cleanupRef.current?.();
1271
+ cleanupRef.current = null;
1272
+ nodeRef.current = node;
1273
+ if (!node) return;
1274
+ node.addEventListener("scroll", measure, { passive: true });
1275
+ const ro = new ResizeObserver(measure);
1276
+ ro.observe(node);
1277
+ const mo = new MutationObserver(measure);
1278
+ mo.observe(node, { childList: true, subtree: true });
1279
+ cleanupRef.current = () => {
1280
+ node.removeEventListener("scroll", measure);
1281
+ ro.disconnect();
1282
+ mo.disconnect();
1283
+ };
1284
+ requestAnimationFrame(measure);
1285
+ },
1286
+ [measure]
1287
+ );
1288
+ (0, import_react4.useEffect)(() => () => cleanupRef.current?.(), []);
1289
+ return {
1290
+ ref,
1291
+ state,
1292
+ fadeProps: {
1293
+ "data-fade-top": state.top ? "" : void 0,
1294
+ "data-fade-bottom": state.bottom ? "" : void 0,
1295
+ className: "matrx-scroll-fade"
1296
+ }
1297
+ };
1298
+ }
1299
+ //# sourceMappingURL=index.cjs.map