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