@mirror-physics/fractal-ui 0.2.0-next.3 → 0.2.0-next.30

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
@@ -50,7 +50,7 @@ var colorPalette = {
50
50
  var sizeConfig = {
51
51
  xs: { height: "28px", padding: "4px 8px", iconOnly: "28px", fontSize: "0.75rem" },
52
52
  sm: { height: "32px", padding: "6px 12px", iconOnly: "32px", fontSize: "0.75rem" },
53
- md: { height: "38px", padding: "8px 16px", iconOnly: "38px", fontSize: "0.875rem" },
53
+ md: { height: "40px", padding: "8px 16px", iconOnly: "40px", fontSize: "0.875rem" },
54
54
  lg: { height: "44px", padding: "10px 20px", iconOnly: "44px", fontSize: "1rem" },
55
55
  xl: { height: "56px", padding: "14px 28px", iconOnly: "56px", fontSize: "1rem" }
56
56
  };
@@ -329,28 +329,32 @@ Label.displayName = "Label";
329
329
 
330
330
  // src/components/Alert/Alert.tsx
331
331
  import * as React6 from "react";
332
+ import { CircleCheck, CircleClose, CircleInfo, Warning } from "@mirror-physics/fractal-icons";
332
333
  import { jsx as jsx5, jsxs as jsxs4 } from "react/jsx-runtime";
333
334
  var defaultIcons = {
334
- default: "\u2139",
335
- info: "\u2139",
336
- success: "\u2713",
337
- warning: "\u26A0",
338
- error: "\u2715"
335
+ default: CircleInfo,
336
+ info: CircleInfo,
337
+ success: CircleCheck,
338
+ warning: Warning,
339
+ error: CircleClose
339
340
  };
340
341
  var Alert = React6.forwardRef(
341
- ({ className, variant = "default", icon, children, ...props }, ref) => /* @__PURE__ */ jsxs4(
342
- "div",
343
- {
344
- ref,
345
- role: "alert",
346
- className: cn("fractal-alert", `fractal-alert--${variant}`, className),
347
- ...props,
348
- children: [
349
- /* @__PURE__ */ jsx5("span", { className: "fractal-alert-icon", "aria-hidden": true, children: icon ?? defaultIcons[variant] }),
350
- /* @__PURE__ */ jsx5("div", { className: "fractal-alert-content", children })
351
- ]
352
- }
353
- )
342
+ ({ className, variant = "default", icon, children, ...props }, ref) => {
343
+ const DefaultIcon = defaultIcons[variant];
344
+ return /* @__PURE__ */ jsxs4(
345
+ "div",
346
+ {
347
+ ref,
348
+ role: "alert",
349
+ className: cn("fractal-alert", `fractal-alert--${variant}`, className),
350
+ ...props,
351
+ children: [
352
+ /* @__PURE__ */ jsx5("span", { className: "fractal-alert-icon", "aria-hidden": true, children: icon ?? /* @__PURE__ */ jsx5(DefaultIcon, { size: 18 }) }),
353
+ /* @__PURE__ */ jsx5("div", { className: "fractal-alert-content", children })
354
+ ]
355
+ }
356
+ );
357
+ }
354
358
  );
355
359
  Alert.displayName = "Alert";
356
360
  var AlertTitle = React6.forwardRef(
@@ -434,27 +438,72 @@ function useEscapeDismiss(priority, handler, enabled = true) {
434
438
  }, [priority, enabled]);
435
439
  }
436
440
 
437
- // src/components/Modal/Modal.tsx
441
+ // src/components/UILoader/UILoader.tsx
442
+ import { useId } from "react";
438
443
  import { jsx as jsx7, jsxs as jsxs6 } from "react/jsx-runtime";
439
- var CloseIcon = () => /* @__PURE__ */ jsx7("svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "none", xmlns: "http://www.w3.org/2000/svg", children: /* @__PURE__ */ jsx7("path", { d: "M12 4L4 12M4 4L12 12", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round" }) });
440
- function Modal({ open, onClose, title, children, danger, warn, footer, noClose, size = "sm", className }) {
441
- useEscapeDismiss(80, onClose, open && !noClose);
444
+ var LEFT = "M0 53C0 51 1.6 49.5 3.6 49.5h16.5c1 0 1.9.4 2.5 1L42.1 69.6c.7.7 1.1 1.6 1.1 2.5v16.2c0 2-1.6 3.5-3.6 3.5H23.1c-1 0-1.9-.4-2.5-1L1.1 71.7C.4 71 0 70.1 0 69.2V53Z";
445
+ var CENTER = "M0 3.5C0 1.6 1.6 0 3.6 0h16.5c1 0 1.9.4 2.5 1L92.5 69.6c.7.7 1 1.6 1 2.5v16.2c0 2-1.6 3.5-3.6 3.5H73.4c-1 0-1.9-.4-2.5-1L1.1 22.2C.4 21.6 0 20.7 0 19.7V3.5Z";
446
+ var RIGHT = "M50.4 3.5C50.4 1.6 52 0 54 0h16.5c1 0 1.9.4 2.5 1l69.8 68.6c.7.7 1.1 1.6 1.1 2.5v16.2c0 2-1.6 3.5-3.6 3.5h-16.5c-1 0-1.9-.4-2.5-1L51.4 22.2c-.7-.6-1-1.5-1-2.5V3.5Z";
447
+ function UILoader({ size = 48, tone = "neutral", className, style, label = "Loading", ariaHidden = false }) {
448
+ const rawId = useId().replace(/[^a-zA-Z0-9_-]/g, "");
449
+ const clipId = `fractal-ui-loader-clip-${rawId}`;
450
+ const gradientId = `fractal-ui-loader-gradient-${rawId}`;
451
+ const height = typeof size === "number" ? `${size}px` : size;
452
+ return /* @__PURE__ */ jsx7(
453
+ "span",
454
+ {
455
+ "aria-hidden": ariaHidden || void 0,
456
+ "aria-label": ariaHidden ? void 0 : label,
457
+ className: cn("fractal-ui-loader", `fractal-ui-loader--${tone}`, className),
458
+ role: ariaHidden ? void 0 : "status",
459
+ style,
460
+ children: /* @__PURE__ */ jsxs6("svg", { "aria-hidden": "true", fill: "none", viewBox: "0 0 144 92", style: { height, width: "auto" }, children: [
461
+ /* @__PURE__ */ jsxs6("defs", { children: [
462
+ /* @__PURE__ */ jsxs6("linearGradient", { id: gradientId, x1: "59", x2: "94.3", y1: "0", y2: "89.1", gradientUnits: "userSpaceOnUse", children: [
463
+ /* @__PURE__ */ jsx7("stop", { className: "fractal-ui-loader__stop-start", offset: "0" }),
464
+ /* @__PURE__ */ jsx7("stop", { className: "fractal-ui-loader__stop-end", offset: "1" })
465
+ ] }),
466
+ /* @__PURE__ */ jsxs6("linearGradient", { id: `${gradientId}-shine`, x1: "0", x2: "1", y1: "0", y2: "0", children: [
467
+ /* @__PURE__ */ jsx7("stop", { offset: "0.4", stopColor: "white", stopOpacity: "0" }),
468
+ /* @__PURE__ */ jsx7("stop", { offset: "0.5", stopColor: "white", stopOpacity: "0.62" }),
469
+ /* @__PURE__ */ jsx7("stop", { offset: "0.6", stopColor: "white", stopOpacity: "0" })
470
+ ] }),
471
+ /* @__PURE__ */ jsxs6("clipPath", { id: clipId, children: [
472
+ /* @__PURE__ */ jsx7("path", { d: LEFT }),
473
+ /* @__PURE__ */ jsx7("path", { d: CENTER }),
474
+ /* @__PURE__ */ jsx7("path", { d: RIGHT })
475
+ ] })
476
+ ] }),
477
+ /* @__PURE__ */ jsx7("path", { d: CENTER, fill: `url(#${gradientId})` }),
478
+ /* @__PURE__ */ jsx7("path", { d: RIGHT, fill: `url(#${gradientId})` }),
479
+ /* @__PURE__ */ jsx7("path", { d: LEFT, fill: `url(#${gradientId})` }),
480
+ /* @__PURE__ */ jsx7("g", { clipPath: `url(#${clipId})`, children: /* @__PURE__ */ jsx7("g", { className: "fractal-ui-loader__shine-axis", transform: "translate(72 46) rotate(68.4)", children: /* @__PURE__ */ jsx7("rect", { className: "fractal-ui-loader__shine", x: "-160", y: "-100", width: "320", height: "200", fill: `url(#${gradientId}-shine)` }) }) })
481
+ ] })
482
+ }
483
+ );
484
+ }
485
+
486
+ // src/components/Modal/Modal.tsx
487
+ import { jsx as jsx8, jsxs as jsxs7 } from "react/jsx-runtime";
488
+ var CloseIcon = () => /* @__PURE__ */ jsx8("svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "none", xmlns: "http://www.w3.org/2000/svg", children: /* @__PURE__ */ jsx8("path", { d: "M12 4L4 12M4 4L12 12", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round" }) });
489
+ function Modal({ open, onClose, title, children, danger, warn, footer, noClose, closeDisabled = false, size = "sm", loading = false, loadingLabel = "Loading", className }) {
490
+ useEscapeDismiss(80, onClose, open && !noClose && !closeDisabled);
442
491
  if (!open) return null;
443
492
  const sizeClass = `fractal-modal-panel--${size}`;
444
493
  const accentClass = danger ? "fractal-modal-panel--danger" : warn ? "fractal-modal-panel--warn" : "";
445
494
  return createPortal(
446
- /* @__PURE__ */ jsx7("div", { className: "fractal-modal-overlay", onClick: noClose ? void 0 : onClose, children: /* @__PURE__ */ jsxs6(
495
+ /* @__PURE__ */ jsx8("div", { className: "fractal-modal-overlay", onClick: noClose || closeDisabled ? void 0 : onClose, children: /* @__PURE__ */ jsxs7(
447
496
  "div",
448
497
  {
449
498
  className: cn("fractal-modal-panel", sizeClass, accentClass, className),
450
499
  onClick: (e) => e.stopPropagation(),
451
500
  children: [
452
- /* @__PURE__ */ jsxs6("div", { className: "fractal-modal-title-row", children: [
453
- /* @__PURE__ */ jsx7("h3", { className: cn("fractal-modal-title", danger && "fractal-modal-title--danger", warn && "fractal-modal-title--warn"), children: title }),
454
- !noClose && /* @__PURE__ */ jsx7("button", { type: "button", onClick: onClose, title: "Close", className: "fractal-modal-close", children: /* @__PURE__ */ jsx7(CloseIcon, {}) })
501
+ /* @__PURE__ */ jsxs7("div", { className: "fractal-modal-title-row", children: [
502
+ /* @__PURE__ */ jsx8("h3", { className: cn("fractal-modal-title", danger && "fractal-modal-title--danger", warn && "fractal-modal-title--warn"), children: title }),
503
+ !noClose && /* @__PURE__ */ jsx8("button", { type: "button", onClick: onClose, title: "Close", disabled: closeDisabled, className: "fractal-modal-close", children: /* @__PURE__ */ jsx8(CloseIcon, {}) })
455
504
  ] }),
456
- /* @__PURE__ */ jsx7("div", { className: cn("fractal-modal-body", size === "lg" && "fractal-modal-body--lg"), children }),
457
- footer && /* @__PURE__ */ jsx7("div", { className: "fractal-modal-footer", children: footer })
505
+ /* @__PURE__ */ jsx8("div", { "aria-busy": loading || void 0, className: cn("fractal-modal-body", size === "lg" && "fractal-modal-body--lg", loading && "fractal-modal-body--loading"), children: loading ? /* @__PURE__ */ jsx8(UILoader, { label: loadingLabel }) : children }),
506
+ footer && /* @__PURE__ */ jsx8("div", { className: "fractal-modal-footer", children: footer })
458
507
  ]
459
508
  }
460
509
  ) }),
@@ -463,17 +512,17 @@ function Modal({ open, onClose, title, children, danger, warn, footer, noClose,
463
512
  }
464
513
 
465
514
  // src/components/DataTable/DataTable.tsx
466
- import { jsx as jsx8, jsxs as jsxs7 } from "react/jsx-runtime";
515
+ import { jsx as jsx9, jsxs as jsxs8 } from "react/jsx-runtime";
467
516
  function DataTable({ columns, data, emptyMessage, header, size = "md", className }) {
468
- return /* @__PURE__ */ jsx8("div", { className: cn("fractal-datatable", `fractal-datatable--${size}`, className), children: /* @__PURE__ */ jsxs7("div", { className: "fractal-datatable-surface", children: [
469
- header && /* @__PURE__ */ jsx8("div", { className: "fractal-datatable-header", children: header }),
470
- data.length === 0 ? /* @__PURE__ */ jsx8("div", { className: "fractal-datatable-empty", children: emptyMessage || "No data" }) : /* @__PURE__ */ jsxs7("table", { className: "fractal-datatable-table", children: [
471
- /* @__PURE__ */ jsx8("thead", { children: /* @__PURE__ */ jsx8("tr", { className: "fractal-datatable-thead-row", children: columns.map((col) => /* @__PURE__ */ jsx8("th", { className: "fractal-datatable-th", style: { width: col.width }, children: col.header }, col.key)) }) }),
472
- /* @__PURE__ */ jsx8("tbody", { children: data.map((row, rowIndex) => /* @__PURE__ */ jsx8(
517
+ return /* @__PURE__ */ jsx9("div", { className: cn("fractal-datatable", `fractal-datatable--${size}`, className), children: /* @__PURE__ */ jsxs8("div", { className: "fractal-datatable-surface", children: [
518
+ header && /* @__PURE__ */ jsx9("div", { className: "fractal-datatable-header", children: header }),
519
+ data.length === 0 ? /* @__PURE__ */ jsx9("div", { className: "fractal-datatable-empty", children: emptyMessage || "No data" }) : /* @__PURE__ */ jsxs8("table", { className: "fractal-datatable-table", children: [
520
+ /* @__PURE__ */ jsx9("thead", { children: /* @__PURE__ */ jsx9("tr", { className: "fractal-datatable-thead-row", children: columns.map((col) => /* @__PURE__ */ jsx9("th", { className: "fractal-datatable-th", style: { width: col.width }, children: col.header }, col.key)) }) }),
521
+ /* @__PURE__ */ jsx9("tbody", { children: data.map((row, rowIndex) => /* @__PURE__ */ jsx9(
473
522
  "tr",
474
523
  {
475
524
  className: "fractal-datatable-row",
476
- children: columns.map((col) => /* @__PURE__ */ jsx8("td", { className: "fractal-datatable-td", style: { width: col.width }, children: col.render(row, rowIndex) }, col.key))
525
+ children: columns.map((col) => /* @__PURE__ */ jsx9("td", { className: "fractal-datatable-td", style: { width: col.width }, children: col.render(row, rowIndex) }, col.key))
477
526
  },
478
527
  rowIndex
479
528
  )) })
@@ -484,7 +533,7 @@ function DataTable({ columns, data, emptyMessage, header, size = "md", className
484
533
  // src/components/Dropdown/Dropdown.tsx
485
534
  import * as React7 from "react";
486
535
  import { ChevronDown, ChevronUp } from "@mirror-physics/fractal-icons";
487
- import { Fragment as Fragment2, jsx as jsx9, jsxs as jsxs8 } from "react/jsx-runtime";
536
+ import { Fragment as Fragment2, jsx as jsx10, jsxs as jsxs9 } from "react/jsx-runtime";
488
537
  var MARGIN = 4;
489
538
  function Dropdown({
490
539
  items,
@@ -495,13 +544,15 @@ function Dropdown({
495
544
  align = "left",
496
545
  triggerContent,
497
546
  label,
547
+ iconOnly = false,
498
548
  noChevron = false,
499
549
  size = "md",
500
550
  triggerVariant = "outline",
501
551
  className,
502
552
  triggerClassName,
503
553
  panelClassName,
504
- closeOnSelect = true
554
+ closeOnSelect = true,
555
+ disabled = false
505
556
  }) {
506
557
  const [open, setOpen] = React7.useState(false);
507
558
  const triggerRef = React7.useRef(null);
@@ -559,12 +610,13 @@ function Dropdown({
559
610
  document.addEventListener("mousedown", handleClickOutside);
560
611
  return () => document.removeEventListener("mousedown", handleClickOutside);
561
612
  }, [open]);
562
- return /* @__PURE__ */ jsxs8("div", { className: cn("fractal-dropdown", className), children: [
563
- /* @__PURE__ */ jsxs8(
613
+ return /* @__PURE__ */ jsxs9("div", { className: cn("fractal-dropdown", className), children: [
614
+ /* @__PURE__ */ jsxs9(
564
615
  "button",
565
616
  {
566
617
  ref: triggerRef,
567
618
  type: "button",
619
+ disabled,
568
620
  "aria-haspopup": "listbox",
569
621
  "aria-expanded": open,
570
622
  "aria-label": triggerLabel,
@@ -573,18 +625,19 @@ function Dropdown({
573
625
  "fractal-dropdown-trigger",
574
626
  `fractal-dropdown-trigger--${size}`,
575
627
  `fractal-dropdown-trigger--${triggerVariant}`,
628
+ iconOnly && "fractal-dropdown-trigger--icon-only",
576
629
  triggerClassName
577
630
  ),
578
631
  children: [
579
- triggerContent != null ? triggerContent : /* @__PURE__ */ jsxs8(Fragment2, { children: [
580
- selected.icon != null && /* @__PURE__ */ jsx9("span", { className: "fractal-dropdown-icon fractal-dropdown-icon--trigger", children: selected.icon }),
581
- selected.description != null && (typeof selected.description !== "string" || selected.description !== "") && /* @__PURE__ */ jsx9("span", { className: "fractal-dropdown-selected-text", children: selected.description })
632
+ triggerContent != null ? triggerContent : /* @__PURE__ */ jsxs9(Fragment2, { children: [
633
+ selected.icon != null && /* @__PURE__ */ jsx10("span", { className: "fractal-dropdown-icon fractal-dropdown-icon--trigger", children: selected.icon }),
634
+ selected.description != null && (typeof selected.description !== "string" || selected.description !== "") && /* @__PURE__ */ jsx10("span", { className: "fractal-dropdown-selected-text", children: selected.description })
582
635
  ] }),
583
- !noChevron && /* @__PURE__ */ jsx9("span", { className: "fractal-dropdown-chevron", "aria-hidden": true, children: /* @__PURE__ */ jsx9(ChevronIcon, { size: size === "sm" ? 14 : 16 }) })
636
+ !noChevron && !iconOnly && /* @__PURE__ */ jsx10("span", { className: "fractal-dropdown-chevron", "aria-hidden": true, children: /* @__PURE__ */ jsx10(ChevronIcon, { size: size === "sm" ? 14 : 16 }) })
584
637
  ]
585
638
  }
586
639
  ),
587
- open && /* @__PURE__ */ jsxs8(
640
+ open && /* @__PURE__ */ jsxs9(
588
641
  "div",
589
642
  {
590
643
  ref: panelRef,
@@ -593,21 +646,21 @@ function Dropdown({
593
646
  className: cn("fractal-dropdown-panel", `fractal-dropdown-panel--${size}`, panelClassName),
594
647
  style: panelStyle,
595
648
  children: [
596
- label != null && /* @__PURE__ */ jsx9("div", { className: "fractal-dropdown-label", children: label }),
649
+ label != null && /* @__PURE__ */ jsx10("div", { className: "fractal-dropdown-label", children: label }),
597
650
  visibleItems.map((item, index) => {
598
- const topDivider = item.border === "top" ? /* @__PURE__ */ jsx9("div", { className: "fractal-dropdown-separator" }) : null;
599
- const bottomDivider = item.border === "bottom" ? /* @__PURE__ */ jsx9("div", { className: "fractal-dropdown-separator" }) : null;
651
+ const topDivider = item.border === "top" ? /* @__PURE__ */ jsx10("div", { className: "fractal-dropdown-separator" }) : null;
652
+ const bottomDivider = item.border === "bottom" ? /* @__PURE__ */ jsx10("div", { className: "fractal-dropdown-separator" }) : null;
600
653
  if (item.kind === "header") {
601
- return /* @__PURE__ */ jsxs8(React7.Fragment, { children: [
654
+ return /* @__PURE__ */ jsxs9(React7.Fragment, { children: [
602
655
  topDivider,
603
- /* @__PURE__ */ jsx9("div", { role: "presentation", className: "fractal-dropdown-label", children: item.description }),
656
+ /* @__PURE__ */ jsx10("div", { role: "presentation", className: "fractal-dropdown-label", children: item.description }),
604
657
  bottomDivider
605
658
  ] }, `header-${index}`);
606
659
  }
607
660
  const isSelected = item.value === selectedValue;
608
- return /* @__PURE__ */ jsxs8(React7.Fragment, { children: [
661
+ return /* @__PURE__ */ jsxs9(React7.Fragment, { children: [
609
662
  topDivider,
610
- /* @__PURE__ */ jsxs8(
663
+ /* @__PURE__ */ jsxs9(
611
664
  "button",
612
665
  {
613
666
  type: "button",
@@ -619,9 +672,9 @@ function Dropdown({
619
672
  },
620
673
  className: "fractal-dropdown-option",
621
674
  children: [
622
- item.icon != null && /* @__PURE__ */ jsx9("span", { className: "fractal-dropdown-icon", children: item.icon }),
623
- item.description != null && (typeof item.description !== "string" || item.description !== "") && /* @__PURE__ */ jsx9("span", { className: "fractal-dropdown-option-text", children: item.description }),
624
- item.trailing != null && /* @__PURE__ */ jsx9("span", { className: "fractal-dropdown-trailing", children: item.trailing })
675
+ item.icon != null && /* @__PURE__ */ jsx10("span", { className: "fractal-dropdown-icon", children: item.icon }),
676
+ item.description != null && (typeof item.description !== "string" || item.description !== "") && /* @__PURE__ */ jsx10("span", { className: "fractal-dropdown-option-text", children: item.description }),
677
+ item.trailing != null && /* @__PURE__ */ jsx10("span", { className: "fractal-dropdown-trailing", children: item.trailing })
625
678
  ]
626
679
  }
627
680
  ),
@@ -636,9 +689,9 @@ function Dropdown({
636
689
 
637
690
  // src/components/Link/Link.tsx
638
691
  import * as React8 from "react";
639
- import { jsx as jsx10 } from "react/jsx-runtime";
692
+ import { jsx as jsx11 } from "react/jsx-runtime";
640
693
  var Link = React8.forwardRef(
641
- ({ className, theme = "default", size = "md", ...props }, ref) => /* @__PURE__ */ jsx10(
694
+ ({ className, theme = "default", size = "md", ...props }, ref) => /* @__PURE__ */ jsx11(
642
695
  "a",
643
696
  {
644
697
  ref,
@@ -651,9 +704,9 @@ Link.displayName = "Link";
651
704
 
652
705
  // src/components/Chip/Chip.tsx
653
706
  import * as React9 from "react";
654
- import { jsx as jsx11 } from "react/jsx-runtime";
707
+ import { jsx as jsx12 } from "react/jsx-runtime";
655
708
  var Chip = React9.forwardRef(
656
- ({ className, tone = "neutral", ...props }, ref) => /* @__PURE__ */ jsx11(
709
+ ({ className, tone = "neutral", ...props }, ref) => /* @__PURE__ */ jsx12(
657
710
  "span",
658
711
  {
659
712
  ref,
@@ -664,35 +717,69 @@ var Chip = React9.forwardRef(
664
717
  );
665
718
  Chip.displayName = "Chip";
666
719
 
720
+ // src/components/Checkbox/Checkbox.tsx
721
+ import { Checkbox as CheckboxIcon, CheckboxChecked } from "@mirror-physics/fractal-icons";
722
+ import { jsx as jsx13 } from "react/jsx-runtime";
723
+ function Checkbox({ checked, onCheckedChange, className, disabled, onClick, onKeyDown, ...props }) {
724
+ return /* @__PURE__ */ jsx13(
725
+ "button",
726
+ {
727
+ ...props,
728
+ className: cn("fractal-checkbox", checked && "fractal-checkbox--checked", className),
729
+ type: "button",
730
+ role: "checkbox",
731
+ "aria-checked": checked,
732
+ disabled,
733
+ onClick: (event) => {
734
+ onClick?.(event);
735
+ if (!event.defaultPrevented) onCheckedChange(!checked);
736
+ },
737
+ onKeyDown: (event) => {
738
+ onKeyDown?.(event);
739
+ if (event.key === " " || event.key === "Enter") event.stopPropagation();
740
+ },
741
+ children: checked ? /* @__PURE__ */ jsx13(CheckboxChecked, { "aria-hidden": "true", size: 18 }) : /* @__PURE__ */ jsx13(CheckboxIcon, { "aria-hidden": "true", size: 18 })
742
+ }
743
+ );
744
+ }
745
+
667
746
  // src/components/SortableTable/SortableTable.tsx
668
747
  import { useMemo, useState as useState3 } from "react";
669
748
  import { ArrowUp, ArrowDown, ArrowsUpDown } from "@mirror-physics/fractal-icons";
670
- import { jsx as jsx12, jsxs as jsxs9 } from "react/jsx-runtime";
749
+ import { jsx as jsx14, jsxs as jsxs10 } from "react/jsx-runtime";
671
750
  function SortableTable({
672
751
  columns,
673
752
  data,
674
753
  emptyMessage,
675
754
  header,
676
755
  size = "md",
756
+ embedded = false,
677
757
  className,
758
+ defaultSort,
678
759
  defaultSortKey = void 0,
679
760
  defaultSortDirection = null,
680
761
  sortKey: controlledKey,
681
762
  sortDirection: controlledDirection,
682
763
  onSortChange,
683
- highlightRow
764
+ highlightRow,
765
+ onRowClick,
766
+ rowAction
684
767
  }) {
685
768
  const [internalKey, setInternalKey] = useState3(defaultSortKey ?? null);
686
769
  const [internalDirection, setInternalDirection] = useState3(defaultSortDirection);
687
770
  const isControlled = controlledKey !== void 0 && controlledDirection !== void 0;
688
771
  const activeKey = isControlled ? controlledKey : internalKey;
689
772
  const activeDirection = isControlled ? controlledDirection : internalDirection;
773
+ const baselineKey = defaultSort?.key ?? null;
774
+ const baselineDirection = defaultSort?.direction ?? null;
775
+ const effectiveKey = activeKey && activeDirection ? activeKey : baselineKey;
776
+ const effectiveDirection = activeKey && activeDirection ? activeDirection : baselineDirection;
690
777
  const sorted = useMemo(() => {
691
- if (!activeKey || !activeDirection) return data;
692
- const col = columns.find((c) => c.key === activeKey);
778
+ if (!effectiveKey || !effectiveDirection) return data;
779
+ const col = columns.find((c) => c.key === effectiveKey);
693
780
  if (!col) return data;
694
781
  const accessor = col.sortAccessor ?? ((row) => {
695
- const value = row[activeKey];
782
+ const value = row[effectiveKey];
696
783
  return typeof value === "string" || typeof value === "number" ? value : null;
697
784
  });
698
785
  const copy = [...data];
@@ -703,26 +790,28 @@ function SortableTable({
703
790
  if (av == null) return 1;
704
791
  if (bv == null) return -1;
705
792
  if (typeof av === "number" && typeof bv === "number") {
706
- return activeDirection === "asc" ? av - bv : bv - av;
793
+ return effectiveDirection === "asc" ? av - bv : bv - av;
707
794
  }
708
795
  const as = String(av).toLowerCase();
709
796
  const bs = String(bv).toLowerCase();
710
- if (as < bs) return activeDirection === "asc" ? -1 : 1;
711
- if (as > bs) return activeDirection === "asc" ? 1 : -1;
797
+ if (as < bs) return effectiveDirection === "asc" ? -1 : 1;
798
+ if (as > bs) return effectiveDirection === "asc" ? 1 : -1;
712
799
  return 0;
713
800
  });
714
801
  return copy;
715
- }, [data, columns, activeKey, activeDirection]);
802
+ }, [data, columns, effectiveKey, effectiveDirection]);
716
803
  const handleSortClick = (key) => {
717
804
  let nextDirection;
718
- if (activeKey !== key) {
805
+ if (!activeKey || !activeDirection) {
806
+ nextDirection = baselineKey === key && baselineDirection ? baselineDirection : "asc";
807
+ } else if (activeKey !== key) {
719
808
  nextDirection = "asc";
809
+ } else if (baselineKey === key && baselineDirection) {
810
+ nextDirection = activeDirection === baselineDirection ? baselineDirection === "asc" ? "desc" : "asc" : null;
720
811
  } else if (activeDirection === "asc") {
721
812
  nextDirection = "desc";
722
- } else if (activeDirection === "desc") {
723
- nextDirection = null;
724
813
  } else {
725
- nextDirection = "asc";
814
+ nextDirection = null;
726
815
  }
727
816
  const nextKey = nextDirection === null ? null : key;
728
817
  if (isControlled) {
@@ -733,13 +822,13 @@ function SortableTable({
733
822
  onSortChange?.(nextKey, nextDirection);
734
823
  }
735
824
  };
736
- return /* @__PURE__ */ jsx12("div", { className: cn("fractal-sortable-table", `fractal-sortable-table--${size}`, className), children: /* @__PURE__ */ jsxs9("div", { className: "fractal-sortable-table-surface", children: [
737
- header && /* @__PURE__ */ jsx12("div", { className: "fractal-sortable-table-header", children: header }),
738
- sorted.length === 0 ? /* @__PURE__ */ jsx12("div", { className: "fractal-sortable-table-empty", children: emptyMessage || "No data" }) : /* @__PURE__ */ jsxs9("table", { className: "fractal-sortable-table-table", children: [
739
- /* @__PURE__ */ jsx12("thead", { children: /* @__PURE__ */ jsx12("tr", { className: "fractal-sortable-table-thead-row", children: columns.map((col) => {
825
+ return /* @__PURE__ */ jsx14("div", { className: cn("fractal-sortable-table", `fractal-sortable-table--${size}`, embedded && "fractal-sortable-table--embedded", className), children: /* @__PURE__ */ jsxs10("div", { className: "fractal-sortable-table-surface", children: [
826
+ header && /* @__PURE__ */ jsx14("div", { className: "fractal-sortable-table-header", children: header }),
827
+ sorted.length === 0 ? /* @__PURE__ */ jsx14("div", { className: "fractal-sortable-table-empty", children: emptyMessage || "No data" }) : /* @__PURE__ */ jsxs10("table", { className: "fractal-sortable-table-table", children: [
828
+ /* @__PURE__ */ jsx14("thead", { children: /* @__PURE__ */ jsx14("tr", { className: "fractal-sortable-table-thead-row", children: columns.map((col) => {
740
829
  const isActive = activeKey === col.key && activeDirection !== null;
741
830
  if (!col.sortable) {
742
- return /* @__PURE__ */ jsx12(
831
+ return /* @__PURE__ */ jsx14(
743
832
  "th",
744
833
  {
745
834
  className: "fractal-sortable-table-th",
@@ -749,12 +838,12 @@ function SortableTable({
749
838
  col.key
750
839
  );
751
840
  }
752
- return /* @__PURE__ */ jsx12(
841
+ return /* @__PURE__ */ jsx14(
753
842
  "th",
754
843
  {
755
844
  className: "fractal-sortable-table-th fractal-sortable-table-th--sortable",
756
845
  style: { width: col.width },
757
- children: /* @__PURE__ */ jsxs9(
846
+ children: /* @__PURE__ */ jsxs10(
758
847
  "button",
759
848
  {
760
849
  type: "button",
@@ -765,8 +854,8 @@ function SortableTable({
765
854
  onClick: () => handleSortClick(col.key),
766
855
  "aria-sort": isActive ? activeDirection === "asc" ? "ascending" : "descending" : "none",
767
856
  children: [
768
- /* @__PURE__ */ jsx12("span", { children: col.header }),
769
- /* @__PURE__ */ jsx12("span", { className: "fractal-sortable-table-sort-icon", "aria-hidden": "true", children: isActive && activeDirection === "asc" ? /* @__PURE__ */ jsx12(ArrowUp, { size: 14 }) : isActive && activeDirection === "desc" ? /* @__PURE__ */ jsx12(ArrowDown, { size: 14 }) : /* @__PURE__ */ jsx12(ArrowsUpDown, { size: 14 }) })
857
+ /* @__PURE__ */ jsx14("span", { children: col.header }),
858
+ /* @__PURE__ */ jsx14("span", { className: "fractal-sortable-table-sort-icon", "aria-hidden": "true", children: isActive && activeDirection === "asc" ? /* @__PURE__ */ jsx14(ArrowUp, { size: 14 }) : isActive && activeDirection === "desc" ? /* @__PURE__ */ jsx14(ArrowDown, { size: 14 }) : /* @__PURE__ */ jsx14(ArrowsUpDown, { size: 14 }) })
770
859
  ]
771
860
  }
772
861
  )
@@ -774,21 +863,42 @@ function SortableTable({
774
863
  col.key
775
864
  );
776
865
  }) }) }),
777
- /* @__PURE__ */ jsx12("tbody", { children: sorted.map((row, rowIndex) => {
866
+ /* @__PURE__ */ jsx14("tbody", { children: sorted.map((row, rowIndex) => {
778
867
  const highlighted = highlightRow?.(row, rowIndex) ?? false;
779
- return /* @__PURE__ */ jsx12(
868
+ const action = rowAction ? {
869
+ label: rowAction.label(row, rowIndex),
870
+ icon: rowAction.icon(row, rowIndex)
871
+ } : null;
872
+ return /* @__PURE__ */ jsx14(
780
873
  "tr",
781
874
  {
782
875
  className: cn(
783
876
  "fractal-sortable-table-row",
784
- highlighted && "fractal-sortable-table-row--highlighted"
877
+ highlighted && "fractal-sortable-table-row--highlighted",
878
+ onRowClick && "fractal-sortable-table-row--interactive"
785
879
  ),
786
- children: columns.map((col) => /* @__PURE__ */ jsx12(
880
+ onClick: onRowClick ? () => onRowClick(row, rowIndex) : void 0,
881
+ onKeyDown: onRowClick ? (event) => {
882
+ if (event.key === "Enter" || event.key === " ") {
883
+ event.preventDefault();
884
+ onRowClick(row, rowIndex);
885
+ }
886
+ } : void 0,
887
+ "aria-label": action?.label,
888
+ role: onRowClick ? "button" : void 0,
889
+ tabIndex: onRowClick ? 0 : void 0,
890
+ children: columns.map((col, columnIndex) => /* @__PURE__ */ jsxs10(
787
891
  "td",
788
892
  {
789
- className: "fractal-sortable-table-td",
893
+ className: cn(
894
+ "fractal-sortable-table-td",
895
+ action && columnIndex === columns.length - 1 && "fractal-sortable-table-td--row-action"
896
+ ),
790
897
  style: { width: col.width, textAlign: col.align ?? "left" },
791
- children: col.render(row, rowIndex)
898
+ children: [
899
+ col.render(row, rowIndex),
900
+ action && columnIndex === columns.length - 1 && /* @__PURE__ */ jsx14("span", { className: "fractal-sortable-table-row-action", "aria-hidden": "true", children: /* @__PURE__ */ jsx14("span", { className: "fractal-sortable-table-row-action__icon", children: action.icon }) })
901
+ ]
792
902
  },
793
903
  col.key
794
904
  ))
@@ -801,23 +911,39 @@ function SortableTable({
801
911
  }
802
912
 
803
913
  // src/components/Tabs/Tabs.tsx
804
- import { useId, useRef as useRef3, useState as useState4 } from "react";
805
- import { jsx as jsx13, jsxs as jsxs10 } from "react/jsx-runtime";
914
+ import { useId as useId2, useLayoutEffect as useLayoutEffect2, useRef as useRef3, useState as useState4 } from "react";
915
+ import { jsx as jsx15, jsxs as jsxs11 } from "react/jsx-runtime";
806
916
  function Tabs({
807
917
  items,
808
918
  defaultValue,
809
919
  value: controlledValue,
810
920
  onValueChange,
811
921
  className,
812
- ariaLabel
922
+ ariaLabel,
923
+ divider = false
813
924
  }) {
814
- const reactId = useId();
925
+ const reactId = useId2();
815
926
  const [internalValue, setInternalValue] = useState4(
816
927
  defaultValue ?? items.find((i) => !i.disabled)?.id ?? items[0]?.id ?? ""
817
928
  );
818
929
  const isControlled = controlledValue !== void 0;
819
930
  const activeId = isControlled ? controlledValue : internalValue;
931
+ const tabListRef = useRef3(null);
820
932
  const tabRefs = useRef3({});
933
+ const [indicator, setIndicator] = useState4(null);
934
+ useLayoutEffect2(() => {
935
+ const updateIndicator = () => {
936
+ const activeTab = tabRefs.current[activeId];
937
+ if (!activeTab) return;
938
+ setIndicator({ offset: activeTab.offsetLeft, width: activeTab.offsetWidth });
939
+ };
940
+ updateIndicator();
941
+ const list = tabListRef.current;
942
+ if (!list || typeof ResizeObserver === "undefined") return;
943
+ const observer = new ResizeObserver(updateIndicator);
944
+ observer.observe(list);
945
+ return () => observer.disconnect();
946
+ }, [activeId, items.length]);
821
947
  const selectTab = (id) => {
822
948
  if (isControlled) {
823
949
  onValueChange?.(id);
@@ -827,6 +953,10 @@ function Tabs({
827
953
  }
828
954
  };
829
955
  const focusable = items.filter((i) => !i.disabled);
956
+ const indicatorStyle = indicator ? {
957
+ "--fractal-tabs-indicator-offset": `${indicator.offset}px`,
958
+ "--fractal-tabs-indicator-width": `${indicator.width}px`
959
+ } : void 0;
830
960
  const onKeyDown = (event) => {
831
961
  const currentIndex = focusable.findIndex((i) => i.id === activeId);
832
962
  if (currentIndex === -1) return;
@@ -852,41 +982,52 @@ function Tabs({
852
982
  selectTab(nextId2);
853
983
  requestAnimationFrame(() => tabRefs.current[nextId2]?.focus());
854
984
  };
855
- return /* @__PURE__ */ jsxs10("div", { className: cn("fractal-tabs", className), children: [
856
- /* @__PURE__ */ jsx13("div", { role: "tablist", "aria-label": ariaLabel, className: "fractal-tabs-list", children: items.map((item) => {
857
- const isActive = item.id === activeId;
858
- const tabId = `${reactId}-tab-${item.id}`;
859
- const panelId = `${reactId}-panel-${item.id}`;
860
- return /* @__PURE__ */ jsx13(
861
- "button",
862
- {
863
- ref: (el) => {
864
- tabRefs.current[item.id] = el;
865
- },
866
- id: tabId,
867
- role: "tab",
868
- type: "button",
869
- "aria-selected": isActive,
870
- "aria-controls": panelId,
871
- tabIndex: isActive ? 0 : -1,
872
- disabled: item.disabled,
873
- className: cn(
874
- "fractal-tabs-trigger",
875
- isActive && "fractal-tabs-trigger--active",
876
- item.disabled && "fractal-tabs-trigger--disabled"
877
- ),
878
- onClick: () => !item.disabled && selectTab(item.id),
879
- onKeyDown,
880
- children: item.label
881
- },
882
- item.id
883
- );
884
- }) }),
985
+ return /* @__PURE__ */ jsxs11("div", { className: cn("fractal-tabs", divider && "fractal-tabs--divider", className), children: [
986
+ /* @__PURE__ */ jsx15(
987
+ "div",
988
+ {
989
+ ref: tabListRef,
990
+ role: "tablist",
991
+ "aria-label": ariaLabel,
992
+ className: "fractal-tabs-list",
993
+ "data-indicator-ready": indicator ? "true" : void 0,
994
+ style: indicatorStyle,
995
+ children: items.map((item) => {
996
+ const isActive = item.id === activeId;
997
+ const tabId = `${reactId}-tab-${item.id}`;
998
+ const panelId = `${reactId}-panel-${item.id}`;
999
+ return /* @__PURE__ */ jsx15(
1000
+ "button",
1001
+ {
1002
+ ref: (el) => {
1003
+ tabRefs.current[item.id] = el;
1004
+ },
1005
+ id: tabId,
1006
+ role: "tab",
1007
+ type: "button",
1008
+ "aria-selected": isActive,
1009
+ "aria-controls": panelId,
1010
+ tabIndex: isActive ? 0 : -1,
1011
+ disabled: item.disabled,
1012
+ className: cn(
1013
+ "fractal-tabs-trigger",
1014
+ isActive && "fractal-tabs-trigger--active",
1015
+ item.disabled && "fractal-tabs-trigger--disabled"
1016
+ ),
1017
+ onClick: () => !item.disabled && selectTab(item.id),
1018
+ onKeyDown,
1019
+ children: item.label
1020
+ },
1021
+ item.id
1022
+ );
1023
+ })
1024
+ }
1025
+ ),
885
1026
  items.map((item) => {
886
1027
  const tabId = `${reactId}-tab-${item.id}`;
887
1028
  const panelId = `${reactId}-panel-${item.id}`;
888
1029
  const isActive = item.id === activeId;
889
- return /* @__PURE__ */ jsx13(
1030
+ return /* @__PURE__ */ jsx15(
890
1031
  "div",
891
1032
  {
892
1033
  id: panelId,
@@ -902,10 +1043,94 @@ function Tabs({
902
1043
  ] });
903
1044
  }
904
1045
 
1046
+ // src/components/ContentSwitcher/ContentSwitcher.tsx
1047
+ import * as React10 from "react";
1048
+ import { jsx as jsx16, jsxs as jsxs12 } from "react/jsx-runtime";
1049
+ function ContentSwitcher({ items, value, onValueChange, ariaLabel, fullWidth = false, className }) {
1050
+ const switcherRef = React10.useRef(null);
1051
+ const buttonRefs = React10.useRef([]);
1052
+ const activeIndex = items.findIndex((item) => item.value === value);
1053
+ React10.useLayoutEffect(() => {
1054
+ const switcher = switcherRef.current;
1055
+ const activeButton = buttonRefs.current[activeIndex];
1056
+ if (!switcher || !activeButton) {
1057
+ switcher?.removeAttribute("data-indicator-ready");
1058
+ return;
1059
+ }
1060
+ const updateIndicator = () => {
1061
+ const switcherRect = switcher.getBoundingClientRect();
1062
+ const activeButtonRect = activeButton.getBoundingClientRect();
1063
+ switcher.style.setProperty("--fractal-content-switcher-indicator-x", `${activeButtonRect.left - switcherRect.left - switcher.clientLeft}px`);
1064
+ switcher.style.setProperty("--fractal-content-switcher-indicator-y", `${activeButtonRect.top - switcherRect.top - switcher.clientTop}px`);
1065
+ switcher.style.setProperty("--fractal-content-switcher-indicator-width", `${activeButtonRect.width}px`);
1066
+ switcher.style.setProperty("--fractal-content-switcher-indicator-height", `${activeButtonRect.height}px`);
1067
+ switcher.setAttribute("data-indicator-ready", "true");
1068
+ };
1069
+ updateIndicator();
1070
+ if (typeof ResizeObserver === "undefined") return;
1071
+ const resizeObserver = new ResizeObserver(updateIndicator);
1072
+ resizeObserver.observe(switcher);
1073
+ buttonRefs.current.forEach((button) => {
1074
+ if (button) resizeObserver.observe(button);
1075
+ });
1076
+ return () => resizeObserver.disconnect();
1077
+ }, [activeIndex, items.length]);
1078
+ const moveFocus = (index, direction) => {
1079
+ let nextIndex = index;
1080
+ do {
1081
+ nextIndex = (nextIndex + direction + items.length) % items.length;
1082
+ } while (items[nextIndex]?.disabled && nextIndex !== index);
1083
+ if (!items[nextIndex]?.disabled) {
1084
+ buttonRefs.current[nextIndex]?.focus();
1085
+ onValueChange(items[nextIndex].value);
1086
+ }
1087
+ };
1088
+ return /* @__PURE__ */ jsxs12(
1089
+ "div",
1090
+ {
1091
+ className: cn("fractal-content-switcher", fullWidth && "fractal-content-switcher--full-width", className),
1092
+ role: "group",
1093
+ "aria-label": ariaLabel,
1094
+ ref: switcherRef,
1095
+ children: [
1096
+ /* @__PURE__ */ jsx16("span", { className: "fractal-content-switcher__indicator", "aria-hidden": true }),
1097
+ items.map((item, index) => {
1098
+ const active = item.value === value;
1099
+ return /* @__PURE__ */ jsx16(
1100
+ "button",
1101
+ {
1102
+ className: cn("fractal-content-switcher__item", active && "fractal-content-switcher__item--active"),
1103
+ type: "button",
1104
+ "aria-pressed": active,
1105
+ disabled: item.disabled,
1106
+ onClick: () => onValueChange(item.value),
1107
+ onKeyDown: (event) => {
1108
+ if (event.key === "ArrowRight") {
1109
+ event.preventDefault();
1110
+ moveFocus(index, 1);
1111
+ }
1112
+ if (event.key === "ArrowLeft") {
1113
+ event.preventDefault();
1114
+ moveFocus(index, -1);
1115
+ }
1116
+ },
1117
+ ref: (element) => {
1118
+ buttonRefs.current[index] = element;
1119
+ },
1120
+ children: item.label
1121
+ },
1122
+ item.value
1123
+ );
1124
+ })
1125
+ ]
1126
+ }
1127
+ );
1128
+ }
1129
+
905
1130
  // src/components/Disclosure/Disclosure.tsx
906
- import { useId as useId2, useState as useState5 } from "react";
1131
+ import { useId as useId3, useState as useState5 } from "react";
907
1132
  import { ChevronRight, ChevronDown as ChevronDown2 } from "@mirror-physics/fractal-icons";
908
- import { jsx as jsx14, jsxs as jsxs11 } from "react/jsx-runtime";
1133
+ import { jsx as jsx17, jsxs as jsxs13 } from "react/jsx-runtime";
909
1134
  function Disclosure({
910
1135
  title,
911
1136
  meta,
@@ -913,16 +1138,18 @@ function Disclosure({
913
1138
  defaultOpen = false,
914
1139
  open: controlledOpen,
915
1140
  onOpenChange,
1141
+ disabled = false,
916
1142
  className,
917
1143
  variant = "card"
918
1144
  }) {
919
- const reactId = useId2();
1145
+ const reactId = useId3();
920
1146
  const headerId = `${reactId}-header`;
921
1147
  const panelId = `${reactId}-panel`;
922
1148
  const [internalOpen, setInternalOpen] = useState5(defaultOpen);
923
1149
  const isControlled = controlledOpen !== void 0;
924
1150
  const isOpen = isControlled ? controlledOpen : internalOpen;
925
1151
  const toggle = () => {
1152
+ if (disabled) return;
926
1153
  const next = !isOpen;
927
1154
  if (isControlled) {
928
1155
  onOpenChange?.(next);
@@ -931,17 +1158,18 @@ function Disclosure({
931
1158
  onOpenChange?.(next);
932
1159
  }
933
1160
  };
934
- return /* @__PURE__ */ jsxs11(
1161
+ return /* @__PURE__ */ jsxs13(
935
1162
  "div",
936
1163
  {
937
1164
  className: cn(
938
1165
  "fractal-disclosure",
939
1166
  variant === "card" && "fractal-disclosure--card",
940
1167
  isOpen && "fractal-disclosure--open",
1168
+ disabled && "fractal-disclosure--disabled",
941
1169
  className
942
1170
  ),
943
1171
  children: [
944
- /* @__PURE__ */ jsxs11(
1172
+ /* @__PURE__ */ jsxs13(
945
1173
  "button",
946
1174
  {
947
1175
  type: "button",
@@ -949,15 +1177,16 @@ function Disclosure({
949
1177
  className: "fractal-disclosure-trigger",
950
1178
  "aria-expanded": isOpen,
951
1179
  "aria-controls": panelId,
1180
+ disabled,
952
1181
  onClick: toggle,
953
1182
  children: [
954
- /* @__PURE__ */ jsx14("span", { className: "fractal-disclosure-icon", "aria-hidden": "true", children: isOpen ? /* @__PURE__ */ jsx14(ChevronDown2, { size: 16 }) : /* @__PURE__ */ jsx14(ChevronRight, { size: 16 }) }),
955
- /* @__PURE__ */ jsx14("span", { className: "fractal-disclosure-title", children: title }),
956
- meta && /* @__PURE__ */ jsx14("span", { className: "fractal-disclosure-meta", children: meta })
1183
+ /* @__PURE__ */ jsx17("span", { className: "fractal-disclosure-icon", "aria-hidden": "true", children: isOpen ? /* @__PURE__ */ jsx17(ChevronDown2, { size: 16 }) : /* @__PURE__ */ jsx17(ChevronRight, { size: 16 }) }),
1184
+ /* @__PURE__ */ jsx17("span", { className: "fractal-disclosure-title", children: title }),
1185
+ meta && /* @__PURE__ */ jsx17("span", { className: "fractal-disclosure-meta", children: meta })
957
1186
  ]
958
1187
  }
959
1188
  ),
960
- /* @__PURE__ */ jsx14(
1189
+ /* @__PURE__ */ jsx17(
961
1190
  "div",
962
1191
  {
963
1192
  id: panelId,
@@ -965,7 +1194,7 @@ function Disclosure({
965
1194
  "aria-labelledby": headerId,
966
1195
  className: "fractal-disclosure-panel",
967
1196
  hidden: !isOpen,
968
- children: /* @__PURE__ */ jsx14("div", { className: "fractal-disclosure-panel-inner", children })
1197
+ children: /* @__PURE__ */ jsx17("div", { className: "fractal-disclosure-panel-inner", children })
969
1198
  }
970
1199
  )
971
1200
  ]
@@ -973,11 +1202,176 @@ function Disclosure({
973
1202
  );
974
1203
  }
975
1204
 
1205
+ // src/components/LatticeLoader/LatticeLoader.tsx
1206
+ import { jsx as jsx18 } from "react/jsx-runtime";
1207
+ var LATTICE_COUNT = 160;
1208
+ var WAVE_PERIOD = 80;
1209
+ function LatticeLoader({ className, label = "Loading", ...props }) {
1210
+ return /* @__PURE__ */ jsx18(
1211
+ "span",
1212
+ {
1213
+ "aria-label": label,
1214
+ className: cn("fractal-lattice-loader", className),
1215
+ role: "progressbar",
1216
+ ...props,
1217
+ children: Array.from({ length: LATTICE_COUNT }, (_, index) => /* @__PURE__ */ jsx18(
1218
+ "span",
1219
+ {
1220
+ "aria-hidden": "true",
1221
+ className: "fractal-lattice-loader__cell",
1222
+ style: { "--fractal-lattice-index": index % WAVE_PERIOD }
1223
+ },
1224
+ index
1225
+ ))
1226
+ }
1227
+ );
1228
+ }
1229
+
1230
+ // src/components/Ghost/Ghost.tsx
1231
+ import { jsx as jsx19, jsxs as jsxs14 } from "react/jsx-runtime";
1232
+ var toCssLength = (value) => typeof value === "number" ? `${value}px` : value;
1233
+ function Ghost({ className, width, height, radius, style, ...props }) {
1234
+ return /* @__PURE__ */ jsx19(
1235
+ "div",
1236
+ {
1237
+ "aria-hidden": "true",
1238
+ className: cn("fractal-ghost", className),
1239
+ style: {
1240
+ width: toCssLength(width),
1241
+ height: toCssLength(height),
1242
+ borderRadius: toCssLength(radius),
1243
+ ...style
1244
+ },
1245
+ ...props
1246
+ }
1247
+ );
1248
+ }
1249
+ function GhostLine({ className, size = "md", width = "100%", ...props }) {
1250
+ return /* @__PURE__ */ jsx19(Ghost, { className: cn("fractal-ghost-line", `fractal-ghost-line--${size}`, className), width, ...props });
1251
+ }
1252
+ function ButtonGhost({ className, size = "md", iconOnly = false, width, ...props }) {
1253
+ return /* @__PURE__ */ jsx19(Ghost, { className: cn("fractal-ghost-button", `fractal-ghost-button--${size}`, iconOnly && "fractal-ghost-button--icon-only", className), width, ...props });
1254
+ }
1255
+ function CardGhost({ className, lines = 3, showHeader = true, ...props }) {
1256
+ return /* @__PURE__ */ jsxs14("div", { "aria-hidden": "true", className: cn("fractal-ghost-card", className), ...props, children: [
1257
+ showHeader && /* @__PURE__ */ jsx19(GhostLine, { width: "38%" }),
1258
+ /* @__PURE__ */ jsx19("div", { className: "fractal-ghost-stack", children: Array.from({ length: lines }, (_, index) => /* @__PURE__ */ jsx19(GhostLine, { width: index === lines - 1 ? "62%" : "100%" }, index)) })
1259
+ ] });
1260
+ }
1261
+ function DataTableGhost({
1262
+ className,
1263
+ showHeader = true,
1264
+ headers,
1265
+ columns = 4,
1266
+ rows = 5,
1267
+ columnWidths,
1268
+ size = "md",
1269
+ ...props
1270
+ }) {
1271
+ const cells = Array.from({ length: columns }, (_, index) => index);
1272
+ return /* @__PURE__ */ jsx19("div", { "aria-busy": "true", "aria-label": "Loading table", className: cn("fractal-ghost-table", `fractal-ghost-table--${size}`, className), role: "status", ...props, children: /* @__PURE__ */ jsxs14("table", { className: "fractal-ghost-table__table", children: [
1273
+ (showHeader || headers) && /* @__PURE__ */ jsx19("thead", { children: /* @__PURE__ */ jsx19("tr", { className: "fractal-ghost-table__head-row", children: cells.map((index) => /* @__PURE__ */ jsx19("th", { style: { width: columnWidths?.[index] }, children: headers?.[index] ?? /* @__PURE__ */ jsx19(GhostLine, { size: "sm", width: index === columns - 1 ? "62%" : "76%" }) }, index)) }) }),
1274
+ /* @__PURE__ */ jsx19("tbody", { children: Array.from({ length: rows }, (_, rowIndex) => /* @__PURE__ */ jsx19("tr", { className: "fractal-ghost-table__row", children: cells.map((columnIndex) => /* @__PURE__ */ jsx19("td", { style: { width: columnWidths?.[columnIndex] }, children: /* @__PURE__ */ jsx19(GhostLine, { width: `${[72, 54, 82, 63, 46][(rowIndex + columnIndex) % 5]}%` }) }, columnIndex)) }, rowIndex)) })
1275
+ ] }) });
1276
+ }
1277
+ function InputGhost({ className, labelWidth = "26%", ...props }) {
1278
+ return /* @__PURE__ */ jsx19(FormFieldGhost, { className, labelWidth, ...props });
1279
+ }
1280
+ function TextareaGhost({ className, labelWidth = "26%", ...props }) {
1281
+ return /* @__PURE__ */ jsx19(FormFieldGhost, { className, labelWidth, multiline: true, ...props });
1282
+ }
1283
+ function LabelGhost({ className, width = "26%", ...props }) {
1284
+ return /* @__PURE__ */ jsx19(GhostLine, { className: cn("fractal-ghost-label", className), size: "sm", width, ...props });
1285
+ }
1286
+ function FormFieldGhost({ className, labelWidth = "26%", multiline = false, ...props }) {
1287
+ return /* @__PURE__ */ jsxs14("div", { "aria-hidden": "true", className: cn("fractal-ghost-field", className), ...props, children: [
1288
+ /* @__PURE__ */ jsx19(LabelGhost, { width: labelWidth }),
1289
+ /* @__PURE__ */ jsx19(Ghost, { className: cn("fractal-ghost-field__control", multiline && "fractal-ghost-field__control--multiline") })
1290
+ ] });
1291
+ }
1292
+ function AlertGhost({ className, ...props }) {
1293
+ return /* @__PURE__ */ jsxs14("div", { "aria-hidden": "true", className: cn("fractal-ghost-alert", className), ...props, children: [
1294
+ /* @__PURE__ */ jsx19(GhostLine, { width: "30%" }),
1295
+ /* @__PURE__ */ jsx19(GhostLine, { size: "sm", width: "78%" })
1296
+ ] });
1297
+ }
1298
+ function CheckboxGhost({ className, ...props }) {
1299
+ return /* @__PURE__ */ jsxs14("div", { "aria-hidden": "true", className: cn("fractal-ghost-choice", className), ...props, children: [
1300
+ /* @__PURE__ */ jsx19(Ghost, { className: "fractal-ghost-choice__control" }),
1301
+ /* @__PURE__ */ jsx19(GhostLine, { width: "42%" })
1302
+ ] });
1303
+ }
1304
+ var ToggleGhost = CheckboxGhost;
1305
+ function ChipGhost({ className, width = 74, ...props }) {
1306
+ return /* @__PURE__ */ jsx19(Ghost, { className: cn("fractal-ghost-chip", className), height: 24, width, ...props });
1307
+ }
1308
+ function LinkGhost({ className, width = 96, ...props }) {
1309
+ return /* @__PURE__ */ jsx19(GhostLine, { className: cn("fractal-ghost-link", className), size: "sm", width, ...props });
1310
+ }
1311
+ var TextButtonGhost = LinkGhost;
1312
+ function DropdownGhost({ className, ...props }) {
1313
+ return /* @__PURE__ */ jsxs14("div", { "aria-hidden": "true", className: cn("fractal-ghost-dropdown", className), ...props, children: [
1314
+ /* @__PURE__ */ jsx19(GhostLine, { width: "38%" }),
1315
+ /* @__PURE__ */ jsx19(Ghost, { className: "fractal-ghost-dropdown__chevron" })
1316
+ ] });
1317
+ }
1318
+ function ContentSwitcherGhost({ className, options = 3, ...props }) {
1319
+ return /* @__PURE__ */ jsx19("div", { "aria-hidden": "true", className: cn("fractal-ghost-switcher", className), ...props, children: Array.from({ length: options }, (_, index) => /* @__PURE__ */ jsx19(Ghost, {}, index)) });
1320
+ }
1321
+ var TabsGhost = ContentSwitcherGhost;
1322
+ function DisclosureGhost({ className, ...props }) {
1323
+ return /* @__PURE__ */ jsxs14("div", { "aria-hidden": "true", className: cn("fractal-ghost-disclosure", className), ...props, children: [
1324
+ /* @__PURE__ */ jsx19(Ghost, { className: "fractal-ghost-disclosure__icon" }),
1325
+ /* @__PURE__ */ jsx19(GhostLine, { width: "42%" })
1326
+ ] });
1327
+ }
1328
+ function PaginationGhost({ className, pages = 5, ...props }) {
1329
+ return /* @__PURE__ */ jsx19("div", { "aria-hidden": "true", className: cn("fractal-ghost-pagination", className), ...props, children: Array.from({ length: pages }, (_, index) => /* @__PURE__ */ jsx19(Ghost, {}, index)) });
1330
+ }
1331
+ function FileDropzoneGhost({ className, ...props }) {
1332
+ return /* @__PURE__ */ jsxs14("div", { "aria-hidden": "true", className: cn("fractal-ghost-dropzone", className), ...props, children: [
1333
+ /* @__PURE__ */ jsx19(Ghost, { className: "fractal-ghost-dropzone__icon" }),
1334
+ /* @__PURE__ */ jsx19(GhostLine, { width: "26%" }),
1335
+ /* @__PURE__ */ jsx19(GhostLine, { size: "sm", width: "46%" })
1336
+ ] });
1337
+ }
1338
+ function PromptCardGhost({ className, ...props }) {
1339
+ return /* @__PURE__ */ jsx19(CardGhost, { className: cn("fractal-ghost-prompt-card", className), lines: 4, ...props });
1340
+ }
1341
+ function SortableTableGhost(props) {
1342
+ return /* @__PURE__ */ jsx19(DataTableGhost, { ...props });
1343
+ }
1344
+ function SidebarGhost({ className, items = 6, ...props }) {
1345
+ return /* @__PURE__ */ jsxs14("aside", { "aria-hidden": "true", className: cn("fractal-ghost-sidebar", className), ...props, children: [
1346
+ /* @__PURE__ */ jsx19(Ghost, { className: "fractal-ghost-sidebar__brand" }),
1347
+ /* @__PURE__ */ jsx19("div", { className: "fractal-ghost-sidebar__items", children: Array.from({ length: items }, (_, index) => /* @__PURE__ */ jsxs14("div", { className: "fractal-ghost-sidebar__item", children: [
1348
+ /* @__PURE__ */ jsx19(Ghost, {}),
1349
+ /* @__PURE__ */ jsx19(GhostLine, { width: `${[54, 68, 44, 61][index % 4]}%` })
1350
+ ] }, index)) })
1351
+ ] });
1352
+ }
1353
+ function ModalGhost({ className, title, lines = 3, ...props }) {
1354
+ return /* @__PURE__ */ jsx19(SurfaceGhost, { className: cn("fractal-ghost-modal", className), title, lines, ...props });
1355
+ }
1356
+ function SidePanelGhost({ className, title, lines = 5, ...props }) {
1357
+ return /* @__PURE__ */ jsx19(SurfaceGhost, { className: cn("fractal-ghost-side-panel", className), title, lines, ...props });
1358
+ }
1359
+ function SurfaceGhost({ className, title, lines = 3, ...props }) {
1360
+ return /* @__PURE__ */ jsxs14("div", { "aria-hidden": "true", className: cn("fractal-ghost-surface", className), ...props, children: [
1361
+ /* @__PURE__ */ jsxs14("div", { className: "fractal-ghost-surface__header", children: [
1362
+ /* @__PURE__ */ jsx19(GhostLine, { width: "42%" }),
1363
+ /* @__PURE__ */ jsx19(Ghost, { className: "fractal-ghost-surface__close" })
1364
+ ] }),
1365
+ title && /* @__PURE__ */ jsx19("div", { className: "fractal-ghost-surface__title", children: title }),
1366
+ /* @__PURE__ */ jsx19("div", { className: "fractal-ghost-stack", children: Array.from({ length: lines }, (_, index) => /* @__PURE__ */ jsx19(GhostLine, { width: index === lines - 1 ? "64%" : "100%" }, index)) })
1367
+ ] });
1368
+ }
1369
+
976
1370
  // src/components/Sidebar/Sidebar.tsx
977
- import * as React10 from "react";
978
- import { jsx as jsx15, jsxs as jsxs12 } from "react/jsx-runtime";
979
- var Sidebar = React10.forwardRef(
980
- ({ className, width, style, ...props }, ref) => /* @__PURE__ */ jsx15(
1371
+ import * as React11 from "react";
1372
+ import { jsx as jsx20, jsxs as jsxs15 } from "react/jsx-runtime";
1373
+ var Sidebar = React11.forwardRef(
1374
+ ({ className, width, style, ...props }, ref) => /* @__PURE__ */ jsx20(
981
1375
  "aside",
982
1376
  {
983
1377
  ref,
@@ -988,24 +1382,47 @@ var Sidebar = React10.forwardRef(
988
1382
  )
989
1383
  );
990
1384
  Sidebar.displayName = "Sidebar";
991
- var SidebarHeader = React10.forwardRef(
992
- ({ className, ...props }, ref) => /* @__PURE__ */ jsx15("div", { ref, className: cn("fractal-sidebar__header", className), ...props })
1385
+ var SidebarHeader = React11.forwardRef(
1386
+ ({ className, ...props }, ref) => /* @__PURE__ */ jsx20("div", { ref, className: cn("fractal-sidebar__header", className), ...props })
993
1387
  );
994
1388
  SidebarHeader.displayName = "SidebarHeader";
995
- var SidebarBrand = React10.forwardRef(
996
- ({ className, type = "button", ...props }, ref) => /* @__PURE__ */ jsx15("button", { ref, type, className: cn("fractal-sidebar__brand", className), ...props })
1389
+ var SidebarBrand = React11.forwardRef(
1390
+ ({ className, type = "button", ...props }, ref) => /* @__PURE__ */ jsx20("button", { ref, type, className: cn("fractal-sidebar__brand", className), ...props })
997
1391
  );
998
1392
  SidebarBrand.displayName = "SidebarBrand";
999
- var SidebarNav = React10.forwardRef(
1000
- ({ className, ...props }, ref) => /* @__PURE__ */ jsx15("nav", { ref, className: cn("fractal-sidebar__nav", className), ...props })
1393
+ function SidebarAccountMenu({
1394
+ name,
1395
+ items,
1396
+ onSelect,
1397
+ triggerLabel = "Account menu",
1398
+ className
1399
+ }) {
1400
+ return /* @__PURE__ */ jsx20(
1401
+ Dropdown,
1402
+ {
1403
+ align: "left",
1404
+ className: cn("fractal-sidebar__account", className),
1405
+ items,
1406
+ onSelect,
1407
+ panelClassName: "fractal-sidebar__account-panel",
1408
+ selectedValue: "",
1409
+ triggerClassName: "fractal-sidebar__account-trigger",
1410
+ triggerContent: /* @__PURE__ */ jsx20("span", { className: "fractal-sidebar__account-name", children: name }),
1411
+ triggerLabel,
1412
+ triggerVariant: "tertiary"
1413
+ }
1414
+ );
1415
+ }
1416
+ var SidebarNav = React11.forwardRef(
1417
+ ({ className, ...props }, ref) => /* @__PURE__ */ jsx20("nav", { ref, className: cn("fractal-sidebar__nav", className), ...props })
1001
1418
  );
1002
1419
  SidebarNav.displayName = "SidebarNav";
1003
- var SidebarSection = React10.forwardRef(
1004
- ({ className, ...props }, ref) => /* @__PURE__ */ jsx15("div", { ref, className: cn("fractal-sidebar__section", className), ...props })
1420
+ var SidebarSection = React11.forwardRef(
1421
+ ({ className, ...props }, ref) => /* @__PURE__ */ jsx20("div", { ref, className: cn("fractal-sidebar__section", className), ...props })
1005
1422
  );
1006
1423
  SidebarSection.displayName = "SidebarSection";
1007
- var SidebarNavItem = React10.forwardRef(
1008
- ({ active = false, icon, endAdornment, className, type = "button", children, ...props }, ref) => /* @__PURE__ */ jsxs12(
1424
+ var SidebarNavItem = React11.forwardRef(
1425
+ ({ active = false, icon, endAdornment, className, type = "button", children, ...props }, ref) => /* @__PURE__ */ jsxs15(
1009
1426
  "button",
1010
1427
  {
1011
1428
  ref,
@@ -1015,51 +1432,313 @@ var SidebarNavItem = React10.forwardRef(
1015
1432
  "aria-current": active ? "page" : void 0,
1016
1433
  ...props,
1017
1434
  children: [
1018
- icon && /* @__PURE__ */ jsx15("span", { className: "fractal-sidebar__nav-item-icon", "aria-hidden": "true", children: icon }),
1019
- /* @__PURE__ */ jsx15("span", { className: "fractal-sidebar__nav-item-label", children }),
1020
- endAdornment && /* @__PURE__ */ jsx15("span", { className: "fractal-sidebar__nav-item-end", children: endAdornment })
1435
+ icon && /* @__PURE__ */ jsx20("span", { className: "fractal-sidebar__nav-item-icon", "aria-hidden": "true", children: icon }),
1436
+ /* @__PURE__ */ jsx20("span", { className: "fractal-sidebar__nav-item-label", children }),
1437
+ endAdornment && /* @__PURE__ */ jsx20("span", { className: "fractal-sidebar__nav-item-end", children: endAdornment })
1021
1438
  ]
1022
1439
  }
1023
1440
  )
1024
1441
  );
1025
1442
  SidebarNavItem.displayName = "SidebarNavItem";
1026
- var SidebarFooter = React10.forwardRef(
1027
- ({ className, ...props }, ref) => /* @__PURE__ */ jsx15("div", { ref, className: cn("fractal-sidebar__footer", className), ...props })
1443
+ var SidebarFooter = React11.forwardRef(
1444
+ ({ className, ...props }, ref) => /* @__PURE__ */ jsx20("div", { ref, className: cn("fractal-sidebar__footer", className), ...props })
1028
1445
  );
1029
1446
  SidebarFooter.displayName = "SidebarFooter";
1447
+
1448
+ // src/components/SidePanel/SidePanel.tsx
1449
+ import { createPortal as createPortal2 } from "react-dom";
1450
+ import { Close } from "@mirror-physics/fractal-icons";
1451
+ import { jsx as jsx21, jsxs as jsxs16 } from "react/jsx-runtime";
1452
+ function SidePanel({ open, onClose, title, description, children, footer, size = "md", loading = false, loadingLabel = "Loading", className }) {
1453
+ useEscapeDismiss(80, onClose, open);
1454
+ if (!open) return null;
1455
+ return createPortal2(
1456
+ /* @__PURE__ */ jsx21("div", { className: "fractal-side-panel-overlay", onMouseDown: onClose, children: /* @__PURE__ */ jsxs16(
1457
+ "aside",
1458
+ {
1459
+ className: cn("fractal-side-panel", `fractal-side-panel--${size}`, className),
1460
+ "aria-modal": "true",
1461
+ "aria-label": typeof title === "string" ? title : "Side panel",
1462
+ role: "dialog",
1463
+ onMouseDown: (event) => event.stopPropagation(),
1464
+ children: [
1465
+ /* @__PURE__ */ jsxs16("header", { className: "fractal-side-panel__header", children: [
1466
+ /* @__PURE__ */ jsxs16("div", { className: "fractal-side-panel__heading", children: [
1467
+ /* @__PURE__ */ jsx21("h2", { className: "fractal-side-panel__title", children: title }),
1468
+ description && /* @__PURE__ */ jsx21("p", { className: "fractal-side-panel__description", children: description })
1469
+ ] }),
1470
+ /* @__PURE__ */ jsx21("button", { type: "button", className: "fractal-side-panel__close", "aria-label": "Close panel", onClick: onClose, children: /* @__PURE__ */ jsx21(Close, { size: 18 }) })
1471
+ ] }),
1472
+ /* @__PURE__ */ jsx21("div", { "aria-busy": loading || void 0, className: cn("fractal-side-panel__body", loading && "fractal-side-panel__body--loading"), children: loading ? /* @__PURE__ */ jsx21(UILoader, { label: loadingLabel }) : children }),
1473
+ footer && /* @__PURE__ */ jsx21("footer", { className: "fractal-side-panel__footer", children: footer })
1474
+ ]
1475
+ }
1476
+ ) }),
1477
+ document.body
1478
+ );
1479
+ }
1480
+
1481
+ // src/components/Textarea/Textarea.tsx
1482
+ import * as React12 from "react";
1483
+ import { jsx as jsx22 } from "react/jsx-runtime";
1484
+ var Textarea = React12.forwardRef(
1485
+ ({ className, ...props }, ref) => /* @__PURE__ */ jsx22("textarea", { ref, className: cn("fractal-textarea", className), ...props })
1486
+ );
1487
+ Textarea.displayName = "Textarea";
1488
+
1489
+ // src/components/TextButton/TextButton.tsx
1490
+ import * as React13 from "react";
1491
+ import { jsx as jsx23, jsxs as jsxs17 } from "react/jsx-runtime";
1492
+ var TextButton = React13.forwardRef(
1493
+ ({ className, icon, iconPosition = "start", size = "md", tone = "primary", children, ...props }, ref) => /* @__PURE__ */ jsxs17(
1494
+ "button",
1495
+ {
1496
+ ref,
1497
+ className: cn("fractal-text-button", `fractal-text-button--${size}`, `fractal-text-button--${tone}`, className),
1498
+ type: "button",
1499
+ ...props,
1500
+ children: [
1501
+ icon && iconPosition === "start" && /* @__PURE__ */ jsx23("span", { className: "fractal-text-button__icon", "aria-hidden": "true", children: icon }),
1502
+ /* @__PURE__ */ jsx23("span", { children }),
1503
+ icon && iconPosition === "end" && /* @__PURE__ */ jsx23("span", { className: "fractal-text-button__icon", "aria-hidden": "true", children: icon })
1504
+ ]
1505
+ }
1506
+ )
1507
+ );
1508
+ TextButton.displayName = "TextButton";
1509
+
1510
+ // src/components/FileDropzone/FileDropzone.tsx
1511
+ import * as React14 from "react";
1512
+ import { Close as Close2, FileArrowUp } from "@mirror-physics/fractal-icons";
1513
+ import { jsx as jsx24, jsxs as jsxs18 } from "react/jsx-runtime";
1514
+ function FileDropzone({
1515
+ files,
1516
+ onFilesChange,
1517
+ accept,
1518
+ multiple = true,
1519
+ disabled = false,
1520
+ title = "Drop files here or click to browse",
1521
+ description = "Files stay attached to this record.",
1522
+ className
1523
+ }) {
1524
+ const inputId = React14.useId();
1525
+ const inputRef = React14.useRef(null);
1526
+ const [isDragging, setIsDragging] = React14.useState(false);
1527
+ const addFiles = (nextFiles) => {
1528
+ if (disabled) return;
1529
+ const additions = Array.from(nextFiles);
1530
+ if (additions.length === 0) return;
1531
+ onFilesChange(multiple ? [...files, ...additions] : additions.slice(0, 1));
1532
+ };
1533
+ return /* @__PURE__ */ jsxs18("div", { className: cn("fractal-file-dropzone", className), children: [
1534
+ /* @__PURE__ */ jsx24(
1535
+ "input",
1536
+ {
1537
+ ref: inputRef,
1538
+ id: inputId,
1539
+ className: "fractal-file-dropzone__input",
1540
+ type: "file",
1541
+ accept,
1542
+ multiple,
1543
+ disabled,
1544
+ onChange: (event) => {
1545
+ if (event.target.files) addFiles(event.target.files);
1546
+ event.target.value = "";
1547
+ }
1548
+ }
1549
+ ),
1550
+ /* @__PURE__ */ jsxs18(
1551
+ "button",
1552
+ {
1553
+ className: "fractal-file-dropzone__target",
1554
+ "data-dragging": isDragging || void 0,
1555
+ type: "button",
1556
+ disabled,
1557
+ onClick: () => inputRef.current?.click(),
1558
+ onDragEnter: (event) => {
1559
+ event.preventDefault();
1560
+ if (!disabled) setIsDragging(true);
1561
+ },
1562
+ onDragOver: (event) => event.preventDefault(),
1563
+ onDragLeave: (event) => {
1564
+ event.preventDefault();
1565
+ setIsDragging(false);
1566
+ },
1567
+ onDrop: (event) => {
1568
+ event.preventDefault();
1569
+ setIsDragging(false);
1570
+ addFiles(event.dataTransfer.files);
1571
+ },
1572
+ children: [
1573
+ /* @__PURE__ */ jsx24(FileArrowUp, { "aria-hidden": "true", size: 20 }),
1574
+ /* @__PURE__ */ jsxs18("span", { className: "fractal-file-dropzone__copy", children: [
1575
+ /* @__PURE__ */ jsx24("span", { className: "fractal-file-dropzone__title", children: title }),
1576
+ description && /* @__PURE__ */ jsx24("span", { className: "fractal-file-dropzone__description", children: description })
1577
+ ] })
1578
+ ]
1579
+ }
1580
+ ),
1581
+ files.length > 0 && /* @__PURE__ */ jsx24("ul", { className: "fractal-file-dropzone__files", "aria-label": "Attached files", children: files.map((file, index) => /* @__PURE__ */ jsxs18("li", { className: "fractal-file-dropzone__file", children: [
1582
+ /* @__PURE__ */ jsx24("span", { className: "fractal-file-dropzone__file-name", children: file.name }),
1583
+ /* @__PURE__ */ jsx24("span", { className: "fractal-file-dropzone__file-meta", children: formatFileSize(file.size) }),
1584
+ /* @__PURE__ */ jsx24(
1585
+ "button",
1586
+ {
1587
+ className: "fractal-file-dropzone__remove",
1588
+ type: "button",
1589
+ "aria-label": `Remove ${file.name}`,
1590
+ disabled,
1591
+ onClick: () => onFilesChange(files.filter((_, fileIndex) => fileIndex !== index)),
1592
+ children: /* @__PURE__ */ jsx24(Close2, { "aria-hidden": "true", size: 14 })
1593
+ }
1594
+ )
1595
+ ] }, `${file.name}-${file.size}-${index}`)) })
1596
+ ] });
1597
+ }
1598
+ function formatFileSize(bytes) {
1599
+ if (bytes < 1024) return `${bytes} B`;
1600
+ if (bytes < 1024 * 1024) return `${Math.round(bytes / 1024)} KB`;
1601
+ return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
1602
+ }
1603
+
1604
+ // src/components/Pagination/Pagination.tsx
1605
+ import { ArrowLeft, ArrowRight } from "@mirror-physics/fractal-icons";
1606
+ import { jsx as jsx25, jsxs as jsxs19 } from "react/jsx-runtime";
1607
+ function Pagination({
1608
+ page,
1609
+ pageSize,
1610
+ totalItems,
1611
+ onPageChange,
1612
+ pageSizeOptions,
1613
+ onPageSizeChange,
1614
+ ariaLabel = "Pagination",
1615
+ className
1616
+ }) {
1617
+ const totalPages = Math.max(1, Math.ceil(totalItems / pageSize));
1618
+ const currentPage = Math.min(Math.max(page, 1), totalPages);
1619
+ const start = totalItems === 0 ? 0 : (currentPage - 1) * pageSize + 1;
1620
+ const end = Math.min(currentPage * pageSize, totalItems);
1621
+ const canChangePageSize = pageSizeOptions != null && pageSizeOptions.length > 0 && onPageSizeChange != null;
1622
+ return /* @__PURE__ */ jsxs19("nav", { className: cn("fractal-pagination", className), "aria-label": ariaLabel, children: [
1623
+ /* @__PURE__ */ jsx25("span", { className: "fractal-pagination__summary", children: totalItems === 0 ? "No results" : `${start}-${end} of ${totalItems}` }),
1624
+ /* @__PURE__ */ jsxs19("div", { className: "fractal-pagination__controls", children: [
1625
+ canChangePageSize && /* @__PURE__ */ jsx25(
1626
+ Dropdown,
1627
+ {
1628
+ align: "right",
1629
+ className: "fractal-pagination__page-size",
1630
+ direction: "up",
1631
+ items: pageSizeOptions.map((option) => ({
1632
+ value: String(option),
1633
+ description: `${option} per page`
1634
+ })),
1635
+ onSelect: (value) => onPageSizeChange(Number(value)),
1636
+ selectedValue: String(pageSize),
1637
+ size: "sm",
1638
+ triggerLabel: "Results per page",
1639
+ triggerVariant: "tertiary"
1640
+ }
1641
+ ),
1642
+ /* @__PURE__ */ jsx25(
1643
+ "button",
1644
+ {
1645
+ className: "fractal-pagination__button",
1646
+ type: "button",
1647
+ "aria-label": "Previous page",
1648
+ title: "Previous page",
1649
+ disabled: currentPage === 1 || totalItems === 0,
1650
+ onClick: () => onPageChange(currentPage - 1),
1651
+ children: /* @__PURE__ */ jsx25(ArrowLeft, { "aria-hidden": "true", size: 16 })
1652
+ }
1653
+ ),
1654
+ /* @__PURE__ */ jsxs19("span", { className: "fractal-pagination__page", "aria-current": "page", children: [
1655
+ currentPage,
1656
+ " of ",
1657
+ totalPages
1658
+ ] }),
1659
+ /* @__PURE__ */ jsx25(
1660
+ "button",
1661
+ {
1662
+ className: "fractal-pagination__button",
1663
+ type: "button",
1664
+ "aria-label": "Next page",
1665
+ title: "Next page",
1666
+ disabled: currentPage === totalPages || totalItems === 0,
1667
+ onClick: () => onPageChange(currentPage + 1),
1668
+ children: /* @__PURE__ */ jsx25(ArrowRight, { "aria-hidden": "true", size: 16 })
1669
+ }
1670
+ )
1671
+ ] })
1672
+ ] });
1673
+ }
1030
1674
  export {
1031
1675
  Alert,
1032
1676
  AlertDescription,
1677
+ AlertGhost,
1033
1678
  AlertTitle,
1034
1679
  Button,
1680
+ ButtonGhost,
1035
1681
  Card,
1036
1682
  CardContent,
1037
1683
  CardDescription,
1038
1684
  CardFooter,
1685
+ CardGhost,
1039
1686
  CardHeader,
1040
1687
  CardTitle,
1688
+ Checkbox,
1689
+ CheckboxGhost,
1041
1690
  Chip,
1691
+ ChipGhost,
1692
+ ContentSwitcher,
1693
+ ContentSwitcherGhost,
1042
1694
  DataTable,
1695
+ DataTableGhost,
1043
1696
  Disclosure,
1697
+ DisclosureGhost,
1044
1698
  Dropdown,
1699
+ DropdownGhost,
1045
1700
  FeatureCard,
1701
+ FileDropzone,
1702
+ FileDropzoneGhost,
1046
1703
  FolderCard,
1704
+ Ghost,
1705
+ GhostLine,
1047
1706
  Input,
1707
+ InputGhost,
1048
1708
  Label,
1709
+ LabelGhost,
1710
+ LatticeLoader,
1049
1711
  Link,
1712
+ LinkGhost,
1050
1713
  Modal,
1714
+ ModalGhost,
1715
+ Pagination,
1716
+ PaginationGhost,
1051
1717
  PromptCard,
1718
+ PromptCardGhost,
1052
1719
  PromptGrid,
1720
+ SidePanel,
1721
+ SidePanelGhost,
1053
1722
  Sidebar,
1723
+ SidebarAccountMenu,
1054
1724
  SidebarBrand,
1055
1725
  SidebarFooter,
1726
+ SidebarGhost,
1056
1727
  SidebarHeader,
1057
1728
  SidebarNav,
1058
1729
  SidebarNavItem,
1059
1730
  SidebarSection,
1060
1731
  SortableTable,
1732
+ SortableTableGhost,
1061
1733
  Tabs,
1734
+ TabsGhost,
1735
+ TextButton,
1736
+ TextButtonGhost,
1737
+ Textarea,
1738
+ TextareaGhost,
1062
1739
  Toggle,
1740
+ ToggleGhost,
1741
+ UILoader,
1063
1742
  cn,
1064
1743
  useEscapeDismiss
1065
1744
  };