@mirror-physics/fractal-ui 0.2.0-next.1 → 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
@@ -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
  };
@@ -664,23 +664,52 @@ 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,
680
707
  sortKey: controlledKey,
681
708
  sortDirection: controlledDirection,
682
709
  onSortChange,
683
- highlightRow
710
+ highlightRow,
711
+ onRowClick,
712
+ rowAction
684
713
  }) {
685
714
  const [internalKey, setInternalKey] = useState3(defaultSortKey ?? null);
686
715
  const [internalDirection, setInternalDirection] = useState3(defaultSortDirection);
@@ -733,13 +762,13 @@ function SortableTable({
733
762
  onSortChange?.(nextKey, nextDirection);
734
763
  }
735
764
  };
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) => {
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) => {
740
769
  const isActive = activeKey === col.key && activeDirection !== null;
741
770
  if (!col.sortable) {
742
- return /* @__PURE__ */ jsx12(
771
+ return /* @__PURE__ */ jsx13(
743
772
  "th",
744
773
  {
745
774
  className: "fractal-sortable-table-th",
@@ -749,7 +778,7 @@ function SortableTable({
749
778
  col.key
750
779
  );
751
780
  }
752
- return /* @__PURE__ */ jsx12(
781
+ return /* @__PURE__ */ jsx13(
753
782
  "th",
754
783
  {
755
784
  className: "fractal-sortable-table-th fractal-sortable-table-th--sortable",
@@ -765,8 +794,8 @@ function SortableTable({
765
794
  onClick: () => handleSortClick(col.key),
766
795
  "aria-sort": isActive ? activeDirection === "asc" ? "ascending" : "descending" : "none",
767
796
  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 }) })
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 }) })
770
799
  ]
771
800
  }
772
801
  )
@@ -774,21 +803,42 @@ function SortableTable({
774
803
  col.key
775
804
  );
776
805
  }) }) }),
777
- /* @__PURE__ */ jsx12("tbody", { children: sorted.map((row, rowIndex) => {
806
+ /* @__PURE__ */ jsx13("tbody", { children: sorted.map((row, rowIndex) => {
778
807
  const highlighted = highlightRow?.(row, rowIndex) ?? false;
779
- 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(
780
813
  "tr",
781
814
  {
782
815
  className: cn(
783
816
  "fractal-sortable-table-row",
784
- highlighted && "fractal-sortable-table-row--highlighted"
817
+ highlighted && "fractal-sortable-table-row--highlighted",
818
+ onRowClick && "fractal-sortable-table-row--interactive"
785
819
  ),
786
- children: columns.map((col) => /* @__PURE__ */ jsx12(
820
+ onClick: onRowClick ? () => onRowClick(row, rowIndex) : void 0,
821
+ onKeyDown: onRowClick ? (event) => {
822
+ if (event.key === "Enter" || event.key === " ") {
823
+ event.preventDefault();
824
+ onRowClick(row, rowIndex);
825
+ }
826
+ } : void 0,
827
+ "aria-label": action?.label,
828
+ role: onRowClick ? "button" : void 0,
829
+ tabIndex: onRowClick ? 0 : void 0,
830
+ children: columns.map((col, columnIndex) => /* @__PURE__ */ jsxs9(
787
831
  "td",
788
832
  {
789
- 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
+ ),
790
837
  style: { width: col.width, textAlign: col.align ?? "left" },
791
- 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
+ ]
792
842
  },
793
843
  col.key
794
844
  ))
@@ -802,7 +852,7 @@ function SortableTable({
802
852
 
803
853
  // src/components/Tabs/Tabs.tsx
804
854
  import { useId, useRef as useRef3, useState as useState4 } from "react";
805
- import { jsx as jsx13, jsxs as jsxs10 } from "react/jsx-runtime";
855
+ import { jsx as jsx14, jsxs as jsxs10 } from "react/jsx-runtime";
806
856
  function Tabs({
807
857
  items,
808
858
  defaultValue,
@@ -853,11 +903,11 @@ function Tabs({
853
903
  requestAnimationFrame(() => tabRefs.current[nextId2]?.focus());
854
904
  };
855
905
  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) => {
906
+ /* @__PURE__ */ jsx14("div", { role: "tablist", "aria-label": ariaLabel, className: "fractal-tabs-list", children: items.map((item) => {
857
907
  const isActive = item.id === activeId;
858
908
  const tabId = `${reactId}-tab-${item.id}`;
859
909
  const panelId = `${reactId}-panel-${item.id}`;
860
- return /* @__PURE__ */ jsx13(
910
+ return /* @__PURE__ */ jsx14(
861
911
  "button",
862
912
  {
863
913
  ref: (el) => {
@@ -886,7 +936,7 @@ function Tabs({
886
936
  const tabId = `${reactId}-tab-${item.id}`;
887
937
  const panelId = `${reactId}-panel-${item.id}`;
888
938
  const isActive = item.id === activeId;
889
- return /* @__PURE__ */ jsx13(
939
+ return /* @__PURE__ */ jsx14(
890
940
  "div",
891
941
  {
892
942
  id: panelId,
@@ -902,10 +952,55 @@ function Tabs({
902
952
  ] });
903
953
  }
904
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
+
905
1000
  // src/components/Disclosure/Disclosure.tsx
906
1001
  import { useId as useId2, useState as useState5 } from "react";
907
1002
  import { ChevronRight, ChevronDown as ChevronDown2 } from "@mirror-physics/fractal-icons";
908
- import { jsx as jsx14, jsxs as jsxs11 } from "react/jsx-runtime";
1003
+ import { jsx as jsx16, jsxs as jsxs11 } from "react/jsx-runtime";
909
1004
  function Disclosure({
910
1005
  title,
911
1006
  meta,
@@ -913,6 +1008,7 @@ function Disclosure({
913
1008
  defaultOpen = false,
914
1009
  open: controlledOpen,
915
1010
  onOpenChange,
1011
+ disabled = false,
916
1012
  className,
917
1013
  variant = "card"
918
1014
  }) {
@@ -923,6 +1019,7 @@ function Disclosure({
923
1019
  const isControlled = controlledOpen !== void 0;
924
1020
  const isOpen = isControlled ? controlledOpen : internalOpen;
925
1021
  const toggle = () => {
1022
+ if (disabled) return;
926
1023
  const next = !isOpen;
927
1024
  if (isControlled) {
928
1025
  onOpenChange?.(next);
@@ -938,6 +1035,7 @@ function Disclosure({
938
1035
  "fractal-disclosure",
939
1036
  variant === "card" && "fractal-disclosure--card",
940
1037
  isOpen && "fractal-disclosure--open",
1038
+ disabled && "fractal-disclosure--disabled",
941
1039
  className
942
1040
  ),
943
1041
  children: [
@@ -949,15 +1047,16 @@ function Disclosure({
949
1047
  className: "fractal-disclosure-trigger",
950
1048
  "aria-expanded": isOpen,
951
1049
  "aria-controls": panelId,
1050
+ disabled,
952
1051
  onClick: toggle,
953
1052
  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 })
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 })
957
1056
  ]
958
1057
  }
959
1058
  ),
960
- /* @__PURE__ */ jsx14(
1059
+ /* @__PURE__ */ jsx16(
961
1060
  "div",
962
1061
  {
963
1062
  id: panelId,
@@ -965,7 +1064,7 @@ function Disclosure({
965
1064
  "aria-labelledby": headerId,
966
1065
  className: "fractal-disclosure-panel",
967
1066
  hidden: !isOpen,
968
- children: /* @__PURE__ */ jsx14("div", { className: "fractal-disclosure-panel-inner", children })
1067
+ children: /* @__PURE__ */ jsx16("div", { className: "fractal-disclosure-panel-inner", children })
969
1068
  }
970
1069
  )
971
1070
  ]
@@ -974,10 +1073,10 @@ function Disclosure({
974
1073
  }
975
1074
 
976
1075
  // 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(
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(
981
1080
  "aside",
982
1081
  {
983
1082
  ref,
@@ -988,23 +1087,23 @@ var Sidebar = React10.forwardRef(
988
1087
  )
989
1088
  );
990
1089
  Sidebar.displayName = "Sidebar";
991
- var SidebarHeader = React10.forwardRef(
992
- ({ 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 })
993
1092
  );
994
1093
  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 })
1094
+ var SidebarBrand = React11.forwardRef(
1095
+ ({ className, type = "button", ...props }, ref) => /* @__PURE__ */ jsx17("button", { ref, type, className: cn("fractal-sidebar__brand", className), ...props })
997
1096
  );
998
1097
  SidebarBrand.displayName = "SidebarBrand";
999
- var SidebarNav = React10.forwardRef(
1000
- ({ 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 })
1001
1100
  );
1002
1101
  SidebarNav.displayName = "SidebarNav";
1003
- var SidebarSection = React10.forwardRef(
1004
- ({ 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 })
1005
1104
  );
1006
1105
  SidebarSection.displayName = "SidebarSection";
1007
- var SidebarNavItem = React10.forwardRef(
1106
+ var SidebarNavItem = React11.forwardRef(
1008
1107
  ({ active = false, icon, endAdornment, className, type = "button", children, ...props }, ref) => /* @__PURE__ */ jsxs12(
1009
1108
  "button",
1010
1109
  {
@@ -1015,18 +1114,245 @@ var SidebarNavItem = React10.forwardRef(
1015
1114
  "aria-current": active ? "page" : void 0,
1016
1115
  ...props,
1017
1116
  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 })
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 })
1021
1120
  ]
1022
1121
  }
1023
1122
  )
1024
1123
  );
1025
1124
  SidebarNavItem.displayName = "SidebarNavItem";
1026
- var SidebarFooter = React10.forwardRef(
1027
- ({ 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 })
1028
1127
  );
1029
1128
  SidebarFooter.displayName = "SidebarFooter";
1129
+
1130
+ // src/components/SidePanel/SidePanel.tsx
1131
+ import { createPortal as createPortal2 } from "react-dom";
1132
+ import { Close } from "@mirror-physics/fractal-icons";
1133
+ import { jsx as jsx18, jsxs as jsxs13 } from "react/jsx-runtime";
1134
+ function SidePanel({ open, onClose, title, description, children, footer, size = "md", className }) {
1135
+ useEscapeDismiss(80, onClose, open);
1136
+ if (!open) return null;
1137
+ return createPortal2(
1138
+ /* @__PURE__ */ jsx18("div", { className: "fractal-side-panel-overlay", onMouseDown: onClose, children: /* @__PURE__ */ jsxs13(
1139
+ "aside",
1140
+ {
1141
+ className: cn("fractal-side-panel", `fractal-side-panel--${size}`, className),
1142
+ "aria-modal": "true",
1143
+ "aria-label": typeof title === "string" ? title : "Side panel",
1144
+ role: "dialog",
1145
+ onMouseDown: (event) => event.stopPropagation(),
1146
+ children: [
1147
+ /* @__PURE__ */ jsxs13("header", { className: "fractal-side-panel__header", children: [
1148
+ /* @__PURE__ */ jsxs13("div", { className: "fractal-side-panel__heading", children: [
1149
+ /* @__PURE__ */ jsx18("h2", { className: "fractal-side-panel__title", children: title }),
1150
+ description && /* @__PURE__ */ jsx18("p", { className: "fractal-side-panel__description", children: description })
1151
+ ] }),
1152
+ /* @__PURE__ */ jsx18("button", { type: "button", className: "fractal-side-panel__close", "aria-label": "Close panel", onClick: onClose, children: /* @__PURE__ */ jsx18(Close, { size: 18 }) })
1153
+ ] }),
1154
+ /* @__PURE__ */ jsx18("div", { className: "fractal-side-panel__body", children }),
1155
+ footer && /* @__PURE__ */ jsx18("footer", { className: "fractal-side-panel__footer", children: footer })
1156
+ ]
1157
+ }
1158
+ ) }),
1159
+ document.body
1160
+ );
1161
+ }
1162
+
1163
+ // src/components/Textarea/Textarea.tsx
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 })
1168
+ );
1169
+ Textarea.displayName = "Textarea";
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
+
1192
+ // src/components/FileDropzone/FileDropzone.tsx
1193
+ import * as React14 from "react";
1194
+ import { Close as Close2, FileArrowUp } from "@mirror-physics/fractal-icons";
1195
+ import { jsx as jsx21, jsxs as jsxs15 } from "react/jsx-runtime";
1196
+ function FileDropzone({
1197
+ files,
1198
+ onFilesChange,
1199
+ accept,
1200
+ multiple = true,
1201
+ disabled = false,
1202
+ title = "Drop files here or click to browse",
1203
+ description = "Files stay attached to this record.",
1204
+ className
1205
+ }) {
1206
+ const inputId = React14.useId();
1207
+ const inputRef = React14.useRef(null);
1208
+ const [isDragging, setIsDragging] = React14.useState(false);
1209
+ const addFiles = (nextFiles) => {
1210
+ if (disabled) return;
1211
+ const additions = Array.from(nextFiles);
1212
+ if (additions.length === 0) return;
1213
+ onFilesChange(multiple ? [...files, ...additions] : additions.slice(0, 1));
1214
+ };
1215
+ return /* @__PURE__ */ jsxs15("div", { className: cn("fractal-file-dropzone", className), children: [
1216
+ /* @__PURE__ */ jsx21(
1217
+ "input",
1218
+ {
1219
+ ref: inputRef,
1220
+ id: inputId,
1221
+ className: "fractal-file-dropzone__input",
1222
+ type: "file",
1223
+ accept,
1224
+ multiple,
1225
+ disabled,
1226
+ onChange: (event) => {
1227
+ if (event.target.files) addFiles(event.target.files);
1228
+ event.target.value = "";
1229
+ }
1230
+ }
1231
+ ),
1232
+ /* @__PURE__ */ jsxs15(
1233
+ "button",
1234
+ {
1235
+ className: "fractal-file-dropzone__target",
1236
+ "data-dragging": isDragging || void 0,
1237
+ type: "button",
1238
+ disabled,
1239
+ onClick: () => inputRef.current?.click(),
1240
+ onDragEnter: (event) => {
1241
+ event.preventDefault();
1242
+ if (!disabled) setIsDragging(true);
1243
+ },
1244
+ onDragOver: (event) => event.preventDefault(),
1245
+ onDragLeave: (event) => {
1246
+ event.preventDefault();
1247
+ setIsDragging(false);
1248
+ },
1249
+ onDrop: (event) => {
1250
+ event.preventDefault();
1251
+ setIsDragging(false);
1252
+ addFiles(event.dataTransfer.files);
1253
+ },
1254
+ children: [
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 })
1259
+ ] })
1260
+ ]
1261
+ }
1262
+ ),
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(
1267
+ "button",
1268
+ {
1269
+ className: "fractal-file-dropzone__remove",
1270
+ type: "button",
1271
+ "aria-label": `Remove ${file.name}`,
1272
+ disabled,
1273
+ onClick: () => onFilesChange(files.filter((_, fileIndex) => fileIndex !== index)),
1274
+ children: /* @__PURE__ */ jsx21(Close2, { "aria-hidden": "true", size: 14 })
1275
+ }
1276
+ )
1277
+ ] }, `${file.name}-${file.size}-${index}`)) })
1278
+ ] });
1279
+ }
1280
+ function formatFileSize(bytes) {
1281
+ if (bytes < 1024) return `${bytes} B`;
1282
+ if (bytes < 1024 * 1024) return `${Math.round(bytes / 1024)} KB`;
1283
+ return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
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
+ }
1030
1356
  export {
1031
1357
  Alert,
1032
1358
  AlertDescription,
@@ -1038,18 +1364,23 @@ export {
1038
1364
  CardFooter,
1039
1365
  CardHeader,
1040
1366
  CardTitle,
1367
+ Checkbox,
1041
1368
  Chip,
1369
+ ContentSwitcher,
1042
1370
  DataTable,
1043
1371
  Disclosure,
1044
1372
  Dropdown,
1045
1373
  FeatureCard,
1374
+ FileDropzone,
1046
1375
  FolderCard,
1047
1376
  Input,
1048
1377
  Label,
1049
1378
  Link,
1050
1379
  Modal,
1380
+ Pagination,
1051
1381
  PromptCard,
1052
1382
  PromptGrid,
1383
+ SidePanel,
1053
1384
  Sidebar,
1054
1385
  SidebarBrand,
1055
1386
  SidebarFooter,
@@ -1059,6 +1390,8 @@ export {
1059
1390
  SidebarSection,
1060
1391
  SortableTable,
1061
1392
  Tabs,
1393
+ TextButton,
1394
+ Textarea,
1062
1395
  Toggle,
1063
1396
  cn,
1064
1397
  useEscapeDismiss