dockview-core 5.2.0 → 6.0.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.
Files changed (117) hide show
  1. package/README.md +3 -1
  2. package/dist/cjs/api/component.api.d.ts +93 -1
  3. package/dist/cjs/api/component.api.js +146 -0
  4. package/dist/cjs/api/dockviewGroupPanelApi.d.ts +26 -0
  5. package/dist/cjs/api/dockviewGroupPanelApi.js +21 -1
  6. package/dist/cjs/api/entryPoints.js +4 -5
  7. package/dist/cjs/array.js +7 -8
  8. package/dist/cjs/dnd/dataTransfer.d.ts +2 -1
  9. package/dist/cjs/dnd/dataTransfer.js +5 -4
  10. package/dist/cjs/dnd/droptarget.d.ts +12 -0
  11. package/dist/cjs/dnd/droptarget.js +38 -10
  12. package/dist/cjs/dnd/ghost.js +1 -2
  13. package/dist/cjs/dockview/components/panel/content.js +5 -1
  14. package/dist/cjs/dockview/components/popupService.d.ts +9 -2
  15. package/dist/cjs/dockview/components/popupService.js +24 -9
  16. package/dist/cjs/dockview/components/tab/tab.d.ts +6 -1
  17. package/dist/cjs/dockview/components/tab/tab.js +81 -9
  18. package/dist/cjs/dockview/components/titlebar/tabGroupChip.d.ts +30 -0
  19. package/dist/cjs/dockview/components/titlebar/tabGroupChip.js +95 -0
  20. package/dist/cjs/dockview/components/titlebar/tabGroupIndicator.d.ts +71 -0
  21. package/dist/cjs/dockview/components/titlebar/tabGroupIndicator.js +471 -0
  22. package/dist/cjs/dockview/components/titlebar/tabGroups.d.ts +57 -0
  23. package/dist/cjs/dockview/components/titlebar/tabGroups.js +612 -0
  24. package/dist/cjs/dockview/components/titlebar/tabOverflowControl.js +1 -2
  25. package/dist/cjs/dockview/components/titlebar/tabs.d.ts +59 -0
  26. package/dist/cjs/dockview/components/titlebar/tabs.js +1227 -144
  27. package/dist/cjs/dockview/components/titlebar/tabsContainer.d.ts +6 -0
  28. package/dist/cjs/dockview/components/titlebar/tabsContainer.js +132 -14
  29. package/dist/cjs/dockview/contextMenu.d.ts +10 -0
  30. package/dist/cjs/dockview/contextMenu.js +298 -0
  31. package/dist/cjs/dockview/dockviewComponent.d.ts +60 -3
  32. package/dist/cjs/dockview/dockviewComponent.js +712 -126
  33. package/dist/cjs/dockview/dockviewGroupPanelModel.d.ts +83 -0
  34. package/dist/cjs/dockview/dockviewGroupPanelModel.js +619 -27
  35. package/dist/cjs/dockview/dockviewShell.d.ts +128 -0
  36. package/dist/cjs/dockview/dockviewShell.js +681 -0
  37. package/dist/cjs/dockview/events.d.ts +9 -0
  38. package/dist/cjs/dockview/framework.d.ts +14 -0
  39. package/dist/cjs/dockview/options.d.ts +92 -10
  40. package/dist/cjs/dockview/options.js +10 -7
  41. package/dist/cjs/dockview/tabGroup.d.ts +99 -0
  42. package/dist/cjs/dockview/tabGroup.js +219 -0
  43. package/dist/cjs/dockview/tabGroupAccent.d.ts +65 -0
  44. package/dist/cjs/dockview/tabGroupAccent.js +128 -0
  45. package/dist/cjs/dockview/theme.d.ts +56 -1
  46. package/dist/cjs/dockview/theme.js +103 -6
  47. package/dist/cjs/dockview/types.d.ts +2 -0
  48. package/dist/cjs/dom.js +19 -19
  49. package/dist/cjs/events.js +2 -2
  50. package/dist/cjs/gridview/baseComponentGridview.d.ts +1 -0
  51. package/dist/cjs/gridview/baseComponentGridview.js +6 -3
  52. package/dist/cjs/gridview/gridview.js +7 -7
  53. package/dist/cjs/index.d.ts +8 -5
  54. package/dist/cjs/index.js +6 -1
  55. package/dist/cjs/popoutWindow.js +3 -3
  56. package/dist/cjs/splitview/splitviewPanel.d.ts +1 -1
  57. package/dist/dockview-core.js +6942 -2777
  58. package/dist/dockview-core.min.js +2 -2
  59. package/dist/dockview-core.min.js.map +1 -1
  60. package/dist/dockview-core.min.noStyle.js +2 -2
  61. package/dist/dockview-core.min.noStyle.js.map +1 -1
  62. package/dist/dockview-core.noStyle.js +6940 -2775
  63. package/dist/esm/api/component.api.d.ts +93 -1
  64. package/dist/esm/api/component.api.js +118 -0
  65. package/dist/esm/api/dockviewGroupPanelApi.d.ts +26 -0
  66. package/dist/esm/api/dockviewGroupPanelApi.js +21 -1
  67. package/dist/esm/dnd/dataTransfer.d.ts +2 -1
  68. package/dist/esm/dnd/dataTransfer.js +2 -1
  69. package/dist/esm/dnd/droptarget.d.ts +12 -0
  70. package/dist/esm/dnd/droptarget.js +33 -5
  71. package/dist/esm/dockview/components/panel/content.js +5 -1
  72. package/dist/esm/dockview/components/popupService.d.ts +9 -2
  73. package/dist/esm/dockview/components/popupService.js +23 -9
  74. package/dist/esm/dockview/components/tab/tab.d.ts +6 -1
  75. package/dist/esm/dockview/components/tab/tab.js +83 -9
  76. package/dist/esm/dockview/components/titlebar/tabGroupChip.d.ts +30 -0
  77. package/dist/esm/dockview/components/titlebar/tabGroupChip.js +68 -0
  78. package/dist/esm/dockview/components/titlebar/tabGroupIndicator.d.ts +71 -0
  79. package/dist/esm/dockview/components/titlebar/tabGroupIndicator.js +354 -0
  80. package/dist/esm/dockview/components/titlebar/tabGroups.d.ts +57 -0
  81. package/dist/esm/dockview/components/titlebar/tabGroups.js +406 -0
  82. package/dist/esm/dockview/components/titlebar/tabs.d.ts +59 -0
  83. package/dist/esm/dockview/components/titlebar/tabs.js +1011 -99
  84. package/dist/esm/dockview/components/titlebar/tabsContainer.d.ts +6 -0
  85. package/dist/esm/dockview/components/titlebar/tabsContainer.js +105 -7
  86. package/dist/esm/dockview/contextMenu.d.ts +10 -0
  87. package/dist/esm/dockview/contextMenu.js +213 -0
  88. package/dist/esm/dockview/dockviewComponent.d.ts +60 -3
  89. package/dist/esm/dockview/dockviewComponent.js +460 -35
  90. package/dist/esm/dockview/dockviewGroupPanelModel.d.ts +83 -0
  91. package/dist/esm/dockview/dockviewGroupPanelModel.js +460 -4
  92. package/dist/esm/dockview/dockviewShell.d.ts +128 -0
  93. package/dist/esm/dockview/dockviewShell.js +621 -0
  94. package/dist/esm/dockview/events.d.ts +9 -0
  95. package/dist/esm/dockview/framework.d.ts +14 -0
  96. package/dist/esm/dockview/options.d.ts +92 -10
  97. package/dist/esm/dockview/options.js +5 -2
  98. package/dist/esm/dockview/tabGroup.d.ts +99 -0
  99. package/dist/esm/dockview/tabGroup.js +144 -0
  100. package/dist/esm/dockview/tabGroupAccent.d.ts +65 -0
  101. package/dist/esm/dockview/tabGroupAccent.js +116 -0
  102. package/dist/esm/dockview/theme.d.ts +56 -1
  103. package/dist/esm/dockview/theme.js +102 -5
  104. package/dist/esm/dockview/types.d.ts +2 -0
  105. package/dist/esm/dom.js +1 -1
  106. package/dist/esm/gridview/baseComponentGridview.d.ts +1 -0
  107. package/dist/esm/gridview/baseComponentGridview.js +4 -1
  108. package/dist/esm/index.d.ts +8 -5
  109. package/dist/esm/index.js +2 -1
  110. package/dist/esm/popoutWindow.js +1 -1
  111. package/dist/esm/splitview/splitviewPanel.d.ts +1 -1
  112. package/dist/package/main.cjs.js +6936 -2801
  113. package/dist/package/main.cjs.min.js +2 -2
  114. package/dist/package/main.esm.min.mjs +2 -2
  115. package/dist/package/main.esm.mjs +6922 -2800
  116. package/dist/styles/dockview.css +1945 -196
  117. package/package.json +5 -1
@@ -59,6 +59,7 @@ var lifecycle_1 = require("../../../lifecycle");
59
59
  var scrollbar_1 = require("../../../scrollbar");
60
60
  var events_2 = require("../../events");
61
61
  var tab_1 = require("../tab/tab");
62
+ var tabGroups_1 = require("./tabGroups");
62
63
  var Tabs = /** @class */ (function (_super) {
63
64
  __extends(Tabs, _super);
64
65
  function Tabs(group, accessor, options) {
@@ -68,10 +69,18 @@ var Tabs = /** @class */ (function (_super) {
68
69
  _this._observerDisposable = new lifecycle_1.MutableDisposable();
69
70
  _this._scrollbar = null;
70
71
  _this._tabs = [];
72
+ _this._tabMap = new Map();
71
73
  _this.selectedIndex = -1;
72
74
  _this._showTabsOverflowControl = false;
73
75
  _this._direction = 'horizontal';
74
76
  _this._animState = null;
77
+ _this._pendingMarginCleanups = new Map();
78
+ _this._pendingCollapse = false;
79
+ _this._flipTransitionCleanup = null;
80
+ _this._voidContainer = null;
81
+ _this._voidContainerListeners = null;
82
+ _this._extendedDropZone = null;
83
+ _this._chipDragCleanup = null;
75
84
  _this._onTabDragStart = new events_1.Emitter();
76
85
  _this.onTabDragStart = _this._onTabDragStart.event;
77
86
  _this._onDrop = new events_1.Emitter();
@@ -92,7 +101,27 @@ var Tabs = /** @class */ (function (_super) {
92
101
  _this._element = _this._scrollbar.element;
93
102
  _this.addDisposables(_this._scrollbar);
94
103
  }
95
- _this.addDisposables(_this._onOverflowTabsChange, _this._observerDisposable, _this._onWillShowOverlay, _this._onDrop, _this._onTabDragStart, (0, events_1.addDisposableListener)(_this.element, 'pointerdown', function (event) {
104
+ _this._tabGroupManager = new tabGroups_1.TabGroupManager({
105
+ group: _this.group,
106
+ accessor: _this.accessor,
107
+ tabsList: _this._tabsList,
108
+ getTabs: function () { return _this._tabs; },
109
+ getTabMap: function () { return _this._tabMap; },
110
+ getDirection: function () { return _this._direction; },
111
+ }, {
112
+ onChipContextMenu: function (tabGroup, event) {
113
+ _this.accessor.contextMenuController.showForChip(tabGroup, _this.group, event);
114
+ },
115
+ onChipDragStart: function (tabGroup, chip, event) {
116
+ _this._handleChipDragStart(tabGroup, chip, event);
117
+ },
118
+ });
119
+ _this.addDisposables(_this._onOverflowTabsChange, _this._observerDisposable, _this._onWillShowOverlay, _this._onDrop, _this._onTabDragStart, {
120
+ dispose: function () {
121
+ var _a;
122
+ (_a = _this._flipTransitionCleanup) === null || _a === void 0 ? void 0 : _a.call(_this);
123
+ },
124
+ }, (0, events_1.addDisposableListener)(_this.element, 'pointerdown', function (event) {
96
125
  if (event.defaultPrevented) {
97
126
  return;
98
127
  }
@@ -101,42 +130,128 @@ var Tabs = /** @class */ (function (_super) {
101
130
  _this.accessor.doSetGroupActive(_this.group);
102
131
  }
103
132
  }), (0, events_1.addDisposableListener)(_this._tabsList, 'dragover', function (event) {
133
+ var _a, _b, _c, _d;
134
+ if (_this.accessor.options.disableDnd) {
135
+ return;
136
+ }
137
+ // If _animState exists but belongs to a different
138
+ // drag (stale from a previous operation), replace it
139
+ // so the current drag is handled correctly.
140
+ if (_this._animState) {
141
+ var data = (0, dataTransfer_1.getPanelData)();
142
+ if ((data === null || data === void 0 ? void 0 : data.tabGroupId) &&
143
+ data.groupId !== _this.group.id &&
144
+ _this._animState.sourceTabGroupId !== data.tabGroupId) {
145
+ _this._animState = null;
146
+ }
147
+ }
104
148
  if (!_this._animState) {
105
- // Check for external drag from another group
106
- if (_this.accessor.options.tabAnimation !== 'smooth' ||
107
- _this.accessor.options.disableDnd) {
149
+ var data_1 = (0, dataTransfer_1.getPanelData)();
150
+ // In default animation mode, individual tab drops
151
+ // are handled by per-tab Droptargets. But tab group
152
+ // chip drags still need tab-list-level handling so
153
+ // that drops on gaps / void space work.
154
+ if (((_a = _this.accessor.options.theme) === null || _a === void 0 ? void 0 : _a.tabAnimation) ===
155
+ 'default' &&
156
+ !(data_1 === null || data_1 === void 0 ? void 0 : data_1.tabGroupId)) {
108
157
  return;
109
158
  }
110
- var data = (0, dataTransfer_1.getPanelData)();
111
- if (data &&
112
- data.panelId &&
113
- data.groupId !== _this.group.id) {
114
- _this._animState = {
115
- sourceTabId: data.panelId,
116
- sourceIndex: -1,
117
- tabPositions: _this.snapshotTabPositions(),
118
- currentInsertionIndex: null,
119
- };
159
+ if (data_1 &&
160
+ (data_1.panelId || data_1.tabGroupId) &&
161
+ data_1.groupId !== _this.group.id) {
162
+ var avgWidth = _this.getAverageTabWidth();
163
+ if (data_1.tabGroupId) {
164
+ // External group drag — look up the
165
+ // source tab group to size the gap
166
+ var sourceGroup = _this.accessor.getPanel(data_1.groupId);
167
+ var sourceTg = sourceGroup === null || sourceGroup === void 0 ? void 0 : sourceGroup.model.getTabGroups().find(function (tg) { return tg.id === data_1.tabGroupId; });
168
+ var panelCount = (_b = sourceTg === null || sourceTg === void 0 ? void 0 : sourceTg.panelIds.length) !== null && _b !== void 0 ? _b : 1;
169
+ var groupGapWidth = avgWidth * panelCount + avgWidth;
170
+ _this._animState = {
171
+ sourceTabId: '',
172
+ sourceIndex: -1,
173
+ tabPositions: _this.snapshotTabPositions(),
174
+ chipPositions: _this._tabGroupManager.snapshotChipWidths(),
175
+ currentInsertionIndex: null,
176
+ targetTabGroupId: null,
177
+ sourceTabGroupId: data_1.tabGroupId,
178
+ sourceGroupPanelIds: sourceTg
179
+ ? new Set(sourceTg.panelIds)
180
+ : new Set(),
181
+ sourceChipWidth: avgWidth,
182
+ cursorOffsetFromDragLeft: groupGapWidth / 2,
183
+ sourceGapWidth: groupGapWidth,
184
+ containerLeft: _this._tabsList.getBoundingClientRect()
185
+ .left,
186
+ };
187
+ }
188
+ else {
189
+ _this._animState = {
190
+ sourceTabId: data_1.panelId,
191
+ sourceIndex: -1,
192
+ tabPositions: _this.snapshotTabPositions(),
193
+ chipPositions: _this._tabGroupManager.snapshotChipWidths(),
194
+ currentInsertionIndex: null,
195
+ targetTabGroupId: null,
196
+ sourceTabGroupId: null,
197
+ sourceGroupPanelIds: null,
198
+ sourceChipWidth: 0,
199
+ cursorOffsetFromDragLeft: avgWidth / 2,
200
+ sourceGapWidth: avgWidth,
201
+ containerLeft: _this._tabsList.getBoundingClientRect()
202
+ .left,
203
+ };
204
+ }
120
205
  }
121
206
  else {
122
207
  return;
123
208
  }
124
209
  }
210
+ event.preventDefault(); // allow drop to fire on the container
211
+ // For intra-group drag (sourceIndex >= 0) the gap
212
+ // animation is the sole visual indicator — clear any
213
+ // stale anchor overlay that may have been set while the
214
+ // cursor was over the panel content area or another zone.
215
+ // External drags (sourceIndex === -1) leave the overlay
216
+ // to the individual tab Droptargets so cross-group
217
+ // animation is not disrupted.
218
+ if (_this._animState.sourceIndex !== -1) {
219
+ (_d = (_c = _this.group.model.dropTargetContainer) === null || _c === void 0 ? void 0 : _c.model) === null || _d === void 0 ? void 0 : _d.clear();
220
+ }
125
221
  _this.handleDragOver(event);
126
222
  }, true), (0, events_1.addDisposableListener)(_this._tabsList, 'dragleave', function (event) {
223
+ var _a, _b, _c;
127
224
  if (!_this._animState) {
128
225
  return;
129
226
  }
130
- // Only handle if leaving the container itself, not moving between children
131
- if (event.relatedTarget &&
132
- _this._tabsList.contains(event.relatedTarget)) {
227
+ var related = event.relatedTarget;
228
+ // Ignore moves between children of the tabs list
229
+ if (related && _this._tabsList.contains(related)) {
230
+ return;
231
+ }
232
+ // If moving into the broader drop zone (e.g. void container,
233
+ // left actions), keep _animState alive so the external
234
+ // dragover listeners can continue the gap animation.
235
+ if (related && ((_a = _this._extendedDropZone) === null || _a === void 0 ? void 0 : _a.contains(related))) {
236
+ _this.resetTabTransforms();
237
+ _this._animState.currentInsertionIndex = null;
238
+ return;
239
+ }
240
+ // When leaving toward the void container (empty header space
241
+ // to the right), keep the animation state so the drop can
242
+ // still land at the end position.
243
+ var rt = event.relatedTarget;
244
+ var isVoid = _this._voidContainer &&
245
+ rt &&
246
+ (rt === _this._voidContainer ||
247
+ _this._voidContainer.contains(rt));
248
+ if (isVoid) {
133
249
  return;
134
250
  }
135
251
  _this.resetTabTransforms();
136
252
  if (_this._animState) {
137
253
  if (_this._animState.sourceIndex === -1) {
138
- // External drag left clear state entirely
139
- // (no dragend will fire on this tab list)
254
+ (_c = (_b = _this.group.model.dropTargetContainer) === null || _b === void 0 ? void 0 : _b.model) === null || _c === void 0 ? void 0 : _c.clear();
140
255
  _this._animState = null;
141
256
  }
142
257
  else {
@@ -144,48 +259,70 @@ var Tabs = /** @class */ (function (_super) {
144
259
  }
145
260
  }
146
261
  }, true), (0, events_1.addDisposableListener)(_this._tabsList, 'dragend', function () {
147
- // Only fires for cancel (not after successful drop, since
148
- // source tab is removed from DOM and doesn't bubble)
149
262
  _this.resetDragAnimation();
150
263
  }), (0, events_1.addDisposableListener)(_this._tabsList, 'drop', function (event) {
151
- if (_this.accessor.options.tabAnimation !== 'smooth' ||
152
- !_this._animState ||
264
+ var _a, _b, _c;
265
+ if (!_this._animState ||
153
266
  _this._animState.currentInsertionIndex === null) {
154
267
  return;
155
268
  }
269
+ // In non-smooth mode only handle group drags here;
270
+ // individual tab drops are handled by tab Droptargets.
271
+ if (((_a = _this.accessor.options.theme) === null || _a === void 0 ? void 0 : _a.tabAnimation) !==
272
+ 'smooth' &&
273
+ !_this._animState.sourceTabGroupId) {
274
+ return;
275
+ }
156
276
  event.stopPropagation();
157
277
  event.preventDefault();
278
+ // The capturing stopPropagation above prevents the
279
+ // individual tab's Droptarget.onDrop from firing, so
280
+ // the anchor overlay won't be cleared by that path.
281
+ // Clear it explicitly here before processing the drop.
282
+ (_c = (_b = _this.group.model.dropTargetContainer) === null || _b === void 0 ? void 0 : _b.model) === null || _c === void 0 ? void 0 : _c.clear();
158
283
  var animState = _this._animState;
159
284
  _this._animState = null;
285
+ _this._pendingCollapse = false;
286
+ // Handle group drag (entire group repositioned)
287
+ if (animState.sourceTabGroupId) {
288
+ _this._commitGroupMove(animState.sourceTabGroupId, animState.currentInsertionIndex);
289
+ return;
290
+ }
160
291
  var insertionIndex = animState.currentInsertionIndex;
161
292
  var sourceIndex = animState.sourceIndex;
162
- // After the source tab is removed, indices after it shift
163
- // down by one, so adjust the target index accordingly.
164
293
  var adjustedIndex = insertionIndex -
165
294
  (sourceIndex !== -1 && sourceIndex < insertionIndex
166
295
  ? 1
167
296
  : 0);
168
- // No-op: drop at the same position, nothing to animate
169
- if (adjustedIndex === sourceIndex) {
297
+ var sourceCurrentGroup = _this.group.model.getTabGroupForPanel(animState.sourceTabId);
298
+ if (adjustedIndex === sourceIndex &&
299
+ !animState.targetTabGroupId &&
300
+ !sourceCurrentGroup) {
301
+ _this._uncollapsSourceTab(animState.sourceTabId);
170
302
  _this.resetTabTransforms();
171
303
  return;
172
304
  }
173
- // Snapshot current visual positions (with margins still applied)
174
- // before resetting transforms, so FLIP starts from what the
175
- // user currently sees — not from a teleported state.
305
+ _this._uncollapsSourceTab(animState.sourceTabId);
176
306
  var firstPositions = _this.snapshotTabPositions();
177
307
  _this.resetTabTransforms();
178
- _this._onDrop.fire({ event: event, index: adjustedIndex });
308
+ _this._onDrop.fire({
309
+ event: event,
310
+ index: adjustedIndex,
311
+ targetTabGroupId: animState.targetTabGroupId,
312
+ });
179
313
  _this.runFlipAnimation(firstPositions, animState.sourceTabId, animState.sourceIndex === -1, {
180
314
  from: Math.min(sourceIndex, adjustedIndex),
181
315
  to: Math.max(sourceIndex, adjustedIndex),
182
316
  });
183
317
  }, true), lifecycle_1.Disposable.from(function () {
184
318
  var e_1, _a;
319
+ var _b;
320
+ (_b = _this._voidContainerListeners) === null || _b === void 0 ? void 0 : _b.dispose();
185
321
  _this.resetDragAnimation();
322
+ _this._tabGroupManager.disposeAll();
186
323
  try {
187
- for (var _b = __values(_this._tabs), _c = _b.next(); !_c.done; _c = _b.next()) {
188
- var _d = _c.value, value = _d.value, disposable = _d.disposable;
324
+ for (var _c = __values(_this._tabs), _d = _c.next(); !_d.done; _d = _c.next()) {
325
+ var _e = _d.value, value = _e.value, disposable = _e.disposable;
189
326
  disposable.dispose();
190
327
  value.dispose();
191
328
  }
@@ -193,11 +330,12 @@ var Tabs = /** @class */ (function (_super) {
193
330
  catch (e_1_1) { e_1 = { error: e_1_1 }; }
194
331
  finally {
195
332
  try {
196
- if (_c && !_c.done && (_a = _b.return)) _a.call(_b);
333
+ if (_d && !_d.done && (_a = _c.return)) _a.call(_c);
197
334
  }
198
335
  finally { if (e_1) throw e_1.error; }
199
336
  }
200
337
  _this._tabs = [];
338
+ _this._tabMap.clear();
201
339
  }));
202
340
  return _this;
203
341
  }
@@ -216,8 +354,14 @@ var Tabs = /** @class */ (function (_super) {
216
354
  this._observerDisposable.value = new lifecycle_1.CompositeDisposable(observer, observer.onDidChange(function (event) {
217
355
  var hasOverflow = event.hasScrollX || event.hasScrollY;
218
356
  _this.toggleDropdown({ reset: !hasOverflow });
357
+ if (_this._tabGroupManager.groupUnderlines.size > 0) {
358
+ _this._tabGroupManager.positionUnderlines();
359
+ }
219
360
  }), (0, events_1.addDisposableListener)(this._tabsList, 'scroll', function () {
220
361
  _this.toggleDropdown({ reset: false });
362
+ if (_this._tabGroupManager.groupUnderlines.size > 0) {
363
+ _this._tabGroupManager.positionUnderlines();
364
+ }
221
365
  }));
222
366
  }
223
367
  },
@@ -231,6 +375,48 @@ var Tabs = /** @class */ (function (_super) {
231
375
  enumerable: false,
232
376
  configurable: true
233
377
  });
378
+ Object.defineProperty(Tabs.prototype, "voidContainer", {
379
+ set: function (el) {
380
+ var _this = this;
381
+ var _a;
382
+ (_a = this._voidContainerListeners) === null || _a === void 0 ? void 0 : _a.dispose();
383
+ this._voidContainerListeners = null;
384
+ this._voidContainer = el;
385
+ if (el) {
386
+ this._voidContainerListeners = new lifecycle_1.CompositeDisposable((0, events_1.addDisposableListener)(el, 'dragover', function (event) {
387
+ if (_this._animState) {
388
+ event.preventDefault();
389
+ }
390
+ }), (0, events_1.addDisposableListener)(el, 'drop', function (event) {
391
+ var _a;
392
+ if (((_a = _this._animState) === null || _a === void 0 ? void 0 : _a.sourceTabGroupId) &&
393
+ _this._animState.currentInsertionIndex !== null) {
394
+ event.preventDefault();
395
+ event.stopPropagation();
396
+ _this.handleVoidDrop();
397
+ }
398
+ }));
399
+ }
400
+ },
401
+ enumerable: false,
402
+ configurable: true
403
+ });
404
+ /**
405
+ * Handle a drop that occurred on the void container (empty header
406
+ * space to the right of the tabs). Returns `true` if the drop was
407
+ * consumed by an active group drag, `false` otherwise.
408
+ */
409
+ Tabs.prototype.handleVoidDrop = function () {
410
+ var _a, _b;
411
+ if (!((_a = this._animState) === null || _a === void 0 ? void 0 : _a.sourceTabGroupId)) {
412
+ return false;
413
+ }
414
+ var sourceTabGroupId = this._animState.sourceTabGroupId;
415
+ var insertionIndex = (_b = this._animState.currentInsertionIndex) !== null && _b !== void 0 ? _b : this._tabs.length;
416
+ this._animState = null;
417
+ this._commitGroupMove(sourceTabGroupId, insertionIndex);
418
+ return true;
419
+ };
234
420
  Object.defineProperty(Tabs.prototype, "panels", {
235
421
  get: function () {
236
422
  return this._tabs.map(function (_) { return _.value.panel.id; });
@@ -257,6 +443,7 @@ var Tabs = /** @class */ (function (_super) {
257
443
  return this._direction;
258
444
  },
259
445
  set: function (value) {
446
+ var e_2, _a;
260
447
  if (this._direction === value) {
261
448
  return;
262
449
  }
@@ -272,6 +459,19 @@ var Tabs = /** @class */ (function (_super) {
272
459
  (0, dom_1.removeClasses)(this._tabsList, 'dv-tabs-container-vertical');
273
460
  (0, dom_1.addClasses)(this._tabsList, 'dv-horizontal');
274
461
  }
462
+ try {
463
+ for (var _b = __values(this._tabs), _c = _b.next(); !_c.done; _c = _b.next()) {
464
+ var tab = _c.value;
465
+ tab.value.setDirection(value);
466
+ }
467
+ }
468
+ catch (e_2_1) { e_2 = { error: e_2_1 }; }
469
+ finally {
470
+ try {
471
+ if (_c && !_c.done && (_a = _b.return)) _a.call(_b);
472
+ }
473
+ finally { if (e_2) throw e_2.error; }
474
+ }
275
475
  },
276
476
  enumerable: false,
277
477
  configurable: true
@@ -284,8 +484,9 @@ var Tabs = /** @class */ (function (_super) {
284
484
  this._tabs[this.selectedIndex].value === tab);
285
485
  };
286
486
  Tabs.prototype.setActivePanel = function (panel) {
287
- var e_2, _a;
288
- var runningWidth = 0;
487
+ var e_3, _a;
488
+ var isVertical = this._direction === 'vertical';
489
+ var running = 0;
289
490
  try {
290
491
  for (var _b = __values(this._tabs), _c = _b.next(); !_c.done; _c = _b.next()) {
291
492
  var tab = _c.value;
@@ -294,40 +495,115 @@ var Tabs = /** @class */ (function (_super) {
294
495
  if (isActivePanel) {
295
496
  var element = tab.value.element;
296
497
  var parentElement = element.parentElement;
297
- if (runningWidth < parentElement.scrollLeft ||
298
- runningWidth + element.clientWidth >
299
- parentElement.scrollLeft + parentElement.clientWidth) {
300
- parentElement.scrollLeft = runningWidth;
498
+ if (isVertical) {
499
+ if (running < parentElement.scrollTop ||
500
+ running + element.clientHeight >
501
+ parentElement.scrollTop + parentElement.clientHeight) {
502
+ parentElement.scrollTop = running;
503
+ }
504
+ }
505
+ else {
506
+ if (running < parentElement.scrollLeft ||
507
+ running + element.clientWidth >
508
+ parentElement.scrollLeft + parentElement.clientWidth) {
509
+ parentElement.scrollLeft = running;
510
+ }
301
511
  }
302
512
  }
303
- runningWidth += tab.value.element.clientWidth;
513
+ running += isVertical
514
+ ? tab.value.element.clientHeight
515
+ : tab.value.element.clientWidth;
304
516
  }
305
517
  }
306
- catch (e_2_1) { e_2 = { error: e_2_1 }; }
518
+ catch (e_3_1) { e_3 = { error: e_3_1 }; }
307
519
  finally {
308
520
  try {
309
521
  if (_c && !_c.done && (_a = _b.return)) _a.call(_b);
310
522
  }
311
- finally { if (e_2) throw e_2.error; }
523
+ finally { if (e_3) throw e_3.error; }
524
+ }
525
+ // Reposition underlines so the wrap-around follows the new active tab
526
+ if (this._tabGroupManager.groupUnderlines.size > 0) {
527
+ this._tabGroupManager.positionUnderlines();
312
528
  }
313
529
  };
314
530
  Tabs.prototype.openPanel = function (panel, index) {
315
531
  var _this = this;
316
532
  if (index === void 0) { index = this._tabs.length; }
317
- if (this._tabs.find(function (tab) { return tab.value.panel.id === panel.id; })) {
533
+ if (this._tabMap.has(panel.id)) {
318
534
  return;
319
535
  }
320
536
  var tab = new tab_1.Tab(panel, this.accessor, this.group);
321
537
  tab.setContent(panel.view.tab);
538
+ if (this._direction !== 'horizontal') {
539
+ tab.setDirection(this._direction);
540
+ }
322
541
  var disposable = new lifecycle_1.CompositeDisposable(tab.onDragStart(function (event) {
542
+ var _a;
323
543
  _this._onTabDragStart.fire({ nativeEvent: event, panel: panel });
324
- if (_this.accessor.options.tabAnimation === 'smooth') {
544
+ if (((_a = _this.accessor.options.theme) === null || _a === void 0 ? void 0 : _a.tabAnimation) === 'smooth') {
545
+ var tabWidth = tab.element.getBoundingClientRect().width;
546
+ var sourceIndex_1 = _this._tabs.findIndex(function (x) { return x.value === tab; });
325
547
  _this._animState = {
326
548
  sourceTabId: panel.id,
327
- sourceIndex: _this._tabs.findIndex(function (x) { return x.value === tab; }),
549
+ sourceIndex: sourceIndex_1,
328
550
  tabPositions: _this.snapshotTabPositions(),
551
+ chipPositions: _this._tabGroupManager.snapshotChipWidths(),
329
552
  currentInsertionIndex: null,
553
+ targetTabGroupId: null,
554
+ sourceTabGroupId: null,
555
+ sourceGroupPanelIds: null,
556
+ sourceChipWidth: 0,
557
+ cursorOffsetFromDragLeft: tabWidth / 2,
558
+ sourceGapWidth: tabWidth,
559
+ containerLeft: _this._tabsList.getBoundingClientRect().left,
330
560
  };
561
+ // Collapse the source tab after the browser captures the
562
+ // drag image, then open the gap at the source position in
563
+ // the same paint frame — no visual jump.
564
+ // Both collapse and gap must be instant (no transition).
565
+ _this._pendingCollapse = true;
566
+ requestAnimationFrame(function () {
567
+ var _a;
568
+ var _b;
569
+ _this._pendingCollapse = false;
570
+ if (!_this._animState) {
571
+ return;
572
+ }
573
+ // Collapse source tab instantly (no transition)
574
+ tab.element.style.transition = 'none';
575
+ (0, dom_1.toggleClass)(tab.element, 'dv-tab--dragging', true);
576
+ void tab.element.offsetHeight; // force reflow
577
+ (_a = (_b = _this._animState).currentInsertionIndex) !== null && _a !== void 0 ? _a : (_b.currentInsertionIndex = sourceIndex_1);
578
+ // Apply gap with transitions disabled on the target
579
+ _this.applyDragOverTransforms(true);
580
+ // Re-enable transitions for subsequent moves
581
+ tab.element.style.removeProperty('transition');
582
+ });
583
+ }
584
+ }), tab.onTabClick(function (event) {
585
+ if (event.defaultPrevented) {
586
+ return;
587
+ }
588
+ if (_this.group.api.location.type !== 'edge') {
589
+ return;
590
+ }
591
+ if (_this.group.activePanel === panel) {
592
+ // Clicking the active tab toggles expansion
593
+ if (_this.group.api.isCollapsed()) {
594
+ _this.group.api.expand();
595
+ }
596
+ else {
597
+ _this.group.api.collapse();
598
+ }
599
+ }
600
+ else {
601
+ // Clicking a non-active tab switches the active tab.
602
+ // If the group is collapsed, also expand it.
603
+ _this.group.model.openPanel(panel);
604
+ if (_this.group.api.isCollapsed()) {
605
+ _this.group.api.expand();
606
+ }
331
607
  }
332
608
  }), tab.onPointerDown(function (event) {
333
609
  if (event.defaultPrevented) {
@@ -351,34 +627,69 @@ var Tabs = /** @class */ (function (_super) {
351
627
  return;
352
628
  }
353
629
  switch (event.button) {
354
- case 0: // left click or touch
355
- if (_this.group.activePanel !== panel) {
356
- _this.group.model.openPanel(panel);
630
+ case 0:
631
+ if (_this.group.api.location.type === 'edge') {
632
+ // All tab interaction for edge groups is handled by
633
+ // onTabClick to avoid race conditions with active panel state
634
+ }
635
+ else {
636
+ if (_this.group.activePanel !== panel) {
637
+ _this.group.model.openPanel(panel);
638
+ }
357
639
  }
358
640
  break;
359
641
  }
360
642
  }), tab.onDrop(function (event) {
643
+ var _a, _b, _c, _d;
361
644
  var animState = _this._animState;
362
645
  _this._animState = null;
363
- var dropIndex = _this._tabs.findIndex(function (x) { return x.value === tab; });
646
+ _this._pendingCollapse = false;
647
+ var tabIndex = _this._tabs.findIndex(function (x) { return x.value === tab; });
364
648
  if (animState) {
649
+ var dropIndex = event.position === 'right' ? tabIndex + 1 : tabIndex;
650
+ if (animState.sourceTabGroupId) {
651
+ _this._commitGroupMove(animState.sourceTabGroupId, (_a = animState.currentInsertionIndex) !== null && _a !== void 0 ? _a : dropIndex);
652
+ return;
653
+ }
654
+ _this._uncollapsSourceTab(animState.sourceTabId);
365
655
  var firstPositions = _this.snapshotTabPositions();
366
656
  _this.resetTabTransforms();
367
657
  _this._onDrop.fire({
368
658
  event: event.nativeEvent,
369
659
  index: dropIndex,
660
+ targetTabGroupId: animState.targetTabGroupId,
370
661
  });
371
- _this.runFlipAnimation(firstPositions, animState.sourceTabId, animState.sourceIndex === -1, animState.sourceIndex !== -1
372
- ? {
373
- from: Math.min(animState.sourceIndex, dropIndex),
374
- to: Math.max(animState.sourceIndex, dropIndex),
375
- }
376
- : undefined);
662
+ if (((_b = _this.accessor.options.theme) === null || _b === void 0 ? void 0 : _b.tabAnimation) === 'smooth') {
663
+ _this.runFlipAnimation(firstPositions, animState.sourceTabId, animState.sourceIndex === -1, animState.sourceIndex !== -1
664
+ ? {
665
+ from: Math.min(animState.sourceIndex, dropIndex),
666
+ to: Math.max(animState.sourceIndex, dropIndex),
667
+ }
668
+ : undefined);
669
+ }
377
670
  }
378
671
  else {
672
+ // Compute insertion index based on which half of the tab
673
+ // the pointer is over, then adjust for same-group removal:
674
+ // when the source tab sits before the insertion point,
675
+ // removing it shifts all subsequent indices down by one.
676
+ var afterPosition = _this._direction === 'vertical' ? 'bottom' : 'right';
677
+ var insertionIndex = event.position === afterPosition
678
+ ? tabIndex + 1
679
+ : tabIndex;
680
+ var data_2 = (0, dataTransfer_1.getPanelData)();
681
+ var sourceIndex = data_2
682
+ ? _this._tabs.findIndex(function (x) { return x.value.panel.id === data_2.panelId; })
683
+ : -1;
684
+ var adjustedIndex = insertionIndex -
685
+ (sourceIndex !== -1 && sourceIndex < insertionIndex
686
+ ? 1
687
+ : 0);
688
+ var targetTabGroupId = (_d = (_c = _this.group.model.getTabGroupForPanel(tab.panel.id)) === null || _c === void 0 ? void 0 : _c.id) !== null && _d !== void 0 ? _d : null;
379
689
  _this._onDrop.fire({
380
690
  event: event.nativeEvent,
381
- index: dropIndex,
691
+ index: adjustedIndex,
692
+ targetTabGroupId: targetTabGroupId,
382
693
  });
383
694
  }
384
695
  }), tab.onWillShowOverlay(function (event) {
@@ -392,9 +703,14 @@ var Tabs = /** @class */ (function (_super) {
392
703
  }));
393
704
  var value = { value: tab, disposable: disposable };
394
705
  this.addTab(value, index);
706
+ // A new tab may have been inserted between a chip and its
707
+ // group's first tab — reposition all chips to stay correct.
708
+ this._tabGroupManager.positionAllChips();
395
709
  // If a tab was added during active drag, refresh positions
396
710
  if (this._animState) {
397
711
  this._animState.tabPositions = this.snapshotTabPositions();
712
+ this._animState.chipPositions =
713
+ this._tabGroupManager.snapshotChipWidths();
398
714
  this.applyDragOverTransforms();
399
715
  }
400
716
  };
@@ -404,8 +720,11 @@ var Tabs = /** @class */ (function (_super) {
404
720
  this.resetTabTransforms();
405
721
  this._animState = null;
406
722
  }
723
+ // Force-clean any pending transitionend listener
724
+ this._tabGroupManager.cleanupTransition(id);
407
725
  var index = this.indexOf(id);
408
726
  var tabToRemove = this._tabs.splice(index, 1)[0];
727
+ this._tabMap.delete(id);
409
728
  var value = tabToRemove.value, disposable = tabToRemove.disposable;
410
729
  disposable.dispose();
411
730
  value.dispose();
@@ -413,6 +732,8 @@ var Tabs = /** @class */ (function (_super) {
413
732
  // If a non-source tab was removed during active drag, refresh positions
414
733
  if (this._animState) {
415
734
  this._animState.tabPositions = this.snapshotTabPositions();
735
+ this._animState.chipPositions =
736
+ this._tabGroupManager.snapshotChipWidths();
416
737
  this.applyDragOverTransforms();
417
738
  }
418
739
  };
@@ -421,43 +742,285 @@ var Tabs = /** @class */ (function (_super) {
421
742
  if (index < 0 || index > this._tabs.length) {
422
743
  throw new Error('invalid location');
423
744
  }
424
- this._tabsList.insertBefore(tab.value.element, this._tabsList.children[index]);
745
+ // Use the tab element at `index` as the reference node rather than
746
+ // `children[index]`, because `_tabsList` may contain non-tab children
747
+ // (e.g. group chips, underlines) that shift the DOM indices.
748
+ var refNode = index < this._tabs.length ? this._tabs[index].value.element : null;
749
+ this._tabsList.insertBefore(tab.value.element, refNode);
425
750
  this._tabs = __spreadArray(__spreadArray(__spreadArray([], __read(this._tabs.slice(0, index)), false), [
426
751
  tab
427
752
  ], false), __read(this._tabs.slice(index)), false);
753
+ this._tabMap.set(tab.value.panel.id, tab);
428
754
  if (this.selectedIndex < 0) {
429
755
  this.selectedIndex = index;
430
756
  }
431
757
  };
432
758
  Tabs.prototype.toggleDropdown = function (options) {
759
+ var e_4, _a, e_5, _b;
433
760
  var _this = this;
434
- var tabs = options.reset
435
- ? []
436
- : this._tabs
437
- .filter(function (tab) {
438
- return !(0, dom_1.isChildEntirelyVisibleWithinParent)(tab.value.element, _this._tabsList);
439
- })
440
- .map(function (x) { return x.value.panel.id; });
441
- this._onOverflowTabsChange.fire({ tabs: tabs, reset: options.reset });
761
+ if (options.reset) {
762
+ this._onOverflowTabsChange.fire({
763
+ tabs: [],
764
+ tabGroups: [],
765
+ reset: true,
766
+ });
767
+ return;
768
+ }
769
+ var tabs = this._tabs
770
+ .filter(function (tab) {
771
+ return !(0, dom_1.isChildEntirelyVisibleWithinParent)(tab.value.element, _this._tabsList);
772
+ })
773
+ .map(function (x) { return x.value.panel.id; });
774
+ // Detect tab groups whose chip is clipped or whose tabs are all
775
+ // in the overflow set (e.g. collapsed groups scrolled out of view).
776
+ var overflowTabSet = new Set(tabs);
777
+ var tabGroups = [];
778
+ try {
779
+ for (var _c = __values(this.group.model.getTabGroups()), _d = _c.next(); !_d.done; _d = _c.next()) {
780
+ var tg = _d.value;
781
+ var chipEntry = this._tabGroupManager.chipRenderers.get(tg.id);
782
+ var chipClipped = chipEntry &&
783
+ !(0, dom_1.isChildEntirelyVisibleWithinParent)(chipEntry.chip.element, this._tabsList);
784
+ // A group is in overflow if its chip is clipped OR all its
785
+ // visible tabs are in the overflow set.
786
+ var allTabsOverflow = tg.panelIds.length > 0 &&
787
+ tg.panelIds.every(function (pid) { return overflowTabSet.has(pid); });
788
+ if (chipClipped || allTabsOverflow) {
789
+ tabGroups.push(tg.id);
790
+ // For collapsed groups whose chip is clipped, ensure all
791
+ // member tabs are included in the overflow list so they
792
+ // appear in the dropdown.
793
+ if (tg.collapsed) {
794
+ try {
795
+ for (var _e = (e_5 = void 0, __values(tg.panelIds)), _f = _e.next(); !_f.done; _f = _e.next()) {
796
+ var pid = _f.value;
797
+ if (!overflowTabSet.has(pid)) {
798
+ overflowTabSet.add(pid);
799
+ tabs.push(pid);
800
+ }
801
+ }
802
+ }
803
+ catch (e_5_1) { e_5 = { error: e_5_1 }; }
804
+ finally {
805
+ try {
806
+ if (_f && !_f.done && (_b = _e.return)) _b.call(_e);
807
+ }
808
+ finally { if (e_5) throw e_5.error; }
809
+ }
810
+ }
811
+ }
812
+ }
813
+ }
814
+ catch (e_4_1) { e_4 = { error: e_4_1 }; }
815
+ finally {
816
+ try {
817
+ if (_d && !_d.done && (_a = _c.return)) _a.call(_c);
818
+ }
819
+ finally { if (e_4) throw e_4.error; }
820
+ }
821
+ this._onOverflowTabsChange.fire({ tabs: tabs, tabGroups: tabGroups, reset: false });
442
822
  };
443
823
  Tabs.prototype.updateDragAndDropState = function () {
444
- var e_3, _a;
824
+ var e_6, _a;
445
825
  try {
446
826
  for (var _b = __values(this._tabs), _c = _b.next(); !_c.done; _c = _b.next()) {
447
827
  var tab = _c.value;
448
828
  tab.value.updateDragAndDropState();
449
829
  }
450
830
  }
451
- catch (e_3_1) { e_3 = { error: e_3_1 }; }
831
+ catch (e_6_1) { e_6 = { error: e_6_1 }; }
452
832
  finally {
453
833
  try {
454
834
  if (_c && !_c.done && (_a = _b.return)) _a.call(_b);
455
835
  }
456
- finally { if (e_3) throw e_3.error; }
836
+ finally { if (e_6) throw e_6.error; }
837
+ }
838
+ };
839
+ /**
840
+ * Synchronize chip elements and CSS classes for all tab groups
841
+ * in the parent group model. Call after any tab group mutation.
842
+ */
843
+ Tabs.prototype.updateTabGroups = function () {
844
+ this._tabGroupManager.update();
845
+ };
846
+ Tabs.prototype.refreshTabGroupAccent = function () {
847
+ this._tabGroupManager.refreshAccents();
848
+ };
849
+ Tabs.prototype._handleChipDragStart = function (tabGroup, chip, event) {
850
+ var e_7, _a;
851
+ var _this = this;
852
+ var _b;
853
+ var firstPanelId = tabGroup.panelIds[0];
854
+ var firstIdx = firstPanelId
855
+ ? this._tabs.findIndex(function (t) { return t.value.panel.id === firstPanelId; })
856
+ : -1;
857
+ var chipRect = chip.element.getBoundingClientRect();
858
+ // Compute total group width (chip + all tabs)
859
+ var groupGapWidth = chipRect.width;
860
+ try {
861
+ for (var _c = __values(tabGroup.panelIds), _d = _c.next(); !_d.done; _d = _c.next()) {
862
+ var pid = _d.value;
863
+ var tabEntry = this._tabMap.get(pid);
864
+ if (tabEntry) {
865
+ groupGapWidth +=
866
+ tabEntry.value.element.getBoundingClientRect().width;
867
+ }
868
+ }
869
+ }
870
+ catch (e_7_1) { e_7 = { error: e_7_1 }; }
871
+ finally {
872
+ try {
873
+ if (_d && !_d.done && (_a = _c.return)) _a.call(_c);
874
+ }
875
+ finally { if (e_7) throw e_7.error; }
876
+ }
877
+ this._animState = {
878
+ sourceTabId: '',
879
+ sourceIndex: firstIdx,
880
+ tabPositions: this.snapshotTabPositions(),
881
+ chipPositions: this._tabGroupManager.snapshotChipWidths(),
882
+ currentInsertionIndex: null,
883
+ targetTabGroupId: null,
884
+ sourceTabGroupId: tabGroup.id,
885
+ sourceGroupPanelIds: new Set(tabGroup.panelIds),
886
+ sourceChipWidth: chipRect.width,
887
+ cursorOffsetFromDragLeft: event.clientX - chipRect.left,
888
+ sourceGapWidth: groupGapWidth,
889
+ containerLeft: this._tabsList.getBoundingClientRect().left,
890
+ };
891
+ // Set LocalSelectionTransfer so drop targets recognise this as
892
+ // an internal dockview drag. panelId is null (group-level),
893
+ // tabGroupId identifies which tab group is being dragged.
894
+ var panelTransfer = dataTransfer_1.LocalSelectionTransfer.getInstance();
895
+ panelTransfer.setData([
896
+ new dataTransfer_1.PanelTransfer(this.accessor.id, this.group.id, null, tabGroup.id),
897
+ ], dataTransfer_1.PanelTransfer.prototype);
898
+ var iframes = (0, dom_1.disableIframePointEvents)();
899
+ this._chipDragCleanup = {
900
+ dispose: function () {
901
+ panelTransfer.clearData(dataTransfer_1.PanelTransfer.prototype);
902
+ iframes.release();
903
+ },
904
+ };
905
+ if (event.dataTransfer) {
906
+ event.dataTransfer.effectAllowed = 'move';
907
+ if (event.dataTransfer.items.length === 0) {
908
+ event.dataTransfer.setData('text/plain', '');
909
+ }
910
+ }
911
+ if (((_b = this.accessor.options.theme) === null || _b === void 0 ? void 0 : _b.tabAnimation) === 'smooth') {
912
+ // Collapse group tabs + chip after the browser
913
+ // captures the drag image, then open the gap at the
914
+ // source position — all instant (no transitions).
915
+ var groupPanelIds_1 = new Set(tabGroup.panelIds);
916
+ this._pendingCollapse = true;
917
+ requestAnimationFrame(function () {
918
+ var e_8, _a, e_9, _b;
919
+ var _c;
920
+ var _d;
921
+ _this._pendingCollapse = false;
922
+ if (!_this._animState) {
923
+ return;
924
+ }
925
+ try {
926
+ // Collapse all group tabs instantly
927
+ for (var _e = __values(_this._tabs), _f = _e.next(); !_f.done; _f = _e.next()) {
928
+ var t = _f.value;
929
+ if (groupPanelIds_1.has(t.value.panel.id)) {
930
+ t.value.element.style.transition = 'none';
931
+ (0, dom_1.toggleClass)(t.value.element, 'dv-tab--dragging', true);
932
+ }
933
+ }
934
+ }
935
+ catch (e_8_1) { e_8 = { error: e_8_1 }; }
936
+ finally {
937
+ try {
938
+ if (_f && !_f.done && (_a = _e.return)) _a.call(_e);
939
+ }
940
+ finally { if (e_8) throw e_8.error; }
941
+ }
942
+ // Collapse the group chip instantly
943
+ var chipEntry = _this._tabGroupManager.chipRenderers.get(tabGroup.id);
944
+ if (chipEntry) {
945
+ chipEntry.chip.element.style.transition = 'none';
946
+ (0, dom_1.toggleClass)(chipEntry.chip.element, 'dv-tab-group-chip--dragging', true);
947
+ }
948
+ // Single reflow for the entire batch
949
+ void _this._tabsList.offsetHeight;
950
+ var underline = _this._tabGroupManager.groupUnderlines.get(tabGroup.id);
951
+ if (underline) {
952
+ underline.style.display = 'none';
953
+ }
954
+ (_c = (_d = _this._animState).currentInsertionIndex) !== null && _c !== void 0 ? _c : (_d.currentInsertionIndex = firstIdx);
955
+ // Apply gap with transitions disabled
956
+ _this.applyDragOverTransforms(true);
957
+ try {
958
+ // Re-enable transitions for subsequent moves
959
+ for (var _g = __values(_this._tabs), _h = _g.next(); !_h.done; _h = _g.next()) {
960
+ var t = _h.value;
961
+ if (groupPanelIds_1.has(t.value.panel.id)) {
962
+ t.value.element.style.removeProperty('transition');
963
+ }
964
+ }
965
+ }
966
+ catch (e_9_1) { e_9 = { error: e_9_1 }; }
967
+ finally {
968
+ try {
969
+ if (_h && !_h.done && (_b = _g.return)) _b.call(_g);
970
+ }
971
+ finally { if (e_9) throw e_9.error; }
972
+ }
973
+ if (chipEntry) {
974
+ chipEntry.chip.element.style.removeProperty('transition');
975
+ }
976
+ });
977
+ }
978
+ // Build a composite drag image showing chip + group tabs
979
+ this._tabGroupManager.setGroupDragImage(event, tabGroup, chip.element);
980
+ };
981
+ /**
982
+ * Sets the broader container that is part of the same logical drop surface
983
+ * as this tab list (e.g. the full header element). When a dragleave from
984
+ * the tabs list lands inside this container, `_animState` is preserved so
985
+ * that external dragover listeners can continue the animation.
986
+ */
987
+ Tabs.prototype.setExtendedDropZone = function (el) {
988
+ this._extendedDropZone = el;
989
+ };
990
+ /**
991
+ * Allows external elements (e.g. void container, left actions) to push an
992
+ * insertion index into the animation while the cursor is outside the tabs
993
+ * list itself. Pass `null` to clear the indicator.
994
+ */
995
+ Tabs.prototype.setExternalInsertionIndex = function (index) {
996
+ if (!this._animState) {
997
+ return;
998
+ }
999
+ if (index === this._animState.currentInsertionIndex) {
1000
+ return;
1001
+ }
1002
+ this._animState.currentInsertionIndex = index;
1003
+ this.applyDragOverTransforms();
1004
+ };
1005
+ /**
1006
+ * Called when the drag cursor leaves the entire header area (not just the
1007
+ * tabs list). Clears animation state for cross-group drags, which never
1008
+ * receive a `dragend` event on this tab list.
1009
+ */
1010
+ Tabs.prototype.clearExternalAnimState = function () {
1011
+ if (!this._animState) {
1012
+ return;
1013
+ }
1014
+ this.resetTabTransforms();
1015
+ if (this._animState.sourceIndex === -1) {
1016
+ this._animState = null;
1017
+ }
1018
+ else {
1019
+ this._animState.currentInsertionIndex = null;
457
1020
  }
458
1021
  };
459
1022
  Tabs.prototype.snapshotTabPositions = function () {
460
- var e_4, _a;
1023
+ var e_10, _a;
461
1024
  var positions = new Map();
462
1025
  try {
463
1026
  for (var _b = __values(this._tabs), _c = _b.next(); !_c.done; _c = _b.next()) {
@@ -465,155 +1028,658 @@ var Tabs = /** @class */ (function (_super) {
465
1028
  positions.set(tab.value.panel.id, tab.value.element.getBoundingClientRect());
466
1029
  }
467
1030
  }
468
- catch (e_4_1) { e_4 = { error: e_4_1 }; }
1031
+ catch (e_10_1) { e_10 = { error: e_10_1 }; }
469
1032
  finally {
470
1033
  try {
471
1034
  if (_c && !_c.done && (_a = _b.return)) _a.call(_b);
472
1035
  }
473
- finally { if (e_4) throw e_4.error; }
1036
+ finally { if (e_10) throw e_10.error; }
474
1037
  }
475
1038
  return positions;
476
1039
  };
477
1040
  Tabs.prototype.getAverageTabWidth = function () {
478
- var e_5, _a;
1041
+ var e_11, _a;
479
1042
  if (this._tabs.length === 0) {
480
1043
  return 0;
481
1044
  }
482
- var totalWidth = 0;
1045
+ var isVertical = this._direction === 'vertical';
1046
+ var total = 0;
483
1047
  try {
484
1048
  for (var _b = __values(this._tabs), _c = _b.next(); !_c.done; _c = _b.next()) {
485
1049
  var tab = _c.value;
486
- totalWidth += tab.value.element.getBoundingClientRect().width;
1050
+ var rect = tab.value.element.getBoundingClientRect();
1051
+ total += isVertical ? rect.height : rect.width;
487
1052
  }
488
1053
  }
489
- catch (e_5_1) { e_5 = { error: e_5_1 }; }
1054
+ catch (e_11_1) { e_11 = { error: e_11_1 }; }
490
1055
  finally {
491
1056
  try {
492
1057
  if (_c && !_c.done && (_a = _b.return)) _a.call(_b);
493
1058
  }
494
- finally { if (e_5) throw e_5.error; }
1059
+ finally { if (e_11) throw e_11.error; }
495
1060
  }
496
- return totalWidth / this._tabs.length;
1061
+ return total / this._tabs.length;
497
1062
  };
498
1063
  Tabs.prototype.handleDragOver = function (event) {
1064
+ var e_12, _a, e_13, _b;
1065
+ var _this = this;
1066
+ var _c, _d, _e, _f, _g;
499
1067
  if (!this._animState) {
500
1068
  return;
501
1069
  }
502
1070
  var mouseX = event.clientX;
503
1071
  var insertionIndex = null;
1072
+ var targetTabGroupId = null;
1073
+ var sourceGroupPanelIds = this._animState.sourceGroupPanelIds;
1074
+ // Accumulation approach: compute where the drag image's left edge
1075
+ // would be, then walk tabs left-to-right using their original widths.
1076
+ // A tab fits to the left of the gap if the cumulative width of all
1077
+ // preceding non-source tabs <= available space.
1078
+ var dragLeftEdge = mouseX - this._animState.cursorOffsetFromDragLeft;
1079
+ var availableSpace = dragLeftEdge - this._animState.containerLeft;
1080
+ var accWidth = 0;
1081
+ // Build lookup: first panel ID of each non-source group → group ID
1082
+ // so we can add chip widths when we encounter a group's first tab.
1083
+ var firstPanelToGroup = new Map();
1084
+ if (this._tabGroupManager.chipRenderers.size > 0) {
1085
+ var tabGroups = this.group.model.getTabGroups();
1086
+ try {
1087
+ for (var tabGroups_2 = __values(tabGroups), tabGroups_2_1 = tabGroups_2.next(); !tabGroups_2_1.done; tabGroups_2_1 = tabGroups_2.next()) {
1088
+ var tg = tabGroups_2_1.value;
1089
+ if (tg.id === this._animState.sourceTabGroupId) {
1090
+ continue;
1091
+ }
1092
+ if (tg.panelIds.length > 0) {
1093
+ firstPanelToGroup.set(tg.panelIds[0], tg.id);
1094
+ }
1095
+ }
1096
+ }
1097
+ catch (e_12_1) { e_12 = { error: e_12_1 }; }
1098
+ finally {
1099
+ try {
1100
+ if (tabGroups_2_1 && !tabGroups_2_1.done && (_a = tabGroups_2.return)) _a.call(tabGroups_2);
1101
+ }
1102
+ finally { if (e_12) throw e_12.error; }
1103
+ }
1104
+ }
504
1105
  for (var i = 0; i < this._tabs.length; i++) {
505
1106
  var tab = this._tabs[i].value;
506
1107
  if (tab.panel.id === this._animState.sourceTabId) {
507
1108
  continue;
508
1109
  }
509
- var rect = tab.element.getBoundingClientRect();
510
- var midpoint = rect.left + rect.width / 2;
511
- if (mouseX < midpoint) {
512
- insertionIndex = i;
1110
+ if (sourceGroupPanelIds === null || sourceGroupPanelIds === void 0 ? void 0 : sourceGroupPanelIds.has(tab.panel.id)) {
1111
+ continue;
1112
+ }
1113
+ // If this tab is the first of a non-source group, include
1114
+ // the chip width (which sits before it in the DOM).
1115
+ var groupId = firstPanelToGroup.get(tab.panel.id);
1116
+ if (groupId) {
1117
+ var chipWidth = (_c = this._animState.chipPositions.get(groupId)) !== null && _c !== void 0 ? _c : 0;
1118
+ if (accWidth + chipWidth > availableSpace) {
1119
+ // Chip alone overflows — gap goes before this group
1120
+ insertionIndex !== null && insertionIndex !== void 0 ? insertionIndex : (insertionIndex = i);
1121
+ break;
1122
+ }
1123
+ accWidth += chipWidth;
1124
+ }
1125
+ // Use original width (before collapse/transforms)
1126
+ var origRect = this._animState.tabPositions.get(tab.panel.id);
1127
+ var tabWidth = origRect
1128
+ ? origRect.width
1129
+ : tab.element.getBoundingClientRect().width;
1130
+ // Shift at the midpoint: a tab moves left once the drag image
1131
+ // covers half of it (like Chrome's tab drag behavior).
1132
+ if (accWidth + tabWidth / 2 <= availableSpace) {
1133
+ accWidth += tabWidth;
1134
+ insertionIndex = i + 1;
1135
+ }
1136
+ else {
1137
+ insertionIndex !== null && insertionIndex !== void 0 ? insertionIndex : (insertionIndex = i);
513
1138
  break;
514
1139
  }
515
- insertionIndex = i + 1;
516
1140
  }
517
- if (insertionIndex === this._animState.currentInsertionIndex) {
1141
+ // Determine which tab group (if any) the insertion index falls within.
1142
+ //
1143
+ // We use snapshot-based positions (accWidth from the accumulation loop
1144
+ // above) to compute original chip boundaries. This avoids reading
1145
+ // getBoundingClientRect() on chips whose live position is shifted by
1146
+ // the drag gap margin, which caused oscillation / visual jumps.
1147
+ if (insertionIndex !== null &&
1148
+ this._tabGroupManager.chipRenderers.size > 0) {
1149
+ var isGroupDrag = !!this._animState.sourceTabGroupId;
1150
+ var tabGroups = this.group.model.getTabGroups();
1151
+ // Rebuild the accumulated width up to insertionIndex so we know
1152
+ // the original right edge of the chip (if any) that precedes it.
1153
+ // We walk exactly the same way as the accumulation loop above.
1154
+ var accUpTo = 0;
1155
+ for (var i = 0; i < this._tabs.length; i++) {
1156
+ var tab = this._tabs[i].value;
1157
+ if (tab.panel.id === this._animState.sourceTabId) {
1158
+ continue;
1159
+ }
1160
+ if (sourceGroupPanelIds === null || sourceGroupPanelIds === void 0 ? void 0 : sourceGroupPanelIds.has(tab.panel.id)) {
1161
+ continue;
1162
+ }
1163
+ if (i >= insertionIndex) {
1164
+ break;
1165
+ }
1166
+ var gid = firstPanelToGroup.get(tab.panel.id);
1167
+ if (gid) {
1168
+ accUpTo += (_d = this._animState.chipPositions.get(gid)) !== null && _d !== void 0 ? _d : 0;
1169
+ }
1170
+ var origRect = this._animState.tabPositions.get(tab.panel.id);
1171
+ accUpTo += origRect
1172
+ ? origRect.width
1173
+ : tab.element.getBoundingClientRect().width;
1174
+ }
1175
+ var _loop_1 = function (tg) {
1176
+ // Build effective panel list: exclude the source tab
1177
+ // so that dragging a tab out of its own group doesn't
1178
+ // inflate the group's index range.
1179
+ var effectivePanelIds = tg.panelIds.filter(function (pid) {
1180
+ return pid !== _this._animState.sourceTabId &&
1181
+ !(sourceGroupPanelIds === null || sourceGroupPanelIds === void 0 ? void 0 : sourceGroupPanelIds.has(pid));
1182
+ });
1183
+ if (effectivePanelIds.length === 0) {
1184
+ return "continue";
1185
+ }
1186
+ var firstIdx = this_1._tabs.findIndex(function (t) { return t.value.panel.id === effectivePanelIds[0]; });
1187
+ var lastIdx = this_1._tabs.findIndex(function (t) {
1188
+ return t.value.panel.id ===
1189
+ effectivePanelIds[effectivePanelIds.length - 1];
1190
+ });
1191
+ if (firstIdx === -1 || lastIdx === -1) {
1192
+ return "continue";
1193
+ }
1194
+ var isInsideRange = insertionIndex >= firstIdx && insertionIndex <= lastIdx;
1195
+ var isJustBeforeGroup = !isInsideRange && insertionIndex === firstIdx - 1;
1196
+ if (!isInsideRange && !isJustBeforeGroup) {
1197
+ return "continue";
1198
+ }
1199
+ if (isGroupDrag) {
1200
+ // A group cannot be dropped inside another group.
1201
+ // Snap the insertion index to just before or just
1202
+ // after this group based on cursor position relative
1203
+ // to the group's midpoint.
1204
+ var groupMid = (firstIdx + lastIdx + 1) / 2;
1205
+ if (insertionIndex < groupMid) {
1206
+ insertionIndex = firstIdx;
1207
+ }
1208
+ else {
1209
+ insertionIndex = lastIdx + 1;
1210
+ }
1211
+ return "break";
1212
+ }
1213
+ if (isJustBeforeGroup) {
1214
+ // Check whether only the source tab (or source group
1215
+ // tabs) sits between insertionIndex and firstIdx.
1216
+ // If so, the source is being dragged away from that
1217
+ // slot, so we ARE effectively "just before" the group
1218
+ // and should still allow dropping into position 0.
1219
+ var allInBetweenAreSource = true;
1220
+ for (var j = insertionIndex; j < firstIdx; j++) {
1221
+ var pid = this_1._tabs[j].value.panel.id;
1222
+ if (pid !== this_1._animState.sourceTabId &&
1223
+ !(sourceGroupPanelIds === null || sourceGroupPanelIds === void 0 ? void 0 : sourceGroupPanelIds.has(pid))) {
1224
+ allInBetweenAreSource = false;
1225
+ break;
1226
+ }
1227
+ }
1228
+ if (!allInBetweenAreSource) {
1229
+ return "continue";
1230
+ }
1231
+ var chipWidth = (_e = this_1._animState.chipPositions.get(tg.id)) !== null && _e !== void 0 ? _e : 0;
1232
+ var threshold = tg.collapsed
1233
+ ? this_1._animState.containerLeft +
1234
+ accUpTo +
1235
+ chipWidth / 2
1236
+ : this_1._animState.containerLeft + accUpTo + chipWidth;
1237
+ if (mouseX >= threshold) {
1238
+ insertionIndex = firstIdx;
1239
+ targetTabGroupId = tg.id;
1240
+ }
1241
+ return "break";
1242
+ }
1243
+ if (isInsideRange) {
1244
+ var chipWidth = (_f = this_1._animState.chipPositions.get(tg.id)) !== null && _f !== void 0 ? _f : 0;
1245
+ var chipOriginalRight = this_1._animState.containerLeft + accUpTo + chipWidth;
1246
+ if (insertionIndex === firstIdx) {
1247
+ if (mouseX >= chipOriginalRight) {
1248
+ targetTabGroupId = tg.id;
1249
+ }
1250
+ }
1251
+ else {
1252
+ targetTabGroupId = tg.id;
1253
+ }
1254
+ return "break";
1255
+ }
1256
+ };
1257
+ var this_1 = this;
1258
+ try {
1259
+ for (var tabGroups_3 = __values(tabGroups), tabGroups_3_1 = tabGroups_3.next(); !tabGroups_3_1.done; tabGroups_3_1 = tabGroups_3.next()) {
1260
+ var tg = tabGroups_3_1.value;
1261
+ var state_1 = _loop_1(tg);
1262
+ if (state_1 === "break")
1263
+ break;
1264
+ }
1265
+ }
1266
+ catch (e_13_1) { e_13 = { error: e_13_1 }; }
1267
+ finally {
1268
+ try {
1269
+ if (tabGroups_3_1 && !tabGroups_3_1.done && (_b = tabGroups_3.return)) _b.call(tabGroups_3);
1270
+ }
1271
+ finally { if (e_13) throw e_13.error; }
1272
+ }
1273
+ }
1274
+ if (insertionIndex === this._animState.currentInsertionIndex &&
1275
+ targetTabGroupId === this._animState.targetTabGroupId) {
518
1276
  return;
519
1277
  }
520
1278
  this._animState.currentInsertionIndex = insertionIndex;
521
- this.applyDragOverTransforms();
1279
+ this._animState.targetTabGroupId = targetTabGroupId;
1280
+ if (((_g = this.accessor.options.theme) === null || _g === void 0 ? void 0 : _g.tabAnimation) === 'smooth') {
1281
+ this.applyDragOverTransforms();
1282
+ }
1283
+ };
1284
+ /**
1285
+ * Batch-remove a CSS class from multiple elements instantly,
1286
+ * forcing only a single reflow for the entire batch.
1287
+ */
1288
+ Tabs.prototype._removeClassInstantlyBatch = function (elements, cls) {
1289
+ var e_14, _a, e_15, _b;
1290
+ var affected = [];
1291
+ try {
1292
+ for (var elements_1 = __values(elements), elements_1_1 = elements_1.next(); !elements_1_1.done; elements_1_1 = elements_1.next()) {
1293
+ var el = elements_1_1.value;
1294
+ if (el.classList.contains(cls)) {
1295
+ el.style.transition = 'none';
1296
+ (0, dom_1.toggleClass)(el, cls, false);
1297
+ affected.push(el);
1298
+ }
1299
+ }
1300
+ }
1301
+ catch (e_14_1) { e_14 = { error: e_14_1 }; }
1302
+ finally {
1303
+ try {
1304
+ if (elements_1_1 && !elements_1_1.done && (_a = elements_1.return)) _a.call(elements_1);
1305
+ }
1306
+ finally { if (e_14) throw e_14.error; }
1307
+ }
1308
+ if (affected.length > 0) {
1309
+ void affected[0].offsetHeight; // single reflow for entire batch
1310
+ try {
1311
+ for (var affected_1 = __values(affected), affected_1_1 = affected_1.next(); !affected_1_1.done; affected_1_1 = affected_1.next()) {
1312
+ var el = affected_1_1.value;
1313
+ el.style.removeProperty('transition');
1314
+ }
1315
+ }
1316
+ catch (e_15_1) { e_15 = { error: e_15_1 }; }
1317
+ finally {
1318
+ try {
1319
+ if (affected_1_1 && !affected_1_1.done && (_b = affected_1.return)) _b.call(affected_1);
1320
+ }
1321
+ finally { if (e_15) throw e_15.error; }
1322
+ }
1323
+ }
1324
+ };
1325
+ /**
1326
+ * Remove `dv-tab--dragging` from the source tab instantly so it
1327
+ * regains its real width before FLIP snapshots.
1328
+ */
1329
+ Tabs.prototype._uncollapsSourceTab = function (sourceTabId) {
1330
+ var entry = this._tabMap.get(sourceTabId);
1331
+ if (entry) {
1332
+ this._removeClassInstantlyBatch([entry.value.element], 'dv-tab--dragging');
1333
+ }
522
1334
  };
523
- Tabs.prototype.applyDragOverTransforms = function () {
1335
+ Tabs.prototype.applyDragOverTransforms = function (skipTransition) {
1336
+ var e_16, _a, e_17, _b;
1337
+ var _this = this;
1338
+ if (skipTransition === void 0) { skipTransition = false; }
524
1339
  if (!this._animState ||
525
1340
  this._animState.currentInsertionIndex === null) {
526
1341
  this.resetTabTransforms();
527
1342
  return;
528
1343
  }
1344
+ // Don't apply transforms until the source tab has been collapsed
1345
+ // in the rAF callback — otherwise the gap + visible source = jump.
1346
+ if (this._pendingCollapse) {
1347
+ return;
1348
+ }
529
1349
  var insertionIndex = this._animState.currentInsertionIndex;
530
- var sourceRect = this._animState.tabPositions.get(this._animState.sourceTabId);
531
- var gapWidth = sourceRect
532
- ? sourceRect.width
533
- : this.getAverageTabWidth();
534
- // Find the first non-source tab at insertionIndex to receive the gap margin
1350
+ // For group drags, gap = sum of all group member widths
1351
+ var gapWidth;
1352
+ var sourceGroupPanelIds = this._animState.sourceGroupPanelIds;
1353
+ if (this._animState.sourceTabGroupId && sourceGroupPanelIds) {
1354
+ gapWidth = this._animState.sourceGapWidth;
1355
+ }
1356
+ else {
1357
+ var sourceRect = this._animState.tabPositions.get(this._animState.sourceTabId);
1358
+ gapWidth = sourceRect
1359
+ ? sourceRect.width
1360
+ : this.getAverageTabWidth();
1361
+ }
1362
+ // When the insertion lands at or before a group's first tab, shift
1363
+ // the chip so the gap appears before the entire group.
1364
+ //
1365
+ // Two cases:
1366
+ // 1. targetTabGroupId is null (standalone drop) — always shift chip.
1367
+ // 2. targetTabGroupId is set AND the group is collapsed — shift chip
1368
+ // because the collapsed tabs are invisible, so putting the gap on
1369
+ // them has no visual effect.
1370
+ var chipToShift = null;
1371
+ if (this._tabGroupManager.chipRenderers.size > 0) {
1372
+ var tabGroups = this.group.model.getTabGroups();
1373
+ var _loop_2 = function (tg) {
1374
+ if (tg.id === this_2._animState.sourceTabGroupId)
1375
+ return "continue";
1376
+ // Skip the group that the dragged tab belongs to — the
1377
+ // gap should appear after the chip (where the tab was),
1378
+ // not before it.
1379
+ if (tg.panelIds.includes(this_2._animState.sourceTabId))
1380
+ return "continue";
1381
+ var effectivePids = tg.panelIds.filter(function (pid) {
1382
+ return pid !== _this._animState.sourceTabId &&
1383
+ !(sourceGroupPanelIds === null || sourceGroupPanelIds === void 0 ? void 0 : sourceGroupPanelIds.has(pid));
1384
+ });
1385
+ if (effectivePids.length === 0)
1386
+ return "continue";
1387
+ var firstIdx = this_2._tabs.findIndex(function (t) { return t.value.panel.id === effectivePids[0]; });
1388
+ // Only consider chip-shifting when dropping outside the
1389
+ // group, or when dropping inside a collapsed group (whose
1390
+ // tabs are invisible).
1391
+ var shouldShiftChip = !this_2._animState.targetTabGroupId ||
1392
+ (this_2._animState.targetTabGroupId === tg.id &&
1393
+ tg.collapsed);
1394
+ if (!shouldShiftChip)
1395
+ return "continue";
1396
+ if (firstIdx >= insertionIndex) {
1397
+ var hasTabs = false;
1398
+ for (var j = insertionIndex; j < firstIdx; j++) {
1399
+ var pid = this_2._tabs[j].value.panel.id;
1400
+ if (pid === this_2._animState.sourceTabId)
1401
+ continue;
1402
+ if (sourceGroupPanelIds === null || sourceGroupPanelIds === void 0 ? void 0 : sourceGroupPanelIds.has(pid))
1403
+ continue;
1404
+ hasTabs = true;
1405
+ break;
1406
+ }
1407
+ if (!hasTabs) {
1408
+ var chipEntry = this_2._tabGroupManager.chipRenderers.get(tg.id);
1409
+ if (chipEntry) {
1410
+ chipToShift = chipEntry.chip.element;
1411
+ }
1412
+ }
1413
+ return "break";
1414
+ }
1415
+ };
1416
+ var this_2 = this;
1417
+ try {
1418
+ for (var tabGroups_4 = __values(tabGroups), tabGroups_4_1 = tabGroups_4.next(); !tabGroups_4_1.done; tabGroups_4_1 = tabGroups_4.next()) {
1419
+ var tg = tabGroups_4_1.value;
1420
+ var state_2 = _loop_2(tg);
1421
+ if (state_2 === "break")
1422
+ break;
1423
+ }
1424
+ }
1425
+ catch (e_16_1) { e_16 = { error: e_16_1 }; }
1426
+ finally {
1427
+ try {
1428
+ if (tabGroups_4_1 && !tabGroups_4_1.done && (_a = tabGroups_4.return)) _a.call(tabGroups_4);
1429
+ }
1430
+ finally { if (e_16) throw e_16.error; }
1431
+ }
1432
+ }
1433
+ // Helper: pick the correct shifting class for tabs vs chips.
1434
+ var shiftingClass = function (el) {
1435
+ return el.classList.contains('dv-tab-group-chip')
1436
+ ? 'dv-tab-group-chip--shifting'
1437
+ : 'dv-tab--shifting';
1438
+ };
1439
+ // Helper: apply a margin-left value to an element, optionally
1440
+ // bypassing CSS transitions for instant positioning.
1441
+ var setMargin = function (el, value) {
1442
+ if (skipTransition) {
1443
+ el.style.transition = 'none';
1444
+ el.style.marginLeft = value;
1445
+ void el.offsetHeight;
1446
+ el.style.removeProperty('transition');
1447
+ }
1448
+ else {
1449
+ el.style.marginLeft = value;
1450
+ }
1451
+ (0, dom_1.toggleClass)(el, shiftingClass(el), true);
1452
+ };
1453
+ var clearMargin = function (el) {
1454
+ var cls = shiftingClass(el);
1455
+ // Remove any previous pending listener for this element
1456
+ var prev = _this._pendingMarginCleanups.get(el);
1457
+ if (prev) {
1458
+ prev();
1459
+ }
1460
+ if (skipTransition || !el.style.marginLeft) {
1461
+ el.style.removeProperty('margin-left');
1462
+ (0, dom_1.toggleClass)(el, cls, false);
1463
+ }
1464
+ else {
1465
+ el.style.marginLeft = '0px';
1466
+ (0, dom_1.toggleClass)(el, cls, true);
1467
+ var onEnd_1 = function () {
1468
+ el.style.removeProperty('margin-left');
1469
+ (0, dom_1.toggleClass)(el, cls, false);
1470
+ el.removeEventListener('transitionend', onEnd_1);
1471
+ clearTimeout(fallbackTimer_1);
1472
+ _this._pendingMarginCleanups.delete(el);
1473
+ };
1474
+ // Fallback in case transitionend never fires
1475
+ // (e.g. element removed from DOM mid-transition)
1476
+ var fallbackTimer_1 = setTimeout(onEnd_1, 300);
1477
+ _this._pendingMarginCleanups.set(el, onEnd_1);
1478
+ el.addEventListener('transitionend', onEnd_1);
1479
+ }
1480
+ };
535
1481
  var gapApplied = false;
536
- var _loop_1 = function (i) {
537
- var tab = this_1._tabs[i].value;
538
- if (tab.panel.id === this_1._animState.sourceTabId) {
539
- return "continue";
1482
+ try {
1483
+ // Reset all non-source chip margins first
1484
+ for (var _c = __values(this._tabGroupManager.chipRenderers), _d = _c.next(); !_d.done; _d = _c.next()) {
1485
+ var _e = __read(_d.value, 2), groupId = _e[0], entry = _e[1];
1486
+ if (groupId === this._animState.sourceTabGroupId)
1487
+ continue;
1488
+ clearMargin(entry.chip.element);
1489
+ }
1490
+ }
1491
+ catch (e_17_1) { e_17 = { error: e_17_1 }; }
1492
+ finally {
1493
+ try {
1494
+ if (_d && !_d.done && (_b = _c.return)) _b.call(_c);
1495
+ }
1496
+ finally { if (e_17) throw e_17.error; }
1497
+ }
1498
+ // Apply gap to chip if insertion is before a group
1499
+ if (chipToShift) {
1500
+ setMargin(chipToShift, "".concat(gapWidth, "px"));
1501
+ gapApplied = true;
1502
+ }
1503
+ for (var i = 0; i < this._tabs.length; i++) {
1504
+ var tab = this._tabs[i].value;
1505
+ if (tab.panel.id === this._animState.sourceTabId) {
1506
+ continue;
1507
+ }
1508
+ if (sourceGroupPanelIds === null || sourceGroupPanelIds === void 0 ? void 0 : sourceGroupPanelIds.has(tab.panel.id)) {
1509
+ continue;
540
1510
  }
541
1511
  if (!gapApplied && i >= insertionIndex) {
542
- tab.element.style.marginLeft = "".concat(gapWidth, "px");
543
- (0, dom_1.toggleClass)(tab.element, 'dv-tab--shifting', true);
1512
+ setMargin(tab.element, "".concat(gapWidth, "px"));
544
1513
  gapApplied = true;
545
1514
  }
546
1515
  else {
547
- // Keep shifting class while margin animates back to 0,
548
- // then remove both once the transition ends
549
- if (tab.element.style.marginLeft) {
550
- tab.element.style.marginLeft = '0px';
551
- (0, dom_1.toggleClass)(tab.element, 'dv-tab--shifting', true);
552
- var onEnd_1 = function () {
553
- tab.element.style.removeProperty('margin-left');
554
- (0, dom_1.toggleClass)(tab.element, 'dv-tab--shifting', false);
555
- tab.element.removeEventListener('transitionend', onEnd_1);
556
- };
557
- tab.element.addEventListener('transitionend', onEnd_1);
558
- }
559
- else {
560
- (0, dom_1.toggleClass)(tab.element, 'dv-tab--shifting', false);
561
- }
1516
+ clearMargin(tab.element);
562
1517
  }
563
- };
564
- var this_1 = this;
565
- for (var i = 0; i < this._tabs.length; i++) {
566
- _loop_1(i);
567
1518
  }
1519
+ // Reposition underlines to follow shifted chips/tabs
1520
+ this._tabGroupManager.trackUnderlines();
568
1521
  };
569
1522
  Tabs.prototype.resetTabTransforms = function () {
570
- var e_6, _a;
1523
+ var e_18, _a, e_19, _b, e_20, _c;
571
1524
  try {
572
- for (var _b = __values(this._tabs), _c = _b.next(); !_c.done; _c = _b.next()) {
573
- var tab = _c.value;
1525
+ // Cancel any pending margin transitionend listeners
1526
+ for (var _d = __values(this._pendingMarginCleanups), _e = _d.next(); !_e.done; _e = _d.next()) {
1527
+ var _f = __read(_e.value, 2), cleanup = _f[1];
1528
+ cleanup();
1529
+ }
1530
+ }
1531
+ catch (e_18_1) { e_18 = { error: e_18_1 }; }
1532
+ finally {
1533
+ try {
1534
+ if (_e && !_e.done && (_a = _d.return)) _a.call(_d);
1535
+ }
1536
+ finally { if (e_18) throw e_18.error; }
1537
+ }
1538
+ this._pendingMarginCleanups.clear();
1539
+ try {
1540
+ for (var _g = __values(this._tabs), _h = _g.next(); !_h.done; _h = _g.next()) {
1541
+ var tab = _h.value;
574
1542
  tab.value.element.style.removeProperty('margin-left');
1543
+ tab.value.element.style.removeProperty('margin-right');
1544
+ tab.value.element.style.removeProperty('margin-top');
1545
+ tab.value.element.style.removeProperty('margin-bottom');
575
1546
  tab.value.element.style.removeProperty('transform');
576
1547
  (0, dom_1.toggleClass)(tab.value.element, 'dv-tab--shifting', false);
577
1548
  }
578
1549
  }
579
- catch (e_6_1) { e_6 = { error: e_6_1 }; }
1550
+ catch (e_19_1) { e_19 = { error: e_19_1 }; }
580
1551
  finally {
581
1552
  try {
582
- if (_c && !_c.done && (_a = _b.return)) _a.call(_b);
1553
+ if (_h && !_h.done && (_b = _g.return)) _b.call(_g);
583
1554
  }
584
- finally { if (e_6) throw e_6.error; }
1555
+ finally { if (e_19) throw e_19.error; }
1556
+ }
1557
+ try {
1558
+ for (var _j = __values(this._tabGroupManager.chipRenderers), _k = _j.next(); !_k.done; _k = _j.next()) {
1559
+ var _l = __read(_k.value, 2), entry = _l[1];
1560
+ entry.chip.element.style.removeProperty('margin-left');
1561
+ (0, dom_1.toggleClass)(entry.chip.element, 'dv-tab-group-chip--shifting', false);
1562
+ }
1563
+ }
1564
+ catch (e_20_1) { e_20 = { error: e_20_1 }; }
1565
+ finally {
1566
+ try {
1567
+ if (_k && !_k.done && (_c = _j.return)) _c.call(_j);
1568
+ }
1569
+ finally { if (e_20) throw e_20.error; }
1570
+ }
1571
+ this._tabGroupManager.positionUnderlines();
1572
+ };
1573
+ /**
1574
+ * Commit a group-drag drop: clear drag classes, move the group
1575
+ * in the model, and run a FLIP animation.
1576
+ */
1577
+ Tabs.prototype._commitGroupMove = function (sourceTabGroupId, insertionIndex) {
1578
+ var _a, _b, _c;
1579
+ // Read transfer data BEFORE disposing cleanup — disposing
1580
+ // _chipDragCleanup clears the global LocalSelectionTransfer
1581
+ // singleton which getPanelData() reads from.
1582
+ var data = (0, dataTransfer_1.getPanelData)();
1583
+ (_a = this._chipDragCleanup) === null || _a === void 0 ? void 0 : _a.dispose();
1584
+ this._chipDragCleanup = null;
1585
+ // Check if the tab group exists in this group (within-group reorder)
1586
+ // or in another group (cross-group move).
1587
+ var isLocal = this.group.model
1588
+ .getTabGroups()
1589
+ .some(function (tg) { return tg.id === sourceTabGroupId; });
1590
+ if (isLocal) {
1591
+ if (((_b = this.accessor.options.theme) === null || _b === void 0 ? void 0 : _b.tabAnimation) === 'smooth') {
1592
+ this._clearGroupDragClasses(sourceTabGroupId);
1593
+ var firstPositions = this.snapshotTabPositions();
1594
+ this.resetTabTransforms();
1595
+ this.group.model.moveTabGroup(sourceTabGroupId, insertionIndex);
1596
+ this.runFlipAnimation(firstPositions, '', false);
1597
+ }
1598
+ else {
1599
+ this._tabGroupManager.skipNextCollapseAnimation = true;
1600
+ this.group.model.moveTabGroup(sourceTabGroupId, insertionIndex);
1601
+ }
1602
+ }
1603
+ else if (data) {
1604
+ // Cross-group: delegate to the component-level move which
1605
+ // handles panel transfer and tab group recreation.
1606
+ // Use the REAL tab group ID from transfer data, not the
1607
+ // potentially stale one from _animState.
1608
+ this.accessor.moveGroupOrPanel({
1609
+ from: {
1610
+ groupId: data.groupId,
1611
+ tabGroupId: (_c = data.tabGroupId) !== null && _c !== void 0 ? _c : sourceTabGroupId,
1612
+ },
1613
+ to: {
1614
+ group: this.group,
1615
+ position: 'center',
1616
+ index: insertionIndex,
1617
+ },
1618
+ });
585
1619
  }
586
1620
  };
1621
+ Tabs.prototype._clearGroupDragClasses = function (sourceTabGroupId) {
1622
+ var chipEntry = this._tabGroupManager.chipRenderers.get(sourceTabGroupId);
1623
+ if (chipEntry) {
1624
+ this._removeClassInstantlyBatch([chipEntry.chip.element], 'dv-tab-group-chip--dragging');
1625
+ }
1626
+ this._removeClassInstantlyBatch(this._tabs.map(function (t) { return t.value.element; }), 'dv-tab--dragging');
1627
+ // Restore underline
1628
+ var underline = this._tabGroupManager.groupUnderlines.get(sourceTabGroupId);
1629
+ if (underline) {
1630
+ underline.style.removeProperty('display');
1631
+ }
1632
+ // The subsequent moveTabGroup will re-create tabs and call
1633
+ // updateTabGroups → _updateTabGroupClasses. For collapsed groups
1634
+ // the new tabs don't have dv-tab--group-collapsed yet, which
1635
+ // would trigger the collapse animation. Skip it.
1636
+ this._tabGroupManager.skipNextCollapseAnimation = true;
1637
+ };
587
1638
  Tabs.prototype.resetDragAnimation = function () {
588
- var e_7, _a;
1639
+ var e_21, _a;
1640
+ var _b, _c;
1641
+ this._pendingCollapse = false;
589
1642
  this.resetTabTransforms();
1643
+ // Clear drag-collapse classes instantly (no transition)
1644
+ if ((_b = this._animState) === null || _b === void 0 ? void 0 : _b.sourceTabGroupId) {
1645
+ this._clearGroupDragClasses(this._animState.sourceTabGroupId);
1646
+ }
1647
+ else {
1648
+ this._removeClassInstantlyBatch(this._tabs.map(function (t) { return t.value.element; }), 'dv-tab--dragging');
1649
+ }
590
1650
  this._animState = null;
1651
+ (_c = this._chipDragCleanup) === null || _c === void 0 ? void 0 : _c.dispose();
1652
+ this._chipDragCleanup = null;
591
1653
  try {
592
- for (var _b = __values(this._tabs), _c = _b.next(); !_c.done; _c = _b.next()) {
593
- var tab = _c.value;
594
- (0, dom_1.toggleClass)(tab.value.element, 'dv-tab--dragging', false);
1654
+ // Restore any hidden underlines from group drags
1655
+ for (var _d = __values(this._tabGroupManager.groupUnderlines), _e = _d.next(); !_e.done; _e = _d.next()) {
1656
+ var _f = __read(_e.value, 2), el = _f[1];
1657
+ el.style.removeProperty('display');
595
1658
  }
596
1659
  }
597
- catch (e_7_1) { e_7 = { error: e_7_1 }; }
1660
+ catch (e_21_1) { e_21 = { error: e_21_1 }; }
598
1661
  finally {
599
1662
  try {
600
- if (_c && !_c.done && (_a = _b.return)) _a.call(_b);
1663
+ if (_e && !_e.done && (_a = _d.return)) _a.call(_d);
601
1664
  }
602
- finally { if (e_7) throw e_7.error; }
1665
+ finally { if (e_21) throw e_21.error; }
603
1666
  }
604
1667
  };
605
1668
  Tabs.prototype.runFlipAnimation = function (firstPositions, sourceTabId, isCrossGroup, animRange) {
606
1669
  var _this = this;
607
1670
  if (isCrossGroup === void 0) { isCrossGroup = false; }
1671
+ var isVertical = this._direction === 'vertical';
608
1672
  var hasAnimation = false;
609
1673
  for (var i = 0; i < this._tabs.length; i++) {
610
1674
  var tab = this._tabs[i];
611
1675
  var panelId = tab.value.panel.id;
612
1676
  if (panelId === sourceTabId) {
613
1677
  if (isCrossGroup) {
614
- // Newly inserted tab: slide in from the right
1678
+ // Newly inserted tab: slide in from the end
615
1679
  var rect = tab.value.element.getBoundingClientRect();
616
- tab.value.element.style.transform = "translateX(".concat(rect.width, "px)");
1680
+ tab.value.element.style.transform = isVertical
1681
+ ? "translateY(".concat(rect.height, "px)")
1682
+ : "translateX(".concat(rect.width, "px)");
617
1683
  (0, dom_1.toggleClass)(tab.value.element, 'dv-tab--shifting', true);
618
1684
  hasAnimation = true;
619
1685
  }
@@ -629,11 +1695,15 @@ var Tabs = /** @class */ (function (_super) {
629
1695
  continue;
630
1696
  }
631
1697
  var lastRect = tab.value.element.getBoundingClientRect();
632
- var deltaX = firstRect.left - lastRect.left;
633
- if (Math.abs(deltaX) < 1) {
1698
+ var delta = isVertical
1699
+ ? firstRect.top - lastRect.top
1700
+ : firstRect.left - lastRect.left;
1701
+ if (Math.abs(delta) < 1) {
634
1702
  continue;
635
1703
  }
636
- tab.value.element.style.transform = "translateX(".concat(deltaX, "px)");
1704
+ tab.value.element.style.transform = isVertical
1705
+ ? "translateY(".concat(delta, "px)")
1706
+ : "translateX(".concat(delta, "px)");
637
1707
  (0, dom_1.toggleClass)(tab.value.element, 'dv-tab--shifting', true);
638
1708
  hasAnimation = true;
639
1709
  }
@@ -641,41 +1711,54 @@ var Tabs = /** @class */ (function (_super) {
641
1711
  return;
642
1712
  }
643
1713
  requestAnimationFrame(function () {
644
- var e_8, _a;
1714
+ var e_22, _a;
1715
+ var _b;
645
1716
  try {
646
- for (var _b = __values(_this._tabs), _c = _b.next(); !_c.done; _c = _b.next()) {
647
- var tab = _c.value;
1717
+ for (var _c = __values(_this._tabs), _d = _c.next(); !_d.done; _d = _c.next()) {
1718
+ var tab = _d.value;
648
1719
  if (tab.value.element.style.transform) {
649
1720
  tab.value.element.style.transform = '';
650
1721
  }
651
1722
  }
652
1723
  }
653
- catch (e_8_1) { e_8 = { error: e_8_1 }; }
1724
+ catch (e_22_1) { e_22 = { error: e_22_1 }; }
654
1725
  finally {
655
1726
  try {
656
- if (_c && !_c.done && (_a = _b.return)) _a.call(_b);
1727
+ if (_d && !_d.done && (_a = _c.return)) _a.call(_c);
657
1728
  }
658
- finally { if (e_8) throw e_8.error; }
1729
+ finally { if (e_22) throw e_22.error; }
659
1730
  }
1731
+ // Track underlines during the FLIP transition so they
1732
+ // follow tabs as they slide to their final positions.
1733
+ _this._tabGroupManager.trackUnderlines();
1734
+ // Clean up any previous flip transition listener
1735
+ (_b = _this._flipTransitionCleanup) === null || _b === void 0 ? void 0 : _b.call(_this);
660
1736
  var onTransitionEnd = function (event) {
661
- var e_9, _a;
1737
+ var e_23, _a;
662
1738
  if (event.propertyName === 'transform') {
663
- _this._tabsList.removeEventListener('transitionend', onTransitionEnd);
1739
+ cleanup();
664
1740
  try {
665
1741
  for (var _b = __values(_this._tabs), _c = _b.next(); !_c.done; _c = _b.next()) {
666
1742
  var tab = _c.value;
667
1743
  (0, dom_1.toggleClass)(tab.value.element, 'dv-tab--shifting', false);
668
1744
  }
669
1745
  }
670
- catch (e_9_1) { e_9 = { error: e_9_1 }; }
1746
+ catch (e_23_1) { e_23 = { error: e_23_1 }; }
671
1747
  finally {
672
1748
  try {
673
1749
  if (_c && !_c.done && (_a = _b.return)) _a.call(_b);
674
1750
  }
675
- finally { if (e_9) throw e_9.error; }
1751
+ finally { if (e_23) throw e_23.error; }
676
1752
  }
1753
+ // Final reposition after animation settles
1754
+ _this._tabGroupManager.positionUnderlines();
677
1755
  }
678
1756
  };
1757
+ var cleanup = function () {
1758
+ _this._tabsList.removeEventListener('transitionend', onTransitionEnd);
1759
+ _this._flipTransitionCleanup = null;
1760
+ };
1761
+ _this._flipTransitionCleanup = cleanup;
679
1762
  _this._tabsList.addEventListener('transitionend', onTransitionEnd);
680
1763
  });
681
1764
  };