@mirror-physics/fractal-ui 0.2.0-next.76 → 0.2.0-next.77

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.cjs CHANGED
@@ -2548,11 +2548,43 @@ var SidebarFooter = React16.forwardRef(
2548
2548
  SidebarFooter.displayName = "SidebarFooter";
2549
2549
 
2550
2550
  // src/components/SidePanel/SidePanel.tsx
2551
+ var React17 = __toESM(require("react"), 1);
2551
2552
  var import_react_dom3 = require("react-dom");
2552
2553
  var import_fractal_icons13 = require("@mirror-physics/fractal-icons");
2553
2554
  var import_jsx_runtime30 = require("react/jsx-runtime");
2554
2555
  function SidePanel({ open, onClose, title, description, children, footer, size = "md", loading = false, loadingLabel = "Loading", className }) {
2555
2556
  useEscapeDismiss(80, onClose, open);
2557
+ const bodyRef = React17.useRef(null);
2558
+ const [scrollEdges, setScrollEdges] = React17.useState({ above: false, below: false });
2559
+ React17.useLayoutEffect(() => {
2560
+ const body = bodyRef.current;
2561
+ if (!open || !body) return;
2562
+ const measure = () => {
2563
+ const maxScroll = Math.max(0, body.scrollHeight - body.clientHeight);
2564
+ const top = Math.max(0, Math.min(body.scrollTop, maxScroll));
2565
+ const above = maxScroll > 1 && top > 0;
2566
+ const below = maxScroll - top > 1;
2567
+ setScrollEdges((previous) => previous.above === above && previous.below === below ? previous : { above, below });
2568
+ };
2569
+ const resize = new ResizeObserver(measure);
2570
+ const observeContent = () => {
2571
+ resize.disconnect();
2572
+ resize.observe(body);
2573
+ for (const child of body.children) resize.observe(child);
2574
+ measure();
2575
+ };
2576
+ const mutations = new MutationObserver(observeContent);
2577
+ mutations.observe(body, { childList: true, subtree: true, characterData: true, attributes: true });
2578
+ body.addEventListener("scroll", measure, { passive: true });
2579
+ body.addEventListener("load", measure, true);
2580
+ observeContent();
2581
+ return () => {
2582
+ resize.disconnect();
2583
+ mutations.disconnect();
2584
+ body.removeEventListener("scroll", measure);
2585
+ body.removeEventListener("load", measure, true);
2586
+ };
2587
+ }, [open, loading]);
2556
2588
  if (!open) return null;
2557
2589
  return (0, import_react_dom3.createPortal)(
2558
2590
  /* @__PURE__ */ (0, import_jsx_runtime30.jsx)("div", { className: "fractal-side-panel-overlay", onMouseDown: onClose, children: /* @__PURE__ */ (0, import_jsx_runtime30.jsxs)(
@@ -2564,15 +2596,15 @@ function SidePanel({ open, onClose, title, description, children, footer, size =
2564
2596
  role: "dialog",
2565
2597
  onMouseDown: (event) => event.stopPropagation(),
2566
2598
  children: [
2567
- /* @__PURE__ */ (0, import_jsx_runtime30.jsxs)("header", { className: "fractal-side-panel__header", children: [
2599
+ /* @__PURE__ */ (0, import_jsx_runtime30.jsxs)("header", { className: "fractal-side-panel__header", "data-scroll-divider": scrollEdges.above, children: [
2568
2600
  /* @__PURE__ */ (0, import_jsx_runtime30.jsxs)("div", { className: "fractal-side-panel__heading", children: [
2569
2601
  typeof title === "string" ? /* @__PURE__ */ (0, import_jsx_runtime30.jsx)("h2", { className: "fractal-side-panel__title", children: title }) : /* @__PURE__ */ (0, import_jsx_runtime30.jsx)("div", { className: "fractal-side-panel__title", children: title }),
2570
2602
  description && /* @__PURE__ */ (0, import_jsx_runtime30.jsx)("p", { className: "fractal-side-panel__description", children: description })
2571
2603
  ] }),
2572
2604
  /* @__PURE__ */ (0, import_jsx_runtime30.jsx)("button", { type: "button", className: "fractal-side-panel__close", "aria-label": "Close panel", onClick: onClose, children: /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(import_fractal_icons13.Close, { size: 18 }) })
2573
2605
  ] }),
2574
- /* @__PURE__ */ (0, import_jsx_runtime30.jsx)("div", { "aria-busy": loading || void 0, className: cn("fractal-side-panel__body", loading && "fractal-side-panel__body--loading"), children: loading ? /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(UILoader, { label: loadingLabel }) : children }),
2575
- footer && /* @__PURE__ */ (0, import_jsx_runtime30.jsx)("footer", { className: "fractal-side-panel__footer", children: footer })
2606
+ /* @__PURE__ */ (0, import_jsx_runtime30.jsx)("div", { ref: bodyRef, "aria-busy": loading || void 0, className: cn("fractal-side-panel__body", loading && "fractal-side-panel__body--loading"), children: loading ? /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(UILoader, { label: loadingLabel }) : children }),
2607
+ footer && /* @__PURE__ */ (0, import_jsx_runtime30.jsx)("footer", { className: "fractal-side-panel__footer", "data-scroll-divider": scrollEdges.below, children: footer })
2576
2608
  ]
2577
2609
  }
2578
2610
  ) }),
@@ -2581,17 +2613,17 @@ function SidePanel({ open, onClose, title, description, children, footer, size =
2581
2613
  }
2582
2614
 
2583
2615
  // src/components/Textarea/Textarea.tsx
2584
- var React17 = __toESM(require("react"), 1);
2616
+ var React18 = __toESM(require("react"), 1);
2585
2617
  var import_jsx_runtime31 = require("react/jsx-runtime");
2586
- var Textarea = React17.forwardRef(
2618
+ var Textarea = React18.forwardRef(
2587
2619
  ({ className, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime31.jsx)("textarea", { ref, className: cn("fractal-textarea", className), ...props })
2588
2620
  );
2589
2621
  Textarea.displayName = "Textarea";
2590
2622
 
2591
2623
  // src/components/TextButton/TextButton.tsx
2592
- var React18 = __toESM(require("react"), 1);
2624
+ var React19 = __toESM(require("react"), 1);
2593
2625
  var import_jsx_runtime32 = require("react/jsx-runtime");
2594
- var TextButton = React18.forwardRef(
2626
+ var TextButton = React19.forwardRef(
2595
2627
  ({ className, icon, iconPosition = "start", size = "md", tone = "primary", children, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)(
2596
2628
  "button",
2597
2629
  {
@@ -2610,7 +2642,7 @@ var TextButton = React18.forwardRef(
2610
2642
  TextButton.displayName = "TextButton";
2611
2643
 
2612
2644
  // src/components/FileDropzone/FileDropzone.tsx
2613
- var React19 = __toESM(require("react"), 1);
2645
+ var React20 = __toESM(require("react"), 1);
2614
2646
  var import_fractal_icons14 = require("@mirror-physics/fractal-icons");
2615
2647
  var import_jsx_runtime33 = require("react/jsx-runtime");
2616
2648
  function FileDropzone({
@@ -2623,9 +2655,9 @@ function FileDropzone({
2623
2655
  description = "Files stay attached to this record.",
2624
2656
  className
2625
2657
  }) {
2626
- const inputId = React19.useId();
2627
- const inputRef = React19.useRef(null);
2628
- const [isDragging, setIsDragging] = React19.useState(false);
2658
+ const inputId = React20.useId();
2659
+ const inputRef = React20.useRef(null);
2660
+ const [isDragging, setIsDragging] = React20.useState(false);
2629
2661
  const addFiles = (nextFiles) => {
2630
2662
  if (disabled) return;
2631
2663
  const additions = Array.from(nextFiles);