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

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
  ]
@@ -985,10 +1073,10 @@ function Disclosure({
985
1073
  }
986
1074
 
987
1075
  // 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(
1076
+ import * as React11 from "react";
1077
+ import { jsx as jsx17, jsxs as jsxs12 } from "react/jsx-runtime";
1078
+ var Sidebar = React11.forwardRef(
1079
+ ({ className, width, style, ...props }, ref) => /* @__PURE__ */ jsx17(
992
1080
  "aside",
993
1081
  {
994
1082
  ref,
@@ -999,23 +1087,23 @@ var Sidebar = React10.forwardRef(
999
1087
  )
1000
1088
  );
1001
1089
  Sidebar.displayName = "Sidebar";
1002
- var SidebarHeader = React10.forwardRef(
1003
- ({ className, ...props }, ref) => /* @__PURE__ */ jsx15("div", { ref, className: cn("fractal-sidebar__header", className), ...props })
1090
+ var SidebarHeader = React11.forwardRef(
1091
+ ({ className, ...props }, ref) => /* @__PURE__ */ jsx17("div", { ref, className: cn("fractal-sidebar__header", className), ...props })
1004
1092
  );
1005
1093
  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 })
1094
+ var SidebarBrand = React11.forwardRef(
1095
+ ({ className, type = "button", ...props }, ref) => /* @__PURE__ */ jsx17("button", { ref, type, className: cn("fractal-sidebar__brand", className), ...props })
1008
1096
  );
1009
1097
  SidebarBrand.displayName = "SidebarBrand";
1010
- var SidebarNav = React10.forwardRef(
1011
- ({ className, ...props }, ref) => /* @__PURE__ */ jsx15("nav", { ref, className: cn("fractal-sidebar__nav", className), ...props })
1098
+ var SidebarNav = React11.forwardRef(
1099
+ ({ className, ...props }, ref) => /* @__PURE__ */ jsx17("nav", { ref, className: cn("fractal-sidebar__nav", className), ...props })
1012
1100
  );
1013
1101
  SidebarNav.displayName = "SidebarNav";
1014
- var SidebarSection = React10.forwardRef(
1015
- ({ className, ...props }, ref) => /* @__PURE__ */ jsx15("div", { ref, className: cn("fractal-sidebar__section", className), ...props })
1102
+ var SidebarSection = React11.forwardRef(
1103
+ ({ className, ...props }, ref) => /* @__PURE__ */ jsx17("div", { ref, className: cn("fractal-sidebar__section", className), ...props })
1016
1104
  );
1017
1105
  SidebarSection.displayName = "SidebarSection";
1018
- var SidebarNavItem = React10.forwardRef(
1106
+ var SidebarNavItem = React11.forwardRef(
1019
1107
  ({ active = false, icon, endAdornment, className, type = "button", children, ...props }, ref) => /* @__PURE__ */ jsxs12(
1020
1108
  "button",
1021
1109
  {
@@ -1026,28 +1114,28 @@ var SidebarNavItem = React10.forwardRef(
1026
1114
  "aria-current": active ? "page" : void 0,
1027
1115
  ...props,
1028
1116
  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 })
1117
+ icon && /* @__PURE__ */ jsx17("span", { className: "fractal-sidebar__nav-item-icon", "aria-hidden": "true", children: icon }),
1118
+ /* @__PURE__ */ jsx17("span", { className: "fractal-sidebar__nav-item-label", children }),
1119
+ endAdornment && /* @__PURE__ */ jsx17("span", { className: "fractal-sidebar__nav-item-end", children: endAdornment })
1032
1120
  ]
1033
1121
  }
1034
1122
  )
1035
1123
  );
1036
1124
  SidebarNavItem.displayName = "SidebarNavItem";
1037
- var SidebarFooter = React10.forwardRef(
1038
- ({ className, ...props }, ref) => /* @__PURE__ */ jsx15("div", { ref, className: cn("fractal-sidebar__footer", className), ...props })
1125
+ var SidebarFooter = React11.forwardRef(
1126
+ ({ className, ...props }, ref) => /* @__PURE__ */ jsx17("div", { ref, className: cn("fractal-sidebar__footer", className), ...props })
1039
1127
  );
1040
1128
  SidebarFooter.displayName = "SidebarFooter";
1041
1129
 
1042
1130
  // src/components/SidePanel/SidePanel.tsx
1043
1131
  import { createPortal as createPortal2 } from "react-dom";
1044
1132
  import { Close } from "@mirror-physics/fractal-icons";
1045
- import { jsx as jsx16, jsxs as jsxs13 } from "react/jsx-runtime";
1133
+ import { jsx as jsx18, jsxs as jsxs13 } from "react/jsx-runtime";
1046
1134
  function SidePanel({ open, onClose, title, description, children, footer, size = "md", className }) {
1047
1135
  useEscapeDismiss(80, onClose, open);
1048
1136
  if (!open) return null;
1049
1137
  return createPortal2(
1050
- /* @__PURE__ */ jsx16("div", { className: "fractal-side-panel-overlay", onMouseDown: onClose, children: /* @__PURE__ */ jsxs13(
1138
+ /* @__PURE__ */ jsx18("div", { className: "fractal-side-panel-overlay", onMouseDown: onClose, children: /* @__PURE__ */ jsxs13(
1051
1139
  "aside",
1052
1140
  {
1053
1141
  className: cn("fractal-side-panel", `fractal-side-panel--${size}`, className),
@@ -1058,13 +1146,13 @@ function SidePanel({ open, onClose, title, description, children, footer, size =
1058
1146
  children: [
1059
1147
  /* @__PURE__ */ jsxs13("header", { className: "fractal-side-panel__header", children: [
1060
1148
  /* @__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 })
1149
+ /* @__PURE__ */ jsx18("h2", { className: "fractal-side-panel__title", children: title }),
1150
+ description && /* @__PURE__ */ jsx18("p", { className: "fractal-side-panel__description", children: description })
1063
1151
  ] }),
1064
- /* @__PURE__ */ jsx16("button", { type: "button", className: "fractal-side-panel__close", "aria-label": "Close panel", onClick: onClose, children: /* @__PURE__ */ jsx16(Close, { size: 18 }) })
1152
+ /* @__PURE__ */ jsx18("button", { type: "button", className: "fractal-side-panel__close", "aria-label": "Close panel", onClick: onClose, children: /* @__PURE__ */ jsx18(Close, { size: 18 }) })
1065
1153
  ] }),
1066
- /* @__PURE__ */ jsx16("div", { className: "fractal-side-panel__body", children }),
1067
- footer && /* @__PURE__ */ jsx16("footer", { className: "fractal-side-panel__footer", children: footer })
1154
+ /* @__PURE__ */ jsx18("div", { className: "fractal-side-panel__body", children }),
1155
+ footer && /* @__PURE__ */ jsx18("footer", { className: "fractal-side-panel__footer", children: footer })
1068
1156
  ]
1069
1157
  }
1070
1158
  ) }),
@@ -1073,17 +1161,38 @@ function SidePanel({ open, onClose, title, description, children, footer, size =
1073
1161
  }
1074
1162
 
1075
1163
  // 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 })
1164
+ import * as React12 from "react";
1165
+ import { jsx as jsx19 } from "react/jsx-runtime";
1166
+ var Textarea = React12.forwardRef(
1167
+ ({ className, ...props }, ref) => /* @__PURE__ */ jsx19("textarea", { ref, className: cn("fractal-textarea", className), ...props })
1080
1168
  );
1081
1169
  Textarea.displayName = "Textarea";
1082
1170
 
1171
+ // src/components/TextButton/TextButton.tsx
1172
+ import * as React13 from "react";
1173
+ import { jsx as jsx20, jsxs as jsxs14 } from "react/jsx-runtime";
1174
+ var TextButton = React13.forwardRef(
1175
+ ({ className, icon, iconPosition = "start", size = "md", tone = "primary", children, ...props }, ref) => /* @__PURE__ */ jsxs14(
1176
+ "button",
1177
+ {
1178
+ ref,
1179
+ className: cn("fractal-text-button", `fractal-text-button--${size}`, `fractal-text-button--${tone}`, className),
1180
+ type: "button",
1181
+ ...props,
1182
+ children: [
1183
+ icon && iconPosition === "start" && /* @__PURE__ */ jsx20("span", { className: "fractal-text-button__icon", "aria-hidden": "true", children: icon }),
1184
+ /* @__PURE__ */ jsx20("span", { children }),
1185
+ icon && iconPosition === "end" && /* @__PURE__ */ jsx20("span", { className: "fractal-text-button__icon", "aria-hidden": "true", children: icon })
1186
+ ]
1187
+ }
1188
+ )
1189
+ );
1190
+ TextButton.displayName = "TextButton";
1191
+
1083
1192
  // src/components/FileDropzone/FileDropzone.tsx
1084
- import * as React12 from "react";
1193
+ import * as React14 from "react";
1085
1194
  import { Close as Close2, FileArrowUp } from "@mirror-physics/fractal-icons";
1086
- import { jsx as jsx18, jsxs as jsxs14 } from "react/jsx-runtime";
1195
+ import { jsx as jsx21, jsxs as jsxs15 } from "react/jsx-runtime";
1087
1196
  function FileDropzone({
1088
1197
  files,
1089
1198
  onFilesChange,
@@ -1094,17 +1203,17 @@ function FileDropzone({
1094
1203
  description = "Files stay attached to this record.",
1095
1204
  className
1096
1205
  }) {
1097
- const inputId = React12.useId();
1098
- const inputRef = React12.useRef(null);
1099
- const [isDragging, setIsDragging] = React12.useState(false);
1206
+ const inputId = React14.useId();
1207
+ const inputRef = React14.useRef(null);
1208
+ const [isDragging, setIsDragging] = React14.useState(false);
1100
1209
  const addFiles = (nextFiles) => {
1101
1210
  if (disabled) return;
1102
1211
  const additions = Array.from(nextFiles);
1103
1212
  if (additions.length === 0) return;
1104
1213
  onFilesChange(multiple ? [...files, ...additions] : additions.slice(0, 1));
1105
1214
  };
1106
- return /* @__PURE__ */ jsxs14("div", { className: cn("fractal-file-dropzone", className), children: [
1107
- /* @__PURE__ */ jsx18(
1215
+ return /* @__PURE__ */ jsxs15("div", { className: cn("fractal-file-dropzone", className), children: [
1216
+ /* @__PURE__ */ jsx21(
1108
1217
  "input",
1109
1218
  {
1110
1219
  ref: inputRef,
@@ -1120,7 +1229,7 @@ function FileDropzone({
1120
1229
  }
1121
1230
  }
1122
1231
  ),
1123
- /* @__PURE__ */ jsxs14(
1232
+ /* @__PURE__ */ jsxs15(
1124
1233
  "button",
1125
1234
  {
1126
1235
  className: "fractal-file-dropzone__target",
@@ -1143,18 +1252,18 @@ function FileDropzone({
1143
1252
  addFiles(event.dataTransfer.files);
1144
1253
  },
1145
1254
  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 })
1255
+ /* @__PURE__ */ jsx21(FileArrowUp, { "aria-hidden": "true", size: 20 }),
1256
+ /* @__PURE__ */ jsxs15("span", { className: "fractal-file-dropzone__copy", children: [
1257
+ /* @__PURE__ */ jsx21("span", { className: "fractal-file-dropzone__title", children: title }),
1258
+ description && /* @__PURE__ */ jsx21("span", { className: "fractal-file-dropzone__description", children: description })
1150
1259
  ] })
1151
1260
  ]
1152
1261
  }
1153
1262
  ),
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(
1263
+ files.length > 0 && /* @__PURE__ */ jsx21("ul", { className: "fractal-file-dropzone__files", "aria-label": "Attached files", children: files.map((file, index) => /* @__PURE__ */ jsxs15("li", { className: "fractal-file-dropzone__file", children: [
1264
+ /* @__PURE__ */ jsx21("span", { className: "fractal-file-dropzone__file-name", children: file.name }),
1265
+ /* @__PURE__ */ jsx21("span", { className: "fractal-file-dropzone__file-meta", children: formatFileSize(file.size) }),
1266
+ /* @__PURE__ */ jsx21(
1158
1267
  "button",
1159
1268
  {
1160
1269
  className: "fractal-file-dropzone__remove",
@@ -1162,7 +1271,7 @@ function FileDropzone({
1162
1271
  "aria-label": `Remove ${file.name}`,
1163
1272
  disabled,
1164
1273
  onClick: () => onFilesChange(files.filter((_, fileIndex) => fileIndex !== index)),
1165
- children: /* @__PURE__ */ jsx18(Close2, { "aria-hidden": "true", size: 14 })
1274
+ children: /* @__PURE__ */ jsx21(Close2, { "aria-hidden": "true", size: 14 })
1166
1275
  }
1167
1276
  )
1168
1277
  ] }, `${file.name}-${file.size}-${index}`)) })
@@ -1173,6 +1282,77 @@ function formatFileSize(bytes) {
1173
1282
  if (bytes < 1024 * 1024) return `${Math.round(bytes / 1024)} KB`;
1174
1283
  return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
1175
1284
  }
1285
+
1286
+ // src/components/Pagination/Pagination.tsx
1287
+ import { ArrowLeft, ArrowRight } from "@mirror-physics/fractal-icons";
1288
+ import { jsx as jsx22, jsxs as jsxs16 } from "react/jsx-runtime";
1289
+ function Pagination({
1290
+ page,
1291
+ pageSize,
1292
+ totalItems,
1293
+ onPageChange,
1294
+ pageSizeOptions,
1295
+ onPageSizeChange,
1296
+ ariaLabel = "Pagination",
1297
+ className
1298
+ }) {
1299
+ const totalPages = Math.max(1, Math.ceil(totalItems / pageSize));
1300
+ const currentPage = Math.min(Math.max(page, 1), totalPages);
1301
+ const start = totalItems === 0 ? 0 : (currentPage - 1) * pageSize + 1;
1302
+ const end = Math.min(currentPage * pageSize, totalItems);
1303
+ const canChangePageSize = pageSizeOptions != null && pageSizeOptions.length > 0 && onPageSizeChange != null;
1304
+ return /* @__PURE__ */ jsxs16("nav", { className: cn("fractal-pagination", className), "aria-label": ariaLabel, children: [
1305
+ /* @__PURE__ */ jsx22("span", { className: "fractal-pagination__summary", children: totalItems === 0 ? "No results" : `${start}-${end} of ${totalItems}` }),
1306
+ /* @__PURE__ */ jsxs16("div", { className: "fractal-pagination__controls", children: [
1307
+ canChangePageSize && /* @__PURE__ */ jsx22(
1308
+ Dropdown,
1309
+ {
1310
+ align: "right",
1311
+ className: "fractal-pagination__page-size",
1312
+ direction: "up",
1313
+ items: pageSizeOptions.map((option) => ({
1314
+ value: String(option),
1315
+ description: `${option} per page`
1316
+ })),
1317
+ onSelect: (value) => onPageSizeChange(Number(value)),
1318
+ selectedValue: String(pageSize),
1319
+ size: "sm",
1320
+ triggerLabel: "Results per page",
1321
+ triggerVariant: "tertiary"
1322
+ }
1323
+ ),
1324
+ /* @__PURE__ */ jsx22(
1325
+ "button",
1326
+ {
1327
+ className: "fractal-pagination__button",
1328
+ type: "button",
1329
+ "aria-label": "Previous page",
1330
+ title: "Previous page",
1331
+ disabled: currentPage === 1 || totalItems === 0,
1332
+ onClick: () => onPageChange(currentPage - 1),
1333
+ children: /* @__PURE__ */ jsx22(ArrowLeft, { "aria-hidden": "true", size: 16 })
1334
+ }
1335
+ ),
1336
+ /* @__PURE__ */ jsxs16("span", { className: "fractal-pagination__page", "aria-current": "page", children: [
1337
+ currentPage,
1338
+ " of ",
1339
+ totalPages
1340
+ ] }),
1341
+ /* @__PURE__ */ jsx22(
1342
+ "button",
1343
+ {
1344
+ className: "fractal-pagination__button",
1345
+ type: "button",
1346
+ "aria-label": "Next page",
1347
+ title: "Next page",
1348
+ disabled: currentPage === totalPages || totalItems === 0,
1349
+ onClick: () => onPageChange(currentPage + 1),
1350
+ children: /* @__PURE__ */ jsx22(ArrowRight, { "aria-hidden": "true", size: 16 })
1351
+ }
1352
+ )
1353
+ ] })
1354
+ ] });
1355
+ }
1176
1356
  export {
1177
1357
  Alert,
1178
1358
  AlertDescription,
@@ -1184,7 +1364,9 @@ export {
1184
1364
  CardFooter,
1185
1365
  CardHeader,
1186
1366
  CardTitle,
1367
+ Checkbox,
1187
1368
  Chip,
1369
+ ContentSwitcher,
1188
1370
  DataTable,
1189
1371
  Disclosure,
1190
1372
  Dropdown,
@@ -1195,6 +1377,7 @@ export {
1195
1377
  Label,
1196
1378
  Link,
1197
1379
  Modal,
1380
+ Pagination,
1198
1381
  PromptCard,
1199
1382
  PromptGrid,
1200
1383
  SidePanel,
@@ -1207,6 +1390,7 @@ export {
1207
1390
  SidebarSection,
1208
1391
  SortableTable,
1209
1392
  Tabs,
1393
+ TextButton,
1210
1394
  Textarea,
1211
1395
  Toggle,
1212
1396
  cn,