dockview-core 4.6.0 → 4.7.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.
- package/dist/cjs/constants.d.ts +1 -0
- package/dist/cjs/constants.js +2 -1
- package/dist/cjs/dnd/droptarget.js +46 -17
- package/dist/cjs/dockview/dockviewComponent.d.ts +6 -0
- package/dist/cjs/dockview/dockviewComponent.js +112 -85
- 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 +215 -55
- 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 +214 -54
- package/dist/dockview-core.amd.noStyle.js.map +1 -1
- package/dist/dockview-core.cjs.js +215 -55
- package/dist/dockview-core.cjs.js.map +1 -1
- package/dist/dockview-core.esm.js +215 -55
- 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 +215 -55
- 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 +214 -54
- 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/droptarget.js +46 -17
- package/dist/esm/dockview/dockviewComponent.d.ts +6 -0
- package/dist/esm/dockview/dockviewComponent.js +62 -29
- 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
|
@@ -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);
|
|
@@ -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() {
|
|
@@ -1474,7 +1499,6 @@ export class DockviewComponent extends BaseGrid {
|
|
|
1474
1499
|
const target = options.to.position;
|
|
1475
1500
|
if (target === 'center') {
|
|
1476
1501
|
const activePanel = from.activePanel;
|
|
1477
|
-
const targetActivePanel = to.activePanel;
|
|
1478
1502
|
const panels = this.movingLock(() => [...from.panels].map((p) => from.model.removePanel(p.id, {
|
|
1479
1503
|
skipSetActive: true,
|
|
1480
1504
|
})));
|
|
@@ -1484,22 +1508,21 @@ export class DockviewComponent extends BaseGrid {
|
|
|
1484
1508
|
this.movingLock(() => {
|
|
1485
1509
|
for (const panel of panels) {
|
|
1486
1510
|
to.model.openPanel(panel, {
|
|
1487
|
-
skipSetActive:
|
|
1511
|
+
skipSetActive: panel !== activePanel,
|
|
1488
1512
|
skipSetGroupActive: true,
|
|
1489
1513
|
});
|
|
1490
1514
|
}
|
|
1491
1515
|
});
|
|
1492
|
-
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
|
|
1496
|
-
|
|
1516
|
+
// Ensure group becomes active after move
|
|
1517
|
+
if (options.skipSetActive !== true) {
|
|
1518
|
+
// For center moves (merges), we need to ensure the target group is active
|
|
1519
|
+
// unless explicitly told not to (skipSetActive: true)
|
|
1520
|
+
this.doSetGroupAndPanelActive(to);
|
|
1497
1521
|
}
|
|
1498
|
-
else if (
|
|
1499
|
-
//
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
});
|
|
1522
|
+
else if (!this.activePanel) {
|
|
1523
|
+
// Even with skipSetActive: true, ensure there's an active panel if none exists
|
|
1524
|
+
// This maintains basic functionality while respecting skipSetActive
|
|
1525
|
+
this.doSetGroupAndPanelActive(to);
|
|
1503
1526
|
}
|
|
1504
1527
|
}
|
|
1505
1528
|
else {
|
|
@@ -1529,20 +1552,26 @@ export class DockviewComponent extends BaseGrid {
|
|
|
1529
1552
|
if (selectedPopoutGroup.referenceGroup) {
|
|
1530
1553
|
const referenceGroup = this.getPanel(selectedPopoutGroup.referenceGroup);
|
|
1531
1554
|
if (referenceGroup && !referenceGroup.api.isVisible) {
|
|
1532
|
-
this.doRemoveGroup(referenceGroup, {
|
|
1555
|
+
this.doRemoveGroup(referenceGroup, {
|
|
1556
|
+
skipActive: true,
|
|
1557
|
+
});
|
|
1533
1558
|
}
|
|
1534
1559
|
}
|
|
1535
1560
|
// Manually dispose the window without triggering restoration
|
|
1536
1561
|
selectedPopoutGroup.window.dispose();
|
|
1537
1562
|
// Update group's location and containers for target
|
|
1538
1563
|
if (to.api.location.type === 'grid') {
|
|
1539
|
-
from.model.renderContainer =
|
|
1540
|
-
|
|
1564
|
+
from.model.renderContainer =
|
|
1565
|
+
this.overlayRenderContainer;
|
|
1566
|
+
from.model.dropTargetContainer =
|
|
1567
|
+
this.rootDropTargetContainer;
|
|
1541
1568
|
from.model.location = { type: 'grid' };
|
|
1542
1569
|
}
|
|
1543
1570
|
else if (to.api.location.type === 'floating') {
|
|
1544
|
-
from.model.renderContainer =
|
|
1545
|
-
|
|
1571
|
+
from.model.renderContainer =
|
|
1572
|
+
this.overlayRenderContainer;
|
|
1573
|
+
from.model.dropTargetContainer =
|
|
1574
|
+
this.rootDropTargetContainer;
|
|
1546
1575
|
from.model.location = { type: 'floating' };
|
|
1547
1576
|
}
|
|
1548
1577
|
break;
|
|
@@ -1610,8 +1639,12 @@ export class DockviewComponent extends BaseGrid {
|
|
|
1610
1639
|
from.panels.forEach((panel) => {
|
|
1611
1640
|
this._onDidMovePanel.fire({ panel, from });
|
|
1612
1641
|
});
|
|
1613
|
-
|
|
1614
|
-
|
|
1642
|
+
// Ensure group becomes active after move
|
|
1643
|
+
if (options.skipSetActive === false) {
|
|
1644
|
+
// Only activate when explicitly requested (skipSetActive: false)
|
|
1645
|
+
// Use 'to' group for non-center moves since 'from' may have been destroyed
|
|
1646
|
+
const targetGroup = to !== null && to !== void 0 ? to : from;
|
|
1647
|
+
this.doSetGroupAndPanelActive(targetGroup);
|
|
1615
1648
|
}
|
|
1616
1649
|
}
|
|
1617
1650
|
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
|
}
|
package/dist/styles/dockview.css
CHANGED
|
@@ -9,6 +9,10 @@
|
|
|
9
9
|
height: 4px;
|
|
10
10
|
border-radius: 2px;
|
|
11
11
|
background-color: transparent;
|
|
12
|
+
/* GPU optimizations */
|
|
13
|
+
will-change: background-color, transform;
|
|
14
|
+
transform: translate3d(0, 0, 0);
|
|
15
|
+
backface-visibility: hidden;
|
|
12
16
|
transition-property: background-color;
|
|
13
17
|
transition-timing-function: ease-in-out;
|
|
14
18
|
transition-duration: 1s;
|
|
@@ -593,9 +597,14 @@
|
|
|
593
597
|
.dv-drop-target-container .dv-drop-target-anchor {
|
|
594
598
|
position: relative;
|
|
595
599
|
border: var(--dv-drag-over-border);
|
|
596
|
-
transition: opacity var(--dv-transition-duration) ease-in, top var(--dv-transition-duration) ease-out, left var(--dv-transition-duration) ease-out, width var(--dv-transition-duration) ease-out, height var(--dv-transition-duration) ease-out;
|
|
597
600
|
background-color: var(--dv-drag-over-background-color);
|
|
598
601
|
opacity: 1;
|
|
602
|
+
/* GPU optimizations */
|
|
603
|
+
will-change: transform, opacity;
|
|
604
|
+
transform: translate3d(0, 0, 0);
|
|
605
|
+
backface-visibility: hidden;
|
|
606
|
+
contain: layout paint;
|
|
607
|
+
transition: opacity var(--dv-transition-duration) ease-in, transform var(--dv-transition-duration) ease-out;
|
|
599
608
|
}
|
|
600
609
|
.dv-drop-target {
|
|
601
610
|
position: relative;
|
|
@@ -636,6 +645,7 @@
|
|
|
636
645
|
.dv-dockview {
|
|
637
646
|
position: relative;
|
|
638
647
|
background-color: var(--dv-group-view-background-color);
|
|
648
|
+
contain: layout;
|
|
639
649
|
}
|
|
640
650
|
.dv-dockview .dv-watermark-container {
|
|
641
651
|
position: absolute;
|
|
@@ -723,12 +733,18 @@
|
|
|
723
733
|
z-index: calc(var(--dv-overlay-z-index) - 2);
|
|
724
734
|
border: 1px solid var(--dv-tab-divider-color);
|
|
725
735
|
box-shadow: var(--dv-floating-box-shadow);
|
|
736
|
+
/* GPU optimizations for floating group movement */
|
|
737
|
+
will-change: transform, opacity;
|
|
738
|
+
transform: translate3d(0, 0, 0);
|
|
739
|
+
backface-visibility: hidden;
|
|
726
740
|
}
|
|
727
741
|
.dv-resize-container.dv-hidden {
|
|
728
742
|
display: none;
|
|
729
743
|
}
|
|
730
744
|
.dv-resize-container.dv-resize-container-dragging {
|
|
731
745
|
opacity: 0.5;
|
|
746
|
+
/* Enhanced GPU acceleration during drag */
|
|
747
|
+
will-change: transform, opacity;
|
|
732
748
|
}
|
|
733
749
|
.dv-resize-container .dv-resize-handle-top {
|
|
734
750
|
height: 4px;
|
|
@@ -806,7 +822,14 @@
|
|
|
806
822
|
--dv-overlay-z-index: var(--dv-overlay-z-index, 999);
|
|
807
823
|
position: absolute;
|
|
808
824
|
z-index: 1;
|
|
825
|
+
width: 100%;
|
|
809
826
|
height: 100%;
|
|
827
|
+
contain: layout paint;
|
|
828
|
+
isolation: isolate;
|
|
829
|
+
/* GPU optimizations */
|
|
830
|
+
will-change: transform;
|
|
831
|
+
transform: translate3d(0, 0, 0);
|
|
832
|
+
backface-visibility: hidden;
|
|
810
833
|
}
|
|
811
834
|
.dv-render-overlay.dv-render-overlay-float {
|
|
812
835
|
z-index: calc(var(--dv-overlay-z-index) - 1);
|
|
@@ -821,8 +844,11 @@
|
|
|
821
844
|
width: 100%;
|
|
822
845
|
}
|
|
823
846
|
.dv-pane-container.dv-animated .dv-view {
|
|
824
|
-
|
|
825
|
-
|
|
847
|
+
/* GPU optimizations for smooth pane animations */
|
|
848
|
+
will-change: transform;
|
|
849
|
+
transform: translate3d(0, 0, 0);
|
|
850
|
+
backface-visibility: hidden;
|
|
851
|
+
transition: transform 0.15s ease-out;
|
|
826
852
|
}
|
|
827
853
|
.dv-pane-container .dv-view {
|
|
828
854
|
overflow: hidden;
|
|
@@ -931,8 +957,11 @@
|
|
|
931
957
|
}
|
|
932
958
|
.dv-split-view-container.dv-animation .dv-view,
|
|
933
959
|
.dv-split-view-container.dv-animation .dv-sash {
|
|
934
|
-
|
|
935
|
-
|
|
960
|
+
/* GPU optimizations for smooth animations */
|
|
961
|
+
will-change: transform;
|
|
962
|
+
transform: translate3d(0, 0, 0);
|
|
963
|
+
backface-visibility: hidden;
|
|
964
|
+
transition: transform 0.15s ease-out;
|
|
936
965
|
}
|
|
937
966
|
.dv-split-view-container.dv-horizontal {
|
|
938
967
|
height: 100%;
|
|
@@ -1104,6 +1133,9 @@
|
|
|
1104
1133
|
height: 100%;
|
|
1105
1134
|
overflow: auto;
|
|
1106
1135
|
scrollbar-width: thin;
|
|
1136
|
+
/* GPU optimizations for smooth scrolling */
|
|
1137
|
+
will-change: scroll-position;
|
|
1138
|
+
transform: translate3d(0, 0, 0);
|
|
1107
1139
|
/* Track */
|
|
1108
1140
|
/* Handle */
|
|
1109
1141
|
}
|