@geomak/ui 7.7.0 → 7.7.2

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.d.cts CHANGED
@@ -705,6 +705,9 @@ interface TreeNode {
705
705
  nodeData?: any;
706
706
  parentLabel?: string;
707
707
  children?: TreeNode[];
708
+ /** Leading icon. On leaves it replaces the default bullet; on parents it
709
+ * renders between the chevron and the label. */
710
+ icon?: react__default.ReactNode;
708
711
  }
709
712
  interface TreeItemClickPayload {
710
713
  isParent: boolean;
@@ -718,6 +721,9 @@ interface TreeProps {
718
721
  onNodeClick: (payload: TreeItemClickPayload) => void;
719
722
  defaultExpandAll?: boolean;
720
723
  defaultExpandedKeys?: string[];
724
+ /** Default leading icon for every leaf (instead of the bullet dot). A
725
+ * node's own `icon` takes precedence. */
726
+ leafIcon?: react__default.ReactNode;
721
727
  /** Extra classes merged onto the tree root. */
722
728
  className?: string;
723
729
  /** Inline style on the tree root. */
@@ -738,7 +744,7 @@ interface TreeProps {
738
744
  * defaultExpandAll
739
745
  * />
740
746
  */
741
- declare function Tree({ nodes, onNodeClick, defaultExpandAll, defaultExpandedKeys, className, style, }: TreeProps): react_jsx_runtime.JSX.Element;
747
+ declare function Tree({ nodes, onNodeClick, defaultExpandAll, defaultExpandedKeys, leafIcon, className, style, }: TreeProps): react_jsx_runtime.JSX.Element;
742
748
 
743
749
  type AccordionVariant = 'separated' | 'contained';
744
750
  interface AccordionProps {
@@ -2082,10 +2088,22 @@ interface ScalableContainerProps {
2082
2088
  width?: react__default.CSSProperties['width'];
2083
2089
  /** Resting height. Any CSS length / percent. Default `'auto'`. */
2084
2090
  height?: react__default.CSSProperties['height'];
2085
- /** Width when expanded. Default `'100%'` (fills parent). */
2091
+ /**
2092
+ * Width when expanded. A concrete value (e.g. `900` or `'60rem'`) makes the
2093
+ * container grow even when its resting width is `'100%'` inside a flex/grid
2094
+ * cell. Falls back to {@link ScalableContainerProps.expandedWidth}, then
2095
+ * `'100%'`.
2096
+ */
2097
+ targetWidth?: react__default.CSSProperties['width'];
2098
+ /**
2099
+ * Height when expanded. A concrete value (e.g. `600`) lets the container
2100
+ * grow taller and push whatever sits below it further down. Falls back to
2101
+ * {@link ScalableContainerProps.expandedHeight}, then `'100%'`.
2102
+ */
2103
+ targetHeight?: react__default.CSSProperties['height'];
2104
+ /** @deprecated Use `targetWidth`. */
2086
2105
  expandedWidth?: react__default.CSSProperties['width'];
2087
- /** Height when expanded. Default `'100%'`. Set a concrete value (e.g. 420)
2088
- * when the container lives in normal flow and should push siblings down. */
2106
+ /** @deprecated Use `targetHeight`. */
2089
2107
  expandedHeight?: react__default.CSSProperties['height'];
2090
2108
  /** Controlled expanded state. */
2091
2109
  expanded?: boolean;
@@ -2104,60 +2122,38 @@ interface ScalableContainerProps {
2104
2122
  * Default `'top-right'` — matches the OS-window convention.
2105
2123
  */
2106
2124
  togglePosition?: 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right';
2107
- /**
2108
- * Bounding element the expansion is allowed to grow within. When the
2109
- * container sits in size-constrained flex-item wrappers (where width /
2110
- * height 100% makes expand-in-place a no-op), providing this ref switches
2111
- * to PUSH expansion: the container's row becomes `flex-wrap: wrap` and the
2112
- * container takes the FULL parent width (`flex-basis: 100%`), so sibling
2113
- * charts reflow below it at full width — pushed, visible, and intact.
2114
- * Column levels between the container and this element get `flex-grow`
2115
- * raised so the expanded row dominates vertically too. Collapsing
2116
- * restores the wrappers' original sizing exactly. Omit for the classic
2117
- * expand-in-place behaviour.
2118
- */
2125
+ /** @deprecated No longer used — set `targetWidth`/`targetHeight` instead. */
2119
2126
  expandContainerRef?: react__default.RefObject<HTMLElement | null>;
2120
- /**
2121
- * Vertical dominance of the pushed expansion (push mode only). Default
2122
- * `3` — the expanded chart keeps expandRatio/(expandRatio+1) of its row's
2123
- * height (and its row takes expandRatio parts per sibling row), leaving
2124
- * the reflowed siblings the remainder. Raise for a more fullscreen feel,
2125
- * lower for a gentler split.
2126
- */
2127
+ /** @deprecated No longer used — set `targetWidth`/`targetHeight` instead. */
2127
2128
  expandRatio?: number;
2128
2129
  /** Extra classes merged onto the container root. */
2129
2130
  className?: string;
2130
2131
  }
2131
2132
  /**
2132
- * Container that smoothly expands on click and collapses back to its
2133
- * resting size. Reads like a macOS / Windows window resizing — subtle
2134
- * elevation shift, smooth scale, no flash of colour or harsh background
2135
- * change.
2136
- *
2137
- * Two expansion modes:
2138
- * - **In place** (default): animates the container's own width/height to
2139
- * `expandedWidth`/`expandedHeight`.
2140
- * - **Push** (`expandContainerRef` set): for containers whose resting size is
2141
- * owned by flex-item wrappers. Expanding makes the container span the FULL
2142
- * parent width (its row becomes `flex-wrap: wrap` and the wrapper takes
2143
- * `flex-basis: 100%`), with sibling charts reflowing below it at full
2144
- * width — visible and intact, not squeezed. Column levels raise `flex-grow`
2145
- * so the expanded row also dominates vertically. Collapsing restores the
2146
- * original layout exactly.
2133
+ * Container that smoothly grows to a target size on click and collapses back.
2134
+ * Reads like an OS window resize — subtle elevation lift, smooth scale, no
2135
+ * colour change.
2136
+ *
2137
+ * Expansion grows the container's OWN box to `targetWidth` × `targetHeight` and
2138
+ * makes it `flex-none` while expanded, so it holds that size and simply pushes
2139
+ * its neighbours along the flow they keep their own dimensions and reflow
2140
+ * (e.g. wrap onto the next row / move down) rather than being squeezed. Nothing
2141
+ * on sibling elements is mutated, so layouts restore exactly on collapse. For
2142
+ * neighbours to reflow *below*, give the parent room to wrap (e.g. a
2143
+ * `flex flex-wrap` row, or block/auto-rows-grid flow).
2147
2144
  *
2148
2145
  * @example
2149
2146
  * ```tsx
2150
- * <ScalableContainer width={480} height={300}>
2151
- * <Chart data={metrics} />
2152
- * </ScalableContainer>
2153
- *
2154
- * // Push mode inside a flex grid:
2155
- * const sectionRef = useRef<HTMLDivElement>(null)
2156
- * <div ref={sectionRef} className="flex flex-col flex-1 min-h-0 gap-2">…
2157
- * <ScalableContainer width="100%" height="100%" expandContainerRef={sectionRef}>
2147
+ * // In a flex-wrap chart grid — expands to a concrete size and pushes the rest down.
2148
+ * <div className="flex flex-wrap gap-2">
2149
+ * <ScalableContainer width="100%" height={240} targetWidth="100%" targetHeight={520}>
2150
+ * <Chart data={metrics} />
2151
+ * </ScalableContainer>
2152
+ *
2153
+ * </div>
2158
2154
  * ```
2159
2155
  */
2160
- declare function ScalableContainer({ width, height, expandedWidth, expandedHeight, expanded, onExpandedChange, children, assignClassOnClick, expandIcon, collapseIcon, togglePosition, expandContainerRef, expandRatio, className, }: ScalableContainerProps): react_jsx_runtime.JSX.Element;
2156
+ declare function ScalableContainer({ width, height, targetWidth, targetHeight, expandedWidth, expandedHeight, expanded, onExpandedChange, children, assignClassOnClick, expandIcon, collapseIcon, togglePosition, className, }: ScalableContainerProps): react_jsx_runtime.JSX.Element;
2161
2157
 
2162
2158
  interface GridCardItem {
2163
2159
  key: string | number;
package/dist/index.d.ts CHANGED
@@ -705,6 +705,9 @@ interface TreeNode {
705
705
  nodeData?: any;
706
706
  parentLabel?: string;
707
707
  children?: TreeNode[];
708
+ /** Leading icon. On leaves it replaces the default bullet; on parents it
709
+ * renders between the chevron and the label. */
710
+ icon?: react__default.ReactNode;
708
711
  }
709
712
  interface TreeItemClickPayload {
710
713
  isParent: boolean;
@@ -718,6 +721,9 @@ interface TreeProps {
718
721
  onNodeClick: (payload: TreeItemClickPayload) => void;
719
722
  defaultExpandAll?: boolean;
720
723
  defaultExpandedKeys?: string[];
724
+ /** Default leading icon for every leaf (instead of the bullet dot). A
725
+ * node's own `icon` takes precedence. */
726
+ leafIcon?: react__default.ReactNode;
721
727
  /** Extra classes merged onto the tree root. */
722
728
  className?: string;
723
729
  /** Inline style on the tree root. */
@@ -738,7 +744,7 @@ interface TreeProps {
738
744
  * defaultExpandAll
739
745
  * />
740
746
  */
741
- declare function Tree({ nodes, onNodeClick, defaultExpandAll, defaultExpandedKeys, className, style, }: TreeProps): react_jsx_runtime.JSX.Element;
747
+ declare function Tree({ nodes, onNodeClick, defaultExpandAll, defaultExpandedKeys, leafIcon, className, style, }: TreeProps): react_jsx_runtime.JSX.Element;
742
748
 
743
749
  type AccordionVariant = 'separated' | 'contained';
744
750
  interface AccordionProps {
@@ -2082,10 +2088,22 @@ interface ScalableContainerProps {
2082
2088
  width?: react__default.CSSProperties['width'];
2083
2089
  /** Resting height. Any CSS length / percent. Default `'auto'`. */
2084
2090
  height?: react__default.CSSProperties['height'];
2085
- /** Width when expanded. Default `'100%'` (fills parent). */
2091
+ /**
2092
+ * Width when expanded. A concrete value (e.g. `900` or `'60rem'`) makes the
2093
+ * container grow even when its resting width is `'100%'` inside a flex/grid
2094
+ * cell. Falls back to {@link ScalableContainerProps.expandedWidth}, then
2095
+ * `'100%'`.
2096
+ */
2097
+ targetWidth?: react__default.CSSProperties['width'];
2098
+ /**
2099
+ * Height when expanded. A concrete value (e.g. `600`) lets the container
2100
+ * grow taller and push whatever sits below it further down. Falls back to
2101
+ * {@link ScalableContainerProps.expandedHeight}, then `'100%'`.
2102
+ */
2103
+ targetHeight?: react__default.CSSProperties['height'];
2104
+ /** @deprecated Use `targetWidth`. */
2086
2105
  expandedWidth?: react__default.CSSProperties['width'];
2087
- /** Height when expanded. Default `'100%'`. Set a concrete value (e.g. 420)
2088
- * when the container lives in normal flow and should push siblings down. */
2106
+ /** @deprecated Use `targetHeight`. */
2089
2107
  expandedHeight?: react__default.CSSProperties['height'];
2090
2108
  /** Controlled expanded state. */
2091
2109
  expanded?: boolean;
@@ -2104,60 +2122,38 @@ interface ScalableContainerProps {
2104
2122
  * Default `'top-right'` — matches the OS-window convention.
2105
2123
  */
2106
2124
  togglePosition?: 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right';
2107
- /**
2108
- * Bounding element the expansion is allowed to grow within. When the
2109
- * container sits in size-constrained flex-item wrappers (where width /
2110
- * height 100% makes expand-in-place a no-op), providing this ref switches
2111
- * to PUSH expansion: the container's row becomes `flex-wrap: wrap` and the
2112
- * container takes the FULL parent width (`flex-basis: 100%`), so sibling
2113
- * charts reflow below it at full width — pushed, visible, and intact.
2114
- * Column levels between the container and this element get `flex-grow`
2115
- * raised so the expanded row dominates vertically too. Collapsing
2116
- * restores the wrappers' original sizing exactly. Omit for the classic
2117
- * expand-in-place behaviour.
2118
- */
2125
+ /** @deprecated No longer used — set `targetWidth`/`targetHeight` instead. */
2119
2126
  expandContainerRef?: react__default.RefObject<HTMLElement | null>;
2120
- /**
2121
- * Vertical dominance of the pushed expansion (push mode only). Default
2122
- * `3` — the expanded chart keeps expandRatio/(expandRatio+1) of its row's
2123
- * height (and its row takes expandRatio parts per sibling row), leaving
2124
- * the reflowed siblings the remainder. Raise for a more fullscreen feel,
2125
- * lower for a gentler split.
2126
- */
2127
+ /** @deprecated No longer used — set `targetWidth`/`targetHeight` instead. */
2127
2128
  expandRatio?: number;
2128
2129
  /** Extra classes merged onto the container root. */
2129
2130
  className?: string;
2130
2131
  }
2131
2132
  /**
2132
- * Container that smoothly expands on click and collapses back to its
2133
- * resting size. Reads like a macOS / Windows window resizing — subtle
2134
- * elevation shift, smooth scale, no flash of colour or harsh background
2135
- * change.
2136
- *
2137
- * Two expansion modes:
2138
- * - **In place** (default): animates the container's own width/height to
2139
- * `expandedWidth`/`expandedHeight`.
2140
- * - **Push** (`expandContainerRef` set): for containers whose resting size is
2141
- * owned by flex-item wrappers. Expanding makes the container span the FULL
2142
- * parent width (its row becomes `flex-wrap: wrap` and the wrapper takes
2143
- * `flex-basis: 100%`), with sibling charts reflowing below it at full
2144
- * width — visible and intact, not squeezed. Column levels raise `flex-grow`
2145
- * so the expanded row also dominates vertically. Collapsing restores the
2146
- * original layout exactly.
2133
+ * Container that smoothly grows to a target size on click and collapses back.
2134
+ * Reads like an OS window resize — subtle elevation lift, smooth scale, no
2135
+ * colour change.
2136
+ *
2137
+ * Expansion grows the container's OWN box to `targetWidth` × `targetHeight` and
2138
+ * makes it `flex-none` while expanded, so it holds that size and simply pushes
2139
+ * its neighbours along the flow they keep their own dimensions and reflow
2140
+ * (e.g. wrap onto the next row / move down) rather than being squeezed. Nothing
2141
+ * on sibling elements is mutated, so layouts restore exactly on collapse. For
2142
+ * neighbours to reflow *below*, give the parent room to wrap (e.g. a
2143
+ * `flex flex-wrap` row, or block/auto-rows-grid flow).
2147
2144
  *
2148
2145
  * @example
2149
2146
  * ```tsx
2150
- * <ScalableContainer width={480} height={300}>
2151
- * <Chart data={metrics} />
2152
- * </ScalableContainer>
2153
- *
2154
- * // Push mode inside a flex grid:
2155
- * const sectionRef = useRef<HTMLDivElement>(null)
2156
- * <div ref={sectionRef} className="flex flex-col flex-1 min-h-0 gap-2">…
2157
- * <ScalableContainer width="100%" height="100%" expandContainerRef={sectionRef}>
2147
+ * // In a flex-wrap chart grid — expands to a concrete size and pushes the rest down.
2148
+ * <div className="flex flex-wrap gap-2">
2149
+ * <ScalableContainer width="100%" height={240} targetWidth="100%" targetHeight={520}>
2150
+ * <Chart data={metrics} />
2151
+ * </ScalableContainer>
2152
+ *
2153
+ * </div>
2158
2154
  * ```
2159
2155
  */
2160
- declare function ScalableContainer({ width, height, expandedWidth, expandedHeight, expanded, onExpandedChange, children, assignClassOnClick, expandIcon, collapseIcon, togglePosition, expandContainerRef, expandRatio, className, }: ScalableContainerProps): react_jsx_runtime.JSX.Element;
2156
+ declare function ScalableContainer({ width, height, targetWidth, targetHeight, expandedWidth, expandedHeight, expanded, onExpandedChange, children, assignClassOnClick, expandIcon, collapseIcon, togglePosition, className, }: ScalableContainerProps): react_jsx_runtime.JSX.Element;
2161
2157
 
2162
2158
  interface GridCardItem {
2163
2159
  key: string | number;
package/dist/index.js CHANGED
@@ -110,9 +110,9 @@ function Box({
110
110
  style,
111
111
  children
112
112
  }) {
113
- const Element = as ?? "div";
113
+ const Element2 = as ?? "div";
114
114
  return /* @__PURE__ */ jsx(
115
- Element,
115
+ Element2,
116
116
  {
117
117
  onClick,
118
118
  className: cx(
@@ -435,9 +435,9 @@ function Typography({
435
435
  style,
436
436
  children
437
437
  }) {
438
- const Element = as ?? DEFAULT_ELEMENT[variant];
438
+ const Element2 = as ?? DEFAULT_ELEMENT[variant];
439
439
  return /* @__PURE__ */ jsx(
440
- Element,
440
+ Element2,
441
441
  {
442
442
  className: cx(
443
443
  VARIANT_CLASS[variant],
@@ -1177,7 +1177,7 @@ function TabsTrigger({ value, icon, badge, closeable, onClose, disabled, classNa
1177
1177
  {
1178
1178
  value,
1179
1179
  disabled,
1180
- className: cx(base, sz.trigger, layoutCls, closeable ? "pr-8" : "", variantCls, className),
1180
+ className: cx(base, sz.trigger, layoutCls, closeable ? "!pr-8" : "", variantCls, className),
1181
1181
  children: [
1182
1182
  variant === "segmented" && isActive && /* @__PURE__ */ jsx(
1183
1183
  motion.span,
@@ -1262,9 +1262,11 @@ function TreeNodeItem({
1262
1262
  item,
1263
1263
  onNodeClick,
1264
1264
  defaultExpandAll,
1265
- defaultExpandedKeys
1265
+ defaultExpandedKeys,
1266
+ leafIcon
1266
1267
  }) {
1267
1268
  if (!isParent(item)) {
1269
+ const glyph = item.icon ?? leafIcon;
1268
1270
  return /* @__PURE__ */ jsxs(
1269
1271
  "button",
1270
1272
  {
@@ -1278,7 +1280,10 @@ function TreeNodeItem({
1278
1280
  parentLabel: item.parentLabel
1279
1281
  }),
1280
1282
  children: [
1281
- /* @__PURE__ */ jsx("span", { className: "w-1.5 h-1.5 rounded-full flex-shrink-0 bg-foreground-muted group-hover:bg-accent transition-colors duration-150" }),
1283
+ glyph != null ? /* @__PURE__ */ jsx("span", { className: "flex h-4 w-4 flex-shrink-0 items-center justify-center text-foreground-muted group-hover:text-accent transition-colors duration-150", children: glyph }) : (
1284
+ /* Default leaf bullet */
1285
+ /* @__PURE__ */ jsx("span", { className: "w-1.5 h-1.5 rounded-full flex-shrink-0 bg-foreground-muted group-hover:bg-accent transition-colors duration-150" })
1286
+ ),
1282
1287
  /* @__PURE__ */ jsx("span", { className: "text-sm text-foreground-secondary group-hover:text-foreground transition-colors duration-150", children: item.label })
1283
1288
  ]
1284
1289
  }
@@ -1286,48 +1291,53 @@ function TreeNodeItem({
1286
1291
  }
1287
1292
  const initialOpen = defaultExpandAll || defaultExpandedKeys.includes(item.key) ? [item.key] : [];
1288
1293
  return /* @__PURE__ */ jsx(AccordionPrimitive.Root, { type: "multiple", defaultValue: initialOpen, children: /* @__PURE__ */ jsxs(AccordionPrimitive.Item, { value: item.key, className: "border-none", children: [
1289
- /* @__PURE__ */ jsxs(
1290
- AccordionPrimitive.Trigger,
1291
- {
1292
- onClick: () => onNodeClick({
1293
- isParent: true,
1294
- key: item.key,
1295
- label: item.label,
1296
- data: item.nodeData,
1297
- parentLabel: item.parentLabel
1298
- }),
1299
- className: "flex items-center gap-2 cursor-pointer py-1.5 px-2 group focus:outline-none focus-visible:ring-2 focus-visible:ring-accent w-full text-left rounded-md hover:bg-surface-raised transition-colors duration-150",
1300
- children: [
1301
- /* @__PURE__ */ jsx(
1294
+ /* @__PURE__ */ jsx(AccordionPrimitive.Header, { asChild: true, children: /* @__PURE__ */ jsxs("div", { className: "flex items-center rounded-md hover:bg-surface-raised transition-colors duration-150", children: [
1295
+ /* @__PURE__ */ jsx(
1296
+ AccordionPrimitive.Trigger,
1297
+ {
1298
+ "aria-label": `Toggle ${item.label}`,
1299
+ className: "group flex flex-shrink-0 items-center justify-center rounded-md p-1.5 cursor-pointer text-foreground-muted hover:text-foreground focus:outline-none focus-visible:ring-2 focus-visible:ring-accent focus-visible:ring-inset transition-colors duration-150",
1300
+ children: /* @__PURE__ */ jsx(
1302
1301
  "svg",
1303
1302
  {
1304
1303
  viewBox: "0 0 24 24",
1305
1304
  fill: "none",
1306
1305
  stroke: "currentColor",
1307
1306
  strokeWidth: 2.5,
1308
- className: "h-3.5 w-3.5 flex-shrink-0 text-foreground-muted transition-transform duration-200 group-data-[state=open]:rotate-0 group-data-[state=closed]:-rotate-90",
1307
+ className: "h-3.5 w-3.5 transition-transform duration-200 group-data-[state=open]:rotate-0 group-data-[state=closed]:-rotate-90",
1309
1308
  "aria-hidden": "true",
1310
- children: /* @__PURE__ */ jsx(
1311
- "path",
1312
- {
1313
- strokeLinecap: "round",
1314
- strokeLinejoin: "round",
1315
- d: "M19 9l-7 7-7-7"
1316
- }
1317
- )
1309
+ children: /* @__PURE__ */ jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M19 9l-7 7-7-7" })
1318
1310
  }
1319
- ),
1320
- /* @__PURE__ */ jsx("span", { className: "text-sm font-semibold text-foreground select-none", children: item.label })
1321
- ]
1322
- }
1323
- ),
1311
+ )
1312
+ }
1313
+ ),
1314
+ /* @__PURE__ */ jsxs(
1315
+ "button",
1316
+ {
1317
+ type: "button",
1318
+ onClick: () => onNodeClick({
1319
+ isParent: true,
1320
+ key: item.key,
1321
+ label: item.label,
1322
+ data: item.nodeData,
1323
+ parentLabel: item.parentLabel
1324
+ }),
1325
+ className: "flex flex-1 min-w-0 items-center gap-2 py-1.5 pr-2 text-left cursor-pointer rounded-md focus:outline-none focus-visible:ring-2 focus-visible:ring-accent focus-visible:ring-inset",
1326
+ children: [
1327
+ item.icon != null && /* @__PURE__ */ jsx("span", { className: "flex h-4 w-4 flex-shrink-0 items-center justify-center text-foreground-muted", children: item.icon }),
1328
+ /* @__PURE__ */ jsx("span", { className: "truncate text-sm font-semibold text-foreground select-none", children: item.label })
1329
+ ]
1330
+ }
1331
+ )
1332
+ ] }) }),
1324
1333
  /* @__PURE__ */ jsx(AccordionPrimitive.Content, { className: "overflow-hidden data-[state=open]:animate-accordion-down data-[state=closed]:animate-accordion-up", children: /* @__PURE__ */ jsx("div", { className: "ml-3.5 border-l border-border py-0.5", children: item.children.map((child) => /* @__PURE__ */ jsx(
1325
1334
  TreeNodeItem,
1326
1335
  {
1327
1336
  item: child,
1328
1337
  onNodeClick,
1329
1338
  defaultExpandAll,
1330
- defaultExpandedKeys
1339
+ defaultExpandedKeys,
1340
+ leafIcon
1331
1341
  },
1332
1342
  child.key
1333
1343
  )) }) })
@@ -1338,6 +1348,7 @@ function Tree({
1338
1348
  onNodeClick,
1339
1349
  defaultExpandAll = false,
1340
1350
  defaultExpandedKeys = [],
1351
+ leafIcon,
1341
1352
  className = "",
1342
1353
  style
1343
1354
  }) {
@@ -1347,7 +1358,8 @@ function Tree({
1347
1358
  item,
1348
1359
  onNodeClick,
1349
1360
  defaultExpandAll,
1350
- defaultExpandedKeys
1361
+ defaultExpandedKeys,
1362
+ leafIcon
1351
1363
  },
1352
1364
  item.key
1353
1365
  )) });
@@ -4645,6 +4657,8 @@ var TOGGLE_POSITION_CLASS = {
4645
4657
  function ScalableContainer({
4646
4658
  width = "100%",
4647
4659
  height = "auto",
4660
+ targetWidth,
4661
+ targetHeight,
4648
4662
  expandedWidth = "100%",
4649
4663
  expandedHeight = "100%",
4650
4664
  expanded,
@@ -4654,100 +4668,40 @@ function ScalableContainer({
4654
4668
  expandIcon,
4655
4669
  collapseIcon,
4656
4670
  togglePosition = "top-right",
4657
- expandContainerRef,
4658
- expandRatio = 3,
4659
4671
  className = ""
4660
4672
  }) {
4661
4673
  const containerRef = useRef(null);
4662
4674
  const [internalScaled, setInternalScaled] = useState(false);
4663
4675
  const isScaled = expanded ?? internalScaled;
4664
4676
  const reduced = useReducedMotion();
4665
- const usePush = expandContainerRef != null;
4666
- const grownRef = useRef([]);
4667
4677
  const prevScaled = useRef(isScaled);
4668
- const kickResizeDuringTransition = () => {
4678
+ const expandW = targetWidth ?? expandedWidth;
4679
+ const expandH = targetHeight ?? expandedHeight;
4680
+ useEffect(() => {
4681
+ if (isScaled === prevScaled.current) return;
4682
+ prevScaled.current = isScaled;
4669
4683
  if (typeof window === "undefined") return;
4670
4684
  const kick = () => window.dispatchEvent(new Event("resize"));
4671
4685
  const interval = window.setInterval(kick, 80);
4672
- window.setTimeout(() => {
4686
+ const stop = window.setTimeout(() => {
4673
4687
  window.clearInterval(interval);
4674
4688
  kick();
4675
- }, reduced ? 0 : 400);
4676
- };
4677
- const growAncestors = () => {
4678
- const bound = expandContainerRef?.current;
4679
- if (!bound || !containerRef.current) return;
4680
- const grown = [];
4681
- let el = containerRef.current.parentElement;
4682
- while (el && el !== bound && bound.contains(el)) {
4683
- const parent = el.parentElement;
4684
- const parentStyle = parent ? getComputedStyle(parent) : null;
4685
- if (parent && parentStyle && parentStyle.display.includes("flex")) {
4686
- grown.push({
4687
- el,
4688
- parent,
4689
- prev: {
4690
- flexGrow: el.style.flexGrow,
4691
- flexBasis: el.style.flexBasis,
4692
- height: el.style.height,
4693
- transition: el.style.transition,
4694
- parentFlexWrap: parent.style.flexWrap
4695
- }
4696
- });
4697
- const t = (prop) => `${prop} ${reduced ? 0 : 0.32}s cubic-bezier(0.16, 1, 0.3, 1)`;
4698
- const ours = `${t("flex-grow")}, ${t("flex-basis")}, ${t("height")}`;
4699
- el.style.transition = el.style.transition ? `${el.style.transition}, ${ours}` : ours;
4700
- if (parentStyle.flexDirection.startsWith("row")) {
4701
- parent.style.flexWrap = "wrap";
4702
- el.style.flexBasis = "100%";
4703
- el.style.height = `${expandRatio / (expandRatio + 1) * 100}%`;
4704
- } else {
4705
- el.style.flexBasis = "0%";
4706
- el.style.flexGrow = String(expandRatio);
4707
- }
4708
- }
4709
- el = parent;
4710
- }
4711
- grownRef.current = grown;
4712
- };
4713
- const restoreAncestors = () => {
4714
- for (const { el, parent, prev } of grownRef.current) {
4715
- el.style.flexGrow = prev.flexGrow;
4716
- el.style.flexBasis = prev.flexBasis;
4717
- el.style.height = prev.height;
4718
- window.setTimeout(() => {
4719
- el.style.transition = prev.transition;
4720
- parent.style.flexWrap = prev.parentFlexWrap;
4721
- }, reduced ? 0 : 360);
4722
- }
4723
- grownRef.current = [];
4724
- };
4725
- useEffect(() => {
4726
- if (!usePush || isScaled === prevScaled.current) return;
4727
- prevScaled.current = isScaled;
4728
- if (isScaled) growAncestors();
4729
- else restoreAncestors();
4730
- kickResizeDuringTransition();
4731
- }, [isScaled, usePush]);
4732
- useEffect(() => () => {
4733
- for (const { el, parent, prev } of grownRef.current) {
4734
- el.style.flexGrow = prev.flexGrow;
4735
- el.style.flexBasis = prev.flexBasis;
4736
- el.style.height = prev.height;
4737
- el.style.transition = prev.transition;
4738
- parent.style.flexWrap = prev.parentFlexWrap;
4689
+ }, reduced ? 0 : 420);
4690
+ if (isScaled) {
4691
+ window.setTimeout(
4692
+ () => containerRef.current?.scrollIntoView({ behavior: "smooth", block: "nearest" }),
4693
+ reduced ? 0 : 360
4694
+ );
4739
4695
  }
4740
- }, []);
4696
+ return () => {
4697
+ window.clearInterval(interval);
4698
+ window.clearTimeout(stop);
4699
+ };
4700
+ }, [isScaled, reduced]);
4741
4701
  const onToggle = () => {
4742
4702
  const next = !isScaled;
4743
4703
  if (expanded === void 0) setInternalScaled(next);
4744
4704
  onExpandedChange?.(next);
4745
- if (next && !usePush) {
4746
- window.setTimeout(
4747
- () => containerRef.current?.scrollIntoView({ behavior: "smooth", block: "nearest" }),
4748
- reduced ? 0 : 340
4749
- );
4750
- }
4751
4705
  };
4752
4706
  const wrapperClass = isScaled ? assignClassOnClick : void 0;
4753
4707
  return /* @__PURE__ */ jsxs(
@@ -4755,24 +4709,20 @@ function ScalableContainer({
4755
4709
  {
4756
4710
  ref: containerRef,
4757
4711
  style: {
4758
- width: isScaled && !usePush ? expandedWidth : width,
4759
- height: isScaled && !usePush ? expandedHeight : height
4760
- },
4761
- animate: {
4762
- // Push mode keeps the container filling its (now growing)
4763
- // wrapper — the wrapper's flex-grow does the work.
4764
- width: isScaled && !usePush ? expandedWidth : width,
4765
- height: isScaled && !usePush ? expandedHeight : height
4712
+ width: isScaled ? expandW : width,
4713
+ height: isScaled ? expandH : height,
4714
+ flex: isScaled ? "none" : void 0
4766
4715
  },
4716
+ animate: { width: isScaled ? expandW : width, height: isScaled ? expandH : height },
4767
4717
  transition: reduced ? { duration: 0 } : {
4768
4718
  width: { type: "tween", duration: 0.32, ease: [0.16, 1, 0.3, 1] },
4769
4719
  height: { type: "tween", duration: 0.32, ease: [0.16, 1, 0.3, 1] }
4770
4720
  },
4771
4721
  className: cx(
4772
4722
  "relative rounded-lg overflow-hidden",
4773
- // OS-window aesthetic: subtle elevation at rest, lifted shadow
4774
- // when expanded. No background colour change.
4775
- isScaled ? "shadow-2xl" : "shadow-md",
4723
+ // OS-window aesthetic: subtle elevation at rest, lifted shadow +
4724
+ // raised stacking when expanded so it sits above neighbours.
4725
+ isScaled ? "z-raised shadow-2xl" : "shadow-md",
4776
4726
  "transition-shadow duration-300",
4777
4727
  className
4778
4728
  ),
@@ -5536,14 +5486,33 @@ function EditableCell({
5536
5486
  onCellEdit
5537
5487
  }) {
5538
5488
  const [editing, setEditing] = useState(false);
5489
+ const editRef = useRef(null);
5539
5490
  const value = row[col.keyBind];
5540
5491
  const commit = (next) => {
5541
5492
  setEditing(false);
5542
5493
  onCellEdit?.({ row, key: col.keyBind, value: next, rowIndex });
5543
5494
  };
5544
5495
  const cancel = () => setEditing(false);
5496
+ useEffect(() => {
5497
+ if (!editing || !col.editor) return;
5498
+ const onMouseDown = (e) => {
5499
+ const target = e.target;
5500
+ if (editRef.current && editRef.current.contains(target)) return;
5501
+ if (target instanceof Element && target.closest("[data-radix-popper-content-wrapper],[data-radix-portal]")) return;
5502
+ cancel();
5503
+ };
5504
+ const onKeyDown = (e) => {
5505
+ if (e.key === "Escape") cancel();
5506
+ };
5507
+ document.addEventListener("mousedown", onMouseDown);
5508
+ document.addEventListener("keydown", onKeyDown);
5509
+ return () => {
5510
+ document.removeEventListener("mousedown", onMouseDown);
5511
+ document.removeEventListener("keydown", onKeyDown);
5512
+ };
5513
+ }, [editing, col.editor]);
5545
5514
  if (editing) {
5546
- if (col.editor) return /* @__PURE__ */ jsx(Fragment, { children: col.editor({ value, row, commit, cancel }) });
5515
+ if (col.editor) return /* @__PURE__ */ jsx("div", { ref: editRef, children: col.editor({ value, row, commit, cancel }) });
5547
5516
  return /* @__PURE__ */ jsx(
5548
5517
  "input",
5549
5518
  {