@geomak/ui 7.4.0 → 7.4.1

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
@@ -4577,10 +4577,6 @@ function List2({
4577
4577
  );
4578
4578
  }) });
4579
4579
  }
4580
- var rectOf = (el) => {
4581
- const r = el.getBoundingClientRect();
4582
- return { left: r.left, top: r.top, width: r.width, height: r.height };
4583
- };
4584
4580
  var TOGGLE_POSITION_CLASS = {
4585
4581
  "top-left": "top-2 left-2",
4586
4582
  "top-right": "top-2 right-2",
@@ -4600,55 +4596,69 @@ function ScalableContainer({
4600
4596
  collapseIcon,
4601
4597
  togglePosition = "top-right",
4602
4598
  expandContainerRef,
4599
+ expandRatio = 5,
4603
4600
  className = ""
4604
4601
  }) {
4605
4602
  const containerRef = React30.useRef(null);
4606
4603
  const [internalScaled, setInternalScaled] = React30.useState(false);
4607
4604
  const isScaled = expanded ?? internalScaled;
4608
4605
  const reduced = framerMotion.useReducedMotion();
4609
- const usePortal = expandContainerRef != null;
4610
- const [overlay, setOverlay] = React30.useState("closed");
4611
- const [fromRect, setFromRect] = React30.useState(null);
4612
- const [targetRect, setTargetRect] = React30.useState(null);
4606
+ const usePush = expandContainerRef != null;
4607
+ const grownRef = React30.useRef([]);
4613
4608
  const prevScaled = React30.useRef(isScaled);
4614
- React30.useEffect(() => {
4615
- if (!usePortal || isScaled === prevScaled.current) return;
4616
- prevScaled.current = isScaled;
4617
- if (isScaled) {
4618
- const src = containerRef.current ? rectOf(containerRef.current) : null;
4619
- const tgt = expandContainerRef.current ? rectOf(expandContainerRef.current) : null;
4620
- if (src && tgt) {
4621
- setFromRect(src);
4622
- setTargetRect(tgt);
4623
- setOverlay("open");
4609
+ const growAncestors = () => {
4610
+ const bound = expandContainerRef?.current;
4611
+ if (!bound || !containerRef.current) return;
4612
+ const grown = [];
4613
+ let el = containerRef.current.parentElement;
4614
+ while (el && el !== bound && bound.contains(el)) {
4615
+ const parent = el.parentElement;
4616
+ if (parent && getComputedStyle(parent).display.includes("flex")) {
4617
+ grown.push({
4618
+ el,
4619
+ prev: {
4620
+ flexGrow: el.style.flexGrow,
4621
+ flexBasis: el.style.flexBasis,
4622
+ transition: el.style.transition
4623
+ }
4624
+ });
4625
+ const grow = `flex-grow ${reduced ? 0 : 0.32}s cubic-bezier(0.16, 1, 0.3, 1), flex-basis ${reduced ? 0 : 0.32}s cubic-bezier(0.16, 1, 0.3, 1)`;
4626
+ el.style.transition = el.style.transition ? `${el.style.transition}, ${grow}` : grow;
4627
+ el.style.flexBasis = "0%";
4628
+ el.style.flexGrow = String(expandRatio);
4624
4629
  }
4625
- } else if (containerRef.current) {
4626
- setTargetRect(rectOf(containerRef.current));
4627
- setOverlay("closing");
4630
+ el = parent;
4628
4631
  }
4629
- }, [isScaled, usePortal, expandContainerRef]);
4630
- React30.useEffect(() => {
4631
- if (overlay !== "closing") return;
4632
- const t = window.setTimeout(() => setOverlay("closed"), reduced ? 0 : 360);
4633
- return () => window.clearTimeout(t);
4634
- }, [overlay, reduced]);
4632
+ grownRef.current = grown;
4633
+ };
4634
+ const restoreAncestors = () => {
4635
+ for (const { el, prev } of grownRef.current) {
4636
+ el.style.flexGrow = prev.flexGrow;
4637
+ el.style.flexBasis = prev.flexBasis;
4638
+ window.setTimeout(() => {
4639
+ el.style.transition = prev.transition;
4640
+ }, reduced ? 0 : 360);
4641
+ }
4642
+ grownRef.current = [];
4643
+ };
4635
4644
  React30.useEffect(() => {
4636
- if (overlay !== "open" || !expandContainerRef?.current) return;
4637
- const update = () => {
4638
- if (expandContainerRef.current) setTargetRect(rectOf(expandContainerRef.current));
4639
- };
4640
- window.addEventListener("resize", update);
4641
- window.addEventListener("scroll", update, true);
4642
- return () => {
4643
- window.removeEventListener("resize", update);
4644
- window.removeEventListener("scroll", update, true);
4645
- };
4646
- }, [overlay, expandContainerRef]);
4645
+ if (!usePush || isScaled === prevScaled.current) return;
4646
+ prevScaled.current = isScaled;
4647
+ if (isScaled) growAncestors();
4648
+ else restoreAncestors();
4649
+ }, [isScaled, usePush]);
4650
+ React30.useEffect(() => () => {
4651
+ for (const { el, prev } of grownRef.current) {
4652
+ el.style.flexGrow = prev.flexGrow;
4653
+ el.style.flexBasis = prev.flexBasis;
4654
+ el.style.transition = prev.transition;
4655
+ }
4656
+ }, []);
4647
4657
  const onToggle = () => {
4648
4658
  const next = !isScaled;
4649
4659
  if (expanded === void 0) setInternalScaled(next);
4650
4660
  onExpandedChange?.(next);
4651
- if (next && !usePortal) {
4661
+ if (next && !usePush) {
4652
4662
  window.setTimeout(
4653
4663
  () => containerRef.current?.scrollIntoView({ behavior: "smooth", block: "nearest" }),
4654
4664
  reduced ? 0 : 340
@@ -4656,79 +4666,52 @@ function ScalableContainer({
4656
4666
  }
4657
4667
  };
4658
4668
  const wrapperClass = isScaled ? assignClassOnClick : void 0;
4659
- const overlayActive = usePortal && overlay !== "closed";
4660
- const toggleButton = (scaled) => /* @__PURE__ */ jsxRuntime.jsx(Tooltip, { placement: "bottom", title: scaled ? "Collapse" : "Expand", children: /* @__PURE__ */ jsxRuntime.jsx(
4661
- "button",
4669
+ return /* @__PURE__ */ jsxRuntime.jsxs(
4670
+ framerMotion.motion.div,
4662
4671
  {
4663
- type: "button",
4664
- onClick: onToggle,
4665
- "aria-label": scaled ? "Collapse container" : "Expand container",
4666
- "aria-expanded": scaled,
4667
- className: [
4668
- "absolute z-10",
4669
- TOGGLE_POSITION_CLASS[togglePosition],
4670
- "w-7 h-7 inline-flex items-center justify-center",
4671
- "rounded-md bg-surface/80 backdrop-blur-sm border border-border",
4672
- "text-foreground-secondary hover:text-foreground hover:bg-surface",
4673
- "shadow-sm transition-colors duration-150",
4674
- "focus:outline-none focus-visible:ring-2 focus-visible:ring-accent"
4675
- ].join(" "),
4676
- children: scaled ? collapseIcon ?? /* @__PURE__ */ jsxRuntime.jsx(CollapseIcon, {}) : expandIcon ?? /* @__PURE__ */ jsxRuntime.jsx(ExpandIcon, {})
4677
- }
4678
- ) });
4679
- return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
4680
- /* @__PURE__ */ jsxRuntime.jsxs(
4681
- framerMotion.motion.div,
4682
- {
4683
- ref: containerRef,
4684
- animate: {
4685
- // Breakout mode never grows in place — the in-flow box stays
4686
- // at its resting size and acts as the collapse target.
4687
- width: isScaled && !usePortal ? expandedWidth : width,
4688
- height: isScaled && !usePortal ? expandedHeight : height
4689
- },
4690
- transition: reduced ? { duration: 0 } : {
4691
- width: { type: "tween", duration: 0.32, ease: [0.16, 1, 0.3, 1] },
4692
- height: { type: "tween", duration: 0.32, ease: [0.16, 1, 0.3, 1] }
4693
- },
4694
- className: cx(
4695
- "relative rounded-lg overflow-hidden",
4696
- // OS-window aesthetic: subtle elevation at rest, lifted shadow
4697
- // when expanded. No background colour change.
4698
- isScaled && !usePortal ? "shadow-2xl" : "shadow-md",
4699
- "transition-shadow duration-300",
4700
- className
4701
- ),
4702
- children: [
4703
- !overlayActive && toggleButton(isScaled),
4704
- !overlayActive && /* @__PURE__ */ jsxRuntime.jsx("div", { className: wrapperClass, children })
4705
- ]
4706
- }
4707
- ),
4708
- overlayActive && fromRect && targetRect && reactDom.createPortal(
4709
- /* @__PURE__ */ jsxRuntime.jsxs(
4710
- framerMotion.motion.div,
4711
- {
4712
- initial: { ...fromRect },
4713
- animate: { ...targetRect },
4714
- transition: reduced ? { duration: 0 } : { type: "tween", duration: 0.32, ease: [0.16, 1, 0.3, 1] },
4715
- onAnimationComplete: () => {
4716
- if (overlay === "closing") setOverlay("closed");
4717
- },
4718
- style: { position: "fixed" },
4719
- className: cx(
4720
- "z-dropdown rounded-lg overflow-hidden bg-surface shadow-2xl",
4721
- className
4722
- ),
4723
- children: [
4724
- toggleButton(isScaled),
4725
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: cx("h-full w-full", wrapperClass), children })
4726
- ]
4727
- }
4672
+ ref: containerRef,
4673
+ animate: {
4674
+ // Push mode keeps the container filling its (now growing)
4675
+ // wrapper — the wrapper's flex-grow does the work.
4676
+ width: isScaled && !usePush ? expandedWidth : width,
4677
+ height: isScaled && !usePush ? expandedHeight : height
4678
+ },
4679
+ transition: reduced ? { duration: 0 } : {
4680
+ width: { type: "tween", duration: 0.32, ease: [0.16, 1, 0.3, 1] },
4681
+ height: { type: "tween", duration: 0.32, ease: [0.16, 1, 0.3, 1] }
4682
+ },
4683
+ className: cx(
4684
+ "relative rounded-lg overflow-hidden",
4685
+ // OS-window aesthetic: subtle elevation at rest, lifted shadow
4686
+ // when expanded. No background colour change.
4687
+ isScaled ? "shadow-2xl" : "shadow-md",
4688
+ "transition-shadow duration-300",
4689
+ className
4728
4690
  ),
4729
- document.body
4730
- )
4731
- ] });
4691
+ children: [
4692
+ /* @__PURE__ */ jsxRuntime.jsx(Tooltip, { placement: "bottom", title: isScaled ? "Collapse" : "Expand", children: /* @__PURE__ */ jsxRuntime.jsx(
4693
+ "button",
4694
+ {
4695
+ type: "button",
4696
+ onClick: onToggle,
4697
+ "aria-label": isScaled ? "Collapse container" : "Expand container",
4698
+ "aria-expanded": isScaled,
4699
+ className: [
4700
+ "absolute z-10",
4701
+ TOGGLE_POSITION_CLASS[togglePosition],
4702
+ "w-7 h-7 inline-flex items-center justify-center",
4703
+ "rounded-md bg-surface/80 backdrop-blur-sm border border-border",
4704
+ "text-foreground-secondary hover:text-foreground hover:bg-surface",
4705
+ "shadow-sm transition-colors duration-150",
4706
+ "focus:outline-none focus-visible:ring-2 focus-visible:ring-accent"
4707
+ ].join(" "),
4708
+ children: isScaled ? collapseIcon ?? /* @__PURE__ */ jsxRuntime.jsx(CollapseIcon, {}) : expandIcon ?? /* @__PURE__ */ jsxRuntime.jsx(ExpandIcon, {})
4709
+ }
4710
+ ) }),
4711
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: cx("h-full w-full", wrapperClass), children })
4712
+ ]
4713
+ }
4714
+ );
4732
4715
  }
4733
4716
  function CollapseIcon() {
4734
4717
  return /* @__PURE__ */ jsxRuntime.jsx("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: 2, className: "w-4 h-4", "aria-hidden": "true", children: /* @__PURE__ */ jsxRuntime.jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M9 9L4 4M9 9V4M9 9H4M15 9L20 4M15 9V4M15 9H20M9 15L4 20M9 15V20M9 15H4M15 15L20 20M15 15V20M15 15H20" }) });