@geomak/ui 5.0.3 → 5.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -4,6 +4,7 @@ var chunk255PCZIW_cjs = require('./chunk-255PCZIW.cjs');
4
4
  var jsxRuntime = require('react/jsx-runtime');
5
5
  var React8 = require('react');
6
6
  var reactDom = require('react-dom');
7
+ var AvatarPrimitive = require('@radix-ui/react-avatar');
7
8
  var Dialog = require('@radix-ui/react-dialog');
8
9
  var framerMotion = require('framer-motion');
9
10
  var TooltipPrimitive = require('@radix-ui/react-tooltip');
@@ -36,6 +37,7 @@ function _interopNamespace(e) {
36
37
  }
37
38
 
38
39
  var React8__default = /*#__PURE__*/_interopDefault(React8);
40
+ var AvatarPrimitive__namespace = /*#__PURE__*/_interopNamespace(AvatarPrimitive);
39
41
  var Dialog__namespace = /*#__PURE__*/_interopNamespace(Dialog);
40
42
  var TooltipPrimitive__namespace = /*#__PURE__*/_interopNamespace(TooltipPrimitive);
41
43
  var TabsPrimitive__namespace = /*#__PURE__*/_interopNamespace(TabsPrimitive);
@@ -217,6 +219,417 @@ function Portal({ children, target }) {
217
219
  }, [target]);
218
220
  return resolved ? reactDom.createPortal(children, resolved) : null;
219
221
  }
222
+ var SPACING_MAP = {
223
+ "none": "0",
224
+ "xs": "1",
225
+ "sm": "2",
226
+ "md": "4",
227
+ "lg": "6",
228
+ "xl": "8",
229
+ "2xl": "12"
230
+ };
231
+ var padding = (s, axis = "p") => s == null ? "" : `${axis}-${SPACING_MAP[s]}`;
232
+ var margin = (s, axis = "m") => s == null ? "" : `${axis}-${SPACING_MAP[s]}`;
233
+ var BG_MAP = {
234
+ "none": "",
235
+ "background": "bg-background",
236
+ "surface": "bg-surface",
237
+ "surface-raised": "bg-surface-raised",
238
+ "accent": "bg-accent text-accent-fg"
239
+ };
240
+ var BORDER_MAP = {
241
+ "none": "",
242
+ "border": "border border-border",
243
+ "border-strong": "border border-border-strong",
244
+ "accent": "border border-accent",
245
+ "status-error": "border border-status-error"
246
+ };
247
+ var RADIUS_MAP = {
248
+ "none": "rounded-none",
249
+ "sm": "rounded-sm",
250
+ "md": "rounded-md",
251
+ "lg": "rounded-lg",
252
+ "xl": "rounded-xl",
253
+ "2xl": "rounded-2xl",
254
+ "full": "rounded-full"
255
+ };
256
+ var SHADOW_MAP = {
257
+ "none": "",
258
+ "sm": "shadow-sm",
259
+ "md": "shadow-md",
260
+ "lg": "shadow-lg",
261
+ "xl": "shadow-xl"
262
+ };
263
+ function Box({
264
+ as,
265
+ p,
266
+ px: px2,
267
+ py,
268
+ pt,
269
+ pr,
270
+ pb,
271
+ pl,
272
+ m,
273
+ mx,
274
+ my,
275
+ mt,
276
+ mr,
277
+ mb,
278
+ ml,
279
+ background = "none",
280
+ border = "none",
281
+ radius,
282
+ shadow = "none",
283
+ width,
284
+ height,
285
+ onClick,
286
+ className = "",
287
+ style,
288
+ children
289
+ }) {
290
+ const Element = as ?? "div";
291
+ return /* @__PURE__ */ jsxRuntime.jsx(
292
+ Element,
293
+ {
294
+ onClick,
295
+ className: [
296
+ padding(p, "p"),
297
+ padding(px2, "px"),
298
+ padding(py, "py"),
299
+ padding(pt, "pt"),
300
+ padding(pr, "pr"),
301
+ padding(pb, "pb"),
302
+ padding(pl, "pl"),
303
+ margin(m, "m"),
304
+ margin(mx, "mx"),
305
+ margin(my, "my"),
306
+ margin(mt, "mt"),
307
+ margin(mr, "mr"),
308
+ margin(mb, "mb"),
309
+ margin(ml, "ml"),
310
+ BG_MAP[background],
311
+ BORDER_MAP[border],
312
+ radius ? RADIUS_MAP[radius] : "",
313
+ SHADOW_MAP[shadow],
314
+ className
315
+ ].filter(Boolean).join(" "),
316
+ style: {
317
+ width: typeof width === "number" ? `${width}px` : width,
318
+ height: typeof height === "number" ? `${height}px` : height,
319
+ ...style
320
+ },
321
+ children
322
+ }
323
+ );
324
+ }
325
+ var GAP_MAP = {
326
+ "none": "gap-0",
327
+ "xs": "gap-1",
328
+ "sm": "gap-2",
329
+ "md": "gap-4",
330
+ "lg": "gap-6",
331
+ "xl": "gap-8",
332
+ "2xl": "gap-12"
333
+ };
334
+ var DIRECTION_CLASS = {
335
+ "row": "flex-row",
336
+ "row-reverse": "flex-row-reverse",
337
+ "col": "flex-col",
338
+ "col-reverse": "flex-col-reverse"
339
+ };
340
+ var ALIGN_CLASS = {
341
+ start: "items-start",
342
+ center: "items-center",
343
+ end: "items-end",
344
+ stretch: "items-stretch",
345
+ baseline: "items-baseline"
346
+ };
347
+ var JUSTIFY_CLASS = {
348
+ start: "justify-start",
349
+ center: "justify-center",
350
+ end: "justify-end",
351
+ between: "justify-between",
352
+ around: "justify-around",
353
+ evenly: "justify-evenly"
354
+ };
355
+ var WRAP_CLASS = {
356
+ "nowrap": "flex-nowrap",
357
+ "wrap": "flex-wrap",
358
+ "wrap-reverse": "flex-wrap-reverse"
359
+ };
360
+ function Flex({
361
+ direction = "row",
362
+ align,
363
+ justify,
364
+ wrap,
365
+ gap,
366
+ inline,
367
+ className = "",
368
+ ...boxProps
369
+ }) {
370
+ return /* @__PURE__ */ jsxRuntime.jsx(
371
+ Box,
372
+ {
373
+ ...boxProps,
374
+ className: [
375
+ inline ? "inline-flex" : "flex",
376
+ DIRECTION_CLASS[direction],
377
+ align ? ALIGN_CLASS[align] : "",
378
+ justify ? JUSTIFY_CLASS[justify] : "",
379
+ wrap ? WRAP_CLASS[wrap] : "",
380
+ gap ? GAP_MAP[gap] : "",
381
+ className
382
+ ].filter(Boolean).join(" ")
383
+ }
384
+ );
385
+ }
386
+ var GAP_MAP2 = {
387
+ "none": "gap-0",
388
+ "xs": "gap-1",
389
+ "sm": "gap-2",
390
+ "md": "gap-4",
391
+ "lg": "gap-6",
392
+ "xl": "gap-8",
393
+ "2xl": "gap-12"
394
+ };
395
+ var COL_MAP = {
396
+ 1: "grid-cols-1",
397
+ 2: "grid-cols-2",
398
+ 3: "grid-cols-3",
399
+ 4: "grid-cols-4",
400
+ 5: "grid-cols-5",
401
+ 6: "grid-cols-6",
402
+ 7: "grid-cols-7",
403
+ 8: "grid-cols-8",
404
+ 9: "grid-cols-9",
405
+ 10: "grid-cols-10",
406
+ 11: "grid-cols-11",
407
+ 12: "grid-cols-12"
408
+ };
409
+ var ROW_MAP = {
410
+ 1: "grid-rows-1",
411
+ 2: "grid-rows-2",
412
+ 3: "grid-rows-3",
413
+ 4: "grid-rows-4",
414
+ 5: "grid-rows-5",
415
+ 6: "grid-rows-6"
416
+ };
417
+ var ALIGN_CLASS2 = {
418
+ start: "items-start",
419
+ center: "items-center",
420
+ end: "items-end",
421
+ stretch: "items-stretch"
422
+ };
423
+ var JUSTIFY_CLASS2 = {
424
+ start: "justify-items-start",
425
+ center: "justify-items-center",
426
+ end: "justify-items-end",
427
+ stretch: "justify-items-stretch"
428
+ };
429
+ function Grid2({
430
+ cols,
431
+ rows,
432
+ gap,
433
+ gapX,
434
+ gapY,
435
+ align,
436
+ justify,
437
+ className = "",
438
+ style,
439
+ ...boxProps
440
+ }) {
441
+ const colClass = typeof cols === "number" ? COL_MAP[cols] ?? "" : "";
442
+ const rowClass = typeof rows === "number" ? ROW_MAP[rows] ?? "" : "";
443
+ const inlineCols = typeof cols === "string" ? cols : void 0;
444
+ const inlineRows = typeof rows === "string" ? rows : void 0;
445
+ return /* @__PURE__ */ jsxRuntime.jsx(
446
+ Box,
447
+ {
448
+ ...boxProps,
449
+ className: [
450
+ "grid",
451
+ colClass,
452
+ rowClass,
453
+ gap ? GAP_MAP2[gap] : "",
454
+ gapX ? GAP_MAP2[gapX].replace("gap-", "gap-x-") : "",
455
+ gapY ? GAP_MAP2[gapY].replace("gap-", "gap-y-") : "",
456
+ align ? ALIGN_CLASS2[align] : "",
457
+ justify ? JUSTIFY_CLASS2[justify] : "",
458
+ className
459
+ ].filter(Boolean).join(" "),
460
+ style: {
461
+ gridTemplateColumns: inlineCols,
462
+ gridTemplateRows: inlineRows,
463
+ ...style
464
+ }
465
+ }
466
+ );
467
+ }
468
+ var SIZE_PX = {
469
+ xs: 20,
470
+ sm: 28,
471
+ md: 36,
472
+ lg: 48,
473
+ xl: 64
474
+ };
475
+ var TEXT_CLASS = {
476
+ xs: "text-[10px]",
477
+ sm: "text-xs",
478
+ md: "text-sm",
479
+ lg: "text-base",
480
+ xl: "text-lg"
481
+ };
482
+ var STATUS_CLASS = {
483
+ online: "bg-status-success",
484
+ offline: "bg-foreground-muted",
485
+ away: "bg-status-warning",
486
+ busy: "bg-status-error"
487
+ };
488
+ function Avatar({
489
+ src,
490
+ alt,
491
+ fallback,
492
+ size = "md",
493
+ shape = "circle",
494
+ status,
495
+ className = ""
496
+ }) {
497
+ const px2 = SIZE_PX[size];
498
+ const initialsFallback = (() => {
499
+ if (fallback) return fallback;
500
+ if (alt) {
501
+ const parts = alt.trim().split(/\s+/).slice(0, 2);
502
+ const initials = parts.map((p) => p[0]?.toUpperCase() ?? "").join("");
503
+ if (initials) return initials;
504
+ }
505
+ return /* @__PURE__ */ jsxRuntime.jsx(PersonSilhouette, {});
506
+ })();
507
+ return /* @__PURE__ */ jsxRuntime.jsxs(
508
+ "span",
509
+ {
510
+ className: `relative inline-block flex-shrink-0 ${className}`,
511
+ style: { width: px2, height: px2 },
512
+ children: [
513
+ /* @__PURE__ */ jsxRuntime.jsxs(
514
+ AvatarPrimitive__namespace.Root,
515
+ {
516
+ className: `flex w-full h-full items-center justify-center overflow-hidden bg-surface-raised text-foreground-secondary select-none ${shape === "circle" ? "rounded-full" : "rounded-md"}`,
517
+ children: [
518
+ src && /* @__PURE__ */ jsxRuntime.jsx(
519
+ AvatarPrimitive__namespace.Image,
520
+ {
521
+ src,
522
+ alt: alt ?? "",
523
+ className: "h-full w-full object-cover"
524
+ }
525
+ ),
526
+ /* @__PURE__ */ jsxRuntime.jsx(
527
+ AvatarPrimitive__namespace.Fallback,
528
+ {
529
+ delayMs: src ? 300 : 0,
530
+ className: `flex h-full w-full items-center justify-center font-semibold ${TEXT_CLASS[size]}`,
531
+ children: initialsFallback
532
+ }
533
+ )
534
+ ]
535
+ }
536
+ ),
537
+ status && /* @__PURE__ */ jsxRuntime.jsx(
538
+ "span",
539
+ {
540
+ className: `absolute bottom-0 right-0 block rounded-full ring-2 ring-background ${STATUS_CLASS[status]}`,
541
+ style: {
542
+ width: Math.max(6, Math.round(px2 / 4)),
543
+ height: Math.max(6, Math.round(px2 / 4))
544
+ },
545
+ "aria-label": `Status: ${status}`,
546
+ role: "status"
547
+ }
548
+ )
549
+ ]
550
+ }
551
+ );
552
+ }
553
+ function PersonSilhouette() {
554
+ return /* @__PURE__ */ jsxRuntime.jsx("svg", { viewBox: "0 0 24 24", fill: "currentColor", className: "w-[60%] h-[60%]", "aria-hidden": "true", children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M12 12a4 4 0 100-8 4 4 0 000 8zM4 20a8 8 0 1116 0H4z" }) });
555
+ }
556
+ var VARIANT_CLASS = {
557
+ display: "text-3xl font-bold leading-tight tracking-tight",
558
+ h1: "text-2xl font-bold leading-tight tracking-tight",
559
+ h2: "text-xl font-semibold leading-snug tracking-tight",
560
+ h3: "text-lg font-semibold leading-snug",
561
+ h4: "text-base font-semibold leading-snug",
562
+ subtitle: "text-sm font-medium leading-snug",
563
+ body: "text-sm leading-normal",
564
+ caption: "text-xs leading-normal",
565
+ overline: "text-[10px] font-semibold leading-normal uppercase tracking-wider",
566
+ code: "text-xs leading-normal font-mono bg-surface-raised text-foreground rounded px-1 py-0.5"
567
+ };
568
+ var DEFAULT_ELEMENT = {
569
+ display: "h1",
570
+ h1: "h1",
571
+ h2: "h2",
572
+ h3: "h3",
573
+ h4: "h4",
574
+ subtitle: "p",
575
+ body: "p",
576
+ caption: "span",
577
+ overline: "span",
578
+ code: "code"
579
+ };
580
+ var COLOR_CLASS = {
581
+ "foreground": "text-foreground",
582
+ "foreground-secondary": "text-foreground-secondary",
583
+ "foreground-muted": "text-foreground-muted",
584
+ "accent": "text-accent",
585
+ "status-error": "text-status-error",
586
+ "status-warning": "text-status-warning",
587
+ "status-success": "text-status-success",
588
+ "status-info": "text-status-info",
589
+ "inherit": ""
590
+ };
591
+ var WEIGHT_CLASS = {
592
+ normal: "font-normal",
593
+ medium: "font-medium",
594
+ semibold: "font-semibold",
595
+ bold: "font-bold"
596
+ };
597
+ var ALIGN_CLASS3 = {
598
+ left: "text-left",
599
+ center: "text-center",
600
+ right: "text-right",
601
+ justify: "text-justify"
602
+ };
603
+ function Typography({
604
+ variant = "body",
605
+ as,
606
+ color = "inherit",
607
+ weight,
608
+ align,
609
+ truncate,
610
+ muted,
611
+ className = "",
612
+ style,
613
+ children
614
+ }) {
615
+ const Element = as ?? DEFAULT_ELEMENT[variant];
616
+ return /* @__PURE__ */ jsxRuntime.jsx(
617
+ Element,
618
+ {
619
+ className: [
620
+ VARIANT_CLASS[variant],
621
+ COLOR_CLASS[color],
622
+ weight ? WEIGHT_CLASS[weight] : "",
623
+ align ? ALIGN_CLASS3[align] : "",
624
+ truncate ? "truncate" : "",
625
+ muted ? "opacity-60" : "",
626
+ className
627
+ ].filter(Boolean).join(" "),
628
+ style,
629
+ children
630
+ }
631
+ );
632
+ }
220
633
  function IconButton({
221
634
  icon,
222
635
  onClick,
@@ -1137,30 +1550,53 @@ function FadingBase({
1137
1550
  )
1138
1551
  );
1139
1552
  }
1140
- function List2({ items, onItemClick, activeKey }) {
1141
- return /* @__PURE__ */ jsxRuntime.jsx("div", { role: "listbox", children: items.map((item) => (
1142
- // tabIndex + Enter/Space onKeyDown makes each option
1143
- // keyboard-activatable. Previously the items were only mouse-
1144
- // clickable — keyboard-only users couldn't select anything.
1145
- /* @__PURE__ */ jsxRuntime.jsx(
1553
+ var DENSITY_PADDING = {
1554
+ "compact": "py-1.5 px-2",
1555
+ "comfortable": "py-2.5 px-3",
1556
+ "spacious": "py-3.5 px-4"
1557
+ };
1558
+ function List2({
1559
+ items,
1560
+ onItemClick,
1561
+ activeKey,
1562
+ density = "comfortable"
1563
+ }) {
1564
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { role: "listbox", className: "flex flex-col", children: items.map((item) => {
1565
+ const isActive = activeKey === item.key;
1566
+ const isDisabled = !!item.disabled;
1567
+ return /* @__PURE__ */ jsxRuntime.jsxs(
1146
1568
  "div",
1147
1569
  {
1148
1570
  role: "option",
1149
- "aria-selected": activeKey === item.key,
1150
- tabIndex: 0,
1151
- className: `hover:bg-surface-raised cursor-pointer p-3 border-b border-border transition-colors duration-150 focus:outline-none focus-visible:ring-2 focus-visible:ring-accent ${activeKey === item.key ? "bg-surface-raised text-foreground" : "text-foreground-secondary"}`,
1152
- onClick: () => onItemClick(item),
1571
+ "aria-selected": isActive,
1572
+ "aria-disabled": isDisabled || void 0,
1573
+ tabIndex: isDisabled ? -1 : 0,
1574
+ onClick: () => !isDisabled && onItemClick(item),
1153
1575
  onKeyDown: (e) => {
1576
+ if (isDisabled) return;
1154
1577
  if (e.key === "Enter" || e.key === " ") {
1155
1578
  e.preventDefault();
1156
1579
  onItemClick(item);
1157
1580
  }
1158
1581
  },
1159
- children: item.label
1582
+ className: [
1583
+ "flex items-center gap-3 cursor-pointer border-b border-border transition-colors duration-150",
1584
+ DENSITY_PADDING[density],
1585
+ "focus:outline-none focus-visible:ring-2 focus-visible:ring-accent focus-visible:ring-inset",
1586
+ isDisabled ? "opacity-50 cursor-not-allowed" : isActive ? "bg-surface-raised text-foreground" : "text-foreground-secondary hover:bg-surface-raised hover:text-foreground"
1587
+ ].join(" "),
1588
+ children: [
1589
+ item.avatar && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "flex-shrink-0", children: item.avatar }),
1590
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex-1 min-w-0", children: [
1591
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-sm font-medium text-foreground truncate", children: item.label }),
1592
+ item.description && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-xs text-foreground-secondary mt-0.5 truncate", children: item.description })
1593
+ ] }),
1594
+ item.trailing && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "flex-shrink-0 text-foreground-muted", children: item.trailing })
1595
+ ]
1160
1596
  },
1161
1597
  item.key
1162
- )
1163
- )) });
1598
+ );
1599
+ }) });
1164
1600
  }
1165
1601
  var TOGGLE_POSITION_CLASS = {
1166
1602
  "top-left": "top-2 left-2",
@@ -1599,21 +2035,50 @@ function Wizard({
1599
2035
  const handlePrev = () => {
1600
2036
  if (activeIndex > 0) setActiveIndex((i) => i - 1);
1601
2037
  };
2038
+ const SPOT_PAD = 6;
1602
2039
  const highlightStyle = bbox ? {
1603
- left: bbox.left - 4,
1604
- top: bbox.top - 4,
1605
- width: bbox.width + 8,
1606
- height: bbox.height + 8
2040
+ left: bbox.left - SPOT_PAD,
2041
+ top: bbox.top - SPOT_PAD,
2042
+ width: bbox.width + SPOT_PAD * 2,
2043
+ height: bbox.height + SPOT_PAD * 2
2044
+ } : { display: "none" };
2045
+ const backdropTop = bbox ? { left: 0, top: 0, right: 0, height: Math.max(0, bbox.top - SPOT_PAD) } : { display: "none" };
2046
+ const backdropBottom = bbox ? { left: 0, top: bbox.bottom + SPOT_PAD, right: 0, bottom: 0 } : { display: "none" };
2047
+ const backdropLeft = bbox ? {
2048
+ left: 0,
2049
+ top: bbox.top - SPOT_PAD,
2050
+ width: Math.max(0, bbox.left - SPOT_PAD),
2051
+ height: bbox.height + SPOT_PAD * 2
2052
+ } : { display: "none" };
2053
+ const backdropRight = bbox ? {
2054
+ left: bbox.right + SPOT_PAD,
2055
+ top: bbox.top - SPOT_PAD,
2056
+ right: 0,
2057
+ height: bbox.height + SPOT_PAD * 2
1607
2058
  } : { display: "none" };
1608
2059
  const tooltipStyle = bbox ? tooltipStyleFor(bbox, step?.placement) : { display: "none" };
1609
2060
  const isLast = activeIndex === steps.length - 1;
1610
2061
  return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
1611
2062
  children,
1612
2063
  /* @__PURE__ */ jsxRuntime.jsx(framerMotion.AnimatePresence, { children: open && step && /* @__PURE__ */ jsxRuntime.jsxs(Portal, { children: [
2064
+ ["top", "bottom", "left", "right"].map((side) => /* @__PURE__ */ jsxRuntime.jsx(
2065
+ framerMotion.motion.div,
2066
+ {
2067
+ className: "fixed z-[7000000] bg-foreground/40 backdrop-blur-[2px] pointer-events-auto",
2068
+ style: side === "top" ? backdropTop : side === "bottom" ? backdropBottom : side === "left" ? backdropLeft : backdropRight,
2069
+ initial: { opacity: 0 },
2070
+ animate: { opacity: 1 },
2071
+ exit: { opacity: 0 },
2072
+ transition: { duration: reduced ? 0 : 0.18, ease: "easeOut" },
2073
+ "aria-hidden": "true"
2074
+ },
2075
+ side
2076
+ )),
1613
2077
  /* @__PURE__ */ jsxRuntime.jsx(
1614
2078
  framerMotion.motion.div,
1615
2079
  {
1616
- className: "fixed inset-0 z-[7000000] bg-foreground/40 backdrop-blur-[1px] pointer-events-auto",
2080
+ className: "fixed z-[7000001] pointer-events-auto",
2081
+ style: highlightStyle,
1617
2082
  initial: { opacity: 0 },
1618
2083
  animate: { opacity: 1 },
1619
2084
  exit: { opacity: 0 },
@@ -1624,7 +2089,7 @@ function Wizard({
1624
2089
  /* @__PURE__ */ jsxRuntime.jsx(
1625
2090
  framerMotion.motion.div,
1626
2091
  {
1627
- className: "fixed z-[7000001] pointer-events-none rounded-md ring-2 ring-accent ring-offset-2 ring-offset-background",
2092
+ className: "fixed z-[7000002] pointer-events-none rounded-md ring-2 ring-accent",
1628
2093
  style: highlightStyle,
1629
2094
  initial: { opacity: 0, scale: 1.08 },
1630
2095
  animate: { opacity: 1, scale: 1 },
@@ -1645,7 +2110,7 @@ function Wizard({
1645
2110
  "aria-modal": "true",
1646
2111
  "aria-labelledby": step.title ? tooltipTitleId : void 0,
1647
2112
  "aria-describedby": tooltipBodyId,
1648
- className: "fixed z-[7000002] rounded-lg bg-surface text-foreground border border-border shadow-xl p-4 pointer-events-auto",
2113
+ className: "fixed z-[7000003] rounded-lg bg-surface text-foreground border border-border shadow-xl p-4 pointer-events-auto",
1649
2114
  style: tooltipStyle,
1650
2115
  initial: { opacity: 0, scale: 0.96, y: 6 },
1651
2116
  animate: { opacity: 1, scale: 1, y: 0 },
@@ -1932,9 +2397,105 @@ function Dropdown({
1932
2397
  ]
1933
2398
  }
1934
2399
  ),
1935
- hasError && /* @__PURE__ */ jsxRuntime.jsx("div", { id: errorId, className: "text-center text-status-error text-xs mt-1", children: errorMessage })
2400
+ hasError && /* @__PURE__ */ jsxRuntime.jsx("div", { id: errorId, className: "text-status-error text-xs mt-1", children: errorMessage })
1936
2401
  ] });
1937
2402
  }
2403
+ var SHIMMER = [
2404
+ "relative overflow-hidden rounded-sm bg-surface-raised",
2405
+ 'before:absolute before:inset-0 before:content-[""]',
2406
+ "before:bg-gradient-to-r before:from-transparent before:via-white/30 before:to-transparent",
2407
+ "before:animate-[shimmer_1.6s_linear_infinite]",
2408
+ // Respect prefers-reduced-motion — the resting bg-surface-raised is still
2409
+ // a perfectly legible placeholder for users who have animations off.
2410
+ "motion-reduce:before:hidden"
2411
+ ].join(" ");
2412
+ function SkeletonBox({ width, height = 16, radius, className = "", style }) {
2413
+ return /* @__PURE__ */ jsxRuntime.jsx(
2414
+ "span",
2415
+ {
2416
+ role: "presentation",
2417
+ "aria-hidden": "true",
2418
+ className: `block ${SHIMMER} ${className}`,
2419
+ style: {
2420
+ width: width ?? "100%",
2421
+ height,
2422
+ borderRadius: radius ?? "var(--radius-md)",
2423
+ ...style
2424
+ }
2425
+ }
2426
+ );
2427
+ }
2428
+ function SkeletonText({
2429
+ lines = 3,
2430
+ lastLineWidth = 60,
2431
+ lineHeight = 14,
2432
+ gap = 8,
2433
+ className = "",
2434
+ style
2435
+ }) {
2436
+ return /* @__PURE__ */ jsxRuntime.jsx(
2437
+ "div",
2438
+ {
2439
+ role: "presentation",
2440
+ "aria-hidden": "true",
2441
+ className: `flex flex-col ${className}`,
2442
+ style: { gap, ...style },
2443
+ children: Array.from({ length: lines }).map((_, i) => {
2444
+ const isLast = i === lines - 1;
2445
+ const width = isLast && lines > 1 ? `${lastLineWidth}%` : "100%";
2446
+ return /* @__PURE__ */ jsxRuntime.jsx(
2447
+ "span",
2448
+ {
2449
+ className: `block ${SHIMMER}`,
2450
+ style: { height: lineHeight, width, borderRadius: "var(--radius-sm)" }
2451
+ },
2452
+ i
2453
+ );
2454
+ })
2455
+ }
2456
+ );
2457
+ }
2458
+ function SkeletonCircle({ size = 40, className = "", style }) {
2459
+ return /* @__PURE__ */ jsxRuntime.jsx(
2460
+ "span",
2461
+ {
2462
+ role: "presentation",
2463
+ "aria-hidden": "true",
2464
+ className: `block flex-shrink-0 ${SHIMMER} ${className}`,
2465
+ style: {
2466
+ width: size,
2467
+ height: size,
2468
+ borderRadius: "50%",
2469
+ ...style
2470
+ }
2471
+ }
2472
+ );
2473
+ }
2474
+ function SkeletonCard({ hasAvatar = true, lines = 3, className = "", style }) {
2475
+ return /* @__PURE__ */ jsxRuntime.jsxs(
2476
+ "div",
2477
+ {
2478
+ role: "presentation",
2479
+ "aria-hidden": "true",
2480
+ className: `rounded-lg border border-border bg-surface p-4 ${className}`,
2481
+ style,
2482
+ children: [
2483
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-3 mb-4", children: [
2484
+ hasAvatar && /* @__PURE__ */ jsxRuntime.jsx(SkeletonCircle, { size: 36 }),
2485
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex-1 flex flex-col gap-2", children: [
2486
+ /* @__PURE__ */ jsxRuntime.jsx(SkeletonBox, { height: 12, width: "55%" }),
2487
+ /* @__PURE__ */ jsxRuntime.jsx(SkeletonBox, { height: 10, width: "35%" })
2488
+ ] })
2489
+ ] }),
2490
+ /* @__PURE__ */ jsxRuntime.jsx(SkeletonText, { lines, lastLineWidth: 55 }),
2491
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-4 flex gap-2", children: [
2492
+ /* @__PURE__ */ jsxRuntime.jsx(SkeletonBox, { height: 28, width: 72 }),
2493
+ /* @__PURE__ */ jsxRuntime.jsx(SkeletonBox, { height: 28, width: 56 })
2494
+ ] })
2495
+ ]
2496
+ }
2497
+ );
2498
+ }
1938
2499
  var DEFAULT_PICKER = [
1939
2500
  { key: 1, value: 5, label: 5 },
1940
2501
  { key: 2, value: 10, label: 10 },
@@ -2120,7 +2681,9 @@ function Table({
2120
2681
  expandRow = DEFAULT_EXPAND,
2121
2682
  hasSearch = true,
2122
2683
  footer = null,
2123
- header = null
2684
+ header = null,
2685
+ loading = false,
2686
+ loadingRowCount = 8
2124
2687
  }) {
2125
2688
  const searchRef = React8.useRef(null);
2126
2689
  const [searchTerm, setSearchTerm] = React8.useState("");
@@ -2204,9 +2767,16 @@ function Table({
2204
2767
  )
2205
2768
  ] }),
2206
2769
  /* @__PURE__ */ jsxRuntime.jsx("div", { children: header }),
2207
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "overflow-x-auto rounded-lg", children: /* @__PURE__ */ jsxRuntime.jsxs("table", { className: "w-full border-collapse", children: [
2770
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "overflow-x-auto rounded-lg", children: /* @__PURE__ */ jsxRuntime.jsxs("table", { className: "w-full border-collapse", "aria-busy": loading || void 0, children: [
2208
2771
  /* @__PURE__ */ jsxRuntime.jsx(TableHeader, { columns, hasExpand: !!expandRow.enabled }),
2209
- /* @__PURE__ */ jsxRuntime.jsx(
2772
+ loading ? /* @__PURE__ */ jsxRuntime.jsx(
2773
+ TableSkeletonBody,
2774
+ {
2775
+ columns,
2776
+ rowCount: loadingRowCount,
2777
+ hasExpand: !!expandRow.enabled
2778
+ }
2779
+ ) : /* @__PURE__ */ jsxRuntime.jsx(
2210
2780
  TableBody,
2211
2781
  {
2212
2782
  columns,
@@ -2219,6 +2789,23 @@ function Table({
2219
2789
  /* @__PURE__ */ jsxRuntime.jsx("div", { children: footer })
2220
2790
  ] });
2221
2791
  }
2792
+ function TableSkeletonBody({
2793
+ columns,
2794
+ rowCount,
2795
+ hasExpand
2796
+ }) {
2797
+ return /* @__PURE__ */ jsxRuntime.jsx("tbody", { "aria-hidden": "true", children: Array.from({ length: rowCount }).map((_, i) => /* @__PURE__ */ jsxRuntime.jsxs(
2798
+ "tr",
2799
+ {
2800
+ className: `border-b border-border ${i % 2 === 0 ? "bg-surface" : "bg-surface-raised"}`,
2801
+ children: [
2802
+ hasExpand && /* @__PURE__ */ jsxRuntime.jsx("td", { className: "p-0 align-middle w-9" }),
2803
+ columns.map((col) => /* @__PURE__ */ jsxRuntime.jsx("td", { className: "py-3 px-3 align-middle", children: /* @__PURE__ */ jsxRuntime.jsx(SkeletonBox, { height: 12, width: `${50 + i % 4 * 12}%` }) }, col.key))
2804
+ ]
2805
+ },
2806
+ i
2807
+ )) });
2808
+ }
2222
2809
  function ThemeSwitch({ checked, onChange, label = "Toggle dark mode" }) {
2223
2810
  const id = React8.useId();
2224
2811
  return /* @__PURE__ */ jsxRuntime.jsx("label", { htmlFor: id, className: "flex items-center gap-2 cursor-pointer select-none", children: /* @__PURE__ */ jsxRuntime.jsx(
@@ -2651,102 +3238,6 @@ function ThemeProvider({
2651
3238
  )
2652
3239
  ] });
2653
3240
  }
2654
- var SHIMMER = [
2655
- "relative overflow-hidden rounded-sm bg-surface-raised",
2656
- 'before:absolute before:inset-0 before:content-[""]',
2657
- "before:bg-gradient-to-r before:from-transparent before:via-white/30 before:to-transparent",
2658
- "before:animate-[shimmer_1.6s_linear_infinite]",
2659
- // Respect prefers-reduced-motion — the resting bg-surface-raised is still
2660
- // a perfectly legible placeholder for users who have animations off.
2661
- "motion-reduce:before:hidden"
2662
- ].join(" ");
2663
- function SkeletonBox({ width, height = 16, radius, className = "", style }) {
2664
- return /* @__PURE__ */ jsxRuntime.jsx(
2665
- "span",
2666
- {
2667
- role: "presentation",
2668
- "aria-hidden": "true",
2669
- className: `block ${SHIMMER} ${className}`,
2670
- style: {
2671
- width: width ?? "100%",
2672
- height,
2673
- borderRadius: radius ?? "var(--radius-md)",
2674
- ...style
2675
- }
2676
- }
2677
- );
2678
- }
2679
- function SkeletonText({
2680
- lines = 3,
2681
- lastLineWidth = 60,
2682
- lineHeight = 14,
2683
- gap = 8,
2684
- className = "",
2685
- style
2686
- }) {
2687
- return /* @__PURE__ */ jsxRuntime.jsx(
2688
- "div",
2689
- {
2690
- role: "presentation",
2691
- "aria-hidden": "true",
2692
- className: `flex flex-col ${className}`,
2693
- style: { gap, ...style },
2694
- children: Array.from({ length: lines }).map((_, i) => {
2695
- const isLast = i === lines - 1;
2696
- const width = isLast && lines > 1 ? `${lastLineWidth}%` : "100%";
2697
- return /* @__PURE__ */ jsxRuntime.jsx(
2698
- "span",
2699
- {
2700
- className: `block ${SHIMMER}`,
2701
- style: { height: lineHeight, width, borderRadius: "var(--radius-sm)" }
2702
- },
2703
- i
2704
- );
2705
- })
2706
- }
2707
- );
2708
- }
2709
- function SkeletonCircle({ size = 40, className = "", style }) {
2710
- return /* @__PURE__ */ jsxRuntime.jsx(
2711
- "span",
2712
- {
2713
- role: "presentation",
2714
- "aria-hidden": "true",
2715
- className: `block flex-shrink-0 ${SHIMMER} ${className}`,
2716
- style: {
2717
- width: size,
2718
- height: size,
2719
- borderRadius: "50%",
2720
- ...style
2721
- }
2722
- }
2723
- );
2724
- }
2725
- function SkeletonCard({ hasAvatar = true, lines = 3, className = "", style }) {
2726
- return /* @__PURE__ */ jsxRuntime.jsxs(
2727
- "div",
2728
- {
2729
- role: "presentation",
2730
- "aria-hidden": "true",
2731
- className: `rounded-lg border border-border bg-surface p-4 ${className}`,
2732
- style,
2733
- children: [
2734
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-3 mb-4", children: [
2735
- hasAvatar && /* @__PURE__ */ jsxRuntime.jsx(SkeletonCircle, { size: 36 }),
2736
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex-1 flex flex-col gap-2", children: [
2737
- /* @__PURE__ */ jsxRuntime.jsx(SkeletonBox, { height: 12, width: "55%" }),
2738
- /* @__PURE__ */ jsxRuntime.jsx(SkeletonBox, { height: 10, width: "35%" })
2739
- ] })
2740
- ] }),
2741
- /* @__PURE__ */ jsxRuntime.jsx(SkeletonText, { lines, lastLineWidth: 55 }),
2742
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-4 flex gap-2", children: [
2743
- /* @__PURE__ */ jsxRuntime.jsx(SkeletonBox, { height: 28, width: 72 }),
2744
- /* @__PURE__ */ jsxRuntime.jsx(SkeletonBox, { height: 28, width: 56 })
2745
- ] })
2746
- ]
2747
- }
2748
- );
2749
- }
2750
3241
  function TextInput({
2751
3242
  value,
2752
3243
  onChange,
@@ -2764,48 +3255,50 @@ function TextInput({
2764
3255
  }) {
2765
3256
  const errorId = React8.useId();
2766
3257
  const hasError = errorMessage != null;
2767
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative flex flex-col items-center justify-center", children: [
3258
+ return (
3259
+ // In horizontal mode the row layout is [label, input-with-error-column].
3260
+ // The error sits under the input ONLY, not spanning the label too.
3261
+ // In vertical mode the whole thing is a column.
2768
3262
  /* @__PURE__ */ jsxRuntime.jsxs(
2769
3263
  "div",
2770
3264
  {
2771
- className: `flex ${layout === "vertical" ? "flex-col" : "flex-row items-center gap-2"}`,
3265
+ className: `flex ${layout === "vertical" ? "flex-col gap-1" : "flex-row items-start gap-2"}`,
2772
3266
  style: style ?? {},
2773
3267
  children: [
2774
- label && // Render <label> only when a label is provided. An empty
2775
- // <label htmlFor=…> announces as an unlabeled control in
2776
- // some screen readers.
2777
- /* @__PURE__ */ jsxRuntime.jsx(
3268
+ label && /* @__PURE__ */ jsxRuntime.jsx(
2778
3269
  "label",
2779
3270
  {
2780
3271
  style: { color: labelColor || void 0 },
2781
- className: `text-sm font-medium ml-1 max-content ${!labelColor && "text-foreground"}`,
3272
+ className: `text-sm font-medium ${layout === "horizontal" ? "mt-2" : ""} max-content ${!labelColor && "text-foreground"}`,
2782
3273
  htmlFor,
2783
3274
  children: label
2784
3275
  }
2785
3276
  ),
2786
- /* @__PURE__ */ jsxRuntime.jsx(
2787
- "input",
2788
- {
2789
- autoComplete: "off",
2790
- disabled,
2791
- value,
2792
- onChange,
2793
- onBlur,
2794
- type: "text",
2795
- name,
2796
- id: htmlFor,
2797
- "aria-invalid": hasError || void 0,
2798
- "aria-describedby": hasError ? errorId : void 0,
2799
- className: `${hasError ? "border border-status-error" : "border border-border"} bg-surface text-foreground p-2 h-9 w-60 mt-1 rounded-lg disabled:bg-surface-raised disabled:text-foreground-muted disabled:cursor-not-allowed focus:outline-none focus:border-transparent focus:ring-2 focus:ring-accent transition-colors`,
2800
- style: inputStyle ?? {},
2801
- placeholder: placeholder ?? ""
2802
- }
2803
- )
3277
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col", children: [
3278
+ /* @__PURE__ */ jsxRuntime.jsx(
3279
+ "input",
3280
+ {
3281
+ autoComplete: "off",
3282
+ disabled,
3283
+ value,
3284
+ onChange,
3285
+ onBlur,
3286
+ type: "text",
3287
+ name,
3288
+ id: htmlFor,
3289
+ "aria-invalid": hasError || void 0,
3290
+ "aria-describedby": hasError ? errorId : void 0,
3291
+ className: `${hasError ? "border border-status-error" : "border border-border"} bg-surface text-foreground p-2 h-9 w-60 rounded-lg disabled:bg-surface-raised disabled:text-foreground-muted disabled:cursor-not-allowed focus:outline-none focus:border-transparent focus:ring-2 focus:ring-accent transition-colors`,
3292
+ style: inputStyle ?? {},
3293
+ placeholder: placeholder ?? ""
3294
+ }
3295
+ ),
3296
+ hasError && /* @__PURE__ */ jsxRuntime.jsx("div", { id: errorId, className: "text-status-error text-xs mt-1", children: errorMessage })
3297
+ ] })
2804
3298
  ]
2805
3299
  }
2806
- ),
2807
- hasError && /* @__PURE__ */ jsxRuntime.jsx("div", { id: errorId, className: "text-center text-status-error text-xs mt-1", children: errorMessage })
2808
- ] });
3300
+ )
3301
+ );
2809
3302
  }
2810
3303
  function NumberInput({
2811
3304
  step = 1,
@@ -2947,64 +3440,71 @@ function Password({
2947
3440
  const [passwordVisible, setPasswordVisible] = React8.useState(false);
2948
3441
  const errorId = React8.useId();
2949
3442
  const hasError = errorMessage != null;
2950
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative flex flex-col items-center justify-center", style: style ?? {}, children: [
2951
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `flex ${layout === "vertical" ? "flex-col" : "flex-row items-center gap-2"}`, children: [
2952
- label && /* @__PURE__ */ jsxRuntime.jsx(
2953
- "label",
2954
- {
2955
- style: { color: labelColor || void 0 },
2956
- className: `text-sm font-medium ml-1 max-content ${!labelColor && "text-foreground"}`,
2957
- htmlFor,
2958
- children: label
2959
- }
2960
- ),
2961
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-1", children: [
2962
- /* @__PURE__ */ jsxRuntime.jsx(
2963
- "input",
3443
+ return /* @__PURE__ */ jsxRuntime.jsxs(
3444
+ "div",
3445
+ {
3446
+ className: `flex ${layout === "vertical" ? "flex-col gap-1" : "flex-row items-start gap-2"}`,
3447
+ style: style ?? {},
3448
+ children: [
3449
+ label && /* @__PURE__ */ jsxRuntime.jsx(
3450
+ "label",
2964
3451
  {
2965
- autoComplete: "off",
2966
- disabled,
2967
- value,
2968
- onChange,
2969
- onBlur,
2970
- type: passwordVisible ? "text" : "password",
2971
- name,
2972
- id: htmlFor,
2973
- "aria-invalid": hasError || void 0,
2974
- "aria-describedby": hasError ? errorId : void 0,
2975
- className: `${hasError ? "border border-status-error" : "border border-border"} bg-surface text-foreground p-2 h-9 w-52 mt-1 rounded-lg disabled:bg-surface-raised disabled:text-foreground-muted disabled:cursor-not-allowed focus:outline-none focus:border-transparent focus:ring-2 focus:ring-accent transition-colors`,
2976
- style: inputStyle ?? {},
2977
- placeholder: placeholder ?? ""
3452
+ style: { color: labelColor || void 0 },
3453
+ className: `text-sm font-medium ${layout === "horizontal" ? "mt-2" : ""} max-content ${!labelColor && "text-foreground"}`,
3454
+ htmlFor,
3455
+ children: label
2978
3456
  }
2979
3457
  ),
2980
- /* @__PURE__ */ jsxRuntime.jsx(
2981
- "button",
2982
- {
2983
- type: "button",
2984
- className: "cursor-pointer p-1 text-foreground-secondary hover:text-foreground rounded-md focus:outline-none focus-visible:ring-2 focus-visible:ring-accent",
2985
- style: iconColor ? { color: iconColor } : void 0,
2986
- onClick: () => setPasswordVisible(!passwordVisible),
2987
- "aria-label": passwordVisible ? "Hide password" : "Show password",
2988
- children: passwordVisible ? (
2989
- /* EyeSlash */
2990
- /* @__PURE__ */ jsxRuntime.jsxs("svg", { xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 24 24", fill: "currentColor", className: "w-6 h-6", children: [
2991
- /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M3.53 2.47a.75.75 0 00-1.06 1.06l18 18a.75.75 0 101.06-1.06l-18-18zM22.676 12.553a11.249 11.249 0 01-2.631 4.31l-3.099-3.099a5.25 5.25 0 00-6.71-6.71L7.759 4.577a11.217 11.217 0 014.242-.827c4.97 0 9.185 3.223 10.675 7.69.12.362.12.752 0 1.113z" }),
2992
- /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M15.75 12c0 .18-.013.357-.037.53l-4.244-4.243A3.75 3.75 0 0115.75 12zM12.53 15.713l-4.243-4.244a3.75 3.75 0 004.243 4.243z" }),
2993
- /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M6.75 12c0-.619.107-1.213.304-1.764l-3.1-3.1a11.25 11.25 0 00-2.63 4.31c-.12.362-.12.752 0 1.114 1.489 4.467 5.704 7.69 10.675 7.69 1.5 0 2.933-.294 4.242-.827l-2.477-2.477A5.25 5.25 0 016.75 12z" })
2994
- ] })
2995
- ) : (
2996
- /* Eye */
2997
- /* @__PURE__ */ jsxRuntime.jsxs("svg", { xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 24 24", fill: "currentColor", className: "w-6 h-6", children: [
2998
- /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M12 15a3 3 0 100-6 3 3 0 000 6z" }),
2999
- /* @__PURE__ */ jsxRuntime.jsx("path", { fillRule: "evenodd", d: "M1.323 11.447C2.811 6.976 7.028 3.75 12.001 3.75c4.97 0 9.185 3.223 10.675 7.69.12.362.12.752 0 1.113-1.487 4.471-5.705 7.697-10.677 7.697-4.97 0-9.186-3.223-10.675-7.69a1.762 1.762 0 010-1.113zM17.25 12a5.25 5.25 0 11-10.5 0 5.25 5.25 0 0110.5 0z", clipRule: "evenodd" })
3000
- ] })
3458
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col", children: [
3459
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-1", children: [
3460
+ /* @__PURE__ */ jsxRuntime.jsx(
3461
+ "input",
3462
+ {
3463
+ autoComplete: "off",
3464
+ disabled,
3465
+ value,
3466
+ onChange,
3467
+ onBlur,
3468
+ type: passwordVisible ? "text" : "password",
3469
+ name,
3470
+ id: htmlFor,
3471
+ "aria-invalid": hasError || void 0,
3472
+ "aria-describedby": hasError ? errorId : void 0,
3473
+ className: `${hasError ? "border border-status-error" : "border border-border"} bg-surface text-foreground p-2 h-9 w-52 rounded-lg disabled:bg-surface-raised disabled:text-foreground-muted disabled:cursor-not-allowed focus:outline-none focus:border-transparent focus:ring-2 focus:ring-accent transition-colors`,
3474
+ style: inputStyle ?? {},
3475
+ placeholder: placeholder ?? ""
3476
+ }
3477
+ ),
3478
+ /* @__PURE__ */ jsxRuntime.jsx(
3479
+ "button",
3480
+ {
3481
+ type: "button",
3482
+ className: "cursor-pointer p-1 text-foreground-secondary hover:text-foreground rounded-md focus:outline-none focus-visible:ring-2 focus-visible:ring-accent",
3483
+ style: iconColor ? { color: iconColor } : void 0,
3484
+ onClick: () => setPasswordVisible(!passwordVisible),
3485
+ "aria-label": passwordVisible ? "Hide password" : "Show password",
3486
+ children: passwordVisible ? (
3487
+ /* EyeSlash */
3488
+ /* @__PURE__ */ jsxRuntime.jsxs("svg", { xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 24 24", fill: "currentColor", className: "w-6 h-6", children: [
3489
+ /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M3.53 2.47a.75.75 0 00-1.06 1.06l18 18a.75.75 0 101.06-1.06l-18-18zM22.676 12.553a11.249 11.249 0 01-2.631 4.31l-3.099-3.099a5.25 5.25 0 00-6.71-6.71L7.759 4.577a11.217 11.217 0 014.242-.827c4.97 0 9.185 3.223 10.675 7.69.12.362.12.752 0 1.113z" }),
3490
+ /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M15.75 12c0 .18-.013.357-.037.53l-4.244-4.243A3.75 3.75 0 0115.75 12zM12.53 15.713l-4.243-4.244a3.75 3.75 0 004.243 4.243z" }),
3491
+ /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M6.75 12c0-.619.107-1.213.304-1.764l-3.1-3.1a11.25 11.25 0 00-2.63 4.31c-.12.362-.12.752 0 1.114 1.489 4.467 5.704 7.69 10.675 7.69 1.5 0 2.933-.294 4.242-.827l-2.477-2.477A5.25 5.25 0 016.75 12z" })
3492
+ ] })
3493
+ ) : (
3494
+ /* Eye */
3495
+ /* @__PURE__ */ jsxRuntime.jsxs("svg", { xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 24 24", fill: "currentColor", className: "w-6 h-6", children: [
3496
+ /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M12 15a3 3 0 100-6 3 3 0 000 6z" }),
3497
+ /* @__PURE__ */ jsxRuntime.jsx("path", { fillRule: "evenodd", d: "M1.323 11.447C2.811 6.976 7.028 3.75 12.001 3.75c4.97 0 9.185 3.223 10.675 7.69.12.362.12.752 0 1.113-1.487 4.471-5.705 7.697-10.677 7.697-4.97 0-9.186-3.223-10.675-7.69a1.762 1.762 0 010-1.113zM17.25 12a5.25 5.25 0 11-10.5 0 5.25 5.25 0 0110.5 0z", clipRule: "evenodd" })
3498
+ ] })
3499
+ )
3500
+ }
3001
3501
  )
3002
- }
3003
- )
3004
- ] })
3005
- ] }),
3006
- hasError && /* @__PURE__ */ jsxRuntime.jsx("div", { id: errorId, className: "text-center text-status-error text-xs mt-1", children: errorMessage })
3007
- ] });
3502
+ ] }),
3503
+ hasError && /* @__PURE__ */ jsxRuntime.jsx("div", { id: errorId, className: "text-status-error text-xs mt-1", children: errorMessage })
3504
+ ] })
3505
+ ]
3506
+ }
3507
+ );
3008
3508
  }
3009
3509
  function Checkbox({
3010
3510
  checked,
@@ -3117,15 +3617,54 @@ function AutoComplete({
3117
3617
  inputStyle,
3118
3618
  style,
3119
3619
  layout = "vertical",
3120
- items = [],
3620
+ items,
3621
+ onSearch,
3622
+ debounce = 250,
3121
3623
  onItemClick,
3122
- emptyText = "No results found"
3624
+ emptyText = "No results found",
3625
+ loadingText = "Searching\u2026"
3123
3626
  }) {
3124
3627
  const [term, setTerm] = React8.useState("");
3125
3628
  const [open, setOpen] = React8.useState(false);
3126
- const foundItems = term.trim() ? items.filter(
3629
+ const [asyncItems, setAsyncItems] = React8.useState([]);
3630
+ const [loading, setLoading] = React8.useState(false);
3631
+ const isAsync = typeof onSearch === "function";
3632
+ const debounceRef = React8.useRef(null);
3633
+ const requestIdRef = React8.useRef(0);
3634
+ const staticFiltered = isAsync || !items ? [] : term.trim() ? items.filter(
3127
3635
  ({ key, label: label2 }) => label2.toLowerCase().includes(term.toLowerCase()) || key.toLowerCase().includes(term.toLowerCase())
3128
3636
  ) : [];
3637
+ React8.useEffect(() => {
3638
+ if (!isAsync) return;
3639
+ if (debounceRef.current) clearTimeout(debounceRef.current);
3640
+ if (!term.trim()) {
3641
+ setAsyncItems([]);
3642
+ setLoading(false);
3643
+ return;
3644
+ }
3645
+ const myId = ++requestIdRef.current;
3646
+ setLoading(true);
3647
+ debounceRef.current = setTimeout(async () => {
3648
+ try {
3649
+ const res = await onSearch(term);
3650
+ if (myId === requestIdRef.current) {
3651
+ setAsyncItems(res);
3652
+ }
3653
+ } catch {
3654
+ if (myId === requestIdRef.current) {
3655
+ setAsyncItems([]);
3656
+ }
3657
+ } finally {
3658
+ if (myId === requestIdRef.current) {
3659
+ setLoading(false);
3660
+ }
3661
+ }
3662
+ }, debounce);
3663
+ return () => {
3664
+ if (debounceRef.current) clearTimeout(debounceRef.current);
3665
+ };
3666
+ }, [term, isAsync, debounce, onSearch]);
3667
+ const foundItems = isAsync ? asyncItems : staticFiltered;
3129
3668
  const handleSelect = (item) => {
3130
3669
  setTerm(`${item.label} (${item.value})`);
3131
3670
  onItemClick?.(item.value);
@@ -3158,10 +3697,11 @@ function AutoComplete({
3158
3697
  autoComplete: "off",
3159
3698
  "aria-haspopup": "listbox",
3160
3699
  "aria-expanded": open,
3161
- "aria-autocomplete": "list"
3700
+ "aria-autocomplete": "list",
3701
+ "aria-busy": loading || void 0
3162
3702
  }
3163
3703
  ),
3164
- /* @__PURE__ */ jsxRuntime.jsx("svg", { xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 24 24", fill: "currentColor", className: "w-5 h-5 flex-shrink-0 text-foreground-muted", "aria-hidden": "true", children: /* @__PURE__ */ jsxRuntime.jsx("path", { fillRule: "evenodd", d: "M10.5 3.75a6.75 6.75 0 100 13.5 6.75 6.75 0 000-13.5zM2.25 10.5a8.25 8.25 0 1114.59 5.28l4.69 4.69a.75.75 0 11-1.06 1.06l-4.69-4.69A8.25 8.25 0 012.25 10.5z", clipRule: "evenodd" }) })
3704
+ loading ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "w-5 h-5 flex-shrink-0 flex items-center justify-center text-accent", "aria-hidden": "true", children: /* @__PURE__ */ jsxRuntime.jsx(LoadingSpinner, { inline: true, size: "xs", spinnerColor: "currentColor" }) }) : /* @__PURE__ */ jsxRuntime.jsx("svg", { xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 24 24", fill: "currentColor", className: "w-5 h-5 flex-shrink-0 text-foreground-muted", "aria-hidden": "true", children: /* @__PURE__ */ jsxRuntime.jsx("path", { fillRule: "evenodd", d: "M10.5 3.75a6.75 6.75 0 100 13.5 6.75 6.75 0 000-13.5zM2.25 10.5a8.25 8.25 0 1114.59 5.28l4.69 4.69a.75.75 0 11-1.06 1.06l-4.69-4.69A8.25 8.25 0 012.25 10.5z", clipRule: "evenodd" }) })
3165
3705
  ] }) }),
3166
3706
  /* @__PURE__ */ jsxRuntime.jsx(Popover__namespace.Portal, { children: /* @__PURE__ */ jsxRuntime.jsx(
3167
3707
  Popover__namespace.Content,
@@ -3170,36 +3710,33 @@ function AutoComplete({
3170
3710
  sideOffset: 4,
3171
3711
  onOpenAutoFocus: (e) => e.preventDefault(),
3172
3712
  className: "w-64 bg-surface border border-border rounded-lg mt-1 shadow-md z-50 overflow-y-auto max-h-36 animate-in fade-in-0 zoom-in-95",
3173
- children: foundItems.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-full w-full flex flex-col items-center justify-center py-4 text-sm text-foreground-secondary", children: emptyText }) : /* @__PURE__ */ jsxRuntime.jsx("div", { role: "listbox", children: foundItems.map((item) => (
3174
- // tabIndex + Enter/Space onKeyDown
3175
- // makes each option keyboard-activatable.
3176
- // Full roving-tabindex / arrow-key nav
3177
- // is deferred to the Phase-5 rewrite.
3178
- /* @__PURE__ */ jsxRuntime.jsxs(
3179
- "div",
3180
- {
3181
- role: "option",
3182
- tabIndex: 0,
3183
- className: "text-sm flex items-center gap-2 p-2 transition-colors duration-150 hover:bg-surface-raised cursor-pointer text-foreground focus:outline-none focus-visible:ring-2 focus-visible:ring-accent",
3184
- onClick: () => handleSelect(item),
3185
- onKeyDown: (e) => {
3186
- if (e.key === "Enter" || e.key === " ") {
3187
- e.preventDefault();
3188
- handleSelect(item);
3189
- }
3190
- },
3191
- children: [
3192
- item.icon,
3193
- /* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
3194
- item.label,
3195
- " (",
3196
- item.value,
3197
- ")"
3198
- ] })
3199
- ]
3713
+ children: loading ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "h-full w-full flex items-center justify-center gap-2 py-4 text-sm text-foreground-secondary", children: [
3714
+ /* @__PURE__ */ jsxRuntime.jsx(LoadingSpinner, { inline: true, size: "xs" }),
3715
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: loadingText })
3716
+ ] }) : foundItems.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-full w-full flex flex-col items-center justify-center py-4 text-sm text-foreground-secondary", children: emptyText }) : /* @__PURE__ */ jsxRuntime.jsx("div", { role: "listbox", children: foundItems.map((item) => /* @__PURE__ */ jsxRuntime.jsxs(
3717
+ "div",
3718
+ {
3719
+ role: "option",
3720
+ tabIndex: 0,
3721
+ className: "text-sm flex items-center gap-2 p-2 transition-colors duration-150 hover:bg-surface-raised cursor-pointer text-foreground focus:outline-none focus-visible:ring-2 focus-visible:ring-accent",
3722
+ onClick: () => handleSelect(item),
3723
+ onKeyDown: (e) => {
3724
+ if (e.key === "Enter" || e.key === " ") {
3725
+ e.preventDefault();
3726
+ handleSelect(item);
3727
+ }
3200
3728
  },
3201
- item.key
3202
- )
3729
+ children: [
3730
+ item.icon,
3731
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
3732
+ item.label,
3733
+ " (",
3734
+ item.value,
3735
+ ")"
3736
+ ] })
3737
+ ]
3738
+ },
3739
+ item.key
3203
3740
  )) })
3204
3741
  }
3205
3742
  ) })
@@ -3933,6 +4470,8 @@ Object.defineProperty(exports, "vars", {
3933
4470
  });
3934
4471
  exports.AppShell = AppShell;
3935
4472
  exports.AutoComplete = AutoComplete;
4473
+ exports.Avatar = Avatar;
4474
+ exports.Box = Box;
3936
4475
  exports.Button = Button;
3937
4476
  exports.Catalog = Catalog;
3938
4477
  exports.CatalogCarousel = CatalogCarousel;
@@ -3943,6 +4482,8 @@ exports.Drawer = Drawer;
3943
4482
  exports.Dropdown = Dropdown;
3944
4483
  exports.FadingBase = FadingBase;
3945
4484
  exports.FileInput = FileInput;
4485
+ exports.Flex = Flex;
4486
+ exports.Grid = Grid2;
3946
4487
  exports.GridCard = GridCard;
3947
4488
  exports.Icon = icons_default;
3948
4489
  exports.IconButton = IconButton;
@@ -3974,6 +4515,7 @@ exports.TooltipProvider = TooltipProvider;
3974
4515
  exports.TopBar = TopBar;
3975
4516
  exports.Tree = Tree;
3976
4517
  exports.TreeSelect = TreeSelect;
4518
+ exports.Typography = Typography;
3977
4519
  exports.Wizard = Wizard;
3978
4520
  exports.useNotification = useNotification;
3979
4521
  //# sourceMappingURL=index.cjs.map