dockview-core 5.1.0 → 6.0.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.
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 +8 -1
  17. package/dist/cjs/dockview/components/tab/tab.js +94 -6
  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 +67 -0
  26. package/dist/cjs/dockview/components/titlebar/tabs.js +1464 -34
  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 +97 -2
  40. package/dist/cjs/dockview/options.js +10 -5
  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 +5188 -729
  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 +5186 -727
  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 +8 -1
  75. package/dist/esm/dockview/components/tab/tab.js +96 -6
  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 +67 -0
  83. package/dist/esm/dockview/components/titlebar/tabs.js +1212 -25
  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 +97 -2
  97. package/dist/esm/dockview/options.js +5 -0
  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 +5182 -753
  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 +5168 -753
  116. package/dist/styles/dockview.css +1968 -195
  117. package/package.json +5 -1
@@ -9,6 +9,7 @@ import { DockviewHeaderDirection } from '../../options';
9
9
  export interface TabDropIndexEvent {
10
10
  readonly event: DragEvent;
11
11
  readonly index: number;
12
+ readonly targetTabGroupId?: string | null;
12
13
  }
13
14
  export interface TabDragEvent {
14
15
  readonly nativeEvent: DragEvent;
@@ -41,6 +42,8 @@ export interface ITabsContainer extends IDisposable {
41
42
  show(): void;
42
43
  hide(): void;
43
44
  updateDragAndDropState(): void;
45
+ updateTabGroups(): void;
46
+ refreshTabGroupAccent(): void;
44
47
  }
45
48
  export declare class TabsContainer extends CompositeDisposable implements ITabsContainer {
46
49
  private readonly accessor;
@@ -58,6 +61,7 @@ export declare class TabsContainer extends CompositeDisposable implements ITabsC
58
61
  private _direction;
59
62
  private dropdownPart;
60
63
  private _overflowTabs;
64
+ private _overflowTabGroups;
61
65
  private readonly _dropdownDisposable;
62
66
  private readonly _onDrop;
63
67
  readonly onDrop: Event<TabDropIndexEvent>;
@@ -89,4 +93,6 @@ export declare class TabsContainer extends CompositeDisposable implements ITabsC
89
93
  private updateClassnames;
90
94
  private toggleDropdown;
91
95
  updateDragAndDropState(): void;
96
+ updateTabGroups(): void;
97
+ refreshTabGroupAccent(): void;
92
98
  }
@@ -6,6 +6,7 @@ import { DockviewWillShowOverlayLocationEvent } from '../../events';
6
6
  import { getPanelData } from '../../../dnd/dataTransfer';
7
7
  import { Tabs } from './tabs';
8
8
  import { createDropdownElementHandle, } from './tabOverflowControl';
9
+ import { applyTabGroupAccent } from '../../tabGroupAccent';
9
10
  export class TabsContainer extends CompositeDisposable {
10
11
  get onTabDragStart() {
11
12
  return this.tabs.onTabDragStart;
@@ -50,6 +51,7 @@ export class TabsContainer extends CompositeDisposable {
50
51
  this._direction = 'horizontal';
51
52
  this.dropdownPart = null;
52
53
  this._overflowTabs = [];
54
+ this._overflowTabGroups = [];
53
55
  this._dropdownDisposable = new MutableDisposable();
54
56
  this._onDrop = new Emitter();
55
57
  this.onDrop = this._onDrop.event;
@@ -70,11 +72,13 @@ export class TabsContainer extends CompositeDisposable {
70
72
  showTabsOverflowControl: !accessor.options.disableTabsOverflowList,
71
73
  });
72
74
  this.voidContainer = new VoidContainer(this.accessor, this.group);
75
+ this.tabs.voidContainer = this.voidContainer.element;
73
76
  this._element.appendChild(this.preActionsContainer);
74
77
  this._element.appendChild(this.tabs.element);
75
78
  this._element.appendChild(this.leftActionsContainer);
76
79
  this._element.appendChild(this.voidContainer.element);
77
80
  this._element.appendChild(this.rightActionsContainer);
81
+ this.tabs.setExtendedDropZone(this._element);
78
82
  this.addDisposables(this.tabs.onDrop((e) => this._onDrop.fire(e)), this.tabs.onWillShowOverlay((e) => this._onWillShowOverlay.fire(e)), accessor.onDidOptionsChange(() => {
79
83
  this.tabs.showTabsOverflowControl =
80
84
  !accessor.options.disableTabsOverflowList;
@@ -86,6 +90,10 @@ export class TabsContainer extends CompositeDisposable {
86
90
  group: this.group,
87
91
  });
88
92
  }), this.voidContainer.onDrop((event) => {
93
+ // If an active group drag is in progress, let Tabs handle it
94
+ if (this.tabs.handleVoidDrop()) {
95
+ return;
96
+ }
89
97
  this._onDrop.fire({
90
98
  event: event.nativeEvent,
91
99
  index: this.tabs.size,
@@ -98,6 +106,25 @@ export class TabsContainer extends CompositeDisposable {
98
106
  group: this.group,
99
107
  getData: getPanelData,
100
108
  }));
109
+ }), addDisposableListener(this.leftActionsContainer, 'dragleave', (event) => {
110
+ const related = event.relatedTarget;
111
+ if (!this.leftActionsContainer.contains(related) &&
112
+ !this._element.contains(related)) {
113
+ // Left the header entirely
114
+ this.tabs.clearExternalAnimState();
115
+ }
116
+ }), addDisposableListener(this.voidContainer.element, 'dragleave', (event) => {
117
+ const related = event.relatedTarget;
118
+ if (!this.voidContainer.element.contains(related)) {
119
+ if (this._element.contains(related)) {
120
+ // Moved to another part of the header — keep state
121
+ this.tabs.setExternalInsertionIndex(null);
122
+ }
123
+ else {
124
+ // Left the header entirely
125
+ this.tabs.clearExternalAnimState();
126
+ }
127
+ }
101
128
  }), addDisposableListener(this.voidContainer.element, 'pointerdown', (event) => {
102
129
  if (event.defaultPrevented) {
103
130
  return;
@@ -105,7 +132,8 @@ export class TabsContainer extends CompositeDisposable {
105
132
  const isFloatingGroupsEnabled = !this.accessor.options.disableFloatingGroups;
106
133
  if (isFloatingGroupsEnabled &&
107
134
  event.shiftKey &&
108
- this.group.api.location.type !== 'floating') {
135
+ this.group.api.location.type !== 'floating' &&
136
+ this.group.api.location.type !== 'edge') {
109
137
  event.preventDefault();
110
138
  const { top, left } = this.element.getBoundingClientRect();
111
139
  const { top: rootTop, left: rootLeft } = this.accessor.element.getBoundingClientRect();
@@ -192,19 +220,22 @@ export class TabsContainer extends CompositeDisposable {
192
220
  }
193
221
  toggleDropdown(options) {
194
222
  const tabs = options.reset ? [] : options.tabs;
223
+ const tabGroups = options.reset ? [] : options.tabGroups;
195
224
  this._overflowTabs = tabs;
196
- if (this._overflowTabs.length > 0 && this.dropdownPart) {
197
- this.dropdownPart.update({ tabs: tabs.length });
225
+ this._overflowTabGroups = tabGroups;
226
+ const totalCount = this._overflowTabs.length;
227
+ if (totalCount > 0 && this.dropdownPart) {
228
+ this.dropdownPart.update({ tabs: totalCount });
198
229
  return;
199
230
  }
200
- if (this._overflowTabs.length === 0) {
231
+ if (totalCount === 0) {
201
232
  this._dropdownDisposable.dispose();
202
233
  return;
203
234
  }
204
235
  const root = document.createElement('div');
205
236
  root.className = 'dv-tabs-overflow-dropdown-root';
206
237
  const part = createDropdownElementHandle();
207
- part.update({ tabs: tabs.length });
238
+ part.update({ tabs: totalCount });
208
239
  this.dropdownPart = part;
209
240
  root.appendChild(part.element);
210
241
  this.rightActionsContainer.prepend(root);
@@ -219,7 +250,58 @@ export class TabsContainer extends CompositeDisposable {
219
250
  const el = document.createElement('div');
220
251
  el.style.overflow = 'auto';
221
252
  el.className = 'dv-tabs-overflow-container';
253
+ // Build lookup: panelId → tabGroup for overflow groups
254
+ const overflowGroupSet = new Set(this._overflowTabGroups);
255
+ const allTabGroups = this.group.model.getTabGroups();
256
+ const panelToGroup = new Map();
257
+ for (const tg of allTabGroups) {
258
+ if (overflowGroupSet.has(tg.id)) {
259
+ for (const pid of tg.panelIds) {
260
+ panelToGroup.set(pid, tg);
261
+ }
262
+ }
263
+ }
264
+ // Track which groups have already been rendered
265
+ const renderedGroups = new Set();
222
266
  for (const tab of this.tabs.tabs.filter((tab) => this._overflowTabs.includes(tab.panel.id))) {
267
+ const tg = panelToGroup.get(tab.panel.id);
268
+ // If this tab belongs to an overflow group, render the
269
+ // group header before its first member tab.
270
+ if (tg && !renderedGroups.has(tg.id)) {
271
+ renderedGroups.add(tg.id);
272
+ const groupHeader = document.createElement('div');
273
+ groupHeader.className = 'dv-tabs-overflow-group-header';
274
+ const colorDot = document.createElement('span');
275
+ colorDot.className = 'dv-tabs-overflow-group-color';
276
+ applyTabGroupAccent(colorDot, tg.color, this.accessor.tabGroupColorPalette);
277
+ groupHeader.appendChild(colorDot);
278
+ const labelSpan = document.createElement('span');
279
+ labelSpan.className = 'dv-tabs-overflow-group-label';
280
+ labelSpan.textContent = tg.label || tg.id;
281
+ groupHeader.appendChild(labelSpan);
282
+ if (tg.collapsed) {
283
+ const badge = document.createElement('span');
284
+ badge.className =
285
+ 'dv-tabs-overflow-group-collapsed-badge';
286
+ badge.textContent = `${tg.panelIds.length}`;
287
+ groupHeader.appendChild(badge);
288
+ }
289
+ groupHeader.addEventListener('click', () => {
290
+ this.accessor
291
+ .getPopupServiceForGroup(this.group)
292
+ .close();
293
+ if (tg.collapsed) {
294
+ tg.expand();
295
+ }
296
+ // Activate the first panel in the group
297
+ const firstPanelId = tg.panelIds[0];
298
+ if (firstPanelId) {
299
+ const panel = this.group.panels.find((p) => p.id === firstPanelId);
300
+ panel === null || panel === void 0 ? void 0 : panel.api.setActive();
301
+ }
302
+ });
303
+ el.appendChild(groupHeader);
304
+ }
223
305
  const panelObject = this.group.panels.find((panel) => panel === tab.panel);
224
306
  const tabComponent = panelObject.view.createTabRenderer('headerOverflow');
225
307
  const child = tabComponent.element;
@@ -227,11 +309,19 @@ export class TabsContainer extends CompositeDisposable {
227
309
  toggleClass(wrapper, 'dv-tab', true);
228
310
  toggleClass(wrapper, 'dv-active-tab', panelObject.api.isActive);
229
311
  toggleClass(wrapper, 'dv-inactive-tab', !panelObject.api.isActive);
312
+ if (tg) {
313
+ toggleClass(wrapper, 'dv-tab--grouped', true);
314
+ }
230
315
  wrapper.addEventListener('click', (event) => {
231
- this.accessor.popupService.close();
316
+ this.accessor
317
+ .getPopupServiceForGroup(this.group)
318
+ .close();
232
319
  if (event.defaultPrevented) {
233
320
  return;
234
321
  }
322
+ if (tg === null || tg === void 0 ? void 0 : tg.collapsed) {
323
+ tg.expand();
324
+ }
235
325
  tab.element.scrollIntoView();
236
326
  tab.panel.api.setActive();
237
327
  });
@@ -239,7 +329,9 @@ export class TabsContainer extends CompositeDisposable {
239
329
  el.appendChild(wrapper);
240
330
  }
241
331
  const relativeParent = findRelativeZIndexParent(root);
242
- this.accessor.popupService.openPopover(el, {
332
+ this.accessor
333
+ .getPopupServiceForGroup(this.group)
334
+ .openPopover(el, {
243
335
  x: event.clientX,
244
336
  y: event.clientY,
245
337
  zIndex: (relativeParent === null || relativeParent === void 0 ? void 0 : relativeParent.style.zIndex)
@@ -252,4 +344,10 @@ export class TabsContainer extends CompositeDisposable {
252
344
  this.tabs.updateDragAndDropState();
253
345
  this.voidContainer.updateDragAndDropState();
254
346
  }
347
+ updateTabGroups() {
348
+ this.tabs.updateTabGroups();
349
+ }
350
+ refreshTabGroupAccent() {
351
+ this.tabs.refreshTabGroupAccent();
352
+ }
255
353
  }
@@ -0,0 +1,10 @@
1
+ import { DockviewComponent } from './dockviewComponent';
2
+ import { DockviewGroupPanel } from './dockviewGroupPanel';
3
+ import { IDockviewPanel } from './dockviewPanel';
4
+ import { ITabGroup } from './tabGroup';
5
+ export declare class ContextMenuController {
6
+ private readonly accessor;
7
+ constructor(accessor: DockviewComponent);
8
+ show(panel: IDockviewPanel, group: DockviewGroupPanel, event: MouseEvent): void;
9
+ showForChip(tabGroup: ITabGroup, group: DockviewGroupPanel, event: MouseEvent): void;
10
+ }
@@ -0,0 +1,213 @@
1
+ import { findRelativeZIndexParent } from '../dom';
2
+ function popoverZIndexFor(target) {
3
+ if (!(target instanceof HTMLElement)) {
4
+ return undefined;
5
+ }
6
+ // Floating overlays live in the shell as siblings of the popover anchor
7
+ // and the AriaLevelTracker sets their inline z-index. Without this, a
8
+ // popover opened from inside a floating group would render behind it
9
+ // because they share the shell stacking context.
10
+ const relativeParent = findRelativeZIndexParent(target);
11
+ return (relativeParent === null || relativeParent === void 0 ? void 0 : relativeParent.style.zIndex)
12
+ ? `calc(${relativeParent.style.zIndex} * 2)`
13
+ : undefined;
14
+ }
15
+ let _nextId = 0;
16
+ const nextContextMenuItemId = () => `dv-ctx-menu-item-${_nextId++}`;
17
+ function isItemConfig(item) {
18
+ return typeof item === 'object';
19
+ }
20
+ function buildItem(label, close, action, disabled) {
21
+ const el = document.createElement('div');
22
+ el.className = 'dv-context-menu-item';
23
+ el.setAttribute('role', 'menuitem');
24
+ if (disabled) {
25
+ el.classList.add('dv-context-menu-item--disabled');
26
+ el.setAttribute('aria-disabled', 'true');
27
+ }
28
+ el.textContent = label;
29
+ if (!disabled) {
30
+ el.addEventListener('click', () => {
31
+ action();
32
+ close();
33
+ });
34
+ }
35
+ return el;
36
+ }
37
+ function buildSeparator() {
38
+ const el = document.createElement('div');
39
+ el.className = 'dv-context-menu-separator';
40
+ el.setAttribute('role', 'separator');
41
+ return el;
42
+ }
43
+ function buildRenameInput(tabGroup) {
44
+ const wrapper = document.createElement('div');
45
+ wrapper.className = 'dv-context-menu-rename';
46
+ const input = document.createElement('input');
47
+ input.className = 'dv-context-menu-rename-input';
48
+ input.type = 'text';
49
+ input.placeholder = 'Name This Group';
50
+ input.value = tabGroup.label;
51
+ input.addEventListener('input', () => {
52
+ tabGroup.setLabel(input.value);
53
+ });
54
+ input.addEventListener('keydown', (e) => {
55
+ if (e.key !== 'Escape' && e.key !== 'Enter') {
56
+ e.stopPropagation();
57
+ }
58
+ });
59
+ input.addEventListener('click', (e) => {
60
+ e.stopPropagation();
61
+ });
62
+ wrapper.appendChild(input);
63
+ requestAnimationFrame(() => {
64
+ input.focus();
65
+ input.select();
66
+ });
67
+ return wrapper;
68
+ }
69
+ function buildColorPicker(tabGroup, palette) {
70
+ const wrapper = document.createElement('div');
71
+ wrapper.className = 'dv-context-menu-color-picker';
72
+ if (!palette.enabled) {
73
+ // Opt-out: render no swatches. Returning a wrapper rather than null
74
+ // keeps the call site simple; the wrapper is empty and visually inert.
75
+ return wrapper;
76
+ }
77
+ for (const entry of palette.entries()) {
78
+ const swatch = document.createElement('div');
79
+ swatch.className = 'dv-context-menu-color-swatch';
80
+ // Use a CSS custom property rather than setting `backgroundColor`
81
+ // directly: the IDL setter validates the value against a color
82
+ // grammar and rejects `var(...)` references in some environments
83
+ // (notably jsdom; some browsers have historically had similar
84
+ // quirks). The matching SCSS rule reads the var at use time.
85
+ swatch.style.setProperty('--dv-tab-group-color', entry.value);
86
+ if (entry.label) {
87
+ swatch.title = entry.label;
88
+ }
89
+ if (tabGroup.color === entry.id) {
90
+ swatch.classList.add('dv-context-menu-color-swatch--selected');
91
+ }
92
+ swatch.addEventListener('click', () => {
93
+ tabGroup.setColor(entry.id);
94
+ });
95
+ wrapper.appendChild(swatch);
96
+ }
97
+ return wrapper;
98
+ }
99
+ export class ContextMenuController {
100
+ constructor(accessor) {
101
+ this.accessor = accessor;
102
+ }
103
+ show(panel, group, event) {
104
+ var _a, _b;
105
+ if (!this.accessor.options.getTabContextMenuItems) {
106
+ return;
107
+ }
108
+ const items = this.accessor.options.getTabContextMenuItems({
109
+ panel,
110
+ group,
111
+ api: this.accessor.api,
112
+ event,
113
+ });
114
+ if (items.length === 0) {
115
+ return;
116
+ }
117
+ event.preventDefault();
118
+ const popupService = this.accessor.getPopupServiceForGroup(group);
119
+ const close = () => popupService.close();
120
+ const menuEl = document.createElement('div');
121
+ menuEl.className = 'dv-context-menu';
122
+ menuEl.setAttribute('role', 'menu');
123
+ for (const item of items) {
124
+ if (item === 'separator') {
125
+ menuEl.appendChild(buildSeparator());
126
+ }
127
+ else if (item === 'close') {
128
+ menuEl.appendChild(buildItem('Close', close, () => panel.api.close()));
129
+ }
130
+ else if (item === 'closeOthers') {
131
+ menuEl.appendChild(buildItem('Close Others', close, () => {
132
+ group.panels
133
+ .filter((p) => p !== panel)
134
+ .forEach((p) => p.api.close());
135
+ }));
136
+ }
137
+ else if (item === 'closeAll') {
138
+ menuEl.appendChild(buildItem('Close All', close, () => {
139
+ [...group.panels].forEach((p) => p.api.close());
140
+ }));
141
+ }
142
+ else if (isItemConfig(item) && item.element) {
143
+ menuEl.appendChild(item.element);
144
+ }
145
+ else if (isItemConfig(item) && item.component) {
146
+ const renderer = (_b = (_a = this.accessor.options).createContextMenuItemComponent) === null || _b === void 0 ? void 0 : _b.call(_a, {
147
+ id: nextContextMenuItemId(),
148
+ component: item.component,
149
+ });
150
+ if (renderer) {
151
+ renderer.init({
152
+ panel,
153
+ group,
154
+ api: this.accessor.api,
155
+ close,
156
+ componentProps: item.componentProps,
157
+ });
158
+ menuEl.appendChild(renderer.element);
159
+ }
160
+ }
161
+ else if (isItemConfig(item) && item.label) {
162
+ menuEl.appendChild(buildItem(item.label, close, () => { var _a; return (_a = item.action) === null || _a === void 0 ? void 0 : _a.call(item); }, item.disabled));
163
+ }
164
+ }
165
+ popupService.openPopover(menuEl, {
166
+ x: event.clientX,
167
+ y: event.clientY,
168
+ zIndex: popoverZIndexFor(event.target),
169
+ });
170
+ }
171
+ showForChip(tabGroup, group, event) {
172
+ if (!this.accessor.options.getTabGroupChipContextMenuItems) {
173
+ return;
174
+ }
175
+ const items = this.accessor.options.getTabGroupChipContextMenuItems({
176
+ tabGroup,
177
+ group,
178
+ api: this.accessor.api,
179
+ event,
180
+ });
181
+ if (items.length === 0) {
182
+ return;
183
+ }
184
+ event.preventDefault();
185
+ const popupService = this.accessor.getPopupServiceForGroup(group);
186
+ const close = () => popupService.close();
187
+ const menuEl = document.createElement('div');
188
+ menuEl.className = 'dv-context-menu';
189
+ menuEl.setAttribute('role', 'menu');
190
+ for (const item of items) {
191
+ if (item === 'separator') {
192
+ menuEl.appendChild(buildSeparator());
193
+ }
194
+ else if (item === 'rename') {
195
+ menuEl.appendChild(buildRenameInput(tabGroup));
196
+ }
197
+ else if (item === 'colorPicker') {
198
+ menuEl.appendChild(buildColorPicker(tabGroup, this.accessor.tabGroupColorPalette));
199
+ }
200
+ else if (isItemConfig(item) && item.element) {
201
+ menuEl.appendChild(item.element);
202
+ }
203
+ else if (isItemConfig(item) && item.label) {
204
+ menuEl.appendChild(buildItem(item.label, close, () => { var _a; return (_a = item.action) === null || _a === void 0 ? void 0 : _a.call(item); }, item.disabled));
205
+ }
206
+ }
207
+ popupService.openPopover(menuEl, {
208
+ x: event.clientX,
209
+ y: event.clientY,
210
+ zIndex: popoverZIndexFor(event.target),
211
+ });
212
+ }
213
+ }
@@ -8,7 +8,7 @@ import { BaseGrid, IBaseGrid } from '../gridview/baseComponentGridview';
8
8
  import { DockviewApi } from '../api/component.api';
9
9
  import { Orientation } from '../splitview/splitview';
10
10
  import { GroupOptions, GroupPanelViewState, DockviewDidDropEvent, DockviewWillDropEvent } from './dockviewGroupPanelModel';
11
- import { DockviewWillShowOverlayLocationEvent } from './events';
11
+ import { DockviewWillShowOverlayLocationEvent, DockviewTabGroupChangeEvent, DockviewTabGroupCollapsedChangeEvent, DockviewTabGroupPanelChangeEvent } from './events';
12
12
  import { DockviewGroupPanel } from './dockviewGroupPanel';
13
13
  import { Parameters } from '../panel/types';
14
14
  import { DockviewFloatingGroupPanel } from './dockviewFloatingGroupPanel';
@@ -16,7 +16,11 @@ import { GroupDragEvent, TabDragEvent } from './components/titlebar/tabsContaine
16
16
  import { AnchoredBox, AnchorPosition, Box } from '../types';
17
17
  import { DockviewPanelRenderer, OverlayRenderContainer } from '../overlay/overlayRenderContainer';
18
18
  import { PopupService } from './components/popupService';
19
+ import { ContextMenuController } from './contextMenu';
19
20
  import { DropTargetAnchorContainer } from '../dnd/dropTargetAnchorContainer';
21
+ import { EdgeGroupPosition, EdgeGroupOptions, SerializedEdgeGroups } from './dockviewShell';
22
+ import { DockviewGroupPanelApi } from '../api/dockviewGroupPanelApi';
23
+ import { TabGroupColorPalette } from './tabGroupAccent';
20
24
  export interface DockviewPopoutGroupOptions {
21
25
  /**
22
26
  * The position of the popout group
@@ -68,6 +72,7 @@ export interface SerializedDockview {
68
72
  activeGroup?: string;
69
73
  floatingGroups?: SerializedFloatingGroup[];
70
74
  popoutGroups?: SerializedPopoutGroup[];
75
+ edgeGroups?: SerializedEdgeGroups;
71
76
  }
72
77
  export interface MovePanelEvent {
73
78
  panel: IDockviewPanel;
@@ -87,6 +92,7 @@ type MoveGroupOrPanelOptions = {
87
92
  from: {
88
93
  groupId: string;
89
94
  panelId?: string;
95
+ tabGroupId?: string;
90
96
  };
91
97
  to: {
92
98
  group: DockviewGroupPanel;
@@ -103,7 +109,7 @@ export interface FloatingGroupOptions {
103
109
  width?: number;
104
110
  position?: AnchorPosition;
105
111
  }
106
- export interface FloatingGroupOptionsInternal extends FloatingGroupOptions {
112
+ interface FloatingGroupOptionsInternal extends FloatingGroupOptions {
107
113
  skipRemoveGroup?: boolean;
108
114
  inDragMode?: boolean;
109
115
  skipActiveGroup?: boolean;
@@ -145,7 +151,14 @@ export interface IDockviewComponent extends IBaseGrid<DockviewGroupPanel> {
145
151
  readonly onDidPopoutGroupSizeChange: Event<PopoutGroupChangeSizeEvent>;
146
152
  readonly onDidPopoutGroupPositionChange: Event<PopoutGroupChangePositionEvent>;
147
153
  readonly onDidOpenPopoutWindowFail: Event<void>;
154
+ readonly onDidCreateTabGroup: Event<DockviewTabGroupChangeEvent>;
155
+ readonly onDidDestroyTabGroup: Event<DockviewTabGroupChangeEvent>;
156
+ readonly onDidAddPanelToTabGroup: Event<DockviewTabGroupPanelChangeEvent>;
157
+ readonly onDidRemovePanelFromTabGroup: Event<DockviewTabGroupPanelChangeEvent>;
158
+ readonly onDidTabGroupChange: Event<DockviewTabGroupChangeEvent>;
159
+ readonly onDidTabGroupCollapsedChange: Event<DockviewTabGroupCollapsedChangeEvent>;
148
160
  readonly options: DockviewComponentOptions;
161
+ readonly tabGroupColorPalette: TabGroupColorPalette;
149
162
  updateOptions(options: DockviewOptions): void;
150
163
  moveGroupOrPanel(options: MoveGroupOrPanelOptions): void;
151
164
  moveGroup(options: MoveGroupOptions): void;
@@ -179,16 +192,24 @@ export interface IDockviewComponent extends IBaseGrid<DockviewGroupPanel> {
179
192
  fromJSON(data: any, options?: {
180
193
  reuseExistingPanels: boolean;
181
194
  }): void;
195
+ addEdgeGroup(position: EdgeGroupPosition, options: EdgeGroupOptions): DockviewGroupPanelApi;
196
+ getEdgeGroup(position: EdgeGroupPosition): DockviewGroupPanelApi | undefined;
197
+ setEdgeGroupVisible(position: EdgeGroupPosition, visible: boolean): void;
198
+ isEdgeGroupVisible(position: EdgeGroupPosition): boolean;
199
+ removeEdgeGroup(position: EdgeGroupPosition): void;
182
200
  }
183
201
  export declare class DockviewComponent extends BaseGrid<DockviewGroupPanel> implements IDockviewComponent {
184
202
  private readonly nextGroupId;
185
203
  private readonly _deserializer;
186
204
  private readonly _api;
187
205
  private _options;
206
+ private _tabGroupColorPalette;
188
207
  private _watermark;
189
- private readonly _themeClassnames;
208
+ private _shellThemeClassnames;
190
209
  readonly overlayRenderContainer: OverlayRenderContainer;
191
210
  readonly popupService: PopupService;
211
+ private readonly _popoutPopupServices;
212
+ readonly contextMenuController: ContextMenuController;
192
213
  readonly rootDropTargetContainer: DropTargetAnchorContainer;
193
214
  private readonly _onWillDragPanel;
194
215
  readonly onWillDragPanel: Event<TabDragEvent>;
@@ -218,8 +239,25 @@ export declare class DockviewComponent extends BaseGrid<DockviewGroupPanel> impl
218
239
  readonly onDidActivePanelChange: Event<IDockviewPanel | undefined>;
219
240
  private readonly _onDidMovePanel;
220
241
  readonly onDidMovePanel: Event<MovePanelEvent>;
242
+ private readonly _onDidCreateTabGroup;
243
+ readonly onDidCreateTabGroup: Event<DockviewTabGroupChangeEvent>;
244
+ private readonly _onDidDestroyTabGroup;
245
+ readonly onDidDestroyTabGroup: Event<DockviewTabGroupChangeEvent>;
246
+ private readonly _onDidAddPanelToTabGroup;
247
+ readonly onDidAddPanelToTabGroup: Event<DockviewTabGroupPanelChangeEvent>;
248
+ private readonly _onDidRemovePanelFromTabGroup;
249
+ readonly onDidRemovePanelFromTabGroup: Event<DockviewTabGroupPanelChangeEvent>;
250
+ private readonly _onDidTabGroupChange;
251
+ readonly onDidTabGroupChange: Event<DockviewTabGroupChangeEvent>;
252
+ private readonly _onDidTabGroupCollapsedChange;
253
+ readonly onDidTabGroupCollapsedChange: Event<DockviewTabGroupChangeEvent>;
221
254
  private readonly _onDidMaximizedGroupChange;
222
255
  readonly onDidMaximizedGroupChange: Event<DockviewMaximizedGroupChanged>;
256
+ private _shellManager;
257
+ private _floatingOverlayHost;
258
+ private _inShellLayout;
259
+ private readonly _edgeGroups;
260
+ private readonly _edgeGroupDisposables;
223
261
  private readonly _floatingGroups;
224
262
  private readonly _popoutGroups;
225
263
  private readonly _rootDropTarget;
@@ -236,6 +274,7 @@ export declare class DockviewComponent extends BaseGrid<DockviewGroupPanel> impl
236
274
  get totalPanels(): number;
237
275
  get panels(): IDockviewPanel[];
238
276
  get options(): DockviewComponentOptions;
277
+ get tabGroupColorPalette(): TabGroupColorPalette;
239
278
  get activePanel(): IDockviewPanel | undefined;
240
279
  get renderer(): DockviewPanelRenderer;
241
280
  get defaultHeaderPosition(): DockviewHeaderPosition;
@@ -248,11 +287,28 @@ export declare class DockviewComponent extends BaseGrid<DockviewGroupPanel> impl
248
287
  get popoutRestorationPromise(): Promise<void>;
249
288
  constructor(container: HTMLElement, options: DockviewComponentOptions);
250
289
  setVisible(panel: DockviewGroupPanel, visible: boolean): void;
290
+ /**
291
+ * Returns the {@link PopupService} that should host popovers (context
292
+ * menus, tab overflow menus) for the given group. Popout groups have their
293
+ * own service rooted in their popout window so the popover renders there
294
+ * and dismisses on events from that window.
295
+ */
296
+ getPopupServiceForGroup(group: DockviewGroupPanel): PopupService;
251
297
  addPopoutGroup(itemToPopout: DockviewPanel | DockviewGroupPanel, options?: DockviewPopoutGroupOptions): Promise<boolean>;
252
298
  addFloatingGroup(item: DockviewPanel | DockviewGroupPanel, options?: FloatingGroupOptionsInternal): void;
253
299
  private orthogonalize;
254
300
  updateOptions(options: Partial<DockviewComponentOptions>): void;
255
301
  layout(width: number, height: number, forceResize?: boolean | undefined): void;
302
+ private _syncFloatingOverlayHost;
303
+ private _layoutFromShell;
304
+ protected forceRelayout(): void;
305
+ addEdgeGroup(position: EdgeGroupPosition, options: EdgeGroupOptions): DockviewGroupPanelApi;
306
+ getEdgeGroup(position: EdgeGroupPosition): DockviewGroupPanelApi | undefined;
307
+ setEdgeGroupVisible(position: EdgeGroupPosition, visible: boolean): void;
308
+ isEdgeGroupVisible(position: EdgeGroupPosition): boolean;
309
+ removeEdgeGroup(position: EdgeGroupPosition): void;
310
+ setEdgeGroupCollapsed(group: DockviewGroupPanel, collapsed: boolean): void;
311
+ isEdgeGroupCollapsed(group: DockviewGroupPanel): boolean;
256
312
  private updateDragAndDropState;
257
313
  focus(): void;
258
314
  getGroupPanel(id: string): IDockviewPanel | undefined;
@@ -297,6 +353,7 @@ export declare class DockviewComponent extends BaseGrid<DockviewGroupPanel> impl
297
353
  private debouncedUpdateAllPositions;
298
354
  movingLock<T>(func: () => T): T;
299
355
  moveGroupOrPanel(options: MoveGroupOrPanelOptions): void;
356
+ private moveTabGroupToGroup;
300
357
  moveGroup(options: MoveGroupOptions): void;
301
358
  doSetGroupActive(group: DockviewGroupPanel | undefined): void;
302
359
  doSetGroupAndPanelActive(group: DockviewGroupPanel | undefined): void;