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.
- package/README.md +3 -1
- package/dist/cjs/api/component.api.d.ts +93 -1
- package/dist/cjs/api/component.api.js +146 -0
- package/dist/cjs/api/dockviewGroupPanelApi.d.ts +26 -0
- package/dist/cjs/api/dockviewGroupPanelApi.js +21 -1
- package/dist/cjs/api/entryPoints.js +4 -5
- package/dist/cjs/array.js +7 -8
- package/dist/cjs/dnd/dataTransfer.d.ts +2 -1
- package/dist/cjs/dnd/dataTransfer.js +5 -4
- package/dist/cjs/dnd/droptarget.d.ts +12 -0
- package/dist/cjs/dnd/droptarget.js +38 -10
- package/dist/cjs/dnd/ghost.js +1 -2
- package/dist/cjs/dockview/components/panel/content.js +5 -1
- package/dist/cjs/dockview/components/popupService.d.ts +9 -2
- package/dist/cjs/dockview/components/popupService.js +24 -9
- package/dist/cjs/dockview/components/tab/tab.d.ts +6 -1
- package/dist/cjs/dockview/components/tab/tab.js +81 -9
- package/dist/cjs/dockview/components/titlebar/tabGroupChip.d.ts +30 -0
- package/dist/cjs/dockview/components/titlebar/tabGroupChip.js +95 -0
- package/dist/cjs/dockview/components/titlebar/tabGroupIndicator.d.ts +71 -0
- package/dist/cjs/dockview/components/titlebar/tabGroupIndicator.js +471 -0
- package/dist/cjs/dockview/components/titlebar/tabGroups.d.ts +57 -0
- package/dist/cjs/dockview/components/titlebar/tabGroups.js +612 -0
- package/dist/cjs/dockview/components/titlebar/tabOverflowControl.js +1 -2
- package/dist/cjs/dockview/components/titlebar/tabs.d.ts +59 -0
- package/dist/cjs/dockview/components/titlebar/tabs.js +1227 -144
- package/dist/cjs/dockview/components/titlebar/tabsContainer.d.ts +6 -0
- package/dist/cjs/dockview/components/titlebar/tabsContainer.js +132 -14
- package/dist/cjs/dockview/contextMenu.d.ts +10 -0
- package/dist/cjs/dockview/contextMenu.js +298 -0
- package/dist/cjs/dockview/dockviewComponent.d.ts +60 -3
- package/dist/cjs/dockview/dockviewComponent.js +712 -126
- package/dist/cjs/dockview/dockviewGroupPanelModel.d.ts +83 -0
- package/dist/cjs/dockview/dockviewGroupPanelModel.js +619 -27
- package/dist/cjs/dockview/dockviewShell.d.ts +128 -0
- package/dist/cjs/dockview/dockviewShell.js +681 -0
- package/dist/cjs/dockview/events.d.ts +9 -0
- package/dist/cjs/dockview/framework.d.ts +14 -0
- package/dist/cjs/dockview/options.d.ts +92 -10
- package/dist/cjs/dockview/options.js +10 -7
- package/dist/cjs/dockview/tabGroup.d.ts +99 -0
- package/dist/cjs/dockview/tabGroup.js +219 -0
- package/dist/cjs/dockview/tabGroupAccent.d.ts +65 -0
- package/dist/cjs/dockview/tabGroupAccent.js +128 -0
- package/dist/cjs/dockview/theme.d.ts +56 -1
- package/dist/cjs/dockview/theme.js +103 -6
- package/dist/cjs/dockview/types.d.ts +2 -0
- package/dist/cjs/dom.js +19 -19
- package/dist/cjs/events.js +2 -2
- package/dist/cjs/gridview/baseComponentGridview.d.ts +1 -0
- package/dist/cjs/gridview/baseComponentGridview.js +6 -3
- package/dist/cjs/gridview/gridview.js +7 -7
- package/dist/cjs/index.d.ts +8 -5
- package/dist/cjs/index.js +6 -1
- package/dist/cjs/popoutWindow.js +3 -3
- package/dist/cjs/splitview/splitviewPanel.d.ts +1 -1
- package/dist/dockview-core.js +6942 -2777
- package/dist/dockview-core.min.js +2 -2
- package/dist/dockview-core.min.js.map +1 -1
- package/dist/dockview-core.min.noStyle.js +2 -2
- package/dist/dockview-core.min.noStyle.js.map +1 -1
- package/dist/dockview-core.noStyle.js +6940 -2775
- package/dist/esm/api/component.api.d.ts +93 -1
- package/dist/esm/api/component.api.js +118 -0
- package/dist/esm/api/dockviewGroupPanelApi.d.ts +26 -0
- package/dist/esm/api/dockviewGroupPanelApi.js +21 -1
- package/dist/esm/dnd/dataTransfer.d.ts +2 -1
- package/dist/esm/dnd/dataTransfer.js +2 -1
- package/dist/esm/dnd/droptarget.d.ts +12 -0
- package/dist/esm/dnd/droptarget.js +33 -5
- package/dist/esm/dockview/components/panel/content.js +5 -1
- package/dist/esm/dockview/components/popupService.d.ts +9 -2
- package/dist/esm/dockview/components/popupService.js +23 -9
- package/dist/esm/dockview/components/tab/tab.d.ts +6 -1
- package/dist/esm/dockview/components/tab/tab.js +83 -9
- package/dist/esm/dockview/components/titlebar/tabGroupChip.d.ts +30 -0
- package/dist/esm/dockview/components/titlebar/tabGroupChip.js +68 -0
- package/dist/esm/dockview/components/titlebar/tabGroupIndicator.d.ts +71 -0
- package/dist/esm/dockview/components/titlebar/tabGroupIndicator.js +354 -0
- package/dist/esm/dockview/components/titlebar/tabGroups.d.ts +57 -0
- package/dist/esm/dockview/components/titlebar/tabGroups.js +406 -0
- package/dist/esm/dockview/components/titlebar/tabs.d.ts +59 -0
- package/dist/esm/dockview/components/titlebar/tabs.js +1011 -99
- package/dist/esm/dockview/components/titlebar/tabsContainer.d.ts +6 -0
- package/dist/esm/dockview/components/titlebar/tabsContainer.js +105 -7
- package/dist/esm/dockview/contextMenu.d.ts +10 -0
- package/dist/esm/dockview/contextMenu.js +213 -0
- package/dist/esm/dockview/dockviewComponent.d.ts +60 -3
- package/dist/esm/dockview/dockviewComponent.js +460 -35
- package/dist/esm/dockview/dockviewGroupPanelModel.d.ts +83 -0
- package/dist/esm/dockview/dockviewGroupPanelModel.js +460 -4
- package/dist/esm/dockview/dockviewShell.d.ts +128 -0
- package/dist/esm/dockview/dockviewShell.js +621 -0
- package/dist/esm/dockview/events.d.ts +9 -0
- package/dist/esm/dockview/framework.d.ts +14 -0
- package/dist/esm/dockview/options.d.ts +92 -10
- package/dist/esm/dockview/options.js +5 -2
- package/dist/esm/dockview/tabGroup.d.ts +99 -0
- package/dist/esm/dockview/tabGroup.js +144 -0
- package/dist/esm/dockview/tabGroupAccent.d.ts +65 -0
- package/dist/esm/dockview/tabGroupAccent.js +116 -0
- package/dist/esm/dockview/theme.d.ts +56 -1
- package/dist/esm/dockview/theme.js +102 -5
- package/dist/esm/dockview/types.d.ts +2 -0
- package/dist/esm/dom.js +1 -1
- package/dist/esm/gridview/baseComponentGridview.d.ts +1 -0
- package/dist/esm/gridview/baseComponentGridview.js +4 -1
- package/dist/esm/index.d.ts +8 -5
- package/dist/esm/index.js +2 -1
- package/dist/esm/popoutWindow.js +1 -1
- package/dist/esm/splitview/splitviewPanel.d.ts +1 -1
- package/dist/package/main.cjs.js +6936 -2801
- package/dist/package/main.cjs.min.js +2 -2
- package/dist/package/main.esm.min.mjs +2 -2
- package/dist/package/main.esm.mjs +6922 -2800
- package/dist/styles/dockview.css +1945 -196
- package/package.json +5 -1
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { IDisposable, IValueDisposable } from '../../../lifecycle';
|
|
2
|
+
import { DockviewComponent } from '../../dockviewComponent';
|
|
3
|
+
import { DockviewGroupPanel } from '../../dockviewGroupPanel';
|
|
4
|
+
import { DockviewHeaderDirection } from '../../options';
|
|
5
|
+
import { Tab } from '../tab/tab';
|
|
6
|
+
import { ITabGroup } from '../../tabGroup';
|
|
7
|
+
import { ITabGroupChipRenderer } from '../../framework';
|
|
8
|
+
export interface TabGroupManagerContext {
|
|
9
|
+
readonly group: DockviewGroupPanel;
|
|
10
|
+
readonly accessor: DockviewComponent;
|
|
11
|
+
readonly tabsList: HTMLElement;
|
|
12
|
+
getTabs(): IValueDisposable<Tab>[];
|
|
13
|
+
getTabMap(): Map<string, IValueDisposable<Tab>>;
|
|
14
|
+
getDirection(): DockviewHeaderDirection;
|
|
15
|
+
}
|
|
16
|
+
export interface TabGroupManagerCallbacks {
|
|
17
|
+
onChipContextMenu(tabGroup: ITabGroup, event: MouseEvent): void;
|
|
18
|
+
onChipDragStart(tabGroup: ITabGroup, chip: ITabGroupChipRenderer, event: DragEvent): void;
|
|
19
|
+
}
|
|
20
|
+
export declare class TabGroupManager {
|
|
21
|
+
private readonly _ctx;
|
|
22
|
+
private readonly _callbacks;
|
|
23
|
+
private readonly _chipRenderers;
|
|
24
|
+
private _indicator;
|
|
25
|
+
private _skipNextCollapseAnimation;
|
|
26
|
+
private readonly _pendingTransitionCleanups;
|
|
27
|
+
get chipRenderers(): ReadonlyMap<string, {
|
|
28
|
+
chip: ITabGroupChipRenderer;
|
|
29
|
+
disposable: IDisposable;
|
|
30
|
+
}>;
|
|
31
|
+
get groupUnderlines(): ReadonlyMap<string, HTMLElement>;
|
|
32
|
+
get skipNextCollapseAnimation(): boolean;
|
|
33
|
+
set skipNextCollapseAnimation(value: boolean);
|
|
34
|
+
constructor(_ctx: TabGroupManagerContext, _callbacks: TabGroupManagerCallbacks);
|
|
35
|
+
/**
|
|
36
|
+
* Synchronize chip elements and CSS classes for all tab groups
|
|
37
|
+
* in the parent group model. Call after any tab group mutation.
|
|
38
|
+
*/
|
|
39
|
+
update(): void;
|
|
40
|
+
/**
|
|
41
|
+
* Re-read the active palette and re-apply colors to chips, tabs and
|
|
42
|
+
* the indicator. Called when `tabGroupColors` / `tabGroupAccent`
|
|
43
|
+
* options change at runtime.
|
|
44
|
+
*/
|
|
45
|
+
refreshAccents(): void;
|
|
46
|
+
positionAllChips(): void;
|
|
47
|
+
snapshotChipWidths(): Map<string, number>;
|
|
48
|
+
positionUnderlines(): void;
|
|
49
|
+
trackUnderlines(): void;
|
|
50
|
+
setGroupDragImage(event: DragEvent, tabGroup: ITabGroup, chipEl: HTMLElement): void;
|
|
51
|
+
cleanupTransition(panelId: string): void;
|
|
52
|
+
disposeAll(): void;
|
|
53
|
+
private _ensureIndicator;
|
|
54
|
+
private _ensureChipForGroup;
|
|
55
|
+
private _positionChipForGroup;
|
|
56
|
+
private _updateTabGroupClasses;
|
|
57
|
+
}
|
|
@@ -0,0 +1,406 @@
|
|
|
1
|
+
import { toggleClass } from '../../../dom';
|
|
2
|
+
import { addDisposableListener } from '../../../events';
|
|
3
|
+
import { CompositeDisposable, } from '../../../lifecycle';
|
|
4
|
+
import { applyTabGroupAccent } from '../../tabGroupAccent';
|
|
5
|
+
import { TabGroupChip } from './tabGroupChip';
|
|
6
|
+
import { NoneTabGroupIndicator, WrapTabGroupIndicator, } from './tabGroupIndicator';
|
|
7
|
+
const EMPTY_MAP = new Map();
|
|
8
|
+
export class TabGroupManager {
|
|
9
|
+
get chipRenderers() {
|
|
10
|
+
return this._chipRenderers;
|
|
11
|
+
}
|
|
12
|
+
get groupUnderlines() {
|
|
13
|
+
var _a, _b;
|
|
14
|
+
return (_b = (_a = this._indicator) === null || _a === void 0 ? void 0 : _a.underlines) !== null && _b !== void 0 ? _b : EMPTY_MAP;
|
|
15
|
+
}
|
|
16
|
+
get skipNextCollapseAnimation() {
|
|
17
|
+
return this._skipNextCollapseAnimation;
|
|
18
|
+
}
|
|
19
|
+
set skipNextCollapseAnimation(value) {
|
|
20
|
+
this._skipNextCollapseAnimation = value;
|
|
21
|
+
}
|
|
22
|
+
constructor(_ctx, _callbacks) {
|
|
23
|
+
this._ctx = _ctx;
|
|
24
|
+
this._callbacks = _callbacks;
|
|
25
|
+
this._chipRenderers = new Map();
|
|
26
|
+
this._indicator = null;
|
|
27
|
+
this._skipNextCollapseAnimation = false;
|
|
28
|
+
this._pendingTransitionCleanups = new Map();
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Synchronize chip elements and CSS classes for all tab groups
|
|
32
|
+
* in the parent group model. Call after any tab group mutation.
|
|
33
|
+
*/
|
|
34
|
+
update() {
|
|
35
|
+
const model = this._ctx.group.model;
|
|
36
|
+
const tabGroups = model.getTabGroups();
|
|
37
|
+
// Track which group IDs are still active
|
|
38
|
+
const activeGroupIds = new Set();
|
|
39
|
+
for (const tabGroup of tabGroups) {
|
|
40
|
+
activeGroupIds.add(tabGroup.id);
|
|
41
|
+
this._ensureChipForGroup(tabGroup);
|
|
42
|
+
this._positionChipForGroup(tabGroup);
|
|
43
|
+
}
|
|
44
|
+
// Remove chips for dissolved/destroyed groups
|
|
45
|
+
for (const [groupId, entry] of this._chipRenderers) {
|
|
46
|
+
if (!activeGroupIds.has(groupId)) {
|
|
47
|
+
entry.chip.element.remove();
|
|
48
|
+
entry.chip.dispose();
|
|
49
|
+
entry.disposable.dispose();
|
|
50
|
+
this._chipRenderers.delete(groupId);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
// Update CSS classes on all tabs
|
|
54
|
+
this._updateTabGroupClasses();
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Re-read the active palette and re-apply colors to chips, tabs and
|
|
58
|
+
* the indicator. Called when `tabGroupColors` / `tabGroupAccent`
|
|
59
|
+
* options change at runtime.
|
|
60
|
+
*/
|
|
61
|
+
refreshAccents() {
|
|
62
|
+
var _a, _b;
|
|
63
|
+
for (const tabGroup of this._ctx.group.model.getTabGroups()) {
|
|
64
|
+
const entry = this._chipRenderers.get(tabGroup.id);
|
|
65
|
+
(_b = entry === null || entry === void 0 ? void 0 : (_a = entry.chip).update) === null || _b === void 0 ? void 0 : _b.call(_a, { tabGroup });
|
|
66
|
+
}
|
|
67
|
+
this._updateTabGroupClasses();
|
|
68
|
+
}
|
|
69
|
+
positionAllChips() {
|
|
70
|
+
if (this._chipRenderers.size === 0) {
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
for (const tabGroup of this._ctx.group.model.getTabGroups()) {
|
|
74
|
+
this._positionChipForGroup(tabGroup);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
snapshotChipWidths() {
|
|
78
|
+
const widths = new Map();
|
|
79
|
+
for (const [groupId, entry] of this._chipRenderers) {
|
|
80
|
+
widths.set(groupId, entry.chip.element.getBoundingClientRect().width);
|
|
81
|
+
}
|
|
82
|
+
return widths;
|
|
83
|
+
}
|
|
84
|
+
positionUnderlines() {
|
|
85
|
+
var _a;
|
|
86
|
+
(_a = this._indicator) === null || _a === void 0 ? void 0 : _a.positionUnderlines();
|
|
87
|
+
}
|
|
88
|
+
trackUnderlines() {
|
|
89
|
+
var _a;
|
|
90
|
+
(_a = this._indicator) === null || _a === void 0 ? void 0 : _a.trackUnderlines();
|
|
91
|
+
}
|
|
92
|
+
setGroupDragImage(event, tabGroup, chipEl) {
|
|
93
|
+
if (!event.dataTransfer) {
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
const isVertical = this._ctx.getDirection() === 'vertical';
|
|
97
|
+
// Clone the entire tabs list so cloned nodes inherit all
|
|
98
|
+
// theme styles, CSS variables and class-based rules.
|
|
99
|
+
const clone = this._ctx.tabsList.cloneNode(true);
|
|
100
|
+
if (isVertical) {
|
|
101
|
+
// Force horizontal orientation for the drag ghost by
|
|
102
|
+
// removing vertical CSS classes and overriding writing-mode.
|
|
103
|
+
clone.classList.remove('dv-tabs-container-vertical', 'dv-vertical');
|
|
104
|
+
clone.classList.add('dv-horizontal');
|
|
105
|
+
clone.style.writingMode = 'horizontal-tb';
|
|
106
|
+
clone.style.height = `${this._ctx.tabsList.offsetWidth}px`;
|
|
107
|
+
}
|
|
108
|
+
else {
|
|
109
|
+
clone.style.height = `${this._ctx.tabsList.offsetHeight}px`;
|
|
110
|
+
}
|
|
111
|
+
clone.style.width = 'auto';
|
|
112
|
+
clone.style.overflow = 'visible';
|
|
113
|
+
clone.style.pointerEvents = 'none';
|
|
114
|
+
// Remove all elements except the chip so the drag ghost
|
|
115
|
+
// shows only the chip regardless of the group's expanded state.
|
|
116
|
+
const children = Array.from(clone.children);
|
|
117
|
+
const realChildren = Array.from(this._ctx.tabsList.children);
|
|
118
|
+
for (let i = children.length - 1; i >= 0; i--) {
|
|
119
|
+
const real = realChildren[i];
|
|
120
|
+
if (real === chipEl) {
|
|
121
|
+
continue; // keep the chip only
|
|
122
|
+
}
|
|
123
|
+
children[i].remove();
|
|
124
|
+
}
|
|
125
|
+
// Wrap the clone in a minimal ancestor chain so that CSS
|
|
126
|
+
// selectors like `.dv-groupview.dv-active-group > .dv-tabs-and-actions-container .dv-tabs-container > .dv-tab`
|
|
127
|
+
// match the cloned tabs and apply correct color/background.
|
|
128
|
+
const wrapper = document.createElement('div');
|
|
129
|
+
wrapper.className = 'dv-groupview dv-active-group';
|
|
130
|
+
wrapper.style.position = 'fixed';
|
|
131
|
+
wrapper.style.top = '-10000px';
|
|
132
|
+
wrapper.style.left = '0px';
|
|
133
|
+
wrapper.style.height = 'auto';
|
|
134
|
+
wrapper.style.width = 'auto';
|
|
135
|
+
wrapper.style.pointerEvents = 'none';
|
|
136
|
+
const actionsWrapper = document.createElement('div');
|
|
137
|
+
actionsWrapper.className = 'dv-tabs-and-actions-container';
|
|
138
|
+
actionsWrapper.style.height = 'auto';
|
|
139
|
+
actionsWrapper.style.width = 'auto';
|
|
140
|
+
wrapper.appendChild(actionsWrapper);
|
|
141
|
+
actionsWrapper.appendChild(clone);
|
|
142
|
+
// Append inside the dockview root so CSS variables are inherited
|
|
143
|
+
this._ctx.accessor.element.appendChild(wrapper);
|
|
144
|
+
// Compute cursor offset relative to the wrapper element.
|
|
145
|
+
// The cloned chip is the first .dv-tab-group-chip in the clone.
|
|
146
|
+
const clonedChip = clone.querySelector('.dv-tab-group-chip');
|
|
147
|
+
const chipRect = chipEl.getBoundingClientRect();
|
|
148
|
+
const cursorInChipX = event.clientX - chipRect.left;
|
|
149
|
+
const cursorInChipY = event.clientY - chipRect.top;
|
|
150
|
+
if (clonedChip) {
|
|
151
|
+
const clonedChipRect = clonedChip.getBoundingClientRect();
|
|
152
|
+
const wrapperRect = wrapper.getBoundingClientRect();
|
|
153
|
+
const offsetX = clonedChipRect.left - wrapperRect.left + cursorInChipX;
|
|
154
|
+
const offsetY = clonedChipRect.top - wrapperRect.top + cursorInChipY;
|
|
155
|
+
event.dataTransfer.setDragImage(wrapper, offsetX, offsetY);
|
|
156
|
+
}
|
|
157
|
+
else {
|
|
158
|
+
event.dataTransfer.setDragImage(wrapper, cursorInChipX, cursorInChipY);
|
|
159
|
+
}
|
|
160
|
+
// Clean up after the browser captures the image
|
|
161
|
+
requestAnimationFrame(() => {
|
|
162
|
+
wrapper.remove();
|
|
163
|
+
});
|
|
164
|
+
}
|
|
165
|
+
cleanupTransition(panelId) {
|
|
166
|
+
var _a;
|
|
167
|
+
(_a = this._pendingTransitionCleanups.get(panelId)) === null || _a === void 0 ? void 0 : _a();
|
|
168
|
+
this._pendingTransitionCleanups.delete(panelId);
|
|
169
|
+
}
|
|
170
|
+
disposeAll() {
|
|
171
|
+
var _a;
|
|
172
|
+
(_a = this._indicator) === null || _a === void 0 ? void 0 : _a.dispose();
|
|
173
|
+
this._indicator = null;
|
|
174
|
+
for (const [, cleanup] of this._pendingTransitionCleanups) {
|
|
175
|
+
cleanup();
|
|
176
|
+
}
|
|
177
|
+
this._pendingTransitionCleanups.clear();
|
|
178
|
+
for (const [, entry] of this._chipRenderers) {
|
|
179
|
+
entry.chip.element.remove();
|
|
180
|
+
entry.chip.dispose();
|
|
181
|
+
entry.disposable.dispose();
|
|
182
|
+
}
|
|
183
|
+
this._chipRenderers.clear();
|
|
184
|
+
}
|
|
185
|
+
_ensureIndicator() {
|
|
186
|
+
var _a, _b;
|
|
187
|
+
const mode = (_b = (_a = this._ctx.accessor.options.theme) === null || _a === void 0 ? void 0 : _a.tabGroupIndicator) !== null && _b !== void 0 ? _b : 'wrap';
|
|
188
|
+
const Ctor = mode === 'none' ? NoneTabGroupIndicator : WrapTabGroupIndicator;
|
|
189
|
+
// Re-create if the indicator type changed (e.g. theme switch)
|
|
190
|
+
if (this._indicator && !(this._indicator instanceof Ctor)) {
|
|
191
|
+
this._indicator.dispose();
|
|
192
|
+
this._indicator = null;
|
|
193
|
+
}
|
|
194
|
+
if (!this._indicator) {
|
|
195
|
+
this._indicator = new Ctor({
|
|
196
|
+
tabsList: this._ctx.tabsList,
|
|
197
|
+
getTabGroups: () => this._ctx.group.model.getTabGroups(),
|
|
198
|
+
getActivePanelId: () => { var _a; return (_a = this._ctx.group.activePanel) === null || _a === void 0 ? void 0 : _a.id; },
|
|
199
|
+
getTabMap: () => this._ctx.getTabMap(),
|
|
200
|
+
getChipElement: (id) => { var _a; return (_a = this._chipRenderers.get(id)) === null || _a === void 0 ? void 0 : _a.chip.element; },
|
|
201
|
+
getDirection: () => this._ctx.getDirection(),
|
|
202
|
+
getColorPalette: () => this._ctx.accessor.tabGroupColorPalette,
|
|
203
|
+
});
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
_ensureChipForGroup(tabGroup) {
|
|
207
|
+
if (this._chipRenderers.has(tabGroup.id)) {
|
|
208
|
+
return;
|
|
209
|
+
}
|
|
210
|
+
const createChip = this._ctx.accessor.options.createTabGroupChipComponent;
|
|
211
|
+
const chip = createChip
|
|
212
|
+
? createChip(tabGroup)
|
|
213
|
+
: new TabGroupChip(this._ctx.accessor.tabGroupColorPalette);
|
|
214
|
+
chip.init({ tabGroup, api: this._ctx.accessor.api });
|
|
215
|
+
const disposables = [
|
|
216
|
+
tabGroup.onDidChange(() => {
|
|
217
|
+
var _a;
|
|
218
|
+
(_a = chip.update) === null || _a === void 0 ? void 0 : _a.call(chip, { tabGroup });
|
|
219
|
+
this._updateTabGroupClasses();
|
|
220
|
+
}),
|
|
221
|
+
tabGroup.onDidPanelChange(() => {
|
|
222
|
+
this._positionChipForGroup(tabGroup);
|
|
223
|
+
this._updateTabGroupClasses();
|
|
224
|
+
}),
|
|
225
|
+
tabGroup.onDidCollapseChange(() => {
|
|
226
|
+
this._updateTabGroupClasses();
|
|
227
|
+
}),
|
|
228
|
+
];
|
|
229
|
+
// Wire chip context menu and drag for all chip renderers
|
|
230
|
+
if (chip instanceof TabGroupChip) {
|
|
231
|
+
disposables.push(chip.onContextMenu((event) => {
|
|
232
|
+
this._callbacks.onChipContextMenu(tabGroup, event);
|
|
233
|
+
}), chip.onDragStart((event) => {
|
|
234
|
+
this._callbacks.onChipDragStart(tabGroup, chip, event);
|
|
235
|
+
}));
|
|
236
|
+
}
|
|
237
|
+
else {
|
|
238
|
+
disposables.push(addDisposableListener(chip.element, 'contextmenu', (event) => {
|
|
239
|
+
this._callbacks.onChipContextMenu(tabGroup, event);
|
|
240
|
+
}), addDisposableListener(chip.element, 'dragstart', (event) => {
|
|
241
|
+
this._callbacks.onChipDragStart(tabGroup, chip, event);
|
|
242
|
+
}));
|
|
243
|
+
}
|
|
244
|
+
const disposable = new CompositeDisposable(...disposables);
|
|
245
|
+
this._chipRenderers.set(tabGroup.id, { chip, disposable });
|
|
246
|
+
// Group is born collapsed (cross-group drop, layout restore, etc.):
|
|
247
|
+
// its tabs are about to be added without the collapsed class. Skip
|
|
248
|
+
// the animation in the upcoming _updateTabGroupClasses call so they
|
|
249
|
+
// apply the class instantly instead of transitioning from expanded.
|
|
250
|
+
if (tabGroup.collapsed) {
|
|
251
|
+
this._skipNextCollapseAnimation = true;
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
_positionChipForGroup(tabGroup) {
|
|
255
|
+
const entry = this._chipRenderers.get(tabGroup.id);
|
|
256
|
+
if (!entry) {
|
|
257
|
+
return;
|
|
258
|
+
}
|
|
259
|
+
const chipEl = entry.chip.element;
|
|
260
|
+
const panelIds = tabGroup.panelIds;
|
|
261
|
+
if (panelIds.length === 0) {
|
|
262
|
+
chipEl.remove();
|
|
263
|
+
return;
|
|
264
|
+
}
|
|
265
|
+
// Find the first tab element of this group
|
|
266
|
+
const firstPanelId = panelIds[0];
|
|
267
|
+
const firstTabEntry = this._ctx.getTabMap().get(firstPanelId);
|
|
268
|
+
if (!firstTabEntry) {
|
|
269
|
+
chipEl.remove();
|
|
270
|
+
return;
|
|
271
|
+
}
|
|
272
|
+
// Insert chip before the first tab of the group
|
|
273
|
+
const firstTabEl = firstTabEntry.value.element;
|
|
274
|
+
if (chipEl.nextSibling !== firstTabEl) {
|
|
275
|
+
this._ctx.tabsList.insertBefore(chipEl, firstTabEl);
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
_updateTabGroupClasses() {
|
|
279
|
+
var _a;
|
|
280
|
+
const model = this._ctx.group.model;
|
|
281
|
+
const tabGroups = model.getTabGroups();
|
|
282
|
+
const tabs = this._ctx.getTabs();
|
|
283
|
+
const tabMap = this._ctx.getTabMap();
|
|
284
|
+
let hasAnimation = false;
|
|
285
|
+
// Build a lookup: panelId → tabGroup
|
|
286
|
+
const panelGroupMap = new Map();
|
|
287
|
+
for (const tg of tabGroups) {
|
|
288
|
+
for (const pid of tg.panelIds) {
|
|
289
|
+
panelGroupMap.set(pid, tg);
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
for (const tabEntry of tabs) {
|
|
293
|
+
const tab = tabEntry.value;
|
|
294
|
+
const panelId = tab.panel.id;
|
|
295
|
+
const tg = panelGroupMap.get(panelId);
|
|
296
|
+
const isGrouped = !!tg;
|
|
297
|
+
toggleClass(tab.element, 'dv-tab--grouped', isGrouped);
|
|
298
|
+
if (tg) {
|
|
299
|
+
const ids = tg.panelIds;
|
|
300
|
+
const isFirst = ids[0] === panelId;
|
|
301
|
+
const isLast = ids[ids.length - 1] === panelId;
|
|
302
|
+
toggleClass(tab.element, 'dv-tab--group-first', isFirst);
|
|
303
|
+
toggleClass(tab.element, 'dv-tab--group-last', isLast);
|
|
304
|
+
// Expose the resolved group color as a CSS custom property
|
|
305
|
+
// so pure-CSS themes can use it for borders, backgrounds, etc.
|
|
306
|
+
applyTabGroupAccent(tab.element, tg.color, this._ctx.accessor.tabGroupColorPalette);
|
|
307
|
+
// Collapse / expand with animation
|
|
308
|
+
const isCollapsed = tab.element.classList.contains('dv-tab--group-collapsed');
|
|
309
|
+
if (!tg.collapsed && isCollapsed) {
|
|
310
|
+
// Collapsed → expanding: animate back
|
|
311
|
+
hasAnimation = true;
|
|
312
|
+
tab.element.classList.remove('dv-tab--group-collapsed');
|
|
313
|
+
tab.element.classList.add('dv-tab--group-expanding');
|
|
314
|
+
// Clean up any previous transitionend listener
|
|
315
|
+
// from a rapid collapse/expand cycle
|
|
316
|
+
(_a = this._pendingTransitionCleanups.get(panelId)) === null || _a === void 0 ? void 0 : _a();
|
|
317
|
+
const onEnd = () => {
|
|
318
|
+
tab.element.classList.remove('dv-tab--group-expanding');
|
|
319
|
+
tab.element.style.removeProperty('width');
|
|
320
|
+
tab.element.removeEventListener('transitionend', onEnd);
|
|
321
|
+
clearTimeout(fallbackTimer);
|
|
322
|
+
this._pendingTransitionCleanups.delete(panelId);
|
|
323
|
+
};
|
|
324
|
+
// Fallback in case transitionend never fires
|
|
325
|
+
// (e.g. element removed from DOM mid-transition)
|
|
326
|
+
const fallbackTimer = setTimeout(onEnd, 300);
|
|
327
|
+
this._pendingTransitionCleanups.set(panelId, onEnd);
|
|
328
|
+
tab.element.addEventListener('transitionend', onEnd);
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
else {
|
|
332
|
+
toggleClass(tab.element, 'dv-tab--group-first', false);
|
|
333
|
+
toggleClass(tab.element, 'dv-tab--group-last', false);
|
|
334
|
+
tab.element.classList.remove('dv-tab--group-collapsed', 'dv-tab--group-expanding');
|
|
335
|
+
tab.element.style.removeProperty('width');
|
|
336
|
+
tab.element.style.removeProperty('--dv-tab-group-color');
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
// Track active group IDs for underline/collapse handling
|
|
340
|
+
const activeGroupIds = new Set();
|
|
341
|
+
// Handle collapse animation per group
|
|
342
|
+
for (const tg of tabGroups) {
|
|
343
|
+
activeGroupIds.add(tg.id);
|
|
344
|
+
// Collapse animation
|
|
345
|
+
const hasNewCollapse = tg.collapsed &&
|
|
346
|
+
tg.panelIds.some((pid) => {
|
|
347
|
+
const te = tabMap.get(pid);
|
|
348
|
+
return (te &&
|
|
349
|
+
!te.value.element.classList.contains('dv-tab--group-collapsed'));
|
|
350
|
+
});
|
|
351
|
+
if (hasNewCollapse) {
|
|
352
|
+
if (this._skipNextCollapseAnimation) {
|
|
353
|
+
// Apply collapsed state instantly (no animation).
|
|
354
|
+
// Disable transitions so the CSS transition on
|
|
355
|
+
// dv-tab--group-collapsed doesn't fire.
|
|
356
|
+
const affected = [];
|
|
357
|
+
for (const pid of tg.panelIds) {
|
|
358
|
+
const te = tabMap.get(pid);
|
|
359
|
+
if (te) {
|
|
360
|
+
te.value.element.style.transition = 'none';
|
|
361
|
+
te.value.element.classList.add('dv-tab--group-collapsed');
|
|
362
|
+
affected.push(te.value.element);
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
if (affected.length > 0) {
|
|
366
|
+
void affected[0].offsetHeight; // single reflow
|
|
367
|
+
for (const el of affected) {
|
|
368
|
+
el.style.removeProperty('transition');
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
else {
|
|
373
|
+
hasAnimation = true;
|
|
374
|
+
const isVert = this._ctx.getDirection() === 'vertical';
|
|
375
|
+
for (const pid of tg.panelIds) {
|
|
376
|
+
const te = tabMap.get(pid);
|
|
377
|
+
if (te &&
|
|
378
|
+
!te.value.element.classList.contains('dv-tab--group-collapsed')) {
|
|
379
|
+
const rect = te.value.element.getBoundingClientRect();
|
|
380
|
+
if (isVert) {
|
|
381
|
+
te.value.element.style.height = `${rect.height}px`;
|
|
382
|
+
}
|
|
383
|
+
else {
|
|
384
|
+
te.value.element.style.width = `${rect.width}px`;
|
|
385
|
+
}
|
|
386
|
+
void te.value.element.offsetHeight; // force reflow
|
|
387
|
+
te.value.element.classList.add('dv-tab--group-collapsed');
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
this._skipNextCollapseAnimation = false;
|
|
394
|
+
// Sync indicator underlines and position them
|
|
395
|
+
this._ensureIndicator();
|
|
396
|
+
if (this._indicator) {
|
|
397
|
+
this._indicator.syncUnderlineElements(activeGroupIds);
|
|
398
|
+
if (hasAnimation) {
|
|
399
|
+
this._indicator.trackUnderlines();
|
|
400
|
+
}
|
|
401
|
+
else {
|
|
402
|
+
this._indicator.positionUnderlines();
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
}
|
|
@@ -15,10 +15,19 @@ export declare class Tabs extends CompositeDisposable {
|
|
|
15
15
|
private readonly _observerDisposable;
|
|
16
16
|
private readonly _scrollbar;
|
|
17
17
|
private _tabs;
|
|
18
|
+
private readonly _tabMap;
|
|
18
19
|
private selectedIndex;
|
|
19
20
|
private _showTabsOverflowControl;
|
|
20
21
|
private _direction;
|
|
21
22
|
private _animState;
|
|
23
|
+
private readonly _pendingMarginCleanups;
|
|
24
|
+
private _pendingCollapse;
|
|
25
|
+
private _flipTransitionCleanup;
|
|
26
|
+
private _voidContainer;
|
|
27
|
+
private _voidContainerListeners;
|
|
28
|
+
private _extendedDropZone;
|
|
29
|
+
private _chipDragCleanup;
|
|
30
|
+
private readonly _tabGroupManager;
|
|
22
31
|
private readonly _onTabDragStart;
|
|
23
32
|
readonly onTabDragStart: Event<TabDragEvent>;
|
|
24
33
|
private readonly _onDrop;
|
|
@@ -28,11 +37,19 @@ export declare class Tabs extends CompositeDisposable {
|
|
|
28
37
|
private readonly _onOverflowTabsChange;
|
|
29
38
|
readonly onOverflowTabsChange: Event<{
|
|
30
39
|
tabs: string[];
|
|
40
|
+
tabGroups: string[];
|
|
31
41
|
reset: boolean;
|
|
32
42
|
}>;
|
|
33
43
|
get showTabsOverflowControl(): boolean;
|
|
34
44
|
set showTabsOverflowControl(value: boolean);
|
|
35
45
|
get element(): HTMLElement;
|
|
46
|
+
set voidContainer(el: HTMLElement | null);
|
|
47
|
+
/**
|
|
48
|
+
* Handle a drop that occurred on the void container (empty header
|
|
49
|
+
* space to the right of the tabs). Returns `true` if the drop was
|
|
50
|
+
* consumed by an active group drag, `false` otherwise.
|
|
51
|
+
*/
|
|
52
|
+
handleVoidDrop(): boolean;
|
|
36
53
|
get panels(): string[];
|
|
37
54
|
get size(): number;
|
|
38
55
|
get tabs(): Tab[];
|
|
@@ -49,11 +66,53 @@ export declare class Tabs extends CompositeDisposable {
|
|
|
49
66
|
private addTab;
|
|
50
67
|
private toggleDropdown;
|
|
51
68
|
updateDragAndDropState(): void;
|
|
69
|
+
/**
|
|
70
|
+
* Synchronize chip elements and CSS classes for all tab groups
|
|
71
|
+
* in the parent group model. Call after any tab group mutation.
|
|
72
|
+
*/
|
|
73
|
+
updateTabGroups(): void;
|
|
74
|
+
refreshTabGroupAccent(): void;
|
|
75
|
+
private _handleChipDragStart;
|
|
76
|
+
/**
|
|
77
|
+
* Sets the broader container that is part of the same logical drop surface
|
|
78
|
+
* as this tab list (e.g. the full header element). When a dragleave from
|
|
79
|
+
* the tabs list lands inside this container, `_animState` is preserved so
|
|
80
|
+
* that external dragover listeners can continue the animation.
|
|
81
|
+
*/
|
|
82
|
+
setExtendedDropZone(el: HTMLElement): void;
|
|
83
|
+
/**
|
|
84
|
+
* Allows external elements (e.g. void container, left actions) to push an
|
|
85
|
+
* insertion index into the animation while the cursor is outside the tabs
|
|
86
|
+
* list itself. Pass `null` to clear the indicator.
|
|
87
|
+
*/
|
|
88
|
+
setExternalInsertionIndex(index: number | null): void;
|
|
89
|
+
/**
|
|
90
|
+
* Called when the drag cursor leaves the entire header area (not just the
|
|
91
|
+
* tabs list). Clears animation state for cross-group drags, which never
|
|
92
|
+
* receive a `dragend` event on this tab list.
|
|
93
|
+
*/
|
|
94
|
+
clearExternalAnimState(): void;
|
|
52
95
|
private snapshotTabPositions;
|
|
53
96
|
private getAverageTabWidth;
|
|
54
97
|
private handleDragOver;
|
|
98
|
+
/**
|
|
99
|
+
* Batch-remove a CSS class from multiple elements instantly,
|
|
100
|
+
* forcing only a single reflow for the entire batch.
|
|
101
|
+
*/
|
|
102
|
+
private _removeClassInstantlyBatch;
|
|
103
|
+
/**
|
|
104
|
+
* Remove `dv-tab--dragging` from the source tab instantly so it
|
|
105
|
+
* regains its real width before FLIP snapshots.
|
|
106
|
+
*/
|
|
107
|
+
private _uncollapsSourceTab;
|
|
55
108
|
private applyDragOverTransforms;
|
|
56
109
|
private resetTabTransforms;
|
|
110
|
+
/**
|
|
111
|
+
* Commit a group-drag drop: clear drag classes, move the group
|
|
112
|
+
* in the model, and run a FLIP animation.
|
|
113
|
+
*/
|
|
114
|
+
private _commitGroupMove;
|
|
115
|
+
private _clearGroupDragClasses;
|
|
57
116
|
private resetDragAnimation;
|
|
58
117
|
private runFlipAnimation;
|
|
59
118
|
}
|