dockview-react 8.5.1 → 8.6.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.
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * dockview-react
3
- * @version 8.5.1
3
+ * @version 8.6.0
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.0
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];
@@ -3910,9 +3928,13 @@ react_dom = __toESM(react_dom);
3910
3928
  }
3911
3929
  /**
3912
3930
  * Remove a panel given the panel object.
3931
+ *
3932
+ * `removeEmptyGroup` overrides the `closeEmptyGroups` option for this
3933
+ * call: `false` keeps the group if the panel was its last one, `true`
3934
+ * removes it.
3913
3935
  */
3914
- removePanel(panel) {
3915
- this.component.withOrigin("api", () => this.component.removePanel(panel));
3936
+ removePanel(panel, options) {
3937
+ this.component.withOrigin("api", () => this.component.removePanel(panel, options));
3916
3938
  }
3917
3939
  /**
3918
3940
  * Add a group and return the created object.
@@ -6887,6 +6909,7 @@ react_dom = __toESM(react_dom);
6887
6909
  dndStrategy: void 0,
6888
6910
  className: void 0,
6889
6911
  noPanelsOverlay: void 0,
6912
+ closeEmptyGroups: void 0,
6890
6913
  dndEdges: void 0,
6891
6914
  dropPositionResolver: void 0,
6892
6915
  dndCompass: void 0,
@@ -11376,7 +11399,7 @@ react_dom = __toESM(react_dom);
11376
11399
  });
11377
11400
  return;
11378
11401
  }
11379
- if (this.tabsContainer.indexOf(data.panelId) !== -1 && this.tabsContainer.size === 1) return;
11402
+ if (this.tabsContainer.indexOf(data.panelId) !== -1 && this.tabsContainer.size === 1 && this.accessor.options.closeEmptyGroups !== false) return;
11380
11403
  const { groupId, panelId } = data;
11381
11404
  if (this.id === groupId && !position) {
11382
11405
  if (this.tabsContainer.indexOf(panelId) === index) return;
@@ -11617,6 +11640,7 @@ react_dom = __toESM(react_dom);
11617
11640
  constructor(id, accessor) {
11618
11641
  super(id, "__dockviewgroup__");
11619
11642
  this.accessor = accessor;
11643
+ this._isApplyingSize = false;
11620
11644
  this._onDidLocationChange = new Emitter();
11621
11645
  this.onDidLocationChange = this._onDidLocationChange.event;
11622
11646
  this._onDidActivePanelChange = new Emitter();
@@ -11629,14 +11653,29 @@ react_dom = __toESM(react_dom);
11629
11653
  this.onDidHeaderDirectionChange = this._onDidHeaderDirectionChange.event;
11630
11654
  this.addDisposables(this._onDidLocationChange, this._onDidActivePanelChange, this._onDidCollapsedChange, this._onDidPeekChange, this._onDidHeaderDirectionChange, this._onDidVisibilityChange.event((event) => {
11631
11655
  if (event.isVisible && this._pendingSize) {
11632
- super.setSize(this._pendingSize);
11656
+ const pendingSize = this._pendingSize;
11633
11657
  this._pendingSize = void 0;
11658
+ super.setSize(pendingSize);
11634
11659
  }
11660
+ }), this.onDidDimensionsChange((event) => {
11661
+ var _this$_reportedSize, _this$_reportedSize2;
11662
+ 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);
11663
+ this._reportedSize = {
11664
+ width: event.width,
11665
+ height: event.height
11666
+ };
11667
+ if (moved && this.isVisible && !this._isApplyingSize) this._pendingSize = void 0;
11635
11668
  }));
11636
11669
  }
11637
11670
  setSize(event) {
11638
- this._pendingSize = _objectSpread2$1({}, event);
11639
- super.setSize(event);
11671
+ if (typeof event.width === "number" || typeof event.height === "number") this._pendingSize = _objectSpread2$1({}, event);
11672
+ const wasApplyingSize = this._isApplyingSize;
11673
+ this._isApplyingSize = true;
11674
+ try {
11675
+ super.setSize(event);
11676
+ } finally {
11677
+ this._isApplyingSize = wasApplyingSize;
11678
+ }
11640
11679
  }
11641
11680
  close() {
11642
11681
  if (!this._group) return;
@@ -14883,6 +14922,20 @@ react_dom = __toESM(react_dom);
14883
14922
  return el;
14884
14923
  }
14885
14924
  };
14925
+ const EDGE_POSITIONS = [
14926
+ "left",
14927
+ "right",
14928
+ "top",
14929
+ "bottom"
14930
+ ];
14931
+ function isHorizontal(position) {
14932
+ return position === "left" || position === "right";
14933
+ }
14934
+ /** A view outside a shell: no gap, and every layout is real. */
14935
+ const UNBOUNDED_AXIS = {
14936
+ marginShare: 0,
14937
+ size: Number.POSITIVE_INFINITY
14938
+ };
14886
14939
  var EdgeGroupView = class {
14887
14940
  /**
14888
14941
  * The effective (pre-gap) collapsed size: the measured tab-strip size when
@@ -14894,7 +14947,7 @@ react_dom = __toESM(react_dom);
14894
14947
  }
14895
14948
  get minimumSize() {
14896
14949
  if (this._isCollapsed) return this.collapsedSize;
14897
- return this._baseMinimumSize !== void 0 ? this._baseMinimumSize + this._gapAdd : this._effectiveBaseCollapsed + 50 + this._gapAdd;
14950
+ return this._baseMinimumSize !== void 0 ? this._baseMinimumSize + this._axis.marginShare : this._effectiveBaseCollapsed + 50 + this._axis.marginShare;
14898
14951
  }
14899
14952
  get maximumSize() {
14900
14953
  return this._isCollapsed ? this.collapsedSize : this._expandedMaximumSize;
@@ -14911,10 +14964,10 @@ react_dom = __toESM(react_dom);
14911
14964
  }
14912
14965
  /** Without the gap share. */
14913
14966
  get lastExpandedContentSize() {
14914
- return this._lastExpandedSize - this._gapAdd;
14967
+ return this._lastExpandedSize - this._axis.marginShare;
14915
14968
  }
14916
14969
  get collapsedSize() {
14917
- return this._effectiveBaseCollapsed + this._gapAdd;
14970
+ return this._effectiveBaseCollapsed + this._axis.marginShare;
14918
14971
  }
14919
14972
  /** The user-configured (pre-gap) geometry constraints, for serialization.
14920
14973
  * These are the raw values passed to `addEdgeGroup`, unlike the effective
@@ -14929,13 +14982,14 @@ react_dom = __toESM(react_dom);
14929
14982
  get configuredCollapsedSize() {
14930
14983
  return this._baseCollapsedSize;
14931
14984
  }
14932
- constructor(options, group, orientation, gapAdd = 0) {
14985
+ constructor(options, group, orientation, axis = UNBOUNDED_AXIS) {
14933
14986
  var _options$collapsedSiz, _options$maximumSize, _options$initialSize;
14934
14987
  this._onDidChange = new Emitter();
14935
14988
  this.onDidChange = this._onDidChange.event;
14936
14989
  this.snap = false;
14937
14990
  this.priority = "low";
14938
14991
  this._isCollapsed = false;
14992
+ this._isVisible = true;
14939
14993
  this._tabSizeDisposables = new CompositeDisposable();
14940
14994
  this._group = group;
14941
14995
  this._orientation = orientation;
@@ -14943,7 +14997,7 @@ react_dom = __toESM(react_dom);
14943
14997
  group.element.dataset.testid = `dv-edge-group-${options.id}`;
14944
14998
  this._baseCollapsedSize = (_options$collapsedSiz = options.collapsedSize) !== null && _options$collapsedSiz !== void 0 ? _options$collapsedSiz : 35;
14945
14999
  this._baseMinimumSize = options.minimumSize;
14946
- this._gapAdd = gapAdd;
15000
+ this._axis = axis;
14947
15001
  this._expandedMaximumSize = (_options$maximumSize = options.maximumSize) !== null && _options$maximumSize !== void 0 ? _options$maximumSize : Number.POSITIVE_INFINITY;
14948
15002
  this._lastExpandedSize = (_options$initialSize = options.initialSize) !== null && _options$initialSize !== void 0 ? _options$initialSize : 200;
14949
15003
  if (options.collapsed) {
@@ -14976,7 +15030,7 @@ react_dom = __toESM(react_dom);
14976
15030
  if (this._isCollapsed) this._onDidChange.fire({ size: this.collapsedSize });
14977
15031
  }
14978
15032
  layout(size, orthogonalSize) {
14979
- if (!this._isCollapsed) this._lastExpandedSize = size + this._gapAdd;
15033
+ if (!this._isCollapsed && this._isVisible && this._axis.size > 0) this._lastExpandedSize = size + this._axis.marginShare;
14980
15034
  if (this._orientation === "horizontal") this._group.layout(size, orthogonalSize);
14981
15035
  else this._group.layout(orthogonalSize, size);
14982
15036
  }
@@ -14985,25 +15039,25 @@ react_dom = __toESM(react_dom);
14985
15039
  this._isCollapsed = collapsed;
14986
15040
  this._group.element.classList.toggle("dv-edge-collapsed", collapsed);
14987
15041
  }
14988
- setVisible(_visible) {}
15042
+ setVisible(visible) {
15043
+ this._isVisible = visible;
15044
+ }
14989
15045
  /**
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.
15046
+ * Set the expanded size without triggering a layout: from serialized
15047
+ * state (before setCollapsed(true) during fromJSON, so that expanding
15048
+ * restores the right size) or from an explicit resize.
14993
15049
  */
14994
15050
  restoreExpandedSize(size) {
14995
15051
  this._lastExpandedSize = size;
14996
15052
  }
14997
15053
  /**
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.
15054
+ * Apply a new base (pre-gap) collapsed size and base minimum size after a
15055
+ * theme change. Base values come from the original config. A live measured
15056
+ * tab size (if any) continues to win over the base collapsed size.
15002
15057
  */
15003
- updateSizing(baseCollapsedSize, baseMinimumSize, gapAdd) {
15058
+ updateSizing(baseCollapsedSize, baseMinimumSize) {
15004
15059
  this._baseCollapsedSize = baseCollapsedSize;
15005
15060
  this._baseMinimumSize = baseMinimumSize;
15006
- this._gapAdd = gapAdd;
15007
15061
  }
15008
15062
  dispose() {
15009
15063
  this._tabSizeDisposables.dispose();
@@ -15105,13 +15159,20 @@ react_dom = __toESM(react_dom);
15105
15159
  const index = position === "top" ? this._topIndex : this._bottomIndex;
15106
15160
  if (index !== void 0) this._splitview.resizeView(index, size);
15107
15161
  }
15108
- /** The inner splitview's primary-axis (height) extent; zero until laid out. */
15109
- get axisSize() {
15110
- return this._splitview.size;
15162
+ /** The inner splitview, as the axis its edge views read. */
15163
+ get axis() {
15164
+ return this._splitview;
15111
15165
  }
15112
15166
  updateMargin(gap) {
15113
15167
  this._splitview.margin = gap;
15114
15168
  }
15169
+ /** Disables the sashes of the top/bottom edge groups. */
15170
+ get disabled() {
15171
+ return this._splitview.disabled;
15172
+ }
15173
+ set disabled(value) {
15174
+ this._splitview.disabled = value;
15175
+ }
15115
15176
  dispose() {
15116
15177
  this._onDidChange.dispose();
15117
15178
  this._splitview.dispose();
@@ -15161,8 +15222,6 @@ react_dom = __toESM(react_dom);
15161
15222
  const width = Math.round(entry.contentRect.width);
15162
15223
  const height = Math.round(entry.contentRect.height);
15163
15224
  if (width === this._currentWidth && height === this._currentHeight) return;
15164
- this._currentWidth = width;
15165
- this._currentHeight = height;
15166
15225
  this.layout(width, height);
15167
15226
  }), this._outerSplitview, this._middleColumn, centerView);
15168
15227
  }
@@ -15170,6 +15229,18 @@ react_dom = __toESM(react_dom);
15170
15229
  return this._shellElement;
15171
15230
  }
15172
15231
  /**
15232
+ * The dock's `locked` option. The edge groups are resized with the sashes
15233
+ * of the shell's own splitviews (left/right in the outer one, top/bottom
15234
+ * in the middle column), so locking disables both.
15235
+ */
15236
+ get locked() {
15237
+ return this._outerSplitview.disabled;
15238
+ }
15239
+ set locked(value) {
15240
+ this._outerSplitview.disabled = value;
15241
+ this._middleColumn.disabled = value;
15242
+ }
15243
+ /**
15173
15244
  * Add an edge group view at the given position. The view wraps the
15174
15245
  * provided group element inside the shell's splitview layout.
15175
15246
  * Throws if a group at this position is already registered.
@@ -15177,14 +15248,7 @@ react_dom = __toESM(react_dom);
15177
15248
  addEdgeView(position, options, group) {
15178
15249
  if (this.hasEdgeGroup(position)) throw new Error(`dockview: edge group already registered at position '${position}'`);
15179
15250
  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);
15251
+ const view = new EdgeGroupView(_objectSpread2$1({ collapsedSize: this._defaultCollapsedSize }, options), group, isHorizontal(position) ? "horizontal" : "vertical", this._axisFor(position));
15188
15252
  const initialSize = view.isCollapsed ? view.collapsedSize : view.lastExpandedSize;
15189
15253
  switch (position) {
15190
15254
  case "left":
@@ -15213,7 +15277,7 @@ react_dom = __toESM(react_dom);
15213
15277
  }
15214
15278
  this._disposables.addDisposables(view);
15215
15279
  if (!view.isCollapsed && !this._canResize(position)) this._pendingSizes.set(position, initialSize);
15216
- this.updateTheme(this._gap, this._defaultCollapsedSize);
15280
+ this._relayout();
15217
15281
  return view;
15218
15282
  }
15219
15283
  /**
@@ -15235,6 +15299,8 @@ react_dom = __toESM(react_dom);
15235
15299
  this.layout(width, height);
15236
15300
  }
15237
15301
  layout(width, height) {
15302
+ this._currentWidth = width;
15303
+ this._currentHeight = height;
15238
15304
  this._outerSplitview.layout(width, height);
15239
15305
  this._flushPendingSizes();
15240
15306
  }
@@ -15244,34 +15310,50 @@ react_dom = __toESM(react_dom);
15244
15310
  * and tab-strip dimensions.
15245
15311
  */
15246
15312
  updateTheme(gap, defaultCollapsedSize) {
15247
- var _this$_leftView, _this$_rightView, _this$_topView, _this$_bottomView;
15248
15313
  this._gap = gap;
15249
15314
  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
15315
  this._outerSplitview.margin = gap;
15255
15316
  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);
15317
+ this._applyThemeSizing();
15318
+ this._relayout();
15319
+ }
15320
+ /** Whether the other edge of `position`'s axis holds a view. */
15321
+ _hasSiblingOn(position) {
15322
+ return isHorizontal(position) ? this._leftView !== void 0 && this._rightView !== void 0 : this._topView !== void 0 && this._bottomView !== void 0;
15323
+ }
15324
+ /** The splitview `position`'s view sits in, as its edge axis. */
15325
+ _axisFor(position) {
15326
+ return isHorizontal(position) ? this._outerSplitview : this._middleColumn.axis;
15327
+ }
15328
+ /** Re-derive every edge view's base sizing from its original config and
15329
+ * the current defaults. */
15330
+ _applyThemeSizing() {
15331
+ for (const position of EDGE_POSITIONS) {
15332
+ const view = this._getView(position);
15333
+ const baseCfg = this._viewConfigs.get(position);
15334
+ if (view && baseCfg) {
15335
+ var _baseCfg$collapsedSiz;
15336
+ view.updateSizing((_baseCfg$collapsedSiz = baseCfg.collapsedSize) !== null && _baseCfg$collapsedSiz !== void 0 ? _baseCfg$collapsedSiz : this._defaultCollapsedSize, baseCfg.minimumSize);
15337
+ }
15338
+ }
15339
+ }
15340
+ /**
15341
+ * Lay the shell out again at its current size, once it has one. The
15342
+ * splitviews clamp every view to its current constraints, which is how a
15343
+ * collapsed strip or a group at its minimum follows a moved gap share.
15344
+ */
15345
+ _relayout() {
15273
15346
  if (this._currentWidth > 0 && this._currentHeight > 0) this.layout(this._currentWidth, this._currentHeight);
15274
15347
  }
15348
+ /** The view's slot in its splitview: its laid-out size plus the gap share. */
15349
+ _slotSize(position) {
15350
+ switch (position) {
15351
+ case "left": return this._leftIndex === void 0 ? 0 : this._outerSplitview.getViewSize(this._leftIndex);
15352
+ case "right": return this._rightIndex === void 0 ? 0 : this._outerSplitview.getViewSize(this._rightIndex);
15353
+ case "top":
15354
+ case "bottom": return this._middleColumn.getViewSize(position);
15355
+ }
15356
+ }
15275
15357
  removeEdgeView(position) {
15276
15358
  const view = this._getView(position);
15277
15359
  if (!view) return;
@@ -15301,7 +15383,7 @@ react_dom = __toESM(react_dom);
15301
15383
  view.dispose();
15302
15384
  this._viewConfigs.delete(position);
15303
15385
  this._pendingSizes.delete(position);
15304
- this.updateTheme(this._gap, this._defaultCollapsedSize);
15386
+ this._relayout();
15305
15387
  }
15306
15388
  hasEdgeGroup(position) {
15307
15389
  switch (position) {
@@ -15312,6 +15394,7 @@ react_dom = __toESM(react_dom);
15312
15394
  }
15313
15395
  }
15314
15396
  setEdgeGroupVisible(position, visible) {
15397
+ const changes = this._getView(position) !== void 0 && visible !== this.isEdgeGroupVisible(position);
15315
15398
  switch (position) {
15316
15399
  case "left":
15317
15400
  if (this._leftIndex !== void 0) this._outerSplitview.setViewVisible(this._leftIndex, visible);
@@ -15324,6 +15407,7 @@ react_dom = __toESM(react_dom);
15324
15407
  this._middleColumn.setViewVisible(position, visible);
15325
15408
  break;
15326
15409
  }
15410
+ if (changes && this._gap > 0 && this._hasSiblingOn(position)) this._relayout();
15327
15411
  if (visible) this._flushPendingSizes();
15328
15412
  }
15329
15413
  isEdgeGroupVisible(position) {
@@ -15373,8 +15457,7 @@ react_dom = __toESM(react_dom);
15373
15457
  /** A resize lands only on a visible view (a hidden one is pinned to zero)
15374
15458
  * whose splitview has extent to distribute. */
15375
15459
  _canResize(position) {
15376
- if (!this.isEdgeGroupVisible(position)) return false;
15377
- return (position === "left" || position === "right" ? this._outerSplitview.size : this._middleColumn.axisSize) > 0;
15460
+ return this.isEdgeGroupVisible(position) && this._axisFor(position).size > 0;
15378
15461
  }
15379
15462
  _resizeView(position, size) {
15380
15463
  switch (position) {
@@ -15419,59 +15502,45 @@ react_dom = __toESM(react_dom);
15419
15502
  }
15420
15503
  toJSON() {
15421
15504
  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)),
15505
+ for (const position of EDGE_POSITIONS) {
15506
+ const view = this._getView(position);
15507
+ if (!view) continue;
15508
+ const visible = this.isEdgeGroupVisible(position);
15509
+ edgeGroups[position] = {
15510
+ size: this._expandedSize(view, position, visible),
15446
15511
  visible,
15447
- collapsed: this._rightView.isCollapsed || void 0
15448
- }, constraints(this._rightView));
15512
+ collapsed: view.isCollapsed || void 0,
15513
+ minimumSize: view.configuredMinimumSize,
15514
+ maximumSize: Number.isFinite(view.configuredMaximumSize) ? view.configuredMaximumSize : void 0,
15515
+ collapsedSize: view.configuredCollapsedSize
15516
+ };
15449
15517
  }
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));
15518
+ return edgeGroups;
15519
+ }
15520
+ /** The size to restore the group to. An expanded-but-hidden group has a
15521
+ * slot of 0, so fall back to its cached visible size (then
15522
+ * lastExpandedSize); otherwise re-showing snaps to minimumSize. */
15523
+ _expandedSize(view, position, visible) {
15524
+ if (view.isCollapsed) return view.lastExpandedSize;
15525
+ const pending = this._pendingSizes.get(position);
15526
+ if (pending !== void 0) return pending;
15527
+ if (!visible) {
15528
+ var _this$_cachedVisibleS;
15529
+ return (_this$_cachedVisibleS = this._cachedVisibleSize(position)) !== null && _this$_cachedVisibleS !== void 0 ? _this$_cachedVisibleS : view.lastExpandedSize;
15457
15530
  }
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));
15531
+ return this._slotSize(position);
15532
+ }
15533
+ /** The slot a hidden view had when it was last visible. */
15534
+ _cachedVisibleSize(position) {
15535
+ switch (position) {
15536
+ case "left": return this._leftIndex === void 0 ? void 0 : this._outerSplitview.getViewCachedVisibleSize(this._leftIndex);
15537
+ case "right": return this._rightIndex === void 0 ? void 0 : this._outerSplitview.getViewCachedVisibleSize(this._rightIndex);
15538
+ case "top":
15539
+ case "bottom": return this._middleColumn.getViewCachedVisibleSize(position);
15465
15540
  }
15466
- return edgeGroups;
15467
15541
  }
15468
15542
  fromJSON(data) {
15469
- for (const position of [
15470
- "left",
15471
- "right",
15472
- "top",
15473
- "bottom"
15474
- ]) {
15543
+ for (const position of EDGE_POSITIONS) {
15475
15544
  var _state$collapsed;
15476
15545
  const state = data[position];
15477
15546
  const view = this._getView(position);
@@ -16179,6 +16248,7 @@ react_dom = __toESM(react_dom);
16179
16248
  this.disableResizing = true;
16180
16249
  this.element.remove();
16181
16250
  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);
16251
+ this._shellManager.locked = !!options.locked;
16182
16252
  this.popupService.updateRoot(this._shellManager.element);
16183
16253
  this._shellThemeClassnames = new Classnames(this._shellManager.element);
16184
16254
  this.rootDropTargetContainer = new DropTargetAnchorContainer(this._shellManager.element, { disabled: true });
@@ -16287,7 +16357,7 @@ react_dom = __toESM(react_dom);
16287
16357
  const service = assertModule(this._popoutWindowService, "PopoutWindow", "api.addPopoutGroup");
16288
16358
  if (!service) return Promise.resolve(false);
16289
16359
  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);
16360
+ if (itemToPopout instanceof DockviewPanel && itemToPopout.group.size === 1 && this.closeEmptyGroups) return this.addPopoutGroup(itemToPopout.group, options);
16291
16361
  const theme = getDockviewTheme(this.gridview.element);
16292
16362
  const element = this.element;
16293
16363
  function getBox() {
@@ -16376,7 +16446,8 @@ react_dom = __toESM(react_dom);
16376
16446
  const overlayRenderContainer = new OverlayRenderContainer(gready, this);
16377
16447
  group.model.renderContainer = overlayRenderContainer;
16378
16448
  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]);
16449
+ if (options === null || options === void 0 ? void 0 : options.overridePopoutGridview) popoutGridview.locked = this.gridview.locked;
16450
+ else popoutGridview.addView(group, Sizing.Distribute, [0]);
16380
16451
  popoutGridview.element.style.width = "100%";
16381
16452
  popoutGridview.element.style.height = "100%";
16382
16453
  popoutGridview.layout(_window.window.innerWidth, _window.window.innerHeight);
@@ -16771,8 +16842,35 @@ react_dom = __toESM(react_dom);
16771
16842
  });
16772
16843
  for (const { panel, from } of movedPanels) this.fireDidMovePanel(panel, from);
16773
16844
  }
16845
+ /** The `closeEmptyGroups` option: remove a group once it empties. */
16846
+ get closeEmptyGroups() {
16847
+ return this.options.closeEmptyGroups !== false;
16848
+ }
16849
+ /** Whether a move leaves its emptied source group in place. */
16850
+ keepEmptyGroups(options) {
16851
+ var _options$keepEmptyGro;
16852
+ return (_options$keepEmptyGro = options.keepEmptyGroups) !== null && _options$keepEmptyGro !== void 0 ? _options$keepEmptyGro : !this.closeEmptyGroups;
16853
+ }
16774
16854
  removeGroupIfEmpty(group) {
16775
- if (group.model.size === 0) this.doRemoveGroup(group, { skipActive: true });
16855
+ if (group.model.size === 0 && this.closeEmptyGroups) this.doRemoveGroup(group, { skipActive: true });
16856
+ }
16857
+ /**
16858
+ * Locks the main grid, the shell whose splitviews host the edge groups,
16859
+ * and the nested gridviews of floating and popout windows. The base
16860
+ * setter stops at the main grid; `updateOptions` reaches this override.
16861
+ */
16862
+ get locked() {
16863
+ return this.gridview.locked;
16864
+ }
16865
+ set locked(value) {
16866
+ this.gridview.locked = value;
16867
+ if (this._shellManager) this._shellManager.locked = value;
16868
+ for (const gridview of this.nestedGridviews()) gridview.locked = value;
16869
+ }
16870
+ /** The gridviews hosted by open floating and popout windows. */
16871
+ nestedGridviews() {
16872
+ var _this$_popoutWindowSe9, _this$_popoutWindowSe10;
16873
+ 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
16874
  }
16777
16875
  /**
16778
16876
  * Build an empty gridview configured to match the main grid's styling and
@@ -16781,7 +16879,7 @@ react_dom = __toESM(react_dom);
16781
16879
  */
16782
16880
  createNestedGridview(orientation = "HORIZONTAL") {
16783
16881
  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);
16882
+ 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
16883
  }
16786
16884
  /**
16787
16885
  * Wrap a (populated) floating gridview in an overlay window: title bar /
@@ -16855,10 +16953,8 @@ react_dom = __toESM(react_dom);
16855
16953
  const newDndStrategy = this.options.dndStrategy;
16856
16954
  if (oldDisableDnd !== newDisableDnd || oldDndStrategy !== newDndStrategy) this.updateDragAndDropState();
16857
16955
  if ("proportionalLayout" in options) {
16858
- var _this$_popoutWindowSe9, _this$_popoutWindowSe10;
16859
16956
  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;
16957
+ for (const gridview of this.nestedGridviews()) gridview.proportionalLayout = proportionalLayout;
16862
16958
  }
16863
16959
  if ("theme" in options) this.updateTheme();
16864
16960
  if ("createRightHeaderActionComponent" in options || "createLeftHeaderActionComponent" in options || "createPrefixHeaderActionComponent" in options) {
@@ -16927,7 +17023,7 @@ react_dom = __toESM(react_dom);
16927
17023
  const resizeDisposable = group.onDidChange((event) => {
16928
17024
  var _this$_shellManager3;
16929
17025
  if (!event) return;
16930
- const size = position === "left" || position === "right" ? event.width : event.height;
17026
+ const size = isHorizontal(position) ? event.width : event.height;
16931
17027
  if (typeof size !== "number") return;
16932
17028
  (_this$_shellManager3 = this._shellManager) === null || _this$_shellManager3 === void 0 || _this$_shellManager3.resizeEdgeGroup(position, size);
16933
17029
  });
@@ -17605,9 +17701,12 @@ react_dom = __toESM(react_dom);
17605
17701
  }
17606
17702
  closeAllGroups() {
17607
17703
  this.mutation("remove", () => {
17608
- for (const entry of this._groups.entries()) {
17609
- const [_, group] = entry;
17610
- group.value.model.closeAllPanels();
17704
+ const groups = Array.from(this._groups.values()).map((_) => _.value);
17705
+ for (const group of groups) {
17706
+ if (!this._groups.has(group.id)) continue;
17707
+ group.model.closeAllPanels();
17708
+ const keepLast = this.options.noPanelsOverlay === "emptyGroup" && this._groups.size === 1;
17709
+ if (!this.closeEmptyGroups && !keepLast && this._groups.has(group.id) && group.model.size === 0) this.removeGroup(group, { skipActive: true });
17611
17710
  }
17612
17711
  });
17613
17712
  }
@@ -17732,7 +17831,7 @@ react_dom = __toESM(react_dom);
17732
17831
  this.mutation("remove", () => {
17733
17832
  var _options$removeEmptyG;
17734
17833
  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,
17834
+ removeEmptyGroup: (_options$removeEmptyG = options === null || options === void 0 ? void 0 : options.removeEmptyGroup) !== null && _options$removeEmptyG !== void 0 ? _options$removeEmptyG : this.closeEmptyGroups,
17736
17835
  skipDispose: options === null || options === void 0 ? void 0 : options.skipDispose,
17737
17836
  skipSetActiveGroup: options === null || options === void 0 ? void 0 : options.skipSetActiveGroup
17738
17837
  });
@@ -17888,7 +17987,7 @@ react_dom = __toESM(react_dom);
17888
17987
  if (!(options === null || options === void 0 ? void 0 : options.skipDispose)) {
17889
17988
  if (!(options === null || options === void 0 ? void 0 : options.skipPopoutAssociated)) {
17890
17989
  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);
17990
+ if ((refGroup === null || refGroup === void 0 ? void 0 : refGroup.panels.length) === 0 && (this.closeEmptyGroups || !refGroup.api.isVisible)) this.removeGroup(refGroup);
17892
17991
  }
17893
17992
  selectedGroup.popoutGroup.dispose();
17894
17993
  this._groups.delete(group.id);
@@ -18148,7 +18247,7 @@ react_dom = __toESM(react_dom);
18148
18247
  skipSetActiveGroup: true
18149
18248
  }));
18150
18249
  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 });
18250
+ if (!this.keepEmptyGroups(options) && sourceGroup.model.size === 0 && sourceGroup !== destinationGroup) this.doRemoveGroup(sourceGroup, { skipActive: true });
18152
18251
  const isDestinationGroupEmpty = destinationGroup.model.size === 0;
18153
18252
  this.movingLock(() => {
18154
18253
  var _options$skipSetActiv;
@@ -18168,10 +18267,12 @@ react_dom = __toESM(react_dom);
18168
18267
  const destinationGridview = this.getGridviewForGroup(destinationGroup);
18169
18268
  const referenceLocation = getGridLocation(destinationGroup.element);
18170
18269
  const targetLocation = getRelativeLocation(destinationGridview.orientation, referenceLocation, destinationTarget);
18171
- if (sourceGroup.size < 2) {
18270
+ if (sourceGroup.size < 2 && !this.keepEmptyGroups(options)) {
18172
18271
  /**
18173
18272
  * 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
18273
+ * moving the group itself rather than moving the panel into a newly created group.
18274
+ * When empty groups are kept the group must stay where it is, so the panel is
18275
+ * moved into a newly created group like any other panel.
18175
18276
  */
18176
18277
  const [targetParentLocation, to] = tail(targetLocation);
18177
18278
  if (sourceGroup.api.location.type === "grid" && destinationGridview === this.gridview) {
@@ -18296,7 +18397,7 @@ react_dom = __toESM(react_dom);
18296
18397
  const dropLocation = getRelativeLocation(this.gridview.orientation, referenceLocation, destinationTarget);
18297
18398
  targetGroup = this.createGroupAtLocation(dropLocation, this.dropSizing(referenceLocation));
18298
18399
  }
18299
- if (!options.keepEmptyGroups && sourceGroup.model.size === 0 && sourceGroup !== targetGroup) this.doRemoveGroup(sourceGroup, { skipActive: true });
18400
+ if (!this.keepEmptyGroups(options) && sourceGroup.model.size === 0 && sourceGroup !== targetGroup) this.doRemoveGroup(sourceGroup, { skipActive: true });
18300
18401
  addPanelsToGroup(targetGroup);
18301
18402
  }
18302
18403
  moveGroup(options) {
@@ -18623,13 +18724,12 @@ react_dom = __toESM(react_dom);
18623
18724
  return location.length % 2 == 1 ? rootOrientation : orthogonal(rootOrientation);
18624
18725
  }
18625
18726
  updateTheme() {
18626
- var _this$_options$theme, _this$_shellThemeClas, _theme$gap, _this$_popoutWindowSe25, _this$_popoutWindowSe26, _this$_shellManager5, _theme$edgeGroupColla, _theme$tabGroupIndica;
18727
+ var _this$_options$theme, _this$_shellThemeClas, _theme$gap, _this$_shellManager5, _theme$edgeGroupColla, _theme$tabGroupIndica;
18627
18728
  const theme = (_this$_options$theme = this._options.theme) !== null && _this$_options$theme !== void 0 ? _this$_options$theme : themeAbyss;
18628
18729
  (_this$_shellThemeClas = this._shellThemeClassnames) === null || _this$_shellThemeClas === void 0 || _this$_shellThemeClas.setClassNames(theme.className);
18629
18730
  const gap = (_theme$gap = theme.gap) !== null && _theme$gap !== void 0 ? _theme$gap : 0;
18630
18731
  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;
18732
+ for (const gridview of this.nestedGridviews()) gridview.margin = gap;
18633
18733
  (_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
18734
  if (theme.dndOverlayBorder === void 0) {
18635
18735
  var _this$_shellManager6;