@mirror-physics/fractal-ui 0.2.0-next.10 → 0.2.0-next.15

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
@@ -664,16 +664,43 @@ var Chip = React9.forwardRef(
664
664
  );
665
665
  Chip.displayName = "Chip";
666
666
 
667
+ // src/components/Checkbox/Checkbox.tsx
668
+ import { Checkbox as CheckboxIcon, CheckboxChecked } from "@mirror-physics/fractal-icons";
669
+ import { jsx as jsx12 } from "react/jsx-runtime";
670
+ function Checkbox({ checked, onCheckedChange, className, disabled, onClick, onKeyDown, ...props }) {
671
+ return /* @__PURE__ */ jsx12(
672
+ "button",
673
+ {
674
+ ...props,
675
+ className: cn("fractal-checkbox", checked && "fractal-checkbox--checked", className),
676
+ type: "button",
677
+ role: "checkbox",
678
+ "aria-checked": checked,
679
+ disabled,
680
+ onClick: (event) => {
681
+ onClick?.(event);
682
+ if (!event.defaultPrevented) onCheckedChange(!checked);
683
+ },
684
+ onKeyDown: (event) => {
685
+ onKeyDown?.(event);
686
+ if (event.key === " " || event.key === "Enter") event.stopPropagation();
687
+ },
688
+ children: checked ? /* @__PURE__ */ jsx12(CheckboxChecked, { "aria-hidden": "true", size: 18 }) : /* @__PURE__ */ jsx12(CheckboxIcon, { "aria-hidden": "true", size: 18 })
689
+ }
690
+ );
691
+ }
692
+
667
693
  // src/components/SortableTable/SortableTable.tsx
668
694
  import { useMemo, useState as useState3 } from "react";
669
695
  import { ArrowUp, ArrowDown, ArrowsUpDown } from "@mirror-physics/fractal-icons";
670
- import { jsx as jsx12, jsxs as jsxs9 } from "react/jsx-runtime";
696
+ import { jsx as jsx13, jsxs as jsxs9 } from "react/jsx-runtime";
671
697
  function SortableTable({
672
698
  columns,
673
699
  data,
674
700
  emptyMessage,
675
701
  header,
676
702
  size = "md",
703
+ embedded = false,
677
704
  className,
678
705
  defaultSortKey = void 0,
679
706
  defaultSortDirection = null,
@@ -681,7 +708,8 @@ function SortableTable({
681
708
  sortDirection: controlledDirection,
682
709
  onSortChange,
683
710
  highlightRow,
684
- onRowClick
711
+ onRowClick,
712
+ rowAction
685
713
  }) {
686
714
  const [internalKey, setInternalKey] = useState3(defaultSortKey ?? null);
687
715
  const [internalDirection, setInternalDirection] = useState3(defaultSortDirection);
@@ -734,13 +762,13 @@ function SortableTable({
734
762
  onSortChange?.(nextKey, nextDirection);
735
763
  }
736
764
  };
737
- return /* @__PURE__ */ jsx12("div", { className: cn("fractal-sortable-table", `fractal-sortable-table--${size}`, className), children: /* @__PURE__ */ jsxs9("div", { className: "fractal-sortable-table-surface", children: [
738
- header && /* @__PURE__ */ jsx12("div", { className: "fractal-sortable-table-header", children: header }),
739
- sorted.length === 0 ? /* @__PURE__ */ jsx12("div", { className: "fractal-sortable-table-empty", children: emptyMessage || "No data" }) : /* @__PURE__ */ jsxs9("table", { className: "fractal-sortable-table-table", children: [
740
- /* @__PURE__ */ jsx12("thead", { children: /* @__PURE__ */ jsx12("tr", { className: "fractal-sortable-table-thead-row", children: columns.map((col) => {
765
+ return /* @__PURE__ */ jsx13("div", { className: cn("fractal-sortable-table", `fractal-sortable-table--${size}`, embedded && "fractal-sortable-table--embedded", className), children: /* @__PURE__ */ jsxs9("div", { className: "fractal-sortable-table-surface", children: [
766
+ header && /* @__PURE__ */ jsx13("div", { className: "fractal-sortable-table-header", children: header }),
767
+ sorted.length === 0 ? /* @__PURE__ */ jsx13("div", { className: "fractal-sortable-table-empty", children: emptyMessage || "No data" }) : /* @__PURE__ */ jsxs9("table", { className: "fractal-sortable-table-table", children: [
768
+ /* @__PURE__ */ jsx13("thead", { children: /* @__PURE__ */ jsx13("tr", { className: "fractal-sortable-table-thead-row", children: columns.map((col) => {
741
769
  const isActive = activeKey === col.key && activeDirection !== null;
742
770
  if (!col.sortable) {
743
- return /* @__PURE__ */ jsx12(
771
+ return /* @__PURE__ */ jsx13(
744
772
  "th",
745
773
  {
746
774
  className: "fractal-sortable-table-th",
@@ -750,7 +778,7 @@ function SortableTable({
750
778
  col.key
751
779
  );
752
780
  }
753
- return /* @__PURE__ */ jsx12(
781
+ return /* @__PURE__ */ jsx13(
754
782
  "th",
755
783
  {
756
784
  className: "fractal-sortable-table-th fractal-sortable-table-th--sortable",
@@ -766,8 +794,8 @@ function SortableTable({
766
794
  onClick: () => handleSortClick(col.key),
767
795
  "aria-sort": isActive ? activeDirection === "asc" ? "ascending" : "descending" : "none",
768
796
  children: [
769
- /* @__PURE__ */ jsx12("span", { children: col.header }),
770
- /* @__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 }) })
797
+ /* @__PURE__ */ jsx13("span", { children: col.header }),
798
+ /* @__PURE__ */ jsx13("span", { className: "fractal-sortable-table-sort-icon", "aria-hidden": "true", children: isActive && activeDirection === "asc" ? /* @__PURE__ */ jsx13(ArrowUp, { size: 14 }) : isActive && activeDirection === "desc" ? /* @__PURE__ */ jsx13(ArrowDown, { size: 14 }) : /* @__PURE__ */ jsx13(ArrowsUpDown, { size: 14 }) })
771
799
  ]
772
800
  }
773
801
  )
@@ -775,9 +803,13 @@ function SortableTable({
775
803
  col.key
776
804
  );
777
805
  }) }) }),
778
- /* @__PURE__ */ jsx12("tbody", { children: sorted.map((row, rowIndex) => {
806
+ /* @__PURE__ */ jsx13("tbody", { children: sorted.map((row, rowIndex) => {
779
807
  const highlighted = highlightRow?.(row, rowIndex) ?? false;
780
- return /* @__PURE__ */ jsx12(
808
+ const action = rowAction ? {
809
+ label: rowAction.label(row, rowIndex),
810
+ icon: rowAction.icon(row, rowIndex)
811
+ } : null;
812
+ return /* @__PURE__ */ jsx13(
781
813
  "tr",
782
814
  {
783
815
  className: cn(
@@ -792,14 +824,21 @@ function SortableTable({
792
824
  onRowClick(row, rowIndex);
793
825
  }
794
826
  } : void 0,
827
+ "aria-label": action?.label,
795
828
  role: onRowClick ? "button" : void 0,
796
829
  tabIndex: onRowClick ? 0 : void 0,
797
- children: columns.map((col) => /* @__PURE__ */ jsx12(
830
+ children: columns.map((col, columnIndex) => /* @__PURE__ */ jsxs9(
798
831
  "td",
799
832
  {
800
- className: "fractal-sortable-table-td",
833
+ className: cn(
834
+ "fractal-sortable-table-td",
835
+ action && columnIndex === columns.length - 1 && "fractal-sortable-table-td--row-action"
836
+ ),
801
837
  style: { width: col.width, textAlign: col.align ?? "left" },
802
- children: col.render(row, rowIndex)
838
+ children: [
839
+ col.render(row, rowIndex),
840
+ action && columnIndex === columns.length - 1 && /* @__PURE__ */ jsx13("span", { className: "fractal-sortable-table-row-action", "aria-hidden": "true", children: /* @__PURE__ */ jsx13("span", { className: "fractal-sortable-table-row-action__icon", children: action.icon }) })
841
+ ]
803
842
  },
804
843
  col.key
805
844
  ))
@@ -813,7 +852,7 @@ function SortableTable({
813
852
 
814
853
  // src/components/Tabs/Tabs.tsx
815
854
  import { useId, useRef as useRef3, useState as useState4 } from "react";
816
- import { jsx as jsx13, jsxs as jsxs10 } from "react/jsx-runtime";
855
+ import { jsx as jsx14, jsxs as jsxs10 } from "react/jsx-runtime";
817
856
  function Tabs({
818
857
  items,
819
858
  defaultValue,
@@ -864,11 +903,11 @@ function Tabs({
864
903
  requestAnimationFrame(() => tabRefs.current[nextId2]?.focus());
865
904
  };
866
905
  return /* @__PURE__ */ jsxs10("div", { className: cn("fractal-tabs", className), children: [
867
- /* @__PURE__ */ jsx13("div", { role: "tablist", "aria-label": ariaLabel, className: "fractal-tabs-list", children: items.map((item) => {
906
+ /* @__PURE__ */ jsx14("div", { role: "tablist", "aria-label": ariaLabel, className: "fractal-tabs-list", children: items.map((item) => {
868
907
  const isActive = item.id === activeId;
869
908
  const tabId = `${reactId}-tab-${item.id}`;
870
909
  const panelId = `${reactId}-panel-${item.id}`;
871
- return /* @__PURE__ */ jsx13(
910
+ return /* @__PURE__ */ jsx14(
872
911
  "button",
873
912
  {
874
913
  ref: (el) => {
@@ -897,7 +936,7 @@ function Tabs({
897
936
  const tabId = `${reactId}-tab-${item.id}`;
898
937
  const panelId = `${reactId}-panel-${item.id}`;
899
938
  const isActive = item.id === activeId;
900
- return /* @__PURE__ */ jsx13(
939
+ return /* @__PURE__ */ jsx14(
901
940
  "div",
902
941
  {
903
942
  id: panelId,
@@ -913,10 +952,55 @@ function Tabs({
913
952
  ] });
914
953
  }
915
954
 
955
+ // src/components/ContentSwitcher/ContentSwitcher.tsx
956
+ import * as React10 from "react";
957
+ import { jsx as jsx15 } from "react/jsx-runtime";
958
+ function ContentSwitcher({ items, value, onValueChange, ariaLabel, fullWidth = false, className }) {
959
+ const buttonRefs = React10.useRef([]);
960
+ const moveFocus = (index, direction) => {
961
+ let nextIndex = index;
962
+ do {
963
+ nextIndex = (nextIndex + direction + items.length) % items.length;
964
+ } while (items[nextIndex]?.disabled && nextIndex !== index);
965
+ if (!items[nextIndex]?.disabled) {
966
+ buttonRefs.current[nextIndex]?.focus();
967
+ onValueChange(items[nextIndex].value);
968
+ }
969
+ };
970
+ return /* @__PURE__ */ jsx15("div", { className: cn("fractal-content-switcher", fullWidth && "fractal-content-switcher--full-width", className), role: "group", "aria-label": ariaLabel, children: items.map((item, index) => {
971
+ const active = item.value === value;
972
+ return /* @__PURE__ */ jsx15(
973
+ "button",
974
+ {
975
+ className: cn("fractal-content-switcher__item", active && "fractal-content-switcher__item--active"),
976
+ type: "button",
977
+ "aria-pressed": active,
978
+ disabled: item.disabled,
979
+ onClick: () => onValueChange(item.value),
980
+ onKeyDown: (event) => {
981
+ if (event.key === "ArrowRight") {
982
+ event.preventDefault();
983
+ moveFocus(index, 1);
984
+ }
985
+ if (event.key === "ArrowLeft") {
986
+ event.preventDefault();
987
+ moveFocus(index, -1);
988
+ }
989
+ },
990
+ ref: (element) => {
991
+ buttonRefs.current[index] = element;
992
+ },
993
+ children: item.label
994
+ },
995
+ item.value
996
+ );
997
+ }) });
998
+ }
999
+
916
1000
  // src/components/Disclosure/Disclosure.tsx
917
1001
  import { useId as useId2, useState as useState5 } from "react";
918
1002
  import { ChevronRight, ChevronDown as ChevronDown2 } from "@mirror-physics/fractal-icons";
919
- import { jsx as jsx14, jsxs as jsxs11 } from "react/jsx-runtime";
1003
+ import { jsx as jsx16, jsxs as jsxs11 } from "react/jsx-runtime";
920
1004
  function Disclosure({
921
1005
  title,
922
1006
  meta,
@@ -924,6 +1008,7 @@ function Disclosure({
924
1008
  defaultOpen = false,
925
1009
  open: controlledOpen,
926
1010
  onOpenChange,
1011
+ disabled = false,
927
1012
  className,
928
1013
  variant = "card"
929
1014
  }) {
@@ -934,6 +1019,7 @@ function Disclosure({
934
1019
  const isControlled = controlledOpen !== void 0;
935
1020
  const isOpen = isControlled ? controlledOpen : internalOpen;
936
1021
  const toggle = () => {
1022
+ if (disabled) return;
937
1023
  const next = !isOpen;
938
1024
  if (isControlled) {
939
1025
  onOpenChange?.(next);
@@ -949,6 +1035,7 @@ function Disclosure({
949
1035
  "fractal-disclosure",
950
1036
  variant === "card" && "fractal-disclosure--card",
951
1037
  isOpen && "fractal-disclosure--open",
1038
+ disabled && "fractal-disclosure--disabled",
952
1039
  className
953
1040
  ),
954
1041
  children: [
@@ -960,15 +1047,16 @@ function Disclosure({
960
1047
  className: "fractal-disclosure-trigger",
961
1048
  "aria-expanded": isOpen,
962
1049
  "aria-controls": panelId,
1050
+ disabled,
963
1051
  onClick: toggle,
964
1052
  children: [
965
- /* @__PURE__ */ jsx14("span", { className: "fractal-disclosure-icon", "aria-hidden": "true", children: isOpen ? /* @__PURE__ */ jsx14(ChevronDown2, { size: 16 }) : /* @__PURE__ */ jsx14(ChevronRight, { size: 16 }) }),
966
- /* @__PURE__ */ jsx14("span", { className: "fractal-disclosure-title", children: title }),
967
- meta && /* @__PURE__ */ jsx14("span", { className: "fractal-disclosure-meta", children: meta })
1053
+ /* @__PURE__ */ jsx16("span", { className: "fractal-disclosure-icon", "aria-hidden": "true", children: isOpen ? /* @__PURE__ */ jsx16(ChevronDown2, { size: 16 }) : /* @__PURE__ */ jsx16(ChevronRight, { size: 16 }) }),
1054
+ /* @__PURE__ */ jsx16("span", { className: "fractal-disclosure-title", children: title }),
1055
+ meta && /* @__PURE__ */ jsx16("span", { className: "fractal-disclosure-meta", children: meta })
968
1056
  ]
969
1057
  }
970
1058
  ),
971
- /* @__PURE__ */ jsx14(
1059
+ /* @__PURE__ */ jsx16(
972
1060
  "div",
973
1061
  {
974
1062
  id: panelId,
@@ -976,7 +1064,7 @@ function Disclosure({
976
1064
  "aria-labelledby": headerId,
977
1065
  className: "fractal-disclosure-panel",
978
1066
  hidden: !isOpen,
979
- children: /* @__PURE__ */ jsx14("div", { className: "fractal-disclosure-panel-inner", children })
1067
+ children: /* @__PURE__ */ jsx16("div", { className: "fractal-disclosure-panel-inner", children })
980
1068
  }
981
1069
  )
982
1070
  ]
@@ -984,11 +1072,35 @@ function Disclosure({
984
1072
  );
985
1073
  }
986
1074
 
1075
+ // src/components/LatticeLoader/LatticeLoader.tsx
1076
+ import { jsx as jsx17 } from "react/jsx-runtime";
1077
+ var LATTICE_COUNT = 48;
1078
+ function LatticeLoader({ className, label = "Loading", ...props }) {
1079
+ return /* @__PURE__ */ jsx17(
1080
+ "span",
1081
+ {
1082
+ "aria-label": label,
1083
+ className: cn("fractal-lattice-loader", className),
1084
+ role: "progressbar",
1085
+ ...props,
1086
+ children: Array.from({ length: LATTICE_COUNT }, (_, index) => /* @__PURE__ */ jsx17(
1087
+ "span",
1088
+ {
1089
+ "aria-hidden": "true",
1090
+ className: "fractal-lattice-loader__cell",
1091
+ style: { "--fractal-lattice-index": index }
1092
+ },
1093
+ index
1094
+ ))
1095
+ }
1096
+ );
1097
+ }
1098
+
987
1099
  // src/components/Sidebar/Sidebar.tsx
988
- import * as React10 from "react";
989
- import { jsx as jsx15, jsxs as jsxs12 } from "react/jsx-runtime";
990
- var Sidebar = React10.forwardRef(
991
- ({ className, width, style, ...props }, ref) => /* @__PURE__ */ jsx15(
1100
+ import * as React11 from "react";
1101
+ import { jsx as jsx18, jsxs as jsxs12 } from "react/jsx-runtime";
1102
+ var Sidebar = React11.forwardRef(
1103
+ ({ className, width, style, ...props }, ref) => /* @__PURE__ */ jsx18(
992
1104
  "aside",
993
1105
  {
994
1106
  ref,
@@ -999,23 +1111,23 @@ var Sidebar = React10.forwardRef(
999
1111
  )
1000
1112
  );
1001
1113
  Sidebar.displayName = "Sidebar";
1002
- var SidebarHeader = React10.forwardRef(
1003
- ({ className, ...props }, ref) => /* @__PURE__ */ jsx15("div", { ref, className: cn("fractal-sidebar__header", className), ...props })
1114
+ var SidebarHeader = React11.forwardRef(
1115
+ ({ className, ...props }, ref) => /* @__PURE__ */ jsx18("div", { ref, className: cn("fractal-sidebar__header", className), ...props })
1004
1116
  );
1005
1117
  SidebarHeader.displayName = "SidebarHeader";
1006
- var SidebarBrand = React10.forwardRef(
1007
- ({ className, type = "button", ...props }, ref) => /* @__PURE__ */ jsx15("button", { ref, type, className: cn("fractal-sidebar__brand", className), ...props })
1118
+ var SidebarBrand = React11.forwardRef(
1119
+ ({ className, type = "button", ...props }, ref) => /* @__PURE__ */ jsx18("button", { ref, type, className: cn("fractal-sidebar__brand", className), ...props })
1008
1120
  );
1009
1121
  SidebarBrand.displayName = "SidebarBrand";
1010
- var SidebarNav = React10.forwardRef(
1011
- ({ className, ...props }, ref) => /* @__PURE__ */ jsx15("nav", { ref, className: cn("fractal-sidebar__nav", className), ...props })
1122
+ var SidebarNav = React11.forwardRef(
1123
+ ({ className, ...props }, ref) => /* @__PURE__ */ jsx18("nav", { ref, className: cn("fractal-sidebar__nav", className), ...props })
1012
1124
  );
1013
1125
  SidebarNav.displayName = "SidebarNav";
1014
- var SidebarSection = React10.forwardRef(
1015
- ({ className, ...props }, ref) => /* @__PURE__ */ jsx15("div", { ref, className: cn("fractal-sidebar__section", className), ...props })
1126
+ var SidebarSection = React11.forwardRef(
1127
+ ({ className, ...props }, ref) => /* @__PURE__ */ jsx18("div", { ref, className: cn("fractal-sidebar__section", className), ...props })
1016
1128
  );
1017
1129
  SidebarSection.displayName = "SidebarSection";
1018
- var SidebarNavItem = React10.forwardRef(
1130
+ var SidebarNavItem = React11.forwardRef(
1019
1131
  ({ active = false, icon, endAdornment, className, type = "button", children, ...props }, ref) => /* @__PURE__ */ jsxs12(
1020
1132
  "button",
1021
1133
  {
@@ -1026,28 +1138,28 @@ var SidebarNavItem = React10.forwardRef(
1026
1138
  "aria-current": active ? "page" : void 0,
1027
1139
  ...props,
1028
1140
  children: [
1029
- icon && /* @__PURE__ */ jsx15("span", { className: "fractal-sidebar__nav-item-icon", "aria-hidden": "true", children: icon }),
1030
- /* @__PURE__ */ jsx15("span", { className: "fractal-sidebar__nav-item-label", children }),
1031
- endAdornment && /* @__PURE__ */ jsx15("span", { className: "fractal-sidebar__nav-item-end", children: endAdornment })
1141
+ icon && /* @__PURE__ */ jsx18("span", { className: "fractal-sidebar__nav-item-icon", "aria-hidden": "true", children: icon }),
1142
+ /* @__PURE__ */ jsx18("span", { className: "fractal-sidebar__nav-item-label", children }),
1143
+ endAdornment && /* @__PURE__ */ jsx18("span", { className: "fractal-sidebar__nav-item-end", children: endAdornment })
1032
1144
  ]
1033
1145
  }
1034
1146
  )
1035
1147
  );
1036
1148
  SidebarNavItem.displayName = "SidebarNavItem";
1037
- var SidebarFooter = React10.forwardRef(
1038
- ({ className, ...props }, ref) => /* @__PURE__ */ jsx15("div", { ref, className: cn("fractal-sidebar__footer", className), ...props })
1149
+ var SidebarFooter = React11.forwardRef(
1150
+ ({ className, ...props }, ref) => /* @__PURE__ */ jsx18("div", { ref, className: cn("fractal-sidebar__footer", className), ...props })
1039
1151
  );
1040
1152
  SidebarFooter.displayName = "SidebarFooter";
1041
1153
 
1042
1154
  // src/components/SidePanel/SidePanel.tsx
1043
1155
  import { createPortal as createPortal2 } from "react-dom";
1044
1156
  import { Close } from "@mirror-physics/fractal-icons";
1045
- import { jsx as jsx16, jsxs as jsxs13 } from "react/jsx-runtime";
1157
+ import { jsx as jsx19, jsxs as jsxs13 } from "react/jsx-runtime";
1046
1158
  function SidePanel({ open, onClose, title, description, children, footer, size = "md", className }) {
1047
1159
  useEscapeDismiss(80, onClose, open);
1048
1160
  if (!open) return null;
1049
1161
  return createPortal2(
1050
- /* @__PURE__ */ jsx16("div", { className: "fractal-side-panel-overlay", onMouseDown: onClose, children: /* @__PURE__ */ jsxs13(
1162
+ /* @__PURE__ */ jsx19("div", { className: "fractal-side-panel-overlay", onMouseDown: onClose, children: /* @__PURE__ */ jsxs13(
1051
1163
  "aside",
1052
1164
  {
1053
1165
  className: cn("fractal-side-panel", `fractal-side-panel--${size}`, className),
@@ -1058,13 +1170,13 @@ function SidePanel({ open, onClose, title, description, children, footer, size =
1058
1170
  children: [
1059
1171
  /* @__PURE__ */ jsxs13("header", { className: "fractal-side-panel__header", children: [
1060
1172
  /* @__PURE__ */ jsxs13("div", { className: "fractal-side-panel__heading", children: [
1061
- /* @__PURE__ */ jsx16("h2", { className: "fractal-side-panel__title", children: title }),
1062
- description && /* @__PURE__ */ jsx16("p", { className: "fractal-side-panel__description", children: description })
1173
+ /* @__PURE__ */ jsx19("h2", { className: "fractal-side-panel__title", children: title }),
1174
+ description && /* @__PURE__ */ jsx19("p", { className: "fractal-side-panel__description", children: description })
1063
1175
  ] }),
1064
- /* @__PURE__ */ jsx16("button", { type: "button", className: "fractal-side-panel__close", "aria-label": "Close panel", onClick: onClose, children: /* @__PURE__ */ jsx16(Close, { size: 18 }) })
1176
+ /* @__PURE__ */ jsx19("button", { type: "button", className: "fractal-side-panel__close", "aria-label": "Close panel", onClick: onClose, children: /* @__PURE__ */ jsx19(Close, { size: 18 }) })
1065
1177
  ] }),
1066
- /* @__PURE__ */ jsx16("div", { className: "fractal-side-panel__body", children }),
1067
- footer && /* @__PURE__ */ jsx16("footer", { className: "fractal-side-panel__footer", children: footer })
1178
+ /* @__PURE__ */ jsx19("div", { className: "fractal-side-panel__body", children }),
1179
+ footer && /* @__PURE__ */ jsx19("footer", { className: "fractal-side-panel__footer", children: footer })
1068
1180
  ]
1069
1181
  }
1070
1182
  ) }),
@@ -1073,17 +1185,38 @@ function SidePanel({ open, onClose, title, description, children, footer, size =
1073
1185
  }
1074
1186
 
1075
1187
  // src/components/Textarea/Textarea.tsx
1076
- import * as React11 from "react";
1077
- import { jsx as jsx17 } from "react/jsx-runtime";
1078
- var Textarea = React11.forwardRef(
1079
- ({ className, ...props }, ref) => /* @__PURE__ */ jsx17("textarea", { ref, className: cn("fractal-textarea", className), ...props })
1188
+ import * as React12 from "react";
1189
+ import { jsx as jsx20 } from "react/jsx-runtime";
1190
+ var Textarea = React12.forwardRef(
1191
+ ({ className, ...props }, ref) => /* @__PURE__ */ jsx20("textarea", { ref, className: cn("fractal-textarea", className), ...props })
1080
1192
  );
1081
1193
  Textarea.displayName = "Textarea";
1082
1194
 
1195
+ // src/components/TextButton/TextButton.tsx
1196
+ import * as React13 from "react";
1197
+ import { jsx as jsx21, jsxs as jsxs14 } from "react/jsx-runtime";
1198
+ var TextButton = React13.forwardRef(
1199
+ ({ className, icon, iconPosition = "start", size = "md", tone = "primary", children, ...props }, ref) => /* @__PURE__ */ jsxs14(
1200
+ "button",
1201
+ {
1202
+ ref,
1203
+ className: cn("fractal-text-button", `fractal-text-button--${size}`, `fractal-text-button--${tone}`, className),
1204
+ type: "button",
1205
+ ...props,
1206
+ children: [
1207
+ icon && iconPosition === "start" && /* @__PURE__ */ jsx21("span", { className: "fractal-text-button__icon", "aria-hidden": "true", children: icon }),
1208
+ /* @__PURE__ */ jsx21("span", { children }),
1209
+ icon && iconPosition === "end" && /* @__PURE__ */ jsx21("span", { className: "fractal-text-button__icon", "aria-hidden": "true", children: icon })
1210
+ ]
1211
+ }
1212
+ )
1213
+ );
1214
+ TextButton.displayName = "TextButton";
1215
+
1083
1216
  // src/components/FileDropzone/FileDropzone.tsx
1084
- import * as React12 from "react";
1217
+ import * as React14 from "react";
1085
1218
  import { Close as Close2, FileArrowUp } from "@mirror-physics/fractal-icons";
1086
- import { jsx as jsx18, jsxs as jsxs14 } from "react/jsx-runtime";
1219
+ import { jsx as jsx22, jsxs as jsxs15 } from "react/jsx-runtime";
1087
1220
  function FileDropzone({
1088
1221
  files,
1089
1222
  onFilesChange,
@@ -1094,17 +1227,17 @@ function FileDropzone({
1094
1227
  description = "Files stay attached to this record.",
1095
1228
  className
1096
1229
  }) {
1097
- const inputId = React12.useId();
1098
- const inputRef = React12.useRef(null);
1099
- const [isDragging, setIsDragging] = React12.useState(false);
1230
+ const inputId = React14.useId();
1231
+ const inputRef = React14.useRef(null);
1232
+ const [isDragging, setIsDragging] = React14.useState(false);
1100
1233
  const addFiles = (nextFiles) => {
1101
1234
  if (disabled) return;
1102
1235
  const additions = Array.from(nextFiles);
1103
1236
  if (additions.length === 0) return;
1104
1237
  onFilesChange(multiple ? [...files, ...additions] : additions.slice(0, 1));
1105
1238
  };
1106
- return /* @__PURE__ */ jsxs14("div", { className: cn("fractal-file-dropzone", className), children: [
1107
- /* @__PURE__ */ jsx18(
1239
+ return /* @__PURE__ */ jsxs15("div", { className: cn("fractal-file-dropzone", className), children: [
1240
+ /* @__PURE__ */ jsx22(
1108
1241
  "input",
1109
1242
  {
1110
1243
  ref: inputRef,
@@ -1120,7 +1253,7 @@ function FileDropzone({
1120
1253
  }
1121
1254
  }
1122
1255
  ),
1123
- /* @__PURE__ */ jsxs14(
1256
+ /* @__PURE__ */ jsxs15(
1124
1257
  "button",
1125
1258
  {
1126
1259
  className: "fractal-file-dropzone__target",
@@ -1143,18 +1276,18 @@ function FileDropzone({
1143
1276
  addFiles(event.dataTransfer.files);
1144
1277
  },
1145
1278
  children: [
1146
- /* @__PURE__ */ jsx18(FileArrowUp, { "aria-hidden": "true", size: 20 }),
1147
- /* @__PURE__ */ jsxs14("span", { className: "fractal-file-dropzone__copy", children: [
1148
- /* @__PURE__ */ jsx18("span", { className: "fractal-file-dropzone__title", children: title }),
1149
- description && /* @__PURE__ */ jsx18("span", { className: "fractal-file-dropzone__description", children: description })
1279
+ /* @__PURE__ */ jsx22(FileArrowUp, { "aria-hidden": "true", size: 20 }),
1280
+ /* @__PURE__ */ jsxs15("span", { className: "fractal-file-dropzone__copy", children: [
1281
+ /* @__PURE__ */ jsx22("span", { className: "fractal-file-dropzone__title", children: title }),
1282
+ description && /* @__PURE__ */ jsx22("span", { className: "fractal-file-dropzone__description", children: description })
1150
1283
  ] })
1151
1284
  ]
1152
1285
  }
1153
1286
  ),
1154
- files.length > 0 && /* @__PURE__ */ jsx18("ul", { className: "fractal-file-dropzone__files", "aria-label": "Attached files", children: files.map((file, index) => /* @__PURE__ */ jsxs14("li", { className: "fractal-file-dropzone__file", children: [
1155
- /* @__PURE__ */ jsx18("span", { className: "fractal-file-dropzone__file-name", children: file.name }),
1156
- /* @__PURE__ */ jsx18("span", { className: "fractal-file-dropzone__file-meta", children: formatFileSize(file.size) }),
1157
- /* @__PURE__ */ jsx18(
1287
+ files.length > 0 && /* @__PURE__ */ jsx22("ul", { className: "fractal-file-dropzone__files", "aria-label": "Attached files", children: files.map((file, index) => /* @__PURE__ */ jsxs15("li", { className: "fractal-file-dropzone__file", children: [
1288
+ /* @__PURE__ */ jsx22("span", { className: "fractal-file-dropzone__file-name", children: file.name }),
1289
+ /* @__PURE__ */ jsx22("span", { className: "fractal-file-dropzone__file-meta", children: formatFileSize(file.size) }),
1290
+ /* @__PURE__ */ jsx22(
1158
1291
  "button",
1159
1292
  {
1160
1293
  className: "fractal-file-dropzone__remove",
@@ -1162,7 +1295,7 @@ function FileDropzone({
1162
1295
  "aria-label": `Remove ${file.name}`,
1163
1296
  disabled,
1164
1297
  onClick: () => onFilesChange(files.filter((_, fileIndex) => fileIndex !== index)),
1165
- children: /* @__PURE__ */ jsx18(Close2, { "aria-hidden": "true", size: 14 })
1298
+ children: /* @__PURE__ */ jsx22(Close2, { "aria-hidden": "true", size: 14 })
1166
1299
  }
1167
1300
  )
1168
1301
  ] }, `${file.name}-${file.size}-${index}`)) })
@@ -1173,6 +1306,77 @@ function formatFileSize(bytes) {
1173
1306
  if (bytes < 1024 * 1024) return `${Math.round(bytes / 1024)} KB`;
1174
1307
  return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
1175
1308
  }
1309
+
1310
+ // src/components/Pagination/Pagination.tsx
1311
+ import { ArrowLeft, ArrowRight } from "@mirror-physics/fractal-icons";
1312
+ import { jsx as jsx23, jsxs as jsxs16 } from "react/jsx-runtime";
1313
+ function Pagination({
1314
+ page,
1315
+ pageSize,
1316
+ totalItems,
1317
+ onPageChange,
1318
+ pageSizeOptions,
1319
+ onPageSizeChange,
1320
+ ariaLabel = "Pagination",
1321
+ className
1322
+ }) {
1323
+ const totalPages = Math.max(1, Math.ceil(totalItems / pageSize));
1324
+ const currentPage = Math.min(Math.max(page, 1), totalPages);
1325
+ const start = totalItems === 0 ? 0 : (currentPage - 1) * pageSize + 1;
1326
+ const end = Math.min(currentPage * pageSize, totalItems);
1327
+ const canChangePageSize = pageSizeOptions != null && pageSizeOptions.length > 0 && onPageSizeChange != null;
1328
+ return /* @__PURE__ */ jsxs16("nav", { className: cn("fractal-pagination", className), "aria-label": ariaLabel, children: [
1329
+ /* @__PURE__ */ jsx23("span", { className: "fractal-pagination__summary", children: totalItems === 0 ? "No results" : `${start}-${end} of ${totalItems}` }),
1330
+ /* @__PURE__ */ jsxs16("div", { className: "fractal-pagination__controls", children: [
1331
+ canChangePageSize && /* @__PURE__ */ jsx23(
1332
+ Dropdown,
1333
+ {
1334
+ align: "right",
1335
+ className: "fractal-pagination__page-size",
1336
+ direction: "up",
1337
+ items: pageSizeOptions.map((option) => ({
1338
+ value: String(option),
1339
+ description: `${option} per page`
1340
+ })),
1341
+ onSelect: (value) => onPageSizeChange(Number(value)),
1342
+ selectedValue: String(pageSize),
1343
+ size: "sm",
1344
+ triggerLabel: "Results per page",
1345
+ triggerVariant: "tertiary"
1346
+ }
1347
+ ),
1348
+ /* @__PURE__ */ jsx23(
1349
+ "button",
1350
+ {
1351
+ className: "fractal-pagination__button",
1352
+ type: "button",
1353
+ "aria-label": "Previous page",
1354
+ title: "Previous page",
1355
+ disabled: currentPage === 1 || totalItems === 0,
1356
+ onClick: () => onPageChange(currentPage - 1),
1357
+ children: /* @__PURE__ */ jsx23(ArrowLeft, { "aria-hidden": "true", size: 16 })
1358
+ }
1359
+ ),
1360
+ /* @__PURE__ */ jsxs16("span", { className: "fractal-pagination__page", "aria-current": "page", children: [
1361
+ currentPage,
1362
+ " of ",
1363
+ totalPages
1364
+ ] }),
1365
+ /* @__PURE__ */ jsx23(
1366
+ "button",
1367
+ {
1368
+ className: "fractal-pagination__button",
1369
+ type: "button",
1370
+ "aria-label": "Next page",
1371
+ title: "Next page",
1372
+ disabled: currentPage === totalPages || totalItems === 0,
1373
+ onClick: () => onPageChange(currentPage + 1),
1374
+ children: /* @__PURE__ */ jsx23(ArrowRight, { "aria-hidden": "true", size: 16 })
1375
+ }
1376
+ )
1377
+ ] })
1378
+ ] });
1379
+ }
1176
1380
  export {
1177
1381
  Alert,
1178
1382
  AlertDescription,
@@ -1184,7 +1388,9 @@ export {
1184
1388
  CardFooter,
1185
1389
  CardHeader,
1186
1390
  CardTitle,
1391
+ Checkbox,
1187
1392
  Chip,
1393
+ ContentSwitcher,
1188
1394
  DataTable,
1189
1395
  Disclosure,
1190
1396
  Dropdown,
@@ -1193,8 +1399,10 @@ export {
1193
1399
  FolderCard,
1194
1400
  Input,
1195
1401
  Label,
1402
+ LatticeLoader,
1196
1403
  Link,
1197
1404
  Modal,
1405
+ Pagination,
1198
1406
  PromptCard,
1199
1407
  PromptGrid,
1200
1408
  SidePanel,
@@ -1207,6 +1415,7 @@ export {
1207
1415
  SidebarSection,
1208
1416
  SortableTable,
1209
1417
  Tabs,
1418
+ TextButton,
1210
1419
  Textarea,
1211
1420
  Toggle,
1212
1421
  cn,