dockview-react 8.5.1 → 8.6.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.
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * dockview-react
3
- * @version 8.5.1
3
+ * @version 8.6.1
4
4
  * @link https://github.com/dockview/dockview
5
5
  * @license MIT
6
6
  */
@@ -51,7 +51,7 @@ react_dom = __toESM(react_dom);
51
51
  //#region ../dockview-core/dist/package/main.esm.mjs
52
52
  /**
53
53
  * dockview-core
54
- * @version 8.5.1
54
+ * @version 8.6.1
55
55
  * @link https://github.com/dockview/dockview
56
56
  * @license MIT
57
57
  */
@@ -1169,8 +1169,9 @@ react_dom = __toESM(react_dom);
1169
1169
  if (visible === this.visible) return;
1170
1170
  if (visible) {
1171
1171
  var _this$_cachedVisibleS;
1172
- this.size = clamp((_this$_cachedVisibleS = this._cachedVisibleSize) !== null && _this$_cachedVisibleS !== void 0 ? _this$_cachedVisibleS : 0, this.viewMinimumSize, this.viewMaximumSize);
1172
+ const cachedVisibleSize = (_this$_cachedVisibleS = this._cachedVisibleSize) !== null && _this$_cachedVisibleS !== void 0 ? _this$_cachedVisibleS : 0;
1173
1173
  this._cachedVisibleSize = void 0;
1174
+ this.size = clamp(cachedVisibleSize, this.viewMinimumSize, this.viewMaximumSize);
1174
1175
  } else {
1175
1176
  this._cachedVisibleSize = typeof size === "number" ? size : this.size;
1176
1177
  this.size = 0;
@@ -1303,6 +1304,15 @@ react_dom = __toESM(react_dom);
1303
1304
  get margin() {
1304
1305
  return this._margin;
1305
1306
  }
1307
+ /**
1308
+ * The size each visible view gives up to the margins: `n` visible views
1309
+ * are separated by `n - 1` margins, spread evenly over the `n`.
1310
+ */
1311
+ get marginShare() {
1312
+ let visibleViewCount = 0;
1313
+ for (const item of this.viewItems) if (item.visible) visibleViewCount++;
1314
+ return this.marginShareFor(visibleViewCount);
1315
+ }
1306
1316
  set margin(value) {
1307
1317
  this._margin = value;
1308
1318
  toggleClass(this.element, "dv-splitview-has-margin", value !== 0);
@@ -1599,6 +1609,7 @@ react_dom = __toESM(react_dom);
1599
1609
  const previousSize = Math.max(this.size, this._contentSize);
1600
1610
  this.size = size;
1601
1611
  this.orthogonalSize = orthogonalSize;
1612
+ if (!this._proportions) this.saveProportions();
1602
1613
  if (this.proportions) {
1603
1614
  let total = 0;
1604
1615
  for (let i = 0; i < this.viewItems.length; i++) {
@@ -1655,7 +1666,10 @@ react_dom = __toESM(react_dom);
1655
1666
  }
1656
1667
  }
1657
1668
  saveProportions() {
1658
- if (this.proportionalLayout && this._contentSize > 0) this._proportions = this.viewItems.map((i) => i.visible ? i.size / this._contentSize : void 0);
1669
+ if (!this.proportionalLayout) return;
1670
+ let contentSize = 0;
1671
+ for (const item of this.viewItems) contentSize += item.size;
1672
+ if (contentSize > 0) this._proportions = this.viewItems.map((i) => i.visible ? i.size / contentSize : void 0);
1659
1673
  }
1660
1674
  /**
1661
1675
  * Margin explain:
@@ -1677,7 +1691,7 @@ react_dom = __toESM(react_dom);
1677
1691
  this.updateSashEnablement();
1678
1692
  if (this.viewItems.length === 0) return;
1679
1693
  const sashCount = Math.max(0, visibleViewCount - 1);
1680
- const marginReducedSize = this.margin * sashCount / Math.max(1, visibleViewCount);
1694
+ const marginReducedSize = this.marginShareFor(visibleViewCount);
1681
1695
  let totalLeftOffset = 0;
1682
1696
  const viewLeftOffsets = [];
1683
1697
  const sashWidth = 4;
@@ -1707,9 +1721,13 @@ react_dom = __toESM(react_dom);
1707
1721
  view.setContainerGeometry("width", "");
1708
1722
  view.setContainerGeometry("left", "");
1709
1723
  }
1710
- view.view.layout(view.size - marginReducedSize, this._orthogonalSize);
1724
+ view.view.layout(size, this._orthogonalSize);
1711
1725
  });
1712
1726
  }
1727
+ marginShareFor(visibleViewCount) {
1728
+ const sashCount = Math.max(0, visibleViewCount - 1);
1729
+ return this._margin * sashCount / Math.max(1, visibleViewCount);
1730
+ }
1713
1731
  findFirstSnapIndex(indexes) {
1714
1732
  for (const index of indexes) {
1715
1733
  const viewItem = this.viewItems[index];
@@ -2241,12 +2259,20 @@ react_dom = __toESM(react_dom);
2241
2259
  if (candiateNode instanceof BranchNode) return findLeaf(candiateNode.children[last ? candiateNode.children.length - 1 : 0], last);
2242
2260
  throw new Error("invalid node");
2243
2261
  }
2262
+ /**
2263
+ * The sizing to re-add a branch's child with, so a hidden child stays hidden
2264
+ * (with its cached size) instead of coming back visible at size 0.
2265
+ */
2266
+ function childSizing(node, index, size, scale = 1) {
2267
+ const cachedVisibleSize = node.getChildCachedVisibleSize(index);
2268
+ return cachedVisibleSize === void 0 ? size : Sizing.Invisible(Math.round(cachedVisibleSize * scale));
2269
+ }
2244
2270
  function cloneNode(node, size, orthogonalSize) {
2245
2271
  if (node instanceof BranchNode) {
2246
2272
  const result = new BranchNode(node.orientation, node.proportionalLayout, node.styles, size, orthogonalSize, node.disabled, node.margin);
2247
2273
  for (let i = node.children.length - 1; i >= 0; i--) {
2248
2274
  const child = node.children[i];
2249
- result.addChild(cloneNode(child, child.size, child.orthogonalSize), child.size, 0, true);
2275
+ result.addChild(cloneNode(child, child.size, child.orthogonalSize), childSizing(node, i, child.size), 0, true);
2250
2276
  }
2251
2277
  return result;
2252
2278
  } else return new LeafNode(node.view, node.orientation, orthogonalSize);
@@ -2255,13 +2281,14 @@ react_dom = __toESM(react_dom);
2255
2281
  if (node instanceof BranchNode) {
2256
2282
  const result = new BranchNode(orthogonal(node.orientation), node.proportionalLayout, node.styles, size, orthogonalSize, node.disabled, node.margin);
2257
2283
  let totalSize = 0;
2284
+ const cachedSizeScale = node.size === 0 ? 1 : size / node.size;
2258
2285
  for (let i = node.children.length - 1; i >= 0; i--) {
2259
2286
  const child = node.children[i];
2260
2287
  const childSize = child instanceof BranchNode ? child.orthogonalSize : child.size;
2261
2288
  let newSize = node.size === 0 ? 0 : Math.round(size * childSize / node.size);
2262
2289
  totalSize += newSize;
2263
2290
  if (i === 0) newSize += size - totalSize;
2264
- result.addChild(flipNode(child, orthogonalSize, newSize), newSize, 0, true);
2291
+ result.addChild(flipNode(child, orthogonalSize, newSize), childSizing(node, i, newSize, cachedSizeScale), 0, true);
2265
2292
  }
2266
2293
  return result;
2267
2294
  } else return new LeafNode(node.view, orthogonal(node.orientation), orthogonalSize);
@@ -2747,7 +2774,8 @@ react_dom = __toESM(react_dom);
2747
2774
  sizes.splice(parentIndex, 1, ...sibling.children.map((c) => c.size));
2748
2775
  for (let i = 0; i < sibling.children.length; i++) {
2749
2776
  const child = sibling.children[i];
2750
- grandParent.addChild(child, child.size, parentIndex + i);
2777
+ const childSize = childSizing(sibling, i, child.size);
2778
+ grandParent.addChild(child, !isSiblingVisible && typeof childSize === "number" ? Sizing.Invisible(childSize) : childSize, parentIndex + i);
2751
2779
  }
2752
2780
  /**
2753
2781
  * clean down the branch node since we need to dipose of it and
@@ -3910,9 +3938,13 @@ react_dom = __toESM(react_dom);
3910
3938
  }
3911
3939
  /**
3912
3940
  * Remove a panel given the panel object.
3941
+ *
3942
+ * `removeEmptyGroup` overrides the `closeEmptyGroups` option for this
3943
+ * call: `false` keeps the group if the panel was its last one, `true`
3944
+ * removes it.
3913
3945
  */
3914
- removePanel(panel) {
3915
- this.component.withOrigin("api", () => this.component.removePanel(panel));
3946
+ removePanel(panel, options) {
3947
+ this.component.withOrigin("api", () => this.component.removePanel(panel, options));
3916
3948
  }
3917
3949
  /**
3918
3950
  * Add a group and return the created object.
@@ -5915,6 +5947,9 @@ react_dom = __toESM(react_dom);
5915
5947
  get element() {
5916
5948
  return this._element;
5917
5949
  }
5950
+ get dimensions() {
5951
+ return this._dimensions;
5952
+ }
5918
5953
  constructor(accessor, group) {
5919
5954
  var _this$accessor$resolv, _this$accessor, _this$accessor$resolv2, _this$accessor2, _this$accessor$onDidO, _this$accessor$onDidO2, _this$accessor3;
5920
5955
  super();
@@ -5925,12 +5960,14 @@ react_dom = __toESM(react_dom);
5925
5960
  this.onDidFocus = this._onDidFocus.event;
5926
5961
  this._onDidBlur = new Emitter();
5927
5962
  this.onDidBlur = this._onDidBlur.event;
5963
+ this._onDidDimensionsChange = new Emitter();
5964
+ this.onDidDimensionsChange = this._onDidDimensionsChange.event;
5928
5965
  this._element = document.createElement("div");
5929
5966
  this._element.className = "dv-content-container";
5930
5967
  this._element.tabIndex = -1;
5931
5968
  this._element.id = nextContentId();
5932
5969
  this._element.setAttribute("role", "tabpanel");
5933
- this.addDisposables(this._onDidFocus, this._onDidBlur);
5970
+ this.addDisposables(this._onDidFocus, this._onDidBlur, this._onDidDimensionsChange);
5934
5971
  const getOverrideTarget = () => {
5935
5972
  var _group$dropTargetCont;
5936
5973
  return (_group$dropTargetCont = group.dropTargetContainer) === null || _group$dropTargetCont === void 0 ? void 0 : _group$dropTargetCont.model;
@@ -6039,7 +6076,15 @@ react_dom = __toESM(react_dom);
6039
6076
  if (this.panel === panel) return;
6040
6077
  this.renderPanel(panel);
6041
6078
  }
6042
- layout(_width, _height) {}
6079
+ layout(width, height) {
6080
+ var _this$_dimensions;
6081
+ if (((_this$_dimensions = this._dimensions) === null || _this$_dimensions === void 0 ? void 0 : _this$_dimensions.width) === width && this._dimensions.height === height) return;
6082
+ this._dimensions = {
6083
+ width,
6084
+ height
6085
+ };
6086
+ this._onDidDimensionsChange.fire(this._dimensions);
6087
+ }
6043
6088
  closePanel() {
6044
6089
  if (this.panel) {
6045
6090
  if (this.panel.api.renderer === "onlyWhenVisible") {
@@ -6887,6 +6932,7 @@ react_dom = __toESM(react_dom);
6887
6932
  dndStrategy: void 0,
6888
6933
  className: void 0,
6889
6934
  noPanelsOverlay: void 0,
6935
+ closeEmptyGroups: void 0,
6890
6936
  dndEdges: void 0,
6891
6937
  dropPositionResolver: void 0,
6892
6938
  dndCompass: void 0,
@@ -10503,7 +10549,6 @@ react_dom = __toESM(react_dom);
10503
10549
  return (_this$_headerPosition = this._headerPosition) !== null && _this$_headerPosition !== void 0 ? _this$_headerPosition : "top";
10504
10550
  }
10505
10551
  set headerPosition(value) {
10506
- var _this$_activePanel;
10507
10552
  this._headerPosition = value;
10508
10553
  this.invalidateHeaderSize();
10509
10554
  removeClasses(this.container, "dv-groupview-header-top", "dv-groupview-header-bottom", "dv-groupview-header-left", "dv-groupview-header-right");
@@ -10513,10 +10558,7 @@ react_dom = __toESM(react_dom);
10513
10558
  this._headerDirection = direction;
10514
10559
  this.tabsContainer.direction = direction;
10515
10560
  this.header.direction = direction;
10516
- if ((_this$_activePanel = this._activePanel) === null || _this$_activePanel === void 0 ? void 0 : _this$_activePanel.layout) {
10517
- const { width, height } = this.contentDimensions();
10518
- this._activePanel.layout(width, height);
10519
- }
10561
+ if (this.panels.length > 0) this.layoutContent();
10520
10562
  this.updateHeaderActions();
10521
10563
  if (previousDirection !== void 0 && previousDirection !== direction) {
10522
10564
  var _this$groupPanel;
@@ -11019,10 +11061,7 @@ react_dom = __toESM(react_dom);
11019
11061
  this.panels.forEach((panel) => {
11020
11062
  this.rerender(panel);
11021
11063
  });
11022
- if (this._activePanel) {
11023
- const { width, height } = this.contentDimensions();
11024
- this._activePanel.layout(width, height);
11025
- }
11064
+ if (this.panels.length > 0) this.layoutContent();
11026
11065
  }
11027
11066
  get renderContainer() {
11028
11067
  var _this$_overwriteRende;
@@ -11069,10 +11108,10 @@ react_dom = __toESM(react_dom);
11069
11108
  return this.tabsContainer.indexOf(panel.id);
11070
11109
  }
11071
11110
  toJSON() {
11072
- var _this$_activePanel2;
11111
+ var _this$_activePanel;
11073
11112
  const result = {
11074
11113
  views: this.tabsContainer.panels,
11075
- activeView: (_this$_activePanel2 = this._activePanel) === null || _this$_activePanel2 === void 0 ? void 0 : _this$_activePanel2.id,
11114
+ activeView: (_this$_activePanel = this._activePanel) === null || _this$_activePanel === void 0 ? void 0 : _this$_activePanel.id,
11076
11115
  id: this.id
11077
11116
  };
11078
11117
  if (this.locked !== false) result.locked = this.locked;
@@ -11110,8 +11149,8 @@ react_dom = __toESM(react_dom);
11110
11149
  init(_params) {}
11111
11150
  update(_params) {}
11112
11151
  focus() {
11113
- var _this$_activePanel3;
11114
- (_this$_activePanel3 = this._activePanel) === null || _this$_activePanel3 === void 0 || _this$_activePanel3.focus();
11152
+ var _this$_activePanel2;
11153
+ (_this$_activePanel2 = this._activePanel) === null || _this$_activePanel2 === void 0 || _this$_activePanel2.focus();
11115
11154
  }
11116
11155
  openPanel(panel, options = {}) {
11117
11156
  if (typeof options.index !== "number" || options.index > this.panels.length) options.index = this.panels.length;
@@ -11169,12 +11208,24 @@ react_dom = __toESM(react_dom);
11169
11208
  this.updateContainer();
11170
11209
  }
11171
11210
  layout(width, height) {
11172
- var _this$_activePanel4;
11173
11211
  this._width = width;
11174
11212
  this._height = height;
11175
- const { width: contentWidth, height: contentHeight } = this.contentDimensions();
11176
- this.contentContainer.layout(contentWidth, contentHeight);
11177
- if ((_this$_activePanel4 = this._activePanel) === null || _this$_activePanel4 === void 0 ? void 0 : _this$_activePanel4.layout) this._activePanel.layout(contentWidth, contentHeight);
11213
+ this.layoutContent();
11214
+ }
11215
+ /**
11216
+ * Apply the current content box (group box minus header extent) to the
11217
+ * content container and the active panel. Every path that can change that
11218
+ * box goes through here so the container's recorded dimensions, which size
11219
+ * overlay-rendered panels, never lag the active panel's. Callers other
11220
+ * than `layout()` skip it for a group with no panels: there is nothing to
11221
+ * size, and measuring the header of a group that is not in the document
11222
+ * yet (the constructor sets `headerPosition`) would cache a zero extent.
11223
+ */
11224
+ layoutContent() {
11225
+ var _this$_activePanel3;
11226
+ const { width, height } = this.contentDimensions();
11227
+ this.contentContainer.layout(width, height);
11228
+ if ((_this$_activePanel3 = this._activePanel) === null || _this$_activePanel3 === void 0 ? void 0 : _this$_activePanel3.layout) this._activePanel.layout(width, height);
11178
11229
  }
11179
11230
  /**
11180
11231
  * Re-run the group's layout with its current dimensions. Used to propagate a
@@ -11267,8 +11318,7 @@ react_dom = __toESM(react_dom);
11267
11318
  this.tabsContainer.setActivePanel(panel);
11268
11319
  this.contentContainer.setLabelledBy(this.tabsContainer.getTabId(panel.id));
11269
11320
  this.contentContainer.openPanel(panel);
11270
- const { width, height } = this.contentDimensions();
11271
- panel.layout(width, height);
11321
+ this.layoutContent();
11272
11322
  this.updateMru(panel);
11273
11323
  this.contentContainer.refreshFocusState();
11274
11324
  this._onDidActivePanelChange.fire({
@@ -11280,8 +11330,8 @@ react_dom = __toESM(react_dom);
11280
11330
  }
11281
11331
  /** Label the group region with its active panel's title (the WAI-ARIA region name). */
11282
11332
  updateAccessibleLabel() {
11283
- var _this$_activePanel5;
11284
- const title = (_this$_activePanel5 = this._activePanel) === null || _this$_activePanel5 === void 0 ? void 0 : _this$_activePanel5.title;
11333
+ var _this$_activePanel4;
11334
+ const title = (_this$_activePanel4 = this._activePanel) === null || _this$_activePanel4 === void 0 ? void 0 : _this$_activePanel4.title;
11285
11335
  if (title) this.container.setAttribute("aria-label", title);
11286
11336
  else this.container.removeAttribute("aria-label");
11287
11337
  }
@@ -11376,7 +11426,7 @@ react_dom = __toESM(react_dom);
11376
11426
  });
11377
11427
  return;
11378
11428
  }
11379
- if (this.tabsContainer.indexOf(data.panelId) !== -1 && this.tabsContainer.size === 1) return;
11429
+ if (this.tabsContainer.indexOf(data.panelId) !== -1 && this.tabsContainer.size === 1 && this.accessor.options.closeEmptyGroups !== false) return;
11380
11430
  const { groupId, panelId } = data;
11381
11431
  if (this.id === groupId && !position) {
11382
11432
  if (this.tabsContainer.indexOf(panelId) === index) return;
@@ -11617,6 +11667,7 @@ react_dom = __toESM(react_dom);
11617
11667
  constructor(id, accessor) {
11618
11668
  super(id, "__dockviewgroup__");
11619
11669
  this.accessor = accessor;
11670
+ this._isApplyingSize = false;
11620
11671
  this._onDidLocationChange = new Emitter();
11621
11672
  this.onDidLocationChange = this._onDidLocationChange.event;
11622
11673
  this._onDidActivePanelChange = new Emitter();
@@ -11629,14 +11680,29 @@ react_dom = __toESM(react_dom);
11629
11680
  this.onDidHeaderDirectionChange = this._onDidHeaderDirectionChange.event;
11630
11681
  this.addDisposables(this._onDidLocationChange, this._onDidActivePanelChange, this._onDidCollapsedChange, this._onDidPeekChange, this._onDidHeaderDirectionChange, this._onDidVisibilityChange.event((event) => {
11631
11682
  if (event.isVisible && this._pendingSize) {
11632
- super.setSize(this._pendingSize);
11683
+ const pendingSize = this._pendingSize;
11633
11684
  this._pendingSize = void 0;
11685
+ super.setSize(pendingSize);
11634
11686
  }
11687
+ }), this.onDidDimensionsChange((event) => {
11688
+ var _this$_reportedSize, _this$_reportedSize2;
11689
+ const moved = event.width !== ((_this$_reportedSize = this._reportedSize) === null || _this$_reportedSize === void 0 ? void 0 : _this$_reportedSize.width) || event.height !== ((_this$_reportedSize2 = this._reportedSize) === null || _this$_reportedSize2 === void 0 ? void 0 : _this$_reportedSize2.height);
11690
+ this._reportedSize = {
11691
+ width: event.width,
11692
+ height: event.height
11693
+ };
11694
+ if (moved && this.isVisible && !this._isApplyingSize) this._pendingSize = void 0;
11635
11695
  }));
11636
11696
  }
11637
11697
  setSize(event) {
11638
- this._pendingSize = _objectSpread2$1({}, event);
11639
- super.setSize(event);
11698
+ if (typeof event.width === "number" || typeof event.height === "number") this._pendingSize = _objectSpread2$1({}, event);
11699
+ const wasApplyingSize = this._isApplyingSize;
11700
+ this._isApplyingSize = true;
11701
+ try {
11702
+ super.setSize(event);
11703
+ } finally {
11704
+ this._isApplyingSize = wasApplyingSize;
11705
+ }
11640
11706
  }
11641
11707
  close() {
11642
11708
  if (!this._group) return;
@@ -14311,6 +14377,15 @@ react_dom = __toESM(react_dom);
14311
14377
  element.tabIndex = -1;
14312
14378
  return element;
14313
14379
  }
14380
+ /**
14381
+ * Write an inline pixel length only when it differs from what is already set,
14382
+ * so a pass that re-derives geometry already in place leaves the element's
14383
+ * style clean and the next measurement does not relayout the overlay subtree.
14384
+ */
14385
+ function setPixels(style, property, value) {
14386
+ const next = `${value}px`;
14387
+ if (style[property] !== next) style[property] = next;
14388
+ }
14314
14389
  var OverlayRenderContainer = class extends CompositeDisposable {
14315
14390
  constructor(element, accessor) {
14316
14391
  super();
@@ -14429,6 +14504,32 @@ react_dom = __toESM(react_dom);
14429
14504
  const generation = mapEntry.generation;
14430
14505
  const focusContainer = mapEntry.element;
14431
14506
  const contentElement = panel.view.content.element;
14507
+ /**
14508
+ * Size the overlay from the layout engine's numbers (the content box
14509
+ * the group model also hands to `panel.layout()`): a style write with
14510
+ * no forced layout, so the content has its real box before anything
14511
+ * can measure it, and `.dv-render-overlay`'s 100%/100% default is never
14512
+ * observed. Hidden panels are sized too: their content is laid out
14513
+ * (hidden by `visibility`) and must not keep the root's box.
14514
+ *
14515
+ * Only while the overlay is not on screen at a measured position,
14516
+ * i.e. hidden or not yet positioned. Once it is, the frame pass owns
14517
+ * the whole box: size and position must change together, and group
14518
+ * layouts driven from an animation frame would otherwise paint one
14519
+ * frame at the new size and the old position. Likewise not while the
14520
+ * peek force-shows the overlay, whose geometry belongs to
14521
+ * `repositionPanelOverlay`: the collapsed group's own layouts describe
14522
+ * the strip, not the peek. A zero box (a container awaiting layout) is
14523
+ * skipped too: whether 0x0 is a real collapse is the frame pass's call.
14524
+ */
14525
+ const applySize = (size) => {
14526
+ if (!size || size.width <= 0 || size.height <= 0) return;
14527
+ const entry = this.map[panel.api.id];
14528
+ if (!entry || entry.forceVisible || entry.positioned && panel.api.isVisible) return;
14529
+ setPixels(focusContainer.style, "width", size.width);
14530
+ setPixels(focusContainer.style, "height", size.height);
14531
+ };
14532
+ applySize(referenceContainer.dimensions);
14432
14533
  if (contentElement.parentElement !== focusContainer) focusContainer.appendChild(contentElement);
14433
14534
  if (focusContainer.parentElement !== this.element) this.element.appendChild(focusContainer);
14434
14535
  const resize = () => {
@@ -14468,10 +14569,10 @@ react_dom = __toESM(react_dom);
14468
14569
  }
14469
14570
  entry.retainPreviousGeometry = false;
14470
14571
  entry.positioned = true;
14471
- focusContainer.style.left = `${left}px`;
14472
- focusContainer.style.top = `${top}px`;
14473
- focusContainer.style.width = `${width}px`;
14474
- focusContainer.style.height = `${height}px`;
14572
+ setPixels(focusContainer.style, "left", left);
14573
+ setPixels(focusContainer.style, "top", top);
14574
+ setPixels(focusContainer.style, "width", width);
14575
+ setPixels(focusContainer.style, "height", height);
14475
14576
  if (panel.api.isVisible || forceVisible) {
14476
14577
  focusContainer.style.visibility = "";
14477
14578
  focusContainer.style.pointerEvents = "";
@@ -14587,6 +14688,9 @@ react_dom = __toESM(react_dom);
14587
14688
  if (!panel.api.isVisible) return;
14588
14689
  resize();
14589
14690
  }),
14691
+ referenceContainer.onDidDimensionsChange((size) => {
14692
+ applySize(size);
14693
+ }),
14590
14694
  panel.api.onDidLocationChange(() => {
14591
14695
  correctLayerPosition();
14592
14696
  })
@@ -14883,6 +14987,20 @@ react_dom = __toESM(react_dom);
14883
14987
  return el;
14884
14988
  }
14885
14989
  };
14990
+ const EDGE_POSITIONS = [
14991
+ "left",
14992
+ "right",
14993
+ "top",
14994
+ "bottom"
14995
+ ];
14996
+ function isHorizontal(position) {
14997
+ return position === "left" || position === "right";
14998
+ }
14999
+ /** A view outside a shell: no gap, and every layout is real. */
15000
+ const UNBOUNDED_AXIS = {
15001
+ marginShare: 0,
15002
+ size: Number.POSITIVE_INFINITY
15003
+ };
14886
15004
  var EdgeGroupView = class {
14887
15005
  /**
14888
15006
  * The effective (pre-gap) collapsed size: the measured tab-strip size when
@@ -14894,7 +15012,7 @@ react_dom = __toESM(react_dom);
14894
15012
  }
14895
15013
  get minimumSize() {
14896
15014
  if (this._isCollapsed) return this.collapsedSize;
14897
- return this._baseMinimumSize !== void 0 ? this._baseMinimumSize + this._gapAdd : this._effectiveBaseCollapsed + 50 + this._gapAdd;
15015
+ return this._baseMinimumSize !== void 0 ? this._baseMinimumSize + this._axis.marginShare : this._effectiveBaseCollapsed + 50 + this._axis.marginShare;
14898
15016
  }
14899
15017
  get maximumSize() {
14900
15018
  return this._isCollapsed ? this.collapsedSize : this._expandedMaximumSize;
@@ -14911,10 +15029,10 @@ react_dom = __toESM(react_dom);
14911
15029
  }
14912
15030
  /** Without the gap share. */
14913
15031
  get lastExpandedContentSize() {
14914
- return this._lastExpandedSize - this._gapAdd;
15032
+ return this._lastExpandedSize - this._axis.marginShare;
14915
15033
  }
14916
15034
  get collapsedSize() {
14917
- return this._effectiveBaseCollapsed + this._gapAdd;
15035
+ return this._effectiveBaseCollapsed + this._axis.marginShare;
14918
15036
  }
14919
15037
  /** The user-configured (pre-gap) geometry constraints, for serialization.
14920
15038
  * These are the raw values passed to `addEdgeGroup`, unlike the effective
@@ -14929,13 +15047,14 @@ react_dom = __toESM(react_dom);
14929
15047
  get configuredCollapsedSize() {
14930
15048
  return this._baseCollapsedSize;
14931
15049
  }
14932
- constructor(options, group, orientation, gapAdd = 0) {
15050
+ constructor(options, group, orientation, axis = UNBOUNDED_AXIS) {
14933
15051
  var _options$collapsedSiz, _options$maximumSize, _options$initialSize;
14934
15052
  this._onDidChange = new Emitter();
14935
15053
  this.onDidChange = this._onDidChange.event;
14936
15054
  this.snap = false;
14937
15055
  this.priority = "low";
14938
15056
  this._isCollapsed = false;
15057
+ this._isVisible = true;
14939
15058
  this._tabSizeDisposables = new CompositeDisposable();
14940
15059
  this._group = group;
14941
15060
  this._orientation = orientation;
@@ -14943,7 +15062,7 @@ react_dom = __toESM(react_dom);
14943
15062
  group.element.dataset.testid = `dv-edge-group-${options.id}`;
14944
15063
  this._baseCollapsedSize = (_options$collapsedSiz = options.collapsedSize) !== null && _options$collapsedSiz !== void 0 ? _options$collapsedSiz : 35;
14945
15064
  this._baseMinimumSize = options.minimumSize;
14946
- this._gapAdd = gapAdd;
15065
+ this._axis = axis;
14947
15066
  this._expandedMaximumSize = (_options$maximumSize = options.maximumSize) !== null && _options$maximumSize !== void 0 ? _options$maximumSize : Number.POSITIVE_INFINITY;
14948
15067
  this._lastExpandedSize = (_options$initialSize = options.initialSize) !== null && _options$initialSize !== void 0 ? _options$initialSize : 200;
14949
15068
  if (options.collapsed) {
@@ -14976,7 +15095,7 @@ react_dom = __toESM(react_dom);
14976
15095
  if (this._isCollapsed) this._onDidChange.fire({ size: this.collapsedSize });
14977
15096
  }
14978
15097
  layout(size, orthogonalSize) {
14979
- if (!this._isCollapsed) this._lastExpandedSize = size + this._gapAdd;
15098
+ if (!this._isCollapsed && this._isVisible && this._axis.size > 0) this._lastExpandedSize = size + this._axis.marginShare;
14980
15099
  if (this._orientation === "horizontal") this._group.layout(size, orthogonalSize);
14981
15100
  else this._group.layout(orthogonalSize, size);
14982
15101
  }
@@ -14985,25 +15104,25 @@ react_dom = __toESM(react_dom);
14985
15104
  this._isCollapsed = collapsed;
14986
15105
  this._group.element.classList.toggle("dv-edge-collapsed", collapsed);
14987
15106
  }
14988
- setVisible(_visible) {}
15107
+ setVisible(visible) {
15108
+ this._isVisible = visible;
15109
+ }
14989
15110
  /**
14990
- * Restore the last-expanded size from serialized state without triggering
14991
- * a layout. Must be called before setCollapsed(true) during fromJSON so
14992
- * that expanding after deserialization restores the correct size.
15111
+ * Set the expanded size without triggering a layout: from serialized
15112
+ * state (before setCollapsed(true) during fromJSON, so that expanding
15113
+ * restores the right size) or from an explicit resize.
14993
15114
  */
14994
15115
  restoreExpandedSize(size) {
14995
15116
  this._lastExpandedSize = size;
14996
15117
  }
14997
15118
  /**
14998
- * Apply a new base (pre-gap) collapsed size, base minimum size and gap
14999
- * contribution after a theme or gap change. Base values come from the
15000
- * original config; the ShellManager owns the gap arithmetic. A live
15001
- * measured tab size (if any) continues to win over the base collapsed size.
15119
+ * Apply a new base (pre-gap) collapsed size and base minimum size after a
15120
+ * theme change. Base values come from the original config. A live measured
15121
+ * tab size (if any) continues to win over the base collapsed size.
15002
15122
  */
15003
- updateSizing(baseCollapsedSize, baseMinimumSize, gapAdd) {
15123
+ updateSizing(baseCollapsedSize, baseMinimumSize) {
15004
15124
  this._baseCollapsedSize = baseCollapsedSize;
15005
15125
  this._baseMinimumSize = baseMinimumSize;
15006
- this._gapAdd = gapAdd;
15007
15126
  }
15008
15127
  dispose() {
15009
15128
  this._tabSizeDisposables.dispose();
@@ -15105,13 +15224,20 @@ react_dom = __toESM(react_dom);
15105
15224
  const index = position === "top" ? this._topIndex : this._bottomIndex;
15106
15225
  if (index !== void 0) this._splitview.resizeView(index, size);
15107
15226
  }
15108
- /** The inner splitview's primary-axis (height) extent; zero until laid out. */
15109
- get axisSize() {
15110
- return this._splitview.size;
15227
+ /** The inner splitview, as the axis its edge views read. */
15228
+ get axis() {
15229
+ return this._splitview;
15111
15230
  }
15112
15231
  updateMargin(gap) {
15113
15232
  this._splitview.margin = gap;
15114
15233
  }
15234
+ /** Disables the sashes of the top/bottom edge groups. */
15235
+ get disabled() {
15236
+ return this._splitview.disabled;
15237
+ }
15238
+ set disabled(value) {
15239
+ this._splitview.disabled = value;
15240
+ }
15115
15241
  dispose() {
15116
15242
  this._onDidChange.dispose();
15117
15243
  this._splitview.dispose();
@@ -15161,8 +15287,6 @@ react_dom = __toESM(react_dom);
15161
15287
  const width = Math.round(entry.contentRect.width);
15162
15288
  const height = Math.round(entry.contentRect.height);
15163
15289
  if (width === this._currentWidth && height === this._currentHeight) return;
15164
- this._currentWidth = width;
15165
- this._currentHeight = height;
15166
15290
  this.layout(width, height);
15167
15291
  }), this._outerSplitview, this._middleColumn, centerView);
15168
15292
  }
@@ -15170,6 +15294,18 @@ react_dom = __toESM(react_dom);
15170
15294
  return this._shellElement;
15171
15295
  }
15172
15296
  /**
15297
+ * The dock's `locked` option. The edge groups are resized with the sashes
15298
+ * of the shell's own splitviews (left/right in the outer one, top/bottom
15299
+ * in the middle column), so locking disables both.
15300
+ */
15301
+ get locked() {
15302
+ return this._outerSplitview.disabled;
15303
+ }
15304
+ set locked(value) {
15305
+ this._outerSplitview.disabled = value;
15306
+ this._middleColumn.disabled = value;
15307
+ }
15308
+ /**
15173
15309
  * Add an edge group view at the given position. The view wraps the
15174
15310
  * provided group element inside the shell's splitview layout.
15175
15311
  * Throws if a group at this position is already registered.
@@ -15177,14 +15313,7 @@ react_dom = __toESM(react_dom);
15177
15313
  addEdgeView(position, options, group) {
15178
15314
  if (this.hasEdgeGroup(position)) throw new Error(`dockview: edge group already registered at position '${position}'`);
15179
15315
  this._viewConfigs.set(position, options);
15180
- const outerN = 1 + (this._viewConfigs.has("left") ? 1 : 0) + (this._viewConfigs.has("right") ? 1 : 0);
15181
- const innerN = 1 + (this._viewConfigs.has("top") ? 1 : 0) + (this._viewConfigs.has("bottom") ? 1 : 0);
15182
- const outerGapAdd = outerN > 1 ? this._gap * (outerN - 1) / outerN : 0;
15183
- const innerGapAdd = innerN > 1 ? this._gap * (innerN - 1) / innerN : 0;
15184
- const isHorizontal = position === "left" || position === "right";
15185
- const gapAdd = isHorizontal ? outerGapAdd : innerGapAdd;
15186
- const orientation = isHorizontal ? "horizontal" : "vertical";
15187
- const view = new EdgeGroupView(_objectSpread2$1({ collapsedSize: this._defaultCollapsedSize }, options), group, orientation, gapAdd);
15316
+ const view = new EdgeGroupView(_objectSpread2$1({ collapsedSize: this._defaultCollapsedSize }, options), group, isHorizontal(position) ? "horizontal" : "vertical", this._axisFor(position));
15188
15317
  const initialSize = view.isCollapsed ? view.collapsedSize : view.lastExpandedSize;
15189
15318
  switch (position) {
15190
15319
  case "left":
@@ -15213,7 +15342,7 @@ react_dom = __toESM(react_dom);
15213
15342
  }
15214
15343
  this._disposables.addDisposables(view);
15215
15344
  if (!view.isCollapsed && !this._canResize(position)) this._pendingSizes.set(position, initialSize);
15216
- this.updateTheme(this._gap, this._defaultCollapsedSize);
15345
+ this._relayout();
15217
15346
  return view;
15218
15347
  }
15219
15348
  /**
@@ -15235,6 +15364,8 @@ react_dom = __toESM(react_dom);
15235
15364
  this.layout(width, height);
15236
15365
  }
15237
15366
  layout(width, height) {
15367
+ this._currentWidth = width;
15368
+ this._currentHeight = height;
15238
15369
  this._outerSplitview.layout(width, height);
15239
15370
  this._flushPendingSizes();
15240
15371
  }
@@ -15244,34 +15375,50 @@ react_dom = __toESM(react_dom);
15244
15375
  * and tab-strip dimensions.
15245
15376
  */
15246
15377
  updateTheme(gap, defaultCollapsedSize) {
15247
- var _this$_leftView, _this$_rightView, _this$_topView, _this$_bottomView;
15248
15378
  this._gap = gap;
15249
15379
  this._defaultCollapsedSize = defaultCollapsedSize;
15250
- const outerN = 1 + (this._viewConfigs.has("left") ? 1 : 0) + (this._viewConfigs.has("right") ? 1 : 0);
15251
- const innerN = 1 + (this._viewConfigs.has("top") ? 1 : 0) + (this._viewConfigs.has("bottom") ? 1 : 0);
15252
- const outerGapAdd = outerN > 1 ? gap * (outerN - 1) / outerN : 0;
15253
- const innerGapAdd = innerN > 1 ? gap * (innerN - 1) / innerN : 0;
15254
15380
  this._outerSplitview.margin = gap;
15255
15381
  this._middleColumn.updateMargin(gap);
15256
- const updateView = (view, baseCfg, gapAdd) => {
15257
- var _baseCfg$collapsedSiz;
15258
- const baseCS = (_baseCfg$collapsedSiz = baseCfg.collapsedSize) !== null && _baseCfg$collapsedSiz !== void 0 ? _baseCfg$collapsedSiz : defaultCollapsedSize;
15259
- view.updateSizing(baseCS, baseCfg.minimumSize, gapAdd);
15260
- };
15261
- const topCfg = this._viewConfigs.get("top");
15262
- if (this._topView && topCfg) updateView(this._topView, topCfg, innerGapAdd);
15263
- const bottomCfg = this._viewConfigs.get("bottom");
15264
- if (this._bottomView && bottomCfg) updateView(this._bottomView, bottomCfg, innerGapAdd);
15265
- const leftCfg = this._viewConfigs.get("left");
15266
- if (this._leftView && leftCfg) updateView(this._leftView, leftCfg, outerGapAdd);
15267
- const rightCfg = this._viewConfigs.get("right");
15268
- if (this._rightView && rightCfg) updateView(this._rightView, rightCfg, outerGapAdd);
15269
- if (((_this$_leftView = this._leftView) === null || _this$_leftView === void 0 ? void 0 : _this$_leftView.isCollapsed) && this._leftIndex !== void 0) this._outerSplitview.resizeView(this._leftIndex, this._leftView.collapsedSize);
15270
- if (((_this$_rightView = this._rightView) === null || _this$_rightView === void 0 ? void 0 : _this$_rightView.isCollapsed) && this._rightIndex !== void 0) this._outerSplitview.resizeView(this._rightIndex, this._rightView.collapsedSize);
15271
- if ((_this$_topView = this._topView) === null || _this$_topView === void 0 ? void 0 : _this$_topView.isCollapsed) this._middleColumn.resizeView("top", this._topView.collapsedSize);
15272
- if ((_this$_bottomView = this._bottomView) === null || _this$_bottomView === void 0 ? void 0 : _this$_bottomView.isCollapsed) this._middleColumn.resizeView("bottom", this._bottomView.collapsedSize);
15382
+ this._applyThemeSizing();
15383
+ this._relayout();
15384
+ }
15385
+ /** Whether the other edge of `position`'s axis holds a view. */
15386
+ _hasSiblingOn(position) {
15387
+ return isHorizontal(position) ? this._leftView !== void 0 && this._rightView !== void 0 : this._topView !== void 0 && this._bottomView !== void 0;
15388
+ }
15389
+ /** The splitview `position`'s view sits in, as its edge axis. */
15390
+ _axisFor(position) {
15391
+ return isHorizontal(position) ? this._outerSplitview : this._middleColumn.axis;
15392
+ }
15393
+ /** Re-derive every edge view's base sizing from its original config and
15394
+ * the current defaults. */
15395
+ _applyThemeSizing() {
15396
+ for (const position of EDGE_POSITIONS) {
15397
+ const view = this._getView(position);
15398
+ const baseCfg = this._viewConfigs.get(position);
15399
+ if (view && baseCfg) {
15400
+ var _baseCfg$collapsedSiz;
15401
+ view.updateSizing((_baseCfg$collapsedSiz = baseCfg.collapsedSize) !== null && _baseCfg$collapsedSiz !== void 0 ? _baseCfg$collapsedSiz : this._defaultCollapsedSize, baseCfg.minimumSize);
15402
+ }
15403
+ }
15404
+ }
15405
+ /**
15406
+ * Lay the shell out again at its current size, once it has one. The
15407
+ * splitviews clamp every view to its current constraints, which is how a
15408
+ * collapsed strip or a group at its minimum follows a moved gap share.
15409
+ */
15410
+ _relayout() {
15273
15411
  if (this._currentWidth > 0 && this._currentHeight > 0) this.layout(this._currentWidth, this._currentHeight);
15274
15412
  }
15413
+ /** The view's slot in its splitview: its laid-out size plus the gap share. */
15414
+ _slotSize(position) {
15415
+ switch (position) {
15416
+ case "left": return this._leftIndex === void 0 ? 0 : this._outerSplitview.getViewSize(this._leftIndex);
15417
+ case "right": return this._rightIndex === void 0 ? 0 : this._outerSplitview.getViewSize(this._rightIndex);
15418
+ case "top":
15419
+ case "bottom": return this._middleColumn.getViewSize(position);
15420
+ }
15421
+ }
15275
15422
  removeEdgeView(position) {
15276
15423
  const view = this._getView(position);
15277
15424
  if (!view) return;
@@ -15301,7 +15448,7 @@ react_dom = __toESM(react_dom);
15301
15448
  view.dispose();
15302
15449
  this._viewConfigs.delete(position);
15303
15450
  this._pendingSizes.delete(position);
15304
- this.updateTheme(this._gap, this._defaultCollapsedSize);
15451
+ this._relayout();
15305
15452
  }
15306
15453
  hasEdgeGroup(position) {
15307
15454
  switch (position) {
@@ -15312,6 +15459,7 @@ react_dom = __toESM(react_dom);
15312
15459
  }
15313
15460
  }
15314
15461
  setEdgeGroupVisible(position, visible) {
15462
+ const changes = this._getView(position) !== void 0 && visible !== this.isEdgeGroupVisible(position);
15315
15463
  switch (position) {
15316
15464
  case "left":
15317
15465
  if (this._leftIndex !== void 0) this._outerSplitview.setViewVisible(this._leftIndex, visible);
@@ -15324,6 +15472,7 @@ react_dom = __toESM(react_dom);
15324
15472
  this._middleColumn.setViewVisible(position, visible);
15325
15473
  break;
15326
15474
  }
15475
+ if (changes && this._gap > 0 && this._hasSiblingOn(position)) this._relayout();
15327
15476
  if (visible) this._flushPendingSizes();
15328
15477
  }
15329
15478
  isEdgeGroupVisible(position) {
@@ -15373,8 +15522,7 @@ react_dom = __toESM(react_dom);
15373
15522
  /** A resize lands only on a visible view (a hidden one is pinned to zero)
15374
15523
  * whose splitview has extent to distribute. */
15375
15524
  _canResize(position) {
15376
- if (!this.isEdgeGroupVisible(position)) return false;
15377
- return (position === "left" || position === "right" ? this._outerSplitview.size : this._middleColumn.axisSize) > 0;
15525
+ return this.isEdgeGroupVisible(position) && this._axisFor(position).size > 0;
15378
15526
  }
15379
15527
  _resizeView(position, size) {
15380
15528
  switch (position) {
@@ -15419,59 +15567,45 @@ react_dom = __toESM(react_dom);
15419
15567
  }
15420
15568
  toJSON() {
15421
15569
  const edgeGroups = {};
15422
- const constraints = (view) => ({
15423
- minimumSize: view.configuredMinimumSize,
15424
- maximumSize: Number.isFinite(view.configuredMaximumSize) ? view.configuredMaximumSize : void 0,
15425
- collapsedSize: view.configuredCollapsedSize
15426
- });
15427
- const expandedSize = (view, position, isVisible, liveSize, cachedVisibleSize) => {
15428
- if (view.isCollapsed) return view.lastExpandedSize;
15429
- const pending = this._pendingSizes.get(position);
15430
- if (pending !== void 0) return pending;
15431
- if (!isVisible) return cachedVisibleSize !== null && cachedVisibleSize !== void 0 ? cachedVisibleSize : view.lastExpandedSize;
15432
- return liveSize;
15433
- };
15434
- if (this._leftView && this._leftIndex !== void 0) {
15435
- const visible = this._outerSplitview.isViewVisible(this._leftIndex);
15436
- edgeGroups.left = _objectSpread2$1({
15437
- size: expandedSize(this._leftView, "left", visible, this._outerSplitview.getViewSize(this._leftIndex), this._outerSplitview.getViewCachedVisibleSize(this._leftIndex)),
15438
- visible,
15439
- collapsed: this._leftView.isCollapsed || void 0
15440
- }, constraints(this._leftView));
15441
- }
15442
- if (this._rightView && this._rightIndex !== void 0) {
15443
- const visible = this._outerSplitview.isViewVisible(this._rightIndex);
15444
- edgeGroups.right = _objectSpread2$1({
15445
- size: expandedSize(this._rightView, "right", visible, this._outerSplitview.getViewSize(this._rightIndex), this._outerSplitview.getViewCachedVisibleSize(this._rightIndex)),
15570
+ for (const position of EDGE_POSITIONS) {
15571
+ const view = this._getView(position);
15572
+ if (!view) continue;
15573
+ const visible = this.isEdgeGroupVisible(position);
15574
+ edgeGroups[position] = {
15575
+ size: this._expandedSize(view, position, visible),
15446
15576
  visible,
15447
- collapsed: this._rightView.isCollapsed || void 0
15448
- }, constraints(this._rightView));
15577
+ collapsed: view.isCollapsed || void 0,
15578
+ minimumSize: view.configuredMinimumSize,
15579
+ maximumSize: Number.isFinite(view.configuredMaximumSize) ? view.configuredMaximumSize : void 0,
15580
+ collapsedSize: view.configuredCollapsedSize
15581
+ };
15449
15582
  }
15450
- if (this._topView) {
15451
- const visible = this._middleColumn.isViewVisible("top");
15452
- edgeGroups.top = _objectSpread2$1({
15453
- size: expandedSize(this._topView, "top", visible, this._middleColumn.getViewSize("top"), this._middleColumn.getViewCachedVisibleSize("top")),
15454
- visible,
15455
- collapsed: this._topView.isCollapsed || void 0
15456
- }, constraints(this._topView));
15583
+ return edgeGroups;
15584
+ }
15585
+ /** The size to restore the group to. An expanded-but-hidden group has a
15586
+ * slot of 0, so fall back to its cached visible size (then
15587
+ * lastExpandedSize); otherwise re-showing snaps to minimumSize. */
15588
+ _expandedSize(view, position, visible) {
15589
+ if (view.isCollapsed) return view.lastExpandedSize;
15590
+ const pending = this._pendingSizes.get(position);
15591
+ if (pending !== void 0) return pending;
15592
+ if (!visible) {
15593
+ var _this$_cachedVisibleS;
15594
+ return (_this$_cachedVisibleS = this._cachedVisibleSize(position)) !== null && _this$_cachedVisibleS !== void 0 ? _this$_cachedVisibleS : view.lastExpandedSize;
15457
15595
  }
15458
- if (this._bottomView) {
15459
- const visible = this._middleColumn.isViewVisible("bottom");
15460
- edgeGroups.bottom = _objectSpread2$1({
15461
- size: expandedSize(this._bottomView, "bottom", visible, this._middleColumn.getViewSize("bottom"), this._middleColumn.getViewCachedVisibleSize("bottom")),
15462
- visible,
15463
- collapsed: this._bottomView.isCollapsed || void 0
15464
- }, constraints(this._bottomView));
15596
+ return this._slotSize(position);
15597
+ }
15598
+ /** The slot a hidden view had when it was last visible. */
15599
+ _cachedVisibleSize(position) {
15600
+ switch (position) {
15601
+ case "left": return this._leftIndex === void 0 ? void 0 : this._outerSplitview.getViewCachedVisibleSize(this._leftIndex);
15602
+ case "right": return this._rightIndex === void 0 ? void 0 : this._outerSplitview.getViewCachedVisibleSize(this._rightIndex);
15603
+ case "top":
15604
+ case "bottom": return this._middleColumn.getViewCachedVisibleSize(position);
15465
15605
  }
15466
- return edgeGroups;
15467
15606
  }
15468
15607
  fromJSON(data) {
15469
- for (const position of [
15470
- "left",
15471
- "right",
15472
- "top",
15473
- "bottom"
15474
- ]) {
15608
+ for (const position of EDGE_POSITIONS) {
15475
15609
  var _state$collapsed;
15476
15610
  const state = data[position];
15477
15611
  const view = this._getView(position);
@@ -16179,6 +16313,7 @@ react_dom = __toESM(react_dom);
16179
16313
  this.disableResizing = true;
16180
16314
  this.element.remove();
16181
16315
  this._shellManager = new ShellManager(container, this.element, (w, h) => this._layoutFromShell(w, h), (_options$theme$gap2 = (_options$theme2 = options.theme) === null || _options$theme2 === void 0 ? void 0 : _options$theme2.gap) !== null && _options$theme$gap2 !== void 0 ? _options$theme$gap2 : 0, (_options$theme3 = options.theme) === null || _options$theme3 === void 0 ? void 0 : _options$theme3.edgeGroupCollapsedSize);
16316
+ this._shellManager.locked = !!options.locked;
16182
16317
  this.popupService.updateRoot(this._shellManager.element);
16183
16318
  this._shellThemeClassnames = new Classnames(this._shellManager.element);
16184
16319
  this.rootDropTargetContainer = new DropTargetAnchorContainer(this._shellManager.element, { disabled: true });
@@ -16287,7 +16422,7 @@ react_dom = __toESM(react_dom);
16287
16422
  const service = assertModule(this._popoutWindowService, "PopoutWindow", "api.addPopoutGroup");
16288
16423
  if (!service) return Promise.resolve(false);
16289
16424
  if (itemToPopout instanceof DockviewGroupPanel && itemToPopout.model.location.type === "edge") return Promise.resolve(false);
16290
- if (itemToPopout instanceof DockviewPanel && itemToPopout.group.size === 1) return this.addPopoutGroup(itemToPopout.group, options);
16425
+ if (itemToPopout instanceof DockviewPanel && itemToPopout.group.size === 1 && this.closeEmptyGroups) return this.addPopoutGroup(itemToPopout.group, options);
16291
16426
  const theme = getDockviewTheme(this.gridview.element);
16292
16427
  const element = this.element;
16293
16428
  function getBox() {
@@ -16376,7 +16511,8 @@ react_dom = __toESM(react_dom);
16376
16511
  const overlayRenderContainer = new OverlayRenderContainer(gready, this);
16377
16512
  group.model.renderContainer = overlayRenderContainer;
16378
16513
  const popoutGridview = (_options$overridePopo4 = options === null || options === void 0 ? void 0 : options.overridePopoutGridview) !== null && _options$overridePopo4 !== void 0 ? _options$overridePopo4 : this.createNestedGridview();
16379
- if (!(options === null || options === void 0 ? void 0 : options.overridePopoutGridview)) popoutGridview.addView(group, Sizing.Distribute, [0]);
16514
+ if (options === null || options === void 0 ? void 0 : options.overridePopoutGridview) popoutGridview.locked = this.gridview.locked;
16515
+ else popoutGridview.addView(group, Sizing.Distribute, [0]);
16380
16516
  popoutGridview.element.style.width = "100%";
16381
16517
  popoutGridview.element.style.height = "100%";
16382
16518
  popoutGridview.layout(_window.window.innerWidth, _window.window.innerHeight);
@@ -16771,8 +16907,35 @@ react_dom = __toESM(react_dom);
16771
16907
  });
16772
16908
  for (const { panel, from } of movedPanels) this.fireDidMovePanel(panel, from);
16773
16909
  }
16910
+ /** The `closeEmptyGroups` option: remove a group once it empties. */
16911
+ get closeEmptyGroups() {
16912
+ return this.options.closeEmptyGroups !== false;
16913
+ }
16914
+ /** Whether a move leaves its emptied source group in place. */
16915
+ keepEmptyGroups(options) {
16916
+ var _options$keepEmptyGro;
16917
+ return (_options$keepEmptyGro = options.keepEmptyGroups) !== null && _options$keepEmptyGro !== void 0 ? _options$keepEmptyGro : !this.closeEmptyGroups;
16918
+ }
16774
16919
  removeGroupIfEmpty(group) {
16775
- if (group.model.size === 0) this.doRemoveGroup(group, { skipActive: true });
16920
+ if (group.model.size === 0 && this.closeEmptyGroups) this.doRemoveGroup(group, { skipActive: true });
16921
+ }
16922
+ /**
16923
+ * Locks the main grid, the shell whose splitviews host the edge groups,
16924
+ * and the nested gridviews of floating and popout windows. The base
16925
+ * setter stops at the main grid; `updateOptions` reaches this override.
16926
+ */
16927
+ get locked() {
16928
+ return this.gridview.locked;
16929
+ }
16930
+ set locked(value) {
16931
+ this.gridview.locked = value;
16932
+ if (this._shellManager) this._shellManager.locked = value;
16933
+ for (const gridview of this.nestedGridviews()) gridview.locked = value;
16934
+ }
16935
+ /** The gridviews hosted by open floating and popout windows. */
16936
+ nestedGridviews() {
16937
+ var _this$_popoutWindowSe9, _this$_popoutWindowSe10;
16938
+ return [...this.floatingGroups.map((floating) => floating.gridview), ...((_this$_popoutWindowSe9 = (_this$_popoutWindowSe10 = this._popoutWindowService) === null || _this$_popoutWindowSe10 === void 0 ? void 0 : _this$_popoutWindowSe10.entries) !== null && _this$_popoutWindowSe9 !== void 0 ? _this$_popoutWindowSe9 : []).map((entry) => entry.gridview)];
16776
16939
  }
16777
16940
  /**
16778
16941
  * Build an empty gridview configured to match the main grid's styling and
@@ -16781,7 +16944,7 @@ react_dom = __toESM(react_dom);
16781
16944
  */
16782
16945
  createNestedGridview(orientation = "HORIZONTAL") {
16783
16946
  var _this$options$theme$g, _this$options$theme2;
16784
- return new Gridview(this.gridview.proportionalLayout, this.options.hideBorders ? { separatorBorder: "transparent" } : void 0, orientation, false, (_this$options$theme$g = (_this$options$theme2 = this.options.theme) === null || _this$options$theme2 === void 0 ? void 0 : _this$options$theme2.gap) !== null && _this$options$theme$g !== void 0 ? _this$options$theme$g : 0);
16947
+ return new Gridview(this.gridview.proportionalLayout, this.options.hideBorders ? { separatorBorder: "transparent" } : void 0, orientation, this.gridview.locked, (_this$options$theme$g = (_this$options$theme2 = this.options.theme) === null || _this$options$theme2 === void 0 ? void 0 : _this$options$theme2.gap) !== null && _this$options$theme$g !== void 0 ? _this$options$theme$g : 0);
16785
16948
  }
16786
16949
  /**
16787
16950
  * Wrap a (populated) floating gridview in an overlay window: title bar /
@@ -16855,10 +17018,8 @@ react_dom = __toESM(react_dom);
16855
17018
  const newDndStrategy = this.options.dndStrategy;
16856
17019
  if (oldDisableDnd !== newDisableDnd || oldDndStrategy !== newDndStrategy) this.updateDragAndDropState();
16857
17020
  if ("proportionalLayout" in options) {
16858
- var _this$_popoutWindowSe9, _this$_popoutWindowSe10;
16859
17021
  const proportionalLayout = this.gridview.proportionalLayout;
16860
- for (const floating of this.floatingGroups) floating.gridview.proportionalLayout = proportionalLayout;
16861
- for (const entry of (_this$_popoutWindowSe9 = (_this$_popoutWindowSe10 = this._popoutWindowService) === null || _this$_popoutWindowSe10 === void 0 ? void 0 : _this$_popoutWindowSe10.entries) !== null && _this$_popoutWindowSe9 !== void 0 ? _this$_popoutWindowSe9 : []) entry.gridview.proportionalLayout = proportionalLayout;
17022
+ for (const gridview of this.nestedGridviews()) gridview.proportionalLayout = proportionalLayout;
16862
17023
  }
16863
17024
  if ("theme" in options) this.updateTheme();
16864
17025
  if ("createRightHeaderActionComponent" in options || "createLeftHeaderActionComponent" in options || "createPrefixHeaderActionComponent" in options) {
@@ -16927,7 +17088,7 @@ react_dom = __toESM(react_dom);
16927
17088
  const resizeDisposable = group.onDidChange((event) => {
16928
17089
  var _this$_shellManager3;
16929
17090
  if (!event) return;
16930
- const size = position === "left" || position === "right" ? event.width : event.height;
17091
+ const size = isHorizontal(position) ? event.width : event.height;
16931
17092
  if (typeof size !== "number") return;
16932
17093
  (_this$_shellManager3 = this._shellManager) === null || _this$_shellManager3 === void 0 || _this$_shellManager3.resizeEdgeGroup(position, size);
16933
17094
  });
@@ -17605,9 +17766,12 @@ react_dom = __toESM(react_dom);
17605
17766
  }
17606
17767
  closeAllGroups() {
17607
17768
  this.mutation("remove", () => {
17608
- for (const entry of this._groups.entries()) {
17609
- const [_, group] = entry;
17610
- group.value.model.closeAllPanels();
17769
+ const groups = Array.from(this._groups.values()).map((_) => _.value);
17770
+ for (const group of groups) {
17771
+ if (!this._groups.has(group.id)) continue;
17772
+ group.model.closeAllPanels();
17773
+ const keepLast = this.options.noPanelsOverlay === "emptyGroup" && this._groups.size === 1;
17774
+ if (!this.closeEmptyGroups && !keepLast && this._groups.has(group.id) && group.model.size === 0) this.removeGroup(group, { skipActive: true });
17611
17775
  }
17612
17776
  });
17613
17777
  }
@@ -17732,7 +17896,7 @@ react_dom = __toESM(react_dom);
17732
17896
  this.mutation("remove", () => {
17733
17897
  var _options$removeEmptyG;
17734
17898
  return this._doRemovePanel(panel, {
17735
- removeEmptyGroup: (_options$removeEmptyG = options === null || options === void 0 ? void 0 : options.removeEmptyGroup) !== null && _options$removeEmptyG !== void 0 ? _options$removeEmptyG : true,
17899
+ removeEmptyGroup: (_options$removeEmptyG = options === null || options === void 0 ? void 0 : options.removeEmptyGroup) !== null && _options$removeEmptyG !== void 0 ? _options$removeEmptyG : this.closeEmptyGroups,
17736
17900
  skipDispose: options === null || options === void 0 ? void 0 : options.skipDispose,
17737
17901
  skipSetActiveGroup: options === null || options === void 0 ? void 0 : options.skipSetActiveGroup
17738
17902
  });
@@ -17888,7 +18052,7 @@ react_dom = __toESM(react_dom);
17888
18052
  if (!(options === null || options === void 0 ? void 0 : options.skipDispose)) {
17889
18053
  if (!(options === null || options === void 0 ? void 0 : options.skipPopoutAssociated)) {
17890
18054
  const refGroup = selectedGroup.referenceGroup ? this.getPanel(selectedGroup.referenceGroup) : void 0;
17891
- if ((refGroup === null || refGroup === void 0 ? void 0 : refGroup.panels.length) === 0) this.removeGroup(refGroup);
18055
+ if ((refGroup === null || refGroup === void 0 ? void 0 : refGroup.panels.length) === 0 && (this.closeEmptyGroups || !refGroup.api.isVisible)) this.removeGroup(refGroup);
17892
18056
  }
17893
18057
  selectedGroup.popoutGroup.dispose();
17894
18058
  this._groups.delete(group.id);
@@ -18148,7 +18312,7 @@ react_dom = __toESM(react_dom);
18148
18312
  skipSetActiveGroup: true
18149
18313
  }));
18150
18314
  if (!removedPanel) throw new Error(`dockview: No panel with id ${sourceItemId}`);
18151
- if (!options.keepEmptyGroups && sourceGroup.model.size === 0 && sourceGroup !== destinationGroup) this.doRemoveGroup(sourceGroup, { skipActive: true });
18315
+ if (!this.keepEmptyGroups(options) && sourceGroup.model.size === 0 && sourceGroup !== destinationGroup) this.doRemoveGroup(sourceGroup, { skipActive: true });
18152
18316
  const isDestinationGroupEmpty = destinationGroup.model.size === 0;
18153
18317
  this.movingLock(() => {
18154
18318
  var _options$skipSetActiv;
@@ -18168,10 +18332,12 @@ react_dom = __toESM(react_dom);
18168
18332
  const destinationGridview = this.getGridviewForGroup(destinationGroup);
18169
18333
  const referenceLocation = getGridLocation(destinationGroup.element);
18170
18334
  const targetLocation = getRelativeLocation(destinationGridview.orientation, referenceLocation, destinationTarget);
18171
- if (sourceGroup.size < 2) {
18335
+ if (sourceGroup.size < 2 && !this.keepEmptyGroups(options)) {
18172
18336
  /**
18173
18337
  * If we are moving from a group which only has one panel left we will consider
18174
- * moving the group itself rather than moving the panel into a newly created group
18338
+ * moving the group itself rather than moving the panel into a newly created group.
18339
+ * When empty groups are kept the group must stay where it is, so the panel is
18340
+ * moved into a newly created group like any other panel.
18175
18341
  */
18176
18342
  const [targetParentLocation, to] = tail(targetLocation);
18177
18343
  if (sourceGroup.api.location.type === "grid" && destinationGridview === this.gridview) {
@@ -18296,7 +18462,7 @@ react_dom = __toESM(react_dom);
18296
18462
  const dropLocation = getRelativeLocation(this.gridview.orientation, referenceLocation, destinationTarget);
18297
18463
  targetGroup = this.createGroupAtLocation(dropLocation, this.dropSizing(referenceLocation));
18298
18464
  }
18299
- if (!options.keepEmptyGroups && sourceGroup.model.size === 0 && sourceGroup !== targetGroup) this.doRemoveGroup(sourceGroup, { skipActive: true });
18465
+ if (!this.keepEmptyGroups(options) && sourceGroup.model.size === 0 && sourceGroup !== targetGroup) this.doRemoveGroup(sourceGroup, { skipActive: true });
18300
18466
  addPanelsToGroup(targetGroup);
18301
18467
  }
18302
18468
  moveGroup(options) {
@@ -18623,13 +18789,12 @@ react_dom = __toESM(react_dom);
18623
18789
  return location.length % 2 == 1 ? rootOrientation : orthogonal(rootOrientation);
18624
18790
  }
18625
18791
  updateTheme() {
18626
- var _this$_options$theme, _this$_shellThemeClas, _theme$gap, _this$_popoutWindowSe25, _this$_popoutWindowSe26, _this$_shellManager5, _theme$edgeGroupColla, _theme$tabGroupIndica;
18792
+ var _this$_options$theme, _this$_shellThemeClas, _theme$gap, _this$_shellManager5, _theme$edgeGroupColla, _theme$tabGroupIndica;
18627
18793
  const theme = (_this$_options$theme = this._options.theme) !== null && _this$_options$theme !== void 0 ? _this$_options$theme : themeAbyss;
18628
18794
  (_this$_shellThemeClas = this._shellThemeClassnames) === null || _this$_shellThemeClas === void 0 || _this$_shellThemeClas.setClassNames(theme.className);
18629
18795
  const gap = (_theme$gap = theme.gap) !== null && _theme$gap !== void 0 ? _theme$gap : 0;
18630
18796
  this.gridview.margin = gap;
18631
- for (const floating of this.floatingGroups) floating.gridview.margin = gap;
18632
- for (const entry of (_this$_popoutWindowSe25 = (_this$_popoutWindowSe26 = this._popoutWindowService) === null || _this$_popoutWindowSe26 === void 0 ? void 0 : _this$_popoutWindowSe26.entries) !== null && _this$_popoutWindowSe25 !== void 0 ? _this$_popoutWindowSe25 : []) entry.gridview.margin = gap;
18797
+ for (const gridview of this.nestedGridviews()) gridview.margin = gap;
18633
18798
  (_this$_shellManager5 = this._shellManager) === null || _this$_shellManager5 === void 0 || _this$_shellManager5.updateTheme(gap, (_theme$edgeGroupColla = theme.edgeGroupCollapsedSize) !== null && _theme$edgeGroupColla !== void 0 ? _theme$edgeGroupColla : 35);
18634
18799
  if (theme.dndOverlayBorder === void 0) {
18635
18800
  var _this$_shellManager6;