@opensumi/ide-core-browser 3.2.6-next-1725007925.0 → 3.3.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/lib/bootstrap/app.view.d.ts.map +1 -1
- package/lib/bootstrap/app.view.js +1 -0
- package/lib/bootstrap/app.view.js.map +1 -1
- package/lib/components/layout/split-panel.d.ts.map +1 -1
- package/lib/components/layout/split-panel.js +7 -8
- package/lib/components/layout/split-panel.js.map +1 -1
- package/lib/components/resize/resize.d.ts.map +1 -1
- package/lib/components/resize/resize.js +111 -95
- package/lib/components/resize/resize.js.map +1 -1
- package/lib/dom/fastdom.d.ts +11 -0
- package/lib/dom/fastdom.d.ts.map +1 -0
- package/lib/dom/fastdom.js +96 -0
- package/lib/dom/fastdom.js.map +1 -0
- package/lib/dom/index.d.ts +2 -0
- package/lib/dom/index.d.ts.map +1 -1
- package/lib/dom/index.js +3 -1
- package/lib/dom/index.js.map +1 -1
- package/lib/dom/resize-observer.d.ts +18 -0
- package/lib/dom/resize-observer.d.ts.map +1 -0
- package/lib/dom/resize-observer.js +43 -0
- package/lib/dom/resize-observer.js.map +1 -0
- package/lib/layout/layout-hooks.d.ts.map +1 -1
- package/lib/layout/layout-hooks.js +27 -22
- package/lib/layout/layout-hooks.js.map +1 -1
- package/lib/layout/layout.interface.d.ts +1 -0
- package/lib/layout/layout.interface.d.ts.map +1 -1
- package/lib/layout/layout.interface.js +3 -0
- package/lib/layout/layout.interface.js.map +1 -1
- package/lib/layout/render.d.ts.map +1 -1
- package/lib/layout/render.js.map +1 -1
- package/lib/react-providers/slot.d.ts.map +1 -1
- package/lib/react-providers/slot.js +3 -0
- package/lib/react-providers/slot.js.map +1 -1
- package/package.json +5 -5
- package/src/bootstrap/app.view.tsx +1 -0
- package/src/components/layout/split-panel.tsx +8 -7
- package/src/components/resize/resize.tsx +127 -89
- package/src/dom/fastdom.ts +110 -0
- package/src/dom/index.ts +3 -0
- package/src/dom/resize-observer.ts +52 -0
- package/src/layout/layout-hooks.ts +35 -24
- package/src/layout/layout.interface.ts +5 -1
- package/src/layout/render.tsx +6 -6
- package/src/react-providers/slot.tsx +3 -0
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
import cls from 'classnames';
|
|
2
2
|
import React from 'react';
|
|
3
3
|
|
|
4
|
+
import { IDisposable } from '@opensumi/ide-core-common';
|
|
5
|
+
|
|
6
|
+
import { fastdom } from '../../dom';
|
|
7
|
+
|
|
4
8
|
import styles from './resize.module.less';
|
|
5
9
|
|
|
6
10
|
export const RESIZE_LOCK = 'resize-lock';
|
|
@@ -45,6 +49,8 @@ export interface IResizeHandleDelegate {
|
|
|
45
49
|
export function preventWebviewCatchMouseEvents() {
|
|
46
50
|
const iframes = document.getElementsByTagName('iframe');
|
|
47
51
|
const webviews = document.getElementsByTagName('webview');
|
|
52
|
+
const shadowRootHost = document.getElementsByClassName('shadow-root-host');
|
|
53
|
+
|
|
48
54
|
for (const webview of webviews as unknown as HTMLElement[]) {
|
|
49
55
|
webview.classList.add('none-pointer-event');
|
|
50
56
|
}
|
|
@@ -52,7 +58,6 @@ export function preventWebviewCatchMouseEvents() {
|
|
|
52
58
|
iframe.classList.add('none-pointer-event');
|
|
53
59
|
}
|
|
54
60
|
|
|
55
|
-
const shadowRootHost = document.getElementsByClassName('shadow-root-host');
|
|
56
61
|
for (const host of shadowRootHost as unknown as HTMLElement[]) {
|
|
57
62
|
host?.classList.add('none-pointer-event');
|
|
58
63
|
}
|
|
@@ -61,6 +66,8 @@ export function preventWebviewCatchMouseEvents() {
|
|
|
61
66
|
export function allowWebviewCatchMouseEvents() {
|
|
62
67
|
const iframes = document.getElementsByTagName('iframe');
|
|
63
68
|
const webviews = document.getElementsByTagName('webview');
|
|
69
|
+
const shadowRootHost = document.getElementsByClassName('shadow-root-host');
|
|
70
|
+
|
|
64
71
|
for (const webview of webviews as unknown as HTMLElement[]) {
|
|
65
72
|
webview.classList.remove('none-pointer-event');
|
|
66
73
|
}
|
|
@@ -68,7 +75,6 @@ export function allowWebviewCatchMouseEvents() {
|
|
|
68
75
|
iframe.classList.remove('none-pointer-event');
|
|
69
76
|
}
|
|
70
77
|
|
|
71
|
-
const shadowRootHost = document.getElementsByClassName('shadow-root-host');
|
|
72
78
|
for (const host of shadowRootHost as unknown as HTMLElement[]) {
|
|
73
79
|
host?.classList.remove('none-pointer-event');
|
|
74
80
|
}
|
|
@@ -82,16 +88,18 @@ export const ResizeHandleHorizontal = (props: ResizeHandleProps) => {
|
|
|
82
88
|
const startNextWidth = React.useRef<number>(0);
|
|
83
89
|
const prevElement = React.useRef<HTMLElement | null>();
|
|
84
90
|
const nextElement = React.useRef<HTMLElement | null>();
|
|
85
|
-
const
|
|
91
|
+
const requestFrameToDispose = React.useRef<IDisposable>();
|
|
86
92
|
|
|
87
93
|
const setSize = (prev: number, next: number) => {
|
|
88
|
-
const parentWidth = ref.current!.parentElement!.offsetWidth;
|
|
89
94
|
const prevEle = props.findPrevElement ? props.findPrevElement() : prevElement.current!;
|
|
90
95
|
const nextEle = props.findNextElement ? props.findNextElement() : nextElement.current!;
|
|
91
96
|
|
|
92
97
|
if ((prevEle && prevEle.classList.contains(RESIZE_LOCK)) || (nextEle && nextEle.classList.contains(RESIZE_LOCK))) {
|
|
93
98
|
return;
|
|
94
99
|
}
|
|
100
|
+
|
|
101
|
+
const parentWidth = ref.current!.parentElement!.offsetWidth;
|
|
102
|
+
|
|
95
103
|
const prevMinResize = Number(prevEle?.dataset.minResize || 0);
|
|
96
104
|
const nextMinResize = Number(nextEle?.dataset.minResize || 0);
|
|
97
105
|
|
|
@@ -107,6 +115,7 @@ export const ResizeHandleHorizontal = (props: ResizeHandleProps) => {
|
|
|
107
115
|
return;
|
|
108
116
|
}
|
|
109
117
|
}
|
|
118
|
+
|
|
110
119
|
if (nextEle) {
|
|
111
120
|
nextEle.style.width = next * 100 + '%';
|
|
112
121
|
}
|
|
@@ -245,32 +254,39 @@ export const ResizeHandleHorizontal = (props: ResizeHandleProps) => {
|
|
|
245
254
|
};
|
|
246
255
|
|
|
247
256
|
const setAbsoluteSize = (size: number, isLatter?: boolean) => {
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
const
|
|
253
|
-
const
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
257
|
+
fastdom.measure(() => {
|
|
258
|
+
const currentPrev = prevElement.current!.clientWidth;
|
|
259
|
+
const currentNext = nextElement.current!.clientWidth;
|
|
260
|
+
|
|
261
|
+
const nextTotolWidth = +nextElement.current!.style.width!.replace('%', '');
|
|
262
|
+
const prevTotalWidth = +prevElement.current!.style.width!.replace('%', '');
|
|
263
|
+
fastdom.mutate(() => {
|
|
264
|
+
const totalSize = currentPrev + currentNext;
|
|
265
|
+
if (props.flexMode) {
|
|
266
|
+
const prevWidth = props.flexMode === ResizeFlexMode.Prev ? size : totalSize - size;
|
|
267
|
+
const nextWidth = props.flexMode === ResizeFlexMode.Next ? size : totalSize - size;
|
|
268
|
+
flexModeSetSize(prevWidth, nextWidth, true);
|
|
269
|
+
} else {
|
|
270
|
+
const currentTotalWidth = nextTotolWidth + prevTotalWidth;
|
|
271
|
+
|
|
272
|
+
if (isLatter) {
|
|
273
|
+
nextElement.current!.style.width = currentTotalWidth * (size / totalSize) + '%';
|
|
274
|
+
prevElement.current!.style.width = currentTotalWidth * (1 - size / totalSize) + '%';
|
|
275
|
+
} else {
|
|
276
|
+
prevElement.current!.style.width = currentTotalWidth * (size / totalSize) + '%';
|
|
277
|
+
nextElement.current!.style.width = currentTotalWidth * (1 - size / totalSize) + '%';
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
if (isLatter) {
|
|
281
|
+
handleZeroSize(totalSize - size, size);
|
|
282
|
+
} else {
|
|
283
|
+
handleZeroSize(size, totalSize - size);
|
|
284
|
+
}
|
|
285
|
+
if (props.onResize) {
|
|
286
|
+
props.onResize(prevElement.current!, nextElement.current!);
|
|
287
|
+
}
|
|
288
|
+
});
|
|
289
|
+
});
|
|
274
290
|
};
|
|
275
291
|
|
|
276
292
|
const getAbsoluteSize = (isLatter?: boolean) => {
|
|
@@ -303,43 +319,57 @@ export const ResizeHandleHorizontal = (props: ResizeHandleProps) => {
|
|
|
303
319
|
}
|
|
304
320
|
const prevWidth = startPrevWidth.current + e.pageX - startX.current;
|
|
305
321
|
const nextWidth = startNextWidth.current - (e.pageX - startX.current);
|
|
306
|
-
if (
|
|
307
|
-
|
|
322
|
+
if (requestFrameToDispose.current) {
|
|
323
|
+
requestFrameToDispose.current.dispose();
|
|
308
324
|
}
|
|
309
|
-
|
|
310
|
-
|
|
325
|
+
|
|
326
|
+
requestFrameToDispose.current = fastdom.mutate(() => {
|
|
311
327
|
if (props.flexMode) {
|
|
312
328
|
flexModeSetSize(prevWidth, nextWidth);
|
|
313
329
|
} else {
|
|
330
|
+
const parentWidth = ref.current!.parentElement!.offsetWidth;
|
|
314
331
|
setSize(prevWidth / parentWidth, nextWidth / parentWidth);
|
|
315
332
|
}
|
|
316
333
|
});
|
|
317
334
|
};
|
|
318
|
-
const onMouseUp = (
|
|
335
|
+
const onMouseUp = () => {
|
|
319
336
|
resizing.current = false;
|
|
320
|
-
ref.current?.classList.remove(styles.active);
|
|
321
337
|
document.removeEventListener('mousemove', onMouseMove);
|
|
322
338
|
document.removeEventListener('mouseup', onMouseUp);
|
|
323
|
-
|
|
324
|
-
restoreScrollBar(prevElement.current!);
|
|
325
|
-
restoreScrollBar(nextElement.current!);
|
|
339
|
+
|
|
326
340
|
if (props.onFinished) {
|
|
327
341
|
props.onFinished();
|
|
328
342
|
}
|
|
329
|
-
|
|
343
|
+
|
|
344
|
+
fastdom.mutate(() => {
|
|
345
|
+
ref.current?.classList.remove(styles.active);
|
|
346
|
+
|
|
347
|
+
// 结束拖拽时恢复拖拽区域滚动条
|
|
348
|
+
restoreScrollBar(prevElement.current!);
|
|
349
|
+
restoreScrollBar(nextElement.current!);
|
|
350
|
+
|
|
351
|
+
allowWebviewCatchMouseEvents();
|
|
352
|
+
});
|
|
330
353
|
};
|
|
331
354
|
const onMouseDown = (e) => {
|
|
332
355
|
resizing.current = true;
|
|
333
|
-
ref.current?.classList.add(styles.active);
|
|
334
356
|
document.addEventListener('mousemove', onMouseMove);
|
|
335
357
|
document.addEventListener('mouseup', onMouseUp);
|
|
336
358
|
startX.current = e.pageX;
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
359
|
+
|
|
360
|
+
fastdom.measure(() => {
|
|
361
|
+
startPrevWidth.current = prevElement.current!.offsetWidth;
|
|
362
|
+
startNextWidth.current = nextElement.current!.offsetWidth;
|
|
363
|
+
|
|
364
|
+
fastdom.mutate(() => {
|
|
365
|
+
ref.current?.classList.add(styles.active);
|
|
366
|
+
|
|
367
|
+
// 开始拖拽时隐藏拖拽区域滚动条
|
|
368
|
+
hideScrollBar(prevElement.current!);
|
|
369
|
+
hideScrollBar(nextElement.current!);
|
|
370
|
+
preventWebviewCatchMouseEvents();
|
|
371
|
+
});
|
|
372
|
+
});
|
|
343
373
|
};
|
|
344
374
|
|
|
345
375
|
React.useEffect(() => {
|
|
@@ -395,7 +425,6 @@ export const ResizeHandleVertical = (props: ResizeHandleProps) => {
|
|
|
395
425
|
const cachedPrevElement = React.useRef<HTMLElement>();
|
|
396
426
|
const cachedNextElement = React.useRef<HTMLElement>();
|
|
397
427
|
|
|
398
|
-
const requestFrame = React.useRef<number>();
|
|
399
428
|
// direction: true 为向下,false 为向上
|
|
400
429
|
const setSize = (prev: number, next: number, direction?: boolean) => {
|
|
401
430
|
const prevEle = props.findPrevElement ? props.findPrevElement(direction) : prevElement.current!;
|
|
@@ -465,7 +494,6 @@ export const ResizeHandleVertical = (props: ResizeHandleProps) => {
|
|
|
465
494
|
let currentTotalHeight;
|
|
466
495
|
if (props.flexMode) {
|
|
467
496
|
currentTotalHeight = ((prevEle.offsetHeight + nextEle.offsetHeight) / prevEle.parentElement!.offsetHeight) * 100;
|
|
468
|
-
// flexModeSetSize(prev / (prev + next) * totalHeight, next / (prev + next) * totalHeight, true);
|
|
469
497
|
} else {
|
|
470
498
|
currentTotalHeight = +nextEle.style.height!.replace('%', '') + +prevEle.style.height!.replace('%', '');
|
|
471
499
|
}
|
|
@@ -584,15 +612,21 @@ export const ResizeHandleVertical = (props: ResizeHandleProps) => {
|
|
|
584
612
|
|
|
585
613
|
const onMouseDown = (e) => {
|
|
586
614
|
resizing.current = true;
|
|
587
|
-
ref.current?.classList.add(styles.active);
|
|
588
615
|
document.addEventListener('mousemove', onMouseMove);
|
|
589
616
|
document.addEventListener('mouseup', onMouseUp);
|
|
590
617
|
startY.current = e.pageY;
|
|
591
618
|
cachedNextElement.current = nextElement.current;
|
|
592
619
|
cachedPrevElement.current = prevElement.current;
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
620
|
+
|
|
621
|
+
fastdom.measure(() => {
|
|
622
|
+
startPrevHeight.current = prevElement.current!.offsetHeight;
|
|
623
|
+
startNextHeight.current = nextElement.current!.offsetHeight;
|
|
624
|
+
|
|
625
|
+
fastdom.mutate(() => {
|
|
626
|
+
ref.current?.classList.add(styles.active);
|
|
627
|
+
preventWebviewCatchMouseEvents();
|
|
628
|
+
});
|
|
629
|
+
});
|
|
596
630
|
};
|
|
597
631
|
|
|
598
632
|
const onMouseMove = (e: MouseEvent) => {
|
|
@@ -600,32 +634,30 @@ export const ResizeHandleVertical = (props: ResizeHandleProps) => {
|
|
|
600
634
|
if (ref.current && ref.current.classList.contains('no-resize')) {
|
|
601
635
|
return;
|
|
602
636
|
}
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
(
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
637
|
+
|
|
638
|
+
fastdom.measure(() => {
|
|
639
|
+
const direction = e.pageY > startY.current;
|
|
640
|
+
// 若上层未传入findNextElement,dynamicNext为null,否则找不到符合要求的panel时返回undefined
|
|
641
|
+
const dynamicNext = props.findNextElement ? props.findNextElement(direction) : null;
|
|
642
|
+
const dynamicPrev = props.findPrevElement ? props.findPrevElement(direction) : null;
|
|
643
|
+
// 作用元素变化重新初始化当前位置,传入findNextElement时默认已传入findPrevElement
|
|
644
|
+
if (
|
|
645
|
+
(dynamicNext !== null && cachedNextElement.current !== dynamicNext) ||
|
|
646
|
+
(dynamicPrev !== null && cachedPrevElement.current !== dynamicPrev)
|
|
647
|
+
) {
|
|
648
|
+
if (!dynamicNext || !dynamicPrev) {
|
|
649
|
+
return;
|
|
650
|
+
}
|
|
651
|
+
cachedNextElement.current = dynamicNext!;
|
|
652
|
+
cachedPrevElement.current = dynamicPrev!;
|
|
653
|
+
startY.current = e.pageY;
|
|
654
|
+
startPrevHeight.current = cachedPrevElement.current!.offsetHeight;
|
|
655
|
+
startNextHeight.current = cachedNextElement.current!.offsetHeight;
|
|
614
656
|
}
|
|
615
|
-
cachedNextElement.current = dynamicNext!;
|
|
616
|
-
cachedPrevElement.current = dynamicPrev!;
|
|
617
|
-
startY.current = e.pageY;
|
|
618
|
-
startPrevHeight.current = cachedPrevElement.current!.offsetHeight;
|
|
619
|
-
startNextHeight.current = cachedNextElement.current!.offsetHeight;
|
|
620
|
-
}
|
|
621
657
|
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
if (requestFrame.current) {
|
|
625
|
-
window.cancelAnimationFrame(requestFrame.current);
|
|
626
|
-
}
|
|
658
|
+
const prevHeight = startPrevHeight.current + e.pageY - startY.current;
|
|
659
|
+
const nextHeight = startNextHeight.current - (e.pageY - startY.current);
|
|
627
660
|
|
|
628
|
-
requestFrame.current = window.requestAnimationFrame(() => {
|
|
629
661
|
const prevMinResize = Number(cachedPrevElement.current!.dataset.minResize || 0);
|
|
630
662
|
const nextMinResize = Number(cachedNextElement.current!.dataset.minResize || 0);
|
|
631
663
|
if (prevMinResize || nextMinResize) {
|
|
@@ -633,26 +665,32 @@ export const ResizeHandleVertical = (props: ResizeHandleProps) => {
|
|
|
633
665
|
return;
|
|
634
666
|
}
|
|
635
667
|
}
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
668
|
+
|
|
669
|
+
fastdom.mutate(() => {
|
|
670
|
+
if (props.flexMode === ResizeFlexMode.Prev || props.flexMode === ResizeFlexMode.Next) {
|
|
671
|
+
flexModeSetSize(prevHeight, nextHeight);
|
|
672
|
+
} else if (props.flexMode === ResizeFlexMode.Percentage) {
|
|
673
|
+
const parentHeight = ref.current!.parentElement!.offsetHeight;
|
|
674
|
+
setSize(prevHeight / parentHeight, nextHeight / parentHeight);
|
|
675
|
+
} else {
|
|
676
|
+
setDomSize(prevHeight, nextHeight, cachedPrevElement.current!, cachedNextElement.current!);
|
|
677
|
+
}
|
|
678
|
+
});
|
|
644
679
|
});
|
|
645
680
|
};
|
|
646
681
|
|
|
647
682
|
const onMouseUp = (e) => {
|
|
648
683
|
resizing.current = false;
|
|
649
|
-
ref.current?.classList.remove(styles.active);
|
|
650
684
|
document.removeEventListener('mousemove', onMouseMove);
|
|
651
685
|
document.removeEventListener('mouseup', onMouseUp);
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
686
|
+
|
|
687
|
+
fastdom.mutate(() => {
|
|
688
|
+
ref.current?.classList.remove(styles.active);
|
|
689
|
+
if (props.onFinished) {
|
|
690
|
+
props.onFinished();
|
|
691
|
+
}
|
|
692
|
+
allowWebviewCatchMouseEvents();
|
|
693
|
+
});
|
|
656
694
|
};
|
|
657
695
|
|
|
658
696
|
React.useEffect(() => {
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import { Heap, IDisposable, onUnexpectedError } from '@opensumi/ide-utils';
|
|
2
|
+
|
|
3
|
+
class DomOperation {
|
|
4
|
+
private _disposed = false;
|
|
5
|
+
|
|
6
|
+
constructor(protected _runner: () => void, public priority: number) {}
|
|
7
|
+
|
|
8
|
+
run() {
|
|
9
|
+
if (this._disposed) {
|
|
10
|
+
return;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
try {
|
|
14
|
+
this._runner();
|
|
15
|
+
} catch (error) {
|
|
16
|
+
onUnexpectedError(error);
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
dispose() {
|
|
21
|
+
this._disposed = true;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const aQueue = new Heap<DomOperation>({
|
|
26
|
+
comparator: (a, b) => b.priority - a.priority,
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
const bQueue = new Heap<DomOperation>({
|
|
30
|
+
comparator: (a, b) => b.priority - a.priority,
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
let runningQueue = aQueue;
|
|
34
|
+
let nextQueue = bQueue;
|
|
35
|
+
|
|
36
|
+
function swapQueue() {
|
|
37
|
+
if (runningQueue === aQueue) {
|
|
38
|
+
runningQueue = bQueue;
|
|
39
|
+
nextQueue = aQueue;
|
|
40
|
+
} else {
|
|
41
|
+
runningQueue = aQueue;
|
|
42
|
+
nextQueue = bQueue;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
let currentFlushHandle: number | undefined;
|
|
47
|
+
let inAnimationFrame = false;
|
|
48
|
+
|
|
49
|
+
function flush() {
|
|
50
|
+
currentFlushHandle = undefined;
|
|
51
|
+
|
|
52
|
+
inAnimationFrame = true;
|
|
53
|
+
|
|
54
|
+
swapQueue();
|
|
55
|
+
|
|
56
|
+
while (runningQueue.size > 0) {
|
|
57
|
+
const op = runningQueue.pop()!;
|
|
58
|
+
op.run();
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
inAnimationFrame = false;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function schedule() {
|
|
65
|
+
if (currentFlushHandle) {
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
currentFlushHandle = requestAnimationFrame(flush);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* 如果当前在动画帧中,将操作加入当前队列,否则加入下一帧队列
|
|
74
|
+
*/
|
|
75
|
+
function runAtThisOrScheduleAtNext(op: DomOperation) {
|
|
76
|
+
if (inAnimationFrame) {
|
|
77
|
+
runningQueue.add(op);
|
|
78
|
+
} else {
|
|
79
|
+
nextQueue.add(op);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function measure(fn: () => void): IDisposable {
|
|
84
|
+
const op = new DomOperation(fn, 10000);
|
|
85
|
+
runAtThisOrScheduleAtNext(op);
|
|
86
|
+
schedule();
|
|
87
|
+
return op;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function mutate(fn: () => void): IDisposable {
|
|
91
|
+
const op = new DomOperation(fn, -10000);
|
|
92
|
+
runAtThisOrScheduleAtNext(op);
|
|
93
|
+
schedule();
|
|
94
|
+
return op;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function measureAtNextFrame(fn: () => void): IDisposable {
|
|
98
|
+
const op = new DomOperation(fn, 10000);
|
|
99
|
+
nextQueue.add(op);
|
|
100
|
+
schedule();
|
|
101
|
+
return op;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
const fastdom = {
|
|
105
|
+
measure,
|
|
106
|
+
measureAtNextFrame,
|
|
107
|
+
mutate,
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
export default fastdom;
|
package/src/dom/index.ts
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { Event as BaseEvent, Disposable, Emitter, IDisposable, isWebKit } from '@opensumi/ide-core-common';
|
|
2
2
|
import { space } from '@opensumi/ide-utils/lib/strings';
|
|
3
3
|
|
|
4
|
+
import fastdom from './fastdom';
|
|
5
|
+
|
|
4
6
|
export * from './event';
|
|
5
7
|
|
|
6
8
|
export const EventType = {
|
|
@@ -177,6 +179,7 @@ export function trackFocus(element: HTMLElement | Window): IFocusTracker {
|
|
|
177
179
|
return new FocusTracker(element);
|
|
178
180
|
}
|
|
179
181
|
|
|
182
|
+
export { fastdom };
|
|
180
183
|
/**
|
|
181
184
|
* https://developer.mozilla.org/zh-CN/docs/Web/API/MouseEvent/button
|
|
182
185
|
*/
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { DisposableStore, Emitter, IDisposable } from '@opensumi/ide-core-common';
|
|
2
|
+
|
|
3
|
+
import fastdom from './fastdom';
|
|
4
|
+
|
|
5
|
+
export interface IDimension {
|
|
6
|
+
width: number;
|
|
7
|
+
height: number;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export class ResizeObserverWrapper implements IDisposable {
|
|
11
|
+
private _disposables = new DisposableStore();
|
|
12
|
+
|
|
13
|
+
private _onDidChange = this._disposables.add(new Emitter<IDimension>());
|
|
14
|
+
public onDidChange = this._onDidChange.event;
|
|
15
|
+
|
|
16
|
+
private _resizeObserver: ResizeObserver;
|
|
17
|
+
|
|
18
|
+
constructor(private _container: HTMLElement) {
|
|
19
|
+
this._resizeObserver = new ResizeObserver(this._callback);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
private _callback = (entries: ResizeObserverEntry[]) => {
|
|
23
|
+
if (entries[0] && entries[0].contentRect) {
|
|
24
|
+
const width = entries[0].contentRect.width;
|
|
25
|
+
const height = entries[0].contentRect.height;
|
|
26
|
+
|
|
27
|
+
this._onDidChange.fire({ width, height });
|
|
28
|
+
}
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
private _observed = false;
|
|
32
|
+
observe() {
|
|
33
|
+
if (this._observed) {
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
this._observed = true;
|
|
37
|
+
|
|
38
|
+
this._resizeObserver.observe(this._container);
|
|
39
|
+
fastdom.measure(() => {
|
|
40
|
+
this._onDidChange.fire({
|
|
41
|
+
width: this._container.clientWidth,
|
|
42
|
+
height: this._container.clientHeight,
|
|
43
|
+
});
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
dispose() {
|
|
48
|
+
this._observed = false;
|
|
49
|
+
this._disposables.dispose();
|
|
50
|
+
this._resizeObserver.disconnect();
|
|
51
|
+
}
|
|
52
|
+
}
|
|
@@ -1,7 +1,9 @@
|
|
|
1
|
+
import throttle from 'lodash/throttle';
|
|
1
2
|
import React from 'react';
|
|
2
3
|
|
|
3
4
|
import { IEventBus } from '@opensumi/ide-core-common';
|
|
4
5
|
|
|
6
|
+
import { fastdom } from '../dom';
|
|
5
7
|
import { useInjectable } from '../react-hooks';
|
|
6
8
|
|
|
7
9
|
import { ResizeEvent } from './layout.interface';
|
|
@@ -20,16 +22,35 @@ export const useViewState = (
|
|
|
20
22
|
const [viewState, setViewState] = React.useState({ width: 0, height: 0 });
|
|
21
23
|
const viewStateRef = React.useRef<ViewState>(viewState);
|
|
22
24
|
|
|
25
|
+
const updateViewState = throttle(
|
|
26
|
+
(height: number, width: number) => {
|
|
27
|
+
// 当视图被隐藏 (display: none) 时不更新 viewState
|
|
28
|
+
// 避免视图切换时触发无效的渲染
|
|
29
|
+
// 真正的 resize 操作不会出现 width/height 为 0 的情况
|
|
30
|
+
if (
|
|
31
|
+
(width !== viewStateRef.current.width || height !== viewStateRef.current.height) &&
|
|
32
|
+
(width !== 0 || height !== 0)
|
|
33
|
+
) {
|
|
34
|
+
setViewState({ width, height });
|
|
35
|
+
viewStateRef.current = { width, height };
|
|
36
|
+
}
|
|
37
|
+
},
|
|
38
|
+
16 * 3,
|
|
39
|
+
{
|
|
40
|
+
leading: true,
|
|
41
|
+
trailing: true,
|
|
42
|
+
},
|
|
43
|
+
);
|
|
44
|
+
|
|
23
45
|
React.useEffect(() => {
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
setViewState({ height: containerRef.current.clientHeight, width: containerRef.current.clientWidth });
|
|
46
|
+
const disposer = eventBus.onDirective(ResizeEvent.createDirective(location), () => {
|
|
47
|
+
if (!manualObserve) {
|
|
48
|
+
fastdom.measureAtNextFrame(() => {
|
|
49
|
+
if (containerRef.current) {
|
|
50
|
+
const height = containerRef.current.clientHeight;
|
|
51
|
+
const width = containerRef.current.clientWidth;
|
|
52
|
+
|
|
53
|
+
updateViewState(height, width);
|
|
33
54
|
}
|
|
34
55
|
});
|
|
35
56
|
}
|
|
@@ -37,27 +58,17 @@ export const useViewState = (
|
|
|
37
58
|
return () => {
|
|
38
59
|
disposer.dispose();
|
|
39
60
|
};
|
|
40
|
-
}, [
|
|
61
|
+
}, []);
|
|
41
62
|
|
|
42
63
|
React.useEffect(() => {
|
|
64
|
+
const ResizeObserver = window.ResizeObserver;
|
|
43
65
|
// TODO: 统一收敛到 resizeEvent 内
|
|
44
66
|
if (manualObserve && containerRef.current) {
|
|
45
|
-
const
|
|
46
|
-
const doUpdate = (entries) => {
|
|
67
|
+
const resizeObserver = new ResizeObserver((entries: ResizeObserverEntry[]) => {
|
|
47
68
|
const width = entries[0].contentRect.width;
|
|
48
69
|
const height = entries[0].contentRect.height;
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
// 真正的 resize 操作不会出现 width/height 为 0 的情况
|
|
52
|
-
if (
|
|
53
|
-
(width !== viewStateRef.current.width || height !== viewStateRef.current.height) &&
|
|
54
|
-
(width !== 0 || height !== 0)
|
|
55
|
-
) {
|
|
56
|
-
setViewState({ width, height });
|
|
57
|
-
viewStateRef.current = { width, height };
|
|
58
|
-
}
|
|
59
|
-
};
|
|
60
|
-
const resizeObserver = new ResizeObserver(doUpdate);
|
|
70
|
+
updateViewState(height, width);
|
|
71
|
+
});
|
|
61
72
|
resizeObserver.observe(containerRef.current);
|
|
62
73
|
return () => {
|
|
63
74
|
if (containerRef.current) {
|
|
@@ -104,6 +104,10 @@ export class ResizePayload {
|
|
|
104
104
|
*/
|
|
105
105
|
constructor(public slotLocation: SlotLocation) {}
|
|
106
106
|
}
|
|
107
|
-
export class ResizeEvent extends BasicEvent<ResizePayload> {
|
|
107
|
+
export class ResizeEvent extends BasicEvent<ResizePayload> {
|
|
108
|
+
static createDirective(location: SlotLocation) {
|
|
109
|
+
return `resize:${location}`;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
108
112
|
|
|
109
113
|
export class RenderedEvent extends BasicEvent<void> {}
|
package/src/layout/render.tsx
CHANGED
|
@@ -6,9 +6,9 @@ import { ErrorBoundary } from '../react-providers';
|
|
|
6
6
|
import { View } from './layout.interface';
|
|
7
7
|
|
|
8
8
|
export const renderView = (view?: View) => (
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
9
|
+
<>
|
|
10
|
+
{view && ReactIs.isValidElementType(view.component) ? (
|
|
11
|
+
<ErrorBoundary>{view.component && React.createElement(view.component, view.initialProps)}</ErrorBoundary>
|
|
12
|
+
) : null}
|
|
13
|
+
</>
|
|
14
|
+
);
|
|
@@ -35,6 +35,9 @@ export const SlotLocation = {
|
|
|
35
35
|
};
|
|
36
36
|
|
|
37
37
|
export function getSlotLocation(moduleName: string, layoutConfig: LayoutConfig) {
|
|
38
|
+
if (!layoutConfig) {
|
|
39
|
+
return '';
|
|
40
|
+
}
|
|
38
41
|
for (const location of Object.keys(layoutConfig)) {
|
|
39
42
|
if (layoutConfig[location].modules && layoutConfig[location].modules.indexOf(moduleName) > -1) {
|
|
40
43
|
return location;
|