@petrarca/sonnet-ui 0.1.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/LICENSE.md +190 -0
- package/dist/index.d.ts +989 -0
- package/dist/index.js +5016 -0
- package/dist/index.js.map +1 -0
- package/dist/json-editor/index.d.ts +51 -0
- package/dist/json-editor/index.js +131 -0
- package/dist/json-editor/index.js.map +1 -0
- package/package.json +70 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,5016 @@
|
|
|
1
|
+
// src/alert.tsx
|
|
2
|
+
import * as React from "react";
|
|
3
|
+
import { cva } from "class-variance-authority";
|
|
4
|
+
import { cn } from "@petrarca/sonnet-core";
|
|
5
|
+
import { jsx } from "react/jsx-runtime";
|
|
6
|
+
var alertVariants = cva(
|
|
7
|
+
"relative w-full rounded-lg border p-4 [&>svg~*]:pl-7 [&>svg+div]:translate-y-[-3px] [&>svg]:absolute [&>svg]:left-4 [&>svg]:top-4 [&>svg]:text-foreground",
|
|
8
|
+
{
|
|
9
|
+
variants: {
|
|
10
|
+
variant: {
|
|
11
|
+
default: "bg-background text-foreground",
|
|
12
|
+
destructive: "border-destructive/50 text-destructive dark:border-destructive [&>svg]:text-destructive"
|
|
13
|
+
}
|
|
14
|
+
},
|
|
15
|
+
defaultVariants: {
|
|
16
|
+
variant: "default"
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
);
|
|
20
|
+
var Alert = React.forwardRef(({ className, variant, ...props }, ref) => /* @__PURE__ */ jsx(
|
|
21
|
+
"div",
|
|
22
|
+
{
|
|
23
|
+
ref,
|
|
24
|
+
role: "alert",
|
|
25
|
+
className: cn(alertVariants({ variant }), className),
|
|
26
|
+
...props
|
|
27
|
+
}
|
|
28
|
+
));
|
|
29
|
+
Alert.displayName = "Alert";
|
|
30
|
+
var AlertTitle = React.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
|
|
31
|
+
"h5",
|
|
32
|
+
{
|
|
33
|
+
ref,
|
|
34
|
+
className: cn("mb-1 font-medium leading-none tracking-tight", className),
|
|
35
|
+
...props
|
|
36
|
+
}
|
|
37
|
+
));
|
|
38
|
+
AlertTitle.displayName = "AlertTitle";
|
|
39
|
+
var AlertDescription = React.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
|
|
40
|
+
"div",
|
|
41
|
+
{
|
|
42
|
+
ref,
|
|
43
|
+
className: cn("text-sm [&_p]:leading-relaxed", className),
|
|
44
|
+
...props
|
|
45
|
+
}
|
|
46
|
+
));
|
|
47
|
+
AlertDescription.displayName = "AlertDescription";
|
|
48
|
+
|
|
49
|
+
// src/avatar.tsx
|
|
50
|
+
import * as React2 from "react";
|
|
51
|
+
import * as AvatarPrimitive from "@radix-ui/react-avatar";
|
|
52
|
+
import { cn as cn2 } from "@petrarca/sonnet-core";
|
|
53
|
+
import { jsx as jsx2 } from "react/jsx-runtime";
|
|
54
|
+
var Avatar = React2.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx2(
|
|
55
|
+
AvatarPrimitive.Root,
|
|
56
|
+
{
|
|
57
|
+
ref,
|
|
58
|
+
className: cn2(
|
|
59
|
+
"relative flex h-10 w-10 shrink-0 overflow-hidden rounded-full",
|
|
60
|
+
className
|
|
61
|
+
),
|
|
62
|
+
...props
|
|
63
|
+
}
|
|
64
|
+
));
|
|
65
|
+
Avatar.displayName = AvatarPrimitive.Root.displayName;
|
|
66
|
+
var AvatarImage = React2.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx2(
|
|
67
|
+
AvatarPrimitive.Image,
|
|
68
|
+
{
|
|
69
|
+
ref,
|
|
70
|
+
className: cn2("aspect-square h-full w-full", className),
|
|
71
|
+
...props
|
|
72
|
+
}
|
|
73
|
+
));
|
|
74
|
+
AvatarImage.displayName = AvatarPrimitive.Image.displayName;
|
|
75
|
+
var AvatarFallback = React2.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx2(
|
|
76
|
+
AvatarPrimitive.Fallback,
|
|
77
|
+
{
|
|
78
|
+
ref,
|
|
79
|
+
className: cn2(
|
|
80
|
+
"flex h-full w-full items-center justify-center rounded-full bg-muted",
|
|
81
|
+
className
|
|
82
|
+
),
|
|
83
|
+
...props
|
|
84
|
+
}
|
|
85
|
+
));
|
|
86
|
+
AvatarFallback.displayName = AvatarPrimitive.Fallback.displayName;
|
|
87
|
+
|
|
88
|
+
// src/badge.tsx
|
|
89
|
+
import { cva as cva2 } from "class-variance-authority";
|
|
90
|
+
import { cn as cn3 } from "@petrarca/sonnet-core";
|
|
91
|
+
import { jsx as jsx3 } from "react/jsx-runtime";
|
|
92
|
+
var badgeVariants = cva2(
|
|
93
|
+
"inline-flex items-center rounded-full border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2",
|
|
94
|
+
{
|
|
95
|
+
variants: {
|
|
96
|
+
variant: {
|
|
97
|
+
default: "border-transparent bg-primary text-primary-foreground hover:bg-primary/80",
|
|
98
|
+
secondary: "border-transparent bg-secondary text-secondary-foreground hover:bg-secondary/80",
|
|
99
|
+
destructive: "border-transparent bg-destructive text-destructive-foreground hover:bg-destructive/80",
|
|
100
|
+
outline: "text-foreground"
|
|
101
|
+
},
|
|
102
|
+
badgeColor: {
|
|
103
|
+
grape: "border-transparent bg-purple-100 text-purple-800 hover:bg-purple-200 dark:bg-purple-900 dark:text-purple-200 dark:hover:bg-purple-800",
|
|
104
|
+
blue: "border-transparent bg-blue-100 text-blue-800 hover:bg-blue-200 dark:bg-blue-900 dark:text-blue-200 dark:hover:bg-blue-800",
|
|
105
|
+
green: "border-transparent bg-green-100 text-green-800 hover:bg-green-200 dark:bg-green-900 dark:text-green-200 dark:hover:bg-green-800",
|
|
106
|
+
red: "border-transparent bg-red-100 text-red-800 hover:bg-red-200 dark:bg-red-900 dark:text-red-200 dark:hover:bg-red-800",
|
|
107
|
+
orange: "border-transparent bg-orange-100 text-orange-800 hover:bg-orange-200 dark:bg-orange-900 dark:text-orange-200 dark:hover:bg-orange-800",
|
|
108
|
+
yellow: "border-transparent bg-yellow-100 text-yellow-800 hover:bg-yellow-200 dark:bg-yellow-900 dark:text-yellow-200 dark:hover:bg-yellow-800",
|
|
109
|
+
teal: "border-transparent bg-teal-100 text-teal-800 hover:bg-teal-200 dark:bg-teal-900 dark:text-teal-200 dark:hover:bg-teal-800",
|
|
110
|
+
cyan: "border-transparent bg-cyan-100 text-cyan-800 hover:bg-cyan-200 dark:bg-cyan-900 dark:text-cyan-200 dark:hover:bg-cyan-800",
|
|
111
|
+
pink: "border-transparent bg-pink-100 text-pink-800 hover:bg-pink-200 dark:bg-pink-900 dark:text-pink-200 dark:hover:bg-pink-800",
|
|
112
|
+
indigo: "border-transparent bg-indigo-100 text-indigo-800 hover:bg-indigo-200 dark:bg-indigo-900 dark:text-indigo-200 dark:hover:bg-indigo-800",
|
|
113
|
+
violet: "border-transparent bg-violet-100 text-violet-800 hover:bg-violet-200 dark:bg-violet-900 dark:text-violet-200 dark:hover:bg-violet-800"
|
|
114
|
+
}
|
|
115
|
+
},
|
|
116
|
+
defaultVariants: {
|
|
117
|
+
variant: "default"
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
);
|
|
121
|
+
function Badge({ className, variant, badgeColor, ...props }) {
|
|
122
|
+
return /* @__PURE__ */ jsx3(
|
|
123
|
+
"div",
|
|
124
|
+
{
|
|
125
|
+
className: cn3(badgeVariants({ variant, badgeColor }), className),
|
|
126
|
+
...props
|
|
127
|
+
}
|
|
128
|
+
);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// src/button.tsx
|
|
132
|
+
import * as React3 from "react";
|
|
133
|
+
import { Slot } from "@radix-ui/react-slot";
|
|
134
|
+
import { cva as cva3 } from "class-variance-authority";
|
|
135
|
+
import { cn as cn4 } from "@petrarca/sonnet-core";
|
|
136
|
+
import { jsx as jsx4 } from "react/jsx-runtime";
|
|
137
|
+
var buttonVariants = cva3(
|
|
138
|
+
"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0",
|
|
139
|
+
{
|
|
140
|
+
variants: {
|
|
141
|
+
variant: {
|
|
142
|
+
default: "bg-primary text-primary-foreground hover:bg-primary/90",
|
|
143
|
+
destructive: "bg-destructive text-destructive-foreground hover:bg-destructive/90",
|
|
144
|
+
outline: "border border-input bg-background hover:bg-accent hover:text-accent-foreground",
|
|
145
|
+
secondary: "bg-secondary text-secondary-foreground hover:bg-secondary/80",
|
|
146
|
+
ghost: "hover:bg-accent hover:text-accent-foreground",
|
|
147
|
+
link: "text-primary underline-offset-4 hover:underline"
|
|
148
|
+
},
|
|
149
|
+
size: {
|
|
150
|
+
default: "h-10 px-4 py-2",
|
|
151
|
+
sm: "h-9 rounded-md px-3",
|
|
152
|
+
lg: "h-11 rounded-md px-8",
|
|
153
|
+
icon: "h-10 w-10",
|
|
154
|
+
compact: "h-7 w-7 p-0"
|
|
155
|
+
}
|
|
156
|
+
},
|
|
157
|
+
defaultVariants: {
|
|
158
|
+
variant: "default",
|
|
159
|
+
size: "default"
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
);
|
|
163
|
+
var Button = React3.forwardRef(
|
|
164
|
+
({ className, variant, size, asChild = false, ...props }, ref) => {
|
|
165
|
+
const Comp = asChild ? Slot : "button";
|
|
166
|
+
return /* @__PURE__ */ jsx4(
|
|
167
|
+
Comp,
|
|
168
|
+
{
|
|
169
|
+
className: cn4(buttonVariants({ variant, size, className })),
|
|
170
|
+
ref,
|
|
171
|
+
...props
|
|
172
|
+
}
|
|
173
|
+
);
|
|
174
|
+
}
|
|
175
|
+
);
|
|
176
|
+
Button.displayName = "Button";
|
|
177
|
+
|
|
178
|
+
// src/card.tsx
|
|
179
|
+
import * as React4 from "react";
|
|
180
|
+
import { cn as cn5 } from "@petrarca/sonnet-core";
|
|
181
|
+
import { jsx as jsx5 } from "react/jsx-runtime";
|
|
182
|
+
var Card = React4.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx5(
|
|
183
|
+
"div",
|
|
184
|
+
{
|
|
185
|
+
ref,
|
|
186
|
+
className: cn5(
|
|
187
|
+
"rounded-lg border bg-card text-card-foreground shadow-sm",
|
|
188
|
+
className
|
|
189
|
+
),
|
|
190
|
+
...props
|
|
191
|
+
}
|
|
192
|
+
));
|
|
193
|
+
Card.displayName = "Card";
|
|
194
|
+
var CardHeader = React4.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx5(
|
|
195
|
+
"div",
|
|
196
|
+
{
|
|
197
|
+
ref,
|
|
198
|
+
className: cn5("flex flex-col space-y-1.5 p-6", className),
|
|
199
|
+
...props
|
|
200
|
+
}
|
|
201
|
+
));
|
|
202
|
+
CardHeader.displayName = "CardHeader";
|
|
203
|
+
var CardTitle = React4.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx5(
|
|
204
|
+
"div",
|
|
205
|
+
{
|
|
206
|
+
ref,
|
|
207
|
+
className: cn5(
|
|
208
|
+
"text-2xl font-semibold leading-none tracking-tight",
|
|
209
|
+
className
|
|
210
|
+
),
|
|
211
|
+
...props
|
|
212
|
+
}
|
|
213
|
+
));
|
|
214
|
+
CardTitle.displayName = "CardTitle";
|
|
215
|
+
var CardDescription = React4.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx5(
|
|
216
|
+
"div",
|
|
217
|
+
{
|
|
218
|
+
ref,
|
|
219
|
+
className: cn5("text-sm text-muted-foreground", className),
|
|
220
|
+
...props
|
|
221
|
+
}
|
|
222
|
+
));
|
|
223
|
+
CardDescription.displayName = "CardDescription";
|
|
224
|
+
var CardContent = React4.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx5("div", { ref, className: cn5("p-6 pt-0", className), ...props }));
|
|
225
|
+
CardContent.displayName = "CardContent";
|
|
226
|
+
var CardFooter = React4.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx5(
|
|
227
|
+
"div",
|
|
228
|
+
{
|
|
229
|
+
ref,
|
|
230
|
+
className: cn5("flex items-center p-6 pt-0", className),
|
|
231
|
+
...props
|
|
232
|
+
}
|
|
233
|
+
));
|
|
234
|
+
CardFooter.displayName = "CardFooter";
|
|
235
|
+
|
|
236
|
+
// src/checkbox.tsx
|
|
237
|
+
import * as React5 from "react";
|
|
238
|
+
import * as CheckboxPrimitive from "@radix-ui/react-checkbox";
|
|
239
|
+
import { Check } from "lucide-react";
|
|
240
|
+
import { cn as cn6 } from "@petrarca/sonnet-core";
|
|
241
|
+
import { jsx as jsx6 } from "react/jsx-runtime";
|
|
242
|
+
var Checkbox = React5.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx6(
|
|
243
|
+
CheckboxPrimitive.Root,
|
|
244
|
+
{
|
|
245
|
+
ref,
|
|
246
|
+
className: cn6(
|
|
247
|
+
"grid place-content-center peer h-4 w-4 shrink-0 rounded-sm border border-primary ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground",
|
|
248
|
+
className
|
|
249
|
+
),
|
|
250
|
+
...props,
|
|
251
|
+
children: /* @__PURE__ */ jsx6(
|
|
252
|
+
CheckboxPrimitive.Indicator,
|
|
253
|
+
{
|
|
254
|
+
className: cn6("grid place-content-center text-current"),
|
|
255
|
+
children: /* @__PURE__ */ jsx6(Check, { className: "h-4 w-4" })
|
|
256
|
+
}
|
|
257
|
+
)
|
|
258
|
+
}
|
|
259
|
+
));
|
|
260
|
+
Checkbox.displayName = CheckboxPrimitive.Root.displayName;
|
|
261
|
+
|
|
262
|
+
// src/collapsible.tsx
|
|
263
|
+
import * as CollapsiblePrimitive from "@radix-ui/react-collapsible";
|
|
264
|
+
var Collapsible = CollapsiblePrimitive.Root;
|
|
265
|
+
var CollapsibleTrigger2 = CollapsiblePrimitive.CollapsibleTrigger;
|
|
266
|
+
var CollapsibleContent2 = CollapsiblePrimitive.CollapsibleContent;
|
|
267
|
+
|
|
268
|
+
// src/command.tsx
|
|
269
|
+
import * as React7 from "react";
|
|
270
|
+
import { Command as CommandPrimitive } from "cmdk";
|
|
271
|
+
import { Search } from "lucide-react";
|
|
272
|
+
import { cn as cn8 } from "@petrarca/sonnet-core";
|
|
273
|
+
|
|
274
|
+
// src/dialog.tsx
|
|
275
|
+
import * as React6 from "react";
|
|
276
|
+
import * as DialogPrimitive from "@radix-ui/react-dialog";
|
|
277
|
+
import { X } from "lucide-react";
|
|
278
|
+
import { cn as cn7 } from "@petrarca/sonnet-core";
|
|
279
|
+
import { jsx as jsx7, jsxs } from "react/jsx-runtime";
|
|
280
|
+
var Dialog = DialogPrimitive.Root;
|
|
281
|
+
var DialogTrigger = DialogPrimitive.Trigger;
|
|
282
|
+
var DialogPortal = DialogPrimitive.Portal;
|
|
283
|
+
var DialogClose = DialogPrimitive.Close;
|
|
284
|
+
var DialogOverlay = React6.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx7(
|
|
285
|
+
DialogPrimitive.Overlay,
|
|
286
|
+
{
|
|
287
|
+
ref,
|
|
288
|
+
className: cn7(
|
|
289
|
+
"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",
|
|
290
|
+
className
|
|
291
|
+
),
|
|
292
|
+
...props
|
|
293
|
+
}
|
|
294
|
+
));
|
|
295
|
+
DialogOverlay.displayName = DialogPrimitive.Overlay.displayName;
|
|
296
|
+
var DialogContent = React6.forwardRef(({ className, children, ...props }, ref) => /* @__PURE__ */ jsxs(DialogPortal, { children: [
|
|
297
|
+
/* @__PURE__ */ jsx7(DialogOverlay, {}),
|
|
298
|
+
/* @__PURE__ */ jsxs(
|
|
299
|
+
DialogPrimitive.Content,
|
|
300
|
+
{
|
|
301
|
+
ref,
|
|
302
|
+
className: cn7(
|
|
303
|
+
"fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border bg-background p-6 shadow-lg duration-200 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-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] sm:rounded-lg",
|
|
304
|
+
className
|
|
305
|
+
),
|
|
306
|
+
...props,
|
|
307
|
+
children: [
|
|
308
|
+
children,
|
|
309
|
+
/* @__PURE__ */ jsxs(DialogPrimitive.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-accent data-[state=open]:text-muted-foreground", children: [
|
|
310
|
+
/* @__PURE__ */ jsx7(X, { className: "h-4 w-4" }),
|
|
311
|
+
/* @__PURE__ */ jsx7("span", { className: "sr-only", children: "Close" })
|
|
312
|
+
] })
|
|
313
|
+
]
|
|
314
|
+
}
|
|
315
|
+
)
|
|
316
|
+
] }));
|
|
317
|
+
DialogContent.displayName = DialogPrimitive.Content.displayName;
|
|
318
|
+
var DialogHeader = ({
|
|
319
|
+
className,
|
|
320
|
+
...props
|
|
321
|
+
}) => /* @__PURE__ */ jsx7(
|
|
322
|
+
"div",
|
|
323
|
+
{
|
|
324
|
+
className: cn7(
|
|
325
|
+
"flex flex-col space-y-1.5 text-center sm:text-left",
|
|
326
|
+
className
|
|
327
|
+
),
|
|
328
|
+
...props
|
|
329
|
+
}
|
|
330
|
+
);
|
|
331
|
+
DialogHeader.displayName = "DialogHeader";
|
|
332
|
+
var DialogFooter = ({
|
|
333
|
+
className,
|
|
334
|
+
...props
|
|
335
|
+
}) => /* @__PURE__ */ jsx7(
|
|
336
|
+
"div",
|
|
337
|
+
{
|
|
338
|
+
className: cn7(
|
|
339
|
+
"flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2",
|
|
340
|
+
className
|
|
341
|
+
),
|
|
342
|
+
...props
|
|
343
|
+
}
|
|
344
|
+
);
|
|
345
|
+
DialogFooter.displayName = "DialogFooter";
|
|
346
|
+
var DialogTitle = React6.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx7(
|
|
347
|
+
DialogPrimitive.Title,
|
|
348
|
+
{
|
|
349
|
+
ref,
|
|
350
|
+
className: cn7(
|
|
351
|
+
"text-lg font-semibold leading-none tracking-tight",
|
|
352
|
+
className
|
|
353
|
+
),
|
|
354
|
+
...props
|
|
355
|
+
}
|
|
356
|
+
));
|
|
357
|
+
DialogTitle.displayName = DialogPrimitive.Title.displayName;
|
|
358
|
+
var DialogDescription = React6.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx7(
|
|
359
|
+
DialogPrimitive.Description,
|
|
360
|
+
{
|
|
361
|
+
ref,
|
|
362
|
+
className: cn7("text-sm text-muted-foreground", className),
|
|
363
|
+
...props
|
|
364
|
+
}
|
|
365
|
+
));
|
|
366
|
+
DialogDescription.displayName = DialogPrimitive.Description.displayName;
|
|
367
|
+
|
|
368
|
+
// src/command.tsx
|
|
369
|
+
import { jsx as jsx8, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
370
|
+
var Command = React7.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx8(
|
|
371
|
+
CommandPrimitive,
|
|
372
|
+
{
|
|
373
|
+
ref,
|
|
374
|
+
className: cn8(
|
|
375
|
+
"flex h-full w-full flex-col overflow-hidden rounded-md bg-popover text-popover-foreground",
|
|
376
|
+
className
|
|
377
|
+
),
|
|
378
|
+
...props
|
|
379
|
+
}
|
|
380
|
+
));
|
|
381
|
+
Command.displayName = CommandPrimitive.displayName;
|
|
382
|
+
var CommandDialog = ({ children, ...props }) => {
|
|
383
|
+
return /* @__PURE__ */ jsx8(Dialog, { ...props, children: /* @__PURE__ */ jsx8(DialogContent, { className: "overflow-hidden p-0 shadow-lg", children: /* @__PURE__ */ jsx8(Command, { className: "[&_[cmdk-group-heading]]:px-2 [&_[cmdk-group-heading]]:font-medium [&_[cmdk-group-heading]]:text-muted-foreground [&_[cmdk-group]:not([hidden])_~[cmdk-group]]:pt-0 [&_[cmdk-group]]:px-2 [&_[cmdk-input-wrapper]_svg]:h-5 [&_[cmdk-input-wrapper]_svg]:w-5 [&_[cmdk-input]]:h-12 [&_[cmdk-item]]:px-2 [&_[cmdk-item]]:py-3 [&_[cmdk-item]_svg]:h-5 [&_[cmdk-item]_svg]:w-5", children }) }) });
|
|
384
|
+
};
|
|
385
|
+
var CommandInput = React7.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsxs2("div", { className: "flex items-center border-b px-3", "cmdk-input-wrapper": "", children: [
|
|
386
|
+
/* @__PURE__ */ jsx8(Search, { className: "mr-2 h-4 w-4 shrink-0 opacity-50" }),
|
|
387
|
+
/* @__PURE__ */ jsx8(
|
|
388
|
+
CommandPrimitive.Input,
|
|
389
|
+
{
|
|
390
|
+
ref,
|
|
391
|
+
className: cn8(
|
|
392
|
+
"flex h-11 w-full rounded-md bg-transparent py-3 text-sm outline-none placeholder:text-muted-foreground disabled:cursor-not-allowed disabled:opacity-50",
|
|
393
|
+
className
|
|
394
|
+
),
|
|
395
|
+
...props
|
|
396
|
+
}
|
|
397
|
+
)
|
|
398
|
+
] }));
|
|
399
|
+
CommandInput.displayName = CommandPrimitive.Input.displayName;
|
|
400
|
+
var CommandList = React7.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx8(
|
|
401
|
+
CommandPrimitive.List,
|
|
402
|
+
{
|
|
403
|
+
ref,
|
|
404
|
+
className: cn8("max-h-[300px] overflow-y-auto overflow-x-hidden", className),
|
|
405
|
+
...props
|
|
406
|
+
}
|
|
407
|
+
));
|
|
408
|
+
CommandList.displayName = CommandPrimitive.List.displayName;
|
|
409
|
+
var CommandEmpty = React7.forwardRef((props, ref) => /* @__PURE__ */ jsx8(
|
|
410
|
+
CommandPrimitive.Empty,
|
|
411
|
+
{
|
|
412
|
+
ref,
|
|
413
|
+
className: "py-6 text-center text-sm",
|
|
414
|
+
...props
|
|
415
|
+
}
|
|
416
|
+
));
|
|
417
|
+
CommandEmpty.displayName = CommandPrimitive.Empty.displayName;
|
|
418
|
+
var CommandGroup = React7.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx8(
|
|
419
|
+
CommandPrimitive.Group,
|
|
420
|
+
{
|
|
421
|
+
ref,
|
|
422
|
+
className: cn8(
|
|
423
|
+
"overflow-hidden p-1 text-foreground [&_[cmdk-group-heading]]:px-2 [&_[cmdk-group-heading]]:py-1.5 [&_[cmdk-group-heading]]:text-xs [&_[cmdk-group-heading]]:font-medium [&_[cmdk-group-heading]]:text-muted-foreground",
|
|
424
|
+
className
|
|
425
|
+
),
|
|
426
|
+
...props
|
|
427
|
+
}
|
|
428
|
+
));
|
|
429
|
+
CommandGroup.displayName = CommandPrimitive.Group.displayName;
|
|
430
|
+
var CommandSeparator = React7.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx8(
|
|
431
|
+
CommandPrimitive.Separator,
|
|
432
|
+
{
|
|
433
|
+
ref,
|
|
434
|
+
className: cn8("-mx-1 h-px bg-border", className),
|
|
435
|
+
...props
|
|
436
|
+
}
|
|
437
|
+
));
|
|
438
|
+
CommandSeparator.displayName = CommandPrimitive.Separator.displayName;
|
|
439
|
+
var CommandItem = React7.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx8(
|
|
440
|
+
CommandPrimitive.Item,
|
|
441
|
+
{
|
|
442
|
+
ref,
|
|
443
|
+
className: cn8(
|
|
444
|
+
"relative flex cursor-default gap-2 select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none data-[disabled=true]:pointer-events-none data-[selected='true']:bg-accent data-[selected=true]:text-accent-foreground data-[disabled=true]:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0",
|
|
445
|
+
className
|
|
446
|
+
),
|
|
447
|
+
...props
|
|
448
|
+
}
|
|
449
|
+
));
|
|
450
|
+
CommandItem.displayName = CommandPrimitive.Item.displayName;
|
|
451
|
+
var CommandShortcut = ({
|
|
452
|
+
className,
|
|
453
|
+
...props
|
|
454
|
+
}) => {
|
|
455
|
+
return /* @__PURE__ */ jsx8(
|
|
456
|
+
"span",
|
|
457
|
+
{
|
|
458
|
+
className: cn8(
|
|
459
|
+
"ml-auto text-xs tracking-widest text-muted-foreground",
|
|
460
|
+
className
|
|
461
|
+
),
|
|
462
|
+
...props
|
|
463
|
+
}
|
|
464
|
+
);
|
|
465
|
+
};
|
|
466
|
+
CommandShortcut.displayName = "CommandShortcut";
|
|
467
|
+
|
|
468
|
+
// src/dropdown-menu.tsx
|
|
469
|
+
import * as React8 from "react";
|
|
470
|
+
import * as DropdownMenuPrimitive from "@radix-ui/react-dropdown-menu";
|
|
471
|
+
import { Check as Check2, ChevronRight, Circle } from "lucide-react";
|
|
472
|
+
import { cn as cn9 } from "@petrarca/sonnet-core";
|
|
473
|
+
import { jsx as jsx9, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
474
|
+
var DropdownMenu = DropdownMenuPrimitive.Root;
|
|
475
|
+
var DropdownMenuTrigger = DropdownMenuPrimitive.Trigger;
|
|
476
|
+
var DropdownMenuGroup = DropdownMenuPrimitive.Group;
|
|
477
|
+
var DropdownMenuPortal = DropdownMenuPrimitive.Portal;
|
|
478
|
+
var DropdownMenuSub = DropdownMenuPrimitive.Sub;
|
|
479
|
+
var DropdownMenuRadioGroup = DropdownMenuPrimitive.RadioGroup;
|
|
480
|
+
var DropdownMenuSubTrigger = React8.forwardRef(({ className, inset, children, ...props }, ref) => /* @__PURE__ */ jsxs3(
|
|
481
|
+
DropdownMenuPrimitive.SubTrigger,
|
|
482
|
+
{
|
|
483
|
+
ref,
|
|
484
|
+
className: cn9(
|
|
485
|
+
"flex cursor-default select-none items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-none focus:bg-accent data-[state=open]:bg-accent [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0",
|
|
486
|
+
inset && "pl-8",
|
|
487
|
+
className
|
|
488
|
+
),
|
|
489
|
+
...props,
|
|
490
|
+
children: [
|
|
491
|
+
children,
|
|
492
|
+
/* @__PURE__ */ jsx9(ChevronRight, { className: "ml-auto" })
|
|
493
|
+
]
|
|
494
|
+
}
|
|
495
|
+
));
|
|
496
|
+
DropdownMenuSubTrigger.displayName = DropdownMenuPrimitive.SubTrigger.displayName;
|
|
497
|
+
var DropdownMenuSubContent = React8.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx9(
|
|
498
|
+
DropdownMenuPrimitive.SubContent,
|
|
499
|
+
{
|
|
500
|
+
ref,
|
|
501
|
+
className: cn9(
|
|
502
|
+
"z-50 min-w-[8rem] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-lg 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 origin-[--radix-dropdown-menu-content-transform-origin]",
|
|
503
|
+
className
|
|
504
|
+
),
|
|
505
|
+
...props
|
|
506
|
+
}
|
|
507
|
+
));
|
|
508
|
+
DropdownMenuSubContent.displayName = DropdownMenuPrimitive.SubContent.displayName;
|
|
509
|
+
var DropdownMenuContent = React8.forwardRef(({ className, sideOffset = 4, ...props }, ref) => /* @__PURE__ */ jsx9(DropdownMenuPrimitive.Portal, { children: /* @__PURE__ */ jsx9(
|
|
510
|
+
DropdownMenuPrimitive.Content,
|
|
511
|
+
{
|
|
512
|
+
ref,
|
|
513
|
+
sideOffset,
|
|
514
|
+
className: cn9(
|
|
515
|
+
"z-50 max-h-[var(--radix-dropdown-menu-content-available-height)] min-w-[8rem] overflow-y-auto overflow-x-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-md 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 origin-[--radix-dropdown-menu-content-transform-origin]",
|
|
516
|
+
className
|
|
517
|
+
),
|
|
518
|
+
...props
|
|
519
|
+
}
|
|
520
|
+
) }));
|
|
521
|
+
DropdownMenuContent.displayName = DropdownMenuPrimitive.Content.displayName;
|
|
522
|
+
var DropdownMenuItem = React8.forwardRef(({ className, inset, ...props }, ref) => /* @__PURE__ */ jsx9(
|
|
523
|
+
DropdownMenuPrimitive.Item,
|
|
524
|
+
{
|
|
525
|
+
ref,
|
|
526
|
+
className: cn9(
|
|
527
|
+
"relative flex cursor-default select-none items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0",
|
|
528
|
+
inset && "pl-8",
|
|
529
|
+
className
|
|
530
|
+
),
|
|
531
|
+
...props
|
|
532
|
+
}
|
|
533
|
+
));
|
|
534
|
+
DropdownMenuItem.displayName = DropdownMenuPrimitive.Item.displayName;
|
|
535
|
+
var DropdownMenuCheckboxItem = React8.forwardRef(({ className, children, checked, ...props }, ref) => /* @__PURE__ */ jsxs3(
|
|
536
|
+
DropdownMenuPrimitive.CheckboxItem,
|
|
537
|
+
{
|
|
538
|
+
ref,
|
|
539
|
+
className: cn9(
|
|
540
|
+
"relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
|
|
541
|
+
className
|
|
542
|
+
),
|
|
543
|
+
checked,
|
|
544
|
+
...props,
|
|
545
|
+
children: [
|
|
546
|
+
/* @__PURE__ */ jsx9("span", { className: "absolute left-2 flex h-3.5 w-3.5 items-center justify-center", children: /* @__PURE__ */ jsx9(DropdownMenuPrimitive.ItemIndicator, { children: /* @__PURE__ */ jsx9(Check2, { className: "h-4 w-4" }) }) }),
|
|
547
|
+
children
|
|
548
|
+
]
|
|
549
|
+
}
|
|
550
|
+
));
|
|
551
|
+
DropdownMenuCheckboxItem.displayName = DropdownMenuPrimitive.CheckboxItem.displayName;
|
|
552
|
+
var DropdownMenuRadioItem = React8.forwardRef(({ className, children, ...props }, ref) => /* @__PURE__ */ jsxs3(
|
|
553
|
+
DropdownMenuPrimitive.RadioItem,
|
|
554
|
+
{
|
|
555
|
+
ref,
|
|
556
|
+
className: cn9(
|
|
557
|
+
"relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
|
|
558
|
+
className
|
|
559
|
+
),
|
|
560
|
+
...props,
|
|
561
|
+
children: [
|
|
562
|
+
/* @__PURE__ */ jsx9("span", { className: "absolute left-2 flex h-3.5 w-3.5 items-center justify-center", children: /* @__PURE__ */ jsx9(DropdownMenuPrimitive.ItemIndicator, { children: /* @__PURE__ */ jsx9(Circle, { className: "h-2 w-2 fill-current" }) }) }),
|
|
563
|
+
children
|
|
564
|
+
]
|
|
565
|
+
}
|
|
566
|
+
));
|
|
567
|
+
DropdownMenuRadioItem.displayName = DropdownMenuPrimitive.RadioItem.displayName;
|
|
568
|
+
var DropdownMenuLabel = React8.forwardRef(({ className, inset, ...props }, ref) => /* @__PURE__ */ jsx9(
|
|
569
|
+
DropdownMenuPrimitive.Label,
|
|
570
|
+
{
|
|
571
|
+
ref,
|
|
572
|
+
className: cn9(
|
|
573
|
+
"px-2 py-1.5 text-sm font-semibold",
|
|
574
|
+
inset && "pl-8",
|
|
575
|
+
className
|
|
576
|
+
),
|
|
577
|
+
...props
|
|
578
|
+
}
|
|
579
|
+
));
|
|
580
|
+
DropdownMenuLabel.displayName = DropdownMenuPrimitive.Label.displayName;
|
|
581
|
+
var DropdownMenuSeparator = React8.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx9(
|
|
582
|
+
DropdownMenuPrimitive.Separator,
|
|
583
|
+
{
|
|
584
|
+
ref,
|
|
585
|
+
className: cn9("-mx-1 my-1 h-px bg-muted", className),
|
|
586
|
+
...props
|
|
587
|
+
}
|
|
588
|
+
));
|
|
589
|
+
DropdownMenuSeparator.displayName = DropdownMenuPrimitive.Separator.displayName;
|
|
590
|
+
var DropdownMenuShortcut = ({
|
|
591
|
+
className,
|
|
592
|
+
...props
|
|
593
|
+
}) => {
|
|
594
|
+
return /* @__PURE__ */ jsx9(
|
|
595
|
+
"span",
|
|
596
|
+
{
|
|
597
|
+
className: cn9("ml-auto text-xs tracking-widest opacity-60", className),
|
|
598
|
+
...props
|
|
599
|
+
}
|
|
600
|
+
);
|
|
601
|
+
};
|
|
602
|
+
DropdownMenuShortcut.displayName = "DropdownMenuShortcut";
|
|
603
|
+
|
|
604
|
+
// src/input.tsx
|
|
605
|
+
import * as React9 from "react";
|
|
606
|
+
import { cn as cn10 } from "@petrarca/sonnet-core";
|
|
607
|
+
import { jsx as jsx10 } from "react/jsx-runtime";
|
|
608
|
+
var Input = React9.forwardRef(
|
|
609
|
+
({ className, type, ...props }, ref) => {
|
|
610
|
+
return /* @__PURE__ */ jsx10(
|
|
611
|
+
"input",
|
|
612
|
+
{
|
|
613
|
+
type,
|
|
614
|
+
className: cn10(
|
|
615
|
+
"flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-base file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-foreground placeholder:text-muted-foreground focus-visible:outline-none disabled:cursor-not-allowed disabled:opacity-50 md:text-sm",
|
|
616
|
+
className
|
|
617
|
+
),
|
|
618
|
+
ref,
|
|
619
|
+
...props
|
|
620
|
+
}
|
|
621
|
+
);
|
|
622
|
+
}
|
|
623
|
+
);
|
|
624
|
+
Input.displayName = "Input";
|
|
625
|
+
|
|
626
|
+
// src/input-group.tsx
|
|
627
|
+
import { cva as cva4 } from "class-variance-authority";
|
|
628
|
+
import { cn as cn12 } from "@petrarca/sonnet-core";
|
|
629
|
+
|
|
630
|
+
// src/textarea.tsx
|
|
631
|
+
import * as React10 from "react";
|
|
632
|
+
import { cn as cn11 } from "@petrarca/sonnet-core";
|
|
633
|
+
import { jsx as jsx11 } from "react/jsx-runtime";
|
|
634
|
+
var Textarea = React10.forwardRef(({ className, ...props }, ref) => {
|
|
635
|
+
return /* @__PURE__ */ jsx11(
|
|
636
|
+
"textarea",
|
|
637
|
+
{
|
|
638
|
+
className: cn11(
|
|
639
|
+
"flex min-h-[80px] w-full rounded-md border border-input bg-background px-3 py-2 text-base placeholder:text-muted-foreground focus-visible:outline-none disabled:cursor-not-allowed disabled:opacity-50 md:text-sm",
|
|
640
|
+
className
|
|
641
|
+
),
|
|
642
|
+
ref,
|
|
643
|
+
...props
|
|
644
|
+
}
|
|
645
|
+
);
|
|
646
|
+
});
|
|
647
|
+
Textarea.displayName = "Textarea";
|
|
648
|
+
|
|
649
|
+
// src/input-group.tsx
|
|
650
|
+
import { jsx as jsx12 } from "react/jsx-runtime";
|
|
651
|
+
function InputGroup({ className, ...props }) {
|
|
652
|
+
return /* @__PURE__ */ jsx12(
|
|
653
|
+
"div",
|
|
654
|
+
{
|
|
655
|
+
"data-slot": "input-group",
|
|
656
|
+
role: "group",
|
|
657
|
+
className: cn12(
|
|
658
|
+
"group/input-group border-input dark:bg-input/30 shadow-xs relative flex w-full items-center rounded-md border outline-none transition-[color,box-shadow]",
|
|
659
|
+
"h-9 has-[>textarea]:h-auto",
|
|
660
|
+
// Variants based on alignment.
|
|
661
|
+
"has-[>[data-align=inline-start]]:[&>input]:pl-2",
|
|
662
|
+
"has-[>[data-align=inline-end]]:[&>input]:pr-2",
|
|
663
|
+
"has-[>[data-align=block-start]]:h-auto has-[>[data-align=block-start]]:flex-col has-[>[data-align=block-start]]:[&>input]:pb-3",
|
|
664
|
+
"has-[>[data-align=block-end]]:h-auto has-[>[data-align=block-end]]:flex-col has-[>[data-align=block-end]]:[&>input]:pt-3",
|
|
665
|
+
// Focus state.
|
|
666
|
+
"has-[[data-slot=input-group-control]:focus-visible]:ring-ring has-[[data-slot=input-group-control]:focus-visible]:ring-1",
|
|
667
|
+
// Error state.
|
|
668
|
+
"has-[[data-slot][aria-invalid=true]]:ring-destructive/20 has-[[data-slot][aria-invalid=true]]:border-destructive dark:has-[[data-slot][aria-invalid=true]]:ring-destructive/40",
|
|
669
|
+
className
|
|
670
|
+
),
|
|
671
|
+
...props
|
|
672
|
+
}
|
|
673
|
+
);
|
|
674
|
+
}
|
|
675
|
+
var inputGroupAddonVariants = cva4(
|
|
676
|
+
"text-muted-foreground flex h-auto cursor-text select-none items-center justify-center gap-2 py-1.5 text-sm font-medium group-data-[disabled=true]/input-group:opacity-50 [&>kbd]:rounded-[calc(var(--radius)-5px)] [&>svg:not([class*='size-'])]:size-4",
|
|
677
|
+
{
|
|
678
|
+
variants: {
|
|
679
|
+
align: {
|
|
680
|
+
"inline-start": "order-first pl-3 has-[>button]:ml-[-0.45rem] has-[>kbd]:ml-[-0.35rem]",
|
|
681
|
+
"inline-end": "order-last pr-3 has-[>button]:mr-[-0.4rem] has-[>kbd]:mr-[-0.35rem]",
|
|
682
|
+
"block-start": "[.border-b]:pb-3 order-first w-full justify-start px-3 pt-3 group-has-[>input]/input-group:pt-2.5",
|
|
683
|
+
"block-end": "[.border-t]:pt-3 order-last w-full justify-start px-3 pb-3 group-has-[>input]/input-group:pb-2.5"
|
|
684
|
+
}
|
|
685
|
+
},
|
|
686
|
+
defaultVariants: {
|
|
687
|
+
align: "inline-start"
|
|
688
|
+
}
|
|
689
|
+
}
|
|
690
|
+
);
|
|
691
|
+
function InputGroupAddon({
|
|
692
|
+
className,
|
|
693
|
+
align = "inline-start",
|
|
694
|
+
...props
|
|
695
|
+
}) {
|
|
696
|
+
return /* @__PURE__ */ jsx12(
|
|
697
|
+
"div",
|
|
698
|
+
{
|
|
699
|
+
role: "group",
|
|
700
|
+
"data-slot": "input-group-addon",
|
|
701
|
+
"data-align": align,
|
|
702
|
+
className: cn12(inputGroupAddonVariants({ align }), className),
|
|
703
|
+
onClick: (e) => {
|
|
704
|
+
if (e.target.closest("button")) {
|
|
705
|
+
return;
|
|
706
|
+
}
|
|
707
|
+
e.currentTarget.parentElement?.querySelector("input")?.focus();
|
|
708
|
+
},
|
|
709
|
+
...props
|
|
710
|
+
}
|
|
711
|
+
);
|
|
712
|
+
}
|
|
713
|
+
var inputGroupButtonVariants = cva4(
|
|
714
|
+
"flex items-center gap-2 text-sm shadow-none",
|
|
715
|
+
{
|
|
716
|
+
variants: {
|
|
717
|
+
size: {
|
|
718
|
+
xs: "h-6 gap-1 rounded-[calc(var(--radius)-5px)] px-2 has-[>svg]:px-2 [&>svg:not([class*='size-'])]:size-3.5",
|
|
719
|
+
sm: "h-8 gap-1.5 rounded-md px-2.5 has-[>svg]:px-2.5",
|
|
720
|
+
"icon-xs": "size-6 rounded-[calc(var(--radius)-5px)] p-0 has-[>svg]:p-0",
|
|
721
|
+
"icon-sm": "size-8 p-0 has-[>svg]:p-0"
|
|
722
|
+
}
|
|
723
|
+
},
|
|
724
|
+
defaultVariants: {
|
|
725
|
+
size: "xs"
|
|
726
|
+
}
|
|
727
|
+
}
|
|
728
|
+
);
|
|
729
|
+
function InputGroupText({ className, ...props }) {
|
|
730
|
+
return /* @__PURE__ */ jsx12(
|
|
731
|
+
"span",
|
|
732
|
+
{
|
|
733
|
+
className: cn12(
|
|
734
|
+
"text-muted-foreground flex items-center gap-2 text-sm [&_svg:not([class*='size-'])]:size-4 [&_svg]:pointer-events-none",
|
|
735
|
+
className
|
|
736
|
+
),
|
|
737
|
+
...props
|
|
738
|
+
}
|
|
739
|
+
);
|
|
740
|
+
}
|
|
741
|
+
function InputGroupInput({
|
|
742
|
+
className,
|
|
743
|
+
...props
|
|
744
|
+
}) {
|
|
745
|
+
return /* @__PURE__ */ jsx12(
|
|
746
|
+
Input,
|
|
747
|
+
{
|
|
748
|
+
"data-slot": "input-group-control",
|
|
749
|
+
className: cn12(
|
|
750
|
+
"flex-1 rounded-none border-0 bg-transparent shadow-none focus-visible:ring-0 dark:bg-transparent",
|
|
751
|
+
className
|
|
752
|
+
),
|
|
753
|
+
...props
|
|
754
|
+
}
|
|
755
|
+
);
|
|
756
|
+
}
|
|
757
|
+
function InputGroupTextarea({
|
|
758
|
+
className,
|
|
759
|
+
...props
|
|
760
|
+
}) {
|
|
761
|
+
return /* @__PURE__ */ jsx12(
|
|
762
|
+
Textarea,
|
|
763
|
+
{
|
|
764
|
+
"data-slot": "input-group-control",
|
|
765
|
+
className: cn12(
|
|
766
|
+
"flex-1 resize-none rounded-none border-0 bg-transparent py-3 shadow-none focus-visible:ring-0 dark:bg-transparent",
|
|
767
|
+
className
|
|
768
|
+
),
|
|
769
|
+
...props
|
|
770
|
+
}
|
|
771
|
+
);
|
|
772
|
+
}
|
|
773
|
+
|
|
774
|
+
// src/label.tsx
|
|
775
|
+
import * as React11 from "react";
|
|
776
|
+
import * as LabelPrimitive from "@radix-ui/react-label";
|
|
777
|
+
import { cva as cva5 } from "class-variance-authority";
|
|
778
|
+
import { cn as cn13 } from "@petrarca/sonnet-core";
|
|
779
|
+
import { jsx as jsx13 } from "react/jsx-runtime";
|
|
780
|
+
var labelVariants = cva5(
|
|
781
|
+
"text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
|
|
782
|
+
);
|
|
783
|
+
var Label2 = React11.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx13(
|
|
784
|
+
LabelPrimitive.Root,
|
|
785
|
+
{
|
|
786
|
+
ref,
|
|
787
|
+
className: cn13(labelVariants(), className),
|
|
788
|
+
...props
|
|
789
|
+
}
|
|
790
|
+
));
|
|
791
|
+
Label2.displayName = LabelPrimitive.Root.displayName;
|
|
792
|
+
|
|
793
|
+
// src/popover.tsx
|
|
794
|
+
import * as React12 from "react";
|
|
795
|
+
import * as PopoverPrimitive from "@radix-ui/react-popover";
|
|
796
|
+
import { cn as cn14 } from "@petrarca/sonnet-core";
|
|
797
|
+
import { jsx as jsx14 } from "react/jsx-runtime";
|
|
798
|
+
var Popover = PopoverPrimitive.Root;
|
|
799
|
+
var PopoverTrigger = PopoverPrimitive.Trigger;
|
|
800
|
+
var PopoverContent = React12.forwardRef(({ className, align = "center", sideOffset = 4, ...props }, ref) => /* @__PURE__ */ jsx14(PopoverPrimitive.Portal, { children: /* @__PURE__ */ jsx14(
|
|
801
|
+
PopoverPrimitive.Content,
|
|
802
|
+
{
|
|
803
|
+
ref,
|
|
804
|
+
align,
|
|
805
|
+
sideOffset,
|
|
806
|
+
className: cn14(
|
|
807
|
+
"z-50 w-72 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 origin-[--radix-popover-content-transform-origin]",
|
|
808
|
+
className
|
|
809
|
+
),
|
|
810
|
+
...props
|
|
811
|
+
}
|
|
812
|
+
) }));
|
|
813
|
+
PopoverContent.displayName = PopoverPrimitive.Content.displayName;
|
|
814
|
+
|
|
815
|
+
// src/scroll-area.tsx
|
|
816
|
+
import * as React13 from "react";
|
|
817
|
+
import * as ScrollAreaPrimitive from "@radix-ui/react-scroll-area";
|
|
818
|
+
import { cn as cn15 } from "@petrarca/sonnet-core";
|
|
819
|
+
import { jsx as jsx15, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
820
|
+
var ScrollArea = React13.forwardRef(({ className, children, ...props }, ref) => /* @__PURE__ */ jsxs4(
|
|
821
|
+
ScrollAreaPrimitive.Root,
|
|
822
|
+
{
|
|
823
|
+
ref,
|
|
824
|
+
className: cn15("relative overflow-hidden", className),
|
|
825
|
+
...props,
|
|
826
|
+
children: [
|
|
827
|
+
/* @__PURE__ */ jsx15(ScrollAreaPrimitive.Viewport, { className: "h-full w-full rounded-[inherit]", children }),
|
|
828
|
+
/* @__PURE__ */ jsx15(ScrollBar, {}),
|
|
829
|
+
/* @__PURE__ */ jsx15(ScrollAreaPrimitive.Corner, {})
|
|
830
|
+
]
|
|
831
|
+
}
|
|
832
|
+
));
|
|
833
|
+
ScrollArea.displayName = ScrollAreaPrimitive.Root.displayName;
|
|
834
|
+
var ScrollBar = React13.forwardRef(({ className, orientation = "vertical", ...props }, ref) => /* @__PURE__ */ jsx15(
|
|
835
|
+
ScrollAreaPrimitive.ScrollAreaScrollbar,
|
|
836
|
+
{
|
|
837
|
+
ref,
|
|
838
|
+
orientation,
|
|
839
|
+
className: cn15(
|
|
840
|
+
"flex touch-none select-none transition-colors",
|
|
841
|
+
orientation === "vertical" && "h-full w-2.5 border-l border-l-transparent p-[1px]",
|
|
842
|
+
orientation === "horizontal" && "h-2.5 flex-col border-t border-t-transparent p-[1px]",
|
|
843
|
+
className
|
|
844
|
+
),
|
|
845
|
+
...props,
|
|
846
|
+
children: /* @__PURE__ */ jsx15(ScrollAreaPrimitive.ScrollAreaThumb, { className: "relative flex-1 rounded-full bg-border" })
|
|
847
|
+
}
|
|
848
|
+
));
|
|
849
|
+
ScrollBar.displayName = ScrollAreaPrimitive.ScrollAreaScrollbar.displayName;
|
|
850
|
+
|
|
851
|
+
// src/separator.tsx
|
|
852
|
+
import * as React14 from "react";
|
|
853
|
+
import * as SeparatorPrimitive from "@radix-ui/react-separator";
|
|
854
|
+
import { cn as cn16 } from "@petrarca/sonnet-core";
|
|
855
|
+
import { jsx as jsx16 } from "react/jsx-runtime";
|
|
856
|
+
var Separator2 = React14.forwardRef(
|
|
857
|
+
({ className, orientation = "horizontal", decorative = true, ...props }, ref) => /* @__PURE__ */ jsx16(
|
|
858
|
+
SeparatorPrimitive.Root,
|
|
859
|
+
{
|
|
860
|
+
ref,
|
|
861
|
+
decorative,
|
|
862
|
+
orientation,
|
|
863
|
+
className: cn16(
|
|
864
|
+
"shrink-0 bg-border",
|
|
865
|
+
orientation === "horizontal" ? "h-[1px] w-full" : "h-full w-[1px]",
|
|
866
|
+
className
|
|
867
|
+
),
|
|
868
|
+
...props
|
|
869
|
+
}
|
|
870
|
+
)
|
|
871
|
+
);
|
|
872
|
+
Separator2.displayName = SeparatorPrimitive.Root.displayName;
|
|
873
|
+
|
|
874
|
+
// src/sheet.tsx
|
|
875
|
+
import * as React15 from "react";
|
|
876
|
+
import * as DialogPrimitive2 from "@radix-ui/react-dialog";
|
|
877
|
+
import { X as X2 } from "lucide-react";
|
|
878
|
+
import { cn as cn17 } from "@petrarca/sonnet-core";
|
|
879
|
+
import { jsx as jsx17, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
880
|
+
var Sheet = DialogPrimitive2.Root;
|
|
881
|
+
var SheetTrigger = DialogPrimitive2.Trigger;
|
|
882
|
+
var SheetClose = DialogPrimitive2.Close;
|
|
883
|
+
var SheetPortal = DialogPrimitive2.Portal;
|
|
884
|
+
var SheetOverlay = React15.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx17(
|
|
885
|
+
DialogPrimitive2.Overlay,
|
|
886
|
+
{
|
|
887
|
+
ref,
|
|
888
|
+
className: cn17(
|
|
889
|
+
"fixed inset-0 z-50 bg-black/50 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0",
|
|
890
|
+
className
|
|
891
|
+
),
|
|
892
|
+
...props
|
|
893
|
+
}
|
|
894
|
+
));
|
|
895
|
+
SheetOverlay.displayName = "SheetOverlay";
|
|
896
|
+
var SheetContent = React15.forwardRef(
|
|
897
|
+
({ className, children, side = "right", offsetTop = "0px", ...props }, ref) => /* @__PURE__ */ jsxs5(SheetPortal, { children: [
|
|
898
|
+
/* @__PURE__ */ jsx17(SheetOverlay, {}),
|
|
899
|
+
/* @__PURE__ */ jsxs5(
|
|
900
|
+
DialogPrimitive2.Content,
|
|
901
|
+
{
|
|
902
|
+
ref,
|
|
903
|
+
className: cn17(
|
|
904
|
+
"fixed z-50 flex flex-col bg-background shadow-xl transition-transform duration-300 ease-in-out",
|
|
905
|
+
"bottom-0 w-[480px] max-w-[90vw] border-l",
|
|
906
|
+
side === "right" && "right-0 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:slide-out-to-right data-[state=open]:slide-in-from-right",
|
|
907
|
+
side === "left" && "left-0 border-l-0 border-r data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:slide-out-to-left data-[state=open]:slide-in-from-left",
|
|
908
|
+
className
|
|
909
|
+
),
|
|
910
|
+
style: { top: offsetTop },
|
|
911
|
+
...props,
|
|
912
|
+
children: [
|
|
913
|
+
children,
|
|
914
|
+
/* @__PURE__ */ jsxs5(DialogPrimitive2.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", children: [
|
|
915
|
+
/* @__PURE__ */ jsx17(X2, { className: "h-4 w-4" }),
|
|
916
|
+
/* @__PURE__ */ jsx17("span", { className: "sr-only", children: "Close" })
|
|
917
|
+
] })
|
|
918
|
+
]
|
|
919
|
+
}
|
|
920
|
+
)
|
|
921
|
+
] })
|
|
922
|
+
);
|
|
923
|
+
SheetContent.displayName = "SheetContent";
|
|
924
|
+
var SheetHeader = ({
|
|
925
|
+
className,
|
|
926
|
+
...props
|
|
927
|
+
}) => /* @__PURE__ */ jsx17("div", { className: cn17("shrink-0 border-b px-6 py-4", className), ...props });
|
|
928
|
+
var SheetTitle = React15.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx17(
|
|
929
|
+
DialogPrimitive2.Title,
|
|
930
|
+
{
|
|
931
|
+
ref,
|
|
932
|
+
className: cn17("text-lg font-semibold", className),
|
|
933
|
+
...props
|
|
934
|
+
}
|
|
935
|
+
));
|
|
936
|
+
SheetTitle.displayName = "SheetTitle";
|
|
937
|
+
var SheetDescription = React15.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx17(
|
|
938
|
+
DialogPrimitive2.Description,
|
|
939
|
+
{
|
|
940
|
+
ref,
|
|
941
|
+
className: cn17("text-sm text-muted-foreground mt-1", className),
|
|
942
|
+
...props
|
|
943
|
+
}
|
|
944
|
+
));
|
|
945
|
+
SheetDescription.displayName = "SheetDescription";
|
|
946
|
+
var SheetBody = ({
|
|
947
|
+
className,
|
|
948
|
+
...props
|
|
949
|
+
}) => /* @__PURE__ */ jsx17(
|
|
950
|
+
"div",
|
|
951
|
+
{
|
|
952
|
+
className: cn17("flex-1 overflow-y-auto px-6 py-4", className),
|
|
953
|
+
...props
|
|
954
|
+
}
|
|
955
|
+
);
|
|
956
|
+
var SheetFooter = ({
|
|
957
|
+
className,
|
|
958
|
+
...props
|
|
959
|
+
}) => /* @__PURE__ */ jsx17(
|
|
960
|
+
"div",
|
|
961
|
+
{
|
|
962
|
+
className: cn17(
|
|
963
|
+
"shrink-0 border-t px-6 py-4 flex justify-end gap-2",
|
|
964
|
+
className
|
|
965
|
+
),
|
|
966
|
+
...props
|
|
967
|
+
}
|
|
968
|
+
);
|
|
969
|
+
|
|
970
|
+
// src/simple-group.tsx
|
|
971
|
+
import { cn as cn18 } from "@petrarca/sonnet-core";
|
|
972
|
+
import { jsx as jsx18 } from "react/jsx-runtime";
|
|
973
|
+
var gapMap = {
|
|
974
|
+
0: "gap-0",
|
|
975
|
+
xs: "gap-1",
|
|
976
|
+
sm: "gap-2",
|
|
977
|
+
md: "gap-4",
|
|
978
|
+
lg: "gap-6",
|
|
979
|
+
xl: "gap-8",
|
|
980
|
+
2: "gap-0.5",
|
|
981
|
+
4: "gap-1",
|
|
982
|
+
6: "gap-1.5",
|
|
983
|
+
8: "gap-2",
|
|
984
|
+
24: "gap-6"
|
|
985
|
+
};
|
|
986
|
+
var alignMap = {
|
|
987
|
+
start: "items-start",
|
|
988
|
+
center: "items-center",
|
|
989
|
+
end: "items-end",
|
|
990
|
+
stretch: "items-stretch",
|
|
991
|
+
"flex-start": "items-start",
|
|
992
|
+
"flex-end": "items-end"
|
|
993
|
+
};
|
|
994
|
+
var justifyMap = {
|
|
995
|
+
start: "justify-start",
|
|
996
|
+
center: "justify-center",
|
|
997
|
+
end: "justify-end",
|
|
998
|
+
between: "justify-between",
|
|
999
|
+
around: "justify-around",
|
|
1000
|
+
"space-between": "justify-between",
|
|
1001
|
+
"flex-start": "justify-start",
|
|
1002
|
+
"flex-end": "justify-end"
|
|
1003
|
+
};
|
|
1004
|
+
var wrapMap = {
|
|
1005
|
+
wrap: "flex-wrap",
|
|
1006
|
+
nowrap: "flex-nowrap",
|
|
1007
|
+
"wrap-reverse": "flex-wrap-reverse"
|
|
1008
|
+
};
|
|
1009
|
+
var mtMap = {
|
|
1010
|
+
xs: "mt-1",
|
|
1011
|
+
sm: "mt-2",
|
|
1012
|
+
md: "mt-4",
|
|
1013
|
+
lg: "mt-6",
|
|
1014
|
+
xl: "mt-8"
|
|
1015
|
+
};
|
|
1016
|
+
function SimpleGroup({
|
|
1017
|
+
children,
|
|
1018
|
+
gap = "md",
|
|
1019
|
+
align,
|
|
1020
|
+
justify,
|
|
1021
|
+
wrap,
|
|
1022
|
+
grow = false,
|
|
1023
|
+
className,
|
|
1024
|
+
style,
|
|
1025
|
+
mt,
|
|
1026
|
+
onClick
|
|
1027
|
+
}) {
|
|
1028
|
+
const gapClass = gapMap[gap] || gapMap.md;
|
|
1029
|
+
const alignClass = align ? alignMap[align] : void 0;
|
|
1030
|
+
const justifyClass = justify ? justifyMap[justify] : void 0;
|
|
1031
|
+
const wrapClass = wrap ? wrapMap[wrap] : void 0;
|
|
1032
|
+
const growClass = grow ? "[&>*]:flex-1" : void 0;
|
|
1033
|
+
const mtClass = mt ? mtMap[mt] : void 0;
|
|
1034
|
+
return /* @__PURE__ */ jsx18(
|
|
1035
|
+
"div",
|
|
1036
|
+
{
|
|
1037
|
+
className: cn18(
|
|
1038
|
+
"flex flex-row",
|
|
1039
|
+
gapClass,
|
|
1040
|
+
alignClass,
|
|
1041
|
+
justifyClass,
|
|
1042
|
+
wrapClass,
|
|
1043
|
+
growClass,
|
|
1044
|
+
mtClass,
|
|
1045
|
+
className
|
|
1046
|
+
),
|
|
1047
|
+
style,
|
|
1048
|
+
onClick,
|
|
1049
|
+
children
|
|
1050
|
+
}
|
|
1051
|
+
);
|
|
1052
|
+
}
|
|
1053
|
+
|
|
1054
|
+
// src/simple-stack.tsx
|
|
1055
|
+
import { cn as cn19 } from "@petrarca/sonnet-core";
|
|
1056
|
+
import { jsx as jsx19 } from "react/jsx-runtime";
|
|
1057
|
+
var gapMap2 = {
|
|
1058
|
+
0: "gap-0",
|
|
1059
|
+
xs: "gap-1",
|
|
1060
|
+
sm: "gap-2",
|
|
1061
|
+
md: "gap-4",
|
|
1062
|
+
lg: "gap-6",
|
|
1063
|
+
xl: "gap-8",
|
|
1064
|
+
2: "gap-0.5",
|
|
1065
|
+
4: "gap-1",
|
|
1066
|
+
6: "gap-1.5",
|
|
1067
|
+
8: "gap-2",
|
|
1068
|
+
24: "gap-6"
|
|
1069
|
+
};
|
|
1070
|
+
var alignMap2 = {
|
|
1071
|
+
start: "items-start",
|
|
1072
|
+
center: "items-center",
|
|
1073
|
+
end: "items-end",
|
|
1074
|
+
stretch: "items-stretch"
|
|
1075
|
+
};
|
|
1076
|
+
var justifyMap2 = {
|
|
1077
|
+
start: "justify-start",
|
|
1078
|
+
center: "justify-center",
|
|
1079
|
+
end: "justify-end",
|
|
1080
|
+
between: "justify-between",
|
|
1081
|
+
around: "justify-around"
|
|
1082
|
+
};
|
|
1083
|
+
function SimpleStack({
|
|
1084
|
+
children,
|
|
1085
|
+
gap = "md",
|
|
1086
|
+
align,
|
|
1087
|
+
justify,
|
|
1088
|
+
className,
|
|
1089
|
+
style,
|
|
1090
|
+
h
|
|
1091
|
+
}) {
|
|
1092
|
+
const gapClass = gapMap2[gap] || gapMap2.md;
|
|
1093
|
+
const alignClass = align ? alignMap2[align] : void 0;
|
|
1094
|
+
const justifyClass = justify ? justifyMap2[justify] : void 0;
|
|
1095
|
+
const heightStyle = h !== void 0 ? { height: typeof h === "number" ? `${h}px` : h } : void 0;
|
|
1096
|
+
return /* @__PURE__ */ jsx19(
|
|
1097
|
+
"div",
|
|
1098
|
+
{
|
|
1099
|
+
className: cn19(
|
|
1100
|
+
"flex flex-col",
|
|
1101
|
+
gapClass,
|
|
1102
|
+
alignClass,
|
|
1103
|
+
justifyClass,
|
|
1104
|
+
className
|
|
1105
|
+
),
|
|
1106
|
+
style: { ...heightStyle, ...style },
|
|
1107
|
+
children
|
|
1108
|
+
}
|
|
1109
|
+
);
|
|
1110
|
+
}
|
|
1111
|
+
|
|
1112
|
+
// src/tooltip.tsx
|
|
1113
|
+
import * as React16 from "react";
|
|
1114
|
+
import * as TooltipPrimitive from "@radix-ui/react-tooltip";
|
|
1115
|
+
import { cn as cn20 } from "@petrarca/sonnet-core";
|
|
1116
|
+
import { jsx as jsx20 } from "react/jsx-runtime";
|
|
1117
|
+
var TooltipProvider = TooltipPrimitive.Provider;
|
|
1118
|
+
var Tooltip = TooltipPrimitive.Root;
|
|
1119
|
+
var TooltipTrigger = TooltipPrimitive.Trigger;
|
|
1120
|
+
var TooltipContent = React16.forwardRef(({ className, sideOffset = 4, ...props }, ref) => /* @__PURE__ */ jsx20(
|
|
1121
|
+
TooltipPrimitive.Content,
|
|
1122
|
+
{
|
|
1123
|
+
ref,
|
|
1124
|
+
sideOffset,
|
|
1125
|
+
className: cn20(
|
|
1126
|
+
"z-50 overflow-hidden rounded-md border bg-popover px-3 py-1.5 text-sm text-popover-foreground shadow-md animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-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 origin-[--radix-tooltip-content-transform-origin]",
|
|
1127
|
+
className
|
|
1128
|
+
),
|
|
1129
|
+
...props
|
|
1130
|
+
}
|
|
1131
|
+
));
|
|
1132
|
+
TooltipContent.displayName = TooltipPrimitive.Content.displayName;
|
|
1133
|
+
|
|
1134
|
+
// src/simple-tooltip.tsx
|
|
1135
|
+
import { jsx as jsx21, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
1136
|
+
function SimpleTooltip({
|
|
1137
|
+
label,
|
|
1138
|
+
children,
|
|
1139
|
+
side,
|
|
1140
|
+
align,
|
|
1141
|
+
delayDuration = 700,
|
|
1142
|
+
sideOffset
|
|
1143
|
+
}) {
|
|
1144
|
+
return /* @__PURE__ */ jsx21(TooltipProvider, { delayDuration, children: /* @__PURE__ */ jsxs6(Tooltip, { children: [
|
|
1145
|
+
/* @__PURE__ */ jsx21(TooltipTrigger, { asChild: true, children }),
|
|
1146
|
+
/* @__PURE__ */ jsx21(TooltipContent, { side, align, sideOffset, children: label })
|
|
1147
|
+
] }) });
|
|
1148
|
+
}
|
|
1149
|
+
|
|
1150
|
+
// src/skeleton.tsx
|
|
1151
|
+
import { cn as cn21 } from "@petrarca/sonnet-core";
|
|
1152
|
+
import { jsx as jsx22 } from "react/jsx-runtime";
|
|
1153
|
+
function Skeleton({
|
|
1154
|
+
className,
|
|
1155
|
+
...props
|
|
1156
|
+
}) {
|
|
1157
|
+
return /* @__PURE__ */ jsx22(
|
|
1158
|
+
"div",
|
|
1159
|
+
{
|
|
1160
|
+
className: cn21("animate-pulse rounded-md bg-muted", className),
|
|
1161
|
+
...props
|
|
1162
|
+
}
|
|
1163
|
+
);
|
|
1164
|
+
}
|
|
1165
|
+
|
|
1166
|
+
// src/spinner.tsx
|
|
1167
|
+
import { cn as cn22 } from "@petrarca/sonnet-core";
|
|
1168
|
+
import { jsx as jsx23 } from "react/jsx-runtime";
|
|
1169
|
+
var sizeClasses = {
|
|
1170
|
+
sm: "h-4 w-4 border-2",
|
|
1171
|
+
md: "h-8 w-8 border-2",
|
|
1172
|
+
lg: "h-12 w-12 border-3"
|
|
1173
|
+
};
|
|
1174
|
+
function Spinner({ className, size = "md" }) {
|
|
1175
|
+
return /* @__PURE__ */ jsx23(
|
|
1176
|
+
"div",
|
|
1177
|
+
{
|
|
1178
|
+
className: cn22(
|
|
1179
|
+
"inline-block animate-spin rounded-full border-solid border-current border-r-transparent align-[-0.125em] motion-reduce:animate-[spin_1.5s_linear_infinite]",
|
|
1180
|
+
sizeClasses[size],
|
|
1181
|
+
className
|
|
1182
|
+
),
|
|
1183
|
+
role: "status",
|
|
1184
|
+
children: /* @__PURE__ */ jsx23("span", { className: "!absolute !-m-px !h-px !w-px !overflow-hidden !whitespace-nowrap !border-0 !p-0 ![clip:rect(0,0,0,0)]", children: "Loading..." })
|
|
1185
|
+
}
|
|
1186
|
+
);
|
|
1187
|
+
}
|
|
1188
|
+
|
|
1189
|
+
// src/tabs.tsx
|
|
1190
|
+
import * as React17 from "react";
|
|
1191
|
+
import * as TabsPrimitive from "@radix-ui/react-tabs";
|
|
1192
|
+
import { cn as cn23 } from "@petrarca/sonnet-core";
|
|
1193
|
+
import { jsx as jsx24 } from "react/jsx-runtime";
|
|
1194
|
+
var Tabs = TabsPrimitive.Root;
|
|
1195
|
+
var TabsList = React17.forwardRef(({ className, align = "start", ...props }, ref) => {
|
|
1196
|
+
const alignClass = align === "center" ? "justify-center" : align === "end" ? "justify-end" : "justify-start";
|
|
1197
|
+
return /* @__PURE__ */ jsx24(
|
|
1198
|
+
TabsPrimitive.List,
|
|
1199
|
+
{
|
|
1200
|
+
ref,
|
|
1201
|
+
className: cn23(
|
|
1202
|
+
"inline-flex h-10 items-center rounded-md bg-muted p-1 text-muted-foreground",
|
|
1203
|
+
alignClass,
|
|
1204
|
+
className
|
|
1205
|
+
),
|
|
1206
|
+
...props
|
|
1207
|
+
}
|
|
1208
|
+
);
|
|
1209
|
+
});
|
|
1210
|
+
TabsList.displayName = TabsPrimitive.List.displayName;
|
|
1211
|
+
var TabsTrigger = React17.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx24(
|
|
1212
|
+
TabsPrimitive.Trigger,
|
|
1213
|
+
{
|
|
1214
|
+
ref,
|
|
1215
|
+
className: cn23(
|
|
1216
|
+
"inline-flex items-center justify-center whitespace-nowrap rounded-sm px-3 py-1.5 text-sm font-medium ring-offset-background transition-all focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 data-[state=active]:bg-background data-[state=active]:text-foreground data-[state=active]:shadow-sm",
|
|
1217
|
+
className
|
|
1218
|
+
),
|
|
1219
|
+
...props
|
|
1220
|
+
}
|
|
1221
|
+
));
|
|
1222
|
+
TabsTrigger.displayName = TabsPrimitive.Trigger.displayName;
|
|
1223
|
+
var TabsContent = React17.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx24(
|
|
1224
|
+
TabsPrimitive.Content,
|
|
1225
|
+
{
|
|
1226
|
+
ref,
|
|
1227
|
+
className: cn23(
|
|
1228
|
+
"mt-2 ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2",
|
|
1229
|
+
"data-[state=inactive]:hidden",
|
|
1230
|
+
className
|
|
1231
|
+
),
|
|
1232
|
+
...props
|
|
1233
|
+
}
|
|
1234
|
+
));
|
|
1235
|
+
TabsContent.displayName = TabsPrimitive.Content.displayName;
|
|
1236
|
+
|
|
1237
|
+
// src/stepper.tsx
|
|
1238
|
+
import { Check as Check3 } from "lucide-react";
|
|
1239
|
+
import {
|
|
1240
|
+
Direction as DirectionPrimitive,
|
|
1241
|
+
Slot as SlotPrimitive
|
|
1242
|
+
} from "radix-ui";
|
|
1243
|
+
import * as React21 from "react";
|
|
1244
|
+
import { useComposedRefs } from "@petrarca/sonnet-core";
|
|
1245
|
+
import { cn as cn24 } from "@petrarca/sonnet-core";
|
|
1246
|
+
|
|
1247
|
+
// src/radix/hooks/use-as-ref.ts
|
|
1248
|
+
import * as React19 from "react";
|
|
1249
|
+
|
|
1250
|
+
// src/radix/hooks/use-isomorphic-layout-effect.ts
|
|
1251
|
+
import * as React18 from "react";
|
|
1252
|
+
var useIsomorphicLayoutEffect = typeof window !== "undefined" ? React18.useLayoutEffect : React18.useEffect;
|
|
1253
|
+
|
|
1254
|
+
// src/radix/hooks/use-as-ref.ts
|
|
1255
|
+
function useAsRef(props) {
|
|
1256
|
+
const ref = React19.useRef(props);
|
|
1257
|
+
useIsomorphicLayoutEffect(() => {
|
|
1258
|
+
ref.current = props;
|
|
1259
|
+
});
|
|
1260
|
+
return ref;
|
|
1261
|
+
}
|
|
1262
|
+
|
|
1263
|
+
// src/radix/hooks/use-lazy-ref.ts
|
|
1264
|
+
import * as React20 from "react";
|
|
1265
|
+
function useLazyRef(fn) {
|
|
1266
|
+
const ref = React20.useRef(null);
|
|
1267
|
+
if (ref.current === null) {
|
|
1268
|
+
ref.current = fn();
|
|
1269
|
+
}
|
|
1270
|
+
return ref;
|
|
1271
|
+
}
|
|
1272
|
+
|
|
1273
|
+
// src/stepper.tsx
|
|
1274
|
+
import { jsx as jsx25 } from "react/jsx-runtime";
|
|
1275
|
+
var ROOT_NAME = "Stepper";
|
|
1276
|
+
var LIST_NAME = "StepperList";
|
|
1277
|
+
var ITEM_NAME = "StepperItem";
|
|
1278
|
+
var TRIGGER_NAME = "StepperTrigger";
|
|
1279
|
+
var INDICATOR_NAME = "StepperIndicator";
|
|
1280
|
+
var SEPARATOR_NAME = "StepperSeparator";
|
|
1281
|
+
var TITLE_NAME = "StepperTitle";
|
|
1282
|
+
var DESCRIPTION_NAME = "StepperDescription";
|
|
1283
|
+
var CONTENT_NAME = "StepperContent";
|
|
1284
|
+
var PREV_NAME = "StepperPrev";
|
|
1285
|
+
var NEXT_NAME = "StepperNext";
|
|
1286
|
+
var ENTRY_FOCUS = "stepperFocusGroup.onEntryFocus";
|
|
1287
|
+
var EVENT_OPTIONS = { bubbles: false, cancelable: true };
|
|
1288
|
+
var ARROW_KEYS = ["ArrowUp", "ArrowDown", "ArrowLeft", "ArrowRight"];
|
|
1289
|
+
function getId(id, variant, value) {
|
|
1290
|
+
return `${id}-${variant}-${value}`;
|
|
1291
|
+
}
|
|
1292
|
+
var MAP_KEY_TO_FOCUS_INTENT = {
|
|
1293
|
+
ArrowLeft: "prev",
|
|
1294
|
+
ArrowUp: "prev",
|
|
1295
|
+
ArrowRight: "next",
|
|
1296
|
+
ArrowDown: "next",
|
|
1297
|
+
PageUp: "first",
|
|
1298
|
+
Home: "first",
|
|
1299
|
+
PageDown: "last",
|
|
1300
|
+
End: "last"
|
|
1301
|
+
};
|
|
1302
|
+
function getDirectionAwareKey(key, dir) {
|
|
1303
|
+
if (dir !== "rtl") return key;
|
|
1304
|
+
return key === "ArrowLeft" ? "ArrowRight" : key === "ArrowRight" ? "ArrowLeft" : key;
|
|
1305
|
+
}
|
|
1306
|
+
function getFocusIntent(event, dir, orientation) {
|
|
1307
|
+
const key = getDirectionAwareKey(event.key, dir);
|
|
1308
|
+
if (orientation === "horizontal" && ["ArrowUp", "ArrowDown"].includes(key))
|
|
1309
|
+
return void 0;
|
|
1310
|
+
if (orientation === "vertical" && ["ArrowLeft", "ArrowRight"].includes(key))
|
|
1311
|
+
return void 0;
|
|
1312
|
+
return MAP_KEY_TO_FOCUS_INTENT[key];
|
|
1313
|
+
}
|
|
1314
|
+
function focusFirst(candidates, preventScroll = false) {
|
|
1315
|
+
const PREVIOUSLY_FOCUSED_ELEMENT = document.activeElement;
|
|
1316
|
+
for (const candidateRef of candidates) {
|
|
1317
|
+
const candidate = candidateRef.current;
|
|
1318
|
+
if (!candidate) continue;
|
|
1319
|
+
if (candidate === PREVIOUSLY_FOCUSED_ELEMENT) return;
|
|
1320
|
+
candidate.focus({ preventScroll });
|
|
1321
|
+
if (document.activeElement !== PREVIOUSLY_FOCUSED_ELEMENT) return;
|
|
1322
|
+
}
|
|
1323
|
+
}
|
|
1324
|
+
function wrapArray(array, startIndex) {
|
|
1325
|
+
return array.map(
|
|
1326
|
+
(_, index) => array[(startIndex + index) % array.length]
|
|
1327
|
+
);
|
|
1328
|
+
}
|
|
1329
|
+
function handleActivationKeys(event, nonInteractive, activationMode, isDisabled, triggerRef) {
|
|
1330
|
+
if (event.key === "Enter" && nonInteractive) {
|
|
1331
|
+
event.preventDefault();
|
|
1332
|
+
return true;
|
|
1333
|
+
}
|
|
1334
|
+
if ((event.key === "Enter" || event.key === " ") && activationMode === "manual" && !nonInteractive) {
|
|
1335
|
+
event.preventDefault();
|
|
1336
|
+
if (!isDisabled && triggerRef.current) {
|
|
1337
|
+
triggerRef.current.click();
|
|
1338
|
+
}
|
|
1339
|
+
return true;
|
|
1340
|
+
}
|
|
1341
|
+
return false;
|
|
1342
|
+
}
|
|
1343
|
+
function hasModifierKey(event) {
|
|
1344
|
+
return event.metaKey || event.ctrlKey || event.altKey || event.shiftKey;
|
|
1345
|
+
}
|
|
1346
|
+
function resolveFocusCandidates(items, focusIntent, currentTarget, loop) {
|
|
1347
|
+
let candidateRefs = items.map((item) => item.ref);
|
|
1348
|
+
if (focusIntent === "last") {
|
|
1349
|
+
candidateRefs.reverse();
|
|
1350
|
+
} else if (focusIntent === "prev" || focusIntent === "next") {
|
|
1351
|
+
if (focusIntent === "prev") candidateRefs.reverse();
|
|
1352
|
+
const currentIndex = candidateRefs.findIndex(
|
|
1353
|
+
(ref) => ref.current === currentTarget
|
|
1354
|
+
);
|
|
1355
|
+
candidateRefs = loop ? wrapArray(candidateRefs, currentIndex + 1) : candidateRefs.slice(currentIndex + 1);
|
|
1356
|
+
}
|
|
1357
|
+
return candidateRefs;
|
|
1358
|
+
}
|
|
1359
|
+
async function handleValidatedNavigation(store, candidateRefs, items, itemValue, value, steps) {
|
|
1360
|
+
if (!store.hasValidation() || candidateRefs.length === 0) return false;
|
|
1361
|
+
const nextRef = candidateRefs[0];
|
|
1362
|
+
const nextElement = nextRef?.current;
|
|
1363
|
+
const nextItem = items.find((item) => item.ref.current === nextElement);
|
|
1364
|
+
if (!nextItem || nextItem.value === itemValue) return false;
|
|
1365
|
+
const currentStepIndex = Array.from(steps.keys()).indexOf(value || "");
|
|
1366
|
+
const targetStepIndex = Array.from(steps.keys()).indexOf(nextItem.value);
|
|
1367
|
+
const direction = targetStepIndex > currentStepIndex ? "next" : "prev";
|
|
1368
|
+
if (direction === "next") {
|
|
1369
|
+
const isValid = await store.setStateWithValidation(
|
|
1370
|
+
nextItem.value,
|
|
1371
|
+
direction
|
|
1372
|
+
);
|
|
1373
|
+
if (!isValid) return true;
|
|
1374
|
+
} else {
|
|
1375
|
+
store.setState("value", nextItem.value);
|
|
1376
|
+
}
|
|
1377
|
+
queueMicrotask(() => nextElement?.focus());
|
|
1378
|
+
return true;
|
|
1379
|
+
}
|
|
1380
|
+
function getDataState(value, itemValue, stepState, steps, variant = "item") {
|
|
1381
|
+
const stepKeys = Array.from(steps.keys());
|
|
1382
|
+
const currentIndex = stepKeys.indexOf(itemValue);
|
|
1383
|
+
if (stepState?.completed) return "completed";
|
|
1384
|
+
if (value === itemValue) {
|
|
1385
|
+
return variant === "separator" ? "inactive" : "active";
|
|
1386
|
+
}
|
|
1387
|
+
if (value) {
|
|
1388
|
+
const activeIndex = stepKeys.indexOf(value);
|
|
1389
|
+
if (activeIndex > currentIndex) return "completed";
|
|
1390
|
+
}
|
|
1391
|
+
return "inactive";
|
|
1392
|
+
}
|
|
1393
|
+
var StoreContext = React21.createContext(null);
|
|
1394
|
+
function useStoreContext(consumerName) {
|
|
1395
|
+
const context = React21.useContext(StoreContext);
|
|
1396
|
+
if (!context) {
|
|
1397
|
+
throw new Error(`\`${consumerName}\` must be used within \`${ROOT_NAME}\``);
|
|
1398
|
+
}
|
|
1399
|
+
return context;
|
|
1400
|
+
}
|
|
1401
|
+
function useStore(selector) {
|
|
1402
|
+
const store = useStoreContext("useStore");
|
|
1403
|
+
const getSnapshot = React21.useCallback(
|
|
1404
|
+
() => selector(store.getState()),
|
|
1405
|
+
[store, selector]
|
|
1406
|
+
);
|
|
1407
|
+
return React21.useSyncExternalStore(store.subscribe, getSnapshot, getSnapshot);
|
|
1408
|
+
}
|
|
1409
|
+
var StepperContext = React21.createContext(null);
|
|
1410
|
+
function useStepperContext(consumerName) {
|
|
1411
|
+
const context = React21.useContext(StepperContext);
|
|
1412
|
+
if (!context) {
|
|
1413
|
+
throw new Error(`\`${consumerName}\` must be used within \`${ROOT_NAME}\``);
|
|
1414
|
+
}
|
|
1415
|
+
return context;
|
|
1416
|
+
}
|
|
1417
|
+
function Stepper(props) {
|
|
1418
|
+
const {
|
|
1419
|
+
value,
|
|
1420
|
+
defaultValue,
|
|
1421
|
+
onValueChange,
|
|
1422
|
+
onValueComplete,
|
|
1423
|
+
onValueAdd,
|
|
1424
|
+
onValueRemove,
|
|
1425
|
+
onValidate,
|
|
1426
|
+
dir: dirProp,
|
|
1427
|
+
orientation = "horizontal",
|
|
1428
|
+
activationMode = "automatic",
|
|
1429
|
+
asChild,
|
|
1430
|
+
disabled = false,
|
|
1431
|
+
nonInteractive = false,
|
|
1432
|
+
loop = false,
|
|
1433
|
+
className,
|
|
1434
|
+
id,
|
|
1435
|
+
...rootProps
|
|
1436
|
+
} = props;
|
|
1437
|
+
const listenersRef = useLazyRef(() => /* @__PURE__ */ new Set());
|
|
1438
|
+
const stateRef = useLazyRef(() => ({
|
|
1439
|
+
steps: /* @__PURE__ */ new Map(),
|
|
1440
|
+
value: value ?? defaultValue ?? ""
|
|
1441
|
+
}));
|
|
1442
|
+
const propsRef = useAsRef({
|
|
1443
|
+
onValueChange,
|
|
1444
|
+
onValueComplete,
|
|
1445
|
+
onValueAdd,
|
|
1446
|
+
onValueRemove,
|
|
1447
|
+
onValidate
|
|
1448
|
+
});
|
|
1449
|
+
const store = React21.useMemo(() => {
|
|
1450
|
+
return {
|
|
1451
|
+
subscribe: (cb) => {
|
|
1452
|
+
listenersRef.current.add(cb);
|
|
1453
|
+
return () => listenersRef.current.delete(cb);
|
|
1454
|
+
},
|
|
1455
|
+
getState: () => stateRef.current,
|
|
1456
|
+
setState: (key, value2) => {
|
|
1457
|
+
if (Object.is(stateRef.current[key], value2)) return;
|
|
1458
|
+
if (key === "value" && typeof value2 === "string") {
|
|
1459
|
+
stateRef.current.value = value2;
|
|
1460
|
+
propsRef.current.onValueChange?.(value2);
|
|
1461
|
+
} else {
|
|
1462
|
+
stateRef.current[key] = value2;
|
|
1463
|
+
}
|
|
1464
|
+
store.notify();
|
|
1465
|
+
},
|
|
1466
|
+
setStateWithValidation: async (value2, direction) => {
|
|
1467
|
+
if (!propsRef.current.onValidate) {
|
|
1468
|
+
store.setState("value", value2);
|
|
1469
|
+
return true;
|
|
1470
|
+
}
|
|
1471
|
+
try {
|
|
1472
|
+
const isValid = await propsRef.current.onValidate(value2, direction);
|
|
1473
|
+
if (isValid) {
|
|
1474
|
+
store.setState("value", value2);
|
|
1475
|
+
}
|
|
1476
|
+
return isValid;
|
|
1477
|
+
} catch {
|
|
1478
|
+
return false;
|
|
1479
|
+
}
|
|
1480
|
+
},
|
|
1481
|
+
hasValidation: () => !!propsRef.current.onValidate,
|
|
1482
|
+
addStep: (value2, completed, disabled2) => {
|
|
1483
|
+
const newStep = { value: value2, completed, disabled: disabled2 };
|
|
1484
|
+
stateRef.current.steps.set(value2, newStep);
|
|
1485
|
+
propsRef.current.onValueAdd?.(value2);
|
|
1486
|
+
store.notify();
|
|
1487
|
+
},
|
|
1488
|
+
removeStep: (value2) => {
|
|
1489
|
+
stateRef.current.steps.delete(value2);
|
|
1490
|
+
propsRef.current.onValueRemove?.(value2);
|
|
1491
|
+
store.notify();
|
|
1492
|
+
},
|
|
1493
|
+
setStep: (value2, completed, disabled2) => {
|
|
1494
|
+
const step = stateRef.current.steps.get(value2);
|
|
1495
|
+
if (step) {
|
|
1496
|
+
const updatedStep = { ...step, completed, disabled: disabled2 };
|
|
1497
|
+
stateRef.current.steps.set(value2, updatedStep);
|
|
1498
|
+
if (completed !== step.completed) {
|
|
1499
|
+
propsRef.current.onValueComplete?.(value2, completed);
|
|
1500
|
+
}
|
|
1501
|
+
store.notify();
|
|
1502
|
+
}
|
|
1503
|
+
},
|
|
1504
|
+
notify: () => {
|
|
1505
|
+
for (const cb of listenersRef.current) {
|
|
1506
|
+
cb();
|
|
1507
|
+
}
|
|
1508
|
+
}
|
|
1509
|
+
};
|
|
1510
|
+
}, [listenersRef, stateRef, propsRef]);
|
|
1511
|
+
useIsomorphicLayoutEffect(() => {
|
|
1512
|
+
if (value !== void 0) {
|
|
1513
|
+
store.setState("value", value);
|
|
1514
|
+
}
|
|
1515
|
+
}, [value]);
|
|
1516
|
+
const dir = DirectionPrimitive.useDirection(dirProp);
|
|
1517
|
+
const instanceId = React21.useId();
|
|
1518
|
+
const rootId = id ?? instanceId;
|
|
1519
|
+
const contextValue = React21.useMemo(
|
|
1520
|
+
() => ({
|
|
1521
|
+
rootId,
|
|
1522
|
+
dir,
|
|
1523
|
+
orientation,
|
|
1524
|
+
activationMode,
|
|
1525
|
+
disabled,
|
|
1526
|
+
nonInteractive,
|
|
1527
|
+
loop
|
|
1528
|
+
}),
|
|
1529
|
+
[rootId, dir, orientation, activationMode, disabled, nonInteractive, loop]
|
|
1530
|
+
);
|
|
1531
|
+
const RootPrimitive = asChild ? SlotPrimitive.Slot : "div";
|
|
1532
|
+
return /* @__PURE__ */ jsx25(StoreContext.Provider, { value: store, children: /* @__PURE__ */ jsx25(StepperContext.Provider, { value: contextValue, children: /* @__PURE__ */ jsx25(
|
|
1533
|
+
RootPrimitive,
|
|
1534
|
+
{
|
|
1535
|
+
id: rootId,
|
|
1536
|
+
"data-disabled": disabled ? "" : void 0,
|
|
1537
|
+
"data-orientation": orientation,
|
|
1538
|
+
"data-slot": "stepper",
|
|
1539
|
+
dir,
|
|
1540
|
+
...rootProps,
|
|
1541
|
+
className: cn24(
|
|
1542
|
+
"flex gap-6",
|
|
1543
|
+
orientation === "horizontal" ? "w-full flex-col" : "flex-row",
|
|
1544
|
+
className
|
|
1545
|
+
)
|
|
1546
|
+
}
|
|
1547
|
+
) }) });
|
|
1548
|
+
}
|
|
1549
|
+
var FocusContext = React21.createContext(null);
|
|
1550
|
+
function useFocusContext(consumerName) {
|
|
1551
|
+
const context = React21.useContext(FocusContext);
|
|
1552
|
+
if (!context) {
|
|
1553
|
+
throw new Error(
|
|
1554
|
+
`\`${consumerName}\` must be used within \`FocusProvider\``
|
|
1555
|
+
);
|
|
1556
|
+
}
|
|
1557
|
+
return context;
|
|
1558
|
+
}
|
|
1559
|
+
function StepperList(props) {
|
|
1560
|
+
const {
|
|
1561
|
+
asChild,
|
|
1562
|
+
onBlur: onBlurProp,
|
|
1563
|
+
onFocus: onFocusProp,
|
|
1564
|
+
onMouseDown: onMouseDownProp,
|
|
1565
|
+
className,
|
|
1566
|
+
children,
|
|
1567
|
+
ref,
|
|
1568
|
+
...listProps
|
|
1569
|
+
} = props;
|
|
1570
|
+
const context = useStepperContext(LIST_NAME);
|
|
1571
|
+
const orientation = context.orientation;
|
|
1572
|
+
const currentValue = useStore((state) => state.value);
|
|
1573
|
+
const propsRef = useAsRef({
|
|
1574
|
+
onBlur: onBlurProp,
|
|
1575
|
+
onFocus: onFocusProp,
|
|
1576
|
+
onMouseDown: onMouseDownProp
|
|
1577
|
+
});
|
|
1578
|
+
const [tabStopId, setTabStopId] = React21.useState(null);
|
|
1579
|
+
const [isTabbingBackOut, setIsTabbingBackOut] = React21.useState(false);
|
|
1580
|
+
const [focusableItemCount, setFocusableItemCount] = React21.useState(0);
|
|
1581
|
+
const isClickFocusRef = React21.useRef(false);
|
|
1582
|
+
const itemsRef = React21.useRef(/* @__PURE__ */ new Map());
|
|
1583
|
+
const listRef = React21.useRef(null);
|
|
1584
|
+
const composedRef = useComposedRefs(ref, listRef);
|
|
1585
|
+
const onItemFocus = React21.useCallback((tabStopId2) => {
|
|
1586
|
+
setTabStopId(tabStopId2);
|
|
1587
|
+
}, []);
|
|
1588
|
+
const onItemShiftTab = React21.useCallback(() => {
|
|
1589
|
+
setIsTabbingBackOut(true);
|
|
1590
|
+
}, []);
|
|
1591
|
+
const onFocusableItemAdd = React21.useCallback(() => {
|
|
1592
|
+
setFocusableItemCount((prevCount) => prevCount + 1);
|
|
1593
|
+
}, []);
|
|
1594
|
+
const onFocusableItemRemove = React21.useCallback(() => {
|
|
1595
|
+
setFocusableItemCount((prevCount) => prevCount - 1);
|
|
1596
|
+
}, []);
|
|
1597
|
+
const onItemRegister = React21.useCallback((item) => {
|
|
1598
|
+
itemsRef.current.set(item.id, item);
|
|
1599
|
+
}, []);
|
|
1600
|
+
const onItemUnregister = React21.useCallback((id) => {
|
|
1601
|
+
itemsRef.current.delete(id);
|
|
1602
|
+
}, []);
|
|
1603
|
+
const getItems = React21.useCallback(() => {
|
|
1604
|
+
return Array.from(itemsRef.current.values()).filter((item) => item.ref.current).sort((a, b) => {
|
|
1605
|
+
const elementA = a.ref.current;
|
|
1606
|
+
const elementB = b.ref.current;
|
|
1607
|
+
if (!elementA || !elementB) return 0;
|
|
1608
|
+
const position = elementA.compareDocumentPosition(elementB);
|
|
1609
|
+
if (position & Node.DOCUMENT_POSITION_FOLLOWING) {
|
|
1610
|
+
return -1;
|
|
1611
|
+
}
|
|
1612
|
+
if (position & Node.DOCUMENT_POSITION_PRECEDING) {
|
|
1613
|
+
return 1;
|
|
1614
|
+
}
|
|
1615
|
+
return 0;
|
|
1616
|
+
});
|
|
1617
|
+
}, []);
|
|
1618
|
+
const onBlur = React21.useCallback(
|
|
1619
|
+
(event) => {
|
|
1620
|
+
propsRef.current.onBlur?.(event);
|
|
1621
|
+
if (event.defaultPrevented) return;
|
|
1622
|
+
setIsTabbingBackOut(false);
|
|
1623
|
+
},
|
|
1624
|
+
[propsRef]
|
|
1625
|
+
);
|
|
1626
|
+
const onFocus = React21.useCallback(
|
|
1627
|
+
(event) => {
|
|
1628
|
+
propsRef.current.onFocus?.(event);
|
|
1629
|
+
if (event.defaultPrevented) return;
|
|
1630
|
+
const isKeyboardFocus = !isClickFocusRef.current;
|
|
1631
|
+
if (event.target === event.currentTarget && isKeyboardFocus && !isTabbingBackOut) {
|
|
1632
|
+
const entryFocusEvent = new CustomEvent(ENTRY_FOCUS, EVENT_OPTIONS);
|
|
1633
|
+
event.currentTarget.dispatchEvent(entryFocusEvent);
|
|
1634
|
+
if (!entryFocusEvent.defaultPrevented) {
|
|
1635
|
+
const items = Array.from(itemsRef.current.values()).filter(
|
|
1636
|
+
(item) => !item.disabled
|
|
1637
|
+
);
|
|
1638
|
+
const selectedItem = currentValue ? items.find((item) => item.value === currentValue) : void 0;
|
|
1639
|
+
const activeItem = items.find((item) => item.active);
|
|
1640
|
+
const currentItem = items.find((item) => item.id === tabStopId);
|
|
1641
|
+
const candidateItems = [
|
|
1642
|
+
selectedItem,
|
|
1643
|
+
activeItem,
|
|
1644
|
+
currentItem,
|
|
1645
|
+
...items
|
|
1646
|
+
].filter(Boolean);
|
|
1647
|
+
const candidateRefs = candidateItems.map((item) => item.ref);
|
|
1648
|
+
focusFirst(candidateRefs, false);
|
|
1649
|
+
}
|
|
1650
|
+
}
|
|
1651
|
+
isClickFocusRef.current = false;
|
|
1652
|
+
},
|
|
1653
|
+
[propsRef, isTabbingBackOut, currentValue, tabStopId]
|
|
1654
|
+
);
|
|
1655
|
+
const onMouseDown = React21.useCallback(
|
|
1656
|
+
(event) => {
|
|
1657
|
+
propsRef.current.onMouseDown?.(event);
|
|
1658
|
+
if (event.defaultPrevented) return;
|
|
1659
|
+
isClickFocusRef.current = true;
|
|
1660
|
+
},
|
|
1661
|
+
[propsRef]
|
|
1662
|
+
);
|
|
1663
|
+
const focusContextValue = React21.useMemo(
|
|
1664
|
+
() => ({
|
|
1665
|
+
tabStopId,
|
|
1666
|
+
onItemFocus,
|
|
1667
|
+
onItemShiftTab,
|
|
1668
|
+
onFocusableItemAdd,
|
|
1669
|
+
onFocusableItemRemove,
|
|
1670
|
+
onItemRegister,
|
|
1671
|
+
onItemUnregister,
|
|
1672
|
+
getItems
|
|
1673
|
+
}),
|
|
1674
|
+
[
|
|
1675
|
+
tabStopId,
|
|
1676
|
+
onItemFocus,
|
|
1677
|
+
onItemShiftTab,
|
|
1678
|
+
onFocusableItemAdd,
|
|
1679
|
+
onFocusableItemRemove,
|
|
1680
|
+
onItemRegister,
|
|
1681
|
+
onItemUnregister,
|
|
1682
|
+
getItems
|
|
1683
|
+
]
|
|
1684
|
+
);
|
|
1685
|
+
const ListPrimitive = asChild ? SlotPrimitive.Slot : "div";
|
|
1686
|
+
return /* @__PURE__ */ jsx25(FocusContext.Provider, { value: focusContextValue, children: /* @__PURE__ */ jsx25(
|
|
1687
|
+
ListPrimitive,
|
|
1688
|
+
{
|
|
1689
|
+
role: "tablist",
|
|
1690
|
+
"aria-orientation": orientation,
|
|
1691
|
+
"data-orientation": orientation,
|
|
1692
|
+
"data-slot": "stepper-list",
|
|
1693
|
+
dir: context.dir,
|
|
1694
|
+
tabIndex: isTabbingBackOut || focusableItemCount === 0 ? -1 : 0,
|
|
1695
|
+
...listProps,
|
|
1696
|
+
ref: composedRef,
|
|
1697
|
+
className: cn24(
|
|
1698
|
+
"flex outline-none",
|
|
1699
|
+
orientation === "horizontal" ? "flex-row items-center" : "flex-col items-start",
|
|
1700
|
+
className
|
|
1701
|
+
),
|
|
1702
|
+
onBlur,
|
|
1703
|
+
onFocus,
|
|
1704
|
+
onMouseDown,
|
|
1705
|
+
children
|
|
1706
|
+
}
|
|
1707
|
+
) });
|
|
1708
|
+
}
|
|
1709
|
+
var StepperItemContext = React21.createContext(
|
|
1710
|
+
null
|
|
1711
|
+
);
|
|
1712
|
+
function useStepperItemContext(consumerName) {
|
|
1713
|
+
const context = React21.useContext(StepperItemContext);
|
|
1714
|
+
if (!context) {
|
|
1715
|
+
throw new Error(`\`${consumerName}\` must be used within \`${ITEM_NAME}\``);
|
|
1716
|
+
}
|
|
1717
|
+
return context;
|
|
1718
|
+
}
|
|
1719
|
+
function StepperItem(props) {
|
|
1720
|
+
const {
|
|
1721
|
+
value: itemValue,
|
|
1722
|
+
completed = false,
|
|
1723
|
+
disabled = false,
|
|
1724
|
+
asChild,
|
|
1725
|
+
className,
|
|
1726
|
+
children,
|
|
1727
|
+
ref,
|
|
1728
|
+
...itemProps
|
|
1729
|
+
} = props;
|
|
1730
|
+
const context = useStepperContext(ITEM_NAME);
|
|
1731
|
+
const store = useStoreContext(ITEM_NAME);
|
|
1732
|
+
const orientation = context.orientation;
|
|
1733
|
+
const value = useStore((state) => state.value);
|
|
1734
|
+
useIsomorphicLayoutEffect(() => {
|
|
1735
|
+
store.addStep(itemValue, completed, disabled);
|
|
1736
|
+
return () => {
|
|
1737
|
+
store.removeStep(itemValue);
|
|
1738
|
+
};
|
|
1739
|
+
}, [itemValue, completed, disabled]);
|
|
1740
|
+
useIsomorphicLayoutEffect(() => {
|
|
1741
|
+
store.setStep(itemValue, completed, disabled);
|
|
1742
|
+
}, [itemValue, completed, disabled]);
|
|
1743
|
+
const stepState = useStore((state) => state.steps.get(itemValue));
|
|
1744
|
+
const steps = useStore((state) => state.steps);
|
|
1745
|
+
const dataState = getDataState(value, itemValue, stepState, steps);
|
|
1746
|
+
const itemContextValue = React21.useMemo(
|
|
1747
|
+
() => ({
|
|
1748
|
+
value: itemValue,
|
|
1749
|
+
stepState
|
|
1750
|
+
}),
|
|
1751
|
+
[itemValue, stepState]
|
|
1752
|
+
);
|
|
1753
|
+
const ItemPrimitive = asChild ? SlotPrimitive.Slot : "div";
|
|
1754
|
+
return /* @__PURE__ */ jsx25(StepperItemContext.Provider, { value: itemContextValue, children: /* @__PURE__ */ jsx25(
|
|
1755
|
+
ItemPrimitive,
|
|
1756
|
+
{
|
|
1757
|
+
"data-disabled": stepState?.disabled ? "" : void 0,
|
|
1758
|
+
"data-orientation": orientation,
|
|
1759
|
+
"data-state": dataState,
|
|
1760
|
+
"data-slot": "stepper-item",
|
|
1761
|
+
dir: context.dir,
|
|
1762
|
+
...itemProps,
|
|
1763
|
+
ref,
|
|
1764
|
+
className: cn24(
|
|
1765
|
+
"relative flex not-last:flex-1 items-center",
|
|
1766
|
+
orientation === "horizontal" ? "flex-row" : "flex-col",
|
|
1767
|
+
className
|
|
1768
|
+
),
|
|
1769
|
+
children
|
|
1770
|
+
}
|
|
1771
|
+
) });
|
|
1772
|
+
}
|
|
1773
|
+
function StepperTrigger(props) {
|
|
1774
|
+
const {
|
|
1775
|
+
asChild,
|
|
1776
|
+
onClick: onClickProp,
|
|
1777
|
+
onFocus: onFocusProp,
|
|
1778
|
+
onKeyDown: onKeyDownProp,
|
|
1779
|
+
onMouseDown: onMouseDownProp,
|
|
1780
|
+
disabled,
|
|
1781
|
+
className,
|
|
1782
|
+
ref,
|
|
1783
|
+
...triggerProps
|
|
1784
|
+
} = props;
|
|
1785
|
+
const context = useStepperContext(TRIGGER_NAME);
|
|
1786
|
+
const itemContext = useStepperItemContext(TRIGGER_NAME);
|
|
1787
|
+
const itemValue = itemContext.value;
|
|
1788
|
+
const store = useStoreContext(TRIGGER_NAME);
|
|
1789
|
+
const focusContext = useFocusContext(TRIGGER_NAME);
|
|
1790
|
+
const value = useStore((state) => state.value);
|
|
1791
|
+
const steps = useStore((state) => state.steps);
|
|
1792
|
+
const stepState = useStore((state) => state.steps.get(itemValue));
|
|
1793
|
+
const propsRef = useAsRef({
|
|
1794
|
+
onClick: onClickProp,
|
|
1795
|
+
onFocus: onFocusProp,
|
|
1796
|
+
onKeyDown: onKeyDownProp,
|
|
1797
|
+
onMouseDown: onMouseDownProp
|
|
1798
|
+
});
|
|
1799
|
+
const activationMode = context.activationMode;
|
|
1800
|
+
const orientation = context.orientation;
|
|
1801
|
+
const loop = context.loop;
|
|
1802
|
+
const stepIndex = Array.from(steps.keys()).indexOf(itemValue);
|
|
1803
|
+
const stepPosition = stepIndex + 1;
|
|
1804
|
+
const stepCount = steps.size;
|
|
1805
|
+
const triggerId = getId(context.rootId, "trigger", itemValue);
|
|
1806
|
+
const contentId = getId(context.rootId, "content", itemValue);
|
|
1807
|
+
const titleId = getId(context.rootId, "title", itemValue);
|
|
1808
|
+
const descriptionId = getId(context.rootId, "description", itemValue);
|
|
1809
|
+
const isDisabled = disabled || stepState?.disabled || context.disabled;
|
|
1810
|
+
const isActive = value === itemValue;
|
|
1811
|
+
const isTabStop = focusContext.tabStopId === triggerId;
|
|
1812
|
+
const dataState = getDataState(value, itemValue, stepState, steps);
|
|
1813
|
+
const triggerRef = React21.useRef(null);
|
|
1814
|
+
const composedRef = useComposedRefs(ref, triggerRef);
|
|
1815
|
+
const isArrowKeyPressedRef = React21.useRef(false);
|
|
1816
|
+
const isMouseClickRef = React21.useRef(false);
|
|
1817
|
+
React21.useEffect(() => {
|
|
1818
|
+
function onKeyDown2(event) {
|
|
1819
|
+
if (ARROW_KEYS.includes(event.key)) {
|
|
1820
|
+
isArrowKeyPressedRef.current = true;
|
|
1821
|
+
}
|
|
1822
|
+
}
|
|
1823
|
+
function onKeyUp() {
|
|
1824
|
+
isArrowKeyPressedRef.current = false;
|
|
1825
|
+
}
|
|
1826
|
+
document.addEventListener("keydown", onKeyDown2);
|
|
1827
|
+
document.addEventListener("keyup", onKeyUp);
|
|
1828
|
+
return () => {
|
|
1829
|
+
document.removeEventListener("keydown", onKeyDown2);
|
|
1830
|
+
document.removeEventListener("keyup", onKeyUp);
|
|
1831
|
+
};
|
|
1832
|
+
}, []);
|
|
1833
|
+
useIsomorphicLayoutEffect(() => {
|
|
1834
|
+
focusContext.onItemRegister({
|
|
1835
|
+
id: triggerId,
|
|
1836
|
+
ref: triggerRef,
|
|
1837
|
+
value: itemValue,
|
|
1838
|
+
active: isTabStop,
|
|
1839
|
+
disabled: !!isDisabled
|
|
1840
|
+
});
|
|
1841
|
+
if (!isDisabled) {
|
|
1842
|
+
focusContext.onFocusableItemAdd();
|
|
1843
|
+
}
|
|
1844
|
+
return () => {
|
|
1845
|
+
focusContext.onItemUnregister(triggerId);
|
|
1846
|
+
if (!isDisabled) {
|
|
1847
|
+
focusContext.onFocusableItemRemove();
|
|
1848
|
+
}
|
|
1849
|
+
};
|
|
1850
|
+
}, [focusContext, triggerId, itemValue, isTabStop, isDisabled]);
|
|
1851
|
+
const onClick = React21.useCallback(
|
|
1852
|
+
async (event) => {
|
|
1853
|
+
propsRef.current.onClick?.(event);
|
|
1854
|
+
if (event.defaultPrevented) return;
|
|
1855
|
+
if (!isDisabled && !context.nonInteractive) {
|
|
1856
|
+
const currentStepIndex = Array.from(steps.keys()).indexOf(value ?? "");
|
|
1857
|
+
const targetStepIndex = Array.from(steps.keys()).indexOf(itemValue);
|
|
1858
|
+
const direction = targetStepIndex > currentStepIndex ? "next" : "prev";
|
|
1859
|
+
await store.setStateWithValidation(itemValue, direction);
|
|
1860
|
+
}
|
|
1861
|
+
},
|
|
1862
|
+
[
|
|
1863
|
+
isDisabled,
|
|
1864
|
+
context.nonInteractive,
|
|
1865
|
+
store,
|
|
1866
|
+
itemValue,
|
|
1867
|
+
value,
|
|
1868
|
+
steps,
|
|
1869
|
+
propsRef
|
|
1870
|
+
]
|
|
1871
|
+
);
|
|
1872
|
+
const onFocus = React21.useCallback(
|
|
1873
|
+
async (event) => {
|
|
1874
|
+
propsRef.current.onFocus?.(event);
|
|
1875
|
+
if (event.defaultPrevented) return;
|
|
1876
|
+
focusContext.onItemFocus(triggerId);
|
|
1877
|
+
const isKeyboardFocus = !isMouseClickRef.current;
|
|
1878
|
+
if (!isActive && !isDisabled && activationMode !== "manual" && !context.nonInteractive && isKeyboardFocus) {
|
|
1879
|
+
const currentStepIndex = Array.from(steps.keys()).indexOf(value || "");
|
|
1880
|
+
const targetStepIndex = Array.from(steps.keys()).indexOf(itemValue);
|
|
1881
|
+
const direction = targetStepIndex > currentStepIndex ? "next" : "prev";
|
|
1882
|
+
await store.setStateWithValidation(itemValue, direction);
|
|
1883
|
+
}
|
|
1884
|
+
isMouseClickRef.current = false;
|
|
1885
|
+
},
|
|
1886
|
+
[
|
|
1887
|
+
focusContext,
|
|
1888
|
+
triggerId,
|
|
1889
|
+
activationMode,
|
|
1890
|
+
isActive,
|
|
1891
|
+
isDisabled,
|
|
1892
|
+
context.nonInteractive,
|
|
1893
|
+
store,
|
|
1894
|
+
itemValue,
|
|
1895
|
+
value,
|
|
1896
|
+
steps,
|
|
1897
|
+
propsRef
|
|
1898
|
+
]
|
|
1899
|
+
);
|
|
1900
|
+
const onKeyDown = React21.useCallback(
|
|
1901
|
+
async (event) => {
|
|
1902
|
+
propsRef.current.onKeyDown?.(event);
|
|
1903
|
+
if (event.defaultPrevented) return;
|
|
1904
|
+
if (handleActivationKeys(
|
|
1905
|
+
event,
|
|
1906
|
+
context.nonInteractive,
|
|
1907
|
+
activationMode,
|
|
1908
|
+
isDisabled,
|
|
1909
|
+
triggerRef
|
|
1910
|
+
))
|
|
1911
|
+
return;
|
|
1912
|
+
if (event.key === "Tab" && event.shiftKey) {
|
|
1913
|
+
focusContext.onItemShiftTab();
|
|
1914
|
+
return;
|
|
1915
|
+
}
|
|
1916
|
+
if (event.target !== event.currentTarget) return;
|
|
1917
|
+
const focusIntent = getFocusIntent(event, context.dir, orientation);
|
|
1918
|
+
if (focusIntent !== void 0) {
|
|
1919
|
+
if (hasModifierKey(event)) return;
|
|
1920
|
+
event.preventDefault();
|
|
1921
|
+
const items = focusContext.getItems().filter((item) => !item.disabled);
|
|
1922
|
+
const candidateRefs = resolveFocusCandidates(
|
|
1923
|
+
items,
|
|
1924
|
+
focusIntent,
|
|
1925
|
+
event.currentTarget,
|
|
1926
|
+
loop
|
|
1927
|
+
);
|
|
1928
|
+
const didNavigate = await handleValidatedNavigation(
|
|
1929
|
+
store,
|
|
1930
|
+
candidateRefs,
|
|
1931
|
+
items,
|
|
1932
|
+
itemValue,
|
|
1933
|
+
value,
|
|
1934
|
+
steps
|
|
1935
|
+
);
|
|
1936
|
+
if (didNavigate) return;
|
|
1937
|
+
queueMicrotask(() => focusFirst(candidateRefs));
|
|
1938
|
+
}
|
|
1939
|
+
},
|
|
1940
|
+
[
|
|
1941
|
+
focusContext,
|
|
1942
|
+
context.nonInteractive,
|
|
1943
|
+
context.dir,
|
|
1944
|
+
activationMode,
|
|
1945
|
+
orientation,
|
|
1946
|
+
loop,
|
|
1947
|
+
isDisabled,
|
|
1948
|
+
store,
|
|
1949
|
+
propsRef,
|
|
1950
|
+
itemValue,
|
|
1951
|
+
value,
|
|
1952
|
+
steps
|
|
1953
|
+
]
|
|
1954
|
+
);
|
|
1955
|
+
const onMouseDown = React21.useCallback(
|
|
1956
|
+
(event) => {
|
|
1957
|
+
propsRef.current.onMouseDown?.(event);
|
|
1958
|
+
if (event.defaultPrevented) return;
|
|
1959
|
+
isMouseClickRef.current = true;
|
|
1960
|
+
if (isDisabled) {
|
|
1961
|
+
event.preventDefault();
|
|
1962
|
+
} else {
|
|
1963
|
+
focusContext.onItemFocus(triggerId);
|
|
1964
|
+
}
|
|
1965
|
+
},
|
|
1966
|
+
[focusContext, triggerId, isDisabled, propsRef]
|
|
1967
|
+
);
|
|
1968
|
+
const TriggerPrimitive = asChild ? SlotPrimitive.Slot : "button";
|
|
1969
|
+
return /* @__PURE__ */ jsx25(
|
|
1970
|
+
TriggerPrimitive,
|
|
1971
|
+
{
|
|
1972
|
+
id: triggerId,
|
|
1973
|
+
role: "tab",
|
|
1974
|
+
type: "button",
|
|
1975
|
+
"aria-controls": contentId,
|
|
1976
|
+
"aria-current": isActive ? "step" : void 0,
|
|
1977
|
+
"aria-describedby": `${titleId} ${descriptionId}`,
|
|
1978
|
+
"aria-posinset": stepPosition,
|
|
1979
|
+
"aria-selected": isActive,
|
|
1980
|
+
"aria-setsize": stepCount,
|
|
1981
|
+
"data-disabled": isDisabled ? "" : void 0,
|
|
1982
|
+
"data-state": dataState,
|
|
1983
|
+
"data-slot": "stepper-trigger",
|
|
1984
|
+
disabled: isDisabled,
|
|
1985
|
+
tabIndex: isTabStop ? 0 : -1,
|
|
1986
|
+
...triggerProps,
|
|
1987
|
+
ref: composedRef,
|
|
1988
|
+
className: cn24(
|
|
1989
|
+
"inline-flex items-center justify-center gap-3 rounded-md text-left outline-none transition-all focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 disabled:pointer-events-none disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 [&_svg:not([class*='size-'])]:size-4 [&_svg]:pointer-events-none [&_svg]:shrink-0",
|
|
1990
|
+
"not-has-data-[slot=description]:rounded-full not-has-data-[slot=title]:rounded-full",
|
|
1991
|
+
className
|
|
1992
|
+
),
|
|
1993
|
+
onClick,
|
|
1994
|
+
onFocus,
|
|
1995
|
+
onKeyDown,
|
|
1996
|
+
onMouseDown
|
|
1997
|
+
}
|
|
1998
|
+
);
|
|
1999
|
+
}
|
|
2000
|
+
function StepperIndicator(props) {
|
|
2001
|
+
const { className, children, asChild, ref, ...indicatorProps } = props;
|
|
2002
|
+
const context = useStepperContext(INDICATOR_NAME);
|
|
2003
|
+
const itemContext = useStepperItemContext(INDICATOR_NAME);
|
|
2004
|
+
const value = useStore((state) => state.value);
|
|
2005
|
+
const itemValue = itemContext.value;
|
|
2006
|
+
const stepState = useStore((state) => state.steps.get(itemValue));
|
|
2007
|
+
const steps = useStore((state) => state.steps);
|
|
2008
|
+
const stepPosition = Array.from(steps.keys()).indexOf(itemValue) + 1;
|
|
2009
|
+
const dataState = getDataState(value, itemValue, stepState, steps);
|
|
2010
|
+
const IndicatorPrimitive = asChild ? SlotPrimitive.Slot : "div";
|
|
2011
|
+
return /* @__PURE__ */ jsx25(
|
|
2012
|
+
IndicatorPrimitive,
|
|
2013
|
+
{
|
|
2014
|
+
"data-state": dataState,
|
|
2015
|
+
"data-slot": "stepper-indicator",
|
|
2016
|
+
dir: context.dir,
|
|
2017
|
+
...indicatorProps,
|
|
2018
|
+
ref,
|
|
2019
|
+
className: cn24(
|
|
2020
|
+
"flex size-7 shrink-0 items-center justify-center rounded-full border-2 border-muted bg-background font-medium text-muted-foreground text-sm transition-colors data-[state=active]:border-primary data-[state=completed]:border-primary data-[state=active]:bg-primary data-[state=completed]:bg-primary data-[state=active]:text-primary-foreground data-[state=completed]:text-primary-foreground",
|
|
2021
|
+
className
|
|
2022
|
+
),
|
|
2023
|
+
children: typeof children === "function" ? children(dataState) : children ? children : dataState === "completed" ? /* @__PURE__ */ jsx25(Check3, { className: "size-4" }) : stepPosition
|
|
2024
|
+
}
|
|
2025
|
+
);
|
|
2026
|
+
}
|
|
2027
|
+
function StepperSeparator(props) {
|
|
2028
|
+
const {
|
|
2029
|
+
className,
|
|
2030
|
+
asChild,
|
|
2031
|
+
forceMount = false,
|
|
2032
|
+
ref,
|
|
2033
|
+
...separatorProps
|
|
2034
|
+
} = props;
|
|
2035
|
+
const context = useStepperContext(SEPARATOR_NAME);
|
|
2036
|
+
const itemContext = useStepperItemContext(SEPARATOR_NAME);
|
|
2037
|
+
const value = useStore((state) => state.value);
|
|
2038
|
+
const steps = useStore((state) => state.steps);
|
|
2039
|
+
const orientation = context.orientation;
|
|
2040
|
+
const stepIndex = Array.from(steps.keys()).indexOf(itemContext.value);
|
|
2041
|
+
const isLastStep = stepIndex === steps.size - 1;
|
|
2042
|
+
if (isLastStep && !forceMount) return null;
|
|
2043
|
+
const dataState = getDataState(
|
|
2044
|
+
value,
|
|
2045
|
+
itemContext.value,
|
|
2046
|
+
itemContext.stepState,
|
|
2047
|
+
steps,
|
|
2048
|
+
"separator"
|
|
2049
|
+
);
|
|
2050
|
+
const SeparatorPrimitive2 = asChild ? SlotPrimitive.Slot : "div";
|
|
2051
|
+
return /* @__PURE__ */ jsx25(
|
|
2052
|
+
SeparatorPrimitive2,
|
|
2053
|
+
{
|
|
2054
|
+
role: "separator",
|
|
2055
|
+
"aria-hidden": "true",
|
|
2056
|
+
"aria-orientation": orientation,
|
|
2057
|
+
"data-orientation": orientation,
|
|
2058
|
+
"data-state": dataState,
|
|
2059
|
+
"data-slot": "stepper-separator",
|
|
2060
|
+
dir: context.dir,
|
|
2061
|
+
...separatorProps,
|
|
2062
|
+
ref,
|
|
2063
|
+
className: cn24(
|
|
2064
|
+
"bg-border transition-colors data-[state=active]:bg-primary data-[state=completed]:bg-primary",
|
|
2065
|
+
orientation === "horizontal" ? "h-px flex-1" : "h-10 w-px",
|
|
2066
|
+
className
|
|
2067
|
+
)
|
|
2068
|
+
}
|
|
2069
|
+
);
|
|
2070
|
+
}
|
|
2071
|
+
function StepperTitle(props) {
|
|
2072
|
+
const { className, asChild, ref, ...titleProps } = props;
|
|
2073
|
+
const context = useStepperContext(TITLE_NAME);
|
|
2074
|
+
const itemContext = useStepperItemContext(TITLE_NAME);
|
|
2075
|
+
const titleId = getId(context.rootId, "title", itemContext.value);
|
|
2076
|
+
const TitlePrimitive = asChild ? SlotPrimitive.Slot : "span";
|
|
2077
|
+
return /* @__PURE__ */ jsx25(
|
|
2078
|
+
TitlePrimitive,
|
|
2079
|
+
{
|
|
2080
|
+
id: titleId,
|
|
2081
|
+
"data-slot": "title",
|
|
2082
|
+
dir: context.dir,
|
|
2083
|
+
...titleProps,
|
|
2084
|
+
ref,
|
|
2085
|
+
className: cn24("font-medium text-sm", className)
|
|
2086
|
+
}
|
|
2087
|
+
);
|
|
2088
|
+
}
|
|
2089
|
+
function StepperDescription(props) {
|
|
2090
|
+
const { className, asChild, ref, ...descriptionProps } = props;
|
|
2091
|
+
const context = useStepperContext(DESCRIPTION_NAME);
|
|
2092
|
+
const itemContext = useStepperItemContext(DESCRIPTION_NAME);
|
|
2093
|
+
const descriptionId = getId(context.rootId, "description", itemContext.value);
|
|
2094
|
+
const DescriptionPrimitive = asChild ? SlotPrimitive.Slot : "span";
|
|
2095
|
+
return /* @__PURE__ */ jsx25(
|
|
2096
|
+
DescriptionPrimitive,
|
|
2097
|
+
{
|
|
2098
|
+
id: descriptionId,
|
|
2099
|
+
"data-slot": "description",
|
|
2100
|
+
dir: context.dir,
|
|
2101
|
+
...descriptionProps,
|
|
2102
|
+
ref,
|
|
2103
|
+
className: cn24("text-muted-foreground text-xs", className)
|
|
2104
|
+
}
|
|
2105
|
+
);
|
|
2106
|
+
}
|
|
2107
|
+
function StepperContent(props) {
|
|
2108
|
+
const {
|
|
2109
|
+
value: valueProp,
|
|
2110
|
+
asChild,
|
|
2111
|
+
forceMount = false,
|
|
2112
|
+
ref,
|
|
2113
|
+
className,
|
|
2114
|
+
...contentProps
|
|
2115
|
+
} = props;
|
|
2116
|
+
const context = useStepperContext(CONTENT_NAME);
|
|
2117
|
+
const value = useStore((state) => state.value);
|
|
2118
|
+
const contentId = getId(context.rootId, "content", valueProp);
|
|
2119
|
+
const triggerId = getId(context.rootId, "trigger", valueProp);
|
|
2120
|
+
if (valueProp !== value && !forceMount) return null;
|
|
2121
|
+
const ContentPrimitive = asChild ? SlotPrimitive.Slot : "div";
|
|
2122
|
+
return /* @__PURE__ */ jsx25(
|
|
2123
|
+
ContentPrimitive,
|
|
2124
|
+
{
|
|
2125
|
+
id: contentId,
|
|
2126
|
+
role: "tabpanel",
|
|
2127
|
+
"aria-labelledby": triggerId,
|
|
2128
|
+
"data-slot": "stepper-content",
|
|
2129
|
+
dir: context.dir,
|
|
2130
|
+
...contentProps,
|
|
2131
|
+
ref,
|
|
2132
|
+
className: cn24("flex-1 outline-none", className)
|
|
2133
|
+
}
|
|
2134
|
+
);
|
|
2135
|
+
}
|
|
2136
|
+
function StepperPrev(props) {
|
|
2137
|
+
const { asChild, onClick: onClickProp, disabled, ...prevProps } = props;
|
|
2138
|
+
const store = useStoreContext(PREV_NAME);
|
|
2139
|
+
const value = useStore((state) => state.value);
|
|
2140
|
+
const steps = useStore((state) => state.steps);
|
|
2141
|
+
const propsRef = useAsRef({
|
|
2142
|
+
onClick: onClickProp
|
|
2143
|
+
});
|
|
2144
|
+
const stepKeys = Array.from(steps.keys());
|
|
2145
|
+
const currentIndex = value ? stepKeys.indexOf(value) : -1;
|
|
2146
|
+
const isDisabled = disabled || currentIndex <= 0;
|
|
2147
|
+
const onClick = React21.useCallback(
|
|
2148
|
+
async (event) => {
|
|
2149
|
+
propsRef.current.onClick?.(event);
|
|
2150
|
+
if (event.defaultPrevented || isDisabled) return;
|
|
2151
|
+
const prevIndex = Math.max(currentIndex - 1, 0);
|
|
2152
|
+
const prevStepValue = stepKeys[prevIndex];
|
|
2153
|
+
if (prevStepValue) {
|
|
2154
|
+
store.setState("value", prevStepValue);
|
|
2155
|
+
}
|
|
2156
|
+
},
|
|
2157
|
+
[propsRef, isDisabled, currentIndex, stepKeys, store]
|
|
2158
|
+
);
|
|
2159
|
+
const PrevPrimitive = asChild ? SlotPrimitive.Slot : "button";
|
|
2160
|
+
return /* @__PURE__ */ jsx25(
|
|
2161
|
+
PrevPrimitive,
|
|
2162
|
+
{
|
|
2163
|
+
type: "button",
|
|
2164
|
+
"data-slot": "stepper-prev",
|
|
2165
|
+
disabled: isDisabled,
|
|
2166
|
+
...prevProps,
|
|
2167
|
+
onClick
|
|
2168
|
+
}
|
|
2169
|
+
);
|
|
2170
|
+
}
|
|
2171
|
+
function StepperNext(props) {
|
|
2172
|
+
const { asChild, onClick: onClickProp, disabled, ...nextProps } = props;
|
|
2173
|
+
const store = useStoreContext(NEXT_NAME);
|
|
2174
|
+
const value = useStore((state) => state.value);
|
|
2175
|
+
const steps = useStore((state) => state.steps);
|
|
2176
|
+
const propsRef = useAsRef({
|
|
2177
|
+
onClick: onClickProp
|
|
2178
|
+
});
|
|
2179
|
+
const stepKeys = Array.from(steps.keys());
|
|
2180
|
+
const currentIndex = value ? stepKeys.indexOf(value) : -1;
|
|
2181
|
+
const isDisabled = disabled || currentIndex >= stepKeys.length - 1;
|
|
2182
|
+
const onClick = React21.useCallback(
|
|
2183
|
+
async (event) => {
|
|
2184
|
+
propsRef.current.onClick?.(event);
|
|
2185
|
+
if (event.defaultPrevented || isDisabled) return;
|
|
2186
|
+
const nextIndex = Math.min(currentIndex + 1, stepKeys.length - 1);
|
|
2187
|
+
const nextStepValue = stepKeys[nextIndex];
|
|
2188
|
+
if (nextStepValue) {
|
|
2189
|
+
await store.setStateWithValidation(nextStepValue, "next");
|
|
2190
|
+
}
|
|
2191
|
+
},
|
|
2192
|
+
[propsRef, isDisabled, currentIndex, stepKeys, store]
|
|
2193
|
+
);
|
|
2194
|
+
const NextPrimitive = asChild ? SlotPrimitive.Slot : "button";
|
|
2195
|
+
return /* @__PURE__ */ jsx25(
|
|
2196
|
+
NextPrimitive,
|
|
2197
|
+
{
|
|
2198
|
+
type: "button",
|
|
2199
|
+
"data-slot": "stepper-next",
|
|
2200
|
+
disabled: isDisabled,
|
|
2201
|
+
...nextProps,
|
|
2202
|
+
onClick
|
|
2203
|
+
}
|
|
2204
|
+
);
|
|
2205
|
+
}
|
|
2206
|
+
|
|
2207
|
+
// src/EntityTable/EntityTable.tsx
|
|
2208
|
+
import {
|
|
2209
|
+
useCallback as useCallback2,
|
|
2210
|
+
useEffect as useEffect3,
|
|
2211
|
+
useMemo as useMemo2,
|
|
2212
|
+
useRef as useRef4,
|
|
2213
|
+
useState as useState2
|
|
2214
|
+
} from "react";
|
|
2215
|
+
import {
|
|
2216
|
+
flexRender,
|
|
2217
|
+
getCoreRowModel,
|
|
2218
|
+
useReactTable
|
|
2219
|
+
} from "@tanstack/react-table";
|
|
2220
|
+
import { ChevronRight as ChevronRight2 } from "lucide-react";
|
|
2221
|
+
import { cn as cn25 } from "@petrarca/sonnet-core";
|
|
2222
|
+
|
|
2223
|
+
// src/EntityTable/tableSchema/mergeUiSchema.ts
|
|
2224
|
+
function resolveCellRenderer(property, uiEntry) {
|
|
2225
|
+
return uiEntry?.["x-ui-cell"] ?? property["x-ui-cell"];
|
|
2226
|
+
}
|
|
2227
|
+
function resolveCellOptions(property, uiEntry) {
|
|
2228
|
+
return {
|
|
2229
|
+
...property["x-ui-cell-options"] ?? {},
|
|
2230
|
+
...uiEntry?.["x-ui-cell-options"] ?? {}
|
|
2231
|
+
};
|
|
2232
|
+
}
|
|
2233
|
+
function mergeColumnConfig(key, property, uiSchema) {
|
|
2234
|
+
const uiEntry = uiSchema?.[key];
|
|
2235
|
+
const merged = {
|
|
2236
|
+
...property["x-ui-column"] ?? {},
|
|
2237
|
+
...uiEntry?.["x-ui-column"] ?? {}
|
|
2238
|
+
};
|
|
2239
|
+
return {
|
|
2240
|
+
order: merged.order,
|
|
2241
|
+
hidden: merged.hidden ?? false,
|
|
2242
|
+
width: merged.width,
|
|
2243
|
+
minWidth: merged.minWidth,
|
|
2244
|
+
label: merged.label,
|
|
2245
|
+
cellRenderer: resolveCellRenderer(property, uiEntry),
|
|
2246
|
+
cellOptions: resolveCellOptions(property, uiEntry)
|
|
2247
|
+
};
|
|
2248
|
+
}
|
|
2249
|
+
|
|
2250
|
+
// src/EntityTable/tableSchema/cellRegistry.ts
|
|
2251
|
+
import React22 from "react";
|
|
2252
|
+
import { warnLog } from "@petrarca/sonnet-core";
|
|
2253
|
+
|
|
2254
|
+
// src/EntityTable/tableSchema/cellRenderers.tsx
|
|
2255
|
+
import { formatDate, formatDateTime } from "@petrarca/sonnet-core";
|
|
2256
|
+
import { Fragment, jsx as jsx26, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
2257
|
+
function resolveValue(value, options) {
|
|
2258
|
+
const key = options.valueKey;
|
|
2259
|
+
if (key && value != null && typeof value === "object") {
|
|
2260
|
+
return value[key];
|
|
2261
|
+
}
|
|
2262
|
+
return value;
|
|
2263
|
+
}
|
|
2264
|
+
function cx(base, options) {
|
|
2265
|
+
return options.nowrap ? [base, "whitespace-nowrap"].filter(Boolean).join(" ") : base;
|
|
2266
|
+
}
|
|
2267
|
+
function TextCell({
|
|
2268
|
+
value,
|
|
2269
|
+
options
|
|
2270
|
+
}) {
|
|
2271
|
+
const text = resolveValue(value, options);
|
|
2272
|
+
return /* @__PURE__ */ jsx26("span", { className: cx("", options), children: text == null ? "\u2014" : String(text) });
|
|
2273
|
+
}
|
|
2274
|
+
function MutedCell({
|
|
2275
|
+
value,
|
|
2276
|
+
options
|
|
2277
|
+
}) {
|
|
2278
|
+
const text = resolveValue(value, options);
|
|
2279
|
+
return /* @__PURE__ */ jsx26("span", { className: cx("text-muted-foreground", options), children: text == null ? "\u2014" : String(text) });
|
|
2280
|
+
}
|
|
2281
|
+
function BoldCell({
|
|
2282
|
+
value,
|
|
2283
|
+
options
|
|
2284
|
+
}) {
|
|
2285
|
+
const text = resolveValue(value, options);
|
|
2286
|
+
return /* @__PURE__ */ jsx26("span", { className: cx("font-medium", options), children: text == null ? "\u2014" : String(text) });
|
|
2287
|
+
}
|
|
2288
|
+
function CodeCell({
|
|
2289
|
+
value,
|
|
2290
|
+
options
|
|
2291
|
+
}) {
|
|
2292
|
+
const text = resolveValue(value, options);
|
|
2293
|
+
return /* @__PURE__ */ jsx26("span", { className: cx("font-mono text-xs", options), children: text == null ? "\u2014" : String(text) });
|
|
2294
|
+
}
|
|
2295
|
+
function BadgeCell({
|
|
2296
|
+
value,
|
|
2297
|
+
options
|
|
2298
|
+
}) {
|
|
2299
|
+
if (value == null)
|
|
2300
|
+
return /* @__PURE__ */ jsx26("span", { className: "text-muted-foreground", children: "\u2014" });
|
|
2301
|
+
const key = String(value);
|
|
2302
|
+
const colors = options.colors;
|
|
2303
|
+
const labels = options.labels;
|
|
2304
|
+
const color = colors?.[key];
|
|
2305
|
+
const label = labels?.[key] ?? key;
|
|
2306
|
+
return /* @__PURE__ */ jsx26(Badge, { badgeColor: color, children: label });
|
|
2307
|
+
}
|
|
2308
|
+
function DateCell({
|
|
2309
|
+
value,
|
|
2310
|
+
options
|
|
2311
|
+
}) {
|
|
2312
|
+
return /* @__PURE__ */ jsx26("span", { className: cx("text-muted-foreground", options), children: formatDate(value) });
|
|
2313
|
+
}
|
|
2314
|
+
function DateTimeCell({
|
|
2315
|
+
value,
|
|
2316
|
+
options
|
|
2317
|
+
}) {
|
|
2318
|
+
return /* @__PURE__ */ jsx26("span", { className: cx("text-muted-foreground", options), children: formatDateTime(value) });
|
|
2319
|
+
}
|
|
2320
|
+
function BooleanCell({ value }) {
|
|
2321
|
+
return /* @__PURE__ */ jsx26("span", { className: value ? "text-green-600" : "text-muted-foreground", children: value ? "Yes" : "No" });
|
|
2322
|
+
}
|
|
2323
|
+
function NumberCell({ value }) {
|
|
2324
|
+
const text = value == null ? "\u2014" : String(value);
|
|
2325
|
+
return /* @__PURE__ */ jsx26("span", { className: "tabular-nums", children: text });
|
|
2326
|
+
}
|
|
2327
|
+
function TruncateCell({
|
|
2328
|
+
value,
|
|
2329
|
+
options
|
|
2330
|
+
}) {
|
|
2331
|
+
const resolved = resolveValue(value, options);
|
|
2332
|
+
const text = resolved == null ? "\u2014" : String(resolved);
|
|
2333
|
+
return /* @__PURE__ */ jsx26("span", { className: "block truncate max-w-xs", title: text, children: text });
|
|
2334
|
+
}
|
|
2335
|
+
function OverflowListCell({
|
|
2336
|
+
value,
|
|
2337
|
+
options
|
|
2338
|
+
}) {
|
|
2339
|
+
const items = Array.isArray(value) ? value : [];
|
|
2340
|
+
if (items.length === 0)
|
|
2341
|
+
return /* @__PURE__ */ jsx26("span", { className: "text-muted-foreground", children: "\u2014" });
|
|
2342
|
+
const displayKey = options.displayKey;
|
|
2343
|
+
const fallbackKey = options.fallbackKey;
|
|
2344
|
+
const prefixKey = options.prefixKey;
|
|
2345
|
+
const first = items[0];
|
|
2346
|
+
const label = displayKey && first[displayKey] || fallbackKey && first[fallbackKey] || "";
|
|
2347
|
+
return /* @__PURE__ */ jsxs7(Fragment, { children: [
|
|
2348
|
+
prefixKey && /* @__PURE__ */ jsxs7("span", { className: "text-muted-foreground", children: [
|
|
2349
|
+
String(first[prefixKey]),
|
|
2350
|
+
":",
|
|
2351
|
+
" "
|
|
2352
|
+
] }),
|
|
2353
|
+
label,
|
|
2354
|
+
items.length > 1 && /* @__PURE__ */ jsxs7("span", { className: "text-muted-foreground ml-1", children: [
|
|
2355
|
+
"+",
|
|
2356
|
+
items.length - 1
|
|
2357
|
+
] })
|
|
2358
|
+
] });
|
|
2359
|
+
}
|
|
2360
|
+
function JoinCell({
|
|
2361
|
+
value,
|
|
2362
|
+
options
|
|
2363
|
+
}) {
|
|
2364
|
+
const sep = options.separator ?? ", ";
|
|
2365
|
+
if (!Array.isArray(value) || value.length === 0)
|
|
2366
|
+
return /* @__PURE__ */ jsx26("span", { className: "text-muted-foreground", children: "\u2014" });
|
|
2367
|
+
return /* @__PURE__ */ jsx26("span", { className: "text-muted-foreground", children: value.join(sep) });
|
|
2368
|
+
}
|
|
2369
|
+
|
|
2370
|
+
// src/EntityTable/tableSchema/cellRegistry.ts
|
|
2371
|
+
var CELL_RENDERERS = {
|
|
2372
|
+
text: TextCell,
|
|
2373
|
+
muted: MutedCell,
|
|
2374
|
+
bold: BoldCell,
|
|
2375
|
+
code: CodeCell,
|
|
2376
|
+
badge: BadgeCell,
|
|
2377
|
+
date: DateCell,
|
|
2378
|
+
datetime: DateTimeCell,
|
|
2379
|
+
boolean: BooleanCell,
|
|
2380
|
+
number: NumberCell,
|
|
2381
|
+
truncate: TruncateCell,
|
|
2382
|
+
"overflow-list": OverflowListCell,
|
|
2383
|
+
join: JoinCell
|
|
2384
|
+
};
|
|
2385
|
+
function inferCellRenderer(type, format) {
|
|
2386
|
+
if (type === "boolean") return "boolean";
|
|
2387
|
+
if (type === "number" || type === "integer") return "number";
|
|
2388
|
+
if (type === "string") {
|
|
2389
|
+
if (format === "date-time") return "datetime";
|
|
2390
|
+
if (format === "date") return "date";
|
|
2391
|
+
}
|
|
2392
|
+
return "text";
|
|
2393
|
+
}
|
|
2394
|
+
function resolveCellRenderer2(name, localRenderers) {
|
|
2395
|
+
return localRenderers?.[name] ?? CELL_RENDERERS[name] ?? TextCell;
|
|
2396
|
+
}
|
|
2397
|
+
function createRenderer(render) {
|
|
2398
|
+
return ({ row, options }) => React22.createElement(React22.Fragment, null, render(row, options));
|
|
2399
|
+
}
|
|
2400
|
+
function validateRendererNames(renderers) {
|
|
2401
|
+
for (const name of Object.keys(renderers)) {
|
|
2402
|
+
if (!name.startsWith("x-")) {
|
|
2403
|
+
warnLog(
|
|
2404
|
+
`[EntityTable] Local renderer "${name}" should use the x- prefix (e.g. "x-${name}"). Built-in renderer names are unprefixed.`
|
|
2405
|
+
);
|
|
2406
|
+
}
|
|
2407
|
+
}
|
|
2408
|
+
}
|
|
2409
|
+
|
|
2410
|
+
// src/EntityTable/tableSchema/columnResolver.ts
|
|
2411
|
+
function resolveColumns(schema, uiSchema) {
|
|
2412
|
+
const properties = schema.properties ?? {};
|
|
2413
|
+
const entries = Object.entries(properties).map(([key, property]) => {
|
|
2414
|
+
const config = mergeColumnConfig(key, property, uiSchema);
|
|
2415
|
+
return { key, property, config };
|
|
2416
|
+
}).filter(({ config }) => !config.hidden);
|
|
2417
|
+
const ordered = entries.filter(({ config }) => config.order !== void 0).sort((a, b) => a.config.order - b.config.order);
|
|
2418
|
+
const unordered = entries.filter(({ config }) => config.order === void 0).sort((a, b) => a.key.localeCompare(b.key));
|
|
2419
|
+
const sorted = [...ordered, ...unordered];
|
|
2420
|
+
return sorted.map(({ key, property, config }) => {
|
|
2421
|
+
const cellRenderer = config.cellRenderer ?? inferCellRenderer(property.type, property.format);
|
|
2422
|
+
const label = config.label ?? property.title ?? keyToLabel(key);
|
|
2423
|
+
return {
|
|
2424
|
+
key,
|
|
2425
|
+
label,
|
|
2426
|
+
property,
|
|
2427
|
+
cellRenderer,
|
|
2428
|
+
cellOptions: config.cellOptions,
|
|
2429
|
+
width: config.width,
|
|
2430
|
+
minWidth: config.minWidth
|
|
2431
|
+
};
|
|
2432
|
+
});
|
|
2433
|
+
}
|
|
2434
|
+
function keyToLabel(key) {
|
|
2435
|
+
return key.replace(/_/g, " ").replace(/\b\w/g, (c) => c.toUpperCase());
|
|
2436
|
+
}
|
|
2437
|
+
|
|
2438
|
+
// src/EntityTable/EntityTable.tsx
|
|
2439
|
+
import { jsx as jsx27, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
2440
|
+
var coreRowModel = getCoreRowModel();
|
|
2441
|
+
function resolveTableDefaults(props) {
|
|
2442
|
+
return {
|
|
2443
|
+
isFiltered: props.isFiltered ?? false,
|
|
2444
|
+
selectionMode: props.selectionMode ?? false
|
|
2445
|
+
};
|
|
2446
|
+
}
|
|
2447
|
+
function buildColumnDefs(resolved, localRenderers) {
|
|
2448
|
+
return resolved.map((col) => {
|
|
2449
|
+
const Renderer = resolveCellRenderer2(col.cellRenderer, localRenderers);
|
|
2450
|
+
const options = col.cellOptions;
|
|
2451
|
+
const colDef = {
|
|
2452
|
+
id: col.key,
|
|
2453
|
+
accessorKey: col.key,
|
|
2454
|
+
header: col.label,
|
|
2455
|
+
cell: ({
|
|
2456
|
+
getValue,
|
|
2457
|
+
row
|
|
2458
|
+
}) => /* @__PURE__ */ jsx27(
|
|
2459
|
+
Renderer,
|
|
2460
|
+
{
|
|
2461
|
+
value: getValue(),
|
|
2462
|
+
row: row.original,
|
|
2463
|
+
options
|
|
2464
|
+
}
|
|
2465
|
+
)
|
|
2466
|
+
};
|
|
2467
|
+
if (col.width !== void 0) colDef.size = col.width;
|
|
2468
|
+
if (col.minWidth !== void 0) colDef.minSize = col.minWidth;
|
|
2469
|
+
return colDef;
|
|
2470
|
+
});
|
|
2471
|
+
}
|
|
2472
|
+
function buildSelectColumn() {
|
|
2473
|
+
return {
|
|
2474
|
+
id: "_select",
|
|
2475
|
+
size: 40,
|
|
2476
|
+
header: ({ table: t }) => /* @__PURE__ */ jsx27(
|
|
2477
|
+
Checkbox,
|
|
2478
|
+
{
|
|
2479
|
+
checked: t.getIsAllPageRowsSelected() || (t.getIsSomePageRowsSelected() ? "indeterminate" : false),
|
|
2480
|
+
onCheckedChange: (value) => t.toggleAllPageRowsSelected(!!value),
|
|
2481
|
+
"aria-label": "Select all"
|
|
2482
|
+
}
|
|
2483
|
+
),
|
|
2484
|
+
cell: ({ row }) => /* @__PURE__ */ jsx27(
|
|
2485
|
+
Checkbox,
|
|
2486
|
+
{
|
|
2487
|
+
checked: row.getIsSelected(),
|
|
2488
|
+
onCheckedChange: (value) => row.toggleSelected(!!value),
|
|
2489
|
+
onClick: (e) => e.stopPropagation(),
|
|
2490
|
+
"aria-label": "Select row"
|
|
2491
|
+
}
|
|
2492
|
+
)
|
|
2493
|
+
};
|
|
2494
|
+
}
|
|
2495
|
+
function buildTableOptions(opts) {
|
|
2496
|
+
const base = {
|
|
2497
|
+
data: opts.dataRows,
|
|
2498
|
+
columns: opts.columns,
|
|
2499
|
+
getCoreRowModel: coreRowModel,
|
|
2500
|
+
state: opts.tableState
|
|
2501
|
+
};
|
|
2502
|
+
const selectionOpts = opts.selectionMode ? { enableRowSelection: true, onRowSelectionChange: opts.setRowSelection } : {};
|
|
2503
|
+
const paginationOpts = opts.isPaginated ? {
|
|
2504
|
+
manualPagination: true,
|
|
2505
|
+
rowCount: opts.data?.meta.total ?? 0,
|
|
2506
|
+
onPaginationChange: opts.onPaginationChange
|
|
2507
|
+
} : {};
|
|
2508
|
+
return { ...base, ...selectionOpts, ...paginationOpts };
|
|
2509
|
+
}
|
|
2510
|
+
function useEntityTableState(props, selectionMode) {
|
|
2511
|
+
const {
|
|
2512
|
+
schema,
|
|
2513
|
+
uiSchema,
|
|
2514
|
+
renderers,
|
|
2515
|
+
data,
|
|
2516
|
+
pagination,
|
|
2517
|
+
onPaginationChange,
|
|
2518
|
+
onSelectionChange
|
|
2519
|
+
} = props;
|
|
2520
|
+
const isPaginated = !!(pagination && onPaginationChange);
|
|
2521
|
+
const dataRows = useMemo2(() => data?.data ?? [], [data]);
|
|
2522
|
+
const total = data?.meta.total;
|
|
2523
|
+
const pageIndex = pagination?.pageIndex ?? 0;
|
|
2524
|
+
const pageSize = pagination?.pageSize ?? 1;
|
|
2525
|
+
useEffect3(() => {
|
|
2526
|
+
if (!isPaginated || total == null || !onPaginationChange) return;
|
|
2527
|
+
const maxPage = Math.max(0, Math.ceil(total / pageSize) - 1);
|
|
2528
|
+
if (pageIndex > maxPage) {
|
|
2529
|
+
onPaginationChange((prev) => ({ ...prev, pageIndex: maxPage }));
|
|
2530
|
+
}
|
|
2531
|
+
}, [total, isPaginated, pageIndex, pageSize, onPaginationChange]);
|
|
2532
|
+
const [rowSelection, setRowSelection] = useState2({});
|
|
2533
|
+
useEffect3(() => {
|
|
2534
|
+
setRowSelection({});
|
|
2535
|
+
}, [data]);
|
|
2536
|
+
const [activeRowIndex, setActiveRowIndex] = useState2(-1);
|
|
2537
|
+
const containerRef = useRef4(null);
|
|
2538
|
+
const dataColumns = useMemo2(() => {
|
|
2539
|
+
if (renderers) validateRendererNames(renderers);
|
|
2540
|
+
return buildColumnDefs(resolveColumns(schema, uiSchema), renderers);
|
|
2541
|
+
}, [schema, uiSchema, renderers]);
|
|
2542
|
+
const selectColumn = useMemo2(
|
|
2543
|
+
() => selectionMode ? buildSelectColumn() : null,
|
|
2544
|
+
[selectionMode]
|
|
2545
|
+
);
|
|
2546
|
+
const columns = useMemo2(
|
|
2547
|
+
() => selectColumn ? [selectColumn, ...dataColumns] : dataColumns,
|
|
2548
|
+
[selectColumn, dataColumns]
|
|
2549
|
+
);
|
|
2550
|
+
const onSelectionChangeRef = useRef4(onSelectionChange);
|
|
2551
|
+
onSelectionChangeRef.current = onSelectionChange;
|
|
2552
|
+
useEffect3(() => {
|
|
2553
|
+
if (!selectionMode || !onSelectionChangeRef.current) return;
|
|
2554
|
+
const selected = Object.keys(rowSelection).filter((k) => rowSelection[k]).map(Number).map((i) => dataRows[i]).filter((r) => r !== void 0);
|
|
2555
|
+
onSelectionChangeRef.current(selected);
|
|
2556
|
+
}, [rowSelection, selectionMode, dataRows]);
|
|
2557
|
+
const tableState = useMemo2(
|
|
2558
|
+
() => ({
|
|
2559
|
+
...isPaginated ? { pagination } : {},
|
|
2560
|
+
...selectionMode ? { rowSelection } : {}
|
|
2561
|
+
}),
|
|
2562
|
+
[isPaginated, pagination, selectionMode, rowSelection]
|
|
2563
|
+
);
|
|
2564
|
+
const tableOptions = buildTableOptions({
|
|
2565
|
+
dataRows,
|
|
2566
|
+
columns,
|
|
2567
|
+
tableState,
|
|
2568
|
+
selectionMode,
|
|
2569
|
+
setRowSelection,
|
|
2570
|
+
isPaginated,
|
|
2571
|
+
data,
|
|
2572
|
+
onPaginationChange
|
|
2573
|
+
});
|
|
2574
|
+
const table = useReactTable(tableOptions);
|
|
2575
|
+
useEffect3(() => {
|
|
2576
|
+
if (activeRowIndex < 0 || !containerRef.current) return;
|
|
2577
|
+
const rows = containerRef.current.querySelectorAll("tbody tr");
|
|
2578
|
+
rows[activeRowIndex]?.scrollIntoView({ block: "nearest" });
|
|
2579
|
+
}, [activeRowIndex]);
|
|
2580
|
+
return {
|
|
2581
|
+
table,
|
|
2582
|
+
dataRows,
|
|
2583
|
+
isPaginated,
|
|
2584
|
+
rowSelection,
|
|
2585
|
+
setRowSelection,
|
|
2586
|
+
activeRowIndex,
|
|
2587
|
+
setActiveRowIndex,
|
|
2588
|
+
containerRef,
|
|
2589
|
+
columns
|
|
2590
|
+
};
|
|
2591
|
+
}
|
|
2592
|
+
function useTableKeyboard(table, activeRowIndex, setActiveRowIndex, selectionMode, onRowClick) {
|
|
2593
|
+
return useCallback2(
|
|
2594
|
+
(e) => {
|
|
2595
|
+
const tableRows = table.getRowModel().rows;
|
|
2596
|
+
if (tableRows.length === 0) return;
|
|
2597
|
+
switch (e.key) {
|
|
2598
|
+
case "ArrowDown": {
|
|
2599
|
+
e.preventDefault();
|
|
2600
|
+
setActiveRowIndex((prev) => Math.min(prev + 1, tableRows.length - 1));
|
|
2601
|
+
break;
|
|
2602
|
+
}
|
|
2603
|
+
case "ArrowUp": {
|
|
2604
|
+
e.preventDefault();
|
|
2605
|
+
setActiveRowIndex((prev) => Math.max(prev - 1, 0));
|
|
2606
|
+
break;
|
|
2607
|
+
}
|
|
2608
|
+
case "Enter": {
|
|
2609
|
+
handleEnterKey(
|
|
2610
|
+
e,
|
|
2611
|
+
tableRows,
|
|
2612
|
+
selectionMode,
|
|
2613
|
+
activeRowIndex,
|
|
2614
|
+
onRowClick
|
|
2615
|
+
);
|
|
2616
|
+
break;
|
|
2617
|
+
}
|
|
2618
|
+
case " ": {
|
|
2619
|
+
handleSpaceKey(e, tableRows, selectionMode, activeRowIndex);
|
|
2620
|
+
break;
|
|
2621
|
+
}
|
|
2622
|
+
}
|
|
2623
|
+
},
|
|
2624
|
+
[activeRowIndex, onRowClick, selectionMode, table, setActiveRowIndex]
|
|
2625
|
+
);
|
|
2626
|
+
}
|
|
2627
|
+
function handleEnterKey(e, tableRows, selectionMode, activeRowIndex, onRowClick) {
|
|
2628
|
+
e.preventDefault();
|
|
2629
|
+
if (selectionMode || activeRowIndex < 0 || !onRowClick) return;
|
|
2630
|
+
const activeRow = tableRows[activeRowIndex];
|
|
2631
|
+
if (activeRow) onRowClick(activeRow.original);
|
|
2632
|
+
}
|
|
2633
|
+
function handleSpaceKey(e, tableRows, selectionMode, activeRowIndex) {
|
|
2634
|
+
if (!selectionMode || activeRowIndex < 0) return;
|
|
2635
|
+
e.preventDefault();
|
|
2636
|
+
tableRows[activeRowIndex]?.toggleSelected();
|
|
2637
|
+
}
|
|
2638
|
+
function resolveTrailingCols(rowActions, onRowClick, selectionMode) {
|
|
2639
|
+
const hasRowActions = !!(rowActions && rowActions.length > 0);
|
|
2640
|
+
const hasChevron = !!(onRowClick && !selectionMode && !hasRowActions);
|
|
2641
|
+
return {
|
|
2642
|
+
hasRowActions,
|
|
2643
|
+
hasChevron,
|
|
2644
|
+
hasTrailingCol: hasRowActions || hasChevron
|
|
2645
|
+
};
|
|
2646
|
+
}
|
|
2647
|
+
function StatusRows({
|
|
2648
|
+
isLoading,
|
|
2649
|
+
isError,
|
|
2650
|
+
errorMessage,
|
|
2651
|
+
entityLabel,
|
|
2652
|
+
isFiltered,
|
|
2653
|
+
emptyMessage,
|
|
2654
|
+
isEmpty,
|
|
2655
|
+
colCount
|
|
2656
|
+
}) {
|
|
2657
|
+
if (isLoading) {
|
|
2658
|
+
return /* @__PURE__ */ jsx27("tr", { children: /* @__PURE__ */ jsx27(
|
|
2659
|
+
"td",
|
|
2660
|
+
{
|
|
2661
|
+
colSpan: colCount,
|
|
2662
|
+
className: "px-4 py-8 text-center text-muted-foreground",
|
|
2663
|
+
children: "Loading..."
|
|
2664
|
+
}
|
|
2665
|
+
) });
|
|
2666
|
+
}
|
|
2667
|
+
if (isError) {
|
|
2668
|
+
return /* @__PURE__ */ jsx27("tr", { children: /* @__PURE__ */ jsx27(
|
|
2669
|
+
"td",
|
|
2670
|
+
{
|
|
2671
|
+
colSpan: colCount,
|
|
2672
|
+
className: "px-4 py-8 text-center text-destructive",
|
|
2673
|
+
children: errorMessage ?? `Failed to load ${entityLabel}.`
|
|
2674
|
+
}
|
|
2675
|
+
) });
|
|
2676
|
+
}
|
|
2677
|
+
if (isEmpty) {
|
|
2678
|
+
return /* @__PURE__ */ jsx27("tr", { children: /* @__PURE__ */ jsx27(
|
|
2679
|
+
"td",
|
|
2680
|
+
{
|
|
2681
|
+
colSpan: colCount,
|
|
2682
|
+
className: "px-4 py-8 text-center text-muted-foreground",
|
|
2683
|
+
children: resolveEmptyMessage(emptyMessage, isFiltered, entityLabel)
|
|
2684
|
+
}
|
|
2685
|
+
) });
|
|
2686
|
+
}
|
|
2687
|
+
return null;
|
|
2688
|
+
}
|
|
2689
|
+
function resolveEmptyMessage(emptyMessage, isFiltered, entityLabel) {
|
|
2690
|
+
if (emptyMessage) return emptyMessage;
|
|
2691
|
+
if (isFiltered) return `No ${entityLabel} match your search.`;
|
|
2692
|
+
return `No ${entityLabel} yet.`;
|
|
2693
|
+
}
|
|
2694
|
+
function RowActionCell({
|
|
2695
|
+
actions,
|
|
2696
|
+
rowOriginal
|
|
2697
|
+
}) {
|
|
2698
|
+
return /* @__PURE__ */ jsx27("td", { className: "px-4 py-3 w-24 text-right", children: /* @__PURE__ */ jsx27("div", { className: "flex items-center justify-end gap-1", children: actions.map((action) => /* @__PURE__ */ jsx27(
|
|
2699
|
+
ActionButton,
|
|
2700
|
+
{
|
|
2701
|
+
action,
|
|
2702
|
+
rowOriginal
|
|
2703
|
+
},
|
|
2704
|
+
action.label
|
|
2705
|
+
)) }) });
|
|
2706
|
+
}
|
|
2707
|
+
function ActionButton({
|
|
2708
|
+
action,
|
|
2709
|
+
rowOriginal
|
|
2710
|
+
}) {
|
|
2711
|
+
const isHidden = action.hidden?.(rowOriginal);
|
|
2712
|
+
if (isHidden) return null;
|
|
2713
|
+
const isDisabled = action.disabled?.(rowOriginal);
|
|
2714
|
+
const Icon = action.icon;
|
|
2715
|
+
return /* @__PURE__ */ jsx27(
|
|
2716
|
+
"button",
|
|
2717
|
+
{
|
|
2718
|
+
title: action.label,
|
|
2719
|
+
disabled: isDisabled,
|
|
2720
|
+
onClick: (e) => {
|
|
2721
|
+
e.stopPropagation();
|
|
2722
|
+
action.onClick(rowOriginal);
|
|
2723
|
+
},
|
|
2724
|
+
className: cn25(
|
|
2725
|
+
"inline-flex items-center justify-center h-7 w-7 rounded-md transition-colors",
|
|
2726
|
+
action.variant === "destructive" ? "text-destructive hover:bg-destructive/10" : "text-muted-foreground hover:bg-accent",
|
|
2727
|
+
isDisabled ? "opacity-30 cursor-not-allowed group-hover/row:opacity-30" : "opacity-0 group-hover/row:opacity-100 focus:opacity-100"
|
|
2728
|
+
),
|
|
2729
|
+
children: Icon ? /* @__PURE__ */ jsx27(Icon, { className: "h-3.5 w-3.5" }) : /* @__PURE__ */ jsx27("span", { className: "text-xs", children: action.label })
|
|
2730
|
+
},
|
|
2731
|
+
action.label
|
|
2732
|
+
);
|
|
2733
|
+
}
|
|
2734
|
+
function DataRow({
|
|
2735
|
+
row,
|
|
2736
|
+
index,
|
|
2737
|
+
selectionMode,
|
|
2738
|
+
activeRowIndex,
|
|
2739
|
+
onRowClick,
|
|
2740
|
+
setActiveRowIndex,
|
|
2741
|
+
containerRef,
|
|
2742
|
+
hasRowActions,
|
|
2743
|
+
hasChevron,
|
|
2744
|
+
rowActions
|
|
2745
|
+
}) {
|
|
2746
|
+
return /* @__PURE__ */ jsxs8(
|
|
2747
|
+
"tr",
|
|
2748
|
+
{
|
|
2749
|
+
onClick: () => {
|
|
2750
|
+
handleRowClick(
|
|
2751
|
+
row,
|
|
2752
|
+
index,
|
|
2753
|
+
selectionMode,
|
|
2754
|
+
onRowClick,
|
|
2755
|
+
setActiveRowIndex,
|
|
2756
|
+
containerRef
|
|
2757
|
+
);
|
|
2758
|
+
},
|
|
2759
|
+
className: cn25(
|
|
2760
|
+
"border-b last:border-b-0 transition-colors group/row",
|
|
2761
|
+
onRowClick && !selectionMode && "hover:bg-accent/50 cursor-pointer",
|
|
2762
|
+
selectionMode && "cursor-default",
|
|
2763
|
+
row.getIsSelected() && "bg-primary/10",
|
|
2764
|
+
index === activeRowIndex && selectionMode && "bg-accent"
|
|
2765
|
+
),
|
|
2766
|
+
children: [
|
|
2767
|
+
row.getVisibleCells().map((cell) => /* @__PURE__ */ jsx27("td", { className: "px-4 py-3", children: flexRender(cell.column.columnDef.cell, cell.getContext()) }, cell.id)),
|
|
2768
|
+
hasRowActions && /* @__PURE__ */ jsx27(RowActionCell, { actions: rowActions, rowOriginal: row.original }),
|
|
2769
|
+
hasChevron && /* @__PURE__ */ jsx27("td", { className: "px-4 py-3 w-10", children: /* @__PURE__ */ jsx27(ChevronRight2, { className: "h-4 w-4 text-muted-foreground" }) })
|
|
2770
|
+
]
|
|
2771
|
+
},
|
|
2772
|
+
row.id
|
|
2773
|
+
);
|
|
2774
|
+
}
|
|
2775
|
+
function handleRowClick(row, index, selectionMode, onRowClick, setActiveRowIndex, containerRef) {
|
|
2776
|
+
if (selectionMode) {
|
|
2777
|
+
row.toggleSelected();
|
|
2778
|
+
setActiveRowIndex(index);
|
|
2779
|
+
containerRef.current?.focus();
|
|
2780
|
+
} else if (onRowClick) {
|
|
2781
|
+
onRowClick(row.original);
|
|
2782
|
+
}
|
|
2783
|
+
}
|
|
2784
|
+
function TableFooter({
|
|
2785
|
+
isPaginated,
|
|
2786
|
+
data,
|
|
2787
|
+
dataRows,
|
|
2788
|
+
entityLabel,
|
|
2789
|
+
table
|
|
2790
|
+
}) {
|
|
2791
|
+
if (!data?.meta) return null;
|
|
2792
|
+
if (isPaginated) {
|
|
2793
|
+
return /* @__PURE__ */ jsx27(PaginationFooter, { table, entityLabel });
|
|
2794
|
+
}
|
|
2795
|
+
return /* @__PURE__ */ jsxs8("p", { className: "text-xs text-muted-foreground mt-3", children: [
|
|
2796
|
+
"Showing ",
|
|
2797
|
+
dataRows.length,
|
|
2798
|
+
" of ",
|
|
2799
|
+
data.meta.total,
|
|
2800
|
+
" ",
|
|
2801
|
+
entityLabel
|
|
2802
|
+
] });
|
|
2803
|
+
}
|
|
2804
|
+
function EntityTable(props) {
|
|
2805
|
+
const {
|
|
2806
|
+
data,
|
|
2807
|
+
isLoading,
|
|
2808
|
+
isError,
|
|
2809
|
+
errorMessage,
|
|
2810
|
+
entityLabel,
|
|
2811
|
+
onRowClick,
|
|
2812
|
+
rowActions
|
|
2813
|
+
} = props;
|
|
2814
|
+
const { isFiltered, selectionMode } = resolveTableDefaults(props);
|
|
2815
|
+
const {
|
|
2816
|
+
table,
|
|
2817
|
+
dataRows,
|
|
2818
|
+
isPaginated,
|
|
2819
|
+
activeRowIndex,
|
|
2820
|
+
setActiveRowIndex,
|
|
2821
|
+
containerRef,
|
|
2822
|
+
columns
|
|
2823
|
+
} = useEntityTableState(props, selectionMode);
|
|
2824
|
+
const handleKeyDown = useTableKeyboard(
|
|
2825
|
+
table,
|
|
2826
|
+
activeRowIndex,
|
|
2827
|
+
setActiveRowIndex,
|
|
2828
|
+
selectionMode,
|
|
2829
|
+
onRowClick
|
|
2830
|
+
);
|
|
2831
|
+
const { hasRowActions, hasChevron, hasTrailingCol } = resolveTrailingCols(
|
|
2832
|
+
rowActions,
|
|
2833
|
+
onRowClick,
|
|
2834
|
+
selectionMode
|
|
2835
|
+
);
|
|
2836
|
+
const colCount = columns.length + (hasTrailingCol ? 1 : 0);
|
|
2837
|
+
return /* @__PURE__ */ jsxs8("div", { children: [
|
|
2838
|
+
/* @__PURE__ */ jsx27(
|
|
2839
|
+
"div",
|
|
2840
|
+
{
|
|
2841
|
+
ref: selectionMode ? containerRef : void 0,
|
|
2842
|
+
tabIndex: selectionMode ? 0 : void 0,
|
|
2843
|
+
onKeyDown: selectionMode ? handleKeyDown : void 0,
|
|
2844
|
+
className: cn25("rounded-lg border", selectionMode && "outline-none"),
|
|
2845
|
+
children: /* @__PURE__ */ jsxs8("table", { className: "w-full text-sm", children: [
|
|
2846
|
+
/* @__PURE__ */ jsx27("thead", { children: /* @__PURE__ */ jsxs8("tr", { className: "border-b bg-muted/40", children: [
|
|
2847
|
+
table.getFlatHeaders().map((header) => /* @__PURE__ */ jsx27(
|
|
2848
|
+
"th",
|
|
2849
|
+
{
|
|
2850
|
+
className: "text-left text-xs font-medium text-muted-foreground px-4 py-2.5",
|
|
2851
|
+
style: header.column.columnDef.size !== void 0 ? { width: header.column.columnDef.size } : void 0,
|
|
2852
|
+
children: header.isPlaceholder ? null : flexRender(
|
|
2853
|
+
header.column.columnDef.header,
|
|
2854
|
+
header.getContext()
|
|
2855
|
+
)
|
|
2856
|
+
},
|
|
2857
|
+
header.id
|
|
2858
|
+
)),
|
|
2859
|
+
hasRowActions && /* @__PURE__ */ jsx27(
|
|
2860
|
+
"th",
|
|
2861
|
+
{
|
|
2862
|
+
"aria-label": "Actions",
|
|
2863
|
+
scope: "col",
|
|
2864
|
+
className: "w-24 text-right text-xs font-medium text-muted-foreground px-4 py-2.5"
|
|
2865
|
+
}
|
|
2866
|
+
),
|
|
2867
|
+
hasChevron && /* @__PURE__ */ jsx27("th", { "aria-label": "Open", scope: "col", className: "w-10" })
|
|
2868
|
+
] }) }),
|
|
2869
|
+
/* @__PURE__ */ jsxs8("tbody", { children: [
|
|
2870
|
+
/* @__PURE__ */ jsx27(
|
|
2871
|
+
StatusRows,
|
|
2872
|
+
{
|
|
2873
|
+
isLoading,
|
|
2874
|
+
isError,
|
|
2875
|
+
errorMessage,
|
|
2876
|
+
entityLabel,
|
|
2877
|
+
isFiltered,
|
|
2878
|
+
emptyMessage: props.emptyMessage,
|
|
2879
|
+
isEmpty: !isLoading && !isError && dataRows.length === 0,
|
|
2880
|
+
colCount
|
|
2881
|
+
}
|
|
2882
|
+
),
|
|
2883
|
+
table.getRowModel().rows.map((row, index) => /* @__PURE__ */ jsx27(
|
|
2884
|
+
DataRow,
|
|
2885
|
+
{
|
|
2886
|
+
row,
|
|
2887
|
+
index,
|
|
2888
|
+
selectionMode,
|
|
2889
|
+
activeRowIndex,
|
|
2890
|
+
onRowClick,
|
|
2891
|
+
setActiveRowIndex,
|
|
2892
|
+
containerRef,
|
|
2893
|
+
hasRowActions,
|
|
2894
|
+
hasChevron,
|
|
2895
|
+
rowActions
|
|
2896
|
+
},
|
|
2897
|
+
row.id
|
|
2898
|
+
))
|
|
2899
|
+
] })
|
|
2900
|
+
] })
|
|
2901
|
+
}
|
|
2902
|
+
),
|
|
2903
|
+
/* @__PURE__ */ jsx27(
|
|
2904
|
+
TableFooter,
|
|
2905
|
+
{
|
|
2906
|
+
isPaginated,
|
|
2907
|
+
data,
|
|
2908
|
+
dataRows,
|
|
2909
|
+
entityLabel,
|
|
2910
|
+
table
|
|
2911
|
+
}
|
|
2912
|
+
)
|
|
2913
|
+
] });
|
|
2914
|
+
}
|
|
2915
|
+
function PaginationFooter({
|
|
2916
|
+
table,
|
|
2917
|
+
entityLabel
|
|
2918
|
+
}) {
|
|
2919
|
+
const { pageIndex, pageSize } = table.getState().pagination;
|
|
2920
|
+
const rowCount = table.getRowCount();
|
|
2921
|
+
const pageCount = table.getPageCount();
|
|
2922
|
+
const from = rowCount === 0 ? 0 : pageIndex * pageSize + 1;
|
|
2923
|
+
const to = Math.min((pageIndex + 1) * pageSize, rowCount);
|
|
2924
|
+
return /* @__PURE__ */ jsxs8("div", { className: "flex items-center justify-between mt-3", children: [
|
|
2925
|
+
/* @__PURE__ */ jsx27("p", { className: "text-xs text-muted-foreground", children: rowCount === 0 ? `No ${entityLabel}` : `Showing ${from}\u2013${to} of ${rowCount} ${entityLabel}` }),
|
|
2926
|
+
/* @__PURE__ */ jsxs8("div", { className: "flex items-center gap-1", children: [
|
|
2927
|
+
/* @__PURE__ */ jsx27(
|
|
2928
|
+
"button",
|
|
2929
|
+
{
|
|
2930
|
+
type: "button",
|
|
2931
|
+
onClick: () => table.previousPage(),
|
|
2932
|
+
disabled: !table.getCanPreviousPage(),
|
|
2933
|
+
className: "px-2 py-1 text-xs rounded border border-border text-muted-foreground hover:text-foreground hover:bg-accent transition-colors disabled:opacity-40 disabled:pointer-events-none",
|
|
2934
|
+
children: "Previous"
|
|
2935
|
+
}
|
|
2936
|
+
),
|
|
2937
|
+
/* @__PURE__ */ jsx27("span", { className: "text-xs text-muted-foreground px-2", children: pageCount > 0 ? `${pageIndex + 1} / ${pageCount}` : "--" }),
|
|
2938
|
+
/* @__PURE__ */ jsx27(
|
|
2939
|
+
"button",
|
|
2940
|
+
{
|
|
2941
|
+
type: "button",
|
|
2942
|
+
onClick: () => table.nextPage(),
|
|
2943
|
+
disabled: !table.getCanNextPage(),
|
|
2944
|
+
className: "px-2 py-1 text-xs rounded border border-border text-muted-foreground hover:text-foreground hover:bg-accent transition-colors disabled:opacity-40 disabled:pointer-events-none",
|
|
2945
|
+
children: "Next"
|
|
2946
|
+
}
|
|
2947
|
+
)
|
|
2948
|
+
] })
|
|
2949
|
+
] });
|
|
2950
|
+
}
|
|
2951
|
+
|
|
2952
|
+
// src/EntitySelect/EntitySelect.tsx
|
|
2953
|
+
import * as React24 from "react";
|
|
2954
|
+
import { Check as Check4, ChevronsUpDown, Loader2 } from "lucide-react";
|
|
2955
|
+
import { cn as cn26 } from "@petrarca/sonnet-core";
|
|
2956
|
+
import {
|
|
2957
|
+
useEntityOptions
|
|
2958
|
+
} from "@petrarca/sonnet-core/hooks";
|
|
2959
|
+
import { Fragment as Fragment2, jsx as jsx28, jsxs as jsxs9 } from "react/jsx-runtime";
|
|
2960
|
+
function emptyStateMessage({
|
|
2961
|
+
mode,
|
|
2962
|
+
search,
|
|
2963
|
+
minChars,
|
|
2964
|
+
isLoading,
|
|
2965
|
+
hasSearched,
|
|
2966
|
+
emptyMessage
|
|
2967
|
+
}) {
|
|
2968
|
+
if (isLoading) return "Loading...";
|
|
2969
|
+
if (mode === "eager") return emptyMessage;
|
|
2970
|
+
if (search.length === 0) return "Start typing to search...";
|
|
2971
|
+
if (mode === "typeahead" && search.length < minChars)
|
|
2972
|
+
return `Type at least ${minChars} character${minChars > 1 ? "s" : ""} to search...`;
|
|
2973
|
+
if (mode === "explicit" && !hasSearched) return "Press Enter to search...";
|
|
2974
|
+
return emptyMessage;
|
|
2975
|
+
}
|
|
2976
|
+
function useHasSearched(submit) {
|
|
2977
|
+
const [hasSearched, setHasSearched] = React24.useState(false);
|
|
2978
|
+
const wrappedSubmit = React24.useCallback(() => {
|
|
2979
|
+
setHasSearched(true);
|
|
2980
|
+
submit();
|
|
2981
|
+
}, [submit]);
|
|
2982
|
+
return [hasSearched, wrappedSubmit];
|
|
2983
|
+
}
|
|
2984
|
+
function resolveEntitySelectDefaults(props) {
|
|
2985
|
+
return {
|
|
2986
|
+
mode: props.mode ?? "eager",
|
|
2987
|
+
minChars: props.minChars ?? 1,
|
|
2988
|
+
placeholder: props.placeholder ?? "Select...",
|
|
2989
|
+
searchPlaceholder: props.searchPlaceholder ?? "Search...",
|
|
2990
|
+
emptyMessage: props.emptyMessage ?? "No results found",
|
|
2991
|
+
disabled: props.disabled ?? false
|
|
2992
|
+
};
|
|
2993
|
+
}
|
|
2994
|
+
function EntitySelectLabel({
|
|
2995
|
+
id,
|
|
2996
|
+
label,
|
|
2997
|
+
required,
|
|
2998
|
+
description
|
|
2999
|
+
}) {
|
|
3000
|
+
const hasLabel = label !== void 0 && label !== false;
|
|
3001
|
+
return /* @__PURE__ */ jsxs9(Fragment2, { children: [
|
|
3002
|
+
hasLabel && /* @__PURE__ */ jsxs9(
|
|
3003
|
+
"label",
|
|
3004
|
+
{
|
|
3005
|
+
htmlFor: id,
|
|
3006
|
+
className: "text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70",
|
|
3007
|
+
children: [
|
|
3008
|
+
label,
|
|
3009
|
+
required && /* @__PURE__ */ jsx28("span", { className: "ml-1 text-destructive", children: "*" })
|
|
3010
|
+
]
|
|
3011
|
+
}
|
|
3012
|
+
),
|
|
3013
|
+
description && /* @__PURE__ */ jsx28("p", { className: "text-sm text-muted-foreground", children: description })
|
|
3014
|
+
] });
|
|
3015
|
+
}
|
|
3016
|
+
function EntitySelectDropdown({
|
|
3017
|
+
searchPlaceholder,
|
|
3018
|
+
search,
|
|
3019
|
+
setSearch,
|
|
3020
|
+
mode,
|
|
3021
|
+
submit,
|
|
3022
|
+
minChars,
|
|
3023
|
+
isLoading,
|
|
3024
|
+
hasSearched,
|
|
3025
|
+
emptyMessage,
|
|
3026
|
+
options,
|
|
3027
|
+
value,
|
|
3028
|
+
handleSelect
|
|
3029
|
+
}) {
|
|
3030
|
+
return /* @__PURE__ */ jsxs9(Command, { shouldFilter: false, children: [
|
|
3031
|
+
/* @__PURE__ */ jsx28(
|
|
3032
|
+
CommandInput,
|
|
3033
|
+
{
|
|
3034
|
+
placeholder: searchPlaceholder,
|
|
3035
|
+
value: search,
|
|
3036
|
+
onValueChange: setSearch,
|
|
3037
|
+
onKeyDown: mode === "explicit" ? (e) => {
|
|
3038
|
+
if (e.key === "Enter") {
|
|
3039
|
+
e.preventDefault();
|
|
3040
|
+
submit();
|
|
3041
|
+
}
|
|
3042
|
+
} : void 0
|
|
3043
|
+
}
|
|
3044
|
+
),
|
|
3045
|
+
/* @__PURE__ */ jsxs9(CommandList, { children: [
|
|
3046
|
+
/* @__PURE__ */ jsx28(CommandEmpty, { children: emptyStateMessage({
|
|
3047
|
+
mode,
|
|
3048
|
+
search,
|
|
3049
|
+
minChars,
|
|
3050
|
+
isLoading,
|
|
3051
|
+
hasSearched,
|
|
3052
|
+
emptyMessage
|
|
3053
|
+
}) }),
|
|
3054
|
+
/* @__PURE__ */ jsx28(CommandGroup, { children: options.map((option) => /* @__PURE__ */ jsxs9(
|
|
3055
|
+
CommandItem,
|
|
3056
|
+
{
|
|
3057
|
+
value: option.value,
|
|
3058
|
+
onSelect: handleSelect,
|
|
3059
|
+
children: [
|
|
3060
|
+
/* @__PURE__ */ jsx28(
|
|
3061
|
+
Check4,
|
|
3062
|
+
{
|
|
3063
|
+
className: cn26(
|
|
3064
|
+
"mr-2 h-4 w-4",
|
|
3065
|
+
value === option.value ? "opacity-100" : "opacity-0"
|
|
3066
|
+
)
|
|
3067
|
+
}
|
|
3068
|
+
),
|
|
3069
|
+
option.label
|
|
3070
|
+
]
|
|
3071
|
+
},
|
|
3072
|
+
option.value
|
|
3073
|
+
)) })
|
|
3074
|
+
] })
|
|
3075
|
+
] });
|
|
3076
|
+
}
|
|
3077
|
+
function EntitySelect(props) {
|
|
3078
|
+
const {
|
|
3079
|
+
fetcher,
|
|
3080
|
+
queryKey,
|
|
3081
|
+
value,
|
|
3082
|
+
onChange,
|
|
3083
|
+
onChangeOption,
|
|
3084
|
+
onBlur,
|
|
3085
|
+
label,
|
|
3086
|
+
required,
|
|
3087
|
+
description,
|
|
3088
|
+
error,
|
|
3089
|
+
className,
|
|
3090
|
+
id
|
|
3091
|
+
} = props;
|
|
3092
|
+
const {
|
|
3093
|
+
mode,
|
|
3094
|
+
minChars,
|
|
3095
|
+
placeholder,
|
|
3096
|
+
searchPlaceholder,
|
|
3097
|
+
emptyMessage,
|
|
3098
|
+
disabled
|
|
3099
|
+
} = resolveEntitySelectDefaults(props);
|
|
3100
|
+
const [open, setOpen] = React24.useState(false);
|
|
3101
|
+
const [selectedLabel, setSelectedLabel] = React24.useState(null);
|
|
3102
|
+
const {
|
|
3103
|
+
options,
|
|
3104
|
+
isLoading,
|
|
3105
|
+
search,
|
|
3106
|
+
setSearch,
|
|
3107
|
+
submit: rawSubmit
|
|
3108
|
+
} = useEntityOptions({ fetcher, queryKey, mode, minChars });
|
|
3109
|
+
const [hasSearched, submit] = useHasSearched(rawSubmit);
|
|
3110
|
+
React24.useEffect(() => {
|
|
3111
|
+
if (!value) {
|
|
3112
|
+
setSelectedLabel(null);
|
|
3113
|
+
return;
|
|
3114
|
+
}
|
|
3115
|
+
const found = options.find((opt) => opt.value === value);
|
|
3116
|
+
if (found) {
|
|
3117
|
+
setSelectedLabel(found.label);
|
|
3118
|
+
}
|
|
3119
|
+
}, [value, options]);
|
|
3120
|
+
const handleSelect = React24.useCallback(
|
|
3121
|
+
(selectedValue) => {
|
|
3122
|
+
const opt = options.find((o) => o.value === selectedValue);
|
|
3123
|
+
if (opt) setSelectedLabel(opt.label);
|
|
3124
|
+
const newValue = selectedValue === value ? null : selectedValue;
|
|
3125
|
+
if (newValue === null) setSelectedLabel(null);
|
|
3126
|
+
onChange?.(newValue);
|
|
3127
|
+
onChangeOption?.(newValue === null ? null : opt ?? null);
|
|
3128
|
+
setOpen(false);
|
|
3129
|
+
setSearch("");
|
|
3130
|
+
},
|
|
3131
|
+
[options, value, onChange, onChangeOption, setSearch]
|
|
3132
|
+
);
|
|
3133
|
+
const handleOpenChange = React24.useCallback(
|
|
3134
|
+
(newOpen) => {
|
|
3135
|
+
setOpen(newOpen);
|
|
3136
|
+
if (!newOpen) {
|
|
3137
|
+
setSearch("");
|
|
3138
|
+
onBlur?.();
|
|
3139
|
+
}
|
|
3140
|
+
},
|
|
3141
|
+
[onBlur, setSearch]
|
|
3142
|
+
);
|
|
3143
|
+
return /* @__PURE__ */ jsxs9("div", { className: cn26("space-y-2", className), children: [
|
|
3144
|
+
/* @__PURE__ */ jsx28(
|
|
3145
|
+
EntitySelectLabel,
|
|
3146
|
+
{
|
|
3147
|
+
id,
|
|
3148
|
+
label,
|
|
3149
|
+
required,
|
|
3150
|
+
description
|
|
3151
|
+
}
|
|
3152
|
+
),
|
|
3153
|
+
/* @__PURE__ */ jsxs9(Popover, { open, onOpenChange: handleOpenChange, children: [
|
|
3154
|
+
/* @__PURE__ */ jsx28(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsxs9(
|
|
3155
|
+
Button,
|
|
3156
|
+
{
|
|
3157
|
+
id,
|
|
3158
|
+
variant: "outline",
|
|
3159
|
+
role: "combobox",
|
|
3160
|
+
"aria-expanded": open,
|
|
3161
|
+
className: cn26(
|
|
3162
|
+
"w-full justify-between",
|
|
3163
|
+
!selectedLabel && "text-muted-foreground",
|
|
3164
|
+
error && "border-destructive"
|
|
3165
|
+
),
|
|
3166
|
+
disabled,
|
|
3167
|
+
children: [
|
|
3168
|
+
/* @__PURE__ */ jsx28("span", { className: "truncate", children: selectedLabel ?? placeholder }),
|
|
3169
|
+
/* @__PURE__ */ jsxs9("div", { className: "flex items-center gap-1", children: [
|
|
3170
|
+
isLoading && /* @__PURE__ */ jsx28(Loader2, { className: "h-4 w-4 animate-spin opacity-50" }),
|
|
3171
|
+
/* @__PURE__ */ jsx28(ChevronsUpDown, { className: "h-4 w-4 shrink-0 opacity-50" })
|
|
3172
|
+
] })
|
|
3173
|
+
]
|
|
3174
|
+
}
|
|
3175
|
+
) }),
|
|
3176
|
+
/* @__PURE__ */ jsx28(
|
|
3177
|
+
PopoverContent,
|
|
3178
|
+
{
|
|
3179
|
+
className: "w-[--radix-popover-trigger-width] p-0",
|
|
3180
|
+
align: "start",
|
|
3181
|
+
children: /* @__PURE__ */ jsx28(
|
|
3182
|
+
EntitySelectDropdown,
|
|
3183
|
+
{
|
|
3184
|
+
searchPlaceholder,
|
|
3185
|
+
search,
|
|
3186
|
+
setSearch,
|
|
3187
|
+
mode,
|
|
3188
|
+
submit,
|
|
3189
|
+
minChars,
|
|
3190
|
+
isLoading,
|
|
3191
|
+
hasSearched,
|
|
3192
|
+
emptyMessage,
|
|
3193
|
+
options,
|
|
3194
|
+
value,
|
|
3195
|
+
handleSelect
|
|
3196
|
+
}
|
|
3197
|
+
)
|
|
3198
|
+
}
|
|
3199
|
+
)
|
|
3200
|
+
] }),
|
|
3201
|
+
error && /* @__PURE__ */ jsx28("p", { className: "text-sm font-medium text-destructive", children: error })
|
|
3202
|
+
] });
|
|
3203
|
+
}
|
|
3204
|
+
|
|
3205
|
+
// src/EntityTree/EntityTree.tsx
|
|
3206
|
+
import {
|
|
3207
|
+
forwardRef as forwardRef19,
|
|
3208
|
+
useImperativeHandle as useImperativeHandle2,
|
|
3209
|
+
useMemo as useMemo4,
|
|
3210
|
+
useRef as useRef6
|
|
3211
|
+
} from "react";
|
|
3212
|
+
import { Pencil } from "lucide-react";
|
|
3213
|
+
import { cn as cn28 } from "@petrarca/sonnet-core";
|
|
3214
|
+
|
|
3215
|
+
// src/TreeView/TreeView.tsx
|
|
3216
|
+
import {
|
|
3217
|
+
forwardRef as forwardRef18,
|
|
3218
|
+
useCallback as useCallback4,
|
|
3219
|
+
useEffect as useEffect5,
|
|
3220
|
+
useId as useId2,
|
|
3221
|
+
useImperativeHandle,
|
|
3222
|
+
useMemo as useMemo3,
|
|
3223
|
+
useRef as useRef5,
|
|
3224
|
+
useState as useState4
|
|
3225
|
+
} from "react";
|
|
3226
|
+
import { ChevronRight as ChevronRight3 } from "lucide-react";
|
|
3227
|
+
import { cn as cn27 } from "@petrarca/sonnet-core";
|
|
3228
|
+
import { jsx as jsx29, jsxs as jsxs10 } from "react/jsx-runtime";
|
|
3229
|
+
function flatten(nodes, expandedIds, depth = 0, parentId = null, result = []) {
|
|
3230
|
+
for (const node of nodes) {
|
|
3231
|
+
result.push({ node, depth, parentId });
|
|
3232
|
+
if (expandedIds.has(node.id) && node.children) {
|
|
3233
|
+
flatten(node.children, expandedIds, depth + 1, node.id, result);
|
|
3234
|
+
}
|
|
3235
|
+
}
|
|
3236
|
+
return result;
|
|
3237
|
+
}
|
|
3238
|
+
var UNCONSTRAINED = /* @__PURE__ */ Symbol.for("tree-view:unconstrained");
|
|
3239
|
+
function ToggleIcon({
|
|
3240
|
+
hasChildren,
|
|
3241
|
+
isLoading,
|
|
3242
|
+
isExpanded
|
|
3243
|
+
}) {
|
|
3244
|
+
if (!hasChildren) {
|
|
3245
|
+
return /* @__PURE__ */ jsx29("span", { className: "block w-1.5 h-1.5 rounded-full bg-border mx-auto" });
|
|
3246
|
+
}
|
|
3247
|
+
if (isLoading) {
|
|
3248
|
+
return /* @__PURE__ */ jsx29("span", { className: "block w-3 h-3 rounded-full border-2 border-muted-foreground/40 border-t-foreground animate-spin" });
|
|
3249
|
+
}
|
|
3250
|
+
return /* @__PURE__ */ jsx29(
|
|
3251
|
+
ChevronRight3,
|
|
3252
|
+
{
|
|
3253
|
+
className: cn27(
|
|
3254
|
+
"h-3.5 w-3.5 text-muted-foreground transition-transform",
|
|
3255
|
+
isExpanded && "rotate-90"
|
|
3256
|
+
)
|
|
3257
|
+
}
|
|
3258
|
+
);
|
|
3259
|
+
}
|
|
3260
|
+
function NodeRow({
|
|
3261
|
+
flat,
|
|
3262
|
+
expandedIds,
|
|
3263
|
+
selectedIds,
|
|
3264
|
+
isSelectable,
|
|
3265
|
+
isFocused,
|
|
3266
|
+
indentPx,
|
|
3267
|
+
treeId,
|
|
3268
|
+
onExpand,
|
|
3269
|
+
onCollapse,
|
|
3270
|
+
onNodeClick,
|
|
3271
|
+
onSelectionChange,
|
|
3272
|
+
onFocus,
|
|
3273
|
+
renderNode
|
|
3274
|
+
}) {
|
|
3275
|
+
const { node, depth } = flat;
|
|
3276
|
+
const isExpanded = expandedIds.has(node.id);
|
|
3277
|
+
const isSelected = selectedIds.has(node.id);
|
|
3278
|
+
const handleToggle = useCallback4(
|
|
3279
|
+
(e) => {
|
|
3280
|
+
e.stopPropagation();
|
|
3281
|
+
if (isExpanded) {
|
|
3282
|
+
onCollapse(node.id);
|
|
3283
|
+
} else if (node.hasChildren) {
|
|
3284
|
+
onExpand(node.id);
|
|
3285
|
+
}
|
|
3286
|
+
},
|
|
3287
|
+
[isExpanded, node.id, node.hasChildren, onExpand, onCollapse]
|
|
3288
|
+
);
|
|
3289
|
+
const handleClick = useCallback4(() => {
|
|
3290
|
+
onFocus(node.id);
|
|
3291
|
+
onNodeClick?.(node);
|
|
3292
|
+
}, [node, onFocus, onNodeClick]);
|
|
3293
|
+
const handleCheckChange = useCallback4(
|
|
3294
|
+
(e) => {
|
|
3295
|
+
onSelectionChange?.(node.id, e.target.checked);
|
|
3296
|
+
},
|
|
3297
|
+
[node.id, onSelectionChange]
|
|
3298
|
+
);
|
|
3299
|
+
return /* @__PURE__ */ jsxs10(
|
|
3300
|
+
"li",
|
|
3301
|
+
{
|
|
3302
|
+
id: `${treeId}-node-${node.id}`,
|
|
3303
|
+
role: "treeitem",
|
|
3304
|
+
"aria-expanded": node.hasChildren ? isExpanded : void 0,
|
|
3305
|
+
"aria-selected": onSelectionChange ? isSelected : void 0,
|
|
3306
|
+
"aria-busy": node.isLoading,
|
|
3307
|
+
tabIndex: isFocused ? 0 : -1,
|
|
3308
|
+
className: cn27(
|
|
3309
|
+
"flex items-center gap-1 px-2 py-1.5 rounded-sm text-sm select-none",
|
|
3310
|
+
"cursor-pointer transition-colors",
|
|
3311
|
+
"hover:bg-accent/60",
|
|
3312
|
+
isFocused && "bg-accent ring-1 ring-ring ring-inset outline-none",
|
|
3313
|
+
isSelected && "bg-primary/10"
|
|
3314
|
+
),
|
|
3315
|
+
style: { paddingLeft: depth * indentPx + 8 },
|
|
3316
|
+
onClick: handleClick,
|
|
3317
|
+
children: [
|
|
3318
|
+
/* @__PURE__ */ jsx29(
|
|
3319
|
+
"span",
|
|
3320
|
+
{
|
|
3321
|
+
className: "shrink-0 flex items-center justify-center w-4 h-4",
|
|
3322
|
+
onClick: handleToggle,
|
|
3323
|
+
"aria-hidden": true,
|
|
3324
|
+
children: /* @__PURE__ */ jsx29(
|
|
3325
|
+
ToggleIcon,
|
|
3326
|
+
{
|
|
3327
|
+
hasChildren: node.hasChildren,
|
|
3328
|
+
isLoading: node.isLoading,
|
|
3329
|
+
isExpanded
|
|
3330
|
+
}
|
|
3331
|
+
)
|
|
3332
|
+
}
|
|
3333
|
+
),
|
|
3334
|
+
onSelectionChange && isSelectable && /* @__PURE__ */ jsx29(
|
|
3335
|
+
"input",
|
|
3336
|
+
{
|
|
3337
|
+
type: "checkbox",
|
|
3338
|
+
className: "shrink-0 h-3.5 w-3.5 rounded",
|
|
3339
|
+
checked: isSelected,
|
|
3340
|
+
onChange: handleCheckChange,
|
|
3341
|
+
onClick: (e) => e.stopPropagation(),
|
|
3342
|
+
"aria-label": `Select ${node.id}`,
|
|
3343
|
+
tabIndex: -1
|
|
3344
|
+
}
|
|
3345
|
+
),
|
|
3346
|
+
/* @__PURE__ */ jsx29("span", { className: "flex-1 min-w-0", children: renderNode(node, depth) }),
|
|
3347
|
+
node.isError && /* @__PURE__ */ jsx29("span", { className: "shrink-0 text-xs text-destructive", "aria-live": "polite", children: "Failed to load" })
|
|
3348
|
+
]
|
|
3349
|
+
}
|
|
3350
|
+
);
|
|
3351
|
+
}
|
|
3352
|
+
function TreeViewInner({
|
|
3353
|
+
nodes,
|
|
3354
|
+
expandedIds,
|
|
3355
|
+
onExpand,
|
|
3356
|
+
onCollapse,
|
|
3357
|
+
onNodeClick,
|
|
3358
|
+
onNodeActivate,
|
|
3359
|
+
renderNode,
|
|
3360
|
+
selectedIds,
|
|
3361
|
+
onSelectionChange,
|
|
3362
|
+
constrainSelectionToSiblings,
|
|
3363
|
+
indentPx = 20,
|
|
3364
|
+
className
|
|
3365
|
+
}, ref) {
|
|
3366
|
+
const treeId = useId2();
|
|
3367
|
+
const [focusedId, setFocusedId] = useState4(null);
|
|
3368
|
+
const listRef = useRef5(null);
|
|
3369
|
+
const [constrainedParent, setConstrainedParent] = useState4(UNCONSTRAINED);
|
|
3370
|
+
const selectionSize = selectedIds?.size ?? 0;
|
|
3371
|
+
useEffect5(() => {
|
|
3372
|
+
if (selectionSize === 0) setConstrainedParent(UNCONSTRAINED);
|
|
3373
|
+
}, [selectionSize]);
|
|
3374
|
+
useImperativeHandle(
|
|
3375
|
+
ref,
|
|
3376
|
+
() => ({
|
|
3377
|
+
focus() {
|
|
3378
|
+
const ul = listRef.current;
|
|
3379
|
+
if (!ul) return;
|
|
3380
|
+
const target = focusedId ? ul.querySelector(
|
|
3381
|
+
`[id$="-node-${CSS.escape(focusedId)}"]`
|
|
3382
|
+
) : ul.querySelector('[role="treeitem"]');
|
|
3383
|
+
target?.focus();
|
|
3384
|
+
}
|
|
3385
|
+
}),
|
|
3386
|
+
[focusedId]
|
|
3387
|
+
);
|
|
3388
|
+
const flatNodes = useMemo3(
|
|
3389
|
+
() => flatten(nodes, expandedIds),
|
|
3390
|
+
[nodes, expandedIds]
|
|
3391
|
+
);
|
|
3392
|
+
const parentById = useMemo3(() => {
|
|
3393
|
+
const map = /* @__PURE__ */ new Map();
|
|
3394
|
+
for (const f of flatNodes) map.set(f.node.id, f.parentId);
|
|
3395
|
+
return map;
|
|
3396
|
+
}, [flatNodes]);
|
|
3397
|
+
const selectableSet = useMemo3(() => {
|
|
3398
|
+
if (!constrainSelectionToSiblings || constrainedParent === UNCONSTRAINED) {
|
|
3399
|
+
return void 0;
|
|
3400
|
+
}
|
|
3401
|
+
const ids = /* @__PURE__ */ new Set();
|
|
3402
|
+
for (const f of flatNodes) {
|
|
3403
|
+
if (f.parentId === constrainedParent) ids.add(f.node.id);
|
|
3404
|
+
}
|
|
3405
|
+
return ids;
|
|
3406
|
+
}, [constrainSelectionToSiblings, constrainedParent, flatNodes]);
|
|
3407
|
+
const handleSelectionChange = useCallback4(
|
|
3408
|
+
(nodeId, selected) => {
|
|
3409
|
+
if (constrainSelectionToSiblings && selected && constrainedParent === UNCONSTRAINED) {
|
|
3410
|
+
setConstrainedParent(parentById.get(nodeId) ?? null);
|
|
3411
|
+
}
|
|
3412
|
+
onSelectionChange?.(nodeId, selected);
|
|
3413
|
+
},
|
|
3414
|
+
[
|
|
3415
|
+
constrainSelectionToSiblings,
|
|
3416
|
+
constrainedParent,
|
|
3417
|
+
parentById,
|
|
3418
|
+
onSelectionChange
|
|
3419
|
+
]
|
|
3420
|
+
);
|
|
3421
|
+
const focusedIndex = flatNodes.findIndex((f) => f.node.id === focusedId);
|
|
3422
|
+
const moveFocus = useCallback4(
|
|
3423
|
+
(delta) => {
|
|
3424
|
+
if (flatNodes.length === 0) return;
|
|
3425
|
+
const next = Math.max(
|
|
3426
|
+
0,
|
|
3427
|
+
Math.min(flatNodes.length - 1, focusedIndex + delta)
|
|
3428
|
+
);
|
|
3429
|
+
const target = flatNodes[next];
|
|
3430
|
+
if (target) {
|
|
3431
|
+
setFocusedId(target.node.id);
|
|
3432
|
+
const el = listRef.current?.querySelector(
|
|
3433
|
+
`#${CSS.escape(`${treeId}-node-${target.node.id}`)}`
|
|
3434
|
+
);
|
|
3435
|
+
el?.scrollIntoView({ block: "nearest" });
|
|
3436
|
+
el?.focus();
|
|
3437
|
+
}
|
|
3438
|
+
},
|
|
3439
|
+
[flatNodes, focusedIndex, treeId]
|
|
3440
|
+
);
|
|
3441
|
+
const handleArrowRight = useCallback4(
|
|
3442
|
+
(current) => {
|
|
3443
|
+
if (current.node.hasChildren && !expandedIds.has(current.node.id)) {
|
|
3444
|
+
onExpand(current.node.id);
|
|
3445
|
+
} else if (current.node.children && current.node.children.length > 0) {
|
|
3446
|
+
moveFocus(1);
|
|
3447
|
+
}
|
|
3448
|
+
},
|
|
3449
|
+
[expandedIds, onExpand, moveFocus]
|
|
3450
|
+
);
|
|
3451
|
+
const handleArrowLeft = useCallback4(
|
|
3452
|
+
(current) => {
|
|
3453
|
+
if (expandedIds.has(current.node.id)) {
|
|
3454
|
+
onCollapse(current.node.id);
|
|
3455
|
+
return;
|
|
3456
|
+
}
|
|
3457
|
+
if (current.parentId === null) return;
|
|
3458
|
+
const parentIdx = flatNodes.findIndex(
|
|
3459
|
+
(f) => f.node.id === current.parentId
|
|
3460
|
+
);
|
|
3461
|
+
if (parentIdx < 0) return;
|
|
3462
|
+
const parentFlat = flatNodes[parentIdx];
|
|
3463
|
+
if (!parentFlat) return;
|
|
3464
|
+
setFocusedId(parentFlat.node.id);
|
|
3465
|
+
const el = listRef.current?.querySelector(
|
|
3466
|
+
`#${CSS.escape(`${treeId}-node-${parentFlat.node.id}`)}`
|
|
3467
|
+
);
|
|
3468
|
+
el?.focus();
|
|
3469
|
+
},
|
|
3470
|
+
[expandedIds, onCollapse, flatNodes, treeId]
|
|
3471
|
+
);
|
|
3472
|
+
const getFocusedNode = useCallback4(() => {
|
|
3473
|
+
if (focusedIndex < 0) return null;
|
|
3474
|
+
return flatNodes[focusedIndex] ?? null;
|
|
3475
|
+
}, [flatNodes, focusedIndex]);
|
|
3476
|
+
const activateNode = useCallback4(
|
|
3477
|
+
(flat) => {
|
|
3478
|
+
if (flat) onNodeActivate?.(flat.node);
|
|
3479
|
+
},
|
|
3480
|
+
[onNodeActivate]
|
|
3481
|
+
);
|
|
3482
|
+
const handleKeyDown = useCallback4(
|
|
3483
|
+
(e) => {
|
|
3484
|
+
switch (e.key) {
|
|
3485
|
+
case "ArrowDown":
|
|
3486
|
+
e.preventDefault();
|
|
3487
|
+
moveFocus(1);
|
|
3488
|
+
break;
|
|
3489
|
+
case "ArrowUp":
|
|
3490
|
+
e.preventDefault();
|
|
3491
|
+
moveFocus(-1);
|
|
3492
|
+
break;
|
|
3493
|
+
case "ArrowRight": {
|
|
3494
|
+
e.preventDefault();
|
|
3495
|
+
const current = getFocusedNode();
|
|
3496
|
+
if (current) handleArrowRight(current);
|
|
3497
|
+
break;
|
|
3498
|
+
}
|
|
3499
|
+
case "ArrowLeft": {
|
|
3500
|
+
e.preventDefault();
|
|
3501
|
+
const current = getFocusedNode();
|
|
3502
|
+
if (current) handleArrowLeft(current);
|
|
3503
|
+
break;
|
|
3504
|
+
}
|
|
3505
|
+
case "Enter":
|
|
3506
|
+
case " ":
|
|
3507
|
+
e.preventDefault();
|
|
3508
|
+
activateNode(getFocusedNode());
|
|
3509
|
+
break;
|
|
3510
|
+
default:
|
|
3511
|
+
break;
|
|
3512
|
+
}
|
|
3513
|
+
},
|
|
3514
|
+
[
|
|
3515
|
+
moveFocus,
|
|
3516
|
+
handleArrowRight,
|
|
3517
|
+
handleArrowLeft,
|
|
3518
|
+
getFocusedNode,
|
|
3519
|
+
activateNode
|
|
3520
|
+
]
|
|
3521
|
+
);
|
|
3522
|
+
if (nodes.length === 0) {
|
|
3523
|
+
return /* @__PURE__ */ jsx29(
|
|
3524
|
+
"div",
|
|
3525
|
+
{
|
|
3526
|
+
className: cn27(
|
|
3527
|
+
"py-8 text-center text-sm text-muted-foreground",
|
|
3528
|
+
className
|
|
3529
|
+
),
|
|
3530
|
+
children: "No items."
|
|
3531
|
+
}
|
|
3532
|
+
);
|
|
3533
|
+
}
|
|
3534
|
+
return /* @__PURE__ */ jsx29(
|
|
3535
|
+
"ul",
|
|
3536
|
+
{
|
|
3537
|
+
ref: listRef,
|
|
3538
|
+
role: "tree",
|
|
3539
|
+
"aria-label": "Tree",
|
|
3540
|
+
className: cn27("outline-none", className),
|
|
3541
|
+
onKeyDown: handleKeyDown,
|
|
3542
|
+
children: flatNodes.map((flat) => /* @__PURE__ */ jsx29(
|
|
3543
|
+
NodeRow,
|
|
3544
|
+
{
|
|
3545
|
+
flat,
|
|
3546
|
+
expandedIds,
|
|
3547
|
+
selectedIds: selectedIds ?? /* @__PURE__ */ new Set(),
|
|
3548
|
+
isSelectable: !selectableSet || selectableSet.has(flat.node.id),
|
|
3549
|
+
isFocused: flat.node.id === focusedId,
|
|
3550
|
+
indentPx,
|
|
3551
|
+
treeId,
|
|
3552
|
+
onExpand,
|
|
3553
|
+
onCollapse,
|
|
3554
|
+
onNodeClick,
|
|
3555
|
+
onSelectionChange: onSelectionChange ? handleSelectionChange : void 0,
|
|
3556
|
+
onFocus: setFocusedId,
|
|
3557
|
+
renderNode
|
|
3558
|
+
},
|
|
3559
|
+
flat.node.id
|
|
3560
|
+
))
|
|
3561
|
+
}
|
|
3562
|
+
);
|
|
3563
|
+
}
|
|
3564
|
+
var TreeView = forwardRef18(TreeViewInner);
|
|
3565
|
+
|
|
3566
|
+
// src/EntityTree/EntityTree.tsx
|
|
3567
|
+
import { jsx as jsx30, jsxs as jsxs11 } from "react/jsx-runtime";
|
|
3568
|
+
function buildCellRow(resolved, activeNodeIdRef, onNodeEdit, localRenderers) {
|
|
3569
|
+
return function renderCells(node) {
|
|
3570
|
+
const isActive = node.id === activeNodeIdRef.current;
|
|
3571
|
+
return /* @__PURE__ */ jsxs11("span", { className: "flex items-center gap-4 min-w-0 w-full group/row", children: [
|
|
3572
|
+
resolved.map((col) => {
|
|
3573
|
+
const Renderer = resolveCellRenderer2(
|
|
3574
|
+
col.cellRenderer,
|
|
3575
|
+
localRenderers
|
|
3576
|
+
);
|
|
3577
|
+
const value = node.data[col.key];
|
|
3578
|
+
return /* @__PURE__ */ jsx30(
|
|
3579
|
+
"span",
|
|
3580
|
+
{
|
|
3581
|
+
className: "shrink-0",
|
|
3582
|
+
style: col.width !== void 0 ? { width: col.width } : void 0,
|
|
3583
|
+
children: /* @__PURE__ */ jsx30(
|
|
3584
|
+
Renderer,
|
|
3585
|
+
{
|
|
3586
|
+
value,
|
|
3587
|
+
row: node.data,
|
|
3588
|
+
options: col.cellOptions
|
|
3589
|
+
}
|
|
3590
|
+
)
|
|
3591
|
+
},
|
|
3592
|
+
col.key
|
|
3593
|
+
);
|
|
3594
|
+
}),
|
|
3595
|
+
onNodeEdit && /* @__PURE__ */ jsx30("span", { className: "ml-auto shrink-0", children: /* @__PURE__ */ jsx30(
|
|
3596
|
+
"button",
|
|
3597
|
+
{
|
|
3598
|
+
type: "button",
|
|
3599
|
+
onClick: (e) => {
|
|
3600
|
+
e.stopPropagation();
|
|
3601
|
+
onNodeEdit(node);
|
|
3602
|
+
},
|
|
3603
|
+
"aria-label": "Edit",
|
|
3604
|
+
className: cn28(
|
|
3605
|
+
"p-1 rounded hover:bg-background text-muted-foreground hover:text-foreground transition-colors",
|
|
3606
|
+
isActive ? "opacity-100" : "opacity-0 group-hover/row:opacity-100"
|
|
3607
|
+
),
|
|
3608
|
+
children: /* @__PURE__ */ jsx30(Pencil, { className: "h-3.5 w-3.5" })
|
|
3609
|
+
}
|
|
3610
|
+
) })
|
|
3611
|
+
] });
|
|
3612
|
+
};
|
|
3613
|
+
}
|
|
3614
|
+
function resolveEntityTreeDefaults(props) {
|
|
3615
|
+
return {
|
|
3616
|
+
isLoading: props.isLoading ?? false,
|
|
3617
|
+
isError: props.isError ?? false,
|
|
3618
|
+
entityLabel: props.entityLabel ?? "items",
|
|
3619
|
+
selectionMode: props.selectionMode ?? false
|
|
3620
|
+
};
|
|
3621
|
+
}
|
|
3622
|
+
function renderTreeStatus({
|
|
3623
|
+
isLoading,
|
|
3624
|
+
isError,
|
|
3625
|
+
errorMessage,
|
|
3626
|
+
entityLabel,
|
|
3627
|
+
nodes,
|
|
3628
|
+
className
|
|
3629
|
+
}) {
|
|
3630
|
+
if (isLoading && nodes.length === 0) {
|
|
3631
|
+
return /* @__PURE__ */ jsx30(
|
|
3632
|
+
"div",
|
|
3633
|
+
{
|
|
3634
|
+
className: cn28(
|
|
3635
|
+
"py-8 text-center text-sm text-muted-foreground",
|
|
3636
|
+
className
|
|
3637
|
+
),
|
|
3638
|
+
children: "Loading\\u2026"
|
|
3639
|
+
}
|
|
3640
|
+
);
|
|
3641
|
+
}
|
|
3642
|
+
if (isError) {
|
|
3643
|
+
return /* @__PURE__ */ jsx30(
|
|
3644
|
+
"div",
|
|
3645
|
+
{
|
|
3646
|
+
className: cn28("py-8 text-center text-sm text-destructive", className),
|
|
3647
|
+
children: errorMessage ?? `Failed to load ${entityLabel}.`
|
|
3648
|
+
}
|
|
3649
|
+
);
|
|
3650
|
+
}
|
|
3651
|
+
if (!isLoading && nodes.length === 0) {
|
|
3652
|
+
return /* @__PURE__ */ jsxs11(
|
|
3653
|
+
"div",
|
|
3654
|
+
{
|
|
3655
|
+
className: cn28(
|
|
3656
|
+
"py-8 text-center text-sm text-muted-foreground",
|
|
3657
|
+
className
|
|
3658
|
+
),
|
|
3659
|
+
children: [
|
|
3660
|
+
"No ",
|
|
3661
|
+
entityLabel,
|
|
3662
|
+
" yet."
|
|
3663
|
+
]
|
|
3664
|
+
}
|
|
3665
|
+
);
|
|
3666
|
+
}
|
|
3667
|
+
return null;
|
|
3668
|
+
}
|
|
3669
|
+
function resolveRenderNode(renderNodeOverride, defaultRenderNode) {
|
|
3670
|
+
if (!renderNodeOverride) return defaultRenderNode;
|
|
3671
|
+
return (node, depth) => {
|
|
3672
|
+
const override = renderNodeOverride(node, depth);
|
|
3673
|
+
if (override !== null) return override;
|
|
3674
|
+
return defaultRenderNode(node);
|
|
3675
|
+
};
|
|
3676
|
+
}
|
|
3677
|
+
function resolveSelectionProps(selectionMode, props) {
|
|
3678
|
+
return {
|
|
3679
|
+
effectiveOnNodeEdit: selectionMode ? void 0 : props.onNodeEdit,
|
|
3680
|
+
onNodeActivate: selectionMode ? void 0 : props.onNodeEdit,
|
|
3681
|
+
selectedIds: selectionMode ? props.selectedIds : void 0,
|
|
3682
|
+
onSelectionChange: selectionMode ? props.onSelectionChange : void 0,
|
|
3683
|
+
constrainSelectionToSiblings: selectionMode ? props.constrainSelectionToSiblings : void 0
|
|
3684
|
+
};
|
|
3685
|
+
}
|
|
3686
|
+
function EntityTreeInner(props, ref) {
|
|
3687
|
+
const {
|
|
3688
|
+
schema,
|
|
3689
|
+
uiSchema,
|
|
3690
|
+
renderers,
|
|
3691
|
+
nodes,
|
|
3692
|
+
expandedIds,
|
|
3693
|
+
onExpand,
|
|
3694
|
+
onCollapse,
|
|
3695
|
+
errorMessage,
|
|
3696
|
+
onNodeSelect,
|
|
3697
|
+
onNodeEdit,
|
|
3698
|
+
activeNodeId,
|
|
3699
|
+
selectedIds,
|
|
3700
|
+
onSelectionChange,
|
|
3701
|
+
constrainSelectionToSiblings,
|
|
3702
|
+
renderNodeOverride,
|
|
3703
|
+
indentPx,
|
|
3704
|
+
className
|
|
3705
|
+
} = props;
|
|
3706
|
+
const { isLoading, isError, entityLabel, selectionMode } = resolveEntityTreeDefaults(props);
|
|
3707
|
+
const treeRef = useRef6(null);
|
|
3708
|
+
useImperativeHandle2(ref, () => ({
|
|
3709
|
+
focus() {
|
|
3710
|
+
treeRef.current?.focus();
|
|
3711
|
+
}
|
|
3712
|
+
}));
|
|
3713
|
+
const resolved = useMemo4(() => {
|
|
3714
|
+
if (renderers) validateRendererNames(renderers);
|
|
3715
|
+
return resolveColumns(schema, uiSchema);
|
|
3716
|
+
}, [schema, uiSchema, renderers]);
|
|
3717
|
+
const activeNodeIdRef = useRef6(activeNodeId);
|
|
3718
|
+
activeNodeIdRef.current = activeNodeId;
|
|
3719
|
+
const selection = resolveSelectionProps(selectionMode, {
|
|
3720
|
+
onNodeEdit,
|
|
3721
|
+
selectedIds,
|
|
3722
|
+
onSelectionChange,
|
|
3723
|
+
constrainSelectionToSiblings
|
|
3724
|
+
});
|
|
3725
|
+
const defaultRenderNode = useMemo4(
|
|
3726
|
+
() => buildCellRow(
|
|
3727
|
+
resolved,
|
|
3728
|
+
activeNodeIdRef,
|
|
3729
|
+
selection.effectiveOnNodeEdit,
|
|
3730
|
+
renderers
|
|
3731
|
+
),
|
|
3732
|
+
[resolved, selection.effectiveOnNodeEdit, renderers]
|
|
3733
|
+
);
|
|
3734
|
+
const renderNode = useMemo4(
|
|
3735
|
+
() => resolveRenderNode(renderNodeOverride, defaultRenderNode),
|
|
3736
|
+
[renderNodeOverride, defaultRenderNode]
|
|
3737
|
+
);
|
|
3738
|
+
const statusView = renderTreeStatus({
|
|
3739
|
+
isLoading,
|
|
3740
|
+
isError,
|
|
3741
|
+
errorMessage,
|
|
3742
|
+
entityLabel,
|
|
3743
|
+
nodes,
|
|
3744
|
+
className
|
|
3745
|
+
});
|
|
3746
|
+
if (statusView) return statusView;
|
|
3747
|
+
return /* @__PURE__ */ jsx30("div", { className: cn28("rounded-lg border", className), children: /* @__PURE__ */ jsx30(
|
|
3748
|
+
TreeView,
|
|
3749
|
+
{
|
|
3750
|
+
ref: treeRef,
|
|
3751
|
+
nodes,
|
|
3752
|
+
expandedIds,
|
|
3753
|
+
onExpand,
|
|
3754
|
+
onCollapse,
|
|
3755
|
+
onNodeClick: onNodeSelect,
|
|
3756
|
+
onNodeActivate: selection.onNodeActivate,
|
|
3757
|
+
renderNode,
|
|
3758
|
+
selectedIds: selection.selectedIds,
|
|
3759
|
+
onSelectionChange: selection.onSelectionChange,
|
|
3760
|
+
constrainSelectionToSiblings: selection.constrainSelectionToSiblings,
|
|
3761
|
+
indentPx,
|
|
3762
|
+
className: "p-1"
|
|
3763
|
+
}
|
|
3764
|
+
) });
|
|
3765
|
+
}
|
|
3766
|
+
var EntityTree = forwardRef19(EntityTreeInner);
|
|
3767
|
+
|
|
3768
|
+
// src/SearchInput/SearchInput.tsx
|
|
3769
|
+
import React29, { useMemo as useMemo6, useRef as useRef9, useState as useState7, useEffect as useEffect9 } from "react";
|
|
3770
|
+
import { Search as Search2, X as X3, ChevronDown } from "lucide-react";
|
|
3771
|
+
import { cn as cn30 } from "@petrarca/sonnet-core";
|
|
3772
|
+
import { getFieldTitle as getFieldTitle2 } from "@petrarca/sonnet-core/schema";
|
|
3773
|
+
import { isEmptyFilterExpr } from "@petrarca/sonnet-core/search";
|
|
3774
|
+
|
|
3775
|
+
// src/SearchInput/useSearchInput.ts
|
|
3776
|
+
import { useCallback as useCallback5, useEffect as useEffect6, useMemo as useMemo5, useRef as useRef7, useState as useState5 } from "react";
|
|
3777
|
+
import { collectTerms } from "@petrarca/sonnet-core/search";
|
|
3778
|
+
|
|
3779
|
+
// src/SearchInput/schemaAdapter.ts
|
|
3780
|
+
import {
|
|
3781
|
+
getFieldTitle,
|
|
3782
|
+
getFieldHelpText,
|
|
3783
|
+
getFieldPlaceholder
|
|
3784
|
+
} from "@petrarca/sonnet-core/schema";
|
|
3785
|
+
function buildFacetDescriptors(schema) {
|
|
3786
|
+
const properties = schema.properties ?? {};
|
|
3787
|
+
const keys = Object.keys(properties);
|
|
3788
|
+
if (keys.length === 0) return [];
|
|
3789
|
+
const requiredSet = new Set(schema.required ?? []);
|
|
3790
|
+
const order = schema["x-ui-order"];
|
|
3791
|
+
const ordered = applyOrder(keys, order);
|
|
3792
|
+
return ordered.map((key) => {
|
|
3793
|
+
const property = properties[key];
|
|
3794
|
+
return {
|
|
3795
|
+
key,
|
|
3796
|
+
label: getFieldTitle(key, property),
|
|
3797
|
+
required: requiredSet.has(key),
|
|
3798
|
+
description: getFieldHelpText(property),
|
|
3799
|
+
placeholder: getFieldPlaceholder(property),
|
|
3800
|
+
property
|
|
3801
|
+
};
|
|
3802
|
+
});
|
|
3803
|
+
}
|
|
3804
|
+
function applyOrder(keys, order) {
|
|
3805
|
+
if (!order || order.length === 0) return keys;
|
|
3806
|
+
const keySet = new Set(keys);
|
|
3807
|
+
const ordered = [];
|
|
3808
|
+
for (const key of order) {
|
|
3809
|
+
if (keySet.has(key)) {
|
|
3810
|
+
ordered.push(key);
|
|
3811
|
+
keySet.delete(key);
|
|
3812
|
+
}
|
|
3813
|
+
}
|
|
3814
|
+
for (const key of keys) {
|
|
3815
|
+
if (keySet.has(key)) {
|
|
3816
|
+
ordered.push(key);
|
|
3817
|
+
}
|
|
3818
|
+
}
|
|
3819
|
+
return ordered;
|
|
3820
|
+
}
|
|
3821
|
+
|
|
3822
|
+
// src/SearchInput/useSearchInput.ts
|
|
3823
|
+
function buildSimpleExpr(text, searchFields) {
|
|
3824
|
+
const trimmed = text.trim();
|
|
3825
|
+
if (!trimmed) return { type: "and", children: [] };
|
|
3826
|
+
if (searchFields && searchFields.length > 0) {
|
|
3827
|
+
const terms = searchFields.map((key) => ({
|
|
3828
|
+
type: "term",
|
|
3829
|
+
key,
|
|
3830
|
+
value: trimmed
|
|
3831
|
+
}));
|
|
3832
|
+
if (terms.length === 1) {
|
|
3833
|
+
return { type: "and", children: terms };
|
|
3834
|
+
}
|
|
3835
|
+
return { type: "and", children: [{ type: "or", children: terms }] };
|
|
3836
|
+
}
|
|
3837
|
+
return {
|
|
3838
|
+
type: "and",
|
|
3839
|
+
children: [{ type: "term", key: "search", value: trimmed }]
|
|
3840
|
+
};
|
|
3841
|
+
}
|
|
3842
|
+
function useSearchInput({
|
|
3843
|
+
value,
|
|
3844
|
+
onChange,
|
|
3845
|
+
mode = "explicit",
|
|
3846
|
+
debounceMs = 300,
|
|
3847
|
+
schema,
|
|
3848
|
+
minChars = 0,
|
|
3849
|
+
searchFields
|
|
3850
|
+
}) {
|
|
3851
|
+
const allFacets = useMemo5(
|
|
3852
|
+
() => schema ? buildFacetDescriptors(schema) : [],
|
|
3853
|
+
[schema]
|
|
3854
|
+
);
|
|
3855
|
+
const isFaceted = allFacets.length > 0;
|
|
3856
|
+
const initialInput = !isFaceted ? (() => {
|
|
3857
|
+
const terms = collectTerms(value);
|
|
3858
|
+
return terms.length === 1 && terms[0].key === "search" ? terms[0].value : "";
|
|
3859
|
+
})() : "";
|
|
3860
|
+
const [inputValue, setInputValue] = useState5(initialInput);
|
|
3861
|
+
const [pendingFacet, setPendingFacet] = useState5(
|
|
3862
|
+
null
|
|
3863
|
+
);
|
|
3864
|
+
const [draftTerms, setDraftTerms] = useState5(
|
|
3865
|
+
() => isFaceted ? collectTerms(value) : []
|
|
3866
|
+
);
|
|
3867
|
+
const valueTerms = useMemo5(
|
|
3868
|
+
() => !isFaceted ? collectTerms(value) : [],
|
|
3869
|
+
[value, isFaceted]
|
|
3870
|
+
);
|
|
3871
|
+
const activeTerms = isFaceted ? draftTerms : valueTerms;
|
|
3872
|
+
const activeFieldKeys = useMemo5(
|
|
3873
|
+
() => new Set(activeTerms.map((t) => t.key)),
|
|
3874
|
+
[activeTerms]
|
|
3875
|
+
);
|
|
3876
|
+
const filteredFacets = useMemo5(() => {
|
|
3877
|
+
if (!isFaceted || pendingFacet || inputValue.endsWith(" ")) return [];
|
|
3878
|
+
const available = allFacets.filter((f) => !activeFieldKeys.has(f.key));
|
|
3879
|
+
if (!inputValue.trim()) return available;
|
|
3880
|
+
const lower = inputValue.toLowerCase();
|
|
3881
|
+
return available.filter((f) => f.label.toLowerCase().startsWith(lower));
|
|
3882
|
+
}, [inputValue, pendingFacet, isFaceted, allFacets, activeFieldKeys]);
|
|
3883
|
+
const onChangeRef = useRef7(onChange);
|
|
3884
|
+
onChangeRef.current = onChange;
|
|
3885
|
+
const draftTermsRef = useRef7(draftTerms);
|
|
3886
|
+
draftTermsRef.current = draftTerms;
|
|
3887
|
+
const pendingFacetRef = useRef7(pendingFacet);
|
|
3888
|
+
pendingFacetRef.current = pendingFacet;
|
|
3889
|
+
const inputValueRef = useRef7(inputValue);
|
|
3890
|
+
inputValueRef.current = inputValue;
|
|
3891
|
+
const isFacetedRef = useRef7(isFaceted);
|
|
3892
|
+
isFacetedRef.current = isFaceted;
|
|
3893
|
+
const allFacetsRef = useRef7(allFacets);
|
|
3894
|
+
allFacetsRef.current = allFacets;
|
|
3895
|
+
const filteredFacetsRef = useRef7(filteredFacets);
|
|
3896
|
+
filteredFacetsRef.current = filteredFacets;
|
|
3897
|
+
const minCharsRef = useRef7(minChars);
|
|
3898
|
+
minCharsRef.current = minChars;
|
|
3899
|
+
useEffect6(() => {
|
|
3900
|
+
if (!isFaceted) return;
|
|
3901
|
+
const terms = collectTerms(value);
|
|
3902
|
+
if (terms.length === 0) {
|
|
3903
|
+
setDraftTerms([]);
|
|
3904
|
+
setInputValue("");
|
|
3905
|
+
setPendingFacet(null);
|
|
3906
|
+
}
|
|
3907
|
+
}, [value, isFaceted]);
|
|
3908
|
+
const searchFieldsRef = useRef7(searchFields);
|
|
3909
|
+
searchFieldsRef.current = searchFields;
|
|
3910
|
+
const isMountedRef = useRef7(false);
|
|
3911
|
+
useEffect6(() => {
|
|
3912
|
+
if (!isMountedRef.current) {
|
|
3913
|
+
isMountedRef.current = true;
|
|
3914
|
+
return;
|
|
3915
|
+
}
|
|
3916
|
+
if (mode !== "debounce" || isFaceted) return;
|
|
3917
|
+
if (inputValue.trim().length < minCharsRef.current) return;
|
|
3918
|
+
const timer = setTimeout(() => {
|
|
3919
|
+
onChangeRef.current(buildSimpleExpr(inputValue, searchFieldsRef.current));
|
|
3920
|
+
}, debounceMs);
|
|
3921
|
+
return () => clearTimeout(timer);
|
|
3922
|
+
}, [inputValue, mode, debounceMs, isFaceted]);
|
|
3923
|
+
const submitDraft = useCallback5((terms) => {
|
|
3924
|
+
onChangeRef.current({ type: "and", children: terms });
|
|
3925
|
+
}, []);
|
|
3926
|
+
const allRequiredSatisfied = useCallback5((terms) => {
|
|
3927
|
+
const requiredKeys = allFacetsRef.current.filter((f) => f.required).map((f) => f.key);
|
|
3928
|
+
if (requiredKeys.length === 0) return true;
|
|
3929
|
+
const presentKeys = new Set(terms.map((t) => t.key));
|
|
3930
|
+
return requiredKeys.every((k) => presentKeys.has(k));
|
|
3931
|
+
}, []);
|
|
3932
|
+
const addChip = useCallback5((text) => {
|
|
3933
|
+
const trimmed = text.trim();
|
|
3934
|
+
if (!trimmed) return;
|
|
3935
|
+
const facet = pendingFacetRef.current ?? allFacetsRef.current[0] ?? null;
|
|
3936
|
+
if (!facet) return;
|
|
3937
|
+
const newTerm = {
|
|
3938
|
+
type: "term",
|
|
3939
|
+
key: facet.key,
|
|
3940
|
+
value: trimmed
|
|
3941
|
+
};
|
|
3942
|
+
const nextTerms = [...draftTermsRef.current, newTerm];
|
|
3943
|
+
setDraftTerms(nextTerms);
|
|
3944
|
+
setInputValue("");
|
|
3945
|
+
setPendingFacet(null);
|
|
3946
|
+
}, []);
|
|
3947
|
+
const commitFacetValue = addChip;
|
|
3948
|
+
const cancelPendingFacet = useCallback5(() => {
|
|
3949
|
+
setInputValue("");
|
|
3950
|
+
setPendingFacet(null);
|
|
3951
|
+
}, []);
|
|
3952
|
+
const handleInputChange = useCallback5(
|
|
3953
|
+
(e) => {
|
|
3954
|
+
setInputValue(e.target.value);
|
|
3955
|
+
},
|
|
3956
|
+
[]
|
|
3957
|
+
);
|
|
3958
|
+
const tryAcceptSingleMatch = useCallback5(
|
|
3959
|
+
(e) => {
|
|
3960
|
+
if (!isFacetedRef.current || pendingFacetRef.current || filteredFacetsRef.current.length !== 1 || !inputValueRef.current.trim()) {
|
|
3961
|
+
return false;
|
|
3962
|
+
}
|
|
3963
|
+
e.preventDefault();
|
|
3964
|
+
setPendingFacet(filteredFacetsRef.current[0]);
|
|
3965
|
+
setInputValue("");
|
|
3966
|
+
return true;
|
|
3967
|
+
},
|
|
3968
|
+
[]
|
|
3969
|
+
);
|
|
3970
|
+
const handleEnterFaceted = useCallback5(() => {
|
|
3971
|
+
const trimmed = inputValueRef.current.trim();
|
|
3972
|
+
if (trimmed) {
|
|
3973
|
+
addChip(trimmed);
|
|
3974
|
+
return false;
|
|
3975
|
+
}
|
|
3976
|
+
if (!pendingFacetRef.current) {
|
|
3977
|
+
if (allRequiredSatisfied(draftTermsRef.current)) {
|
|
3978
|
+
submitDraft(draftTermsRef.current);
|
|
3979
|
+
} else {
|
|
3980
|
+
return true;
|
|
3981
|
+
}
|
|
3982
|
+
}
|
|
3983
|
+
return false;
|
|
3984
|
+
}, [addChip, submitDraft, allRequiredSatisfied]);
|
|
3985
|
+
const handleEnterSimple = useCallback5(() => {
|
|
3986
|
+
if (inputValueRef.current.trim().length >= minCharsRef.current) {
|
|
3987
|
+
onChangeRef.current(
|
|
3988
|
+
buildSimpleExpr(inputValueRef.current, searchFieldsRef.current)
|
|
3989
|
+
);
|
|
3990
|
+
}
|
|
3991
|
+
}, []);
|
|
3992
|
+
const handleBackspaceEmpty = useCallback5(() => {
|
|
3993
|
+
if (isFacetedRef.current) {
|
|
3994
|
+
if (pendingFacetRef.current) {
|
|
3995
|
+
setPendingFacet(null);
|
|
3996
|
+
} else if (draftTermsRef.current.length > 0) {
|
|
3997
|
+
setDraftTerms((prev) => prev.slice(0, -1));
|
|
3998
|
+
}
|
|
3999
|
+
} else {
|
|
4000
|
+
onChangeRef.current({ type: "and", children: [] });
|
|
4001
|
+
}
|
|
4002
|
+
}, []);
|
|
4003
|
+
const handleKeyDown = useCallback5(
|
|
4004
|
+
(e) => {
|
|
4005
|
+
if ((e.key === "Tab" || e.key === "Enter") && tryAcceptSingleMatch(e)) {
|
|
4006
|
+
return false;
|
|
4007
|
+
}
|
|
4008
|
+
if (e.key === "Enter") {
|
|
4009
|
+
e.preventDefault();
|
|
4010
|
+
if (isFacetedRef.current) {
|
|
4011
|
+
return handleEnterFaceted();
|
|
4012
|
+
}
|
|
4013
|
+
handleEnterSimple();
|
|
4014
|
+
return false;
|
|
4015
|
+
}
|
|
4016
|
+
if (e.key === "Backspace" && inputValueRef.current === "") {
|
|
4017
|
+
handleBackspaceEmpty();
|
|
4018
|
+
}
|
|
4019
|
+
return false;
|
|
4020
|
+
},
|
|
4021
|
+
[
|
|
4022
|
+
tryAcceptSingleMatch,
|
|
4023
|
+
handleEnterFaceted,
|
|
4024
|
+
handleEnterSimple,
|
|
4025
|
+
handleBackspaceEmpty
|
|
4026
|
+
]
|
|
4027
|
+
);
|
|
4028
|
+
const handleCommit = useCallback5(() => {
|
|
4029
|
+
if (isFacetedRef.current) {
|
|
4030
|
+
const trimmed = inputValueRef.current.trim();
|
|
4031
|
+
if (trimmed) {
|
|
4032
|
+
addChip(trimmed);
|
|
4033
|
+
} else if (!pendingFacetRef.current) {
|
|
4034
|
+
if (allRequiredSatisfied(draftTermsRef.current)) {
|
|
4035
|
+
submitDraft(draftTermsRef.current);
|
|
4036
|
+
}
|
|
4037
|
+
}
|
|
4038
|
+
} else if (inputValueRef.current.trim().length >= minCharsRef.current) {
|
|
4039
|
+
onChangeRef.current(
|
|
4040
|
+
buildSimpleExpr(inputValueRef.current, searchFieldsRef.current)
|
|
4041
|
+
);
|
|
4042
|
+
}
|
|
4043
|
+
}, [addChip, submitDraft, allRequiredSatisfied]);
|
|
4044
|
+
const handleRemoveTerm = useCallback5((index) => {
|
|
4045
|
+
const next = draftTermsRef.current.filter((_, i) => i !== index);
|
|
4046
|
+
setDraftTerms(next);
|
|
4047
|
+
onChangeRef.current({ type: "and", children: next });
|
|
4048
|
+
}, []);
|
|
4049
|
+
const handleClear = useCallback5(() => {
|
|
4050
|
+
setInputValue("");
|
|
4051
|
+
setPendingFacet(null);
|
|
4052
|
+
setDraftTerms([]);
|
|
4053
|
+
onChangeRef.current({ type: "and", children: [] });
|
|
4054
|
+
}, []);
|
|
4055
|
+
const selectFacet = useCallback5(
|
|
4056
|
+
(f) => setPendingFacet(f),
|
|
4057
|
+
[]
|
|
4058
|
+
);
|
|
4059
|
+
return {
|
|
4060
|
+
inputValue,
|
|
4061
|
+
pendingFacet,
|
|
4062
|
+
isFaceted,
|
|
4063
|
+
filteredFacets,
|
|
4064
|
+
allFacets,
|
|
4065
|
+
selectFacet,
|
|
4066
|
+
handleInputChange,
|
|
4067
|
+
handleKeyDown,
|
|
4068
|
+
handleCommit,
|
|
4069
|
+
handleRemoveTerm,
|
|
4070
|
+
handleClear,
|
|
4071
|
+
activeTerms,
|
|
4072
|
+
commitFacetValue,
|
|
4073
|
+
cancelPendingFacet
|
|
4074
|
+
};
|
|
4075
|
+
}
|
|
4076
|
+
|
|
4077
|
+
// src/SearchInput/widgets/TextSearchWidget.tsx
|
|
4078
|
+
import { useRef as useRef8, useEffect as useEffect7 } from "react";
|
|
4079
|
+
import { jsx as jsx31 } from "react/jsx-runtime";
|
|
4080
|
+
function TextSearchWidget({
|
|
4081
|
+
value,
|
|
4082
|
+
onChange,
|
|
4083
|
+
onCommit,
|
|
4084
|
+
onCancel,
|
|
4085
|
+
placeholder,
|
|
4086
|
+
autoFocus
|
|
4087
|
+
}) {
|
|
4088
|
+
const inputRef = useRef8(null);
|
|
4089
|
+
useEffect7(() => {
|
|
4090
|
+
if (autoFocus) inputRef.current?.focus();
|
|
4091
|
+
}, [autoFocus]);
|
|
4092
|
+
return /* @__PURE__ */ jsx31(
|
|
4093
|
+
Input,
|
|
4094
|
+
{
|
|
4095
|
+
ref: inputRef,
|
|
4096
|
+
value,
|
|
4097
|
+
onChange: (e) => onChange(e.target.value),
|
|
4098
|
+
onKeyDown: (e) => {
|
|
4099
|
+
if (e.key === "Enter") {
|
|
4100
|
+
e.preventDefault();
|
|
4101
|
+
const trimmed = value.trim();
|
|
4102
|
+
if (trimmed) onCommit(trimmed);
|
|
4103
|
+
} else if (e.key === "Escape") {
|
|
4104
|
+
e.preventDefault();
|
|
4105
|
+
onCancel();
|
|
4106
|
+
} else if (e.key === "Backspace" && value === "") {
|
|
4107
|
+
e.preventDefault();
|
|
4108
|
+
onCancel();
|
|
4109
|
+
}
|
|
4110
|
+
},
|
|
4111
|
+
placeholder,
|
|
4112
|
+
className: "border-0 shadow-none focus-visible:ring-0 h-auto p-0 text-sm bg-transparent"
|
|
4113
|
+
}
|
|
4114
|
+
);
|
|
4115
|
+
}
|
|
4116
|
+
|
|
4117
|
+
// src/SearchInput/widgets/EnumSearchWidget.tsx
|
|
4118
|
+
import { useEffect as useEffect8, useState as useState6 } from "react";
|
|
4119
|
+
import { cn as cn29 } from "@petrarca/sonnet-core";
|
|
4120
|
+
import { jsx as jsx32, jsxs as jsxs12 } from "react/jsx-runtime";
|
|
4121
|
+
function EnumSearchWidget({
|
|
4122
|
+
property,
|
|
4123
|
+
options: widgetOptions,
|
|
4124
|
+
onCommit,
|
|
4125
|
+
onCancel,
|
|
4126
|
+
placeholder,
|
|
4127
|
+
autoFocus
|
|
4128
|
+
}) {
|
|
4129
|
+
const [open, setOpen] = useState6(false);
|
|
4130
|
+
const [search, setSearch] = useState6("");
|
|
4131
|
+
const [highlightedIndex, setHighlightedIndex] = useState6(0);
|
|
4132
|
+
const enumValues = property.enum ?? [];
|
|
4133
|
+
const enumNames = widgetOptions.enumNames ?? {};
|
|
4134
|
+
const items = enumValues.map((val) => ({
|
|
4135
|
+
value: val,
|
|
4136
|
+
label: enumNames[val] ?? val
|
|
4137
|
+
}));
|
|
4138
|
+
const filtered = search ? items.filter(
|
|
4139
|
+
(item) => item.label.toLowerCase().includes(search.toLowerCase())
|
|
4140
|
+
) : items;
|
|
4141
|
+
useEffect8(() => {
|
|
4142
|
+
setHighlightedIndex(0);
|
|
4143
|
+
}, [filtered.length, search]);
|
|
4144
|
+
useEffect8(() => {
|
|
4145
|
+
if (autoFocus) {
|
|
4146
|
+
requestAnimationFrame(() => setOpen(true));
|
|
4147
|
+
}
|
|
4148
|
+
}, [autoFocus]);
|
|
4149
|
+
function handleCommit(val) {
|
|
4150
|
+
onCommit(val);
|
|
4151
|
+
setOpen(false);
|
|
4152
|
+
setSearch("");
|
|
4153
|
+
}
|
|
4154
|
+
function handleOpenChange(next) {
|
|
4155
|
+
setOpen(next);
|
|
4156
|
+
if (!next) setSearch("");
|
|
4157
|
+
}
|
|
4158
|
+
function handleKeyDown(e) {
|
|
4159
|
+
if (e.key === "Escape") {
|
|
4160
|
+
e.preventDefault();
|
|
4161
|
+
e.stopPropagation();
|
|
4162
|
+
setOpen(false);
|
|
4163
|
+
onCancel();
|
|
4164
|
+
return;
|
|
4165
|
+
}
|
|
4166
|
+
if (e.key === "ArrowDown") {
|
|
4167
|
+
e.preventDefault();
|
|
4168
|
+
setHighlightedIndex((i) => Math.min(i + 1, filtered.length - 1));
|
|
4169
|
+
return;
|
|
4170
|
+
}
|
|
4171
|
+
if (e.key === "ArrowUp") {
|
|
4172
|
+
e.preventDefault();
|
|
4173
|
+
setHighlightedIndex((i) => Math.max(i - 1, 0));
|
|
4174
|
+
return;
|
|
4175
|
+
}
|
|
4176
|
+
if (e.key === "Enter") {
|
|
4177
|
+
e.preventDefault();
|
|
4178
|
+
e.stopPropagation();
|
|
4179
|
+
const item = filtered[highlightedIndex];
|
|
4180
|
+
if (item) handleCommit(item.value);
|
|
4181
|
+
return;
|
|
4182
|
+
}
|
|
4183
|
+
}
|
|
4184
|
+
return /* @__PURE__ */ jsxs12(Popover, { open, onOpenChange: handleOpenChange, children: [
|
|
4185
|
+
/* @__PURE__ */ jsx32(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsx32(
|
|
4186
|
+
"button",
|
|
4187
|
+
{
|
|
4188
|
+
type: "button",
|
|
4189
|
+
className: "text-sm text-muted-foreground hover:text-foreground transition-colors truncate text-left",
|
|
4190
|
+
onClick: () => setOpen(true),
|
|
4191
|
+
children: placeholder ?? "Select..."
|
|
4192
|
+
}
|
|
4193
|
+
) }),
|
|
4194
|
+
/* @__PURE__ */ jsxs12(
|
|
4195
|
+
PopoverContent,
|
|
4196
|
+
{
|
|
4197
|
+
className: "w-48 p-1",
|
|
4198
|
+
align: "start",
|
|
4199
|
+
onOpenAutoFocus: (e) => e.preventDefault(),
|
|
4200
|
+
children: [
|
|
4201
|
+
/* @__PURE__ */ jsx32(
|
|
4202
|
+
"input",
|
|
4203
|
+
{
|
|
4204
|
+
autoFocus: true,
|
|
4205
|
+
value: search,
|
|
4206
|
+
onChange: (e) => setSearch(e.target.value),
|
|
4207
|
+
onKeyDown: handleKeyDown,
|
|
4208
|
+
placeholder: "Search...",
|
|
4209
|
+
className: "w-full px-2 py-1.5 text-sm bg-transparent outline-none border-b border-border mb-1 placeholder:text-muted-foreground"
|
|
4210
|
+
}
|
|
4211
|
+
),
|
|
4212
|
+
/* @__PURE__ */ jsxs12("div", { children: [
|
|
4213
|
+
filtered.length === 0 && /* @__PURE__ */ jsx32("p", { className: "px-2 py-1.5 text-sm text-muted-foreground", children: "No matches" }),
|
|
4214
|
+
filtered.map((item, i) => /* @__PURE__ */ jsx32(
|
|
4215
|
+
"button",
|
|
4216
|
+
{
|
|
4217
|
+
type: "button",
|
|
4218
|
+
className: cn29(
|
|
4219
|
+
"w-full text-left px-2 py-1.5 text-sm rounded transition-colors",
|
|
4220
|
+
i === highlightedIndex ? "bg-accent text-accent-foreground" : "hover:bg-accent hover:text-accent-foreground"
|
|
4221
|
+
),
|
|
4222
|
+
onMouseDown: (e) => {
|
|
4223
|
+
e.preventDefault();
|
|
4224
|
+
handleCommit(item.value);
|
|
4225
|
+
},
|
|
4226
|
+
onMouseEnter: () => setHighlightedIndex(i),
|
|
4227
|
+
children: item.label
|
|
4228
|
+
},
|
|
4229
|
+
item.value
|
|
4230
|
+
))
|
|
4231
|
+
] })
|
|
4232
|
+
]
|
|
4233
|
+
}
|
|
4234
|
+
)
|
|
4235
|
+
] });
|
|
4236
|
+
}
|
|
4237
|
+
|
|
4238
|
+
// src/SearchInput/widgets/resolveSearchWidget.ts
|
|
4239
|
+
function resolveSearchWidget(property, registry) {
|
|
4240
|
+
const widgetName = property["x-ui-widget"];
|
|
4241
|
+
if (widgetName && registry[widgetName]) {
|
|
4242
|
+
return registry[widgetName];
|
|
4243
|
+
}
|
|
4244
|
+
if (property.enum && property.enum.length > 0 && registry["enum"]) {
|
|
4245
|
+
return registry["enum"];
|
|
4246
|
+
}
|
|
4247
|
+
return registry["text"];
|
|
4248
|
+
}
|
|
4249
|
+
|
|
4250
|
+
// src/SearchInput/widgets/index.ts
|
|
4251
|
+
var DEFAULT_SEARCH_WIDGETS = {
|
|
4252
|
+
text: TextSearchWidget,
|
|
4253
|
+
enum: EnumSearchWidget
|
|
4254
|
+
};
|
|
4255
|
+
|
|
4256
|
+
// src/SearchInput/SearchInput.tsx
|
|
4257
|
+
import { Fragment as Fragment3, jsx as jsx33, jsxs as jsxs13 } from "react/jsx-runtime";
|
|
4258
|
+
function resolveSearchDefaults(props) {
|
|
4259
|
+
return {
|
|
4260
|
+
mode: props.mode ?? "explicit",
|
|
4261
|
+
debounceMs: props.debounceMs ?? 300,
|
|
4262
|
+
minChars: props.minChars ?? 0,
|
|
4263
|
+
showSearchButton: props.showSearchButton ?? false,
|
|
4264
|
+
disabled: props.disabled ?? false
|
|
4265
|
+
};
|
|
4266
|
+
}
|
|
4267
|
+
function FacetPicker({
|
|
4268
|
+
pickerVisible,
|
|
4269
|
+
hasFacetsToShow,
|
|
4270
|
+
filteredFacets,
|
|
4271
|
+
highlightedIndex,
|
|
4272
|
+
setPickerOpen,
|
|
4273
|
+
setHighlightedIndex,
|
|
4274
|
+
acceptFacet,
|
|
4275
|
+
inputRef
|
|
4276
|
+
}) {
|
|
4277
|
+
return /* @__PURE__ */ jsxs13(Popover, { open: pickerVisible, onOpenChange: setPickerOpen, modal: false, children: [
|
|
4278
|
+
/* @__PURE__ */ jsx33(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsx33(
|
|
4279
|
+
"button",
|
|
4280
|
+
{
|
|
4281
|
+
type: "button",
|
|
4282
|
+
className: "inline-flex items-center gap-0.5 text-xs text-muted-foreground hover:text-foreground transition-colors shrink-0",
|
|
4283
|
+
tabIndex: -1,
|
|
4284
|
+
onClick: (e) => {
|
|
4285
|
+
e.stopPropagation();
|
|
4286
|
+
setPickerOpen((o) => !o);
|
|
4287
|
+
inputRef.current?.focus();
|
|
4288
|
+
},
|
|
4289
|
+
children: /* @__PURE__ */ jsx33(ChevronDown, { className: "h-3 w-3" })
|
|
4290
|
+
}
|
|
4291
|
+
) }),
|
|
4292
|
+
/* @__PURE__ */ jsx33(
|
|
4293
|
+
PopoverContent,
|
|
4294
|
+
{
|
|
4295
|
+
className: cn30("w-44 p-1", !hasFacetsToShow && "hidden"),
|
|
4296
|
+
align: "start",
|
|
4297
|
+
onOpenAutoFocus: (e) => e.preventDefault(),
|
|
4298
|
+
onFocusOutside: (e) => e.preventDefault(),
|
|
4299
|
+
children: filteredFacets.map((facet, i) => /* @__PURE__ */ jsx33(
|
|
4300
|
+
"button",
|
|
4301
|
+
{
|
|
4302
|
+
type: "button",
|
|
4303
|
+
className: cn30(
|
|
4304
|
+
"w-full text-left px-2 py-1.5 text-sm rounded transition-colors",
|
|
4305
|
+
i === highlightedIndex ? "bg-accent text-accent-foreground" : "hover:bg-accent hover:text-accent-foreground"
|
|
4306
|
+
),
|
|
4307
|
+
onMouseDown: (e) => {
|
|
4308
|
+
e.preventDefault();
|
|
4309
|
+
e.stopPropagation();
|
|
4310
|
+
acceptFacet(facet);
|
|
4311
|
+
},
|
|
4312
|
+
onMouseEnter: () => setHighlightedIndex(i),
|
|
4313
|
+
children: facet.label
|
|
4314
|
+
},
|
|
4315
|
+
facet.key
|
|
4316
|
+
))
|
|
4317
|
+
}
|
|
4318
|
+
)
|
|
4319
|
+
] });
|
|
4320
|
+
}
|
|
4321
|
+
function ValueEntry({
|
|
4322
|
+
pendingFacet,
|
|
4323
|
+
PendingWidget,
|
|
4324
|
+
pendingWidgetOptions,
|
|
4325
|
+
inputValue,
|
|
4326
|
+
handleInputChange,
|
|
4327
|
+
commitFacetValue,
|
|
4328
|
+
cancelPendingFacet,
|
|
4329
|
+
resolvedPlaceholder,
|
|
4330
|
+
disabled,
|
|
4331
|
+
inputRef,
|
|
4332
|
+
onInputKeyDown,
|
|
4333
|
+
onInputChange,
|
|
4334
|
+
onInputFocus
|
|
4335
|
+
}) {
|
|
4336
|
+
if (pendingFacet && PendingWidget) {
|
|
4337
|
+
return /* @__PURE__ */ jsx33(
|
|
4338
|
+
PendingWidget,
|
|
4339
|
+
{
|
|
4340
|
+
property: pendingFacet.property,
|
|
4341
|
+
options: pendingWidgetOptions,
|
|
4342
|
+
value: inputValue,
|
|
4343
|
+
onChange: (v) => handleInputChange({
|
|
4344
|
+
target: { value: v }
|
|
4345
|
+
}),
|
|
4346
|
+
onCommit: (v) => {
|
|
4347
|
+
commitFacetValue(v);
|
|
4348
|
+
requestAnimationFrame(() => inputRef.current?.focus());
|
|
4349
|
+
},
|
|
4350
|
+
onCancel: () => {
|
|
4351
|
+
cancelPendingFacet();
|
|
4352
|
+
requestAnimationFrame(() => inputRef.current?.focus());
|
|
4353
|
+
},
|
|
4354
|
+
placeholder: pendingFacet.placeholder ?? resolvedPlaceholder,
|
|
4355
|
+
autoFocus: true
|
|
4356
|
+
}
|
|
4357
|
+
);
|
|
4358
|
+
}
|
|
4359
|
+
return /* @__PURE__ */ jsx33(
|
|
4360
|
+
Input,
|
|
4361
|
+
{
|
|
4362
|
+
ref: inputRef,
|
|
4363
|
+
value: inputValue,
|
|
4364
|
+
onChange: onInputChange,
|
|
4365
|
+
onKeyDown: onInputKeyDown,
|
|
4366
|
+
onFocus: onInputFocus,
|
|
4367
|
+
placeholder: resolvedPlaceholder,
|
|
4368
|
+
disabled,
|
|
4369
|
+
autoComplete: "off",
|
|
4370
|
+
className: "border-0 shadow-none focus-visible:ring-0 h-auto p-0 text-sm bg-transparent"
|
|
4371
|
+
}
|
|
4372
|
+
);
|
|
4373
|
+
}
|
|
4374
|
+
function handlePickerArrowDown(ctx) {
|
|
4375
|
+
if (!ctx.isFaceted || ctx.pendingFacet) return false;
|
|
4376
|
+
ctx.setPickerNavigated(true);
|
|
4377
|
+
if (!ctx.pickerVisible) {
|
|
4378
|
+
ctx.setPickerOpen(true);
|
|
4379
|
+
} else {
|
|
4380
|
+
ctx.setHighlightedIndex(
|
|
4381
|
+
(i) => Math.min(i + 1, ctx.filteredFacets.length - 1)
|
|
4382
|
+
);
|
|
4383
|
+
}
|
|
4384
|
+
return true;
|
|
4385
|
+
}
|
|
4386
|
+
function handlePickerEnterSelect(ctx) {
|
|
4387
|
+
if (!ctx.hasFacetsToShow) return false;
|
|
4388
|
+
if (!ctx.pickerNavigated && !ctx.inputValue.trim()) return false;
|
|
4389
|
+
const clamped = Math.min(ctx.highlightedIndex, ctx.filteredFacets.length - 1);
|
|
4390
|
+
if (clamped >= 0 && ctx.filteredFacets[clamped]) {
|
|
4391
|
+
ctx.setPickerNavigated(false);
|
|
4392
|
+
ctx.acceptFacet(ctx.filteredFacets[clamped]);
|
|
4393
|
+
return true;
|
|
4394
|
+
}
|
|
4395
|
+
return false;
|
|
4396
|
+
}
|
|
4397
|
+
function handleInputKeyDown(e, ctx) {
|
|
4398
|
+
if (e.key === "ArrowDown" && handlePickerArrowDown(ctx)) {
|
|
4399
|
+
e.preventDefault();
|
|
4400
|
+
return;
|
|
4401
|
+
}
|
|
4402
|
+
if (e.key === "ArrowUp" && ctx.hasFacetsToShow) {
|
|
4403
|
+
e.preventDefault();
|
|
4404
|
+
ctx.setPickerNavigated(true);
|
|
4405
|
+
ctx.setHighlightedIndex((i) => Math.max(i - 1, 0));
|
|
4406
|
+
return;
|
|
4407
|
+
}
|
|
4408
|
+
if (e.key === "Enter" && handlePickerEnterSelect(ctx)) {
|
|
4409
|
+
e.preventDefault();
|
|
4410
|
+
return;
|
|
4411
|
+
}
|
|
4412
|
+
const reopenPicker = ctx.handleKeyDown(e);
|
|
4413
|
+
if (e.key === "Enter") {
|
|
4414
|
+
ctx.setPickerNavigated(false);
|
|
4415
|
+
ctx.setPickerOpen(!!reopenPicker);
|
|
4416
|
+
}
|
|
4417
|
+
}
|
|
4418
|
+
function resolvePlaceholder(isFaceted, hasActiveTerms, hasPendingFacet, placeholder) {
|
|
4419
|
+
const defaultPlaceholder = isFaceted ? "Add filter..." : "Search...";
|
|
4420
|
+
if (isFaceted && (hasActiveTerms || hasPendingFacet)) return "Add filter...";
|
|
4421
|
+
return placeholder ?? defaultPlaceholder;
|
|
4422
|
+
}
|
|
4423
|
+
function ActiveChips({
|
|
4424
|
+
terms,
|
|
4425
|
+
schema,
|
|
4426
|
+
onRemove
|
|
4427
|
+
}) {
|
|
4428
|
+
return /* @__PURE__ */ jsx33(Fragment3, { children: terms.map((term, i) => {
|
|
4429
|
+
const property = schema?.properties?.[term.key];
|
|
4430
|
+
const label = property ? getFieldTitle2(term.key, property) : term.key;
|
|
4431
|
+
return /* @__PURE__ */ jsx33(
|
|
4432
|
+
FilterChip,
|
|
4433
|
+
{
|
|
4434
|
+
label: `${label}: ${term.value}`,
|
|
4435
|
+
onRemove: () => onRemove(i)
|
|
4436
|
+
},
|
|
4437
|
+
`${term.key}:${term.value}`
|
|
4438
|
+
);
|
|
4439
|
+
}) });
|
|
4440
|
+
}
|
|
4441
|
+
function resolveWidgetForFacet(pendingFacet, registry) {
|
|
4442
|
+
if (!pendingFacet) return { Widget: null, options: {} };
|
|
4443
|
+
return {
|
|
4444
|
+
Widget: resolveSearchWidget(pendingFacet.property, registry),
|
|
4445
|
+
options: pendingFacet.property["x-ui-options"] ?? {}
|
|
4446
|
+
};
|
|
4447
|
+
}
|
|
4448
|
+
function SearchActions({
|
|
4449
|
+
isEmpty,
|
|
4450
|
+
showSearchButton,
|
|
4451
|
+
mode,
|
|
4452
|
+
onClear,
|
|
4453
|
+
onCommit
|
|
4454
|
+
}) {
|
|
4455
|
+
return /* @__PURE__ */ jsxs13(Fragment3, { children: [
|
|
4456
|
+
!isEmpty && /* @__PURE__ */ jsx33(
|
|
4457
|
+
"button",
|
|
4458
|
+
{
|
|
4459
|
+
type: "button",
|
|
4460
|
+
onClick: (e) => {
|
|
4461
|
+
e.stopPropagation();
|
|
4462
|
+
onClear();
|
|
4463
|
+
},
|
|
4464
|
+
className: "text-muted-foreground hover:text-foreground transition-colors shrink-0",
|
|
4465
|
+
"aria-label": "Clear search",
|
|
4466
|
+
children: /* @__PURE__ */ jsx33(X3, { className: "h-4 w-4" })
|
|
4467
|
+
}
|
|
4468
|
+
),
|
|
4469
|
+
showSearchButton && mode === "explicit" && /* @__PURE__ */ jsx33(
|
|
4470
|
+
Button,
|
|
4471
|
+
{
|
|
4472
|
+
type: "button",
|
|
4473
|
+
size: "sm",
|
|
4474
|
+
variant: "ghost",
|
|
4475
|
+
className: "h-6 px-2 shrink-0",
|
|
4476
|
+
onClick: (e) => {
|
|
4477
|
+
e.stopPropagation();
|
|
4478
|
+
onCommit();
|
|
4479
|
+
},
|
|
4480
|
+
children: "Search"
|
|
4481
|
+
}
|
|
4482
|
+
)
|
|
4483
|
+
] });
|
|
4484
|
+
}
|
|
4485
|
+
function mergeWidgetRegistry(custom) {
|
|
4486
|
+
return custom ? { ...DEFAULT_SEARCH_WIDGETS, ...custom } : DEFAULT_SEARCH_WIDGETS;
|
|
4487
|
+
}
|
|
4488
|
+
function SearchInput(props) {
|
|
4489
|
+
const {
|
|
4490
|
+
value,
|
|
4491
|
+
onChange,
|
|
4492
|
+
schema,
|
|
4493
|
+
widgets: customWidgets,
|
|
4494
|
+
searchFields,
|
|
4495
|
+
placeholder,
|
|
4496
|
+
className
|
|
4497
|
+
} = props;
|
|
4498
|
+
const { mode, debounceMs, minChars, showSearchButton, disabled } = resolveSearchDefaults(props);
|
|
4499
|
+
const inputRef = useRef9(null);
|
|
4500
|
+
const containerRef = useRef9(null);
|
|
4501
|
+
const [pickerOpen, setPickerOpen] = useState7(false);
|
|
4502
|
+
const [highlightedIndex, setHighlightedIndex] = useState7(-1);
|
|
4503
|
+
const [pickerNavigated, setPickerNavigated] = useState7(false);
|
|
4504
|
+
useEffect9(() => {
|
|
4505
|
+
if (!pickerOpen) return;
|
|
4506
|
+
function handleMouseDown(e) {
|
|
4507
|
+
if (containerRef.current && !containerRef.current.contains(e.target)) {
|
|
4508
|
+
setPickerOpen(false);
|
|
4509
|
+
}
|
|
4510
|
+
}
|
|
4511
|
+
document.addEventListener("mousedown", handleMouseDown);
|
|
4512
|
+
return () => document.removeEventListener("mousedown", handleMouseDown);
|
|
4513
|
+
}, [pickerOpen]);
|
|
4514
|
+
const widgetRegistry = useMemo6(
|
|
4515
|
+
() => mergeWidgetRegistry(customWidgets),
|
|
4516
|
+
[customWidgets]
|
|
4517
|
+
);
|
|
4518
|
+
const {
|
|
4519
|
+
inputValue,
|
|
4520
|
+
pendingFacet,
|
|
4521
|
+
isFaceted,
|
|
4522
|
+
filteredFacets,
|
|
4523
|
+
selectFacet,
|
|
4524
|
+
handleInputChange,
|
|
4525
|
+
handleKeyDown,
|
|
4526
|
+
handleCommit,
|
|
4527
|
+
handleRemoveTerm,
|
|
4528
|
+
handleClear,
|
|
4529
|
+
activeTerms,
|
|
4530
|
+
commitFacetValue,
|
|
4531
|
+
cancelPendingFacet
|
|
4532
|
+
} = useSearchInput({
|
|
4533
|
+
value,
|
|
4534
|
+
onChange,
|
|
4535
|
+
mode,
|
|
4536
|
+
debounceMs,
|
|
4537
|
+
minChars,
|
|
4538
|
+
schema,
|
|
4539
|
+
searchFields
|
|
4540
|
+
});
|
|
4541
|
+
const isEmpty = isEmptyFilterExpr(value) && inputValue === "";
|
|
4542
|
+
useEffect9(() => {
|
|
4543
|
+
setHighlightedIndex(-1);
|
|
4544
|
+
}, [filteredFacets, pickerOpen]);
|
|
4545
|
+
const resolvedPlaceholder = resolvePlaceholder(
|
|
4546
|
+
isFaceted,
|
|
4547
|
+
activeTerms.length > 0,
|
|
4548
|
+
pendingFacet !== null,
|
|
4549
|
+
placeholder
|
|
4550
|
+
);
|
|
4551
|
+
const showFacetControls = isFaceted && !pendingFacet;
|
|
4552
|
+
const pickerVisible = showFacetControls && pickerOpen;
|
|
4553
|
+
const hasFacetsToShow = pickerVisible && filteredFacets.length > 0;
|
|
4554
|
+
const acceptFacet = React29.useCallback(
|
|
4555
|
+
(facet) => {
|
|
4556
|
+
selectFacet(facet);
|
|
4557
|
+
setPickerOpen(false);
|
|
4558
|
+
inputRef.current?.focus();
|
|
4559
|
+
},
|
|
4560
|
+
[selectFacet]
|
|
4561
|
+
);
|
|
4562
|
+
const { Widget: PendingWidget, options: pendingWidgetOptions } = resolveWidgetForFacet(pendingFacet, widgetRegistry);
|
|
4563
|
+
const keyDownCtx = {
|
|
4564
|
+
isFaceted,
|
|
4565
|
+
pendingFacet,
|
|
4566
|
+
pickerVisible,
|
|
4567
|
+
hasFacetsToShow,
|
|
4568
|
+
filteredFacets,
|
|
4569
|
+
highlightedIndex,
|
|
4570
|
+
inputValue,
|
|
4571
|
+
pickerNavigated,
|
|
4572
|
+
setPickerNavigated,
|
|
4573
|
+
setPickerOpen,
|
|
4574
|
+
setHighlightedIndex,
|
|
4575
|
+
acceptFacet,
|
|
4576
|
+
handleKeyDown
|
|
4577
|
+
};
|
|
4578
|
+
return /* @__PURE__ */ jsxs13(
|
|
4579
|
+
"div",
|
|
4580
|
+
{
|
|
4581
|
+
ref: containerRef,
|
|
4582
|
+
className: cn30(
|
|
4583
|
+
"flex flex-wrap items-center gap-1.5 rounded-md border border-input bg-background px-2 py-1.5 text-sm min-h-9",
|
|
4584
|
+
disabled ? "opacity-50 cursor-not-allowed" : "cursor-text",
|
|
4585
|
+
className
|
|
4586
|
+
),
|
|
4587
|
+
onClick: () => !disabled && inputRef.current?.focus(),
|
|
4588
|
+
onBlur: (e) => {
|
|
4589
|
+
const relatedTarget = e.relatedTarget;
|
|
4590
|
+
if (relatedTarget && !e.currentTarget.contains(relatedTarget)) {
|
|
4591
|
+
setPickerOpen(false);
|
|
4592
|
+
}
|
|
4593
|
+
},
|
|
4594
|
+
children: [
|
|
4595
|
+
/* @__PURE__ */ jsx33(Search2, { className: "h-4 w-4 text-muted-foreground shrink-0" }),
|
|
4596
|
+
isFaceted && /* @__PURE__ */ jsx33(
|
|
4597
|
+
ActiveChips,
|
|
4598
|
+
{
|
|
4599
|
+
terms: activeTerms,
|
|
4600
|
+
schema,
|
|
4601
|
+
onRemove: handleRemoveTerm
|
|
4602
|
+
}
|
|
4603
|
+
),
|
|
4604
|
+
pendingFacet && /* @__PURE__ */ jsxs13("span", { className: "inline-flex items-center gap-1 rounded-full bg-muted px-2 py-0.5 text-xs font-medium text-muted-foreground", children: [
|
|
4605
|
+
pendingFacet.label,
|
|
4606
|
+
":"
|
|
4607
|
+
] }),
|
|
4608
|
+
/* @__PURE__ */ jsxs13("div", { className: "flex items-center gap-1 flex-1 min-w-[120px]", children: [
|
|
4609
|
+
showFacetControls && /* @__PURE__ */ jsx33(
|
|
4610
|
+
FacetPicker,
|
|
4611
|
+
{
|
|
4612
|
+
pickerVisible,
|
|
4613
|
+
hasFacetsToShow,
|
|
4614
|
+
filteredFacets,
|
|
4615
|
+
highlightedIndex,
|
|
4616
|
+
setPickerOpen,
|
|
4617
|
+
setHighlightedIndex,
|
|
4618
|
+
acceptFacet,
|
|
4619
|
+
inputRef
|
|
4620
|
+
}
|
|
4621
|
+
),
|
|
4622
|
+
/* @__PURE__ */ jsx33(
|
|
4623
|
+
ValueEntry,
|
|
4624
|
+
{
|
|
4625
|
+
pendingFacet,
|
|
4626
|
+
PendingWidget,
|
|
4627
|
+
pendingWidgetOptions,
|
|
4628
|
+
inputValue,
|
|
4629
|
+
handleInputChange,
|
|
4630
|
+
commitFacetValue,
|
|
4631
|
+
cancelPendingFacet,
|
|
4632
|
+
resolvedPlaceholder,
|
|
4633
|
+
disabled,
|
|
4634
|
+
inputRef,
|
|
4635
|
+
onInputKeyDown: (e) => handleInputKeyDown(e, keyDownCtx),
|
|
4636
|
+
onInputChange: (e) => {
|
|
4637
|
+
handleInputChange(e);
|
|
4638
|
+
setPickerNavigated(false);
|
|
4639
|
+
},
|
|
4640
|
+
onInputFocus: () => {
|
|
4641
|
+
if (isFaceted && !pendingFacet) setPickerNavigated(false);
|
|
4642
|
+
}
|
|
4643
|
+
}
|
|
4644
|
+
)
|
|
4645
|
+
] }),
|
|
4646
|
+
/* @__PURE__ */ jsx33(
|
|
4647
|
+
SearchActions,
|
|
4648
|
+
{
|
|
4649
|
+
isEmpty,
|
|
4650
|
+
showSearchButton,
|
|
4651
|
+
mode,
|
|
4652
|
+
onClear: handleClear,
|
|
4653
|
+
onCommit: handleCommit
|
|
4654
|
+
}
|
|
4655
|
+
)
|
|
4656
|
+
]
|
|
4657
|
+
}
|
|
4658
|
+
);
|
|
4659
|
+
}
|
|
4660
|
+
function FilterChip({ label, onRemove }) {
|
|
4661
|
+
return /* @__PURE__ */ jsxs13(
|
|
4662
|
+
Badge,
|
|
4663
|
+
{
|
|
4664
|
+
variant: "secondary",
|
|
4665
|
+
className: "inline-flex items-center gap-1 px-2 py-0.5 text-xs font-medium rounded-full",
|
|
4666
|
+
children: [
|
|
4667
|
+
label,
|
|
4668
|
+
/* @__PURE__ */ jsx33(
|
|
4669
|
+
"button",
|
|
4670
|
+
{
|
|
4671
|
+
type: "button",
|
|
4672
|
+
onClick: (e) => {
|
|
4673
|
+
e.stopPropagation();
|
|
4674
|
+
onRemove();
|
|
4675
|
+
},
|
|
4676
|
+
className: "ml-0.5 hover:text-destructive transition-colors",
|
|
4677
|
+
"aria-label": `Remove filter ${label}`,
|
|
4678
|
+
children: /* @__PURE__ */ jsx33(X3, { className: "h-3 w-3" })
|
|
4679
|
+
}
|
|
4680
|
+
)
|
|
4681
|
+
]
|
|
4682
|
+
}
|
|
4683
|
+
);
|
|
4684
|
+
}
|
|
4685
|
+
|
|
4686
|
+
// src/Clipboard/ClipboardButton.tsx
|
|
4687
|
+
import { Copy } from "lucide-react";
|
|
4688
|
+
import { jsx as jsx34 } from "react/jsx-runtime";
|
|
4689
|
+
function ClipboardButton({ hasItems, onClick }) {
|
|
4690
|
+
return /* @__PURE__ */ jsx34(
|
|
4691
|
+
Button,
|
|
4692
|
+
{
|
|
4693
|
+
variant: "ghost",
|
|
4694
|
+
size: "icon",
|
|
4695
|
+
onClick,
|
|
4696
|
+
disabled: !hasItems,
|
|
4697
|
+
style: {
|
|
4698
|
+
opacity: hasItems ? 1 : 0.4,
|
|
4699
|
+
cursor: hasItems ? "pointer" : "not-allowed"
|
|
4700
|
+
},
|
|
4701
|
+
children: /* @__PURE__ */ jsx34(Copy, { size: 16 })
|
|
4702
|
+
}
|
|
4703
|
+
);
|
|
4704
|
+
}
|
|
4705
|
+
var ClipboardButton_default = ClipboardButton;
|
|
4706
|
+
|
|
4707
|
+
// src/Clipboard/ClipboardItem.tsx
|
|
4708
|
+
import { X as X4, Plus } from "lucide-react";
|
|
4709
|
+
import { jsx as jsx35, jsxs as jsxs14 } from "react/jsx-runtime";
|
|
4710
|
+
function ClipboardItemComponent({ item, onAddItem, onRemove }) {
|
|
4711
|
+
const handleAddClick = (e) => {
|
|
4712
|
+
const keepInClipboard = e.shiftKey;
|
|
4713
|
+
onAddItem(item, keepInClipboard);
|
|
4714
|
+
};
|
|
4715
|
+
return /* @__PURE__ */ jsx35(
|
|
4716
|
+
"div",
|
|
4717
|
+
{
|
|
4718
|
+
className: "p-3 rounded mb-2",
|
|
4719
|
+
style: {
|
|
4720
|
+
borderLeft: `4px solid ${item.color}`,
|
|
4721
|
+
backgroundColor: "#f8f9fa"
|
|
4722
|
+
},
|
|
4723
|
+
children: /* @__PURE__ */ jsxs14(SimpleGroup, { justify: "space-between", wrap: "nowrap", align: "flex-start", children: [
|
|
4724
|
+
/* @__PURE__ */ jsxs14("div", { className: "flex-1 min-w-0 overflow-hidden", children: [
|
|
4725
|
+
/* @__PURE__ */ jsx35("p", { className: "text-sm font-semibold truncate", children: item.label }),
|
|
4726
|
+
/* @__PURE__ */ jsx35("p", { className: "text-xs text-muted-foreground truncate", children: item.displayName }),
|
|
4727
|
+
/* @__PURE__ */ jsxs14("p", { className: "text-xs text-muted-foreground font-mono", children: [
|
|
4728
|
+
"#",
|
|
4729
|
+
item.entityId
|
|
4730
|
+
] })
|
|
4731
|
+
] }),
|
|
4732
|
+
/* @__PURE__ */ jsxs14(SimpleGroup, { gap: 4, wrap: "nowrap", style: { flexShrink: 0 }, children: [
|
|
4733
|
+
/* @__PURE__ */ jsxs14(
|
|
4734
|
+
Button,
|
|
4735
|
+
{
|
|
4736
|
+
size: "sm",
|
|
4737
|
+
variant: "secondary",
|
|
4738
|
+
onClick: handleAddClick,
|
|
4739
|
+
title: "Click to add and remove from clipboard, Shift+Click to keep in clipboard",
|
|
4740
|
+
children: [
|
|
4741
|
+
/* @__PURE__ */ jsx35(Plus, { size: 16 }),
|
|
4742
|
+
"Add"
|
|
4743
|
+
]
|
|
4744
|
+
}
|
|
4745
|
+
),
|
|
4746
|
+
/* @__PURE__ */ jsx35(
|
|
4747
|
+
Button,
|
|
4748
|
+
{
|
|
4749
|
+
size: "sm",
|
|
4750
|
+
variant: "ghost",
|
|
4751
|
+
onClick: () => onRemove(item.id),
|
|
4752
|
+
title: "Remove from clipboard",
|
|
4753
|
+
children: /* @__PURE__ */ jsx35(X4, { size: 16 })
|
|
4754
|
+
}
|
|
4755
|
+
)
|
|
4756
|
+
] })
|
|
4757
|
+
] })
|
|
4758
|
+
}
|
|
4759
|
+
);
|
|
4760
|
+
}
|
|
4761
|
+
var ClipboardItem_default = ClipboardItemComponent;
|
|
4762
|
+
|
|
4763
|
+
// src/Clipboard/ClipboardMenu.tsx
|
|
4764
|
+
import { Trash2 } from "lucide-react";
|
|
4765
|
+
|
|
4766
|
+
// src/Clipboard/clipboardStore.ts
|
|
4767
|
+
import { create } from "zustand";
|
|
4768
|
+
import { persist } from "zustand/middleware";
|
|
4769
|
+
import { devLog } from "@petrarca/sonnet-core";
|
|
4770
|
+
function getItemKey(item) {
|
|
4771
|
+
return `${item.type}:${item.entityId}:${item.label}`;
|
|
4772
|
+
}
|
|
4773
|
+
function getInputKey(input) {
|
|
4774
|
+
return `${input.type}:${input.entityId}:${input.label}`;
|
|
4775
|
+
}
|
|
4776
|
+
function uuid() {
|
|
4777
|
+
return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, (c) => {
|
|
4778
|
+
const r = Math.random() * 16 | 0;
|
|
4779
|
+
const v = c === "x" ? r : r & 3 | 8;
|
|
4780
|
+
return v.toString(16);
|
|
4781
|
+
});
|
|
4782
|
+
}
|
|
4783
|
+
var MAX_CLIPBOARD_SIZE = 100;
|
|
4784
|
+
var useClipboard = create()(
|
|
4785
|
+
persist(
|
|
4786
|
+
(set, get) => ({
|
|
4787
|
+
items: [],
|
|
4788
|
+
addItems: (inputs) => {
|
|
4789
|
+
const currentItems = get().items;
|
|
4790
|
+
const existingKeys = new Set(currentItems.map(getItemKey));
|
|
4791
|
+
const newItems = inputs.map(
|
|
4792
|
+
(input) => ({
|
|
4793
|
+
id: uuid(),
|
|
4794
|
+
type: input.type,
|
|
4795
|
+
entityId: input.entityId,
|
|
4796
|
+
label: input.label,
|
|
4797
|
+
displayName: input.displayName,
|
|
4798
|
+
color: input.color
|
|
4799
|
+
})
|
|
4800
|
+
).filter((item) => !existingKeys.has(getInputKey(item)));
|
|
4801
|
+
if (newItems.length === 0) {
|
|
4802
|
+
devLog(
|
|
4803
|
+
"[ClipboardStore] addItems - all items were duplicates, skipped"
|
|
4804
|
+
);
|
|
4805
|
+
return;
|
|
4806
|
+
}
|
|
4807
|
+
let updatedItems = [...currentItems, ...newItems];
|
|
4808
|
+
if (updatedItems.length > MAX_CLIPBOARD_SIZE) {
|
|
4809
|
+
const overflow = updatedItems.length - MAX_CLIPBOARD_SIZE;
|
|
4810
|
+
updatedItems = updatedItems.slice(overflow);
|
|
4811
|
+
devLog(
|
|
4812
|
+
"[ClipboardStore] addItems - size limit exceeded, removed oldest",
|
|
4813
|
+
{ overflow }
|
|
4814
|
+
);
|
|
4815
|
+
}
|
|
4816
|
+
devLog("[ClipboardStore] addItems", {
|
|
4817
|
+
added: newItems.length,
|
|
4818
|
+
total: updatedItems.length,
|
|
4819
|
+
skippedDuplicates: inputs.length - newItems.length
|
|
4820
|
+
});
|
|
4821
|
+
set({ items: updatedItems });
|
|
4822
|
+
},
|
|
4823
|
+
removeItem: (itemId) => {
|
|
4824
|
+
const currentItems = get().items;
|
|
4825
|
+
const updatedItems = currentItems.filter((item) => item.id !== itemId);
|
|
4826
|
+
if (updatedItems.length === currentItems.length) {
|
|
4827
|
+
devLog("[ClipboardStore] removeItem - item not found", { itemId });
|
|
4828
|
+
return;
|
|
4829
|
+
}
|
|
4830
|
+
devLog("[ClipboardStore] removeItem", {
|
|
4831
|
+
itemId,
|
|
4832
|
+
remainingCount: updatedItems.length
|
|
4833
|
+
});
|
|
4834
|
+
set({ items: updatedItems });
|
|
4835
|
+
},
|
|
4836
|
+
clear: () => {
|
|
4837
|
+
const currentCount = get().items.length;
|
|
4838
|
+
devLog("[ClipboardStore] clear", { clearedCount: currentCount });
|
|
4839
|
+
set({ items: [] });
|
|
4840
|
+
}
|
|
4841
|
+
}),
|
|
4842
|
+
{
|
|
4843
|
+
name: "clipboard-items",
|
|
4844
|
+
version: 2
|
|
4845
|
+
}
|
|
4846
|
+
)
|
|
4847
|
+
);
|
|
4848
|
+
var clipboardStore_default = useClipboard;
|
|
4849
|
+
|
|
4850
|
+
// src/Clipboard/ClipboardMenu.tsx
|
|
4851
|
+
import { jsx as jsx36, jsxs as jsxs15 } from "react/jsx-runtime";
|
|
4852
|
+
function ClipboardMenu({ onAddItem }) {
|
|
4853
|
+
const { items, removeItem, clear } = clipboardStore_default();
|
|
4854
|
+
if (items.length === 0) {
|
|
4855
|
+
return /* @__PURE__ */ jsxs15("div", { style: { padding: "16px", textAlign: "center", width: "300px" }, children: [
|
|
4856
|
+
/* @__PURE__ */ jsx36("p", { className: "text-sm text-muted-foreground", children: "No items in clipboard" }),
|
|
4857
|
+
/* @__PURE__ */ jsx36("p", { className: "text-xs text-muted-foreground mt-1", children: 'Right-click a node and select "Copy"' })
|
|
4858
|
+
] });
|
|
4859
|
+
}
|
|
4860
|
+
return /* @__PURE__ */ jsxs15(
|
|
4861
|
+
"div",
|
|
4862
|
+
{
|
|
4863
|
+
style: {
|
|
4864
|
+
width: "380px",
|
|
4865
|
+
maxWidth: "90vw",
|
|
4866
|
+
display: "flex",
|
|
4867
|
+
flexDirection: "column",
|
|
4868
|
+
maxHeight: "500px"
|
|
4869
|
+
},
|
|
4870
|
+
children: [
|
|
4871
|
+
/* @__PURE__ */ jsx36(ScrollArea, { className: "flex-1 p-3 min-h-0", children: /* @__PURE__ */ jsx36(SimpleStack, { gap: 0, children: items.map((item) => /* @__PURE__ */ jsx36(
|
|
4872
|
+
ClipboardItem_default,
|
|
4873
|
+
{
|
|
4874
|
+
item,
|
|
4875
|
+
onAddItem,
|
|
4876
|
+
onRemove: removeItem
|
|
4877
|
+
},
|
|
4878
|
+
item.id
|
|
4879
|
+
)) }) }),
|
|
4880
|
+
/* @__PURE__ */ jsx36(Separator2, {}),
|
|
4881
|
+
/* @__PURE__ */ jsx36("div", { style: { padding: "8px 12px", flexShrink: 0 }, children: /* @__PURE__ */ jsxs15(
|
|
4882
|
+
Button,
|
|
4883
|
+
{
|
|
4884
|
+
className: "w-full",
|
|
4885
|
+
variant: "destructive",
|
|
4886
|
+
size: "sm",
|
|
4887
|
+
onClick: clear,
|
|
4888
|
+
children: [
|
|
4889
|
+
/* @__PURE__ */ jsx36(Trash2, { size: 16 }),
|
|
4890
|
+
"Clear All (",
|
|
4891
|
+
items.length,
|
|
4892
|
+
")"
|
|
4893
|
+
]
|
|
4894
|
+
}
|
|
4895
|
+
) })
|
|
4896
|
+
]
|
|
4897
|
+
}
|
|
4898
|
+
);
|
|
4899
|
+
}
|
|
4900
|
+
var ClipboardMenu_default = ClipboardMenu;
|
|
4901
|
+
export {
|
|
4902
|
+
Alert,
|
|
4903
|
+
AlertDescription,
|
|
4904
|
+
AlertTitle,
|
|
4905
|
+
Avatar,
|
|
4906
|
+
AvatarFallback,
|
|
4907
|
+
AvatarImage,
|
|
4908
|
+
Badge,
|
|
4909
|
+
Button,
|
|
4910
|
+
CELL_RENDERERS,
|
|
4911
|
+
Card,
|
|
4912
|
+
CardContent,
|
|
4913
|
+
CardDescription,
|
|
4914
|
+
CardFooter,
|
|
4915
|
+
CardHeader,
|
|
4916
|
+
CardTitle,
|
|
4917
|
+
Checkbox,
|
|
4918
|
+
ClipboardButton_default as ClipboardButton,
|
|
4919
|
+
ClipboardItem_default as ClipboardItem,
|
|
4920
|
+
ClipboardMenu_default as ClipboardMenu,
|
|
4921
|
+
Collapsible,
|
|
4922
|
+
CollapsibleContent2 as CollapsibleContent,
|
|
4923
|
+
CollapsibleTrigger2 as CollapsibleTrigger,
|
|
4924
|
+
Command,
|
|
4925
|
+
CommandDialog,
|
|
4926
|
+
CommandEmpty,
|
|
4927
|
+
CommandGroup,
|
|
4928
|
+
CommandInput,
|
|
4929
|
+
CommandItem,
|
|
4930
|
+
CommandList,
|
|
4931
|
+
CommandSeparator,
|
|
4932
|
+
CommandShortcut,
|
|
4933
|
+
Dialog,
|
|
4934
|
+
DialogClose,
|
|
4935
|
+
DialogContent,
|
|
4936
|
+
DialogDescription,
|
|
4937
|
+
DialogFooter,
|
|
4938
|
+
DialogHeader,
|
|
4939
|
+
DialogOverlay,
|
|
4940
|
+
DialogPortal,
|
|
4941
|
+
DialogTitle,
|
|
4942
|
+
DialogTrigger,
|
|
4943
|
+
DropdownMenu,
|
|
4944
|
+
DropdownMenuCheckboxItem,
|
|
4945
|
+
DropdownMenuContent,
|
|
4946
|
+
DropdownMenuGroup,
|
|
4947
|
+
DropdownMenuItem,
|
|
4948
|
+
DropdownMenuLabel,
|
|
4949
|
+
DropdownMenuPortal,
|
|
4950
|
+
DropdownMenuRadioGroup,
|
|
4951
|
+
DropdownMenuRadioItem,
|
|
4952
|
+
DropdownMenuSeparator,
|
|
4953
|
+
DropdownMenuShortcut,
|
|
4954
|
+
DropdownMenuSub,
|
|
4955
|
+
DropdownMenuSubContent,
|
|
4956
|
+
DropdownMenuSubTrigger,
|
|
4957
|
+
DropdownMenuTrigger,
|
|
4958
|
+
EntitySelect,
|
|
4959
|
+
EntityTable,
|
|
4960
|
+
EntityTree,
|
|
4961
|
+
Input,
|
|
4962
|
+
InputGroup,
|
|
4963
|
+
InputGroupAddon,
|
|
4964
|
+
InputGroupInput,
|
|
4965
|
+
InputGroupText,
|
|
4966
|
+
InputGroupTextarea,
|
|
4967
|
+
Label2 as Label,
|
|
4968
|
+
Popover,
|
|
4969
|
+
PopoverContent,
|
|
4970
|
+
PopoverTrigger,
|
|
4971
|
+
ScrollArea,
|
|
4972
|
+
ScrollBar,
|
|
4973
|
+
SearchInput,
|
|
4974
|
+
Separator2 as Separator,
|
|
4975
|
+
Sheet,
|
|
4976
|
+
SheetBody,
|
|
4977
|
+
SheetClose,
|
|
4978
|
+
SheetContent,
|
|
4979
|
+
SheetDescription,
|
|
4980
|
+
SheetFooter,
|
|
4981
|
+
SheetHeader,
|
|
4982
|
+
SheetOverlay,
|
|
4983
|
+
SheetTitle,
|
|
4984
|
+
SheetTrigger,
|
|
4985
|
+
SimpleGroup,
|
|
4986
|
+
SimpleStack,
|
|
4987
|
+
SimpleTooltip,
|
|
4988
|
+
Skeleton,
|
|
4989
|
+
Spinner,
|
|
4990
|
+
Stepper,
|
|
4991
|
+
StepperContent,
|
|
4992
|
+
StepperDescription,
|
|
4993
|
+
StepperIndicator,
|
|
4994
|
+
StepperItem,
|
|
4995
|
+
StepperList,
|
|
4996
|
+
StepperNext,
|
|
4997
|
+
StepperPrev,
|
|
4998
|
+
StepperSeparator,
|
|
4999
|
+
StepperTitle,
|
|
5000
|
+
StepperTrigger,
|
|
5001
|
+
Tabs,
|
|
5002
|
+
TabsContent,
|
|
5003
|
+
TabsList,
|
|
5004
|
+
TabsTrigger,
|
|
5005
|
+
Textarea,
|
|
5006
|
+
Tooltip,
|
|
5007
|
+
TooltipContent,
|
|
5008
|
+
TooltipProvider,
|
|
5009
|
+
TooltipTrigger,
|
|
5010
|
+
TreeView,
|
|
5011
|
+
badgeVariants,
|
|
5012
|
+
buttonVariants,
|
|
5013
|
+
createRenderer,
|
|
5014
|
+
useStore as useStepper
|
|
5015
|
+
};
|
|
5016
|
+
//# sourceMappingURL=index.js.map
|