dockview-core 6.0.5 → 6.0.7

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-core
3
- * @version 6.0.5
3
+ * @version 6.0.7
4
4
  * @link https://github.com/mathuo/dockview
5
5
  * @license MIT
6
6
  */
@@ -7199,10 +7199,12 @@ class Tabs extends CompositeDisposable {
7199
7199
  const index = this.indexOf(id);
7200
7200
  const tabToRemove = this._tabs.splice(index, 1)[0];
7201
7201
  this._tabMap.delete(id);
7202
- const { value, disposable } = tabToRemove;
7203
- disposable.dispose();
7204
- value.dispose();
7205
- value.element.remove();
7202
+ if (tabToRemove) {
7203
+ const { value, disposable } = tabToRemove;
7204
+ disposable.dispose();
7205
+ value.dispose();
7206
+ value.element.remove();
7207
+ }
7206
7208
  // If a non-source tab was removed during active drag, refresh positions
7207
7209
  if (this._animState) {
7208
7210
  this._animState.tabPositions = this.snapshotTabPositions();
@@ -7324,8 +7326,23 @@ class Tabs extends CompositeDisposable {
7324
7326
  new PanelTransfer(this.accessor.id, this.group.id, null, tabGroup.id),
7325
7327
  ], PanelTransfer.prototype);
7326
7328
  const iframes = disableIframePointEvents();
7329
+ // The dragend listener on `_tabsList` is unreachable for chip
7330
+ // drags because cross-group drops detach the chip from the DOM
7331
+ // before dragend fires (the source tab group becomes empty, so
7332
+ // `_positionChipForGroup` removes the chip element). Without
7333
+ // bubbling, the tabsList listener never runs and `_animState`,
7334
+ // `_chipDragCleanup`, and the dragging CSS classes leak. Listen
7335
+ // directly on the chip element so cleanup happens regardless of
7336
+ // whether it's still attached. (Issue #1254.)
7337
+ const chipElement = chip.element;
7338
+ const onChipDragEnd = () => {
7339
+ chipElement.removeEventListener('dragend', onChipDragEnd);
7340
+ this.resetDragAnimation();
7341
+ };
7342
+ chipElement.addEventListener('dragend', onChipDragEnd);
7327
7343
  this._chipDragCleanup = {
7328
7344
  dispose: () => {
7345
+ chipElement.removeEventListener('dragend', onChipDragEnd);
7329
7346
  panelTransfer.clearData(PanelTransfer.prototype);
7330
7347
  iframes.release();
7331
7348
  },
@@ -7874,6 +7891,14 @@ class Tabs extends CompositeDisposable {
7874
7891
  // handles panel transfer and tab group recreation.
7875
7892
  // Use the REAL tab group ID from transfer data, not the
7876
7893
  // potentially stale one from _animState.
7894
+ //
7895
+ // Clear any inline gap margin / shifting class applied to
7896
+ // destination tabs during dragover. Cross-group moves don't
7897
+ // run the FLIP path, and `moveGroupOrPanel` only inserts new
7898
+ // panels — it doesn't recreate existing destination tabs, so
7899
+ // their inline `margin-left` would otherwise persist as a
7900
+ // visible gap (issue #1243).
7901
+ this.resetTabTransforms();
7877
7902
  this.accessor.moveGroupOrPanel({
7878
7903
  from: {
7879
7904
  groupId: data.groupId,
@@ -9654,15 +9679,17 @@ class DockviewGroupPanelModel extends CompositeDisposable {
9654
9679
  if (position === 'center') {
9655
9680
  return;
9656
9681
  }
9657
- if (data.panelId === null) {
9658
- // don't allow group move to drop anywhere on self
9682
+ if (data.panelId === null && !data.tabGroupId) {
9683
+ // Full-group drops on self are a no-op.
9684
+ // Tab-group drags are partial moves: an edge drop
9685
+ // splits the layout and creates a new group.
9659
9686
  return;
9660
9687
  }
9661
9688
  }
9662
9689
  }
9663
9690
  if (type === 'header') {
9664
9691
  if (data.groupId === this.id) {
9665
- if (data.panelId === null) {
9692
+ if (data.panelId === null && !data.tabGroupId) {
9666
9693
  return;
9667
9694
  }
9668
9695
  }
@@ -13530,7 +13557,7 @@ class DockviewComponent extends BaseGrid {
13530
13557
  // Collapse when the group becomes empty
13531
13558
  const autoCollapseDisposable = group.model.onDidRemovePanel(() => {
13532
13559
  if (group.model.isEmpty) {
13533
- this._shellManager.setEdgeGroupCollapsed(position, true);
13560
+ this.setEdgeGroupCollapsed(group, true);
13534
13561
  }
13535
13562
  });
13536
13563
  this._edgeGroupDisposables.set(position, autoCollapseDisposable);
@@ -13574,6 +13601,13 @@ class DockviewComponent extends BaseGrid {
13574
13601
  setEdgeGroupCollapsed(group, collapsed) {
13575
13602
  for (const [position, edgeGroup] of this._edgeGroups) {
13576
13603
  if (edgeGroup === group) {
13604
+ if (this._shellManager.isEdgeGroupCollapsed(position) ===
13605
+ collapsed) {
13606
+ // Skip the splitview resize on a no-op: with non-zero
13607
+ // theme gap, redundant resizeView calls accumulate
13608
+ // rounding drift that gradually shrinks the group.
13609
+ return;
13610
+ }
13577
13611
  this._shellManager.setEdgeGroupCollapsed(position, collapsed);
13578
13612
  edgeGroup.api._onDidCollapsedChange.fire({
13579
13613
  isCollapsed: collapsed,
@@ -14526,7 +14560,14 @@ class DockviewComponent extends BaseGrid {
14526
14560
  const label = tabGroup.label;
14527
14561
  const color = tabGroup.color;
14528
14562
  const collapsed = tabGroup.collapsed;
14563
+ const componentParams = tabGroup.componentParams;
14529
14564
  const panelIds = [...tabGroup.panelIds];
14565
+ // Capture the destination's grid location BEFORE potentially
14566
+ // removing the source group, in case source === destination and
14567
+ // the source becomes empty after panel removal.
14568
+ const referenceLocation = destinationTarget && destinationTarget !== 'center'
14569
+ ? getGridLocation(destinationGroup.element)
14570
+ : undefined;
14530
14571
  // Remove panels from the source group
14531
14572
  const removedPanels = this.movingLock(() => panelIds
14532
14573
  .map((pid) => sourceGroup.model.removePanel(pid, {
@@ -14537,11 +14578,6 @@ class DockviewComponent extends BaseGrid {
14537
14578
  if (removedPanels.length === 0) {
14538
14579
  return;
14539
14580
  }
14540
- if (!options.keepEmptyGroups &&
14541
- sourceGroup.model.size === 0 &&
14542
- sourceGroup !== destinationGroup) {
14543
- this.doRemoveGroup(sourceGroup, { skipActive: true });
14544
- }
14545
14581
  const addPanelsToGroup = (targetGroup) => {
14546
14582
  this.movingLock(() => {
14547
14583
  for (const panel of removedPanels) {
@@ -14557,6 +14593,7 @@ class DockviewComponent extends BaseGrid {
14557
14593
  label,
14558
14594
  color,
14559
14595
  collapsed,
14596
+ componentParams,
14560
14597
  });
14561
14598
  for (const panel of removedPanels) {
14562
14599
  targetGroup.model.addPanelToTabGroup(newTabGroup.id, panel.id);
@@ -14571,15 +14608,27 @@ class DockviewComponent extends BaseGrid {
14571
14608
  });
14572
14609
  }
14573
14610
  };
14574
- if (!destinationTarget || destinationTarget === 'center') {
14575
- addPanelsToGroup(destinationGroup);
14611
+ let targetGroup;
14612
+ if (!destinationTarget ||
14613
+ destinationTarget === 'center' ||
14614
+ !referenceLocation) {
14615
+ targetGroup = destinationGroup;
14576
14616
  }
14577
14617
  else {
14578
- const referenceLocation = getGridLocation(destinationGroup.element);
14579
14618
  const dropLocation = getRelativeLocation(this.gridview.orientation, referenceLocation, destinationTarget);
14580
- const newGroup = this.createGroupAtLocation(dropLocation);
14581
- addPanelsToGroup(newGroup);
14619
+ targetGroup = this.createGroupAtLocation(dropLocation);
14582
14620
  }
14621
+ // Remove the source group if it became empty. We compare against
14622
+ // the actual targetGroup (which is a freshly-created group for
14623
+ // edge drops) rather than the originally-passed destinationGroup,
14624
+ // so a tab-group drag onto its own group's edge still cleans up
14625
+ // the now-empty source.
14626
+ if (!options.keepEmptyGroups &&
14627
+ sourceGroup.model.size === 0 &&
14628
+ sourceGroup !== targetGroup) {
14629
+ this.doRemoveGroup(sourceGroup, { skipActive: true });
14630
+ }
14631
+ addPanelsToGroup(targetGroup);
14583
14632
  }
14584
14633
  moveGroup(options) {
14585
14634
  const from = options.from.group;
@@ -14591,6 +14640,16 @@ class DockviewComponent extends BaseGrid {
14591
14640
  let source = from;
14592
14641
  if (target === 'center') {
14593
14642
  const activePanel = from.activePanel;
14643
+ // Snapshot tab group metadata before removing panels so we
14644
+ // can recreate the tab groups in the destination after the
14645
+ // panels are merged in.
14646
+ const tabGroupSnapshots = from.model.getTabGroups().map((tg) => ({
14647
+ label: tg.label,
14648
+ color: tg.color,
14649
+ collapsed: tg.collapsed,
14650
+ componentParams: tg.componentParams,
14651
+ panelIds: [...tg.panelIds],
14652
+ }));
14594
14653
  const panels = this.movingLock(() => [...from.panels].map((p) => from.model.removePanel(p.id, {
14595
14654
  skipSetActive: true,
14596
14655
  })));
@@ -14605,6 +14664,17 @@ class DockviewComponent extends BaseGrid {
14605
14664
  });
14606
14665
  }
14607
14666
  });
14667
+ for (const snapshot of tabGroupSnapshots) {
14668
+ const newTabGroup = to.model.createTabGroup({
14669
+ label: snapshot.label,
14670
+ color: snapshot.color,
14671
+ collapsed: snapshot.collapsed,
14672
+ componentParams: snapshot.componentParams,
14673
+ });
14674
+ for (const panelId of snapshot.panelIds) {
14675
+ to.model.addPanelToTabGroup(newTabGroup.id, panelId);
14676
+ }
14677
+ }
14608
14678
  // Ensure group becomes active after move
14609
14679
  if (options.skipSetActive !== true) {
14610
14680
  // For center moves (merges), we need to ensure the target group is active
@@ -14628,6 +14698,17 @@ class DockviewComponent extends BaseGrid {
14628
14698
  * positions `source` like any other moved group.
14629
14699
  */
14630
14700
  const activePanel = from.activePanel;
14701
+ // Snapshot tab group metadata so the new group inherits
14702
+ // the tab grouping from the edge slot.
14703
+ const tabGroupSnapshots = from.model
14704
+ .getTabGroups()
14705
+ .map((tg) => ({
14706
+ label: tg.label,
14707
+ color: tg.color,
14708
+ collapsed: tg.collapsed,
14709
+ componentParams: tg.componentParams,
14710
+ panelIds: [...tg.panelIds],
14711
+ }));
14631
14712
  const movedPanels = this.movingLock(() => [...from.panels].map((p) => from.model.removePanel(p.id, { skipSetActive: true })));
14632
14713
  source = this.createGroup();
14633
14714
  this.movingLock(() => {
@@ -14638,6 +14719,17 @@ class DockviewComponent extends BaseGrid {
14638
14719
  });
14639
14720
  }
14640
14721
  });
14722
+ for (const snapshot of tabGroupSnapshots) {
14723
+ const newTabGroup = source.model.createTabGroup({
14724
+ label: snapshot.label,
14725
+ color: snapshot.color,
14726
+ collapsed: snapshot.collapsed,
14727
+ componentParams: snapshot.componentParams,
14728
+ });
14729
+ for (const panelId of snapshot.panelIds) {
14730
+ source.model.addPanelToTabGroup(newTabGroup.id, panelId);
14731
+ }
14732
+ }
14641
14733
  }
14642
14734
  else {
14643
14735
  switch (from.api.location.type) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dockview-core",
3
- "version": "6.0.5",
3
+ "version": "6.0.7",
4
4
  "description": "Zero dependency layout manager supporting tabs, groups, grids and splitviews for vanilla TypeScript",
5
5
  "keywords": [
6
6
  "splitview",