@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.js CHANGED
@@ -2440,11 +2440,43 @@ var SidebarFooter = React16.forwardRef(
2440
2440
  SidebarFooter.displayName = "SidebarFooter";
2441
2441
 
2442
2442
  // src/components/SidePanel/SidePanel.tsx
2443
+ import * as React17 from "react";
2443
2444
  import { createPortal as createPortal3 } from "react-dom";
2444
2445
  import { Close as Close2 } from "@mirror-physics/fractal-icons";
2445
2446
  import { jsx as jsx29, jsxs as jsxs25 } from "react/jsx-runtime";
2446
2447
  function SidePanel({ open, onClose, title, description, children, footer, size = "md", loading = false, loadingLabel = "Loading", className }) {
2447
2448
  useEscapeDismiss(80, onClose, open);
2449
+ const bodyRef = React17.useRef(null);
2450
+ const [scrollEdges, setScrollEdges] = React17.useState({ above: false, below: false });
2451
+ React17.useLayoutEffect(() => {
2452
+ const body = bodyRef.current;
2453
+ if (!open || !body) return;
2454
+ const measure = () => {
2455
+ const maxScroll = Math.max(0, body.scrollHeight - body.clientHeight);
2456
+ const top = Math.max(0, Math.min(body.scrollTop, maxScroll));
2457
+ const above = maxScroll > 1 && top > 0;
2458
+ const below = maxScroll - top > 1;
2459
+ setScrollEdges((previous) => previous.above === above && previous.below === below ? previous : { above, below });
2460
+ };
2461
+ const resize = new ResizeObserver(measure);
2462
+ const observeContent = () => {
2463
+ resize.disconnect();
2464
+ resize.observe(body);
2465
+ for (const child of body.children) resize.observe(child);
2466
+ measure();
2467
+ };
2468
+ const mutations = new MutationObserver(observeContent);
2469
+ mutations.observe(body, { childList: true, subtree: true, characterData: true, attributes: true });
2470
+ body.addEventListener("scroll", measure, { passive: true });
2471
+ body.addEventListener("load", measure, true);
2472
+ observeContent();
2473
+ return () => {
2474
+ resize.disconnect();
2475
+ mutations.disconnect();
2476
+ body.removeEventListener("scroll", measure);
2477
+ body.removeEventListener("load", measure, true);
2478
+ };
2479
+ }, [open, loading]);
2448
2480
  if (!open) return null;
2449
2481
  return createPortal3(
2450
2482
  /* @__PURE__ */ jsx29("div", { className: "fractal-side-panel-overlay", onMouseDown: onClose, children: /* @__PURE__ */ jsxs25(
@@ -2456,15 +2488,15 @@ function SidePanel({ open, onClose, title, description, children, footer, size =
2456
2488
  role: "dialog",
2457
2489
  onMouseDown: (event) => event.stopPropagation(),
2458
2490
  children: [
2459
- /* @__PURE__ */ jsxs25("header", { className: "fractal-side-panel__header", children: [
2491
+ /* @__PURE__ */ jsxs25("header", { className: "fractal-side-panel__header", "data-scroll-divider": scrollEdges.above, children: [
2460
2492
  /* @__PURE__ */ jsxs25("div", { className: "fractal-side-panel__heading", children: [
2461
2493
  typeof title === "string" ? /* @__PURE__ */ jsx29("h2", { className: "fractal-side-panel__title", children: title }) : /* @__PURE__ */ jsx29("div", { className: "fractal-side-panel__title", children: title }),
2462
2494
  description && /* @__PURE__ */ jsx29("p", { className: "fractal-side-panel__description", children: description })
2463
2495
  ] }),
2464
2496
  /* @__PURE__ */ jsx29("button", { type: "button", className: "fractal-side-panel__close", "aria-label": "Close panel", onClick: onClose, children: /* @__PURE__ */ jsx29(Close2, { size: 18 }) })
2465
2497
  ] }),
2466
- /* @__PURE__ */ jsx29("div", { "aria-busy": loading || void 0, className: cn("fractal-side-panel__body", loading && "fractal-side-panel__body--loading"), children: loading ? /* @__PURE__ */ jsx29(UILoader, { label: loadingLabel }) : children }),
2467
- footer && /* @__PURE__ */ jsx29("footer", { className: "fractal-side-panel__footer", children: footer })
2498
+ /* @__PURE__ */ jsx29("div", { ref: bodyRef, "aria-busy": loading || void 0, className: cn("fractal-side-panel__body", loading && "fractal-side-panel__body--loading"), children: loading ? /* @__PURE__ */ jsx29(UILoader, { label: loadingLabel }) : children }),
2499
+ footer && /* @__PURE__ */ jsx29("footer", { className: "fractal-side-panel__footer", "data-scroll-divider": scrollEdges.below, children: footer })
2468
2500
  ]
2469
2501
  }
2470
2502
  ) }),
@@ -2473,17 +2505,17 @@ function SidePanel({ open, onClose, title, description, children, footer, size =
2473
2505
  }
2474
2506
 
2475
2507
  // src/components/Textarea/Textarea.tsx
2476
- import * as React17 from "react";
2508
+ import * as React18 from "react";
2477
2509
  import { jsx as jsx30 } from "react/jsx-runtime";
2478
- var Textarea = React17.forwardRef(
2510
+ var Textarea = React18.forwardRef(
2479
2511
  ({ className, ...props }, ref) => /* @__PURE__ */ jsx30("textarea", { ref, className: cn("fractal-textarea", className), ...props })
2480
2512
  );
2481
2513
  Textarea.displayName = "Textarea";
2482
2514
 
2483
2515
  // src/components/TextButton/TextButton.tsx
2484
- import * as React18 from "react";
2516
+ import * as React19 from "react";
2485
2517
  import { jsx as jsx31, jsxs as jsxs26 } from "react/jsx-runtime";
2486
- var TextButton = React18.forwardRef(
2518
+ var TextButton = React19.forwardRef(
2487
2519
  ({ className, icon, iconPosition = "start", size = "md", tone = "primary", children, ...props }, ref) => /* @__PURE__ */ jsxs26(
2488
2520
  "button",
2489
2521
  {
@@ -2502,7 +2534,7 @@ var TextButton = React18.forwardRef(
2502
2534
  TextButton.displayName = "TextButton";
2503
2535
 
2504
2536
  // src/components/FileDropzone/FileDropzone.tsx
2505
- import * as React19 from "react";
2537
+ import * as React20 from "react";
2506
2538
  import { Close as Close3, FileArrowUp } from "@mirror-physics/fractal-icons";
2507
2539
  import { jsx as jsx32, jsxs as jsxs27 } from "react/jsx-runtime";
2508
2540
  function FileDropzone({
@@ -2515,9 +2547,9 @@ function FileDropzone({
2515
2547
  description = "Files stay attached to this record.",
2516
2548
  className
2517
2549
  }) {
2518
- const inputId = React19.useId();
2519
- const inputRef = React19.useRef(null);
2520
- const [isDragging, setIsDragging] = React19.useState(false);
2550
+ const inputId = React20.useId();
2551
+ const inputRef = React20.useRef(null);
2552
+ const [isDragging, setIsDragging] = React20.useState(false);
2521
2553
  const addFiles = (nextFiles) => {
2522
2554
  if (disabled) return;
2523
2555
  const additions = Array.from(nextFiles);