@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/CHANGELOG.md +52 -0
- package/README.md +4 -2
- package/dist/index.cjs +1299 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +415 -0
- package/dist/index.d.ts +386 -1
- package/dist/index.js +1172 -15
- package/dist/index.js.map +1 -1
- package/package.json +19 -5
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
|
-
|
|
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 =
|
|
261
|
+
var Button = React3.forwardRef(
|
|
67
262
|
({ className, variant, size, asChild = false, ...props }, ref) => {
|
|
68
263
|
const Component = asChild ? Slot : "button";
|
|
69
|
-
return /* @__PURE__ */
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
109
|
-
|
|
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
|