@classic-homes/theme-react 0.1.52 → 0.1.53

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -58,9 +58,18 @@ __export(index_exports, {
58
58
  DialogTrigger: () => DialogTrigger,
59
59
  Input: () => Input,
60
60
  Label: () => Label,
61
+ LiveRegion: () => LiveRegion,
62
+ LoadingLogo: () => LoadingLogo,
63
+ LoadingOverlay: () => LoadingOverlay,
64
+ LoadingPage: () => LoadingPage,
65
+ LoadingSection: () => LoadingSection,
61
66
  PageHeader: () => PageHeader,
62
67
  PasswordInput: () => PasswordInput,
68
+ ProgressBar: () => ProgressBar,
63
69
  Separator: () => Separator,
70
+ Skeleton: () => Skeleton,
71
+ SkipLink: () => SkipLink,
72
+ Spinner: () => Spinner,
64
73
  Switch: () => Switch,
65
74
  badgeVariants: () => badgeVariants,
66
75
  buttonVariants: () => buttonVariants,
@@ -69,7 +78,18 @@ __export(index_exports, {
69
78
  pageHeaderContainerVariants: () => pageHeaderContainerVariants,
70
79
  pageHeaderSubtitleVariants: () => pageHeaderSubtitleVariants,
71
80
  pageHeaderTitleVariants: () => pageHeaderTitleVariants,
72
- useDataPanel: () => useDataPanel
81
+ progressBarVariants: () => progressBarVariants,
82
+ skeletonVariants: () => skeletonVariants,
83
+ spinnerVariants: () => spinnerVariants,
84
+ useAnnounce: () => useAnnounce,
85
+ useArrowNavigation: () => useArrowNavigation,
86
+ useClickOutside: () => useClickOutside,
87
+ useDataPanel: () => useDataPanel,
88
+ useEscapeKey: () => useEscapeKey,
89
+ useFocusRef: () => useFocusRef,
90
+ useFocusTrap: () => useFocusTrap,
91
+ useReducedMotion: () => useReducedMotion,
92
+ useRovingTabindex: () => useRovingTabindex
73
93
  });
74
94
  module.exports = __toCommonJS(index_exports);
75
95
 
@@ -114,9 +134,9 @@ var buttonVariants = (0, import_class_variance_authority.cva)(
114
134
  }
115
135
  );
116
136
  var Button = React.forwardRef(
117
- ({ className, variant, size, asChild = false, ...props }, ref) => {
137
+ ({ className, variant, size, asChild = false, children, ...props }, ref) => {
118
138
  const Comp = asChild ? import_react_slot.Slot : "button";
119
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Comp, { className: cn(buttonVariants({ variant, size, className })), ref, ...props });
139
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Comp, { className: cn(buttonVariants({ variant, size, className })), ref, ...props, children });
120
140
  }
121
141
  );
122
142
  Button.displayName = "Button";
@@ -125,8 +145,8 @@ Button.displayName = "Button";
125
145
  var React2 = __toESM(require("react"));
126
146
  var import_jsx_runtime2 = require("react/jsx-runtime");
127
147
  var Card = React2.forwardRef(
128
- ({ className, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
129
- "div",
148
+ ({ className, as: Component = "div", ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
149
+ Component,
130
150
  {
131
151
  ref,
132
152
  className: cn(
@@ -143,8 +163,8 @@ var CardHeader = React2.forwardRef(
143
163
  );
144
164
  CardHeader.displayName = "CardHeader";
145
165
  var CardTitle = React2.forwardRef(
146
- ({ className, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
147
- "h3",
166
+ ({ className, as: Component = "h3", ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
167
+ Component,
148
168
  {
149
169
  ref,
150
170
  className: cn("text-2xl font-semibold leading-none tracking-tight", className),
@@ -168,13 +188,32 @@ CardFooter.displayName = "CardFooter";
168
188
  var React3 = __toESM(require("react"));
169
189
  var import_jsx_runtime3 = require("react/jsx-runtime");
170
190
  var Input = React3.forwardRef(
171
- ({ className, type, ...props }, ref) => {
191
+ ({
192
+ className,
193
+ type,
194
+ "aria-invalid": ariaInvalid,
195
+ required,
196
+ "aria-describedby": ariaDescribedBy,
197
+ ...props
198
+ }, ref) => {
199
+ if (process.env.NODE_ENV !== "production") {
200
+ if (!props["aria-label"] && !props["aria-labelledby"] && !props.id) {
201
+ console.warn(
202
+ "Input: Missing accessible label. Provide aria-label, aria-labelledby, or associate with a Label using id/htmlFor for WCAG compliance."
203
+ );
204
+ }
205
+ }
172
206
  return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
173
207
  "input",
174
208
  {
175
209
  type,
210
+ "aria-invalid": ariaInvalid,
211
+ "aria-required": required,
212
+ "aria-describedby": ariaDescribedBy,
213
+ required,
176
214
  className: cn(
177
215
  "flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50",
216
+ ariaInvalid === true || ariaInvalid === "true" ? "border-destructive focus-visible:ring-destructive" : "",
178
217
  className
179
218
  ),
180
219
  ref,
@@ -186,7 +225,57 @@ var Input = React3.forwardRef(
186
225
  Input.displayName = "Input";
187
226
 
188
227
  // src/components/PasswordInput.tsx
228
+ var React5 = __toESM(require("react"));
229
+
230
+ // src/hooks/useAnnounce.ts
189
231
  var React4 = __toESM(require("react"));
232
+ function useAnnounce(defaultOptions) {
233
+ const [message, setMessage] = React4.useState("");
234
+ const [politeness, setPoliteness] = React4.useState(
235
+ defaultOptions?.politeness ?? "polite"
236
+ );
237
+ const clearTimeoutRef = React4.useRef(null);
238
+ const clear = React4.useCallback(() => {
239
+ setMessage("");
240
+ if (clearTimeoutRef.current) {
241
+ clearTimeout(clearTimeoutRef.current);
242
+ clearTimeoutRef.current = null;
243
+ }
244
+ }, []);
245
+ const announce = React4.useCallback(
246
+ (newMessage, options) => {
247
+ if (clearTimeoutRef.current) {
248
+ clearTimeout(clearTimeoutRef.current);
249
+ }
250
+ if (options?.politeness) {
251
+ setPoliteness(options.politeness);
252
+ }
253
+ setMessage(newMessage);
254
+ const delay = options?.clearDelay ?? defaultOptions?.clearDelay ?? 1e3;
255
+ if (delay > 0) {
256
+ clearTimeoutRef.current = setTimeout(() => {
257
+ setMessage("");
258
+ }, delay);
259
+ }
260
+ },
261
+ [defaultOptions?.clearDelay]
262
+ );
263
+ React4.useEffect(() => {
264
+ return () => {
265
+ if (clearTimeoutRef.current) {
266
+ clearTimeout(clearTimeoutRef.current);
267
+ }
268
+ };
269
+ }, []);
270
+ return {
271
+ announce,
272
+ message,
273
+ politeness,
274
+ clear
275
+ };
276
+ }
277
+
278
+ // src/components/PasswordInput.tsx
190
279
  var import_jsx_runtime4 = require("react/jsx-runtime");
191
280
  var EyeIcon = () => /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
192
281
  "svg",
@@ -199,6 +288,8 @@ var EyeIcon = () => /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
199
288
  strokeWidth: 2,
200
289
  strokeLinecap: "round",
201
290
  strokeLinejoin: "round",
291
+ "aria-hidden": "true",
292
+ focusable: "false",
202
293
  children: [
203
294
  /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("path", { d: "M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z" }),
204
295
  /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("circle", { cx: "12", cy: "12", r: "3" })
@@ -216,20 +307,48 @@ var EyeOffIcon = () => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
216
307
  strokeWidth: 2,
217
308
  strokeLinecap: "round",
218
309
  strokeLinejoin: "round",
310
+ "aria-hidden": "true",
311
+ focusable: "false",
219
312
  children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("path", { d: "M17.94 17.94A10.07 10.07 0 0 1 12 20c-7 0-11-8-11-8a18.45 18.45 0 0 1 5.06-5.94M9.9 4.24A9.12 9.12 0 0 1 12 4c7 0 11 8 11 8a18.5 18.5 0 0 1-2.16 3.19m-6.72-1.07a3 3 0 1 1-4.24-4.24M1 1l22 22" })
220
313
  }
221
314
  );
222
- var PasswordInput = React4.forwardRef(
223
- ({ className, disabled, showLabel = "Show password", hideLabel = "Hide password", ...props }, ref) => {
224
- const [showPassword, setShowPassword] = React4.useState(false);
315
+ var PasswordInput = React5.forwardRef(
316
+ ({
317
+ className,
318
+ disabled,
319
+ showLabel = "Show password",
320
+ hideLabel = "Hide password",
321
+ visibleAnnouncement = "Password is now visible",
322
+ hiddenAnnouncement = "Password is now hidden",
323
+ id,
324
+ "aria-label": ariaLabel,
325
+ "aria-labelledby": ariaLabelledBy,
326
+ ...props
327
+ }, ref) => {
328
+ const [showPassword, setShowPassword] = React5.useState(false);
329
+ const { announce, message, politeness } = useAnnounce();
330
+ if (process.env.NODE_ENV !== "production") {
331
+ if (!ariaLabel && !ariaLabelledBy && !id) {
332
+ console.warn(
333
+ "PasswordInput: Missing accessible label. Provide aria-label, aria-labelledby, or an id to associate with a Label element for WCAG compliance."
334
+ );
335
+ }
336
+ }
225
337
  const toggleVisibility = () => {
226
- setShowPassword((prev) => !prev);
338
+ setShowPassword((prev) => {
339
+ const newState = !prev;
340
+ announce(newState ? visibleAnnouncement : hiddenAnnouncement);
341
+ return newState;
342
+ });
227
343
  };
228
344
  return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "relative", children: [
229
345
  /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
230
346
  "input",
231
347
  {
232
348
  type: showPassword ? "text" : "password",
349
+ id,
350
+ "aria-label": ariaLabel,
351
+ "aria-labelledby": ariaLabelledBy,
233
352
  className: cn(
234
353
  "flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 pr-10 text-sm ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50",
235
354
  className
@@ -243,35 +362,37 @@ var PasswordInput = React4.forwardRef(
243
362
  "button",
244
363
  {
245
364
  type: "button",
246
- className: "absolute right-0 top-0 h-10 w-10 flex items-center justify-center text-muted-foreground hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 rounded-r-md disabled:pointer-events-none disabled:opacity-50",
365
+ className: "absolute right-0 top-0 h-11 w-11 -mr-0.5 -mt-0.5 flex items-center justify-center text-muted-foreground hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background rounded-r-md disabled:pointer-events-none disabled:opacity-50",
247
366
  onClick: toggleVisibility,
248
367
  disabled,
249
368
  "aria-label": showPassword ? hideLabel : showLabel,
369
+ "aria-pressed": showPassword,
250
370
  children: showPassword ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(EyeOffIcon, {}) : /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(EyeIcon, {})
251
371
  }
252
- )
372
+ ),
373
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { role: "status", "aria-live": politeness, "aria-atomic": "true", className: "sr-only", children: message })
253
374
  ] });
254
375
  }
255
376
  );
256
377
  PasswordInput.displayName = "PasswordInput";
257
378
 
258
379
  // src/components/Label.tsx
259
- var React5 = __toESM(require("react"));
380
+ var React6 = __toESM(require("react"));
260
381
  var LabelPrimitive = __toESM(require("@radix-ui/react-label"));
261
382
  var import_class_variance_authority2 = require("class-variance-authority");
262
383
  var import_jsx_runtime5 = require("react/jsx-runtime");
263
384
  var labelVariants = (0, import_class_variance_authority2.cva)(
264
385
  "text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
265
386
  );
266
- var Label = React5.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(LabelPrimitive.Root, { ref, className: cn(labelVariants(), className), ...props }));
387
+ var Label = React6.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(LabelPrimitive.Root, { ref, className: cn(labelVariants(), className), ...props }));
267
388
  Label.displayName = LabelPrimitive.Root.displayName;
268
389
 
269
390
  // src/components/Badge.tsx
270
- var React6 = __toESM(require("react"));
391
+ var React7 = __toESM(require("react"));
271
392
  var import_class_variance_authority3 = require("class-variance-authority");
272
393
  var import_jsx_runtime6 = require("react/jsx-runtime");
273
394
  var badgeVariants = (0, import_class_variance_authority3.cva)(
274
- "inline-flex items-center rounded-full border border-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",
395
+ "inline-flex items-center rounded-full border border-border px-2.5 py-0.5 text-xs font-semibold transition-colors",
275
396
  {
276
397
  variants: {
277
398
  variant: {
@@ -286,15 +407,28 @@ var badgeVariants = (0, import_class_variance_authority3.cva)(
286
407
  }
287
408
  }
288
409
  );
289
- var Badge = React6.forwardRef(
290
- ({ className, variant, ...props }, ref) => {
291
- return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
410
+ var Badge = React7.forwardRef(
411
+ ({ className, variant, live, label, children, ...props }, ref) => {
412
+ if (process.env.NODE_ENV !== "production") {
413
+ if (live && !label) {
414
+ console.warn(
415
+ 'Badge: When using live={true} for dynamic content, consider providing a label prop for better screen reader context (e.g., label="unread notifications" for a count badge).'
416
+ );
417
+ }
418
+ }
419
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
292
420
  "div",
293
421
  {
294
422
  ref,
295
- role: "status",
423
+ role: live ? "status" : void 0,
424
+ "aria-live": live ? "polite" : void 0,
425
+ "aria-atomic": live ? "true" : void 0,
296
426
  className: cn(badgeVariants({ variant }), className),
297
- ...props
427
+ ...props,
428
+ children: [
429
+ children,
430
+ label && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "sr-only", children: label })
431
+ ]
298
432
  }
299
433
  );
300
434
  }
@@ -302,81 +436,123 @@ var Badge = React6.forwardRef(
302
436
  Badge.displayName = "Badge";
303
437
 
304
438
  // src/components/Separator.tsx
305
- var React7 = __toESM(require("react"));
439
+ var React8 = __toESM(require("react"));
306
440
  var SeparatorPrimitive = __toESM(require("@radix-ui/react-separator"));
307
441
  var import_jsx_runtime7 = require("react/jsx-runtime");
308
- var Separator = React7.forwardRef(({ className, orientation = "horizontal", decorative = true, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
309
- SeparatorPrimitive.Root,
310
- {
311
- ref,
312
- decorative,
313
- orientation,
314
- className: cn(
315
- "shrink-0 bg-border",
316
- orientation === "horizontal" ? "h-[1px] w-full" : "h-full w-[1px]",
317
- className
318
- ),
319
- ...props
442
+ var Separator = React8.forwardRef(({ className, orientation = "horizontal", decorative = true, ...props }, ref) => {
443
+ if (process.env.NODE_ENV !== "production") {
444
+ if (!decorative && !props["aria-label"]) {
445
+ console.warn(
446
+ "Separator: Non-decorative separators (decorative={false}) should have an aria-label to describe the content they separate."
447
+ );
448
+ }
320
449
  }
321
- ));
450
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
451
+ SeparatorPrimitive.Root,
452
+ {
453
+ ref,
454
+ decorative,
455
+ orientation,
456
+ className: cn(
457
+ "shrink-0 bg-border",
458
+ orientation === "horizontal" ? "h-[1px] w-full" : "h-full w-[1px]",
459
+ className
460
+ ),
461
+ ...props
462
+ }
463
+ );
464
+ });
322
465
  Separator.displayName = SeparatorPrimitive.Root.displayName;
323
466
 
324
467
  // src/components/Switch.tsx
325
- var React8 = __toESM(require("react"));
468
+ var React9 = __toESM(require("react"));
326
469
  var SwitchPrimitives = __toESM(require("@radix-ui/react-switch"));
327
470
  var import_jsx_runtime8 = require("react/jsx-runtime");
328
471
  var SwitchRoot = SwitchPrimitives.Root;
329
472
  var SwitchThumb = SwitchPrimitives.Thumb;
330
- var Switch = React8.forwardRef(
331
- ({ className, size = "default", ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
332
- SwitchRoot,
333
- {
334
- className: cn(
335
- "peer inline-flex shrink-0 cursor-pointer items-center rounded-full border-2 border-transparent transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-primary data-[state=unchecked]:bg-input",
336
- size === "default" && "h-7 w-12",
337
- size === "sm" && "h-5 w-9",
338
- className
339
- ),
340
- ref,
341
- ...props,
342
- children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
343
- SwitchThumb,
344
- {
345
- className: cn(
346
- "pointer-events-none block rounded-full bg-background shadow-lg ring-0 transition-transform",
347
- size === "default" && "h-5 w-5 data-[state=checked]:translate-x-[22px] data-[state=unchecked]:translate-x-0.5",
348
- size === "sm" && "h-4 w-4 data-[state=checked]:translate-x-4 data-[state=unchecked]:translate-x-0"
349
- )
350
- }
351
- )
473
+ var Switch = React9.forwardRef(
474
+ ({
475
+ className,
476
+ size = "default",
477
+ "aria-label": ariaLabel,
478
+ "aria-labelledby": ariaLabelledBy,
479
+ "aria-describedby": ariaDescribedBy,
480
+ ...props
481
+ }, ref) => {
482
+ if (process.env.NODE_ENV !== "production") {
483
+ if (!ariaLabel && !ariaLabelledBy && !props.id) {
484
+ console.warn(
485
+ "Switch: Missing accessible label. Provide aria-label, aria-labelledby, or associate with a Label using id/htmlFor for WCAG compliance."
486
+ );
487
+ }
352
488
  }
353
- )
489
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
490
+ SwitchRoot,
491
+ {
492
+ className: cn(
493
+ "peer inline-flex shrink-0 cursor-pointer items-center rounded-full border-2 border-transparent transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-primary data-[state=unchecked]:bg-input",
494
+ size === "default" && "h-7 w-12",
495
+ size === "sm" && "h-5 w-9",
496
+ className
497
+ ),
498
+ ref,
499
+ "aria-label": ariaLabel,
500
+ "aria-labelledby": ariaLabelledBy,
501
+ "aria-describedby": ariaDescribedBy,
502
+ ...props,
503
+ children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
504
+ SwitchThumb,
505
+ {
506
+ className: cn(
507
+ "pointer-events-none block rounded-full bg-background shadow-lg ring-0 transition-transform",
508
+ size === "default" && "h-5 w-5 data-[state=checked]:translate-x-[22px] data-[state=unchecked]:translate-x-0.5",
509
+ size === "sm" && "h-4 w-4 data-[state=checked]:translate-x-4 data-[state=unchecked]:translate-x-0"
510
+ )
511
+ }
512
+ )
513
+ }
514
+ );
515
+ }
354
516
  );
355
517
  Switch.displayName = SwitchPrimitives.Root.displayName;
356
518
 
357
519
  // src/components/Avatar.tsx
358
- var React9 = __toESM(require("react"));
520
+ var React10 = __toESM(require("react"));
359
521
  var AvatarPrimitive = __toESM(require("@radix-ui/react-avatar"));
360
522
  var import_jsx_runtime9 = require("react/jsx-runtime");
361
- var Avatar = React9.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
362
- AvatarPrimitive.Root,
363
- {
364
- ref,
365
- className: cn("relative flex h-10 w-10 shrink-0 overflow-hidden rounded-full", className),
366
- ...props
523
+ var Avatar = React10.forwardRef(
524
+ ({ className, name, "aria-label": ariaLabel, ...props }, ref) => {
525
+ if (process.env.NODE_ENV !== "production") {
526
+ if (!ariaLabel && !name) {
527
+ console.warn(
528
+ 'Avatar: Missing accessible text. Provide either a "name" prop or "aria-label" for screen reader users.'
529
+ );
530
+ }
531
+ }
532
+ const computedAriaLabel = ariaLabel ?? (name ? `Avatar for ${name}` : void 0);
533
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
534
+ AvatarPrimitive.Root,
535
+ {
536
+ ref,
537
+ "aria-label": computedAriaLabel,
538
+ className: cn("relative flex h-10 w-10 shrink-0 overflow-hidden rounded-full", className),
539
+ ...props
540
+ }
541
+ );
367
542
  }
368
- ));
543
+ );
369
544
  Avatar.displayName = AvatarPrimitive.Root.displayName;
370
- var AvatarImage = React9.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
545
+ var AvatarImage = React10.forwardRef(({ className, alt, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
371
546
  AvatarPrimitive.Image,
372
547
  {
373
548
  ref,
549
+ alt,
374
550
  className: cn("aspect-square h-full w-full object-cover", className),
375
551
  ...props
376
552
  }
377
553
  ));
378
554
  AvatarImage.displayName = AvatarPrimitive.Image.displayName;
379
- var AvatarFallback = React9.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
555
+ var AvatarFallback = React10.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
380
556
  AvatarPrimitive.Fallback,
381
557
  {
382
558
  ref,
@@ -390,7 +566,7 @@ var AvatarFallback = React9.forwardRef(({ className, ...props }, ref) => /* @__P
390
566
  AvatarFallback.displayName = AvatarPrimitive.Fallback.displayName;
391
567
 
392
568
  // src/components/Dialog.tsx
393
- var React10 = __toESM(require("react"));
569
+ var React11 = __toESM(require("react"));
394
570
  var DialogPrimitive = __toESM(require("@radix-ui/react-dialog"));
395
571
  var import_jsx_runtime10 = require("react/jsx-runtime");
396
572
  var PrimitiveOverlay = DialogPrimitive.Overlay;
@@ -401,7 +577,7 @@ var Dialog = DialogPrimitive.Root;
401
577
  var DialogTrigger = DialogPrimitive.Trigger;
402
578
  var DialogPortal = DialogPrimitive.Portal;
403
579
  var DialogClose = DialogPrimitive.Close;
404
- var DialogOverlay = React10.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
580
+ var DialogOverlay = React11.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
405
581
  PrimitiveOverlay,
406
582
  {
407
583
  ref,
@@ -413,7 +589,7 @@ var DialogOverlay = React10.forwardRef(({ className, ...props }, ref) => /* @__P
413
589
  }
414
590
  ));
415
591
  DialogOverlay.displayName = DialogPrimitive.Overlay.displayName;
416
- var DialogContent = React10.forwardRef(({ className, children, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(DialogPortal, { children: [
592
+ var DialogContent = React11.forwardRef(({ className, children, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(DialogPortal, { children: [
417
593
  /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(DialogOverlay, {}),
418
594
  /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
419
595
  PrimitiveContent,
@@ -439,7 +615,7 @@ var DialogFooter = ({ className, ...props }) => /* @__PURE__ */ (0, import_jsx_r
439
615
  }
440
616
  );
441
617
  DialogFooter.displayName = "DialogFooter";
442
- var DialogTitle = React10.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
618
+ var DialogTitle = React11.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
443
619
  PrimitiveTitle,
444
620
  {
445
621
  ref,
@@ -448,7 +624,7 @@ var DialogTitle = React10.forwardRef(({ className, ...props }, ref) => /* @__PUR
448
624
  }
449
625
  ));
450
626
  DialogTitle.displayName = DialogPrimitive.Title.displayName;
451
- var DialogDescription = React10.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
627
+ var DialogDescription = React11.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
452
628
  PrimitiveDescription,
453
629
  {
454
630
  ref,
@@ -459,7 +635,7 @@ var DialogDescription = React10.forwardRef(({ className, ...props }, ref) => /*
459
635
  DialogDescription.displayName = DialogPrimitive.Description.displayName;
460
636
 
461
637
  // src/components/PageHeader.tsx
462
- var React11 = __toESM(require("react"));
638
+ var React12 = __toESM(require("react"));
463
639
  var import_class_variance_authority4 = require("class-variance-authority");
464
640
  var import_jsx_runtime11 = require("react/jsx-runtime");
465
641
  var pageHeaderContainerVariants = (0, import_class_variance_authority4.cva)("", {
@@ -510,25 +686,34 @@ var pageHeaderActionsVariants = (0, import_class_variance_authority4.cva)("flex
510
686
  variant: "default"
511
687
  }
512
688
  });
513
- var PageHeader = React11.forwardRef(
514
- ({ className, variant, title, subtitle, actions, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(
515
- "header",
516
- {
517
- ref,
518
- className: cn(pageHeaderContainerVariants({ variant }), className),
519
- ...props,
520
- children: [
521
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("h1", { className: pageHeaderTitleVariants({ variant }), children: title }),
522
- subtitle && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("p", { className: pageHeaderSubtitleVariants({ variant }), children: subtitle }),
523
- actions && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { className: pageHeaderActionsVariants({ variant }), children: actions })
524
- ]
689
+ var PageHeader = React12.forwardRef(
690
+ ({ className, variant, title, subtitle, actions, as: Heading = "h1", ...props }, ref) => {
691
+ if (process.env.NODE_ENV !== "production") {
692
+ if (Heading === "h1") {
693
+ console.warn(
694
+ 'PageHeader: Using as="h1" (the default). Ensure there is only one h1 per page for proper document structure. Consider as="h2" for secondary headers.'
695
+ );
696
+ }
525
697
  }
526
- )
698
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(
699
+ "header",
700
+ {
701
+ ref,
702
+ className: cn(pageHeaderContainerVariants({ variant }), className),
703
+ ...props,
704
+ children: [
705
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Heading, { className: pageHeaderTitleVariants({ variant }), children: title }),
706
+ subtitle && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("p", { className: pageHeaderSubtitleVariants({ variant }), children: subtitle }),
707
+ actions && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { className: pageHeaderActionsVariants({ variant }), children: actions })
708
+ ]
709
+ }
710
+ );
711
+ }
527
712
  );
528
713
  PageHeader.displayName = "PageHeader";
529
714
 
530
715
  // src/components/DataPanel.tsx
531
- var React12 = __toESM(require("react"));
716
+ var React13 = __toESM(require("react"));
532
717
  var DropdownMenuPrimitive = __toESM(require("@radix-ui/react-dropdown-menu"));
533
718
  var import_data_panel_core = require("@classic-homes/data-panel-core");
534
719
  var import_jsx_runtime12 = require("react/jsx-runtime");
@@ -546,28 +731,28 @@ function useDataPanel(options = {}) {
546
731
  constraints = {}
547
732
  } = options;
548
733
  const resolvedConstraints = { ...import_data_panel_core.DEFAULT_CONSTRAINTS, ...constraints };
549
- const [persistedState] = React12.useState(() => {
734
+ const [persistedState] = React13.useState(() => {
550
735
  if (persistKey && typeof window !== "undefined") {
551
736
  return (0, import_data_panel_core.loadPanelState)(persistKey);
552
737
  }
553
738
  return null;
554
739
  });
555
- const [mode, setMode] = React12.useState(persistedState?.mode ?? initialMode);
556
- const [edge, setEdge] = React12.useState(persistedState?.edge ?? initialEdge);
557
- const [isExpanded, setIsExpanded] = React12.useState(
740
+ const [mode, setMode] = React13.useState(persistedState?.mode ?? initialMode);
741
+ const [edge, setEdge] = React13.useState(persistedState?.edge ?? initialEdge);
742
+ const [isExpanded, setIsExpanded] = React13.useState(
558
743
  persistedState?.isExpanded ?? initialExpanded
559
744
  );
560
- const [detachedPosition, setDetachedPosition] = React12.useState(
745
+ const [detachedPosition, setDetachedPosition] = React13.useState(
561
746
  persistedState?.detachedPosition ?? import_data_panel_core.DEFAULT_PANEL_STATE.detachedPosition
562
747
  );
563
- const [detachedSize, setDetachedSize] = React12.useState(
748
+ const [detachedSize, setDetachedSize] = React13.useState(
564
749
  persistedState?.detachedSize ?? import_data_panel_core.DEFAULT_PANEL_STATE.detachedSize
565
750
  );
566
- const [pinnedSize, setPinnedSize] = React12.useState(
751
+ const [pinnedSize, setPinnedSize] = React13.useState(
567
752
  persistedState?.pinnedSize ?? import_data_panel_core.DEFAULT_PANEL_STATE.pinnedSize
568
753
  );
569
- const saveTimeoutRef = React12.useRef(null);
570
- const debouncedSave = React12.useCallback(() => {
754
+ const saveTimeoutRef = React13.useRef(null);
755
+ const debouncedSave = React13.useCallback(() => {
571
756
  if (!persistKey) return;
572
757
  if (saveTimeoutRef.current) {
573
758
  clearTimeout(saveTimeoutRef.current);
@@ -585,7 +770,7 @@ function useDataPanel(options = {}) {
585
770
  });
586
771
  }, 300);
587
772
  }, [persistKey, mode, edge, isExpanded, detachedPosition, detachedSize, pinnedSize]);
588
- const handleSetMode = React12.useCallback(
773
+ const handleSetMode = React13.useCallback(
589
774
  (newMode) => {
590
775
  setMode(newMode);
591
776
  if (newMode === "detached" && typeof window !== "undefined" && detachedPosition.x === import_data_panel_core.DEFAULT_PANEL_STATE.detachedPosition.x && detachedPosition.y === import_data_panel_core.DEFAULT_PANEL_STATE.detachedPosition.y) {
@@ -596,7 +781,7 @@ function useDataPanel(options = {}) {
596
781
  },
597
782
  [detachedPosition, detachedSize]
598
783
  );
599
- const handleSetDetachedPosition = React12.useCallback(
784
+ const handleSetDetachedPosition = React13.useCallback(
600
785
  (position) => {
601
786
  if (typeof window === "undefined") {
602
787
  setDetachedPosition(position);
@@ -608,13 +793,13 @@ function useDataPanel(options = {}) {
608
793
  },
609
794
  [detachedSize]
610
795
  );
611
- const handleSetDetachedSize = React12.useCallback(
796
+ const handleSetDetachedSize = React13.useCallback(
612
797
  (size) => {
613
798
  setDetachedSize((0, import_data_panel_core.constrainSize)(size, resolvedConstraints));
614
799
  },
615
800
  [resolvedConstraints]
616
801
  );
617
- const handleSetPinnedSize = React12.useCallback(
802
+ const handleSetPinnedSize = React13.useCallback(
618
803
  (size) => {
619
804
  if (typeof window === "undefined") {
620
805
  setPinnedSize(size);
@@ -628,7 +813,7 @@ function useDataPanel(options = {}) {
628
813
  },
629
814
  [edge, resolvedConstraints]
630
815
  );
631
- React12.useEffect(() => {
816
+ React13.useEffect(() => {
632
817
  debouncedSave();
633
818
  }, [debouncedSave]);
634
819
  return {
@@ -704,10 +889,11 @@ var edgeLabels = {
704
889
  top: "Pin to top",
705
890
  bottom: "Pin to bottom"
706
891
  };
707
- var DataPanelHeader = React12.forwardRef(
892
+ var DataPanelHeader = React13.forwardRef(
708
893
  ({
709
894
  title,
710
895
  subtitle,
896
+ titleId,
711
897
  mode,
712
898
  edge,
713
899
  isExpanded,
@@ -734,7 +920,7 @@ var DataPanelHeader = React12.forwardRef(
734
920
  "data-panel-header": true,
735
921
  children: [
736
922
  /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: "flex flex-col min-w-0 flex-1", children: headerContent ? headerContent : /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(import_jsx_runtime12.Fragment, { children: [
737
- title && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("h3", { className: "text-sm font-semibold leading-none truncate", children: title }),
923
+ title && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("h3", { id: titleId, className: "text-sm font-semibold leading-none truncate", children: title }),
738
924
  subtitle && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("p", { className: "text-xs text-muted-foreground mt-1 truncate", children: subtitle })
739
925
  ] }) }),
740
926
  /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "flex items-center gap-1 shrink-0", children: [
@@ -816,13 +1002,22 @@ var DataPanelHeader = React12.forwardRef(
816
1002
  }
817
1003
  );
818
1004
  DataPanelHeader.displayName = "DataPanelHeader";
819
- var DataPanelContent = React12.forwardRef(
820
- ({ children, className }, ref) => {
821
- return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { ref, className: cn("flex-1 overflow-auto p-4", className), children });
1005
+ var DataPanelContent = React13.forwardRef(
1006
+ ({ children, className, "aria-label": ariaLabel = "Panel content" }, ref) => {
1007
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
1008
+ "div",
1009
+ {
1010
+ ref,
1011
+ role: "region",
1012
+ "aria-label": ariaLabel,
1013
+ className: cn("flex-1 overflow-auto p-4", className),
1014
+ children
1015
+ }
1016
+ );
822
1017
  }
823
1018
  );
824
1019
  DataPanelContent.displayName = "DataPanelContent";
825
- var DataPanelFooter = React12.forwardRef(
1020
+ var DataPanelFooter = React13.forwardRef(
826
1021
  ({ actions = [], footerContent, footerActions, className }, ref) => {
827
1022
  if (!footerContent && actions.length === 0 && !footerActions) {
828
1023
  return null;
@@ -858,7 +1053,7 @@ var DataPanelFooter = React12.forwardRef(
858
1053
  }
859
1054
  );
860
1055
  DataPanelFooter.displayName = "DataPanelFooter";
861
- var DataPanelTab = React12.forwardRef(
1056
+ var DataPanelTab = React13.forwardRef(
862
1057
  ({ title = "Panel", edge, onClick, className }, ref) => {
863
1058
  const positionClasses = {
864
1059
  left: "left-0 top-1/2 -translate-y-1/2 rounded-r-md border-l-0",
@@ -895,7 +1090,186 @@ var DataPanelTab = React12.forwardRef(
895
1090
  }
896
1091
  );
897
1092
  DataPanelTab.displayName = "DataPanelTab";
898
- var DataPanel = React12.forwardRef(
1093
+ var KEYBOARD_RESIZE_STEP = 10;
1094
+ var KEYBOARD_RESIZE_STEP_LARGE = 50;
1095
+ function ResizeHandles({
1096
+ mode,
1097
+ edge,
1098
+ detachedSize,
1099
+ pinnedSize,
1100
+ onResizeStart,
1101
+ onSizeChange,
1102
+ onPinnedSizeChange,
1103
+ constraints
1104
+ }) {
1105
+ const handleDetachedKeyDown = React13.useCallback(
1106
+ (e, handle) => {
1107
+ const step = e.shiftKey ? KEYBOARD_RESIZE_STEP_LARGE : KEYBOARD_RESIZE_STEP;
1108
+ let newSize = { ...detachedSize };
1109
+ let handled = false;
1110
+ switch (e.key) {
1111
+ case "ArrowUp":
1112
+ if (handle.includes("top")) {
1113
+ newSize.height = Math.max(constraints.minHeight || 200, detachedSize.height + step);
1114
+ handled = true;
1115
+ } else if (handle.includes("bottom")) {
1116
+ newSize.height = Math.max(constraints.minHeight || 200, detachedSize.height - step);
1117
+ handled = true;
1118
+ }
1119
+ break;
1120
+ case "ArrowDown":
1121
+ if (handle.includes("top")) {
1122
+ newSize.height = Math.max(constraints.minHeight || 200, detachedSize.height - step);
1123
+ handled = true;
1124
+ } else if (handle.includes("bottom")) {
1125
+ newSize.height = Math.min(constraints.maxHeight || 800, detachedSize.height + step);
1126
+ handled = true;
1127
+ }
1128
+ break;
1129
+ case "ArrowLeft":
1130
+ if (handle.includes("left")) {
1131
+ newSize.width = Math.max(constraints.minWidth || 280, detachedSize.width + step);
1132
+ handled = true;
1133
+ } else if (handle.includes("right")) {
1134
+ newSize.width = Math.max(constraints.minWidth || 280, detachedSize.width - step);
1135
+ handled = true;
1136
+ }
1137
+ break;
1138
+ case "ArrowRight":
1139
+ if (handle.includes("left")) {
1140
+ newSize.width = Math.max(constraints.minWidth || 280, detachedSize.width - step);
1141
+ handled = true;
1142
+ } else if (handle.includes("right")) {
1143
+ newSize.width = Math.min(constraints.maxWidth || 600, detachedSize.width + step);
1144
+ handled = true;
1145
+ }
1146
+ break;
1147
+ }
1148
+ if (handled) {
1149
+ e.preventDefault();
1150
+ onSizeChange(newSize);
1151
+ }
1152
+ },
1153
+ [detachedSize, constraints, onSizeChange]
1154
+ );
1155
+ const handlePinnedKeyDown = React13.useCallback(
1156
+ (e) => {
1157
+ const step = e.shiftKey ? KEYBOARD_RESIZE_STEP_LARGE : KEYBOARD_RESIZE_STEP;
1158
+ let newSize = pinnedSize;
1159
+ let handled = false;
1160
+ const isHorizontal2 = edge === "left" || edge === "right";
1161
+ if (isHorizontal2) {
1162
+ if (e.key === "ArrowLeft" && edge === "right" || e.key === "ArrowRight" && edge === "left") {
1163
+ newSize = Math.min(constraints.maxWidth || 600, pinnedSize + step);
1164
+ handled = true;
1165
+ } else if (e.key === "ArrowRight" && edge === "right" || e.key === "ArrowLeft" && edge === "left") {
1166
+ newSize = Math.max(constraints.minWidth || 280, pinnedSize - step);
1167
+ handled = true;
1168
+ }
1169
+ } else {
1170
+ if (e.key === "ArrowUp" && edge === "bottom" || e.key === "ArrowDown" && edge === "top") {
1171
+ newSize = Math.min(constraints.maxHeight || 800, pinnedSize + step);
1172
+ handled = true;
1173
+ } else if (e.key === "ArrowDown" && edge === "bottom" || e.key === "ArrowUp" && edge === "top") {
1174
+ newSize = Math.max(constraints.minHeight || 200, pinnedSize - step);
1175
+ handled = true;
1176
+ }
1177
+ }
1178
+ if (handled) {
1179
+ e.preventDefault();
1180
+ onPinnedSizeChange(newSize);
1181
+ }
1182
+ },
1183
+ [edge, pinnedSize, constraints, onPinnedSizeChange]
1184
+ );
1185
+ const getAriaValueNow = (handle) => {
1186
+ if (handle.includes("left") || handle.includes("right")) {
1187
+ return mode === "detached" ? detachedSize.width : pinnedSize;
1188
+ }
1189
+ return mode === "detached" ? detachedSize.height : pinnedSize;
1190
+ };
1191
+ const getAriaValueMinMax = (handle) => {
1192
+ const isWidth = handle.includes("left") || handle.includes("right");
1193
+ if (isWidth) {
1194
+ return {
1195
+ min: constraints.minWidth || 280,
1196
+ max: constraints.maxWidth || 600
1197
+ };
1198
+ }
1199
+ return {
1200
+ min: constraints.minHeight || 200,
1201
+ max: constraints.maxHeight || 800
1202
+ };
1203
+ };
1204
+ if (mode === "detached") {
1205
+ const handles = [
1206
+ "top",
1207
+ "bottom",
1208
+ "left",
1209
+ "right",
1210
+ "top-left",
1211
+ "top-right",
1212
+ "bottom-left",
1213
+ "bottom-right"
1214
+ ];
1215
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_jsx_runtime12.Fragment, { children: handles.map((handle) => {
1216
+ const isCorner = handle.includes("-");
1217
+ const isHorizontal2 = handle === "top" || handle === "bottom";
1218
+ const isVertical = handle === "left" || handle === "right";
1219
+ const { min: min2, max: max2 } = getAriaValueMinMax(handle);
1220
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
1221
+ "div",
1222
+ {
1223
+ className: cn(
1224
+ "resize-handle",
1225
+ isHorizontal2 && "resize-handle--horizontal",
1226
+ isVertical && "resize-handle--vertical",
1227
+ isCorner && "resize-handle--corner",
1228
+ `resize-handle--${handle}`
1229
+ ),
1230
+ role: "slider",
1231
+ tabIndex: 0,
1232
+ "aria-orientation": isVertical ? "vertical" : "horizontal",
1233
+ "aria-label": `Resize panel from ${handle.replace("-", " ")} ${isCorner ? "corner" : "edge"}`,
1234
+ "aria-valuenow": getAriaValueNow(handle),
1235
+ "aria-valuemin": min2,
1236
+ "aria-valuemax": max2,
1237
+ "aria-valuetext": `${getAriaValueNow(handle)} pixels`,
1238
+ style: isCorner ? { "--handle-radius": "6px" } : void 0,
1239
+ onPointerDown: (e) => onResizeStart(e, handle),
1240
+ onKeyDown: (e) => handleDetachedKeyDown(e, handle)
1241
+ },
1242
+ handle
1243
+ );
1244
+ }) });
1245
+ }
1246
+ const pinnedHandle = (0, import_data_panel_core.getPinnedResizeHandle)(edge);
1247
+ const isHorizontal = edge === "left" || edge === "right";
1248
+ const { min, max } = getAriaValueMinMax(pinnedHandle);
1249
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
1250
+ "div",
1251
+ {
1252
+ className: cn(
1253
+ "resize-handle",
1254
+ edge === "left" && "resize-handle--vertical resize-handle--right",
1255
+ edge === "right" && "resize-handle--vertical resize-handle--left",
1256
+ edge === "top" && "resize-handle--horizontal resize-handle--bottom",
1257
+ edge === "bottom" && "resize-handle--horizontal resize-handle--top"
1258
+ ),
1259
+ role: "slider",
1260
+ tabIndex: 0,
1261
+ "aria-orientation": isHorizontal ? "horizontal" : "vertical",
1262
+ "aria-label": `Resize panel from ${edge} edge`,
1263
+ "aria-valuenow": pinnedSize,
1264
+ "aria-valuemin": min,
1265
+ "aria-valuemax": max,
1266
+ "aria-valuetext": `${pinnedSize} pixels`,
1267
+ onPointerDown: (e) => onResizeStart(e, pinnedHandle),
1268
+ onKeyDown: handlePinnedKeyDown
1269
+ }
1270
+ );
1271
+ }
1272
+ var DataPanel = React13.forwardRef(
899
1273
  ({
900
1274
  open: controlledOpen,
901
1275
  onOpenChange,
@@ -924,31 +1298,33 @@ var DataPanel = React12.forwardRef(
924
1298
  className
925
1299
  }, ref) => {
926
1300
  const resolvedConstraints = { ...import_data_panel_core.DEFAULT_CONSTRAINTS, ...constraints };
927
- const [internalOpen, setInternalOpen] = React12.useState(true);
928
- const [internalMode, setInternalMode] = React12.useState(import_data_panel_core.DEFAULT_PANEL_STATE.mode);
929
- const [internalEdge, setInternalEdge] = React12.useState(import_data_panel_core.DEFAULT_PANEL_STATE.edge);
930
- const [internalExpanded, setInternalExpanded] = React12.useState(
1301
+ const panelId = React13.useId();
1302
+ const titleId = `${panelId}-title`;
1303
+ const [internalOpen, setInternalOpen] = React13.useState(true);
1304
+ const [internalMode, setInternalMode] = React13.useState(import_data_panel_core.DEFAULT_PANEL_STATE.mode);
1305
+ const [internalEdge, setInternalEdge] = React13.useState(import_data_panel_core.DEFAULT_PANEL_STATE.edge);
1306
+ const [internalExpanded, setInternalExpanded] = React13.useState(
931
1307
  import_data_panel_core.DEFAULT_PANEL_STATE.isExpanded
932
1308
  );
933
- const [detachedPosition, setDetachedPosition] = React12.useState(
1309
+ const [detachedPosition, setDetachedPosition] = React13.useState(
934
1310
  import_data_panel_core.DEFAULT_PANEL_STATE.detachedPosition
935
1311
  );
936
- const [detachedSize, setDetachedSize] = React12.useState(import_data_panel_core.DEFAULT_PANEL_STATE.detachedSize);
937
- const [pinnedSize, setPinnedSize] = React12.useState(import_data_panel_core.DEFAULT_PANEL_STATE.pinnedSize);
938
- const [isDragging, setIsDragging] = React12.useState(false);
939
- const [isResizing, setIsResizing] = React12.useState(false);
940
- const [snapPreview, setSnapPreview] = React12.useState(null);
941
- const [isPinnedDragging, setIsPinnedDragging] = React12.useState(false);
942
- const [pullOffset, setPullOffset] = React12.useState(0);
943
- const dragStateRef = React12.useRef({
1312
+ const [detachedSize, setDetachedSize] = React13.useState(import_data_panel_core.DEFAULT_PANEL_STATE.detachedSize);
1313
+ const [pinnedSize, setPinnedSize] = React13.useState(import_data_panel_core.DEFAULT_PANEL_STATE.pinnedSize);
1314
+ const [isDragging, setIsDragging] = React13.useState(false);
1315
+ const [isResizing, setIsResizing] = React13.useState(false);
1316
+ const [snapPreview, setSnapPreview] = React13.useState(null);
1317
+ const [isPinnedDragging, setIsPinnedDragging] = React13.useState(false);
1318
+ const [pullOffset, setPullOffset] = React13.useState(0);
1319
+ const dragStateRef = React13.useRef({
944
1320
  startPosition: { x: 0, y: 0 },
945
1321
  startPanelPosition: { x: 0, y: 0 }
946
1322
  });
947
- const pinnedDragStateRef = React12.useRef({
1323
+ const pinnedDragStateRef = React13.useRef({
948
1324
  startPosition: { x: 0, y: 0 },
949
1325
  hasDetached: false
950
1326
  });
951
- const resizeStateRef = React12.useRef({
1327
+ const resizeStateRef = React13.useRef({
952
1328
  handle: null,
953
1329
  startPosition: { x: 0, y: 0 },
954
1330
  startSize: { width: 0, height: 0 },
@@ -958,8 +1334,8 @@ var DataPanel = React12.forwardRef(
958
1334
  const mode = controlledMode ?? internalMode;
959
1335
  const edge = controlledEdge ?? internalEdge;
960
1336
  const isExpanded = controlledExpanded ?? internalExpanded;
961
- const saveTimeoutRef = React12.useRef(null);
962
- const debouncedSave = React12.useCallback(() => {
1337
+ const saveTimeoutRef = React13.useRef(null);
1338
+ const debouncedSave = React13.useCallback(() => {
963
1339
  if (!persistKey) return;
964
1340
  if (saveTimeoutRef.current) {
965
1341
  clearTimeout(saveTimeoutRef.current);
@@ -985,7 +1361,7 @@ var DataPanel = React12.forwardRef(
985
1361
  detachedSize,
986
1362
  pinnedSize
987
1363
  ]);
988
- React12.useEffect(() => {
1364
+ React13.useEffect(() => {
989
1365
  if (!persistKey) return;
990
1366
  const persisted = (0, import_data_panel_core.loadPanelState)(persistKey);
991
1367
  if (persisted) {
@@ -998,7 +1374,7 @@ var DataPanel = React12.forwardRef(
998
1374
  if (persisted.pinnedSize) setPinnedSize(persisted.pinnedSize);
999
1375
  }
1000
1376
  }, [persistKey, controlledMode, controlledEdge, controlledExpanded]);
1001
- React12.useEffect(() => {
1377
+ React13.useEffect(() => {
1002
1378
  if (mode === "detached" && detachedPosition.x === import_data_panel_core.DEFAULT_PANEL_STATE.detachedPosition.x && detachedPosition.y === import_data_panel_core.DEFAULT_PANEL_STATE.detachedPosition.y) {
1003
1379
  setDetachedPosition(
1004
1380
  (0, import_data_panel_core.getInitialDetachedPosition)(detachedSize, window.innerWidth, window.innerHeight)
@@ -1056,7 +1432,7 @@ var DataPanel = React12.forwardRef(
1056
1432
  document.body.style.userSelect = "none";
1057
1433
  }
1058
1434
  };
1059
- const handleDragMove = React12.useCallback(
1435
+ const handleDragMove = React13.useCallback(
1060
1436
  (e) => {
1061
1437
  if (!isDragging) return;
1062
1438
  const currentPos = (0, import_data_panel_core.getPointerPosition)(e);
@@ -1083,7 +1459,7 @@ var DataPanel = React12.forwardRef(
1083
1459
  },
1084
1460
  [isDragging, detachedSize, snapThreshold]
1085
1461
  );
1086
- const handleDragEnd = React12.useCallback(() => {
1462
+ const handleDragEnd = React13.useCallback(() => {
1087
1463
  if (!isDragging) return;
1088
1464
  setIsDragging(false);
1089
1465
  document.body.style.cursor = "";
@@ -1096,7 +1472,7 @@ var DataPanel = React12.forwardRef(
1096
1472
  debouncedSave();
1097
1473
  }
1098
1474
  }, [isDragging, snapPreview, handleModeChange, handleEdgeChange, debouncedSave]);
1099
- const handlePinnedDragMove = React12.useCallback(
1475
+ const handlePinnedDragMove = React13.useCallback(
1100
1476
  (e) => {
1101
1477
  if (!isPinnedDragging) return;
1102
1478
  const currentPos = (0, import_data_panel_core.getPointerPosition)(e);
@@ -1132,7 +1508,7 @@ var DataPanel = React12.forwardRef(
1132
1508
  },
1133
1509
  [isPinnedDragging, edge, detachThreshold, pinnedSize, detachedSize, handleModeChange]
1134
1510
  );
1135
- const handlePinnedDragEnd = React12.useCallback(() => {
1511
+ const handlePinnedDragEnd = React13.useCallback(() => {
1136
1512
  if (!isPinnedDragging) return;
1137
1513
  setIsPinnedDragging(false);
1138
1514
  setPullOffset(0);
@@ -1154,7 +1530,7 @@ var DataPanel = React12.forwardRef(
1154
1530
  document.body.style.cursor = (0, import_data_panel_core.getResizeCursor)(handle);
1155
1531
  document.body.style.userSelect = "none";
1156
1532
  };
1157
- const handleResizeMove = React12.useCallback(
1533
+ const handleResizeMove = React13.useCallback(
1158
1534
  (e) => {
1159
1535
  if (!isResizing || !resizeStateRef.current.handle) return;
1160
1536
  const currentPos = (0, import_data_panel_core.getPointerPosition)(e);
@@ -1192,7 +1568,7 @@ var DataPanel = React12.forwardRef(
1192
1568
  },
1193
1569
  [isResizing, mode, edge, resolvedConstraints]
1194
1570
  );
1195
- const handleResizeEnd = React12.useCallback(() => {
1571
+ const handleResizeEnd = React13.useCallback(() => {
1196
1572
  if (!isResizing) return;
1197
1573
  setIsResizing(false);
1198
1574
  resizeStateRef.current.handle = null;
@@ -1200,7 +1576,7 @@ var DataPanel = React12.forwardRef(
1200
1576
  document.body.style.userSelect = "";
1201
1577
  debouncedSave();
1202
1578
  }, [isResizing, debouncedSave]);
1203
- React12.useEffect(() => {
1579
+ React13.useEffect(() => {
1204
1580
  if (isDragging) {
1205
1581
  window.addEventListener("pointermove", handleDragMove);
1206
1582
  window.addEventListener("pointerup", handleDragEnd);
@@ -1210,7 +1586,7 @@ var DataPanel = React12.forwardRef(
1210
1586
  };
1211
1587
  }
1212
1588
  }, [isDragging, handleDragMove, handleDragEnd]);
1213
- React12.useEffect(() => {
1589
+ React13.useEffect(() => {
1214
1590
  if (isPinnedDragging) {
1215
1591
  window.addEventListener("pointermove", handlePinnedDragMove);
1216
1592
  window.addEventListener("pointerup", handlePinnedDragEnd);
@@ -1220,7 +1596,7 @@ var DataPanel = React12.forwardRef(
1220
1596
  };
1221
1597
  }
1222
1598
  }, [isPinnedDragging, handlePinnedDragMove, handlePinnedDragEnd]);
1223
- React12.useEffect(() => {
1599
+ React13.useEffect(() => {
1224
1600
  if (isResizing) {
1225
1601
  window.addEventListener("pointermove", handleResizeMove);
1226
1602
  window.addEventListener("pointerup", handleResizeEnd);
@@ -1230,7 +1606,7 @@ var DataPanel = React12.forwardRef(
1230
1606
  };
1231
1607
  }
1232
1608
  }, [isResizing, handleResizeMove, handleResizeEnd]);
1233
- const panelStyles = React12.useMemo(() => {
1609
+ const panelStyles = React13.useMemo(() => {
1234
1610
  if (mode === "pinned") {
1235
1611
  const baseStyles = (0, import_data_panel_core.getPinnedPositionStyles)(edge, pinnedSize);
1236
1612
  if (pullOffset > 0) {
@@ -1283,9 +1659,10 @@ var DataPanel = React12.forwardRef(
1283
1659
  className
1284
1660
  ),
1285
1661
  style: panelStyles,
1286
- role: mode === "detached" ? "dialog" : void 0,
1662
+ role: mode === "detached" ? "dialog" : "complementary",
1287
1663
  "aria-modal": mode === "detached" ? "true" : void 0,
1288
- "aria-labelledby": title ? "data-panel-title" : void 0,
1664
+ "aria-labelledby": title ? titleId : void 0,
1665
+ "aria-label": !title ? "Side panel" : void 0,
1289
1666
  onPointerDown: handleDragStart,
1290
1667
  children: [
1291
1668
  /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
@@ -1293,6 +1670,7 @@ var DataPanel = React12.forwardRef(
1293
1670
  {
1294
1671
  title,
1295
1672
  subtitle,
1673
+ titleId,
1296
1674
  mode,
1297
1675
  edge,
1298
1676
  isExpanded,
@@ -1316,80 +1694,31 @@ var DataPanel = React12.forwardRef(
1316
1694
  footerActions
1317
1695
  }
1318
1696
  ),
1319
- !disableResize && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_jsx_runtime12.Fragment, { children: mode === "detached" ? /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(import_jsx_runtime12.Fragment, { children: [
1320
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
1321
- "div",
1322
- {
1323
- className: "resize-handle resize-handle--horizontal resize-handle--top",
1324
- onPointerDown: (e) => handleResizeStart(e, "top")
1325
- }
1326
- ),
1327
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
1328
- "div",
1329
- {
1330
- className: "resize-handle resize-handle--horizontal resize-handle--bottom",
1331
- onPointerDown: (e) => handleResizeStart(e, "bottom")
1332
- }
1333
- ),
1334
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
1335
- "div",
1336
- {
1337
- className: "resize-handle resize-handle--vertical resize-handle--left",
1338
- onPointerDown: (e) => handleResizeStart(e, "left")
1339
- }
1340
- ),
1341
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
1342
- "div",
1343
- {
1344
- className: "resize-handle resize-handle--vertical resize-handle--right",
1345
- onPointerDown: (e) => handleResizeStart(e, "right")
1346
- }
1347
- ),
1348
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
1349
- "div",
1350
- {
1351
- className: "resize-handle resize-handle--corner resize-handle--top-left",
1352
- style: { "--handle-radius": "6px" },
1353
- onPointerDown: (e) => handleResizeStart(e, "top-left")
1354
- }
1355
- ),
1356
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
1357
- "div",
1358
- {
1359
- className: "resize-handle resize-handle--corner resize-handle--top-right",
1360
- style: { "--handle-radius": "6px" },
1361
- onPointerDown: (e) => handleResizeStart(e, "top-right")
1362
- }
1363
- ),
1364
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
1365
- "div",
1366
- {
1367
- className: "resize-handle resize-handle--corner resize-handle--bottom-left",
1368
- style: { "--handle-radius": "6px" },
1369
- onPointerDown: (e) => handleResizeStart(e, "bottom-left")
1370
- }
1371
- ),
1372
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
1373
- "div",
1374
- {
1375
- className: "resize-handle resize-handle--corner resize-handle--bottom-right",
1376
- style: { "--handle-radius": "6px" },
1377
- onPointerDown: (e) => handleResizeStart(e, "bottom-right")
1378
- }
1379
- )
1380
- ] }) : /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
1381
- "div",
1697
+ !disableResize && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
1698
+ ResizeHandles,
1382
1699
  {
1383
- className: cn(
1384
- "resize-handle",
1385
- edge === "left" && "resize-handle--vertical resize-handle--right",
1386
- edge === "right" && "resize-handle--vertical resize-handle--left",
1387
- edge === "top" && "resize-handle--horizontal resize-handle--bottom",
1388
- edge === "bottom" && "resize-handle--horizontal resize-handle--top"
1389
- ),
1390
- onPointerDown: (e) => handleResizeStart(e, (0, import_data_panel_core.getPinnedResizeHandle)(edge))
1700
+ mode,
1701
+ edge,
1702
+ detachedSize,
1703
+ pinnedSize,
1704
+ onResizeStart: handleResizeStart,
1705
+ onSizeChange: (size) => setDetachedSize((0, import_data_panel_core.constrainSize)(size, resolvedConstraints)),
1706
+ onPinnedSizeChange: (size) => {
1707
+ if (typeof window === "undefined") {
1708
+ setPinnedSize(size);
1709
+ return;
1710
+ }
1711
+ if ((0, import_data_panel_core.isHorizontalEdge)(edge)) {
1712
+ setPinnedSize((0, import_data_panel_core.constrainPinnedWidth)(size, resolvedConstraints, window.innerWidth));
1713
+ } else {
1714
+ setPinnedSize(
1715
+ (0, import_data_panel_core.constrainPinnedHeight)(size, resolvedConstraints, window.innerHeight)
1716
+ );
1717
+ }
1718
+ },
1719
+ constraints: resolvedConstraints
1391
1720
  }
1392
- ) })
1721
+ )
1393
1722
  ]
1394
1723
  }
1395
1724
  )
@@ -1397,6 +1726,891 @@ var DataPanel = React12.forwardRef(
1397
1726
  }
1398
1727
  );
1399
1728
  DataPanel.displayName = "DataPanel";
1729
+
1730
+ // src/components/Skeleton.tsx
1731
+ var React14 = __toESM(require("react"));
1732
+ var import_class_variance_authority5 = require("class-variance-authority");
1733
+ var import_jsx_runtime13 = require("react/jsx-runtime");
1734
+ var skeletonVariants = (0, import_class_variance_authority5.cva)("rounded-md bg-muted", {
1735
+ variants: {
1736
+ variant: {
1737
+ default: "",
1738
+ text: "h-4 w-full",
1739
+ avatar: "h-12 w-12 rounded-full",
1740
+ card: "h-32 w-full",
1741
+ button: "h-10 w-24",
1742
+ title: "h-6 w-3/4"
1743
+ }
1744
+ },
1745
+ defaultVariants: {
1746
+ variant: "default"
1747
+ }
1748
+ });
1749
+ var Skeleton = React14.forwardRef(
1750
+ ({ className, variant, animation = "pulse", ...props }, ref) => {
1751
+ const animationClass = animation === "pulse" ? "animate-pulse" : animation === "shimmer" ? "animate-shimmer" : "";
1752
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
1753
+ "div",
1754
+ {
1755
+ ref,
1756
+ className: cn(skeletonVariants({ variant }), animationClass, className),
1757
+ ...props
1758
+ }
1759
+ );
1760
+ }
1761
+ );
1762
+ Skeleton.displayName = "Skeleton";
1763
+
1764
+ // src/components/Spinner.tsx
1765
+ var React15 = __toESM(require("react"));
1766
+ var import_class_variance_authority6 = require("class-variance-authority");
1767
+ var import_jsx_runtime14 = require("react/jsx-runtime");
1768
+ var spinnerVariants = (0, import_class_variance_authority6.cva)("animate-spin text-current", {
1769
+ variants: {
1770
+ size: {
1771
+ sm: "h-4 w-4",
1772
+ md: "h-6 w-6",
1773
+ lg: "h-8 w-8",
1774
+ xl: "h-12 w-12"
1775
+ }
1776
+ },
1777
+ defaultVariants: {
1778
+ size: "md"
1779
+ }
1780
+ });
1781
+ var Spinner = React15.forwardRef(
1782
+ ({ className, size, label = "Loading...", ...props }, ref) => {
1783
+ return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(
1784
+ "svg",
1785
+ {
1786
+ ref,
1787
+ className: cn(spinnerVariants({ size }), className),
1788
+ xmlns: "http://www.w3.org/2000/svg",
1789
+ fill: "none",
1790
+ viewBox: "0 0 24 24",
1791
+ "aria-label": label,
1792
+ role: "status",
1793
+ ...props,
1794
+ children: [
1795
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
1796
+ "circle",
1797
+ {
1798
+ className: "opacity-25",
1799
+ cx: "12",
1800
+ cy: "12",
1801
+ r: "10",
1802
+ stroke: "currentColor",
1803
+ strokeWidth: "4"
1804
+ }
1805
+ ),
1806
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
1807
+ "path",
1808
+ {
1809
+ className: "opacity-75",
1810
+ fill: "currentColor",
1811
+ d: "M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"
1812
+ }
1813
+ ),
1814
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("title", { children: label })
1815
+ ]
1816
+ }
1817
+ );
1818
+ }
1819
+ );
1820
+ Spinner.displayName = "Spinner";
1821
+
1822
+ // src/components/LoadingLogo.tsx
1823
+ var React16 = __toESM(require("react"));
1824
+ var import_jsx_runtime15 = require("react/jsx-runtime");
1825
+ var logoPaths = [
1826
+ "M9.902 16.6c.3.7 1.7.8 1-.6-.6-1.4-2.3-5.1-6.1-6.3-1.7-.5-3.7.4-4.3 1.6-.4.7-1.1 2.9.5 4.3.8.7 1.9 1.2 3.6 1 .4-.1.9-.5.6-.8-.5-.4-1.9-1.7-1.1-2.7.7-.8 1.4-.6 2-.3.4.2 2.1 1 3.7 3.6l.1.2z",
1827
+ "M14.602 16.6c-.3.7-1.7.8-1-.6.6-1.4 2.3-5.1 6.1-6.3 1.7-.5 3.7.4 4.3 1.6.4.7 1.1 2.9-.5 4.3-.8.7-1.9 1.2-3.6 1-.4-.1-.9-.5-.6-.8.5-.4 1.9-1.7 1.1-2.7-.7-.8-1.4-.6-2-.3-.4.2-2.1 1-3.7 3.6l-.1.2z",
1828
+ "M12.202 0c.7 1.3 2.1 3.2 2.3 5.3.1 1.5-.8 8.3-2.3 11.7-1.5-3.5-2.4-10.3-2.3-11.7.2-2.1 1.6-4 2.3-5.3z",
1829
+ "M9.002 18.1c-.8 0-.7.8-.1.8h6.7c.7 0 .7-.8-.1-.8h-6.5z",
1830
+ "M13.802 24.8c-1.1-1.1-.9-4.6-.9-4.6h-1.3s.2 3.6-.9 4.6h3.1z",
1831
+ "M14.202 22.5c-.1-.3-.5-1.4-.6-2.3 0-.2.6-.5.7.1.1.6.4 1.6.6 1.9.3.5-.4.9-.7.3z",
1832
+ "M10.302 22.5c.1-.3.5-1.4.6-2.2 0-.3-.5-.6-.7 0-.1.6-.4 1.6-.6 1.9-.3.5.4.9.7.3z"
1833
+ ];
1834
+ var colors = {
1835
+ dark: {
1836
+ fill: "#C50F22",
1837
+ fillLoading: "#ffffff",
1838
+ stroke: "#C50F22"
1839
+ },
1840
+ light: {
1841
+ fill: "#ffffff",
1842
+ fillLoading: "#C50F22",
1843
+ stroke: "#ffffff"
1844
+ }
1845
+ };
1846
+ var LoadingLogo = React16.forwardRef(
1847
+ ({ width = 40, height = 40, loading = false, variant = "dark", className, ...props }, ref) => {
1848
+ const currentColors = colors[variant];
1849
+ const fillColor = loading ? currentColors.fillLoading : currentColors.fill;
1850
+ const strokeColor = currentColors.stroke;
1851
+ return /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(
1852
+ "div",
1853
+ {
1854
+ ref,
1855
+ className: cn("inline-flex items-center justify-center", className),
1856
+ style: { width: `${width}px`, height: `${height}px` },
1857
+ role: loading ? "status" : "img",
1858
+ "aria-label": loading ? "Loading" : "Classic Homes",
1859
+ ...props,
1860
+ children: [
1861
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(
1862
+ "svg",
1863
+ {
1864
+ width: "100%",
1865
+ height: "100%",
1866
+ viewBox: "-1 0 26 25",
1867
+ fill: "none",
1868
+ xmlns: "http://www.w3.org/2000/svg",
1869
+ "aria-hidden": "true",
1870
+ children: [
1871
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("g", { fill: fillColor, className: "transition-all duration-300", children: logoPaths.map((path, i) => /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("path", { d: path }, i)) }),
1872
+ loading && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
1873
+ "g",
1874
+ {
1875
+ fill: "none",
1876
+ stroke: strokeColor,
1877
+ strokeWidth: "0.5",
1878
+ strokeDasharray: "10 4",
1879
+ className: "animate-trace",
1880
+ children: logoPaths.map((path, i) => /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("path", { d: path }, i))
1881
+ }
1882
+ )
1883
+ ]
1884
+ }
1885
+ ),
1886
+ loading && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: "sr-only", children: "Loading" })
1887
+ ]
1888
+ }
1889
+ );
1890
+ }
1891
+ );
1892
+ LoadingLogo.displayName = "LoadingLogo";
1893
+
1894
+ // src/components/ProgressBar.tsx
1895
+ var React17 = __toESM(require("react"));
1896
+ var import_class_variance_authority7 = require("class-variance-authority");
1897
+ var import_jsx_runtime16 = require("react/jsx-runtime");
1898
+ var progressBarVariants = (0, import_class_variance_authority7.cva)("w-full overflow-hidden rounded-full bg-muted", {
1899
+ variants: {
1900
+ size: {
1901
+ sm: "h-1",
1902
+ md: "h-2",
1903
+ lg: "h-3"
1904
+ }
1905
+ },
1906
+ defaultVariants: {
1907
+ size: "md"
1908
+ }
1909
+ });
1910
+ var progressFillVariants = (0, import_class_variance_authority7.cva)("h-full rounded-full transition-all duration-300", {
1911
+ variants: {
1912
+ variant: {
1913
+ default: "bg-primary",
1914
+ success: "bg-success",
1915
+ warning: "bg-warning",
1916
+ destructive: "bg-destructive"
1917
+ }
1918
+ },
1919
+ defaultVariants: {
1920
+ variant: "default"
1921
+ }
1922
+ });
1923
+ var ProgressBar = React17.forwardRef(
1924
+ ({ className, size, variant, value, max = 100, label = "Progress", showValue = false, ...props }, ref) => {
1925
+ const isIndeterminate = value === void 0;
1926
+ const clampedValue = isIndeterminate ? 0 : Math.min(Math.max(0, value), max);
1927
+ const percentage = isIndeterminate ? 0 : Math.round(clampedValue / max * 100);
1928
+ return /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(
1929
+ "div",
1930
+ {
1931
+ ref,
1932
+ role: "progressbar",
1933
+ "aria-label": label,
1934
+ "aria-valuenow": isIndeterminate ? void 0 : clampedValue,
1935
+ "aria-valuemin": 0,
1936
+ "aria-valuemax": max,
1937
+ className: cn("flex items-center gap-2", className),
1938
+ ...props,
1939
+ children: [
1940
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("div", { className: cn(progressBarVariants({ size })), children: /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
1941
+ "div",
1942
+ {
1943
+ className: cn(
1944
+ progressFillVariants({ variant }),
1945
+ isIndeterminate && "w-1/4 animate-progress-indeterminate"
1946
+ ),
1947
+ style: isIndeterminate ? void 0 : { width: `${percentage}%` }
1948
+ }
1949
+ ) }),
1950
+ showValue && !isIndeterminate && /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("span", { className: "text-sm tabular-nums text-muted-foreground", children: [
1951
+ percentage,
1952
+ "%"
1953
+ ] })
1954
+ ]
1955
+ }
1956
+ );
1957
+ }
1958
+ );
1959
+ ProgressBar.displayName = "ProgressBar";
1960
+
1961
+ // src/components/LoadingOverlay.tsx
1962
+ var React18 = __toESM(require("react"));
1963
+ var import_jsx_runtime17 = require("react/jsx-runtime");
1964
+ var spinnerSizeMap = { sm: "sm", md: "md", lg: "lg", xl: "xl" };
1965
+ var logoSizeMap = { sm: 32, md: 40, lg: 56, xl: 80 };
1966
+ var LoadingOverlay = React18.forwardRef(
1967
+ ({
1968
+ loading = true,
1969
+ variant = "spinner",
1970
+ mode = "overlay",
1971
+ size = "md",
1972
+ message,
1973
+ progress,
1974
+ blur = true,
1975
+ opaque = false,
1976
+ delay = 0,
1977
+ className,
1978
+ children,
1979
+ ...props
1980
+ }, ref) => {
1981
+ const [visible, setVisible] = React18.useState(delay === 0 && loading);
1982
+ React18.useEffect(() => {
1983
+ if (!loading) {
1984
+ setVisible(false);
1985
+ return;
1986
+ }
1987
+ if (delay === 0) {
1988
+ setVisible(true);
1989
+ return;
1990
+ }
1991
+ const timer = setTimeout(() => setVisible(true), delay);
1992
+ return () => clearTimeout(timer);
1993
+ }, [loading, delay]);
1994
+ if (!visible) {
1995
+ return mode === "inline" ? null : /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_jsx_runtime17.Fragment, { children });
1996
+ }
1997
+ const indicator = /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(import_jsx_runtime17.Fragment, { children: [
1998
+ variant === "spinner" && /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(Spinner, { size: spinnerSizeMap[size] }),
1999
+ variant === "logo" && /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(LoadingLogo, { loading: true, width: logoSizeMap[size], height: logoSizeMap[size] }),
2000
+ variant === "progress" && /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(ProgressBar, { value: progress, className: "w-48 max-w-full" }),
2001
+ variant === "skeleton" && /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { className: "w-full space-y-3", children: [
2002
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(Skeleton, { variant: "title" }),
2003
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(Skeleton, { variant: "text" }),
2004
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(Skeleton, { variant: "text" }),
2005
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(Skeleton, { variant: "text", className: "w-2/3" })
2006
+ ] }),
2007
+ message && /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("p", { className: "mt-2 text-sm text-muted-foreground animate-pulse-subtle", children: message }),
2008
+ !message && /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("span", { className: "sr-only", children: "Loading" })
2009
+ ] });
2010
+ if (mode === "inline") {
2011
+ return /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
2012
+ "div",
2013
+ {
2014
+ ref,
2015
+ role: "status",
2016
+ "aria-live": "polite",
2017
+ className: cn("inline-flex items-center gap-2", className),
2018
+ ...props,
2019
+ children: indicator
2020
+ }
2021
+ );
2022
+ }
2023
+ const bgClass = opaque ? "bg-background" : mode === "fullscreen" ? "bg-background/80" : "bg-background/60";
2024
+ const positionClass = mode === "fullscreen" ? "fixed inset-0 z-50" : "absolute inset-0 z-10";
2025
+ return /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(import_jsx_runtime17.Fragment, { children: [
2026
+ children,
2027
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
2028
+ "div",
2029
+ {
2030
+ ref,
2031
+ role: "status",
2032
+ "aria-live": "polite",
2033
+ className: cn(
2034
+ positionClass,
2035
+ bgClass,
2036
+ blur && "backdrop-blur-sm",
2037
+ "flex flex-col items-center justify-center",
2038
+ className
2039
+ ),
2040
+ ...props,
2041
+ children: indicator
2042
+ }
2043
+ )
2044
+ ] });
2045
+ }
2046
+ );
2047
+ LoadingOverlay.displayName = "LoadingOverlay";
2048
+
2049
+ // src/components/LoadingPage.tsx
2050
+ var React19 = __toESM(require("react"));
2051
+ var import_jsx_runtime18 = require("react/jsx-runtime");
2052
+ var LoadingPage = React19.forwardRef(
2053
+ ({
2054
+ loading = true,
2055
+ message,
2056
+ progress,
2057
+ showLogo = true,
2058
+ logoVariant = "dark",
2059
+ delay = 200,
2060
+ className,
2061
+ ...props
2062
+ }, ref) => {
2063
+ const [visible, setVisible] = React19.useState(delay === 0 && loading);
2064
+ React19.useEffect(() => {
2065
+ if (!loading) {
2066
+ setVisible(false);
2067
+ return;
2068
+ }
2069
+ if (delay === 0) {
2070
+ setVisible(true);
2071
+ return;
2072
+ }
2073
+ const timer = setTimeout(() => setVisible(true), delay);
2074
+ return () => clearTimeout(timer);
2075
+ }, [loading, delay]);
2076
+ if (!visible) return null;
2077
+ return /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(
2078
+ "div",
2079
+ {
2080
+ ref,
2081
+ role: "status",
2082
+ "aria-live": "polite",
2083
+ className: cn(
2084
+ "fixed inset-0 z-50 flex flex-col items-center justify-center bg-background",
2085
+ className
2086
+ ),
2087
+ ...props,
2088
+ children: [
2089
+ showLogo ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(LoadingLogo, { loading: true, width: 64, height: 64, variant: logoVariant }) : /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(Spinner, { size: "xl" }),
2090
+ progress !== void 0 && /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(ProgressBar, { value: progress, className: "mt-6 w-48 max-w-full" }),
2091
+ message && /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("p", { className: "mt-4 text-sm text-muted-foreground animate-pulse-subtle", children: message }),
2092
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("span", { className: "sr-only", children: [
2093
+ message || "Loading",
2094
+ progress !== void 0 && `, ${Math.round(progress)}% complete`
2095
+ ] })
2096
+ ]
2097
+ }
2098
+ );
2099
+ }
2100
+ );
2101
+ LoadingPage.displayName = "LoadingPage";
2102
+
2103
+ // src/components/LoadingSection.tsx
2104
+ var React20 = __toESM(require("react"));
2105
+ var import_jsx_runtime19 = require("react/jsx-runtime");
2106
+ var LoadingSection = React20.forwardRef(
2107
+ ({
2108
+ loading = false,
2109
+ variant = "spinner",
2110
+ message,
2111
+ progress,
2112
+ minHeight = "12rem",
2113
+ blur = true,
2114
+ delay = 0,
2115
+ className,
2116
+ children,
2117
+ ...props
2118
+ }, ref) => {
2119
+ return /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(
2120
+ "div",
2121
+ {
2122
+ ref,
2123
+ className: cn("relative", className),
2124
+ style: { minHeight },
2125
+ "aria-busy": loading,
2126
+ ...props,
2127
+ children: [
2128
+ children,
2129
+ loading && /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
2130
+ LoadingOverlay,
2131
+ {
2132
+ mode: "overlay",
2133
+ variant,
2134
+ message,
2135
+ progress,
2136
+ blur,
2137
+ delay
2138
+ }
2139
+ )
2140
+ ]
2141
+ }
2142
+ );
2143
+ }
2144
+ );
2145
+ LoadingSection.displayName = "LoadingSection";
2146
+
2147
+ // src/components/LiveRegion.tsx
2148
+ var React21 = __toESM(require("react"));
2149
+ var import_jsx_runtime20 = require("react/jsx-runtime");
2150
+ var LiveRegion = React21.forwardRef(
2151
+ ({ message, politeness = "polite", atomic = true, visuallyHidden = true, className, children }, ref) => {
2152
+ return /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
2153
+ "div",
2154
+ {
2155
+ ref,
2156
+ role: "status",
2157
+ "aria-live": politeness,
2158
+ "aria-atomic": atomic,
2159
+ className: cn(
2160
+ visuallyHidden && "sr-only absolute h-px w-px overflow-hidden whitespace-nowrap border-0 p-0",
2161
+ className
2162
+ ),
2163
+ children: message || children
2164
+ }
2165
+ );
2166
+ }
2167
+ );
2168
+ LiveRegion.displayName = "LiveRegion";
2169
+
2170
+ // src/components/SkipLink.tsx
2171
+ var React22 = __toESM(require("react"));
2172
+ var import_jsx_runtime21 = require("react/jsx-runtime");
2173
+ var SkipLink = React22.forwardRef(
2174
+ ({ targetId, className, children = "Skip to main content", ...props }, ref) => {
2175
+ const handleClick = (e) => {
2176
+ e.preventDefault();
2177
+ const target = document.getElementById(targetId);
2178
+ if (target) {
2179
+ if (target.tabIndex === -1) {
2180
+ target.setAttribute("tabindex", "-1");
2181
+ }
2182
+ target.focus();
2183
+ target.scrollIntoView();
2184
+ } else if (process.env.NODE_ENV !== "production") {
2185
+ console.warn(
2186
+ `SkipLink: Target element with id="${targetId}" not found. Ensure the target element exists and has the correct id attribute.`
2187
+ );
2188
+ }
2189
+ props.onClick?.(e);
2190
+ };
2191
+ return /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
2192
+ "a",
2193
+ {
2194
+ ref,
2195
+ href: `#${targetId}`,
2196
+ className: cn("skip-link", className),
2197
+ onClick: handleClick,
2198
+ ...props,
2199
+ children
2200
+ }
2201
+ );
2202
+ }
2203
+ );
2204
+ SkipLink.displayName = "SkipLink";
2205
+
2206
+ // src/hooks/useFocusTrap.ts
2207
+ var import_react = require("react");
2208
+ var FOCUSABLE_SELECTOR = [
2209
+ "a[href]",
2210
+ "area[href]",
2211
+ 'input:not([disabled]):not([type="hidden"])',
2212
+ "select:not([disabled])",
2213
+ "textarea:not([disabled])",
2214
+ "button:not([disabled])",
2215
+ "iframe",
2216
+ "object",
2217
+ "embed",
2218
+ "[contenteditable]",
2219
+ '[tabindex]:not([tabindex="-1"])'
2220
+ ].join(",");
2221
+ function getFocusableElements(container) {
2222
+ const elements = container.querySelectorAll(FOCUSABLE_SELECTOR);
2223
+ return Array.from(elements).filter(
2224
+ (el) => el.offsetParent !== null && getComputedStyle(el).visibility !== "hidden"
2225
+ );
2226
+ }
2227
+ function useFocusTrap(options = {}) {
2228
+ const { active = true, returnFocusTo, initialFocus, preventScroll = false } = options;
2229
+ const containerRef = (0, import_react.useRef)(null);
2230
+ const previousActiveElementRef = (0, import_react.useRef)(null);
2231
+ const handleKeyDown = (0, import_react.useCallback)(
2232
+ (event) => {
2233
+ if (!active || event.key !== "Tab") return;
2234
+ const container = containerRef.current;
2235
+ if (!container) return;
2236
+ const focusableElements = getFocusableElements(container);
2237
+ if (focusableElements.length === 0) return;
2238
+ const firstElement = focusableElements[0];
2239
+ const lastElement = focusableElements[focusableElements.length - 1];
2240
+ if (event.shiftKey) {
2241
+ if (document.activeElement === firstElement) {
2242
+ event.preventDefault();
2243
+ lastElement.focus({ preventScroll });
2244
+ }
2245
+ } else {
2246
+ if (document.activeElement === lastElement) {
2247
+ event.preventDefault();
2248
+ firstElement.focus({ preventScroll });
2249
+ }
2250
+ }
2251
+ },
2252
+ [active, preventScroll]
2253
+ );
2254
+ (0, import_react.useEffect)(() => {
2255
+ if (!active) return;
2256
+ const container = containerRef.current;
2257
+ if (!container) return;
2258
+ previousActiveElementRef.current = document.activeElement;
2259
+ const focusableElements = getFocusableElements(container);
2260
+ if (focusableElements.length > 0) {
2261
+ let elementToFocus = null;
2262
+ if (initialFocus) {
2263
+ elementToFocus = container.querySelector(initialFocus);
2264
+ }
2265
+ if (!elementToFocus) {
2266
+ elementToFocus = focusableElements[0];
2267
+ }
2268
+ requestAnimationFrame(() => {
2269
+ elementToFocus?.focus({ preventScroll });
2270
+ });
2271
+ }
2272
+ document.addEventListener("keydown", handleKeyDown);
2273
+ return () => {
2274
+ document.removeEventListener("keydown", handleKeyDown);
2275
+ const elementToReturn = returnFocusTo ?? previousActiveElementRef.current;
2276
+ if (elementToReturn && typeof elementToReturn.focus === "function") {
2277
+ elementToReturn.focus({ preventScroll });
2278
+ }
2279
+ };
2280
+ }, [active, initialFocus, returnFocusTo, handleKeyDown, preventScroll]);
2281
+ return containerRef;
2282
+ }
2283
+
2284
+ // src/hooks/useFocusRef.ts
2285
+ var import_react2 = require("react");
2286
+ function useFocusRef(options = {}) {
2287
+ const { focusOnMount = false, preventScroll = false, delay = 0 } = options;
2288
+ const ref = (0, import_react2.useRef)(null);
2289
+ (0, import_react2.useEffect)(() => {
2290
+ if (!focusOnMount) return;
2291
+ const focus = () => {
2292
+ if (ref.current && typeof ref.current.focus === "function") {
2293
+ ref.current.focus({ preventScroll });
2294
+ }
2295
+ };
2296
+ if (delay > 0) {
2297
+ const timer = setTimeout(focus, delay);
2298
+ return () => clearTimeout(timer);
2299
+ } else {
2300
+ const frame = requestAnimationFrame(focus);
2301
+ return () => cancelAnimationFrame(frame);
2302
+ }
2303
+ }, [focusOnMount, preventScroll, delay]);
2304
+ return ref;
2305
+ }
2306
+
2307
+ // src/hooks/useReducedMotion.ts
2308
+ var import_react3 = require("react");
2309
+ function useReducedMotion() {
2310
+ const [reducedMotion, setReducedMotion] = (0, import_react3.useState)(false);
2311
+ (0, import_react3.useEffect)(() => {
2312
+ const mediaQuery = window.matchMedia("(prefers-reduced-motion: reduce)");
2313
+ setReducedMotion(mediaQuery.matches);
2314
+ const handleChange = (event) => {
2315
+ setReducedMotion(event.matches);
2316
+ };
2317
+ mediaQuery.addEventListener("change", handleChange);
2318
+ return () => {
2319
+ mediaQuery.removeEventListener("change", handleChange);
2320
+ };
2321
+ }, []);
2322
+ return reducedMotion;
2323
+ }
2324
+
2325
+ // src/hooks/useArrowNavigation.ts
2326
+ var import_react4 = require("react");
2327
+ var DEFAULT_SELECTOR = '[role="menuitem"], [role="option"], [role="tab"], button, a, [tabindex]:not([tabindex="-1"])';
2328
+ function useArrowNavigation(options = {}) {
2329
+ const {
2330
+ orientation = "vertical",
2331
+ loop = true,
2332
+ itemSelector = DEFAULT_SELECTOR,
2333
+ onFocusChange
2334
+ } = options;
2335
+ const containerRef = (0, import_react4.useRef)(null);
2336
+ const focusedIndexRef = (0, import_react4.useRef)(-1);
2337
+ const getFocusableItems = (0, import_react4.useCallback)(() => {
2338
+ if (!containerRef.current) return [];
2339
+ return Array.from(containerRef.current.querySelectorAll(itemSelector));
2340
+ }, [itemSelector]);
2341
+ const focusItem = (0, import_react4.useCallback)(
2342
+ (index) => {
2343
+ const items = getFocusableItems();
2344
+ if (items.length === 0) return;
2345
+ let targetIndex = index;
2346
+ if (loop) {
2347
+ if (targetIndex < 0) targetIndex = items.length - 1;
2348
+ if (targetIndex >= items.length) targetIndex = 0;
2349
+ } else {
2350
+ targetIndex = Math.max(0, Math.min(targetIndex, items.length - 1));
2351
+ }
2352
+ const element = items[targetIndex];
2353
+ if (element) {
2354
+ element.focus();
2355
+ focusedIndexRef.current = targetIndex;
2356
+ onFocusChange?.(targetIndex, element);
2357
+ }
2358
+ },
2359
+ [getFocusableItems, loop, onFocusChange]
2360
+ );
2361
+ const handleKeyDown = (0, import_react4.useCallback)(
2362
+ (event) => {
2363
+ const items = getFocusableItems();
2364
+ if (items.length === 0) return;
2365
+ const currentIndex = focusedIndexRef.current;
2366
+ let handled = false;
2367
+ switch (event.key) {
2368
+ case "ArrowUp":
2369
+ if (orientation === "vertical" || orientation === "both") {
2370
+ focusItem(currentIndex - 1);
2371
+ handled = true;
2372
+ }
2373
+ break;
2374
+ case "ArrowDown":
2375
+ if (orientation === "vertical" || orientation === "both") {
2376
+ focusItem(currentIndex + 1);
2377
+ handled = true;
2378
+ }
2379
+ break;
2380
+ case "ArrowLeft":
2381
+ if (orientation === "horizontal" || orientation === "both") {
2382
+ focusItem(currentIndex - 1);
2383
+ handled = true;
2384
+ }
2385
+ break;
2386
+ case "ArrowRight":
2387
+ if (orientation === "horizontal" || orientation === "both") {
2388
+ focusItem(currentIndex + 1);
2389
+ handled = true;
2390
+ }
2391
+ break;
2392
+ case "Home":
2393
+ focusItem(0);
2394
+ handled = true;
2395
+ break;
2396
+ case "End":
2397
+ focusItem(items.length - 1);
2398
+ handled = true;
2399
+ break;
2400
+ }
2401
+ if (handled) {
2402
+ event.preventDefault();
2403
+ event.stopPropagation();
2404
+ }
2405
+ },
2406
+ [orientation, focusItem, getFocusableItems]
2407
+ );
2408
+ const handleFocus = (0, import_react4.useCallback)(
2409
+ (event) => {
2410
+ const items = getFocusableItems();
2411
+ const index = items.indexOf(event.target);
2412
+ if (index !== -1) {
2413
+ focusedIndexRef.current = index;
2414
+ }
2415
+ },
2416
+ [getFocusableItems]
2417
+ );
2418
+ const containerProps = {
2419
+ ref: (node) => {
2420
+ containerRef.current = node;
2421
+ },
2422
+ onKeyDown: handleKeyDown,
2423
+ onFocus: handleFocus
2424
+ };
2425
+ const getItemProps = (0, import_react4.useCallback)(
2426
+ (index) => ({
2427
+ tabIndex: index === 0 ? 0 : -1,
2428
+ onFocus: () => {
2429
+ focusedIndexRef.current = index;
2430
+ }
2431
+ }),
2432
+ []
2433
+ );
2434
+ return {
2435
+ containerProps,
2436
+ getItemProps,
2437
+ focusItem,
2438
+ getFocusedIndex: () => focusedIndexRef.current
2439
+ };
2440
+ }
2441
+
2442
+ // src/hooks/useRovingTabindex.ts
2443
+ var import_react5 = require("react");
2444
+ var DEFAULT_SELECTOR2 = "[data-roving-tabindex-item]";
2445
+ function useRovingTabindex(options = {}) {
2446
+ const { loop = true, itemSelector = DEFAULT_SELECTOR2, initialIndex = 0, onIndexChange } = options;
2447
+ const [activeIndex, setActiveIndex] = (0, import_react5.useState)(initialIndex);
2448
+ const containerRef = (0, import_react5.useRef)(null);
2449
+ const itemsRef = (0, import_react5.useRef)(/* @__PURE__ */ new Map());
2450
+ const getItems = (0, import_react5.useCallback)(() => {
2451
+ if (!containerRef.current) {
2452
+ return Array.from(itemsRef.current.values());
2453
+ }
2454
+ return Array.from(containerRef.current.querySelectorAll(itemSelector));
2455
+ }, [itemSelector]);
2456
+ const focusIndex = (0, import_react5.useCallback)(
2457
+ (index) => {
2458
+ const items = getItems();
2459
+ if (items.length === 0) return;
2460
+ let targetIndex = index;
2461
+ if (loop) {
2462
+ if (targetIndex < 0) targetIndex = items.length - 1;
2463
+ if (targetIndex >= items.length) targetIndex = 0;
2464
+ } else {
2465
+ targetIndex = Math.max(0, Math.min(targetIndex, items.length - 1));
2466
+ }
2467
+ setActiveIndex(targetIndex);
2468
+ onIndexChange?.(targetIndex);
2469
+ const element = items[targetIndex];
2470
+ if (element) {
2471
+ element.focus();
2472
+ }
2473
+ },
2474
+ [getItems, loop, onIndexChange]
2475
+ );
2476
+ const handleKeyDown = (0, import_react5.useCallback)(
2477
+ (event) => {
2478
+ const items = getItems();
2479
+ if (items.length === 0) return;
2480
+ let handled = false;
2481
+ switch (event.key) {
2482
+ case "ArrowUp":
2483
+ case "ArrowLeft":
2484
+ focusIndex(activeIndex - 1);
2485
+ handled = true;
2486
+ break;
2487
+ case "ArrowDown":
2488
+ case "ArrowRight":
2489
+ focusIndex(activeIndex + 1);
2490
+ handled = true;
2491
+ break;
2492
+ case "Home":
2493
+ focusIndex(0);
2494
+ handled = true;
2495
+ break;
2496
+ case "End":
2497
+ focusIndex(items.length - 1);
2498
+ handled = true;
2499
+ break;
2500
+ }
2501
+ if (handled) {
2502
+ event.preventDefault();
2503
+ event.stopPropagation();
2504
+ }
2505
+ },
2506
+ [activeIndex, focusIndex, getItems]
2507
+ );
2508
+ const containerProps = {
2509
+ ref: (node) => {
2510
+ containerRef.current = node;
2511
+ },
2512
+ onKeyDown: handleKeyDown
2513
+ };
2514
+ const getItemProps = (0, import_react5.useCallback)(
2515
+ (index) => ({
2516
+ "data-roving-tabindex-item": true,
2517
+ tabIndex: index === activeIndex ? 0 : -1,
2518
+ ref: (node) => {
2519
+ if (node) {
2520
+ itemsRef.current.set(index, node);
2521
+ } else {
2522
+ itemsRef.current.delete(index);
2523
+ }
2524
+ },
2525
+ onClick: () => {
2526
+ setActiveIndex(index);
2527
+ onIndexChange?.(index);
2528
+ },
2529
+ onFocus: () => {
2530
+ if (activeIndex !== index) {
2531
+ setActiveIndex(index);
2532
+ onIndexChange?.(index);
2533
+ }
2534
+ }
2535
+ }),
2536
+ [activeIndex, onIndexChange]
2537
+ );
2538
+ return {
2539
+ containerProps,
2540
+ getItemProps,
2541
+ activeIndex,
2542
+ setActiveIndex: focusIndex,
2543
+ focusNext: () => focusIndex(activeIndex + 1),
2544
+ focusPrevious: () => focusIndex(activeIndex - 1),
2545
+ focusFirst: () => focusIndex(0),
2546
+ focusLast: () => focusIndex(getItems().length - 1)
2547
+ };
2548
+ }
2549
+
2550
+ // src/hooks/useEscapeKey.ts
2551
+ var React23 = __toESM(require("react"));
2552
+ function useEscapeKey(callback, options) {
2553
+ const { enabled = true, preventDefault = true, stopPropagation = false } = options ?? {};
2554
+ const callbackRef = React23.useRef(callback);
2555
+ React23.useEffect(() => {
2556
+ callbackRef.current = callback;
2557
+ }, [callback]);
2558
+ React23.useEffect(() => {
2559
+ if (!enabled) return;
2560
+ const handleKeyDown = (event) => {
2561
+ if (event.key === "Escape") {
2562
+ if (preventDefault) {
2563
+ event.preventDefault();
2564
+ }
2565
+ if (stopPropagation) {
2566
+ event.stopPropagation();
2567
+ }
2568
+ callbackRef.current(event);
2569
+ }
2570
+ };
2571
+ document.addEventListener("keydown", handleKeyDown);
2572
+ return () => {
2573
+ document.removeEventListener("keydown", handleKeyDown);
2574
+ };
2575
+ }, [enabled, preventDefault, stopPropagation]);
2576
+ }
2577
+
2578
+ // src/hooks/useClickOutside.ts
2579
+ var React24 = __toESM(require("react"));
2580
+ function useClickOutside(ref, callback, options) {
2581
+ const {
2582
+ enabled = true,
2583
+ events = ["mousedown", "touchstart"],
2584
+ ignoreElements = []
2585
+ } = options ?? {};
2586
+ const callbackRef = React24.useRef(callback);
2587
+ React24.useEffect(() => {
2588
+ callbackRef.current = callback;
2589
+ }, [callback]);
2590
+ React24.useEffect(() => {
2591
+ if (!enabled) return;
2592
+ const handleEvent = (event) => {
2593
+ const target = event.target;
2594
+ if (ref.current?.contains(target)) {
2595
+ return;
2596
+ }
2597
+ for (const ignoreRef of ignoreElements) {
2598
+ if (ignoreRef.current?.contains(target)) {
2599
+ return;
2600
+ }
2601
+ }
2602
+ callbackRef.current(event);
2603
+ };
2604
+ events.forEach((eventType) => {
2605
+ document.addEventListener(eventType, handleEvent);
2606
+ });
2607
+ return () => {
2608
+ events.forEach((eventType) => {
2609
+ document.removeEventListener(eventType, handleEvent);
2610
+ });
2611
+ };
2612
+ }, [enabled, events, ignoreElements, ref]);
2613
+ }
1400
2614
  // Annotate the CommonJS export names for ESM import in node:
1401
2615
  0 && (module.exports = {
1402
2616
  Avatar,
@@ -1427,9 +2641,18 @@ DataPanel.displayName = "DataPanel";
1427
2641
  DialogTrigger,
1428
2642
  Input,
1429
2643
  Label,
2644
+ LiveRegion,
2645
+ LoadingLogo,
2646
+ LoadingOverlay,
2647
+ LoadingPage,
2648
+ LoadingSection,
1430
2649
  PageHeader,
1431
2650
  PasswordInput,
2651
+ ProgressBar,
1432
2652
  Separator,
2653
+ Skeleton,
2654
+ SkipLink,
2655
+ Spinner,
1433
2656
  Switch,
1434
2657
  badgeVariants,
1435
2658
  buttonVariants,
@@ -1438,5 +2661,16 @@ DataPanel.displayName = "DataPanel";
1438
2661
  pageHeaderContainerVariants,
1439
2662
  pageHeaderSubtitleVariants,
1440
2663
  pageHeaderTitleVariants,
1441
- useDataPanel
2664
+ progressBarVariants,
2665
+ skeletonVariants,
2666
+ spinnerVariants,
2667
+ useAnnounce,
2668
+ useArrowNavigation,
2669
+ useClickOutside,
2670
+ useDataPanel,
2671
+ useEscapeKey,
2672
+ useFocusRef,
2673
+ useFocusTrap,
2674
+ useReducedMotion,
2675
+ useRovingTabindex
1442
2676
  });