@dashfoo/react 0.8.3 → 0.9.0

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
@@ -13,7 +13,7 @@ import { Group, Panel as Panel$1, Separator } from "react-resizable-panels";
13
13
  const createTopmostPointerIntersection = (topElementAt = (x, y) => document.elementFromPoint(x, y)) => {
14
14
  let cached = null;
15
15
  const topAt = (x, y) => {
16
- if (cached && cached.x === x && cached.y === y) return cached.top;
16
+ if (cached?.x === x && cached.y === y) return cached.top;
17
17
  const top = topElementAt(x, y);
18
18
  cached = {
19
19
  top,
@@ -602,7 +602,7 @@ const insertionIndex = (rects, pointerX) => {
602
602
  };
603
603
  const INSERTION_LINE_WIDTH = 4;
604
604
  const insertionLineRect = (stripRect, itemRects, index) => {
605
- const at = itemRects[index];
605
+ const at = itemRects.at(index);
606
606
  const last = itemRects.at(-1);
607
607
  const left = (at?.x ?? (last ? last.x + last.width : stripRect.x)) - INSERTION_LINE_WIDTH / 2;
608
608
  return {
@@ -620,26 +620,16 @@ const shouldAllowDrop = (draggedId, targetId, tabIds) => {
620
620
 
621
621
  //#endregion
622
622
  //#region src/components/drag-preview-overlay.tsx
623
- const overlayBase = {
624
- boxSizing: "border-box",
625
- pointerEvents: "none",
626
- position: "fixed",
627
- transition: "var(--dashfoo-dock-transition, left 60ms, top 60ms, width 60ms, height 60ms)",
628
- zIndex: 9999
629
- };
630
- const paneStyle = (zone) => ({
631
- ...overlayBase,
632
- background: "var(--dashfoo-dock-fill, oklch(0.556 0 0 / 0.18))",
633
- border: "var(--dashfoo-dock-border-width, 1px) solid var(--dashfoo-dock-border, oklch(0.708 0 0 / 0.75))",
634
- borderRadius: "var(--dashfoo-dock-radius, 6px)",
635
- height: zone.height,
636
- left: zone.x,
637
- top: zone.y,
638
- width: zone.width
639
- });
640
- const lineStyle = (zone) => ({
641
- ...paneStyle(zone),
642
- borderRadius: "var(--dashfoo-dock-line-radius, 2px)"
623
+ const DockZone = ({ insertion = false, zIndex, zone }) => /* @__PURE__ */ jsx("div", {
624
+ "data-dashfoo": "dock-indicator",
625
+ "data-insertion": insertion || void 0,
626
+ style: {
627
+ "--dashfoo-dock-height": `${zone.height}px`,
628
+ "--dashfoo-dock-left": `${zone.x}px`,
629
+ "--dashfoo-dock-top": `${zone.y}px`,
630
+ "--dashfoo-dock-width": `${zone.width}px`,
631
+ "--dashfoo-dock-z": zIndex
632
+ }
643
633
  });
644
634
  const tabItemRects = (strip, excludeId) => [...strip.querySelectorAll("[data-dashfoo=\"tab-item\"]")].flatMap((item) => item.querySelector("[data-dashfoo=\"tab\"]")?.dataset.tabId === excludeId ? [] : [item.getBoundingClientRect()]);
645
635
  const DockIndicator = ({ actorRef, getTabsetElement }) => {
@@ -649,27 +639,22 @@ const DockIndicator = ({ actorRef, getTabsetElement }) => {
649
639
  const draggedId = drag.subject.id;
650
640
  const element = getTabsetElement(intent.targetId);
651
641
  if (!element) return null;
652
- const zIndex = element.closest("[data-dashfoo=\"float\"]") ? overlayBase.zIndex : "auto";
642
+ const zIndex = element.closest("[data-dashfoo=\"float\"]") ? 9999 : "auto";
653
643
  if (intent.location === "center") {
654
644
  const strip = element.querySelector("[data-dashfoo=\"tabstrip\"]");
655
645
  if (strip) {
656
646
  const line = insertionLineRect(strip.getBoundingClientRect(), tabItemRects(strip, draggedId), intent.index ?? 0);
657
- return /* @__PURE__ */ jsx("div", {
658
- "data-dashfoo": "dock-indicator",
659
- style: {
660
- ...lineStyle(line),
661
- zIndex
662
- }
647
+ return /* @__PURE__ */ jsx(DockZone, {
648
+ insertion: true,
649
+ zIndex,
650
+ zone: line
663
651
  });
664
652
  }
665
653
  }
666
654
  const zone = zoneRect(element.getBoundingClientRect(), intent.location);
667
- return /* @__PURE__ */ jsx("div", {
668
- "data-dashfoo": "dock-indicator",
669
- style: {
670
- ...paneStyle(zone),
671
- zIndex
672
- }
655
+ return /* @__PURE__ */ jsx(DockZone, {
656
+ zIndex,
657
+ zone
673
658
  });
674
659
  };
675
660
  const PREVIEW_OFFSET = {
@@ -714,11 +699,8 @@ const DragPreviewOverlay = ({ manager }) => {
714
699
  children: chip === null ? null : /* @__PURE__ */ jsx("div", {
715
700
  "data-dashfoo": "drag-preview",
716
701
  style: {
717
- left: 0,
718
- position: "absolute",
719
- top: 0,
720
- transform: `translate(${chip.x}px, ${chip.y}px)`,
721
- width: "max-content"
702
+ "--dashfoo-preview-x": `${chip.x}px`,
703
+ "--dashfoo-preview-y": `${chip.y}px`
722
704
  },
723
705
  children: chip.label
724
706
  })
@@ -960,119 +942,62 @@ const useHasFloatLayer = () => useContext(FloatLayerContext);
960
942
 
961
943
  //#endregion
962
944
  //#region src/components/float-resize-handles.ts
963
- const EDGE_THICKNESS = 12;
964
- const EDGE_OFFSET = -6;
965
- const CORNER_SIZE = 20;
966
- const CORNER_OFFSET = -8;
967
- const EDGE_INSET = 12;
968
945
  const RESIZE_HANDLES = [
969
946
  {
970
947
  edges: {
971
948
  x: 0,
972
949
  y: -1
973
950
  },
974
- key: "n",
975
- style: {
976
- cursor: "ns-resize",
977
- height: EDGE_THICKNESS,
978
- insetInline: EDGE_INSET,
979
- top: EDGE_OFFSET
980
- }
951
+ key: "n"
981
952
  },
982
953
  {
983
954
  edges: {
984
955
  x: 0,
985
956
  y: 1
986
957
  },
987
- key: "s",
988
- style: {
989
- bottom: EDGE_OFFSET,
990
- cursor: "ns-resize",
991
- height: EDGE_THICKNESS,
992
- insetInline: EDGE_INSET
993
- }
958
+ key: "s"
994
959
  },
995
960
  {
996
961
  edges: {
997
962
  x: 1,
998
963
  y: 0
999
964
  },
1000
- key: "e",
1001
- style: {
1002
- cursor: "ew-resize",
1003
- insetBlock: EDGE_INSET,
1004
- right: EDGE_OFFSET,
1005
- width: EDGE_THICKNESS
1006
- }
965
+ key: "e"
1007
966
  },
1008
967
  {
1009
968
  edges: {
1010
969
  x: -1,
1011
970
  y: 0
1012
971
  },
1013
- key: "w",
1014
- style: {
1015
- cursor: "ew-resize",
1016
- insetBlock: EDGE_INSET,
1017
- left: EDGE_OFFSET,
1018
- width: EDGE_THICKNESS
1019
- }
972
+ key: "w"
1020
973
  },
1021
974
  {
1022
975
  edges: {
1023
976
  x: 1,
1024
977
  y: -1
1025
978
  },
1026
- key: "ne",
1027
- style: {
1028
- cursor: "nesw-resize",
1029
- height: CORNER_SIZE,
1030
- right: CORNER_OFFSET,
1031
- top: CORNER_OFFSET,
1032
- width: CORNER_SIZE
1033
- }
979
+ key: "ne"
1034
980
  },
1035
981
  {
1036
982
  edges: {
1037
983
  x: -1,
1038
984
  y: -1
1039
985
  },
1040
- key: "nw",
1041
- style: {
1042
- cursor: "nwse-resize",
1043
- height: CORNER_SIZE,
1044
- left: CORNER_OFFSET,
1045
- top: CORNER_OFFSET,
1046
- width: CORNER_SIZE
1047
- }
986
+ key: "nw"
1048
987
  },
1049
988
  {
1050
989
  edges: {
1051
990
  x: 1,
1052
991
  y: 1
1053
992
  },
1054
- key: "se",
1055
- style: {
1056
- bottom: CORNER_OFFSET,
1057
- cursor: "nwse-resize",
1058
- height: CORNER_SIZE,
1059
- right: CORNER_OFFSET,
1060
- width: CORNER_SIZE
1061
- }
993
+ key: "se"
1062
994
  },
1063
995
  {
1064
996
  edges: {
1065
997
  x: -1,
1066
998
  y: 1
1067
999
  },
1068
- key: "sw",
1069
- style: {
1070
- bottom: CORNER_OFFSET,
1071
- cursor: "nesw-resize",
1072
- height: CORNER_SIZE,
1073
- left: CORNER_OFFSET,
1074
- width: CORNER_SIZE
1075
- }
1000
+ key: "sw"
1076
1001
  }
1077
1002
  ];
1078
1003
  const EDGE_BY_KEY = new Map(RESIZE_HANDLES.map((h) => [h.key, h.edges]));
@@ -1130,8 +1055,9 @@ const CHIP_SIZE = {
1130
1055
  width: 168
1131
1056
  };
1132
1057
  const TAP_SLOP = 4;
1133
- /** Writes inline style and dispatches only on pointer-up: routing every move
1134
- * through the model would re-render the whole tree behind the float. */
1058
+ const releaseCapture = (panel, pointerId) => {
1059
+ if (panel.hasPointerCapture?.(pointerId)) panel.releasePointerCapture(pointerId);
1060
+ };
1135
1061
  const useFloatGesture = ({ editable, geometry, minimized, onCommit, onTap }) => {
1136
1062
  const panelRef = useRef(null);
1137
1063
  const gestureRef = useRef(null);
@@ -1142,7 +1068,7 @@ const useFloatGesture = ({ editable, geometry, minimized, onCommit, onTap }) =>
1142
1068
  const panel = panelRef.current;
1143
1069
  if (!panel || !editable) return;
1144
1070
  const prior = gestureRef.current;
1145
- if (prior && panel.hasPointerCapture?.(prior.pointerId)) panel.releasePointerCapture(prior.pointerId);
1071
+ if (prior) releaseCapture(panel, prior.pointerId);
1146
1072
  const edgeKey = event.currentTarget.dataset.edge;
1147
1073
  const parent = panel.offsetParent instanceof HTMLElement ? panel.offsetParent : null;
1148
1074
  const rect = panel.getBoundingClientRect();
@@ -1159,10 +1085,10 @@ const useFloatGesture = ({ editable, geometry, minimized, onCommit, onTap }) =>
1159
1085
  },
1160
1086
  edges: edgeKey === void 0 ? null : EDGE_BY_KEY.get(edgeKey) ?? null,
1161
1087
  initialStyle: {
1162
- height: panel.style.height,
1163
- left: panel.style.left,
1164
- top: panel.style.top,
1165
- width: panel.style.width
1088
+ "--dashfoo-float-height": panel.style.getPropertyValue("--dashfoo-float-height"),
1089
+ "--dashfoo-float-left": panel.style.getPropertyValue("--dashfoo-float-left"),
1090
+ "--dashfoo-float-top": panel.style.getPropertyValue("--dashfoo-float-top"),
1091
+ "--dashfoo-float-width": panel.style.getPropertyValue("--dashfoo-float-width")
1166
1092
  },
1167
1093
  latest: visibleGeometry,
1168
1094
  moved: false,
@@ -1179,7 +1105,7 @@ const useFloatGesture = ({ editable, geometry, minimized, onCommit, onTap }) =>
1179
1105
  if (!gesture || event.pointerId !== gesture.pointerId) return;
1180
1106
  gestureRef.current = null;
1181
1107
  const panel = panelRef.current;
1182
- if (panel?.hasPointerCapture?.(event.pointerId) === true) panel.releasePointerCapture(event.pointerId);
1108
+ if (panel) releaseCapture(panel, event.pointerId);
1183
1109
  if (!gesture.moved) {
1184
1110
  onTap();
1185
1111
  return;
@@ -1207,11 +1133,11 @@ const useFloatGesture = ({ editable, geometry, minimized, onCommit, onTap }) =>
1207
1133
  top: gesture.start.top + dy
1208
1134
  }, gesture.bounds, minimized ? CHIP_SIZE : void 0);
1209
1135
  gesture.latest = next;
1210
- panel.style.left = `${next.left}px`;
1211
- panel.style.top = `${next.top}px`;
1136
+ panel.style.setProperty("--dashfoo-float-left", `${next.left}px`);
1137
+ panel.style.setProperty("--dashfoo-float-top", `${next.top}px`);
1212
1138
  if (!minimized) {
1213
- panel.style.width = `${next.width}px`;
1214
- panel.style.height = `${next.height}px`;
1139
+ panel.style.setProperty("--dashfoo-float-width", `${next.width}px`);
1140
+ panel.style.setProperty("--dashfoo-float-height", `${next.height}px`);
1215
1141
  }
1216
1142
  };
1217
1143
  const handlePointerCancel = (event) => {
@@ -1219,9 +1145,9 @@ const useFloatGesture = ({ editable, geometry, minimized, onCommit, onTap }) =>
1219
1145
  const panel = panelRef.current;
1220
1146
  if (!gesture || !panel || event.pointerId !== gesture.pointerId) return;
1221
1147
  gestureRef.current = null;
1222
- if (panel.hasPointerCapture?.(event.pointerId)) panel.releasePointerCapture(event.pointerId);
1148
+ releaseCapture(panel, event.pointerId);
1223
1149
  if (!gesture.moved) return;
1224
- Object.assign(panel.style, gesture.initialStyle);
1150
+ for (const [property, value] of Object.entries(gesture.initialStyle)) panel.style.setProperty(property, value);
1225
1151
  };
1226
1152
  return {
1227
1153
  handlers: {
@@ -1232,10 +1158,10 @@ const useFloatGesture = ({ editable, geometry, minimized, onCommit, onTap }) =>
1232
1158
  },
1233
1159
  ref: setPanel,
1234
1160
  style: {
1235
- height: minimized ? void 0 : `min(${geometry.height}px, 100dvh)`,
1236
- left: `clamp(0px, ${geometry.left}px, calc(100vw - min(${minimized ? CHIP_SIZE.width : geometry.width}px, 100vw)))`,
1237
- top: `clamp(0px, ${geometry.top}px, calc(100dvh - min(${minimized ? CHIP_SIZE.height : geometry.height}px, 100dvh)))`,
1238
- width: minimized ? void 0 : `min(${geometry.width}px, 100vw)`
1161
+ "--dashfoo-float-height": `${geometry.height}px`,
1162
+ "--dashfoo-float-left": `${geometry.left}px`,
1163
+ "--dashfoo-float-top": `${geometry.top}px`,
1164
+ "--dashfoo-float-width": `${geometry.width}px`
1239
1165
  }
1240
1166
  };
1241
1167
  };
@@ -1275,16 +1201,15 @@ const useInlineRename = ({ currentName, inputRef, onCommit, onDone }) => {
1275
1201
 
1276
1202
  //#endregion
1277
1203
  //#region src/components/close-icon.tsx
1278
- const iconStyle = { pointerEvents: "none" };
1279
1204
  const Icon = ({ children }) => /* @__PURE__ */ jsx("svg", {
1280
1205
  "aria-hidden": "true",
1206
+ "data-dashfoo": "icon",
1281
1207
  fill: "none",
1282
1208
  height: "12",
1283
1209
  stroke: "currentColor",
1284
1210
  strokeLinecap: "round",
1285
1211
  strokeLinejoin: "round",
1286
1212
  strokeWidth: "2",
1287
- style: iconStyle,
1288
1213
  viewBox: "0 0 24 24",
1289
1214
  width: "12",
1290
1215
  children
@@ -1365,12 +1290,6 @@ const DockIcon = () => /* @__PURE__ */ jsxs(Icon, { children: [
1365
1290
  //#endregion
1366
1291
  //#region src/components/layout-root.tsx
1367
1292
  const DEFAULT_TABSET_MIN_SIZE = 320;
1368
- const rootStyle = {
1369
- display: "flex",
1370
- height: "100%",
1371
- position: "relative",
1372
- width: "100%"
1373
- };
1374
1293
  const resolveCapabilities = (global, options) => {
1375
1294
  const structural = options.editable && options.restructurable;
1376
1295
  return {
@@ -1383,7 +1302,7 @@ const resolveCapabilities = (global, options) => {
1383
1302
  resizableSplits: structural && options.resizableSplits && global.enableSplitResize !== false
1384
1303
  };
1385
1304
  };
1386
- const LayoutRoot = ({ children, closableTabs = true, dispatch, draggableTabs = true, draggableTabsets = true, editable = true, floatable = false, keepMounted = false, maximizable = true, model, renamableTabs = true, renderers, resizableSplits = true, restructurable = true, rootRef, snap, style, ...props }) => {
1305
+ const LayoutRoot = ({ children, closableTabs = true, dispatch, draggableTabs = true, draggableTabsets = true, editable = true, floatable = false, keepMounted = false, maximizable = true, model, renamableTabs = true, renderers, resizableSplits = true, restructurable = true, rootRef, snap, ...props }) => {
1387
1306
  const global = model.global;
1388
1307
  const snapshot = {
1389
1308
  ...resolveCapabilities(global, {
@@ -1414,18 +1333,13 @@ const LayoutRoot = ({ children, closableTabs = true, dispatch, draggableTabs = t
1414
1333
  useLayoutEffect(() => {
1415
1334
  store.setState(snapshot);
1416
1335
  });
1417
- const layoutStyle = {
1418
- ...rootStyle,
1419
- ...style
1420
- };
1421
- if (global.splitterSize !== void 0) layoutStyle["--dashfoo-splitter-size"] = `${global.splitterSize}px`;
1422
1336
  return /* @__PURE__ */ jsx(LayoutStoreContext.Provider, {
1423
1337
  value: store,
1424
1338
  children: /* @__PURE__ */ jsx("div", {
1425
1339
  ...props,
1426
1340
  "data-dashfoo": "layout",
1427
1341
  ref: rootRef,
1428
- style: layoutStyle,
1342
+ style: { "--dashfoo-splitter-size": global.splitterSize === void 0 ? void 0 : `${global.splitterSize}px` },
1429
1343
  children
1430
1344
  })
1431
1345
  });
@@ -1447,7 +1361,6 @@ const LayoutOverrides = ({ children, overrides }) => {
1447
1361
  value: store,
1448
1362
  children: /* @__PURE__ */ jsx("div", {
1449
1363
  "data-dashfoo": "layout",
1450
- style: rootStyle,
1451
1364
  children
1452
1365
  })
1453
1366
  });
@@ -1472,10 +1385,9 @@ const layoutFromOrdered = (sizes, children) => children.reduce((layout, child, i
1472
1385
  return layout;
1473
1386
  }, {});
1474
1387
  const layoutsMatch = (current, expected) => {
1475
- const currentIds = Object.keys(current);
1476
- const expectedIds = Object.keys(expected);
1477
- if (currentIds.length !== expectedIds.length) return false;
1478
- return expectedIds.every((id) => Math.abs((current[id] ?? NaN) - expected[id]) <= WEIGHT_EPSILON);
1388
+ const expectedEntries = Object.entries(expected);
1389
+ if (Object.keys(current).length !== expectedEntries.length) return false;
1390
+ return expectedEntries.every(([id, weight]) => Math.abs((current[id] ?? NaN) - weight) <= WEIGHT_EPSILON);
1479
1391
  };
1480
1392
  const useSnapResize = ({ children, onWeightsSettled, resizableSplits, snap }) => {
1481
1393
  const groupRef = useRef(null);
@@ -1536,7 +1448,7 @@ const useSnapResize = ({ children, onWeightsSettled, resizableSplits, snap }) =>
1536
1448
  if (!resizableSplits) return;
1537
1449
  const weights = settleSnap(children.map((child) => layout[child.id] ?? child.weight), boundary, snap);
1538
1450
  if (!weights.some((weight, index) => {
1539
- const child = children[index];
1451
+ const child = children.at(index);
1540
1452
  return child !== void 0 && Math.abs(weight - child.weight) > WEIGHT_EPSILON;
1541
1453
  })) return;
1542
1454
  onWeightsSettled(weights);
@@ -1631,8 +1543,8 @@ const createTabsetStore = (initial) => createStore((set, get) => ({
1631
1543
  type: "selectTab"
1632
1544
  });
1633
1545
  if (options?.focus === true) {
1634
- const tabId = node.children[index]?.id;
1635
- if (tabId) tabButtonIn(tablistElement, node.id, tabId)?.focus();
1546
+ const tab = node.children[index];
1547
+ if (tab) tabButtonIn(tablistElement, node.id, tab.id)?.focus();
1636
1548
  }
1637
1549
  },
1638
1550
  startEditing: (tabId) => {
@@ -1669,11 +1581,6 @@ const useTab = () => {
1669
1581
 
1670
1582
  //#endregion
1671
1583
  //#region src/components/tabset/tabset-content.tsx
1672
- const contentStyle = {
1673
- flex: 1,
1674
- minHeight: 0,
1675
- overflow: "auto"
1676
- };
1677
1584
  const TabPanel = ({ render, tab }) => {
1678
1585
  const renderTab = useLayout((state) => state.renderTab);
1679
1586
  return render ? render(tab) : renderTab(tab);
@@ -1682,12 +1589,7 @@ const TabsetContent = forwardRef(({ children, style, ...props }, ref) => {
1682
1589
  const node = useTabset((state) => state.node);
1683
1590
  const editingTabId = useTabset((state) => state.editingTabId);
1684
1591
  const visualSelected = useTabset((state) => state.visualSelected);
1685
- const keepMounted = useLayout((state) => state.keepMounted);
1686
- const mergedStyle = {
1687
- ...contentStyle,
1688
- ...style
1689
- };
1690
- if (keepMounted && node.children.length > 0) return node.children.map((tab, index) => /* @__PURE__ */ createElement("div", {
1592
+ if (useLayout((state) => state.keepMounted) && node.children.length > 0) return node.children.map((tab, index) => /* @__PURE__ */ createElement("div", {
1691
1593
  ...props,
1692
1594
  "aria-label": editingTabId === tab.id ? tab.name : void 0,
1693
1595
  "aria-labelledby": editingTabId === tab.id ? void 0 : tabDomId(node.id, tab.id),
@@ -1697,13 +1599,13 @@ const TabsetContent = forwardRef(({ children, style, ...props }, ref) => {
1697
1599
  key: tab.id,
1698
1600
  ref: index === visualSelected ? ref : void 0,
1699
1601
  role: index === visualSelected ? "tabpanel" : void 0,
1700
- style: mergedStyle,
1602
+ style,
1701
1603
  tabIndex: index === visualSelected ? 0 : void 0
1702
1604
  }, /* @__PURE__ */ jsx(TabPanel, {
1703
1605
  render: children,
1704
1606
  tab
1705
1607
  })));
1706
- const active = node.children[visualSelected];
1608
+ const active = visualSelected < 0 ? void 0 : node.children.at(visualSelected);
1707
1609
  if (active) return /* @__PURE__ */ jsx("div", {
1708
1610
  ...props,
1709
1611
  "aria-label": editingTabId === active.id ? active.name : void 0,
@@ -1712,7 +1614,7 @@ const TabsetContent = forwardRef(({ children, style, ...props }, ref) => {
1712
1614
  id: panelDomId(node.id),
1713
1615
  ref,
1714
1616
  role: "tabpanel",
1715
- style: mergedStyle,
1617
+ style,
1716
1618
  tabIndex: 0,
1717
1619
  children: /* @__PURE__ */ jsx(TabPanel, {
1718
1620
  render: children,
@@ -1723,7 +1625,7 @@ const TabsetContent = forwardRef(({ children, style, ...props }, ref) => {
1723
1625
  ...props,
1724
1626
  "data-dashfoo": "tabcontent",
1725
1627
  ref,
1726
- style: mergedStyle
1628
+ style
1727
1629
  });
1728
1630
  });
1729
1631
  TabsetContent.displayName = "TabsetContent";
@@ -1754,12 +1656,6 @@ const OverflowIcon = () => /* @__PURE__ */ jsxs("svg", {
1754
1656
  })
1755
1657
  ]
1756
1658
  });
1757
- const menuStyle = {
1758
- position: "absolute",
1759
- right: 0,
1760
- top: "100%",
1761
- zIndex: 10
1762
- };
1763
1659
  const TabOverflowMenu = ({ items, onSelect }) => {
1764
1660
  const [open, setOpen] = useState(false);
1765
1661
  const [activeIndex, setActiveIndex] = useState(0);
@@ -1826,7 +1722,6 @@ const TabOverflowMenu = ({ items, onSelect }) => {
1826
1722
  return /* @__PURE__ */ jsxs("div", {
1827
1723
  "data-dashfoo": "tab-overflow-root",
1828
1724
  ref: rootRef,
1829
- style: { position: "relative" },
1830
1725
  children: [/* @__PURE__ */ jsx("button", {
1831
1726
  "aria-expanded": open,
1832
1727
  "aria-haspopup": "menu",
@@ -1842,7 +1737,6 @@ const TabOverflowMenu = ({ items, onSelect }) => {
1842
1737
  onBlur: handleFocusOut,
1843
1738
  onKeyDown: handleMenuKeyDown,
1844
1739
  role: "menu",
1845
- style: menuStyle,
1846
1740
  tabIndex: -1,
1847
1741
  children: items.map((item, index) => /* @__PURE__ */ jsx("button", {
1848
1742
  "data-dashfoo": "tab-overflow-item",
@@ -1997,14 +1891,6 @@ const fallbackSelectedIndex = (count, removedIndex) => removedIndex + 1 < count
1997
1891
 
1998
1892
  //#endregion
1999
1893
  //#region src/components/tabset/tabset-root.tsx
2000
- const tabsetStyle = {
2001
- display: "flex",
2002
- flexDirection: "column",
2003
- height: "100%",
2004
- minHeight: 0,
2005
- minWidth: 0,
2006
- width: "100%"
2007
- };
2008
1894
  const TabsetRoot = forwardRef(({ children, node, style, ...props }, userRef) => {
2009
1895
  const closableTabs = useLayout((state) => state.closableTabs);
2010
1896
  const dispatch = useLayout((state) => state.dispatch);
@@ -2055,8 +1941,8 @@ const TabsetRoot = forwardRef(({ children, node, style, ...props }, userRef) =>
2055
1941
  if (pendingCloseTabId === null) return;
2056
1942
  store.setState({ pendingCloseTabId: null });
2057
1943
  if (node.children.some((tab) => tab.id === pendingCloseTabId)) return;
2058
- const activeTabId = node.children[node.selected]?.id;
2059
- if (activeTabId) tabsetRef.current?.querySelector(`#${CSS.escape(tabDomId(node.id, activeTabId))}`)?.focus();
1944
+ const selectedTab = node.children[node.selected];
1945
+ if (selectedTab) tabsetRef.current?.querySelector(`#${CSS.escape(tabDomId(node.id, selectedTab.id))}`)?.focus();
2060
1946
  else tabsetRef.current?.focus();
2061
1947
  }, [
2062
1948
  node.children,
@@ -2077,10 +1963,7 @@ const TabsetRoot = forwardRef(({ children, node, style, ...props }, userRef) =>
2077
1963
  if (typeof userRef === "function") userRef(element);
2078
1964
  else if (userRef) userRef.current = element;
2079
1965
  },
2080
- style: {
2081
- ...tabsetStyle,
2082
- ...style
2083
- },
1966
+ style,
2084
1967
  tabIndex: -1,
2085
1968
  children
2086
1969
  })
@@ -2127,7 +2010,7 @@ const TabsetTab = forwardRef(({ children, tab, ...props }, userRef) => {
2127
2010
  });
2128
2011
  });
2129
2012
  TabsetTab.displayName = "TabsetTab";
2130
- const TabsetTrigger = forwardRef(({ children, onClick, onDoubleClick, ...props }, userRef) => {
2013
+ const TabsetTrigger = forwardRef(({ children, onClick, onDoubleClick, onKeyDown, ...props }, userRef) => {
2131
2014
  const { index, tab } = useTab();
2132
2015
  const node = useTabset((state) => state.node);
2133
2016
  const editing = useTabset((state) => state.editingTabId === tab.id);
@@ -2150,6 +2033,21 @@ const TabsetTrigger = forwardRef(({ children, onClick, onDoubleClick, ...props }
2150
2033
  onDoubleClick?.(event);
2151
2034
  if (renamable) startEditing(tab.id);
2152
2035
  };
2036
+ const handleKeyDown = (event) => {
2037
+ onKeyDown?.(event);
2038
+ const count = node.children.length;
2039
+ if (event.defaultPrevented || count === 0) return;
2040
+ const from = Math.max(visualSelected, 0);
2041
+ const next = (/* @__PURE__ */ new Map([
2042
+ ["ArrowLeft", (from - 1 + count) % count],
2043
+ ["ArrowRight", (from + 1) % count],
2044
+ ["End", count - 1],
2045
+ ["Home", 0]
2046
+ ])).get(event.key);
2047
+ if (next === void 0) return;
2048
+ event.preventDefault();
2049
+ selectTab(next, { focus: true });
2050
+ };
2153
2051
  return /* @__PURE__ */ jsx("button", {
2154
2052
  "aria-label": renderTabLabel ? tab.name : void 0,
2155
2053
  ...props,
@@ -2160,6 +2058,7 @@ const TabsetTrigger = forwardRef(({ children, onClick, onDoubleClick, ...props }
2160
2058
  id: tabDomId(node.id, tab.id),
2161
2059
  onClick: handleClick,
2162
2060
  onDoubleClick: handleDoubleClick,
2061
+ onKeyDown: handleKeyDown,
2163
2062
  ref: refCallback,
2164
2063
  role: "tab",
2165
2064
  tabIndex: selected ? 0 : -1,
@@ -2171,59 +2070,24 @@ TabsetTrigger.displayName = "TabsetTrigger";
2171
2070
 
2172
2071
  //#endregion
2173
2072
  //#region src/components/tabset/tabset-tablist.tsx
2174
- const stripStyle = {
2175
- display: "flex",
2176
- flexShrink: 0
2177
- };
2178
- const tablistStyle = {
2179
- display: "flex",
2180
- minWidth: 0,
2181
- overflowX: "auto"
2182
- };
2183
2073
  const TabsetTabStrip = forwardRef(({ style, ...props }, ref) => /* @__PURE__ */ jsx("div", {
2184
2074
  ...props,
2185
2075
  "data-dashfoo": "tabstrip",
2186
2076
  ref,
2187
- style: {
2188
- ...stripStyle,
2189
- ...style
2190
- }
2077
+ style
2191
2078
  }));
2192
2079
  TabsetTabStrip.displayName = "TabsetTabStrip";
2193
- const TabsetTablist = forwardRef(({ "aria-label": ariaLabel, children, onKeyDown, style, ...props }, userRef) => {
2080
+ const TabsetTablist = forwardRef(({ "aria-label": ariaLabel, children, style, ...props }, userRef) => {
2194
2081
  const node = useTabset((state) => state.node);
2195
2082
  const registerTablist = useTabset((state) => state.registerTablist);
2196
- const selectTab = useTabset((state) => state.selectTab);
2197
2083
  const editingTabId = useTabset((state) => state.editingTabId);
2198
- const visualSelected = useTabset((state) => state.visualSelected);
2199
- const handleKeyDown = (event) => {
2200
- onKeyDown?.(event);
2201
- if (event.defaultPrevented) return;
2202
- if (!(event.target instanceof HTMLElement) || !event.target.closest("[role=\"tab\"]")) return;
2203
- const count = node.children.length;
2204
- if (count === 0) return;
2205
- const from = Math.max(visualSelected, 0);
2206
- const next = (/* @__PURE__ */ new Map([
2207
- ["ArrowLeft", (from - 1 + count) % count],
2208
- ["ArrowRight", (from + 1) % count],
2209
- ["End", count - 1],
2210
- ["Home", 0]
2211
- ])).get(event.key);
2212
- if (next === void 0) return;
2213
- event.preventDefault();
2214
- selectTab(next, { focus: true });
2215
- };
2216
2084
  const refCallback = useMemo(() => mergeRefs(registerTablist, userRef), [registerTablist, userRef]);
2217
- const ownedTabs = node.children.filter((tab) => tab.id !== editingTabId).map((tab) => tabDomId(node.id, tab.id)).join(" ");
2085
+ const ownedTabs = node.children.flatMap((tab) => tab.id === editingTabId ? [] : [tabDomId(node.id, tab.id)]).join(" ");
2218
2086
  return /* @__PURE__ */ jsxs("div", {
2219
2087
  ...props,
2220
2088
  "data-dashfoo": "tablist",
2221
- onKeyDown: handleKeyDown,
2222
2089
  ref: refCallback,
2223
- style: {
2224
- ...tablistStyle,
2225
- ...style
2226
- },
2090
+ style,
2227
2091
  tabIndex: -1,
2228
2092
  children: [ownedTabs ? /* @__PURE__ */ jsx("span", {
2229
2093
  "aria-label": ariaLabel ?? node.name ?? "Tabs",
@@ -2237,20 +2101,11 @@ TabsetTablist.displayName = "TabsetTablist";
2237
2101
 
2238
2102
  //#endregion
2239
2103
  //#region src/components/tabset/tabset-toolbar.tsx
2240
- const toolbarStyle = {
2241
- alignItems: "center",
2242
- display: "flex",
2243
- flexShrink: 0,
2244
- marginInlineStart: "auto"
2245
- };
2246
2104
  const TabsetToolbar = forwardRef(({ style, ...props }, ref) => /* @__PURE__ */ jsx("div", {
2247
2105
  ...props,
2248
2106
  "data-dashfoo": "tabset-toolbar",
2249
2107
  ref,
2250
- style: {
2251
- ...toolbarStyle,
2252
- ...style
2253
- }
2108
+ style
2254
2109
  }));
2255
2110
  TabsetToolbar.displayName = "TabsetToolbar";
2256
2111
  /**
@@ -2374,12 +2229,6 @@ const TabsetView = ({ node }) => /* @__PURE__ */ jsx(TabsetRoot, {
2374
2229
  //#region src/components/row-view.tsx
2375
2230
  const dimensionToSize = (dimension) => `${dimension.value}${dimension.unit}`;
2376
2231
  const dimensionToPixels = (dimension) => dimension?.unit === "px" ? dimension.value : void 0;
2377
- const groupStyle = {
2378
- display: "flex",
2379
- flex: 1,
2380
- minHeight: 0,
2381
- minWidth: 0
2382
- };
2383
2232
  const descendantMinSize = (child, axis, tabsetMinSize) => {
2384
2233
  const explicit = dimensionToPixels(child.min);
2385
2234
  if (explicit !== void 0) return explicit;
@@ -2424,7 +2273,6 @@ const RowView = ({ node, renderTabset }) => {
2424
2273
  onLayoutChange: snapActive ? onLayoutChange : void 0,
2425
2274
  onLayoutChanged,
2426
2275
  orientation,
2427
- style: groupStyle,
2428
2276
  children: node.children.map((child, index) => {
2429
2277
  const percent = child.weight / total * 100;
2430
2278
  let min = child.min ? dimensionToSize(child.min) : void 0;
@@ -2457,31 +2305,6 @@ const RowView = ({ node, renderTabset }) => {
2457
2305
  //#endregion
2458
2306
  //#region src/components/float-panel.tsx
2459
2307
  const floatTitle = (node) => node.name ?? "Panel";
2460
- const titleBarStyle = {
2461
- alignItems: "center",
2462
- display: "flex",
2463
- flexShrink: 0,
2464
- gap: "0.375rem",
2465
- touchAction: "none"
2466
- };
2467
- const titleStyle = {
2468
- flex: 1,
2469
- minWidth: 0,
2470
- overflow: "hidden",
2471
- textOverflow: "ellipsis",
2472
- whiteSpace: "nowrap"
2473
- };
2474
- const dockButtonStyle = {
2475
- alignItems: "center",
2476
- cursor: "pointer",
2477
- display: "inline-flex",
2478
- flexShrink: 0
2479
- };
2480
- const bodyStyle = {
2481
- flex: 1,
2482
- minHeight: 0,
2483
- position: "relative"
2484
- };
2485
2308
  const FLOAT_OVERRIDES = {
2486
2309
  draggableTabsets: false,
2487
2310
  floatable: false,
@@ -2512,7 +2335,6 @@ const FloatTitleEditor = ({ dispatch, node, onDone }) => {
2512
2335
  event.stopPropagation();
2513
2336
  },
2514
2337
  ref: inputRef,
2515
- style: titleStyle,
2516
2338
  type: "text"
2517
2339
  });
2518
2340
  };
@@ -2530,7 +2352,6 @@ const FloatTitle = ({ dispatch, node, renamable }) => {
2530
2352
  onDoubleClick: renamable ? () => {
2531
2353
  setEditing(true);
2532
2354
  } : void 0,
2533
- style: titleStyle,
2534
2355
  title: renamable ? "Double-click to rename" : void 0,
2535
2356
  children: floatTitle(node)
2536
2357
  });
@@ -2564,10 +2385,11 @@ const FloatPanel = ({ node, onFocus, zIndex }) => {
2564
2385
  });
2565
2386
  const { onPointerDown, ...rootHandlers } = handlers;
2566
2387
  const frameStyle = {
2567
- ...style,
2568
- pointerEvents: "auto",
2569
- position: "absolute",
2570
- zIndex
2388
+ "--dashfoo-float-height": style["--dashfoo-float-height"],
2389
+ "--dashfoo-float-left": style["--dashfoo-float-left"],
2390
+ "--dashfoo-float-top": style["--dashfoo-float-top"],
2391
+ "--dashfoo-float-width": style["--dashfoo-float-width"],
2392
+ "--dashfoo-float-z": zIndex
2571
2393
  };
2572
2394
  if (minimized) return /* @__PURE__ */ jsxs("button", {
2573
2395
  ...rootHandlers,
@@ -2599,11 +2421,8 @@ const FloatPanel = ({ node, onFocus, zIndex }) => {
2599
2421
  children: [
2600
2422
  /* @__PURE__ */ jsxs("div", {
2601
2423
  "data-dashfoo": "float-titlebar",
2424
+ "data-editable": editable || void 0,
2602
2425
  onPointerDown,
2603
- style: editable ? titleBarStyle : {
2604
- ...titleBarStyle,
2605
- cursor: "default"
2606
- },
2607
2426
  children: [
2608
2427
  /* @__PURE__ */ jsx("span", {
2609
2428
  "aria-hidden": "true",
@@ -2628,7 +2447,6 @@ const FloatPanel = ({ node, onFocus, zIndex }) => {
2628
2447
  onPointerDown: (event) => {
2629
2448
  event.stopPropagation();
2630
2449
  },
2631
- style: dockButtonStyle,
2632
2450
  title: "Minimize panel",
2633
2451
  type: "button",
2634
2452
  children: /* @__PURE__ */ jsx(MinimizeIcon, {})
@@ -2644,7 +2462,6 @@ const FloatPanel = ({ node, onFocus, zIndex }) => {
2644
2462
  onPointerDown: (event) => {
2645
2463
  event.stopPropagation();
2646
2464
  },
2647
- style: dockButtonStyle,
2648
2465
  title: "Dock panel back",
2649
2466
  type: "button",
2650
2467
  children: /* @__PURE__ */ jsx(DockIcon, {})
@@ -2653,7 +2470,6 @@ const FloatPanel = ({ node, onFocus, zIndex }) => {
2653
2470
  }),
2654
2471
  /* @__PURE__ */ jsx("div", {
2655
2472
  "data-dashfoo": "float-body",
2656
- style: bodyStyle,
2657
2473
  children: /* @__PURE__ */ jsx(LayoutOverrides, {
2658
2474
  overrides: FLOAT_OVERRIDES,
2659
2475
  children: /* @__PURE__ */ jsx(DragProvider, { children: /* @__PURE__ */ jsx(RowView, { node: node.layout }) })
@@ -2662,13 +2478,7 @@ const FloatPanel = ({ node, onFocus, zIndex }) => {
2662
2478
  editable && RESIZE_HANDLES.map((handle) => /* @__PURE__ */ jsx("div", {
2663
2479
  "data-dashfoo": "float-resize",
2664
2480
  "data-edge": handle.key,
2665
- onPointerDown,
2666
- style: {
2667
- position: "absolute",
2668
- touchAction: "none",
2669
- zIndex: 1,
2670
- ...handle.style
2671
- }
2481
+ onPointerDown
2672
2482
  }, handle.key))
2673
2483
  ]
2674
2484
  });
@@ -2676,18 +2486,11 @@ const FloatPanel = ({ node, onFocus, zIndex }) => {
2676
2486
 
2677
2487
  //#endregion
2678
2488
  //#region src/components/float-layer.tsx
2679
- const overlayStyle = {
2680
- inset: 0,
2681
- pointerEvents: "none",
2682
- position: "fixed",
2683
- zIndex: 1
2684
- };
2685
2489
  const FloatOverlay = ({ floats }) => {
2686
2490
  const [topId, setTopId] = useState(null);
2687
2491
  if (floats.length === 0) return null;
2688
2492
  return /* @__PURE__ */ jsx("div", {
2689
2493
  "data-dashfoo": "float-overlay",
2690
- style: overlayStyle,
2691
2494
  children: floats.map((node) => /* @__PURE__ */ jsx(FloatPanel, {
2692
2495
  node,
2693
2496
  onFocus: () => {
@@ -2840,19 +2643,20 @@ const DashfooLayout = forwardRef((props, ref) => {
2840
2643
  ]);
2841
2644
  const [containerRef, width] = useContainerWidth();
2842
2645
  const isCompact = responsive !== void 0 && width <= responsive.maxWidth;
2646
+ const compactOrientation = responsive?.orientation;
2843
2647
  /** Everything the compact breakpoint changes, decided once. */
2844
2648
  const presentation = useMemo(() => isCompact ? {
2845
2649
  maximizable: false,
2846
- model: compactModel(store.model, responsive?.orientation),
2650
+ model: compactModel(store.model, compactOrientation),
2847
2651
  restructurable: false
2848
2652
  } : {
2849
2653
  maximizable,
2850
2654
  model: store.model,
2851
2655
  restructurable: true
2852
2656
  }, [
2657
+ compactOrientation,
2853
2658
  isCompact,
2854
2659
  maximizable,
2855
- responsive?.orientation,
2856
2660
  store.model
2857
2661
  ]);
2858
2662
  const view = presentation.model;