@mirror-physics/fractal-ui 0.2.0-next.65 → 0.2.0-next.67

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
@@ -927,6 +927,7 @@ function Toggle({ checked, onChange, label, className }) {
927
927
  }
928
928
 
929
929
  // src/components/Modal/Modal.tsx
930
+ import * as React10 from "react";
930
931
  import { createPortal } from "react-dom";
931
932
 
932
933
  // src/hooks/useEscapeDismiss.ts
@@ -1028,7 +1029,11 @@ function UILoader({ size = 48, tone = "neutral", className, style, label = "Load
1028
1029
  import { jsx as jsx13, jsxs as jsxs12 } from "react/jsx-runtime";
1029
1030
  var CloseIcon = () => /* @__PURE__ */ jsx13("svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "none", xmlns: "http://www.w3.org/2000/svg", children: /* @__PURE__ */ jsx13("path", { d: "M12 4L4 12M4 4L12 12", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round" }) });
1030
1031
  function Modal({ open, onClose, title, children, danger, warn, footer, noClose, closeDisabled = false, size = "sm", loading = false, loadingLabel = "Loading", scrollable = false, className }) {
1032
+ const [bodyScrolled, setBodyScrolled] = React10.useState(false);
1031
1033
  useEscapeDismiss(80, onClose, open && !noClose && !closeDisabled);
1034
+ React10.useEffect(() => {
1035
+ if (open) setBodyScrolled(false);
1036
+ }, [open]);
1032
1037
  if (!open) return null;
1033
1038
  const sizeClass = `fractal-modal-panel--${size}`;
1034
1039
  const accentClass = danger ? "fractal-modal-panel--danger" : warn ? "fractal-modal-panel--warn" : "";
@@ -1039,10 +1044,20 @@ function Modal({ open, onClose, title, children, danger, warn, footer, noClose,
1039
1044
  className: cn("fractal-modal-panel", sizeClass, accentClass, scrollable && "fractal-modal-panel--scrollable", className),
1040
1045
  onClick: (e) => e.stopPropagation(),
1041
1046
  children: [
1042
- /* @__PURE__ */ jsxs12("div", { className: "fractal-modal-title-row", children: [
1043
- /* @__PURE__ */ jsx13("h3", { className: cn("fractal-modal-title", danger && "fractal-modal-title--danger", warn && "fractal-modal-title--warn"), children: title }),
1044
- !noClose && /* @__PURE__ */ jsx13("button", { type: "button", onClick: onClose, title: "Close", disabled: closeDisabled, className: "fractal-modal-close", children: /* @__PURE__ */ jsx13(CloseIcon, {}) })
1045
- ] }),
1047
+ /* @__PURE__ */ jsxs12(
1048
+ "div",
1049
+ {
1050
+ className: cn(
1051
+ "fractal-modal-title-row",
1052
+ scrollable && "fractal-modal-title-row--scrollable",
1053
+ scrollable && bodyScrolled && "fractal-modal-title-row--scrolled"
1054
+ ),
1055
+ children: [
1056
+ /* @__PURE__ */ jsx13("h3", { className: cn("fractal-modal-title", danger && "fractal-modal-title--danger", warn && "fractal-modal-title--warn"), children: title }),
1057
+ !noClose && /* @__PURE__ */ jsx13("button", { type: "button", onClick: onClose, title: "Close", disabled: closeDisabled, className: "fractal-modal-close", children: /* @__PURE__ */ jsx13(CloseIcon, {}) })
1058
+ ]
1059
+ }
1060
+ ),
1046
1061
  /* @__PURE__ */ jsx13(
1047
1062
  "div",
1048
1063
  {
@@ -1053,6 +1068,7 @@ function Modal({ open, onClose, title, children, danger, warn, footer, noClose,
1053
1068
  loading && "fractal-modal-body--loading",
1054
1069
  scrollable && "fractal-modal-body--scrollable"
1055
1070
  ),
1071
+ onScroll: scrollable ? (event) => setBodyScrolled(event.currentTarget.scrollTop > 0) : void 0,
1056
1072
  children: loading ? /* @__PURE__ */ jsx13(UILoader, { label: loadingLabel }) : children
1057
1073
  }
1058
1074
  ),
@@ -1084,7 +1100,7 @@ function DataTable({ columns, data, emptyMessage, header, size = "md", className
1084
1100
  }
1085
1101
 
1086
1102
  // src/components/Dropdown/Dropdown.tsx
1087
- import * as React10 from "react";
1103
+ import * as React11 from "react";
1088
1104
  import { ChevronDown, ChevronLeft, ChevronRight as ChevronRight2, ChevronUp } from "@mirror-physics/fractal-icons";
1089
1105
  import { Fragment as Fragment2, jsx as jsx15, jsxs as jsxs14 } from "react/jsx-runtime";
1090
1106
  var MARGIN = 4;
@@ -1108,10 +1124,10 @@ function Dropdown({
1108
1124
  closeOnSelect = true,
1109
1125
  disabled = false
1110
1126
  }) {
1111
- const [open, setOpen] = React10.useState(false);
1112
- const triggerRef = React10.useRef(null);
1113
- const panelRef = React10.useRef(null);
1114
- const [panelStyle, setPanelStyle] = React10.useState({});
1127
+ const [open, setOpen] = React11.useState(false);
1128
+ const triggerRef = React11.useRef(null);
1129
+ const panelRef = React11.useRef(null);
1130
+ const [panelStyle, setPanelStyle] = React11.useState({});
1115
1131
  const visibleItems = items.filter(
1116
1132
  (item) => item.icon != null || item.description != null && (typeof item.description === "string" ? item.description !== "" : true)
1117
1133
  );
@@ -1121,7 +1137,7 @@ function Dropdown({
1121
1137
  const isUp = direction === "up";
1122
1138
  const isHorizontalDirection = direction === "left" || direction === "right";
1123
1139
  const ChevronIcon = direction === "up" ? ChevronUp : direction === "left" ? ChevronLeft : direction === "right" ? ChevronRight2 : ChevronDown;
1124
- React10.useLayoutEffect(() => {
1140
+ React11.useLayoutEffect(() => {
1125
1141
  if (!open || !triggerRef.current || !panelRef.current) return;
1126
1142
  const triggerRect = triggerRef.current.getBoundingClientRect();
1127
1143
  const panelRect = panelRef.current.getBoundingClientRect();
@@ -1172,7 +1188,7 @@ function Dropdown({
1172
1188
  }
1173
1189
  setPanelStyle({ top, left, right });
1174
1190
  }, [open, direction, isHorizontalDirection, isUp, align]);
1175
- React10.useEffect(() => {
1191
+ React11.useEffect(() => {
1176
1192
  if (!open) return;
1177
1193
  const handleClickOutside = (e) => {
1178
1194
  const target = e.target;
@@ -1224,14 +1240,14 @@ function Dropdown({
1224
1240
  const topDivider = item.border === "top" ? /* @__PURE__ */ jsx15("div", { className: "fractal-dropdown-separator" }) : null;
1225
1241
  const bottomDivider = item.border === "bottom" ? /* @__PURE__ */ jsx15("div", { className: "fractal-dropdown-separator" }) : null;
1226
1242
  if (item.kind === "header") {
1227
- return /* @__PURE__ */ jsxs14(React10.Fragment, { children: [
1243
+ return /* @__PURE__ */ jsxs14(React11.Fragment, { children: [
1228
1244
  topDivider,
1229
1245
  /* @__PURE__ */ jsx15("div", { role: "presentation", className: "fractal-dropdown-label", children: item.description }),
1230
1246
  bottomDivider
1231
1247
  ] }, `header-${index}`);
1232
1248
  }
1233
1249
  const isSelected = item.value === selectedValue;
1234
- return /* @__PURE__ */ jsxs14(React10.Fragment, { children: [
1250
+ return /* @__PURE__ */ jsxs14(React11.Fragment, { children: [
1235
1251
  topDivider,
1236
1252
  /* @__PURE__ */ jsxs14(
1237
1253
  "button",
@@ -1261,9 +1277,9 @@ function Dropdown({
1261
1277
  }
1262
1278
 
1263
1279
  // src/components/Link/Link.tsx
1264
- import * as React11 from "react";
1280
+ import * as React12 from "react";
1265
1281
  import { jsx as jsx16 } from "react/jsx-runtime";
1266
- var Link = React11.forwardRef(
1282
+ var Link = React12.forwardRef(
1267
1283
  ({ className, theme = "default", size = "md", ...props }, ref) => /* @__PURE__ */ jsx16(
1268
1284
  "a",
1269
1285
  {
@@ -1276,9 +1292,9 @@ var Link = React11.forwardRef(
1276
1292
  Link.displayName = "Link";
1277
1293
 
1278
1294
  // src/components/Chip/Chip.tsx
1279
- import * as React12 from "react";
1295
+ import * as React13 from "react";
1280
1296
  import { jsx as jsx17 } from "react/jsx-runtime";
1281
- var Chip = React12.forwardRef(
1297
+ var Chip = React13.forwardRef(
1282
1298
  ({ className, tone = "neutral", ...props }, ref) => /* @__PURE__ */ jsx17(
1283
1299
  "span",
1284
1300
  {
@@ -1334,7 +1350,7 @@ function Checkbox({
1334
1350
  }
1335
1351
 
1336
1352
  // src/components/SortableTable/SortableTable.tsx
1337
- import { useMemo as useMemo2, useState as useState5 } from "react";
1353
+ import { useMemo as useMemo2, useState as useState6 } from "react";
1338
1354
  import { ArrowUp, ArrowDown, ArrowsUpDown } from "@mirror-physics/fractal-icons";
1339
1355
  import { jsx as jsx19, jsxs as jsxs16 } from "react/jsx-runtime";
1340
1356
  function SortableTable({
@@ -1356,12 +1372,13 @@ function SortableTable({
1356
1372
  highlightRow,
1357
1373
  selection,
1358
1374
  isRowDisabled,
1375
+ isRowSelectionDisabled,
1359
1376
  onRowClick,
1360
1377
  onRowDoubleClick,
1361
1378
  rowAction
1362
1379
  }) {
1363
- const [internalKey, setInternalKey] = useState5(defaultSortKey ?? null);
1364
- const [internalDirection, setInternalDirection] = useState5(defaultSortDirection);
1380
+ const [internalKey, setInternalKey] = useState6(defaultSortKey ?? null);
1381
+ const [internalDirection, setInternalDirection] = useState6(defaultSortDirection);
1365
1382
  const isControlled = controlledKey !== void 0 && controlledDirection !== void 0;
1366
1383
  const activeKey = isControlled ? controlledKey : internalKey;
1367
1384
  const activeDirection = isControlled ? controlledDirection : internalDirection;
@@ -1403,7 +1420,7 @@ function SortableTable({
1403
1420
  const startIndex = (page - 1) * pageSize;
1404
1421
  return sorted.slice(startIndex, startIndex + pageSize);
1405
1422
  }, [pagination, sorted]);
1406
- const selectableRows = selection ? data.filter((row) => !isRowDisabled?.(row)) : [];
1423
+ const selectableRows = selection ? data.filter((row) => !isRowDisabled?.(row) && !isRowSelectionDisabled?.(row)) : [];
1407
1424
  const selectedRowCount = selection ? selectableRows.filter(selection.isSelected).length : 0;
1408
1425
  const allRowsSelected = selectableRows.length > 0 && selectedRowCount === selectableRows.length;
1409
1426
  const someRowsSelected = selectedRowCount > 0 && !allRowsSelected;
@@ -1500,6 +1517,7 @@ function SortableTable({
1500
1517
  /* @__PURE__ */ jsx19("tbody", { children: visibleRows.map((row, rowIndex) => {
1501
1518
  const highlighted = highlightRow?.(row, rowIndex) ?? false;
1502
1519
  const disabled = isRowDisabled?.(row) ?? false;
1520
+ const selectionDisabled = disabled || (isRowSelectionDisabled?.(row) ?? false);
1503
1521
  const interactive = !disabled && Boolean(onRowClick || onRowDoubleClick);
1504
1522
  const action = !disabled && rowAction ? {
1505
1523
  label: rowAction.label(row, rowIndex),
@@ -1533,11 +1551,11 @@ function SortableTable({
1533
1551
  Checkbox,
1534
1552
  {
1535
1553
  "aria-label": selection.getRowLabel(row),
1536
- checked: !disabled && selection.isSelected(row),
1537
- disabled,
1554
+ checked: !selectionDisabled && selection.isSelected(row),
1555
+ disabled: selectionDisabled,
1538
1556
  onClick: (event) => event.stopPropagation(),
1539
1557
  onCheckedChange: (checked) => {
1540
- if (!disabled) selection.onSelectedChange(row, checked);
1558
+ if (!selectionDisabled) selection.onSelectedChange(row, checked);
1541
1559
  }
1542
1560
  }
1543
1561
  ) }),
@@ -1566,7 +1584,7 @@ function SortableTable({
1566
1584
  }
1567
1585
 
1568
1586
  // src/components/Tabs/Tabs.tsx
1569
- import { useId as useId2, useLayoutEffect as useLayoutEffect2, useRef as useRef5, useState as useState6 } from "react";
1587
+ import { useId as useId2, useLayoutEffect as useLayoutEffect2, useRef as useRef5, useState as useState7 } from "react";
1570
1588
  import { jsx as jsx20, jsxs as jsxs17 } from "react/jsx-runtime";
1571
1589
  function Tabs({
1572
1590
  items,
@@ -1579,14 +1597,14 @@ function Tabs({
1579
1597
  variant = "line"
1580
1598
  }) {
1581
1599
  const reactId = useId2();
1582
- const [internalValue, setInternalValue] = useState6(
1600
+ const [internalValue, setInternalValue] = useState7(
1583
1601
  defaultValue ?? items.find((i) => !i.disabled)?.id ?? items[0]?.id ?? ""
1584
1602
  );
1585
1603
  const isControlled = controlledValue !== void 0;
1586
1604
  const activeId = isControlled ? controlledValue : internalValue;
1587
1605
  const tabListRef = useRef5(null);
1588
1606
  const tabRefs = useRef5({});
1589
- const [indicator, setIndicator] = useState6(null);
1607
+ const [indicator, setIndicator] = useState7(null);
1590
1608
  useLayoutEffect2(() => {
1591
1609
  const updateIndicator = () => {
1592
1610
  const activeTab = tabRefs.current[activeId];
@@ -1700,14 +1718,14 @@ function Tabs({
1700
1718
  }
1701
1719
 
1702
1720
  // src/components/ContentSwitcher/ContentSwitcher.tsx
1703
- import * as React13 from "react";
1721
+ import * as React14 from "react";
1704
1722
  import { jsx as jsx21, jsxs as jsxs18 } from "react/jsx-runtime";
1705
1723
  function ContentSwitcher({ items, value, onValueChange, ariaLabel, fullWidth = false, className }) {
1706
- const switcherRef = React13.useRef(null);
1707
- const buttonRefs = React13.useRef([]);
1708
- const indicatorMotionReadyRef = React13.useRef(false);
1724
+ const switcherRef = React14.useRef(null);
1725
+ const buttonRefs = React14.useRef([]);
1726
+ const indicatorMotionReadyRef = React14.useRef(false);
1709
1727
  const activeIndex = items.findIndex((item) => item.value === value);
1710
- React13.useLayoutEffect(() => {
1728
+ React14.useLayoutEffect(() => {
1711
1729
  const switcher = switcherRef.current;
1712
1730
  const activeButton = buttonRefs.current[activeIndex];
1713
1731
  let motionFrame = 0;
@@ -1802,7 +1820,7 @@ function ContentSwitcher({ items, value, onValueChange, ariaLabel, fullWidth = f
1802
1820
  }
1803
1821
 
1804
1822
  // src/components/Disclosure/Disclosure.tsx
1805
- import { useId as useId3, useState as useState7 } from "react";
1823
+ import { useId as useId3, useState as useState8 } from "react";
1806
1824
  import { ChevronRight as ChevronRight3, ChevronDown as ChevronDown2 } from "@mirror-physics/fractal-icons";
1807
1825
  import { jsx as jsx22, jsxs as jsxs19 } from "react/jsx-runtime";
1808
1826
  function Disclosure({
@@ -1819,7 +1837,7 @@ function Disclosure({
1819
1837
  const reactId = useId3();
1820
1838
  const headerId = `${reactId}-header`;
1821
1839
  const panelId = `${reactId}-panel`;
1822
- const [internalOpen, setInternalOpen] = useState7(defaultOpen);
1840
+ const [internalOpen, setInternalOpen] = useState8(defaultOpen);
1823
1841
  const isControlled = controlledOpen !== void 0;
1824
1842
  const isOpen = isControlled ? controlledOpen : internalOpen;
1825
1843
  const toggle = () => {
@@ -2070,9 +2088,9 @@ function SurfaceGhost({ className, title, lines = 3, ...props }) {
2070
2088
  }
2071
2089
 
2072
2090
  // src/components/Sidebar/Sidebar.tsx
2073
- import * as React14 from "react";
2091
+ import * as React15 from "react";
2074
2092
  import { Fragment as Fragment3, jsx as jsx25, jsxs as jsxs21 } from "react/jsx-runtime";
2075
- var Sidebar = React14.forwardRef(
2093
+ var Sidebar = React15.forwardRef(
2076
2094
  ({ className, width, compact = false, style, ...props }, ref) => /* @__PURE__ */ jsx25(
2077
2095
  "aside",
2078
2096
  {
@@ -2085,11 +2103,11 @@ var Sidebar = React14.forwardRef(
2085
2103
  )
2086
2104
  );
2087
2105
  Sidebar.displayName = "Sidebar";
2088
- var SidebarHeader = React14.forwardRef(
2106
+ var SidebarHeader = React15.forwardRef(
2089
2107
  ({ className, ...props }, ref) => /* @__PURE__ */ jsx25("div", { ref, className: cn("fractal-sidebar__header", className), ...props })
2090
2108
  );
2091
2109
  SidebarHeader.displayName = "SidebarHeader";
2092
- var SidebarBrand = React14.forwardRef(
2110
+ var SidebarBrand = React15.forwardRef(
2093
2111
  ({ className, type = "button", ...props }, ref) => /* @__PURE__ */ jsx25("button", { ref, type, className: cn("fractal-sidebar__brand", className), ...props })
2094
2112
  );
2095
2113
  SidebarBrand.displayName = "SidebarBrand";
@@ -2120,15 +2138,15 @@ function SidebarAccountMenu({
2120
2138
  }
2121
2139
  );
2122
2140
  }
2123
- var SidebarNav = React14.forwardRef(
2141
+ var SidebarNav = React15.forwardRef(
2124
2142
  ({ className, ...props }, ref) => /* @__PURE__ */ jsx25("nav", { ref, className: cn("fractal-sidebar__nav", className), ...props })
2125
2143
  );
2126
2144
  SidebarNav.displayName = "SidebarNav";
2127
- var SidebarSection = React14.forwardRef(
2145
+ var SidebarSection = React15.forwardRef(
2128
2146
  ({ className, ...props }, ref) => /* @__PURE__ */ jsx25("div", { ref, className: cn("fractal-sidebar__section", className), ...props })
2129
2147
  );
2130
2148
  SidebarSection.displayName = "SidebarSection";
2131
- var SidebarNavItem = React14.forwardRef(
2149
+ var SidebarNavItem = React15.forwardRef(
2132
2150
  ({ active = false, icon, endAdornment, className, type = "button", children, ...props }, ref) => /* @__PURE__ */ jsxs21(
2133
2151
  "button",
2134
2152
  {
@@ -2147,7 +2165,7 @@ var SidebarNavItem = React14.forwardRef(
2147
2165
  )
2148
2166
  );
2149
2167
  SidebarNavItem.displayName = "SidebarNavItem";
2150
- var SidebarFooter = React14.forwardRef(
2168
+ var SidebarFooter = React15.forwardRef(
2151
2169
  ({ className, ...props }, ref) => /* @__PURE__ */ jsx25("div", { ref, className: cn("fractal-sidebar__footer", className), ...props })
2152
2170
  );
2153
2171
  SidebarFooter.displayName = "SidebarFooter";
@@ -2186,17 +2204,17 @@ function SidePanel({ open, onClose, title, description, children, footer, size =
2186
2204
  }
2187
2205
 
2188
2206
  // src/components/Textarea/Textarea.tsx
2189
- import * as React15 from "react";
2207
+ import * as React16 from "react";
2190
2208
  import { jsx as jsx27 } from "react/jsx-runtime";
2191
- var Textarea = React15.forwardRef(
2209
+ var Textarea = React16.forwardRef(
2192
2210
  ({ className, ...props }, ref) => /* @__PURE__ */ jsx27("textarea", { ref, className: cn("fractal-textarea", className), ...props })
2193
2211
  );
2194
2212
  Textarea.displayName = "Textarea";
2195
2213
 
2196
2214
  // src/components/TextButton/TextButton.tsx
2197
- import * as React16 from "react";
2215
+ import * as React17 from "react";
2198
2216
  import { jsx as jsx28, jsxs as jsxs23 } from "react/jsx-runtime";
2199
- var TextButton = React16.forwardRef(
2217
+ var TextButton = React17.forwardRef(
2200
2218
  ({ className, icon, iconPosition = "start", size = "md", tone = "primary", children, ...props }, ref) => /* @__PURE__ */ jsxs23(
2201
2219
  "button",
2202
2220
  {
@@ -2215,7 +2233,7 @@ var TextButton = React16.forwardRef(
2215
2233
  TextButton.displayName = "TextButton";
2216
2234
 
2217
2235
  // src/components/FileDropzone/FileDropzone.tsx
2218
- import * as React17 from "react";
2236
+ import * as React18 from "react";
2219
2237
  import { Close as Close2, FileArrowUp } from "@mirror-physics/fractal-icons";
2220
2238
  import { jsx as jsx29, jsxs as jsxs24 } from "react/jsx-runtime";
2221
2239
  function FileDropzone({
@@ -2228,9 +2246,9 @@ function FileDropzone({
2228
2246
  description = "Files stay attached to this record.",
2229
2247
  className
2230
2248
  }) {
2231
- const inputId = React17.useId();
2232
- const inputRef = React17.useRef(null);
2233
- const [isDragging, setIsDragging] = React17.useState(false);
2249
+ const inputId = React18.useId();
2250
+ const inputRef = React18.useRef(null);
2251
+ const [isDragging, setIsDragging] = React18.useState(false);
2234
2252
  const addFiles = (nextFiles) => {
2235
2253
  if (disabled) return;
2236
2254
  const additions = Array.from(nextFiles);