flexlayout-react 0.5.15 → 0.5.19
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/ChangeLog.txt +26 -0
- package/README.md +121 -97
- package/declarations/DragDrop.d.ts +1 -0
- package/declarations/I18nLabel.d.ts +1 -0
- package/declarations/Rect.d.ts +4 -0
- package/declarations/Types.d.ts +9 -1
- package/declarations/model/Actions.d.ts +18 -11
- package/declarations/model/BorderNode.d.ts +2 -1
- package/declarations/model/IJsonModel.d.ts +10 -0
- package/declarations/model/Model.d.ts +1 -0
- package/declarations/model/TabNode.d.ts +1 -0
- package/declarations/model/TabSetNode.d.ts +1 -0
- package/declarations/view/Layout.d.ts +20 -6
- package/dist/flexlayout.js +20 -20
- package/dist/flexlayout_min.js +1 -1
- package/lib/DockLocation.js +25 -11
- package/lib/DockLocation.js.map +1 -1
- package/lib/DragDrop.js +19 -3
- package/lib/DragDrop.js.map +1 -1
- package/lib/I18nLabel.js +1 -0
- package/lib/I18nLabel.js.map +1 -1
- package/lib/PopupMenu.js +14 -9
- package/lib/PopupMenu.js.map +1 -1
- package/lib/Rect.js +3 -0
- package/lib/Rect.js.map +1 -1
- package/lib/Types.js +8 -0
- package/lib/Types.js.map +1 -1
- package/lib/model/Actions.js +20 -11
- package/lib/model/Actions.js.map +1 -1
- package/lib/model/BorderNode.js +61 -14
- package/lib/model/BorderNode.js.map +1 -1
- package/lib/model/BorderSet.js +33 -19
- package/lib/model/BorderSet.js.map +1 -1
- package/lib/model/Model.js +39 -1
- package/lib/model/Model.js.map +1 -1
- package/lib/model/RowNode.js +19 -5
- package/lib/model/RowNode.js.map +1 -1
- package/lib/model/TabNode.js +14 -0
- package/lib/model/TabNode.js.map +1 -1
- package/lib/model/TabSetNode.js +42 -19
- package/lib/model/TabSetNode.js.map +1 -1
- package/lib/view/BorderButton.js +14 -3
- package/lib/view/BorderButton.js.map +1 -1
- package/lib/view/BorderTabSet.js +15 -4
- package/lib/view/BorderTabSet.js.map +1 -1
- package/lib/view/Layout.js +206 -45
- package/lib/view/Layout.js.map +1 -1
- package/lib/view/Splitter.js +34 -3
- package/lib/view/Splitter.js.map +1 -1
- package/lib/view/TabButton.js +11 -2
- package/lib/view/TabButton.js.map +1 -1
- package/lib/view/TabFloating.js +23 -11
- package/lib/view/TabFloating.js.map +1 -1
- package/lib/view/TabSet.js +50 -17
- package/lib/view/TabSet.js.map +1 -1
- package/package.json +1 -1
- package/src/DockLocation.ts +30 -9
- package/src/DragDrop.ts +26 -3
- package/src/I18nLabel.ts +1 -0
- package/src/PopupMenu.tsx +28 -10
- package/src/Rect.ts +6 -2
- package/src/Types.ts +9 -0
- package/src/model/Actions.ts +21 -11
- package/src/model/BorderNode.ts +57 -15
- package/src/model/BorderSet.ts +32 -19
- package/src/model/IJsonModel.ts +10 -0
- package/src/model/Model.ts +43 -1
- package/src/model/RowNode.ts +8 -5
- package/src/model/TabNode.ts +16 -0
- package/src/model/TabSetNode.ts +43 -19
- package/src/view/BorderButton.tsx +22 -3
- package/src/view/BorderTabSet.tsx +21 -4
- package/src/view/Layout.tsx +263 -70
- package/src/view/Splitter.tsx +49 -3
- package/src/view/TabButton.tsx +17 -1
- package/src/view/TabFloating.tsx +36 -19
- package/src/view/TabSet.tsx +76 -16
- package/style/_base.scss +75 -44
- package/style/dark.css +90 -61
- package/style/dark.css.map +1 -1
- package/style/dark.scss +20 -20
- package/style/gray.css +90 -61
- package/style/gray.css.map +1 -1
- package/style/gray.scss +20 -20
- package/style/light.css +90 -61
- package/style/light.css.map +1 -1
- package/style/light.scss +18 -18
- package/yarn-error.log +0 -11828
package/src/view/Layout.tsx
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
|
+
import * as ReactDOM from "react-dom";
|
|
2
3
|
import DockLocation from "../DockLocation";
|
|
3
4
|
import DragDrop from "../DragDrop";
|
|
4
5
|
import DropInfo from "../DropInfo";
|
|
@@ -25,6 +26,11 @@ import { FloatingWindowTab } from "./FloatingWindowTab";
|
|
|
25
26
|
import { TabFloating } from "./TabFloating";
|
|
26
27
|
import { IJsonTabNode } from "../model/IJsonModel";
|
|
27
28
|
|
|
29
|
+
export type CustomDragCallback = (dragging: TabNode | IJsonTabNode, over: TabNode, x: number, y: number, location: DockLocation) => void;
|
|
30
|
+
export type DragRectRenderCallback = (text: String, node?: Node, json?: IJsonTabNode) => React.ReactElement | undefined;
|
|
31
|
+
export type FloatingTabPlaceholderRenderCallback = (dockPopout: () => void, showPopout: () => void) => React.ReactElement | undefined;
|
|
32
|
+
export type NodeMouseEvent = (node: TabNode | TabSetNode | BorderNode, event: React.MouseEvent<HTMLElement, MouseEvent>) => void;
|
|
33
|
+
|
|
28
34
|
export interface ILayoutProps {
|
|
29
35
|
model: Model;
|
|
30
36
|
factory: (node: TabNode) => React.ReactNode;
|
|
@@ -37,30 +43,37 @@ export interface ILayoutProps {
|
|
|
37
43
|
onAction?: (action: Action) => Action | undefined;
|
|
38
44
|
onRenderTab?: (
|
|
39
45
|
node: TabNode,
|
|
40
|
-
renderValues: ITabRenderValues,
|
|
46
|
+
renderValues: ITabRenderValues, // change the values in this object as required
|
|
41
47
|
) => void;
|
|
42
48
|
onRenderTabSet?: (
|
|
43
49
|
tabSetNode: TabSetNode | BorderNode,
|
|
44
|
-
renderValues: ITabSetRenderValues,
|
|
50
|
+
renderValues: ITabSetRenderValues, // change the values in this object as required
|
|
45
51
|
) => void;
|
|
46
52
|
onModelChange?: (model: Model) => void;
|
|
47
53
|
onExternalDrag?: (event: React.DragEvent<HTMLDivElement>) => undefined | {
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
54
|
+
dragText: string,
|
|
55
|
+
json: any,
|
|
56
|
+
onDrop?: (node?: Node, event?: Event) => void
|
|
51
57
|
};
|
|
52
58
|
classNameMapper?: (defaultClassName: string) => string;
|
|
53
59
|
i18nMapper?: (id: I18nLabel, param?: string) => string | undefined;
|
|
54
60
|
supportsPopout?: boolean | undefined;
|
|
55
61
|
popoutURL?: string | undefined;
|
|
56
62
|
realtimeResize?: boolean | undefined;
|
|
57
|
-
onTabDrag?: (dragging: TabNode | IJsonTabNode, over: TabNode, x: number, y: number, location: DockLocation) => undefined | {
|
|
63
|
+
onTabDrag?: (dragging: TabNode | IJsonTabNode, over: TabNode, x: number, y: number, location: DockLocation, refresh: () => void) => undefined | {
|
|
58
64
|
x: number,
|
|
59
65
|
y: number,
|
|
60
66
|
width: number,
|
|
61
67
|
height: number,
|
|
62
|
-
callback:
|
|
68
|
+
callback: CustomDragCallback,
|
|
69
|
+
// Called once when `callback` is not going to be called anymore (user canceled the drag, moved mouse and you returned a different callback, etc)
|
|
70
|
+
invalidated?: () => void,
|
|
71
|
+
cursor?: string | undefined
|
|
63
72
|
};
|
|
73
|
+
onRenderDragRect?: DragRectRenderCallback;
|
|
74
|
+
onRenderFloatingTabPlaceholder?: FloatingTabPlaceholderRenderCallback;
|
|
75
|
+
onContextMenu?: NodeMouseEvent;
|
|
76
|
+
onAuxMouseClick?: NodeMouseEvent;
|
|
64
77
|
}
|
|
65
78
|
export interface IFontValues {
|
|
66
79
|
size?: string;
|
|
@@ -94,10 +107,12 @@ export interface ILayoutState {
|
|
|
94
107
|
calculatedTabBarSize: number;
|
|
95
108
|
calculatedBorderBarSize: number;
|
|
96
109
|
editingTab?: TabNode;
|
|
110
|
+
showHiddenBorder: DockLocation;
|
|
97
111
|
}
|
|
98
112
|
|
|
99
113
|
export interface IIcons {
|
|
100
114
|
close?: React.ReactNode;
|
|
115
|
+
closeTabset?: React.ReactNode;
|
|
101
116
|
popout?: React.ReactNode;
|
|
102
117
|
maximize?: React.ReactNode;
|
|
103
118
|
restore?: React.ReactNode;
|
|
@@ -106,12 +121,14 @@ export interface IIcons {
|
|
|
106
121
|
|
|
107
122
|
export interface ICustomDropDestination {
|
|
108
123
|
rect: Rect;
|
|
109
|
-
callback:
|
|
124
|
+
callback: CustomDragCallback;
|
|
125
|
+
invalidated: (() => void) | undefined;
|
|
110
126
|
dragging: TabNode | IJsonTabNode;
|
|
111
127
|
over: TabNode;
|
|
112
128
|
x: number;
|
|
113
129
|
y: number;
|
|
114
130
|
location: DockLocation;
|
|
131
|
+
cursor: string | undefined;
|
|
115
132
|
}
|
|
116
133
|
|
|
117
134
|
/** @hidden @internal */
|
|
@@ -145,7 +162,9 @@ export interface ILayoutCallbacks {
|
|
|
145
162
|
styleFont: (style: Record<string, string>) => Record<string, string>;
|
|
146
163
|
setEditingTab(tabNode?: TabNode): void;
|
|
147
164
|
getEditingTab(): TabNode | undefined;
|
|
148
|
-
|
|
165
|
+
getOnRenderFloatingTabPlaceholder(): FloatingTabPlaceholderRenderCallback | undefined;
|
|
166
|
+
showContextMenu(node: TabNode | TabSetNode | BorderNode, event: React.MouseEvent<HTMLElement, MouseEvent>) : void;
|
|
167
|
+
auxMouseClick(node: TabNode | TabSetNode | BorderNode, event: React.MouseEvent<HTMLElement, MouseEvent>) : void;
|
|
149
168
|
}
|
|
150
169
|
|
|
151
170
|
// Popout windows work in latest browsers based on webkit (Chrome, Opera, Safari, latest Edge) and Firefox. They do
|
|
@@ -163,6 +182,7 @@ const defaultSupportsPopout: boolean = isDesktop && !isIEorEdge;
|
|
|
163
182
|
* A React component that hosts a multi-tabbed layout
|
|
164
183
|
*/
|
|
165
184
|
export class Layout extends React.Component<ILayoutProps, ILayoutState> {
|
|
185
|
+
|
|
166
186
|
/** @hidden @internal */
|
|
167
187
|
private selfRef: React.RefObject<HTMLDivElement>;
|
|
168
188
|
/** @hidden @internal */
|
|
@@ -192,6 +212,8 @@ export class Layout extends React.Component<ILayoutProps, ILayoutState> {
|
|
|
192
212
|
/** @hidden @internal */
|
|
193
213
|
private dragDiv?: HTMLDivElement;
|
|
194
214
|
/** @hidden @internal */
|
|
215
|
+
private dragRectRendered: boolean = true;
|
|
216
|
+
/** @hidden @internal */
|
|
195
217
|
private dragDivText: string = "";
|
|
196
218
|
/** @hidden @internal */
|
|
197
219
|
private dropInfo: DropInfo | undefined;
|
|
@@ -229,7 +251,7 @@ export class Layout extends React.Component<ILayoutProps, ILayoutState> {
|
|
|
229
251
|
/** @hidden @internal */
|
|
230
252
|
private firstRender: boolean;
|
|
231
253
|
/** @hidden @internal */
|
|
232
|
-
private resizeObserver
|
|
254
|
+
private resizeObserver?: ResizeObserver;
|
|
233
255
|
|
|
234
256
|
constructor(props: ILayoutProps) {
|
|
235
257
|
super(props);
|
|
@@ -245,11 +267,13 @@ export class Layout extends React.Component<ILayoutProps, ILayoutState> {
|
|
|
245
267
|
this.icons = props.closeIcon ? Object.assign({ close: props.closeIcon }, props.icons) : props.icons;
|
|
246
268
|
this.firstRender = true;
|
|
247
269
|
|
|
248
|
-
this.state = {
|
|
249
|
-
|
|
250
|
-
|
|
270
|
+
this.state = {
|
|
271
|
+
rect: new Rect(0, 0, 0, 0),
|
|
272
|
+
calculatedHeaderBarSize: 25,
|
|
273
|
+
calculatedTabBarSize: 26,
|
|
251
274
|
calculatedBorderBarSize: 30,
|
|
252
275
|
editingTab: undefined,
|
|
276
|
+
showHiddenBorder: DockLocation.CENTER,
|
|
253
277
|
};
|
|
254
278
|
|
|
255
279
|
this.onDragEnter = this.onDragEnter.bind(this);
|
|
@@ -387,8 +411,8 @@ export class Layout extends React.Component<ILayoutProps, ILayoutState> {
|
|
|
387
411
|
}
|
|
388
412
|
|
|
389
413
|
/** @hidden @internal */
|
|
390
|
-
onTabDrag(
|
|
391
|
-
return this.props.onTabDrag?.(
|
|
414
|
+
onTabDrag(...args: Parameters<Required<ILayoutProps>['onTabDrag']>) {
|
|
415
|
+
return this.props.onTabDrag?.(...args);
|
|
392
416
|
}
|
|
393
417
|
|
|
394
418
|
/** @hidden @internal */
|
|
@@ -403,7 +427,7 @@ export class Layout extends React.Component<ILayoutProps, ILayoutState> {
|
|
|
403
427
|
|
|
404
428
|
/** @hidden @internal */
|
|
405
429
|
setEditingTab(tabNode?: TabNode) {
|
|
406
|
-
this.setState({editingTab:tabNode});
|
|
430
|
+
this.setState({ editingTab: tabNode });
|
|
407
431
|
}
|
|
408
432
|
|
|
409
433
|
/** @hidden @internal */
|
|
@@ -436,6 +460,7 @@ export class Layout extends React.Component<ILayoutProps, ILayoutState> {
|
|
|
436
460
|
tabBarSize: this.state.calculatedTabBarSize,
|
|
437
461
|
borderBarSize: this.state.calculatedBorderBarSize,
|
|
438
462
|
};
|
|
463
|
+
this.props.model._setShowHiddenBorder(this.state.showHiddenBorder);
|
|
439
464
|
|
|
440
465
|
this.centerRect = this.props.model._layout(this.state.rect, metrics);
|
|
441
466
|
|
|
@@ -641,7 +666,7 @@ export class Layout extends React.Component<ILayoutProps, ILayoutState> {
|
|
|
641
666
|
* Adds a new tab by dragging a labeled panel to the drop location, dragging starts immediatelly
|
|
642
667
|
* @param dragText the text to show on the drag panel
|
|
643
668
|
* @param json the json for the new tab node
|
|
644
|
-
* @param onDrop a callback to call when the drag is complete
|
|
669
|
+
* @param onDrop a callback to call when the drag is complete (node and event will be undefined if the drag was cancelled)
|
|
645
670
|
*/
|
|
646
671
|
addTabWithDragAndDrop(dragText: string, json: IJsonTabNode, onDrop?: (node?: Node, event?: Event) => void) {
|
|
647
672
|
this.fnNewNodeDropped = onDrop;
|
|
@@ -655,9 +680,9 @@ export class Layout extends React.Component<ILayoutProps, ILayoutState> {
|
|
|
655
680
|
*
|
|
656
681
|
* @param dragText the text to show on the drag panel
|
|
657
682
|
* @param json the json for the new tab node
|
|
658
|
-
* @param onDrop a callback to call when the drag is complete
|
|
683
|
+
* @param onDrop a callback to call when the drag is complete (node and event will be undefined if the drag was cancelled)
|
|
659
684
|
*/
|
|
660
|
-
addTabWithDragAndDropIndirect(dragText: string, json: IJsonTabNode, onDrop?: () => void) {
|
|
685
|
+
addTabWithDragAndDropIndirect(dragText: string, json: IJsonTabNode, onDrop?: (node?: Node, event?: Event) => void) {
|
|
661
686
|
this.fnNewNodeDropped = onDrop;
|
|
662
687
|
this.newTabJson = json;
|
|
663
688
|
|
|
@@ -665,15 +690,21 @@ export class Layout extends React.Component<ILayoutProps, ILayoutState> {
|
|
|
665
690
|
|
|
666
691
|
this.dragDivText = dragText;
|
|
667
692
|
this.dragDiv = this.currentDocument!.createElement("div");
|
|
668
|
-
this.dragDiv.className = this.getClassName(
|
|
669
|
-
this.dragDiv.innerHTML = this.dragDivText;
|
|
693
|
+
this.dragDiv.className = this.getClassName(CLASSES.FLEXLAYOUT__DRAG_RECT);
|
|
670
694
|
this.dragDiv.addEventListener("mousedown", this.onDragDivMouseDown);
|
|
671
695
|
this.dragDiv.addEventListener("touchstart", this.onDragDivMouseDown);
|
|
672
696
|
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
697
|
+
this.dragRectRender(this.dragDivText, undefined, this.newTabJson, () => {
|
|
698
|
+
if (this.dragDiv) {
|
|
699
|
+
// now it's been rendered into the dom it can be centered
|
|
700
|
+
this.dragDiv.style.visibility = "visible";
|
|
701
|
+
const domRect = this.dragDiv.getBoundingClientRect();
|
|
702
|
+
const r = new Rect(0, 0, domRect?.width, domRect?.height);
|
|
703
|
+
r.centerInRect(this.state.rect);
|
|
704
|
+
this.dragDiv.style.left = r.x + "px";
|
|
705
|
+
this.dragDiv.style.top = r.y + "px";
|
|
706
|
+
}
|
|
707
|
+
});
|
|
677
708
|
|
|
678
709
|
const rootdiv = this.selfRef.current;
|
|
679
710
|
rootdiv!.appendChild(this.dragDiv);
|
|
@@ -688,8 +719,16 @@ export class Layout extends React.Component<ILayoutProps, ILayoutState> {
|
|
|
688
719
|
this.fnNewNodeDropped();
|
|
689
720
|
this.fnNewNodeDropped = undefined;
|
|
690
721
|
}
|
|
722
|
+
|
|
723
|
+
try {
|
|
724
|
+
this.customDrop?.invalidated?.()
|
|
725
|
+
} catch (e) {
|
|
726
|
+
console.error(e)
|
|
727
|
+
}
|
|
728
|
+
|
|
691
729
|
DragDrop.instance.hideGlass();
|
|
692
730
|
this.newTabJson = undefined;
|
|
731
|
+
this.customDrop = undefined;
|
|
693
732
|
};
|
|
694
733
|
|
|
695
734
|
/** @hidden @internal */
|
|
@@ -699,11 +738,11 @@ export class Layout extends React.Component<ILayoutProps, ILayoutState> {
|
|
|
699
738
|
|
|
700
739
|
try {
|
|
701
740
|
rootdiv.removeChild(this.outlineDiv!);
|
|
702
|
-
} catch (e) {}
|
|
741
|
+
} catch (e) { }
|
|
703
742
|
|
|
704
743
|
try {
|
|
705
744
|
rootdiv.removeChild(this.dragDiv!);
|
|
706
|
-
} catch (e) {}
|
|
745
|
+
} catch (e) { }
|
|
707
746
|
|
|
708
747
|
this.dragDiv = undefined;
|
|
709
748
|
this.hideEdges(rootdiv);
|
|
@@ -711,9 +750,19 @@ export class Layout extends React.Component<ILayoutProps, ILayoutState> {
|
|
|
711
750
|
this.fnNewNodeDropped();
|
|
712
751
|
this.fnNewNodeDropped = undefined;
|
|
713
752
|
}
|
|
753
|
+
|
|
754
|
+
try {
|
|
755
|
+
this.customDrop?.invalidated?.()
|
|
756
|
+
} catch (e) {
|
|
757
|
+
console.error(e)
|
|
758
|
+
}
|
|
759
|
+
|
|
714
760
|
DragDrop.instance.hideGlass();
|
|
715
761
|
this.newTabJson = undefined;
|
|
762
|
+
this.customDrop = undefined;
|
|
716
763
|
}
|
|
764
|
+
this.setState({ showHiddenBorder: DockLocation.CENTER });
|
|
765
|
+
|
|
717
766
|
};
|
|
718
767
|
|
|
719
768
|
/** @hidden @internal */
|
|
@@ -740,6 +789,31 @@ export class Layout extends React.Component<ILayoutProps, ILayoutState> {
|
|
|
740
789
|
}
|
|
741
790
|
};
|
|
742
791
|
|
|
792
|
+
/** @hidden @internal */
|
|
793
|
+
dragRectRender = (text: String, node?: Node, json?: IJsonTabNode, onRendered?: () => void) => {
|
|
794
|
+
let content: React.ReactElement | undefined = <div style={{ whiteSpace: "pre" }}>{text.replace("<br>", "\n")}</div>;
|
|
795
|
+
|
|
796
|
+
if (this.props.onRenderDragRect !== undefined) {
|
|
797
|
+
const customContent = this.props.onRenderDragRect(text, node, json);
|
|
798
|
+
if (customContent !== undefined) {
|
|
799
|
+
content = customContent;
|
|
800
|
+
}
|
|
801
|
+
}
|
|
802
|
+
|
|
803
|
+
// hide div until the render is complete
|
|
804
|
+
this.dragDiv!.style.visibility = "hidden";
|
|
805
|
+
this.dragRectRendered = false;
|
|
806
|
+
ReactDOM.render(<DragRectRenderWrapper
|
|
807
|
+
// wait for it to be rendered
|
|
808
|
+
onRendered={() => {
|
|
809
|
+
this.dragRectRendered = true;
|
|
810
|
+
onRendered?.();
|
|
811
|
+
}}
|
|
812
|
+
>
|
|
813
|
+
{content}
|
|
814
|
+
</DragRectRenderWrapper>, this.dragDiv!);
|
|
815
|
+
};
|
|
816
|
+
|
|
743
817
|
/** @hidden @internal */
|
|
744
818
|
onDragStart = () => {
|
|
745
819
|
this.dropInfo = undefined;
|
|
@@ -753,7 +827,8 @@ export class Layout extends React.Component<ILayoutProps, ILayoutState> {
|
|
|
753
827
|
if (this.dragDiv == null) {
|
|
754
828
|
this.dragDiv = this.currentDocument!.createElement("div");
|
|
755
829
|
this.dragDiv.className = this.getClassName(CLASSES.FLEXLAYOUT__DRAG_RECT);
|
|
756
|
-
this.
|
|
830
|
+
this.dragRectRender(this.dragDivText, this.dragNode, this.newTabJson);
|
|
831
|
+
|
|
757
832
|
rootdiv.appendChild(this.dragDiv);
|
|
758
833
|
}
|
|
759
834
|
// add edge indicators
|
|
@@ -780,53 +855,33 @@ export class Layout extends React.Component<ILayoutProps, ILayoutState> {
|
|
|
780
855
|
y: event.clientY - clientRect.top,
|
|
781
856
|
};
|
|
782
857
|
|
|
783
|
-
this.
|
|
858
|
+
this.checkForBorderToShow(pos.x, pos.y);
|
|
859
|
+
|
|
860
|
+
// keep it between left & right
|
|
861
|
+
const dragRect = this.dragDiv!.getBoundingClientRect();
|
|
862
|
+
let newLeft = pos.x - dragRect.width / 2;
|
|
863
|
+
if (newLeft + dragRect.width > clientRect.width) {
|
|
864
|
+
newLeft = clientRect.width - dragRect.width;
|
|
865
|
+
}
|
|
866
|
+
newLeft = Math.max(0, newLeft);
|
|
867
|
+
|
|
868
|
+
this.dragDiv!.style.left = newLeft + "px";
|
|
784
869
|
this.dragDiv!.style.top = pos.y + 5 + "px";
|
|
870
|
+
if (this.dragRectRendered && this.dragDiv!.style.visibility === "hidden") {
|
|
871
|
+
// make visible once the drag rect has been rendered
|
|
872
|
+
this.dragDiv!.style.visibility = "visible";
|
|
873
|
+
}
|
|
785
874
|
|
|
786
875
|
let dropInfo = this.props.model._findDropTargetNode(this.dragNode!, pos.x, pos.y);
|
|
787
876
|
if (dropInfo) {
|
|
788
|
-
this.
|
|
789
|
-
|
|
790
|
-
const dragging = this.newTabJson || (this.dragNode instanceof TabNode ? this.dragNode : undefined);
|
|
791
|
-
if (dragging && (dropInfo.node instanceof TabSetNode || dropInfo.node instanceof BorderNode) && dropInfo.index === -1) {
|
|
792
|
-
const selected = dropInfo.node.getSelectedNode() as TabNode | undefined;
|
|
793
|
-
const tabRect = selected?.getRect()
|
|
794
|
-
|
|
795
|
-
if (selected && tabRect?.contains(pos.x, pos.y)) {
|
|
796
|
-
let customDrop: ICustomDropDestination | undefined = undefined;
|
|
797
|
-
|
|
798
|
-
try {
|
|
799
|
-
const dest = this.onTabDrag(dragging, selected, pos.x - tabRect.x, pos.y - tabRect.y, dropInfo.location);
|
|
800
|
-
|
|
801
|
-
if (dest) {
|
|
802
|
-
customDrop = {
|
|
803
|
-
rect: new Rect(dest.x + tabRect.x, dest.y + tabRect.y, dest.width, dest.height),
|
|
804
|
-
callback: dest.callback,
|
|
805
|
-
dragging: dragging,
|
|
806
|
-
over: selected,
|
|
807
|
-
x: pos.x - tabRect.x,
|
|
808
|
-
y: pos.y - tabRect.y,
|
|
809
|
-
location: dropInfo.location
|
|
810
|
-
};
|
|
811
|
-
}
|
|
812
|
-
} catch (e) {
|
|
813
|
-
console.error(e)
|
|
814
|
-
}
|
|
815
|
-
|
|
816
|
-
this.customDrop = customDrop;
|
|
817
|
-
}
|
|
818
|
-
}
|
|
819
|
-
|
|
820
|
-
this.dropInfo = dropInfo;
|
|
821
|
-
this.outlineDiv!.className = this.getClassName(this.customDrop ? "flexlayout__outline_rect" : dropInfo.className);
|
|
822
|
-
|
|
823
|
-
if (this.customDrop) {
|
|
824
|
-
this.customDrop.rect.positionElement(this.outlineDiv!);
|
|
877
|
+
if (this.props.onTabDrag) {
|
|
878
|
+
this.handleCustomTabDrag(dropInfo, pos, event);
|
|
825
879
|
} else {
|
|
880
|
+
this.dropInfo = dropInfo;
|
|
881
|
+
this.outlineDiv!.className = this.getClassName(dropInfo.className);
|
|
826
882
|
dropInfo.rect.positionElement(this.outlineDiv!);
|
|
883
|
+
this.outlineDiv!.style.visibility = "visible";
|
|
827
884
|
}
|
|
828
|
-
|
|
829
|
-
this.outlineDiv!.style.visibility = "visible";
|
|
830
885
|
}
|
|
831
886
|
};
|
|
832
887
|
|
|
@@ -844,8 +899,12 @@ export class Layout extends React.Component<ILayoutProps, ILayoutState> {
|
|
|
844
899
|
this.newTabJson = undefined;
|
|
845
900
|
|
|
846
901
|
try {
|
|
847
|
-
const {callback, dragging, over, x, y, location} = this.customDrop;
|
|
902
|
+
const { callback, dragging, over, x, y, location } = this.customDrop;
|
|
848
903
|
callback(dragging, over, x, y, location);
|
|
904
|
+
if (this.fnNewNodeDropped != null) {
|
|
905
|
+
this.fnNewNodeDropped();
|
|
906
|
+
this.fnNewNodeDropped = undefined;
|
|
907
|
+
}
|
|
849
908
|
} catch (e) {
|
|
850
909
|
console.error(e)
|
|
851
910
|
}
|
|
@@ -861,8 +920,70 @@ export class Layout extends React.Component<ILayoutProps, ILayoutState> {
|
|
|
861
920
|
this.doAction(Actions.moveNode(this.dragNode.getId(), this.dropInfo.node.getId(), this.dropInfo.location, this.dropInfo.index));
|
|
862
921
|
}
|
|
863
922
|
}
|
|
923
|
+
this.setState({ showHiddenBorder: DockLocation.CENTER });
|
|
864
924
|
};
|
|
865
925
|
|
|
926
|
+
/** @hidden @internal */
|
|
927
|
+
private handleCustomTabDrag(dropInfo: DropInfo, pos: { x: number; y: number; }, event: React.MouseEvent<Element, MouseEvent>) {
|
|
928
|
+
let invalidated = this.customDrop?.invalidated;
|
|
929
|
+
const currentCallback = this.customDrop?.callback;
|
|
930
|
+
this.customDrop = undefined;
|
|
931
|
+
|
|
932
|
+
const dragging = this.newTabJson || (this.dragNode instanceof TabNode ? this.dragNode : undefined);
|
|
933
|
+
if (dragging && (dropInfo.node instanceof TabSetNode || dropInfo.node instanceof BorderNode) && dropInfo.index === -1) {
|
|
934
|
+
const selected = dropInfo.node.getSelectedNode() as TabNode | undefined;
|
|
935
|
+
const tabRect = selected?.getRect();
|
|
936
|
+
|
|
937
|
+
if (selected && tabRect?.contains(pos.x, pos.y)) {
|
|
938
|
+
let customDrop: ICustomDropDestination | undefined = undefined;
|
|
939
|
+
|
|
940
|
+
try {
|
|
941
|
+
const dest = this.onTabDrag(dragging, selected, pos.x - tabRect.x, pos.y - tabRect.y, dropInfo.location, () => this.onDragMove(event));
|
|
942
|
+
|
|
943
|
+
if (dest) {
|
|
944
|
+
customDrop = {
|
|
945
|
+
rect: new Rect(dest.x + tabRect.x, dest.y + tabRect.y, dest.width, dest.height),
|
|
946
|
+
callback: dest.callback,
|
|
947
|
+
invalidated: dest.invalidated,
|
|
948
|
+
dragging: dragging,
|
|
949
|
+
over: selected,
|
|
950
|
+
x: pos.x - tabRect.x,
|
|
951
|
+
y: pos.y - tabRect.y,
|
|
952
|
+
location: dropInfo.location,
|
|
953
|
+
cursor: dest.cursor
|
|
954
|
+
};
|
|
955
|
+
}
|
|
956
|
+
} catch (e) {
|
|
957
|
+
console.error(e);
|
|
958
|
+
}
|
|
959
|
+
|
|
960
|
+
if (customDrop?.callback === currentCallback) {
|
|
961
|
+
invalidated = undefined;
|
|
962
|
+
}
|
|
963
|
+
|
|
964
|
+
this.customDrop = customDrop;
|
|
965
|
+
}
|
|
966
|
+
}
|
|
967
|
+
|
|
968
|
+
this.dropInfo = dropInfo;
|
|
969
|
+
this.outlineDiv!.className = this.getClassName(this.customDrop ? CLASSES.FLEXLAYOUT__OUTLINE_RECT : dropInfo.className);
|
|
970
|
+
|
|
971
|
+
if (this.customDrop) {
|
|
972
|
+
this.customDrop.rect.positionElement(this.outlineDiv!);
|
|
973
|
+
} else {
|
|
974
|
+
dropInfo.rect.positionElement(this.outlineDiv!);
|
|
975
|
+
}
|
|
976
|
+
|
|
977
|
+
DragDrop.instance.setGlassCursorOverride(this.customDrop?.cursor);
|
|
978
|
+
this.outlineDiv!.style.visibility = "visible";
|
|
979
|
+
|
|
980
|
+
try {
|
|
981
|
+
invalidated?.();
|
|
982
|
+
} catch (e) {
|
|
983
|
+
console.error(e);
|
|
984
|
+
}
|
|
985
|
+
}
|
|
986
|
+
|
|
866
987
|
/** @hidden @internal */
|
|
867
988
|
onDragEnter(event: React.DragEvent<HTMLDivElement>) {
|
|
868
989
|
// DragDrop keeps track of number of dragenters minus the number of
|
|
@@ -878,6 +999,40 @@ export class Layout extends React.Component<ILayoutProps, ILayoutState> {
|
|
|
878
999
|
}
|
|
879
1000
|
}
|
|
880
1001
|
|
|
1002
|
+
|
|
1003
|
+
/** @hidden @internal */
|
|
1004
|
+
checkForBorderToShow(x: number, y: number) {
|
|
1005
|
+
const r = this.props.model._getOuterInnerRects().outer;
|
|
1006
|
+
const c = r.getCenter();
|
|
1007
|
+
const margin = this.edgeRectWidth;
|
|
1008
|
+
const offset = this.edgeRectLength / 2;
|
|
1009
|
+
|
|
1010
|
+
let overEdge = false;
|
|
1011
|
+
if (this.props.model.isEnableEdgeDock() && this.state.showHiddenBorder === DockLocation.CENTER) {
|
|
1012
|
+
if ((y > c.y - offset && y < c.y + offset) ||
|
|
1013
|
+
(x > c.x - offset && x < c.x + offset)) {
|
|
1014
|
+
overEdge = true;
|
|
1015
|
+
}
|
|
1016
|
+
}
|
|
1017
|
+
|
|
1018
|
+
let location = DockLocation.CENTER;
|
|
1019
|
+
if (!overEdge) {
|
|
1020
|
+
if (x <= r.x + margin) {
|
|
1021
|
+
location = DockLocation.LEFT;
|
|
1022
|
+
} else if (x >= r.getRight() - margin) {
|
|
1023
|
+
location = DockLocation.RIGHT;
|
|
1024
|
+
} else if (y <= r.y + margin) {
|
|
1025
|
+
location = DockLocation.TOP;
|
|
1026
|
+
} else if (y >= r.getBottom() - margin) {
|
|
1027
|
+
location = DockLocation.BOTTOM;
|
|
1028
|
+
}
|
|
1029
|
+
}
|
|
1030
|
+
|
|
1031
|
+
if (location !== this.state.showHiddenBorder) {
|
|
1032
|
+
this.setState({ showHiddenBorder: location });
|
|
1033
|
+
}
|
|
1034
|
+
}
|
|
1035
|
+
|
|
881
1036
|
/** @hidden @internal */
|
|
882
1037
|
showEdges(rootdiv: HTMLElement) {
|
|
883
1038
|
if (this.props.model.isEnableEdgeDock()) {
|
|
@@ -951,7 +1106,7 @@ export class Layout extends React.Component<ILayoutProps, ILayoutState> {
|
|
|
951
1106
|
rootdiv.removeChild(this.edgeLeftDiv!);
|
|
952
1107
|
rootdiv.removeChild(this.edgeBottomDiv!);
|
|
953
1108
|
rootdiv.removeChild(this.edgeRightDiv!);
|
|
954
|
-
} catch (e) {}
|
|
1109
|
+
} catch (e) { }
|
|
955
1110
|
}
|
|
956
1111
|
|
|
957
1112
|
this.edgesShown = false;
|
|
@@ -993,6 +1148,44 @@ export class Layout extends React.Component<ILayoutProps, ILayoutState> {
|
|
|
993
1148
|
}
|
|
994
1149
|
return message;
|
|
995
1150
|
}
|
|
1151
|
+
|
|
1152
|
+
/** @hidden @internal */
|
|
1153
|
+
getOnRenderFloatingTabPlaceholder() {
|
|
1154
|
+
return this.props.onRenderFloatingTabPlaceholder;
|
|
1155
|
+
}
|
|
1156
|
+
|
|
1157
|
+
/** @hidden @internal */
|
|
1158
|
+
showContextMenu(node: TabNode | TabSetNode | BorderNode, event: React.MouseEvent<HTMLElement, MouseEvent>) {
|
|
1159
|
+
if (this.props.onContextMenu) {
|
|
1160
|
+
this.props.onContextMenu(node, event);
|
|
1161
|
+
}
|
|
1162
|
+
}
|
|
1163
|
+
|
|
1164
|
+
/** @hidden @internal */
|
|
1165
|
+
auxMouseClick(node: TabNode | TabSetNode | BorderNode, event: React.MouseEvent<HTMLElement, MouseEvent>) {
|
|
1166
|
+
if (this.props.onAuxMouseClick) {
|
|
1167
|
+
this.props.onAuxMouseClick(node, event);
|
|
1168
|
+
}
|
|
1169
|
+
}
|
|
996
1170
|
}
|
|
997
1171
|
|
|
1172
|
+
// wrapper round the drag rect renderer that can call
|
|
1173
|
+
// a method once the rendering is written to the dom
|
|
1174
|
+
|
|
1175
|
+
/** @hidden @internal */
|
|
1176
|
+
interface IDragRectRenderWrapper {
|
|
1177
|
+
onRendered?: () => void;
|
|
1178
|
+
children: React.ReactNode;
|
|
1179
|
+
}
|
|
1180
|
+
|
|
1181
|
+
/** @hidden @internal */
|
|
1182
|
+
const DragRectRenderWrapper = (props: IDragRectRenderWrapper) => {
|
|
1183
|
+
React.useEffect(() => {
|
|
1184
|
+
props.onRendered?.();
|
|
1185
|
+
}, [props]);
|
|
1186
|
+
|
|
1187
|
+
return (<React.Fragment>
|
|
1188
|
+
{props.children}
|
|
1189
|
+
</React.Fragment>)
|
|
1190
|
+
}
|
|
998
1191
|
export default Layout;
|
package/src/view/Splitter.tsx
CHANGED
|
@@ -24,6 +24,7 @@ export const Splitter = (props: ISplitterProps) => {
|
|
|
24
24
|
const parentNode = node.getParent() as RowNode | BorderNode;
|
|
25
25
|
|
|
26
26
|
const onMouseDown = (event: Event | React.MouseEvent<HTMLDivElement, MouseEvent> | React.TouchEvent<HTMLDivElement>) => {
|
|
27
|
+
DragDrop.instance.setGlassCursorOverride(node.getOrientation() === Orientation.HORZ ? "ns-resize" : "ew-resize");
|
|
27
28
|
DragDrop.instance.startDrag(event, onDragStart, onDragMove, onDragEnd, onDragCancel, undefined, undefined, layout.getCurrentDocument(), layout.getRootDiv());
|
|
28
29
|
pBounds.current = parentNode._getSplitterBounds(node, true);
|
|
29
30
|
const rootdiv = layout.getRootDiv();
|
|
@@ -31,7 +32,14 @@ export const Splitter = (props: ISplitterProps) => {
|
|
|
31
32
|
outlineDiv.current.style.position = "absolute";
|
|
32
33
|
outlineDiv.current.className = layout.getClassName(CLASSES.FLEXLAYOUT__SPLITTER_DRAG);
|
|
33
34
|
outlineDiv.current.style.cursor = node.getOrientation() === Orientation.HORZ ? "ns-resize" : "ew-resize";
|
|
34
|
-
node.getRect()
|
|
35
|
+
const r = node.getRect();
|
|
36
|
+
if (node.getOrientation() === Orientation.VERT && r.width < 2) {
|
|
37
|
+
r.width = 2;
|
|
38
|
+
} else if (node.getOrientation() === Orientation.HORZ && r.height < 2) {
|
|
39
|
+
r.height = 2;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
r.positionElement(outlineDiv.current);
|
|
35
43
|
rootdiv.appendChild(outlineDiv.current);
|
|
36
44
|
};
|
|
37
45
|
|
|
@@ -106,7 +114,8 @@ export const Splitter = (props: ISplitterProps) => {
|
|
|
106
114
|
};
|
|
107
115
|
|
|
108
116
|
const cm = layout.getClassName;
|
|
109
|
-
|
|
117
|
+
let r = node.getRect();
|
|
118
|
+
const style = r.styleWithPosition({
|
|
110
119
|
cursor: node.getOrientation() === Orientation.HORZ ? "ns-resize" : "ew-resize",
|
|
111
120
|
});
|
|
112
121
|
let className = cm(CLASSES.FLEXLAYOUT__SPLITTER) + " " + cm(CLASSES.FLEXLAYOUT__SPLITTER_ + node.getOrientation().getName());
|
|
@@ -119,5 +128,42 @@ export const Splitter = (props: ISplitterProps) => {
|
|
|
119
128
|
}
|
|
120
129
|
}
|
|
121
130
|
|
|
122
|
-
|
|
131
|
+
const extra = node.getModel().getSplitterExtra();
|
|
132
|
+
if (extra === 0) {
|
|
133
|
+
return (<div
|
|
134
|
+
style={style}
|
|
135
|
+
className={className}
|
|
136
|
+
onTouchStart={onMouseDown}
|
|
137
|
+
onMouseDown={onMouseDown}>
|
|
138
|
+
</div>);
|
|
139
|
+
} else {
|
|
140
|
+
// add extended transparent div for hit testing
|
|
141
|
+
// extends forward only, so as not to interfere with scrollbars
|
|
142
|
+
let r2 = r.clone();
|
|
143
|
+
r2.x = 0;
|
|
144
|
+
r2.y = 0;
|
|
145
|
+
if (node.getOrientation() === Orientation.VERT) {
|
|
146
|
+
r2.width += extra;
|
|
147
|
+
} else {
|
|
148
|
+
r2.height += extra;
|
|
149
|
+
}
|
|
150
|
+
const style2 = r2.styleWithPosition({
|
|
151
|
+
cursor: node.getOrientation() === Orientation.HORZ ? "ns-resize" : "ew-resize"
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
const className2 = cm(CLASSES.FLEXLAYOUT__SPLITTER_EXTRA);
|
|
155
|
+
|
|
156
|
+
return (
|
|
157
|
+
<div
|
|
158
|
+
style={style}
|
|
159
|
+
className={className}>
|
|
160
|
+
<div
|
|
161
|
+
style={style2}
|
|
162
|
+
className={className2}
|
|
163
|
+
onTouchStart={onMouseDown}
|
|
164
|
+
onMouseDown={onMouseDown}>
|
|
165
|
+
</div>
|
|
166
|
+
</div>);
|
|
167
|
+
}
|
|
168
|
+
|
|
123
169
|
};
|
package/src/view/TabButton.tsx
CHANGED
|
@@ -7,6 +7,7 @@ import Rect from "../Rect";
|
|
|
7
7
|
import { IIcons, ILayoutCallbacks, ITitleObject } from "./Layout";
|
|
8
8
|
import { ICloseType } from "../model/ICloseType";
|
|
9
9
|
import { CLASSES } from "../Types";
|
|
10
|
+
import { isAuxMouseEvent } from "./TabSet";
|
|
10
11
|
|
|
11
12
|
/** @hidden @internal */
|
|
12
13
|
export interface ITabButtonProps {
|
|
@@ -28,12 +29,23 @@ export const TabButton = (props: ITabButtonProps) => {
|
|
|
28
29
|
const contentWidth = React.useRef<number>(0);
|
|
29
30
|
|
|
30
31
|
const onMouseDown = (event: React.MouseEvent<HTMLDivElement, MouseEvent> | React.TouchEvent<HTMLDivElement>) => {
|
|
31
|
-
|
|
32
|
+
|
|
33
|
+
if (!isAuxMouseEvent(event) && !layout.getEditingTab()) {
|
|
32
34
|
const message = layout.i18nName(I18nLabel.Move_Tab, node.getName());
|
|
33
35
|
layout.dragStart(event, message, node, node.isEnableDrag(), onClick, onDoubleClick);
|
|
34
36
|
}
|
|
35
37
|
};
|
|
36
38
|
|
|
39
|
+
const onAuxMouseClick = (event: React.MouseEvent<HTMLDivElement, MouseEvent>) => {
|
|
40
|
+
if (isAuxMouseEvent(event)) {
|
|
41
|
+
layout.auxMouseClick(node, event);
|
|
42
|
+
}
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
const onContextMenu = (event: React.MouseEvent<HTMLDivElement, MouseEvent>) => {
|
|
46
|
+
layout.showContextMenu(node, event);
|
|
47
|
+
};
|
|
48
|
+
|
|
37
49
|
const onClick = () => {
|
|
38
50
|
layout.doAction(Actions.selectTab(node.getId()));
|
|
39
51
|
};
|
|
@@ -211,7 +223,11 @@ export const TabButton = (props: ITabButtonProps) => {
|
|
|
211
223
|
}}
|
|
212
224
|
className={classNames}
|
|
213
225
|
onMouseDown={onMouseDown}
|
|
226
|
+
onClick={onAuxMouseClick}
|
|
227
|
+
onAuxClick={onAuxMouseClick}
|
|
228
|
+
onContextMenu={onContextMenu}
|
|
214
229
|
onTouchStart={onMouseDown}
|
|
230
|
+
title={node.getHelpText()}
|
|
215
231
|
>
|
|
216
232
|
{leading}
|
|
217
233
|
{content}
|