dockview-core 4.6.2 → 4.7.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/dist/cjs/constants.d.ts +1 -0
- package/dist/cjs/constants.js +2 -1
- package/dist/cjs/dnd/abstractDragHandler.d.ts +3 -1
- package/dist/cjs/dnd/abstractDragHandler.js +6 -2
- package/dist/cjs/dnd/droptarget.js +46 -17
- package/dist/cjs/dnd/groupDragHandler.d.ts +1 -1
- package/dist/cjs/dnd/groupDragHandler.js +2 -2
- package/dist/cjs/dockview/components/tab/tab.d.ts +1 -0
- package/dist/cjs/dockview/components/tab/tab.js +6 -5
- package/dist/cjs/dockview/components/titlebar/voidContainer.d.ts +1 -0
- package/dist/cjs/dockview/components/titlebar/voidContainer.js +3 -2
- package/dist/cjs/dockview/dockviewComponent.d.ts +6 -0
- package/dist/cjs/dockview/dockviewComponent.js +115 -86
- package/dist/cjs/gridview/gridview.d.ts +1 -0
- package/dist/cjs/gridview/gridview.js +37 -0
- package/dist/cjs/overlay/overlayRenderContainer.d.ts +3 -0
- package/dist/cjs/overlay/overlayRenderContainer.js +82 -8
- package/dist/dockview-core.amd.js +235 -67
- package/dist/dockview-core.amd.js.map +1 -1
- package/dist/dockview-core.amd.min.js +2 -2
- package/dist/dockview-core.amd.min.js.map +1 -1
- package/dist/dockview-core.amd.min.noStyle.js +2 -2
- package/dist/dockview-core.amd.min.noStyle.js.map +1 -1
- package/dist/dockview-core.amd.noStyle.js +234 -66
- package/dist/dockview-core.amd.noStyle.js.map +1 -1
- package/dist/dockview-core.cjs.js +235 -67
- package/dist/dockview-core.cjs.js.map +1 -1
- package/dist/dockview-core.esm.js +235 -67
- package/dist/dockview-core.esm.js.map +1 -1
- package/dist/dockview-core.esm.min.js +2 -2
- package/dist/dockview-core.esm.min.js.map +1 -1
- package/dist/dockview-core.js +235 -67
- package/dist/dockview-core.js.map +1 -1
- 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 +234 -66
- package/dist/dockview-core.noStyle.js.map +1 -1
- package/dist/esm/constants.d.ts +1 -0
- package/dist/esm/constants.js +1 -0
- package/dist/esm/dnd/abstractDragHandler.d.ts +3 -1
- package/dist/esm/dnd/abstractDragHandler.js +6 -2
- package/dist/esm/dnd/droptarget.js +46 -17
- package/dist/esm/dnd/groupDragHandler.d.ts +1 -1
- package/dist/esm/dnd/groupDragHandler.js +2 -2
- package/dist/esm/dockview/components/tab/tab.d.ts +1 -0
- package/dist/esm/dockview/components/tab/tab.js +6 -5
- package/dist/esm/dockview/components/titlebar/voidContainer.d.ts +1 -0
- package/dist/esm/dockview/components/titlebar/voidContainer.js +3 -2
- package/dist/esm/dockview/dockviewComponent.d.ts +6 -0
- package/dist/esm/dockview/dockviewComponent.js +65 -30
- package/dist/esm/gridview/gridview.d.ts +1 -0
- package/dist/esm/gridview/gridview.js +36 -0
- package/dist/esm/overlay/overlayRenderContainer.d.ts +3 -0
- package/dist/esm/overlay/overlayRenderContainer.js +69 -8
- package/dist/styles/dockview.css +37 -5
- package/package.json +1 -1
package/dist/esm/constants.d.ts
CHANGED
package/dist/esm/constants.js
CHANGED
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
import { CompositeDisposable, IDisposable } from '../lifecycle';
|
|
2
2
|
export declare abstract class DragHandler extends CompositeDisposable {
|
|
3
3
|
protected readonly el: HTMLElement;
|
|
4
|
+
private disabled?;
|
|
4
5
|
private readonly dataDisposable;
|
|
5
6
|
private readonly pointerEventsDisposable;
|
|
6
7
|
private readonly _onDragStart;
|
|
7
8
|
readonly onDragStart: import("../events").Event<DragEvent>;
|
|
8
|
-
constructor(el: HTMLElement);
|
|
9
|
+
constructor(el: HTMLElement, disabled?: boolean | undefined);
|
|
10
|
+
setDisabled(disabled: boolean): void;
|
|
9
11
|
abstract getData(event: DragEvent): IDisposable;
|
|
10
12
|
protected isCancelled(_event: DragEvent): boolean;
|
|
11
13
|
private configure;
|
|
@@ -2,9 +2,10 @@ import { disableIframePointEvents } from '../dom';
|
|
|
2
2
|
import { addDisposableListener, Emitter } from '../events';
|
|
3
3
|
import { CompositeDisposable, MutableDisposable, } from '../lifecycle';
|
|
4
4
|
export class DragHandler extends CompositeDisposable {
|
|
5
|
-
constructor(el) {
|
|
5
|
+
constructor(el, disabled) {
|
|
6
6
|
super();
|
|
7
7
|
this.el = el;
|
|
8
|
+
this.disabled = disabled;
|
|
8
9
|
this.dataDisposable = new MutableDisposable();
|
|
9
10
|
this.pointerEventsDisposable = new MutableDisposable();
|
|
10
11
|
this._onDragStart = new Emitter();
|
|
@@ -12,12 +13,15 @@ export class DragHandler extends CompositeDisposable {
|
|
|
12
13
|
this.addDisposables(this._onDragStart, this.dataDisposable, this.pointerEventsDisposable);
|
|
13
14
|
this.configure();
|
|
14
15
|
}
|
|
16
|
+
setDisabled(disabled) {
|
|
17
|
+
this.disabled = disabled;
|
|
18
|
+
}
|
|
15
19
|
isCancelled(_event) {
|
|
16
20
|
return false;
|
|
17
21
|
}
|
|
18
22
|
configure() {
|
|
19
23
|
this.addDisposables(this._onDragStart, addDisposableListener(this.el, 'dragstart', (event) => {
|
|
20
|
-
if (event.defaultPrevented || this.isCancelled(event)) {
|
|
24
|
+
if (event.defaultPrevented || this.isCancelled(event) || this.disabled) {
|
|
21
25
|
event.preventDefault();
|
|
22
26
|
return;
|
|
23
27
|
}
|
|
@@ -3,6 +3,48 @@ import { DockviewEvent, Emitter } from '../events';
|
|
|
3
3
|
import { CompositeDisposable } from '../lifecycle';
|
|
4
4
|
import { DragAndDropObserver } from './dnd';
|
|
5
5
|
import { clamp } from '../math';
|
|
6
|
+
function setGPUOptimizedBounds(element, bounds) {
|
|
7
|
+
const { top, left, width, height } = bounds;
|
|
8
|
+
const topPx = `${Math.round(top)}px`;
|
|
9
|
+
const leftPx = `${Math.round(left)}px`;
|
|
10
|
+
const widthPx = `${Math.round(width)}px`;
|
|
11
|
+
const heightPx = `${Math.round(height)}px`;
|
|
12
|
+
// Use traditional positioning but maintain GPU layer
|
|
13
|
+
element.style.top = topPx;
|
|
14
|
+
element.style.left = leftPx;
|
|
15
|
+
element.style.width = widthPx;
|
|
16
|
+
element.style.height = heightPx;
|
|
17
|
+
element.style.visibility = 'visible';
|
|
18
|
+
// Ensure GPU layer is maintained
|
|
19
|
+
if (!element.style.transform || element.style.transform === '') {
|
|
20
|
+
element.style.transform = 'translate3d(0, 0, 0)';
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
function setGPUOptimizedBoundsFromStrings(element, bounds) {
|
|
24
|
+
const { top, left, width, height } = bounds;
|
|
25
|
+
// Use traditional positioning but maintain GPU layer
|
|
26
|
+
element.style.top = top;
|
|
27
|
+
element.style.left = left;
|
|
28
|
+
element.style.width = width;
|
|
29
|
+
element.style.height = height;
|
|
30
|
+
element.style.visibility = 'visible';
|
|
31
|
+
// Ensure GPU layer is maintained
|
|
32
|
+
if (!element.style.transform || element.style.transform === '') {
|
|
33
|
+
element.style.transform = 'translate3d(0, 0, 0)';
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
function checkBoundsChanged(element, bounds) {
|
|
37
|
+
const { top, left, width, height } = bounds;
|
|
38
|
+
const topPx = `${Math.round(top)}px`;
|
|
39
|
+
const leftPx = `${Math.round(left)}px`;
|
|
40
|
+
const widthPx = `${Math.round(width)}px`;
|
|
41
|
+
const heightPx = `${Math.round(height)}px`;
|
|
42
|
+
// Check if position or size changed (back to traditional method)
|
|
43
|
+
return element.style.top !== topPx ||
|
|
44
|
+
element.style.left !== leftPx ||
|
|
45
|
+
element.style.width !== widthPx ||
|
|
46
|
+
element.style.height !== heightPx;
|
|
47
|
+
}
|
|
6
48
|
export class WillShowOverlayEvent extends DockviewEvent {
|
|
7
49
|
get nativeEvent() {
|
|
8
50
|
return this.options.nativeEvent;
|
|
@@ -286,21 +328,11 @@ export class Droptarget extends CompositeDisposable {
|
|
|
286
328
|
box.left = rootLeft + width - 4;
|
|
287
329
|
box.width = 4;
|
|
288
330
|
}
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
const widthPx = `${Math.round(box.width)}px`;
|
|
292
|
-
const heightPx = `${Math.round(box.height)}px`;
|
|
293
|
-
if (overlay.style.top === topPx &&
|
|
294
|
-
overlay.style.left === leftPx &&
|
|
295
|
-
overlay.style.width === widthPx &&
|
|
296
|
-
overlay.style.height === heightPx) {
|
|
331
|
+
// Use GPU-optimized bounds checking and setting
|
|
332
|
+
if (!checkBoundsChanged(overlay, box)) {
|
|
297
333
|
return;
|
|
298
334
|
}
|
|
299
|
-
overlay
|
|
300
|
-
overlay.style.left = leftPx;
|
|
301
|
-
overlay.style.width = widthPx;
|
|
302
|
-
overlay.style.height = heightPx;
|
|
303
|
-
overlay.style.visibility = 'visible';
|
|
335
|
+
setGPUOptimizedBounds(overlay, box);
|
|
304
336
|
overlay.className = `dv-drop-target-anchor${this.options.className ? ` ${this.options.className}` : ''}`;
|
|
305
337
|
toggleClass(overlay, 'dv-drop-target-left', isLeft);
|
|
306
338
|
toggleClass(overlay, 'dv-drop-target-right', isRight);
|
|
@@ -352,10 +384,7 @@ export class Droptarget extends CompositeDisposable {
|
|
|
352
384
|
box.top = `${100 * (1 - size)}%`;
|
|
353
385
|
box.height = `${100 * size}%`;
|
|
354
386
|
}
|
|
355
|
-
this.overlayElement
|
|
356
|
-
this.overlayElement.style.left = box.left;
|
|
357
|
-
this.overlayElement.style.width = box.width;
|
|
358
|
-
this.overlayElement.style.height = box.height;
|
|
387
|
+
setGPUOptimizedBoundsFromStrings(this.overlayElement, box);
|
|
359
388
|
toggleClass(this.overlayElement, 'dv-drop-target-small-vertical', isSmallY);
|
|
360
389
|
toggleClass(this.overlayElement, 'dv-drop-target-small-horizontal', isSmallX);
|
|
361
390
|
toggleClass(this.overlayElement, 'dv-drop-target-left', isLeft);
|
|
@@ -6,7 +6,7 @@ export declare class GroupDragHandler extends DragHandler {
|
|
|
6
6
|
private readonly accessor;
|
|
7
7
|
private readonly group;
|
|
8
8
|
private readonly panelTransfer;
|
|
9
|
-
constructor(element: HTMLElement, accessor: DockviewComponent, group: DockviewGroupPanel);
|
|
9
|
+
constructor(element: HTMLElement, accessor: DockviewComponent, group: DockviewGroupPanel, disabled?: boolean);
|
|
10
10
|
isCancelled(_event: DragEvent): boolean;
|
|
11
11
|
getData(dragEvent: DragEvent): IDisposable;
|
|
12
12
|
}
|
|
@@ -4,8 +4,8 @@ import { DragHandler } from './abstractDragHandler';
|
|
|
4
4
|
import { LocalSelectionTransfer, PanelTransfer } from './dataTransfer';
|
|
5
5
|
import { addGhostImage } from './ghost';
|
|
6
6
|
export class GroupDragHandler extends DragHandler {
|
|
7
|
-
constructor(element, accessor, group) {
|
|
8
|
-
super(element);
|
|
7
|
+
constructor(element, accessor, group, disabled) {
|
|
8
|
+
super(element, disabled);
|
|
9
9
|
this.accessor = accessor;
|
|
10
10
|
this.group = group;
|
|
11
11
|
this.panelTransfer = LocalSelectionTransfer.getInstance();
|
|
@@ -12,6 +12,7 @@ export declare class Tab extends CompositeDisposable {
|
|
|
12
12
|
private readonly _element;
|
|
13
13
|
private readonly dropTarget;
|
|
14
14
|
private content;
|
|
15
|
+
private readonly dragHandler;
|
|
15
16
|
private readonly _onPointDown;
|
|
16
17
|
readonly onPointerDown: Event<MouseEvent>;
|
|
17
18
|
private readonly _onDropped;
|
|
@@ -6,8 +6,8 @@ import { Droptarget, } from '../../../dnd/droptarget';
|
|
|
6
6
|
import { DragHandler } from '../../../dnd/abstractDragHandler';
|
|
7
7
|
import { addGhostImage } from '../../../dnd/ghost';
|
|
8
8
|
class TabDragHandler extends DragHandler {
|
|
9
|
-
constructor(element, accessor, group, panel) {
|
|
10
|
-
super(element);
|
|
9
|
+
constructor(element, accessor, group, panel, disabled) {
|
|
10
|
+
super(element, disabled);
|
|
11
11
|
this.accessor = accessor;
|
|
12
12
|
this.group = group;
|
|
13
13
|
this.panel = panel;
|
|
@@ -43,7 +43,7 @@ export class Tab extends CompositeDisposable {
|
|
|
43
43
|
this._element.tabIndex = 0;
|
|
44
44
|
this._element.draggable = !this.accessor.options.disableDnd;
|
|
45
45
|
toggleClass(this.element, 'dv-inactive-tab', true);
|
|
46
|
-
|
|
46
|
+
this.dragHandler = new TabDragHandler(this._element, this.accessor, this.group, this.panel, !!this.accessor.options.disableDnd);
|
|
47
47
|
this.dropTarget = new Droptarget(this._element, {
|
|
48
48
|
acceptedTargetZones: ['left', 'right'],
|
|
49
49
|
overlayModel: { activationSize: { value: 50, type: 'percentage' } },
|
|
@@ -60,7 +60,7 @@ export class Tab extends CompositeDisposable {
|
|
|
60
60
|
getOverrideTarget: () => { var _a; return (_a = group.model.dropTargetContainer) === null || _a === void 0 ? void 0 : _a.model; },
|
|
61
61
|
});
|
|
62
62
|
this.onWillShowOverlay = this.dropTarget.onWillShowOverlay;
|
|
63
|
-
this.addDisposables(this._onPointDown, this._onDropped, this._onDragStart, dragHandler.onDragStart((event) => {
|
|
63
|
+
this.addDisposables(this._onPointDown, this._onDropped, this._onDragStart, this.dragHandler.onDragStart((event) => {
|
|
64
64
|
if (event.dataTransfer) {
|
|
65
65
|
const style = getComputedStyle(this.element);
|
|
66
66
|
const newNode = this.element.cloneNode(true);
|
|
@@ -72,7 +72,7 @@ export class Tab extends CompositeDisposable {
|
|
|
72
72
|
});
|
|
73
73
|
}
|
|
74
74
|
this._onDragStart.fire(event);
|
|
75
|
-
}), dragHandler, addDisposableListener(this._element, 'pointerdown', (event) => {
|
|
75
|
+
}), this.dragHandler, addDisposableListener(this._element, 'pointerdown', (event) => {
|
|
76
76
|
this._onPointDown.fire(event);
|
|
77
77
|
}), this.dropTarget.onDrop((event) => {
|
|
78
78
|
this._onDropped.fire(event);
|
|
@@ -91,6 +91,7 @@ export class Tab extends CompositeDisposable {
|
|
|
91
91
|
}
|
|
92
92
|
updateDragAndDropState() {
|
|
93
93
|
this._element.draggable = !this.accessor.options.disableDnd;
|
|
94
|
+
this.dragHandler.setDisabled(!!this.accessor.options.disableDnd);
|
|
94
95
|
}
|
|
95
96
|
dispose() {
|
|
96
97
|
super.dispose();
|
|
@@ -8,6 +8,7 @@ export declare class VoidContainer extends CompositeDisposable {
|
|
|
8
8
|
private readonly group;
|
|
9
9
|
private readonly _element;
|
|
10
10
|
private readonly dropTarget;
|
|
11
|
+
private readonly handler;
|
|
11
12
|
private readonly _onDrop;
|
|
12
13
|
readonly onDrop: Event<DroptargetEvent>;
|
|
13
14
|
private readonly _onDragStart;
|
|
@@ -23,7 +23,7 @@ export class VoidContainer extends CompositeDisposable {
|
|
|
23
23
|
this.addDisposables(this._onDrop, this._onDragStart, addDisposableListener(this._element, 'pointerdown', () => {
|
|
24
24
|
this.accessor.doSetGroupActive(this.group);
|
|
25
25
|
}));
|
|
26
|
-
|
|
26
|
+
this.handler = new GroupDragHandler(this._element, accessor, group, !!this.accessor.options.disableDnd);
|
|
27
27
|
this.dropTarget = new Droptarget(this._element, {
|
|
28
28
|
acceptedTargetZones: ['center'],
|
|
29
29
|
canDisplayOverlay: (event, position) => {
|
|
@@ -36,7 +36,7 @@ export class VoidContainer extends CompositeDisposable {
|
|
|
36
36
|
getOverrideTarget: () => { var _a; return (_a = group.model.dropTargetContainer) === null || _a === void 0 ? void 0 : _a.model; },
|
|
37
37
|
});
|
|
38
38
|
this.onWillShowOverlay = this.dropTarget.onWillShowOverlay;
|
|
39
|
-
this.addDisposables(handler, handler.onDragStart((event) => {
|
|
39
|
+
this.addDisposables(this.handler, this.handler.onDragStart((event) => {
|
|
40
40
|
this._onDragStart.fire(event);
|
|
41
41
|
}), this.dropTarget.onDrop((event) => {
|
|
42
42
|
this._onDrop.fire(event);
|
|
@@ -45,5 +45,6 @@ export class VoidContainer extends CompositeDisposable {
|
|
|
45
45
|
updateDragAndDropState() {
|
|
46
46
|
this._element.draggable = !this.accessor.options.disableDnd;
|
|
47
47
|
toggleClass(this._element, 'dv-draggable', !this.accessor.options.disableDnd);
|
|
48
|
+
this.handler.setDisabled(!!this.accessor.options.disableDnd);
|
|
48
49
|
}
|
|
49
50
|
}
|
|
@@ -219,6 +219,7 @@ export declare class DockviewComponent extends BaseGrid<DockviewGroupPanel> impl
|
|
|
219
219
|
private readonly _floatingGroups;
|
|
220
220
|
private readonly _popoutGroups;
|
|
221
221
|
private readonly _rootDropTarget;
|
|
222
|
+
private _popoutRestorationPromise;
|
|
222
223
|
private readonly _onDidRemoveGroup;
|
|
223
224
|
readonly onDidRemoveGroup: Event<DockviewGroupPanel>;
|
|
224
225
|
protected readonly _onDidAddGroup: Emitter<DockviewGroupPanel>;
|
|
@@ -235,6 +236,11 @@ export declare class DockviewComponent extends BaseGrid<DockviewGroupPanel> impl
|
|
|
235
236
|
get renderer(): DockviewPanelRenderer;
|
|
236
237
|
get api(): DockviewApi;
|
|
237
238
|
get floatingGroups(): DockviewFloatingGroupPanel[];
|
|
239
|
+
/**
|
|
240
|
+
* Promise that resolves when all popout groups from the last fromJSON call are restored.
|
|
241
|
+
* Useful for tests that need to wait for delayed popout creation.
|
|
242
|
+
*/
|
|
243
|
+
get popoutRestorationPromise(): Promise<void>;
|
|
238
244
|
constructor(container: HTMLElement, options: DockviewComponentOptions);
|
|
239
245
|
setVisible(panel: DockviewGroupPanel, visible: boolean): void;
|
|
240
246
|
addPopoutGroup(itemToPopout: DockviewPanel | DockviewGroupPanel, options?: DockviewPopoutGroupOptions): Promise<boolean>;
|
|
@@ -19,7 +19,7 @@ import { getPanelData } from '../dnd/dataTransfer';
|
|
|
19
19
|
import { Overlay } from '../overlay/overlay';
|
|
20
20
|
import { addTestId, Classnames, getDockviewTheme, onDidWindowResizeEnd, onDidWindowMoveEnd, toggleClass, watchElementResize, } from '../dom';
|
|
21
21
|
import { DockviewFloatingGroupPanel } from './dockviewFloatingGroupPanel';
|
|
22
|
-
import { DEFAULT_FLOATING_GROUP_OVERFLOW_SIZE, DEFAULT_FLOATING_GROUP_POSITION, } from '../constants';
|
|
22
|
+
import { DEFAULT_FLOATING_GROUP_OVERFLOW_SIZE, DEFAULT_FLOATING_GROUP_POSITION, DESERIALIZATION_POPOUT_DELAY_MS, } from '../constants';
|
|
23
23
|
import { OverlayRenderContainer, } from '../overlay/overlayRenderContainer';
|
|
24
24
|
import { PopoutWindow } from '../popoutWindow';
|
|
25
25
|
import { StrictEventsSequencing } from './strictEventsSequencing';
|
|
@@ -74,6 +74,13 @@ export class DockviewComponent extends BaseGrid {
|
|
|
74
74
|
get floatingGroups() {
|
|
75
75
|
return this._floatingGroups;
|
|
76
76
|
}
|
|
77
|
+
/**
|
|
78
|
+
* Promise that resolves when all popout groups from the last fromJSON call are restored.
|
|
79
|
+
* Useful for tests that need to wait for delayed popout creation.
|
|
80
|
+
*/
|
|
81
|
+
get popoutRestorationPromise() {
|
|
82
|
+
return this._popoutRestorationPromise;
|
|
83
|
+
}
|
|
77
84
|
constructor(container, options) {
|
|
78
85
|
var _a, _b, _c;
|
|
79
86
|
super(container, {
|
|
@@ -122,6 +129,7 @@ export class DockviewComponent extends BaseGrid {
|
|
|
122
129
|
this.onDidMaximizedGroupChange = this._onDidMaximizedGroupChange.event;
|
|
123
130
|
this._floatingGroups = [];
|
|
124
131
|
this._popoutGroups = [];
|
|
132
|
+
this._popoutRestorationPromise = Promise.resolve();
|
|
125
133
|
this._onDidRemoveGroup = new Emitter();
|
|
126
134
|
this.onDidRemoveGroup = this._onDidRemoveGroup.event;
|
|
127
135
|
this._onDidAddGroup = new Emitter();
|
|
@@ -671,6 +679,7 @@ export class DockviewComponent extends BaseGrid {
|
|
|
671
679
|
this.updateWatermark();
|
|
672
680
|
}
|
|
673
681
|
orthogonalize(position, options) {
|
|
682
|
+
this.gridview.normalize();
|
|
674
683
|
switch (position) {
|
|
675
684
|
case 'top':
|
|
676
685
|
case 'bottom':
|
|
@@ -916,18 +925,30 @@ export class DockviewComponent extends BaseGrid {
|
|
|
916
925
|
});
|
|
917
926
|
}
|
|
918
927
|
const serializedPopoutGroups = (_b = data.popoutGroups) !== null && _b !== void 0 ? _b : [];
|
|
919
|
-
|
|
928
|
+
// Create a promise that resolves when all popout groups are created
|
|
929
|
+
const popoutPromises = [];
|
|
930
|
+
// Queue popup group creation with delays to avoid browser blocking
|
|
931
|
+
serializedPopoutGroups.forEach((serializedPopoutGroup, index) => {
|
|
920
932
|
const { data, position, gridReferenceGroup, url } = serializedPopoutGroup;
|
|
921
933
|
const group = createGroupFromSerializedState(data);
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
934
|
+
// Add a small delay for each popup after the first to avoid browser popup blocking
|
|
935
|
+
const popoutPromise = new Promise((resolve) => {
|
|
936
|
+
setTimeout(() => {
|
|
937
|
+
this.addPopoutGroup(group, {
|
|
938
|
+
position: position !== null && position !== void 0 ? position : undefined,
|
|
939
|
+
overridePopoutGroup: gridReferenceGroup ? group : undefined,
|
|
940
|
+
referenceGroup: gridReferenceGroup
|
|
941
|
+
? this.getPanel(gridReferenceGroup)
|
|
942
|
+
: undefined,
|
|
943
|
+
popoutUrl: url,
|
|
944
|
+
});
|
|
945
|
+
resolve();
|
|
946
|
+
}, index * DESERIALIZATION_POPOUT_DELAY_MS); // 100ms delay between each popup
|
|
929
947
|
});
|
|
930
|
-
|
|
948
|
+
popoutPromises.push(popoutPromise);
|
|
949
|
+
});
|
|
950
|
+
// Store the promise for tests to wait on
|
|
951
|
+
this._popoutRestorationPromise = Promise.all(popoutPromises).then(() => void 0);
|
|
931
952
|
for (const floatingGroup of this._floatingGroups) {
|
|
932
953
|
floatingGroup.overlay.setBounds();
|
|
933
954
|
}
|
|
@@ -974,6 +995,10 @@ export class DockviewComponent extends BaseGrid {
|
|
|
974
995
|
throw err;
|
|
975
996
|
}
|
|
976
997
|
this.updateWatermark();
|
|
998
|
+
// Force position updates for always visible panels after DOM layout is complete
|
|
999
|
+
requestAnimationFrame(() => {
|
|
1000
|
+
this.overlayRenderContainer.updateAllPositions();
|
|
1001
|
+
});
|
|
977
1002
|
this._onDidLayoutFromJSON.fire();
|
|
978
1003
|
}
|
|
979
1004
|
clear() {
|
|
@@ -1361,11 +1386,13 @@ export class DockviewComponent extends BaseGrid {
|
|
|
1361
1386
|
// remove the group and do not set a new group as active
|
|
1362
1387
|
this.doRemoveGroup(sourceGroup, { skipActive: true });
|
|
1363
1388
|
}
|
|
1389
|
+
// Check if destination group is empty - if so, force render the component
|
|
1390
|
+
const isDestinationGroupEmpty = destinationGroup.model.size === 0;
|
|
1364
1391
|
this.movingLock(() => {
|
|
1365
1392
|
var _a;
|
|
1366
1393
|
return destinationGroup.model.openPanel(removedPanel, {
|
|
1367
1394
|
index: destinationIndex,
|
|
1368
|
-
skipSetActive: (_a = options.skipSetActive) !== null && _a !== void 0 ? _a : false,
|
|
1395
|
+
skipSetActive: ((_a = options.skipSetActive) !== null && _a !== void 0 ? _a : false) && !isDestinationGroupEmpty,
|
|
1369
1396
|
skipSetGroupActive: true,
|
|
1370
1397
|
});
|
|
1371
1398
|
});
|
|
@@ -1474,7 +1501,6 @@ export class DockviewComponent extends BaseGrid {
|
|
|
1474
1501
|
const target = options.to.position;
|
|
1475
1502
|
if (target === 'center') {
|
|
1476
1503
|
const activePanel = from.activePanel;
|
|
1477
|
-
const targetActivePanel = to.activePanel;
|
|
1478
1504
|
const panels = this.movingLock(() => [...from.panels].map((p) => from.model.removePanel(p.id, {
|
|
1479
1505
|
skipSetActive: true,
|
|
1480
1506
|
})));
|
|
@@ -1484,22 +1510,21 @@ export class DockviewComponent extends BaseGrid {
|
|
|
1484
1510
|
this.movingLock(() => {
|
|
1485
1511
|
for (const panel of panels) {
|
|
1486
1512
|
to.model.openPanel(panel, {
|
|
1487
|
-
skipSetActive:
|
|
1513
|
+
skipSetActive: panel !== activePanel,
|
|
1488
1514
|
skipSetGroupActive: true,
|
|
1489
1515
|
});
|
|
1490
1516
|
}
|
|
1491
1517
|
});
|
|
1492
|
-
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
|
|
1496
|
-
|
|
1518
|
+
// Ensure group becomes active after move
|
|
1519
|
+
if (options.skipSetActive !== true) {
|
|
1520
|
+
// For center moves (merges), we need to ensure the target group is active
|
|
1521
|
+
// unless explicitly told not to (skipSetActive: true)
|
|
1522
|
+
this.doSetGroupAndPanelActive(to);
|
|
1497
1523
|
}
|
|
1498
|
-
else if (
|
|
1499
|
-
//
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
});
|
|
1524
|
+
else if (!this.activePanel) {
|
|
1525
|
+
// Even with skipSetActive: true, ensure there's an active panel if none exists
|
|
1526
|
+
// This maintains basic functionality while respecting skipSetActive
|
|
1527
|
+
this.doSetGroupAndPanelActive(to);
|
|
1503
1528
|
}
|
|
1504
1529
|
}
|
|
1505
1530
|
else {
|
|
@@ -1529,20 +1554,26 @@ export class DockviewComponent extends BaseGrid {
|
|
|
1529
1554
|
if (selectedPopoutGroup.referenceGroup) {
|
|
1530
1555
|
const referenceGroup = this.getPanel(selectedPopoutGroup.referenceGroup);
|
|
1531
1556
|
if (referenceGroup && !referenceGroup.api.isVisible) {
|
|
1532
|
-
this.doRemoveGroup(referenceGroup, {
|
|
1557
|
+
this.doRemoveGroup(referenceGroup, {
|
|
1558
|
+
skipActive: true,
|
|
1559
|
+
});
|
|
1533
1560
|
}
|
|
1534
1561
|
}
|
|
1535
1562
|
// Manually dispose the window without triggering restoration
|
|
1536
1563
|
selectedPopoutGroup.window.dispose();
|
|
1537
1564
|
// Update group's location and containers for target
|
|
1538
1565
|
if (to.api.location.type === 'grid') {
|
|
1539
|
-
from.model.renderContainer =
|
|
1540
|
-
|
|
1566
|
+
from.model.renderContainer =
|
|
1567
|
+
this.overlayRenderContainer;
|
|
1568
|
+
from.model.dropTargetContainer =
|
|
1569
|
+
this.rootDropTargetContainer;
|
|
1541
1570
|
from.model.location = { type: 'grid' };
|
|
1542
1571
|
}
|
|
1543
1572
|
else if (to.api.location.type === 'floating') {
|
|
1544
|
-
from.model.renderContainer =
|
|
1545
|
-
|
|
1573
|
+
from.model.renderContainer =
|
|
1574
|
+
this.overlayRenderContainer;
|
|
1575
|
+
from.model.dropTargetContainer =
|
|
1576
|
+
this.rootDropTargetContainer;
|
|
1546
1577
|
from.model.location = { type: 'floating' };
|
|
1547
1578
|
}
|
|
1548
1579
|
break;
|
|
@@ -1610,8 +1641,12 @@ export class DockviewComponent extends BaseGrid {
|
|
|
1610
1641
|
from.panels.forEach((panel) => {
|
|
1611
1642
|
this._onDidMovePanel.fire({ panel, from });
|
|
1612
1643
|
});
|
|
1613
|
-
|
|
1614
|
-
|
|
1644
|
+
// Ensure group becomes active after move
|
|
1645
|
+
if (options.skipSetActive === false) {
|
|
1646
|
+
// Only activate when explicitly requested (skipSetActive: false)
|
|
1647
|
+
// Use 'to' group for non-center moves since 'from' may have been destroyed
|
|
1648
|
+
const targetGroup = to !== null && to !== void 0 ? to : from;
|
|
1649
|
+
this.doSetGroupAndPanelActive(targetGroup);
|
|
1615
1650
|
}
|
|
1616
1651
|
}
|
|
1617
1652
|
doSetGroupActive(group) {
|
|
@@ -134,6 +134,7 @@ export declare class Gridview implements IDisposable {
|
|
|
134
134
|
private _deserializeNode;
|
|
135
135
|
private get root();
|
|
136
136
|
private set root(value);
|
|
137
|
+
normalize(): void;
|
|
137
138
|
/**
|
|
138
139
|
* If the root is orientated as a VERTICAL node then nest the existing root within a new HORIZIONTAL root node
|
|
139
140
|
* If the root is orientated as a HORIZONTAL node then nest the existing root within a new VERITCAL root node
|
|
@@ -17,6 +17,19 @@ function findLeaf(candiateNode, last) {
|
|
|
17
17
|
}
|
|
18
18
|
throw new Error('invalid node');
|
|
19
19
|
}
|
|
20
|
+
function cloneNode(node, size, orthogonalSize) {
|
|
21
|
+
if (node instanceof BranchNode) {
|
|
22
|
+
const result = new BranchNode(node.orientation, node.proportionalLayout, node.styles, size, orthogonalSize, node.disabled, node.margin);
|
|
23
|
+
for (let i = node.children.length - 1; i >= 0; i--) {
|
|
24
|
+
const child = node.children[i];
|
|
25
|
+
result.addChild(cloneNode(child, child.size, child.orthogonalSize), child.size, 0, true);
|
|
26
|
+
}
|
|
27
|
+
return result;
|
|
28
|
+
}
|
|
29
|
+
else {
|
|
30
|
+
return new LeafNode(node.view, node.orientation, orthogonalSize);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
20
33
|
function flipNode(node, size, orthogonalSize) {
|
|
21
34
|
if (node instanceof BranchNode) {
|
|
22
35
|
const result = new BranchNode(orthogonal(node.orientation), node.proportionalLayout, node.styles, size, orthogonalSize, node.disabled, node.margin);
|
|
@@ -367,6 +380,29 @@ export class Gridview {
|
|
|
367
380
|
this._onDidChange.fire(e);
|
|
368
381
|
});
|
|
369
382
|
}
|
|
383
|
+
normalize() {
|
|
384
|
+
if (!this._root) {
|
|
385
|
+
return;
|
|
386
|
+
}
|
|
387
|
+
if (this._root.children.length !== 1) {
|
|
388
|
+
return;
|
|
389
|
+
}
|
|
390
|
+
const oldRoot = this.root;
|
|
391
|
+
// can remove one level of redundant branching if there is only a single child
|
|
392
|
+
const childReference = oldRoot.children[0];
|
|
393
|
+
if (childReference instanceof LeafNode) {
|
|
394
|
+
return;
|
|
395
|
+
}
|
|
396
|
+
oldRoot.element.remove();
|
|
397
|
+
const child = oldRoot.removeChild(0); // Remove child to prevent double disposal
|
|
398
|
+
oldRoot.dispose(); // Dispose old root (won't dispose removed child)
|
|
399
|
+
child.dispose(); // Dispose the removed child
|
|
400
|
+
this._root = cloneNode(childReference, childReference.size, childReference.orthogonalSize);
|
|
401
|
+
this.element.appendChild(this._root.element);
|
|
402
|
+
this.disposable.value = this._root.onDidChange((e) => {
|
|
403
|
+
this._onDidChange.fire(e);
|
|
404
|
+
});
|
|
405
|
+
}
|
|
370
406
|
/**
|
|
371
407
|
* If the root is orientated as a VERTICAL node then nest the existing root within a new HORIZIONTAL root node
|
|
372
408
|
* If the root is orientated as a HORIZONTAL node then nest the existing root within a new VERITCAL root node
|
|
@@ -12,7 +12,10 @@ export declare class OverlayRenderContainer extends CompositeDisposable {
|
|
|
12
12
|
readonly accessor: DockviewComponent;
|
|
13
13
|
private readonly map;
|
|
14
14
|
private _disposed;
|
|
15
|
+
private readonly positionCache;
|
|
16
|
+
private readonly pendingUpdates;
|
|
15
17
|
constructor(element: HTMLElement, accessor: DockviewComponent);
|
|
18
|
+
updateAllPositions(): void;
|
|
16
19
|
detatch(panel: IDockviewPanel): boolean;
|
|
17
20
|
attach(options: {
|
|
18
21
|
panel: IDockviewPanel;
|
|
@@ -1,6 +1,34 @@
|
|
|
1
1
|
import { DragAndDropObserver } from '../dnd/dnd';
|
|
2
2
|
import { getDomNodePagePosition, toggleClass } from '../dom';
|
|
3
3
|
import { CompositeDisposable, Disposable, MutableDisposable, } from '../lifecycle';
|
|
4
|
+
class PositionCache {
|
|
5
|
+
constructor() {
|
|
6
|
+
this.cache = new Map();
|
|
7
|
+
this.currentFrameId = 0;
|
|
8
|
+
this.rafId = null;
|
|
9
|
+
}
|
|
10
|
+
getPosition(element) {
|
|
11
|
+
const cached = this.cache.get(element);
|
|
12
|
+
if (cached && cached.frameId === this.currentFrameId) {
|
|
13
|
+
return cached.rect;
|
|
14
|
+
}
|
|
15
|
+
this.scheduleFrameUpdate();
|
|
16
|
+
const rect = getDomNodePagePosition(element);
|
|
17
|
+
this.cache.set(element, { rect, frameId: this.currentFrameId });
|
|
18
|
+
return rect;
|
|
19
|
+
}
|
|
20
|
+
invalidate() {
|
|
21
|
+
this.currentFrameId++;
|
|
22
|
+
}
|
|
23
|
+
scheduleFrameUpdate() {
|
|
24
|
+
if (this.rafId)
|
|
25
|
+
return;
|
|
26
|
+
this.rafId = requestAnimationFrame(() => {
|
|
27
|
+
this.currentFrameId++;
|
|
28
|
+
this.rafId = null;
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
}
|
|
4
32
|
function createFocusableElement() {
|
|
5
33
|
const element = document.createElement('div');
|
|
6
34
|
element.tabIndex = -1;
|
|
@@ -13,6 +41,8 @@ export class OverlayRenderContainer extends CompositeDisposable {
|
|
|
13
41
|
this.accessor = accessor;
|
|
14
42
|
this.map = {};
|
|
15
43
|
this._disposed = false;
|
|
44
|
+
this.positionCache = new PositionCache();
|
|
45
|
+
this.pendingUpdates = new Set();
|
|
16
46
|
this.addDisposables(Disposable.from(() => {
|
|
17
47
|
for (const value of Object.values(this.map)) {
|
|
18
48
|
value.disposable.dispose();
|
|
@@ -21,6 +51,19 @@ export class OverlayRenderContainer extends CompositeDisposable {
|
|
|
21
51
|
this._disposed = true;
|
|
22
52
|
}));
|
|
23
53
|
}
|
|
54
|
+
updateAllPositions() {
|
|
55
|
+
if (this._disposed) {
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
// Invalidate position cache to force recalculation
|
|
59
|
+
this.positionCache.invalidate();
|
|
60
|
+
// Call resize function directly for all visible panels
|
|
61
|
+
for (const entry of Object.values(this.map)) {
|
|
62
|
+
if (entry.panel.api.isVisible && entry.resize) {
|
|
63
|
+
entry.resize();
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
24
67
|
detatch(panel) {
|
|
25
68
|
if (this.map[panel.api.id]) {
|
|
26
69
|
const { disposable, destroy } = this.map[panel.api.id];
|
|
@@ -51,17 +94,33 @@ export class OverlayRenderContainer extends CompositeDisposable {
|
|
|
51
94
|
this.element.appendChild(focusContainer);
|
|
52
95
|
}
|
|
53
96
|
const resize = () => {
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
97
|
+
const panelId = panel.api.id;
|
|
98
|
+
if (this.pendingUpdates.has(panelId)) {
|
|
99
|
+
return; // Update already scheduled
|
|
100
|
+
}
|
|
101
|
+
this.pendingUpdates.add(panelId);
|
|
102
|
+
requestAnimationFrame(() => {
|
|
103
|
+
this.pendingUpdates.delete(panelId);
|
|
104
|
+
if (this.isDisposed || !this.map[panelId]) {
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
const box = this.positionCache.getPosition(referenceContainer.element);
|
|
108
|
+
const box2 = this.positionCache.getPosition(this.element);
|
|
109
|
+
// Use traditional positioning for overlay containers
|
|
110
|
+
const left = box.left - box2.left;
|
|
111
|
+
const top = box.top - box2.top;
|
|
112
|
+
const width = box.width;
|
|
113
|
+
const height = box.height;
|
|
114
|
+
focusContainer.style.left = `${left}px`;
|
|
115
|
+
focusContainer.style.top = `${top}px`;
|
|
116
|
+
focusContainer.style.width = `${width}px`;
|
|
117
|
+
focusContainer.style.height = `${height}px`;
|
|
118
|
+
toggleClass(focusContainer, 'dv-render-overlay-float', panel.group.api.location.type === 'floating');
|
|
119
|
+
});
|
|
62
120
|
};
|
|
63
121
|
const visibilityChanged = () => {
|
|
64
122
|
if (panel.api.isVisible) {
|
|
123
|
+
this.positionCache.invalidate();
|
|
65
124
|
resize();
|
|
66
125
|
}
|
|
67
126
|
focusContainer.style.display = panel.api.isVisible ? '' : 'none';
|
|
@@ -156,6 +215,8 @@ export class OverlayRenderContainer extends CompositeDisposable {
|
|
|
156
215
|
this.map[panel.api.id].disposable.dispose();
|
|
157
216
|
// and reset the disposable to the active reference-container
|
|
158
217
|
this.map[panel.api.id].disposable = disposable;
|
|
218
|
+
// store the resize function for direct access
|
|
219
|
+
this.map[panel.api.id].resize = resize;
|
|
159
220
|
return focusContainer;
|
|
160
221
|
}
|
|
161
222
|
}
|