flexlayout-react 0.5.17 → 0.5.21
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 +20 -0
- package/README.md +123 -104
- package/declarations/Rect.d.ts +4 -0
- package/declarations/Types.d.ts +7 -1
- package/declarations/model/BorderNode.d.ts +1 -0
- package/declarations/model/IJsonModel.d.ts +3 -0
- package/declarations/model/Model.d.ts +1 -0
- package/declarations/view/Layout.d.ts +11 -3
- package/declarations/view/Tab.d.ts +1 -1
- package/dist/flexlayout.js +17 -17
- package/dist/flexlayout_min.js +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 +6 -0
- package/lib/Types.js.map +1 -1
- package/lib/model/BorderNode.js +22 -8
- package/lib/model/BorderNode.js.map +1 -1
- package/lib/model/BorderSet.js +15 -17
- package/lib/model/BorderSet.js.map +1 -1
- package/lib/model/Model.js +21 -3
- 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/TabSetNode.js +8 -4
- package/lib/model/TabSetNode.js.map +1 -1
- package/lib/view/BorderButton.js +17 -6
- package/lib/view/BorderButton.js.map +1 -1
- package/lib/view/BorderTabSet.js +17 -6
- package/lib/view/BorderTabSet.js.map +1 -1
- package/lib/view/FloatingWindow.js +13 -5
- package/lib/view/FloatingWindow.js.map +1 -1
- package/lib/view/Layout.js +205 -70
- package/lib/view/Layout.js.map +1 -1
- package/lib/view/Splitter.js +3 -3
- package/lib/view/Splitter.js.map +1 -1
- package/lib/view/Tab.js +18 -7
- package/lib/view/Tab.js.map +1 -1
- package/lib/view/TabButton.js +15 -8
- package/lib/view/TabButton.js.map +1 -1
- package/lib/view/TabFloating.js +29 -15
- package/lib/view/TabFloating.js.map +1 -1
- package/lib/view/TabSet.js +51 -25
- package/lib/view/TabSet.js.map +1 -1
- package/package.json +11 -6
- package/src/PopupMenu.tsx +32 -11
- package/src/Rect.ts +6 -2
- package/src/Types.ts +6 -0
- package/src/model/BorderNode.ts +22 -8
- package/src/model/BorderSet.ts +15 -17
- package/src/model/IJsonModel.ts +3 -0
- package/src/model/Model.ts +28 -3
- package/src/model/RowNode.ts +8 -5
- package/src/model/TabSetNode.ts +8 -4
- package/src/view/BorderButton.tsx +34 -6
- package/src/view/BorderTabSet.tsx +25 -5
- package/src/view/FloatingWindow.tsx +14 -6
- package/src/view/Layout.tsx +271 -92
- package/src/view/Splitter.tsx +4 -1
- package/src/view/Tab.tsx +22 -6
- package/src/view/TabButton.tsx +31 -11
- package/src/view/TabFloating.tsx +47 -23
- package/src/view/TabSet.tsx +72 -20
- package/style/_base.scss +72 -48
- package/style/dark.css +88 -66
- package/style/dark.css.map +1 -1
- package/style/dark.scss +20 -20
- package/style/gray.css +88 -66
- package/style/gray.css.map +1 -1
- package/style/gray.scss +20 -20
- package/style/light.css +88 -66
- package/style/light.css.map +1 -1
- package/style/light.scss +18 -18
|
@@ -16,7 +16,7 @@ export interface IFloatingWindowProps {
|
|
|
16
16
|
interface IStyleSheet {
|
|
17
17
|
href: string | null;
|
|
18
18
|
type: string;
|
|
19
|
-
rules: string[];
|
|
19
|
+
rules: string[] | null;
|
|
20
20
|
}
|
|
21
21
|
|
|
22
22
|
/** @hidden @internal */
|
|
@@ -31,17 +31,23 @@ export const FloatingWindow = (props: React.PropsWithChildren<IFloatingWindowPro
|
|
|
31
31
|
// the floating window. window.document.styleSheets is mutable and we can't guarantee
|
|
32
32
|
// the styles will exist when 'popoutWindow.load' is called below.
|
|
33
33
|
const styles = Array.from(window.document.styleSheets).reduce((result, styleSheet) => {
|
|
34
|
+
let rules: CSSRuleList | undefined = undefined;
|
|
35
|
+
try {
|
|
36
|
+
rules = styleSheet.cssRules;
|
|
37
|
+
} catch (e) {
|
|
38
|
+
// styleSheet.cssRules can throw security exception
|
|
39
|
+
}
|
|
40
|
+
|
|
34
41
|
try {
|
|
35
42
|
return [
|
|
36
43
|
...result,
|
|
37
44
|
{
|
|
38
45
|
href: styleSheet.href,
|
|
39
46
|
type: styleSheet.type,
|
|
40
|
-
rules: Array.from(
|
|
47
|
+
rules: rules ? Array.from(rules).map(rule => rule.cssText) : null,
|
|
41
48
|
}
|
|
42
49
|
];
|
|
43
50
|
} catch (e) {
|
|
44
|
-
// styleSheet.cssRules can throw security exception
|
|
45
51
|
return result;
|
|
46
52
|
}
|
|
47
53
|
}, [] as IStyleSheet[]);
|
|
@@ -113,9 +119,11 @@ function copyStyles(doc: Document, styleSheets: IStyleSheet[]): Promise<boolean[
|
|
|
113
119
|
})
|
|
114
120
|
);
|
|
115
121
|
} else {
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
122
|
+
if (styleSheet.rules) {
|
|
123
|
+
const style = doc.createElement("style");
|
|
124
|
+
styleSheet.rules.forEach(rule => style.appendChild(doc.createTextNode(rule)));
|
|
125
|
+
head.appendChild(style);
|
|
126
|
+
}
|
|
119
127
|
}
|
|
120
128
|
});
|
|
121
129
|
return Promise.all(promises);
|
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";
|
|
@@ -24,8 +25,12 @@ import { FloatingWindow } from "./FloatingWindow";
|
|
|
24
25
|
import { FloatingWindowTab } from "./FloatingWindowTab";
|
|
25
26
|
import { TabFloating } from "./TabFloating";
|
|
26
27
|
import { IJsonTabNode } from "../model/IJsonModel";
|
|
28
|
+
import { Orientation } from "..";
|
|
27
29
|
|
|
28
30
|
export type CustomDragCallback = (dragging: TabNode | IJsonTabNode, over: TabNode, x: number, y: number, location: DockLocation) => void;
|
|
31
|
+
export type DragRectRenderCallback = (text: String, node?: Node, json?: IJsonTabNode) => React.ReactElement | undefined;
|
|
32
|
+
export type FloatingTabPlaceholderRenderCallback = (dockPopout: () => void, showPopout: () => void) => React.ReactElement | undefined;
|
|
33
|
+
export type NodeMouseEvent = (node: TabNode | TabSetNode | BorderNode, event: React.MouseEvent<HTMLElement, MouseEvent>) => void;
|
|
29
34
|
|
|
30
35
|
export interface ILayoutProps {
|
|
31
36
|
model: Model;
|
|
@@ -39,17 +44,17 @@ export interface ILayoutProps {
|
|
|
39
44
|
onAction?: (action: Action) => Action | undefined;
|
|
40
45
|
onRenderTab?: (
|
|
41
46
|
node: TabNode,
|
|
42
|
-
renderValues: ITabRenderValues,
|
|
47
|
+
renderValues: ITabRenderValues, // change the values in this object as required
|
|
43
48
|
) => void;
|
|
44
49
|
onRenderTabSet?: (
|
|
45
50
|
tabSetNode: TabSetNode | BorderNode,
|
|
46
|
-
renderValues: ITabSetRenderValues,
|
|
51
|
+
renderValues: ITabSetRenderValues, // change the values in this object as required
|
|
47
52
|
) => void;
|
|
48
53
|
onModelChange?: (model: Model) => void;
|
|
49
54
|
onExternalDrag?: (event: React.DragEvent<HTMLDivElement>) => undefined | {
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
55
|
+
dragText: string,
|
|
56
|
+
json: any,
|
|
57
|
+
onDrop?: (node?: Node, event?: Event) => void
|
|
53
58
|
};
|
|
54
59
|
classNameMapper?: (defaultClassName: string) => string;
|
|
55
60
|
i18nMapper?: (id: I18nLabel, param?: string) => string | undefined;
|
|
@@ -66,6 +71,10 @@ export interface ILayoutProps {
|
|
|
66
71
|
invalidated?: () => void,
|
|
67
72
|
cursor?: string | undefined
|
|
68
73
|
};
|
|
74
|
+
onRenderDragRect?: DragRectRenderCallback;
|
|
75
|
+
onRenderFloatingTabPlaceholder?: FloatingTabPlaceholderRenderCallback;
|
|
76
|
+
onContextMenu?: NodeMouseEvent;
|
|
77
|
+
onAuxMouseClick?: NodeMouseEvent;
|
|
69
78
|
}
|
|
70
79
|
export interface IFontValues {
|
|
71
80
|
size?: string;
|
|
@@ -99,6 +108,7 @@ export interface ILayoutState {
|
|
|
99
108
|
calculatedTabBarSize: number;
|
|
100
109
|
calculatedBorderBarSize: number;
|
|
101
110
|
editingTab?: TabNode;
|
|
111
|
+
showHiddenBorder: DockLocation;
|
|
102
112
|
}
|
|
103
113
|
|
|
104
114
|
export interface IIcons {
|
|
@@ -153,7 +163,9 @@ export interface ILayoutCallbacks {
|
|
|
153
163
|
styleFont: (style: Record<string, string>) => Record<string, string>;
|
|
154
164
|
setEditingTab(tabNode?: TabNode): void;
|
|
155
165
|
getEditingTab(): TabNode | undefined;
|
|
156
|
-
|
|
166
|
+
getOnRenderFloatingTabPlaceholder(): FloatingTabPlaceholderRenderCallback | undefined;
|
|
167
|
+
showContextMenu(node: TabNode | TabSetNode | BorderNode, event: React.MouseEvent<HTMLElement, MouseEvent>): void;
|
|
168
|
+
auxMouseClick(node: TabNode | TabSetNode | BorderNode, event: React.MouseEvent<HTMLElement, MouseEvent>): void;
|
|
157
169
|
}
|
|
158
170
|
|
|
159
171
|
// Popout windows work in latest browsers based on webkit (Chrome, Opera, Safari, latest Edge) and Firefox. They do
|
|
@@ -171,6 +183,7 @@ const defaultSupportsPopout: boolean = isDesktop && !isIEorEdge;
|
|
|
171
183
|
* A React component that hosts a multi-tabbed layout
|
|
172
184
|
*/
|
|
173
185
|
export class Layout extends React.Component<ILayoutProps, ILayoutState> {
|
|
186
|
+
|
|
174
187
|
/** @hidden @internal */
|
|
175
188
|
private selfRef: React.RefObject<HTMLDivElement>;
|
|
176
189
|
/** @hidden @internal */
|
|
@@ -200,6 +213,8 @@ export class Layout extends React.Component<ILayoutProps, ILayoutState> {
|
|
|
200
213
|
/** @hidden @internal */
|
|
201
214
|
private dragDiv?: HTMLDivElement;
|
|
202
215
|
/** @hidden @internal */
|
|
216
|
+
private dragRectRendered: boolean = true;
|
|
217
|
+
/** @hidden @internal */
|
|
203
218
|
private dragDivText: string = "";
|
|
204
219
|
/** @hidden @internal */
|
|
205
220
|
private dropInfo: DropInfo | undefined;
|
|
@@ -237,7 +252,7 @@ export class Layout extends React.Component<ILayoutProps, ILayoutState> {
|
|
|
237
252
|
/** @hidden @internal */
|
|
238
253
|
private firstRender: boolean;
|
|
239
254
|
/** @hidden @internal */
|
|
240
|
-
private resizeObserver
|
|
255
|
+
private resizeObserver?: ResizeObserver;
|
|
241
256
|
|
|
242
257
|
constructor(props: ILayoutProps) {
|
|
243
258
|
super(props);
|
|
@@ -253,11 +268,13 @@ export class Layout extends React.Component<ILayoutProps, ILayoutState> {
|
|
|
253
268
|
this.icons = props.closeIcon ? Object.assign({ close: props.closeIcon }, props.icons) : props.icons;
|
|
254
269
|
this.firstRender = true;
|
|
255
270
|
|
|
256
|
-
this.state = {
|
|
257
|
-
|
|
258
|
-
|
|
271
|
+
this.state = {
|
|
272
|
+
rect: new Rect(0, 0, 0, 0),
|
|
273
|
+
calculatedHeaderBarSize: 25,
|
|
274
|
+
calculatedTabBarSize: 26,
|
|
259
275
|
calculatedBorderBarSize: 30,
|
|
260
276
|
editingTab: undefined,
|
|
277
|
+
showHiddenBorder: DockLocation.CENTER,
|
|
261
278
|
};
|
|
262
279
|
|
|
263
280
|
this.onDragEnter = this.onDragEnter.bind(this);
|
|
@@ -411,7 +428,7 @@ export class Layout extends React.Component<ILayoutProps, ILayoutState> {
|
|
|
411
428
|
|
|
412
429
|
/** @hidden @internal */
|
|
413
430
|
setEditingTab(tabNode?: TabNode) {
|
|
414
|
-
this.setState({editingTab:tabNode});
|
|
431
|
+
this.setState({ editingTab: tabNode });
|
|
415
432
|
}
|
|
416
433
|
|
|
417
434
|
/** @hidden @internal */
|
|
@@ -444,11 +461,12 @@ export class Layout extends React.Component<ILayoutProps, ILayoutState> {
|
|
|
444
461
|
tabBarSize: this.state.calculatedTabBarSize,
|
|
445
462
|
borderBarSize: this.state.calculatedBorderBarSize,
|
|
446
463
|
};
|
|
464
|
+
this.props.model._setShowHiddenBorder(this.state.showHiddenBorder);
|
|
447
465
|
|
|
448
466
|
this.centerRect = this.props.model._layout(this.state.rect, metrics);
|
|
449
467
|
|
|
450
468
|
this.renderBorder(this.props.model.getBorderSet(), borderComponents, tabComponents, floatingWindows, splitterComponents);
|
|
451
|
-
this.renderChildren(this.props.model.getRoot(), tabSetComponents, tabComponents, floatingWindows, splitterComponents);
|
|
469
|
+
this.renderChildren("", this.props.model.getRoot(), tabSetComponents, tabComponents, floatingWindows, splitterComponents);
|
|
452
470
|
|
|
453
471
|
if (this.edgesShown) {
|
|
454
472
|
this.repositionEdges(this.state.rect)
|
|
@@ -526,10 +544,12 @@ export class Layout extends React.Component<ILayoutProps, ILayoutState> {
|
|
|
526
544
|
/** @hidden @internal */
|
|
527
545
|
renderBorder(borderSet: BorderSet, borderComponents: React.ReactNode[], tabComponents: Record<string, React.ReactNode>, floatingWindows: React.ReactNode[], splitterComponents: React.ReactNode[]) {
|
|
528
546
|
for (const border of borderSet.getBorders()) {
|
|
547
|
+
const borderPath = `/border/${border.getLocation().getName()}`;
|
|
529
548
|
if (border.isShowing()) {
|
|
530
549
|
borderComponents.push(
|
|
531
550
|
<BorderTabSet
|
|
532
|
-
key={
|
|
551
|
+
key={`border_${border.getLocation().getName()}`}
|
|
552
|
+
path={borderPath}
|
|
533
553
|
border={border}
|
|
534
554
|
layout={this}
|
|
535
555
|
iconFactory={this.props.iconFactory}
|
|
@@ -539,10 +559,13 @@ export class Layout extends React.Component<ILayoutProps, ILayoutState> {
|
|
|
539
559
|
);
|
|
540
560
|
const drawChildren = border._getDrawChildren();
|
|
541
561
|
let i = 0;
|
|
562
|
+
let tabCount = 0;
|
|
542
563
|
for (const child of drawChildren) {
|
|
543
564
|
if (child instanceof SplitterNode) {
|
|
544
|
-
|
|
565
|
+
let path = borderPath + "/s";
|
|
566
|
+
splitterComponents.push(<Splitter key={child.getId()} layout={this} node={child} path={path} />);
|
|
545
567
|
} else if (child instanceof TabNode) {
|
|
568
|
+
let path = borderPath + "/t" + tabCount++;
|
|
546
569
|
if (this.supportsPopout && child.isFloating()) {
|
|
547
570
|
const rect = this._getScreenRect(child);
|
|
548
571
|
floatingWindows.push(
|
|
@@ -558,9 +581,19 @@ export class Layout extends React.Component<ILayoutProps, ILayoutState> {
|
|
|
558
581
|
<FloatingWindowTab layout={this} node={child} factory={this.props.factory} />
|
|
559
582
|
</FloatingWindow>
|
|
560
583
|
);
|
|
561
|
-
tabComponents[child.getId()] = <TabFloating key={child.getId()}
|
|
584
|
+
tabComponents[child.getId()] = <TabFloating key={child.getId()}
|
|
585
|
+
layout={this}
|
|
586
|
+
path={path}
|
|
587
|
+
node={child}
|
|
588
|
+
selected={i === border.getSelected()
|
|
589
|
+
} />;
|
|
562
590
|
} else {
|
|
563
|
-
tabComponents[child.getId()] = <Tab key={child.getId()}
|
|
591
|
+
tabComponents[child.getId()] = <Tab key={child.getId()}
|
|
592
|
+
layout={this}
|
|
593
|
+
path={path}
|
|
594
|
+
node={child}
|
|
595
|
+
selected={i === border.getSelected()}
|
|
596
|
+
factory={this.props.factory} />;
|
|
564
597
|
}
|
|
565
598
|
}
|
|
566
599
|
i++;
|
|
@@ -570,16 +603,22 @@ export class Layout extends React.Component<ILayoutProps, ILayoutState> {
|
|
|
570
603
|
}
|
|
571
604
|
|
|
572
605
|
/** @hidden @internal */
|
|
573
|
-
renderChildren(node: RowNode | TabSetNode, tabSetComponents: React.ReactNode[], tabComponents: Record<string, React.ReactNode>, floatingWindows: React.ReactNode[], splitterComponents: React.ReactNode[]) {
|
|
606
|
+
renderChildren(path: string, node: RowNode | TabSetNode, tabSetComponents: React.ReactNode[], tabComponents: Record<string, React.ReactNode>, floatingWindows: React.ReactNode[], splitterComponents: React.ReactNode[]) {
|
|
574
607
|
const drawChildren = node._getDrawChildren();
|
|
608
|
+
let splitterCount = 0;
|
|
609
|
+
let tabCount = 0;
|
|
610
|
+
let rowCount = 0;
|
|
575
611
|
|
|
576
612
|
for (const child of drawChildren!) {
|
|
577
613
|
if (child instanceof SplitterNode) {
|
|
578
|
-
|
|
614
|
+
const newPath = path + "/s" + (splitterCount++);
|
|
615
|
+
splitterComponents.push(<Splitter key={child.getId()} layout={this} path={newPath} node={child} />);
|
|
579
616
|
} else if (child instanceof TabSetNode) {
|
|
580
|
-
|
|
581
|
-
|
|
617
|
+
const newPath = path + "/ts" + (rowCount++);
|
|
618
|
+
tabSetComponents.push(<TabSet key={child.getId()} layout={this} path={newPath} node={child} iconFactory={this.props.iconFactory} titleFactory={this.props.titleFactory} icons={this.icons} />);
|
|
619
|
+
this.renderChildren(newPath, child, tabSetComponents, tabComponents, floatingWindows, splitterComponents);
|
|
582
620
|
} else if (child instanceof TabNode) {
|
|
621
|
+
const newPath = path + "/t" + (tabCount++);
|
|
583
622
|
const selectedTab = child.getParent()!.getChildren()[(child.getParent() as TabSetNode).getSelected()];
|
|
584
623
|
if (selectedTab === undefined) {
|
|
585
624
|
// this should not happen!
|
|
@@ -600,13 +639,14 @@ export class Layout extends React.Component<ILayoutProps, ILayoutState> {
|
|
|
600
639
|
<FloatingWindowTab layout={this} node={child} factory={this.props.factory} />
|
|
601
640
|
</FloatingWindow>
|
|
602
641
|
);
|
|
603
|
-
tabComponents[child.getId()] = <TabFloating key={child.getId()} layout={this} node={child} selected={child === selectedTab} />;
|
|
642
|
+
tabComponents[child.getId()] = <TabFloating key={child.getId()} layout={this} path={newPath} node={child} selected={child === selectedTab} />;
|
|
604
643
|
} else {
|
|
605
|
-
tabComponents[child.getId()] = <Tab key={child.getId()} layout={this} node={child} selected={child === selectedTab} factory={this.props.factory} />;
|
|
644
|
+
tabComponents[child.getId()] = <Tab key={child.getId()} layout={this} path={newPath} node={child} selected={child === selectedTab} factory={this.props.factory} />;
|
|
606
645
|
}
|
|
607
646
|
} else {
|
|
608
647
|
// is row
|
|
609
|
-
|
|
648
|
+
const newPath = path + ((child.getOrientation() === Orientation.HORZ) ? "/r" : "/c") + (rowCount++);
|
|
649
|
+
this.renderChildren(newPath, child as RowNode, tabSetComponents, tabComponents, floatingWindows, splitterComponents);
|
|
610
650
|
}
|
|
611
651
|
}
|
|
612
652
|
}
|
|
@@ -649,7 +689,7 @@ export class Layout extends React.Component<ILayoutProps, ILayoutState> {
|
|
|
649
689
|
* Adds a new tab by dragging a labeled panel to the drop location, dragging starts immediatelly
|
|
650
690
|
* @param dragText the text to show on the drag panel
|
|
651
691
|
* @param json the json for the new tab node
|
|
652
|
-
* @param onDrop a callback to call when the drag is complete
|
|
692
|
+
* @param onDrop a callback to call when the drag is complete (node and event will be undefined if the drag was cancelled)
|
|
653
693
|
*/
|
|
654
694
|
addTabWithDragAndDrop(dragText: string, json: IJsonTabNode, onDrop?: (node?: Node, event?: Event) => void) {
|
|
655
695
|
this.fnNewNodeDropped = onDrop;
|
|
@@ -663,9 +703,9 @@ export class Layout extends React.Component<ILayoutProps, ILayoutState> {
|
|
|
663
703
|
*
|
|
664
704
|
* @param dragText the text to show on the drag panel
|
|
665
705
|
* @param json the json for the new tab node
|
|
666
|
-
* @param onDrop a callback to call when the drag is complete
|
|
706
|
+
* @param onDrop a callback to call when the drag is complete (node and event will be undefined if the drag was cancelled)
|
|
667
707
|
*/
|
|
668
|
-
addTabWithDragAndDropIndirect(dragText: string, json: IJsonTabNode, onDrop?: () => void) {
|
|
708
|
+
addTabWithDragAndDropIndirect(dragText: string, json: IJsonTabNode, onDrop?: (node?: Node, event?: Event) => void) {
|
|
669
709
|
this.fnNewNodeDropped = onDrop;
|
|
670
710
|
this.newTabJson = json;
|
|
671
711
|
|
|
@@ -673,15 +713,22 @@ export class Layout extends React.Component<ILayoutProps, ILayoutState> {
|
|
|
673
713
|
|
|
674
714
|
this.dragDivText = dragText;
|
|
675
715
|
this.dragDiv = this.currentDocument!.createElement("div");
|
|
676
|
-
this.dragDiv.className = this.getClassName(
|
|
677
|
-
this.dragDiv.innerHTML = this.dragDivText;
|
|
716
|
+
this.dragDiv.className = this.getClassName(CLASSES.FLEXLAYOUT__DRAG_RECT);
|
|
678
717
|
this.dragDiv.addEventListener("mousedown", this.onDragDivMouseDown);
|
|
679
718
|
this.dragDiv.addEventListener("touchstart", this.onDragDivMouseDown);
|
|
680
719
|
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
720
|
+
this.dragRectRender(this.dragDivText, undefined, this.newTabJson, () => {
|
|
721
|
+
if (this.dragDiv) {
|
|
722
|
+
// now it's been rendered into the dom it can be centered
|
|
723
|
+
this.dragDiv.style.visibility = "visible";
|
|
724
|
+
const domRect = this.dragDiv.getBoundingClientRect();
|
|
725
|
+
const r = new Rect(0, 0, domRect?.width, domRect?.height);
|
|
726
|
+
r.centerInRect(this.state.rect);
|
|
727
|
+
this.dragDiv.setAttribute("data-layout-path", "/drag-rectangle");
|
|
728
|
+
this.dragDiv.style.left = r.x + "px";
|
|
729
|
+
this.dragDiv.style.top = r.y + "px";
|
|
730
|
+
}
|
|
731
|
+
});
|
|
685
732
|
|
|
686
733
|
const rootdiv = this.selfRef.current;
|
|
687
734
|
rootdiv!.appendChild(this.dragDiv);
|
|
@@ -715,11 +762,11 @@ export class Layout extends React.Component<ILayoutProps, ILayoutState> {
|
|
|
715
762
|
|
|
716
763
|
try {
|
|
717
764
|
rootdiv.removeChild(this.outlineDiv!);
|
|
718
|
-
} catch (e) {}
|
|
765
|
+
} catch (e) { }
|
|
719
766
|
|
|
720
767
|
try {
|
|
721
768
|
rootdiv.removeChild(this.dragDiv!);
|
|
722
|
-
} catch (e) {}
|
|
769
|
+
} catch (e) { }
|
|
723
770
|
|
|
724
771
|
this.dragDiv = undefined;
|
|
725
772
|
this.hideEdges(rootdiv);
|
|
@@ -738,6 +785,8 @@ export class Layout extends React.Component<ILayoutProps, ILayoutState> {
|
|
|
738
785
|
this.newTabJson = undefined;
|
|
739
786
|
this.customDrop = undefined;
|
|
740
787
|
}
|
|
788
|
+
this.setState({ showHiddenBorder: DockLocation.CENTER });
|
|
789
|
+
|
|
741
790
|
};
|
|
742
791
|
|
|
743
792
|
/** @hidden @internal */
|
|
@@ -764,6 +813,31 @@ export class Layout extends React.Component<ILayoutProps, ILayoutState> {
|
|
|
764
813
|
}
|
|
765
814
|
};
|
|
766
815
|
|
|
816
|
+
/** @hidden @internal */
|
|
817
|
+
dragRectRender = (text: String, node?: Node, json?: IJsonTabNode, onRendered?: () => void) => {
|
|
818
|
+
let content: React.ReactElement | undefined = <div style={{ whiteSpace: "pre" }}>{text.replace("<br>", "\n")}</div>;
|
|
819
|
+
|
|
820
|
+
if (this.props.onRenderDragRect !== undefined) {
|
|
821
|
+
const customContent = this.props.onRenderDragRect(text, node, json);
|
|
822
|
+
if (customContent !== undefined) {
|
|
823
|
+
content = customContent;
|
|
824
|
+
}
|
|
825
|
+
}
|
|
826
|
+
|
|
827
|
+
// hide div until the render is complete
|
|
828
|
+
this.dragDiv!.style.visibility = "hidden";
|
|
829
|
+
this.dragRectRendered = false;
|
|
830
|
+
ReactDOM.render(<DragRectRenderWrapper
|
|
831
|
+
// wait for it to be rendered
|
|
832
|
+
onRendered={() => {
|
|
833
|
+
this.dragRectRendered = true;
|
|
834
|
+
onRendered?.();
|
|
835
|
+
}}
|
|
836
|
+
>
|
|
837
|
+
{content}
|
|
838
|
+
</DragRectRenderWrapper>, this.dragDiv!);
|
|
839
|
+
};
|
|
840
|
+
|
|
767
841
|
/** @hidden @internal */
|
|
768
842
|
onDragStart = () => {
|
|
769
843
|
this.dropInfo = undefined;
|
|
@@ -777,7 +851,9 @@ export class Layout extends React.Component<ILayoutProps, ILayoutState> {
|
|
|
777
851
|
if (this.dragDiv == null) {
|
|
778
852
|
this.dragDiv = this.currentDocument!.createElement("div");
|
|
779
853
|
this.dragDiv.className = this.getClassName(CLASSES.FLEXLAYOUT__DRAG_RECT);
|
|
780
|
-
this.dragDiv.
|
|
854
|
+
this.dragDiv.setAttribute("data-layout-path", "/drag-rectangle");
|
|
855
|
+
this.dragRectRender(this.dragDivText, this.dragNode, this.newTabJson);
|
|
856
|
+
|
|
781
857
|
rootdiv.appendChild(this.dragDiv);
|
|
782
858
|
}
|
|
783
859
|
// add edge indicators
|
|
@@ -804,67 +880,32 @@ export class Layout extends React.Component<ILayoutProps, ILayoutState> {
|
|
|
804
880
|
y: event.clientY - clientRect.top,
|
|
805
881
|
};
|
|
806
882
|
|
|
807
|
-
this.
|
|
883
|
+
this.checkForBorderToShow(pos.x, pos.y);
|
|
884
|
+
|
|
885
|
+
// keep it between left & right
|
|
886
|
+
const dragRect = this.dragDiv!.getBoundingClientRect();
|
|
887
|
+
let newLeft = pos.x - dragRect.width / 2;
|
|
888
|
+
if (newLeft + dragRect.width > clientRect.width) {
|
|
889
|
+
newLeft = clientRect.width - dragRect.width;
|
|
890
|
+
}
|
|
891
|
+
newLeft = Math.max(0, newLeft);
|
|
892
|
+
|
|
893
|
+
this.dragDiv!.style.left = newLeft + "px";
|
|
808
894
|
this.dragDiv!.style.top = pos.y + 5 + "px";
|
|
895
|
+
if (this.dragRectRendered && this.dragDiv!.style.visibility === "hidden") {
|
|
896
|
+
// make visible once the drag rect has been rendered
|
|
897
|
+
this.dragDiv!.style.visibility = "visible";
|
|
898
|
+
}
|
|
809
899
|
|
|
810
900
|
let dropInfo = this.props.model._findDropTargetNode(this.dragNode!, pos.x, pos.y);
|
|
811
901
|
if (dropInfo) {
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
this.customDrop = undefined;
|
|
815
|
-
|
|
816
|
-
const dragging = this.newTabJson || (this.dragNode instanceof TabNode ? this.dragNode : undefined);
|
|
817
|
-
if (dragging && (dropInfo.node instanceof TabSetNode || dropInfo.node instanceof BorderNode) && dropInfo.index === -1) {
|
|
818
|
-
const selected = dropInfo.node.getSelectedNode() as TabNode | undefined;
|
|
819
|
-
const tabRect = selected?.getRect()
|
|
820
|
-
|
|
821
|
-
if (selected && tabRect?.contains(pos.x, pos.y)) {
|
|
822
|
-
let customDrop: ICustomDropDestination | undefined = undefined;
|
|
823
|
-
|
|
824
|
-
try {
|
|
825
|
-
const dest = this.onTabDrag(dragging, selected, pos.x - tabRect.x, pos.y - tabRect.y, dropInfo.location, () => this.onDragMove(event));
|
|
826
|
-
|
|
827
|
-
if (dest) {
|
|
828
|
-
customDrop = {
|
|
829
|
-
rect: new Rect(dest.x + tabRect.x, dest.y + tabRect.y, dest.width, dest.height),
|
|
830
|
-
callback: dest.callback,
|
|
831
|
-
invalidated: dest.invalidated,
|
|
832
|
-
dragging: dragging,
|
|
833
|
-
over: selected,
|
|
834
|
-
x: pos.x - tabRect.x,
|
|
835
|
-
y: pos.y - tabRect.y,
|
|
836
|
-
location: dropInfo.location,
|
|
837
|
-
cursor: dest.cursor
|
|
838
|
-
};
|
|
839
|
-
}
|
|
840
|
-
} catch (e) {
|
|
841
|
-
console.error(e)
|
|
842
|
-
}
|
|
843
|
-
|
|
844
|
-
if (customDrop?.callback == currentCallback) {
|
|
845
|
-
invalidated = undefined;
|
|
846
|
-
}
|
|
847
|
-
|
|
848
|
-
this.customDrop = customDrop;
|
|
849
|
-
}
|
|
850
|
-
}
|
|
851
|
-
|
|
852
|
-
this.dropInfo = dropInfo;
|
|
853
|
-
this.outlineDiv!.className = this.getClassName(this.customDrop ? "flexlayout__outline_rect" : dropInfo.className);
|
|
854
|
-
|
|
855
|
-
if (this.customDrop) {
|
|
856
|
-
this.customDrop.rect.positionElement(this.outlineDiv!);
|
|
902
|
+
if (this.props.onTabDrag) {
|
|
903
|
+
this.handleCustomTabDrag(dropInfo, pos, event);
|
|
857
904
|
} else {
|
|
905
|
+
this.dropInfo = dropInfo;
|
|
906
|
+
this.outlineDiv!.className = this.getClassName(dropInfo.className);
|
|
858
907
|
dropInfo.rect.positionElement(this.outlineDiv!);
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
DragDrop.instance.setGlassCursorOverride(this.customDrop?.cursor);
|
|
862
|
-
this.outlineDiv!.style.visibility = "visible";
|
|
863
|
-
|
|
864
|
-
try {
|
|
865
|
-
invalidated?.();
|
|
866
|
-
} catch (e) {
|
|
867
|
-
console.error(e);
|
|
908
|
+
this.outlineDiv!.style.visibility = "visible";
|
|
868
909
|
}
|
|
869
910
|
}
|
|
870
911
|
};
|
|
@@ -883,8 +924,12 @@ export class Layout extends React.Component<ILayoutProps, ILayoutState> {
|
|
|
883
924
|
this.newTabJson = undefined;
|
|
884
925
|
|
|
885
926
|
try {
|
|
886
|
-
const {callback, dragging, over, x, y, location} = this.customDrop;
|
|
927
|
+
const { callback, dragging, over, x, y, location } = this.customDrop;
|
|
887
928
|
callback(dragging, over, x, y, location);
|
|
929
|
+
if (this.fnNewNodeDropped != null) {
|
|
930
|
+
this.fnNewNodeDropped();
|
|
931
|
+
this.fnNewNodeDropped = undefined;
|
|
932
|
+
}
|
|
888
933
|
} catch (e) {
|
|
889
934
|
console.error(e)
|
|
890
935
|
}
|
|
@@ -900,8 +945,70 @@ export class Layout extends React.Component<ILayoutProps, ILayoutState> {
|
|
|
900
945
|
this.doAction(Actions.moveNode(this.dragNode.getId(), this.dropInfo.node.getId(), this.dropInfo.location, this.dropInfo.index));
|
|
901
946
|
}
|
|
902
947
|
}
|
|
948
|
+
this.setState({ showHiddenBorder: DockLocation.CENTER });
|
|
903
949
|
};
|
|
904
950
|
|
|
951
|
+
/** @hidden @internal */
|
|
952
|
+
private handleCustomTabDrag(dropInfo: DropInfo, pos: { x: number; y: number; }, event: React.MouseEvent<Element, MouseEvent>) {
|
|
953
|
+
let invalidated = this.customDrop?.invalidated;
|
|
954
|
+
const currentCallback = this.customDrop?.callback;
|
|
955
|
+
this.customDrop = undefined;
|
|
956
|
+
|
|
957
|
+
const dragging = this.newTabJson || (this.dragNode instanceof TabNode ? this.dragNode : undefined);
|
|
958
|
+
if (dragging && (dropInfo.node instanceof TabSetNode || dropInfo.node instanceof BorderNode) && dropInfo.index === -1) {
|
|
959
|
+
const selected = dropInfo.node.getSelectedNode() as TabNode | undefined;
|
|
960
|
+
const tabRect = selected?.getRect();
|
|
961
|
+
|
|
962
|
+
if (selected && tabRect?.contains(pos.x, pos.y)) {
|
|
963
|
+
let customDrop: ICustomDropDestination | undefined = undefined;
|
|
964
|
+
|
|
965
|
+
try {
|
|
966
|
+
const dest = this.onTabDrag(dragging, selected, pos.x - tabRect.x, pos.y - tabRect.y, dropInfo.location, () => this.onDragMove(event));
|
|
967
|
+
|
|
968
|
+
if (dest) {
|
|
969
|
+
customDrop = {
|
|
970
|
+
rect: new Rect(dest.x + tabRect.x, dest.y + tabRect.y, dest.width, dest.height),
|
|
971
|
+
callback: dest.callback,
|
|
972
|
+
invalidated: dest.invalidated,
|
|
973
|
+
dragging: dragging,
|
|
974
|
+
over: selected,
|
|
975
|
+
x: pos.x - tabRect.x,
|
|
976
|
+
y: pos.y - tabRect.y,
|
|
977
|
+
location: dropInfo.location,
|
|
978
|
+
cursor: dest.cursor
|
|
979
|
+
};
|
|
980
|
+
}
|
|
981
|
+
} catch (e) {
|
|
982
|
+
console.error(e);
|
|
983
|
+
}
|
|
984
|
+
|
|
985
|
+
if (customDrop?.callback === currentCallback) {
|
|
986
|
+
invalidated = undefined;
|
|
987
|
+
}
|
|
988
|
+
|
|
989
|
+
this.customDrop = customDrop;
|
|
990
|
+
}
|
|
991
|
+
}
|
|
992
|
+
|
|
993
|
+
this.dropInfo = dropInfo;
|
|
994
|
+
this.outlineDiv!.className = this.getClassName(this.customDrop ? CLASSES.FLEXLAYOUT__OUTLINE_RECT : dropInfo.className);
|
|
995
|
+
|
|
996
|
+
if (this.customDrop) {
|
|
997
|
+
this.customDrop.rect.positionElement(this.outlineDiv!);
|
|
998
|
+
} else {
|
|
999
|
+
dropInfo.rect.positionElement(this.outlineDiv!);
|
|
1000
|
+
}
|
|
1001
|
+
|
|
1002
|
+
DragDrop.instance.setGlassCursorOverride(this.customDrop?.cursor);
|
|
1003
|
+
this.outlineDiv!.style.visibility = "visible";
|
|
1004
|
+
|
|
1005
|
+
try {
|
|
1006
|
+
invalidated?.();
|
|
1007
|
+
} catch (e) {
|
|
1008
|
+
console.error(e);
|
|
1009
|
+
}
|
|
1010
|
+
}
|
|
1011
|
+
|
|
905
1012
|
/** @hidden @internal */
|
|
906
1013
|
onDragEnter(event: React.DragEvent<HTMLDivElement>) {
|
|
907
1014
|
// DragDrop keeps track of number of dragenters minus the number of
|
|
@@ -917,6 +1024,40 @@ export class Layout extends React.Component<ILayoutProps, ILayoutState> {
|
|
|
917
1024
|
}
|
|
918
1025
|
}
|
|
919
1026
|
|
|
1027
|
+
|
|
1028
|
+
/** @hidden @internal */
|
|
1029
|
+
checkForBorderToShow(x: number, y: number) {
|
|
1030
|
+
const r = this.props.model._getOuterInnerRects().outer;
|
|
1031
|
+
const c = r.getCenter();
|
|
1032
|
+
const margin = this.edgeRectWidth;
|
|
1033
|
+
const offset = this.edgeRectLength / 2;
|
|
1034
|
+
|
|
1035
|
+
let overEdge = false;
|
|
1036
|
+
if (this.props.model.isEnableEdgeDock() && this.state.showHiddenBorder === DockLocation.CENTER) {
|
|
1037
|
+
if ((y > c.y - offset && y < c.y + offset) ||
|
|
1038
|
+
(x > c.x - offset && x < c.x + offset)) {
|
|
1039
|
+
overEdge = true;
|
|
1040
|
+
}
|
|
1041
|
+
}
|
|
1042
|
+
|
|
1043
|
+
let location = DockLocation.CENTER;
|
|
1044
|
+
if (!overEdge) {
|
|
1045
|
+
if (x <= r.x + margin) {
|
|
1046
|
+
location = DockLocation.LEFT;
|
|
1047
|
+
} else if (x >= r.getRight() - margin) {
|
|
1048
|
+
location = DockLocation.RIGHT;
|
|
1049
|
+
} else if (y <= r.y + margin) {
|
|
1050
|
+
location = DockLocation.TOP;
|
|
1051
|
+
} else if (y >= r.getBottom() - margin) {
|
|
1052
|
+
location = DockLocation.BOTTOM;
|
|
1053
|
+
}
|
|
1054
|
+
}
|
|
1055
|
+
|
|
1056
|
+
if (location !== this.state.showHiddenBorder) {
|
|
1057
|
+
this.setState({ showHiddenBorder: location });
|
|
1058
|
+
}
|
|
1059
|
+
}
|
|
1060
|
+
|
|
920
1061
|
/** @hidden @internal */
|
|
921
1062
|
showEdges(rootdiv: HTMLElement) {
|
|
922
1063
|
if (this.props.model.isEnableEdgeDock()) {
|
|
@@ -990,7 +1131,7 @@ export class Layout extends React.Component<ILayoutProps, ILayoutState> {
|
|
|
990
1131
|
rootdiv.removeChild(this.edgeLeftDiv!);
|
|
991
1132
|
rootdiv.removeChild(this.edgeBottomDiv!);
|
|
992
1133
|
rootdiv.removeChild(this.edgeRightDiv!);
|
|
993
|
-
} catch (e) {}
|
|
1134
|
+
} catch (e) { }
|
|
994
1135
|
}
|
|
995
1136
|
|
|
996
1137
|
this.edgesShown = false;
|
|
@@ -1032,6 +1173,44 @@ export class Layout extends React.Component<ILayoutProps, ILayoutState> {
|
|
|
1032
1173
|
}
|
|
1033
1174
|
return message;
|
|
1034
1175
|
}
|
|
1176
|
+
|
|
1177
|
+
/** @hidden @internal */
|
|
1178
|
+
getOnRenderFloatingTabPlaceholder() {
|
|
1179
|
+
return this.props.onRenderFloatingTabPlaceholder;
|
|
1180
|
+
}
|
|
1181
|
+
|
|
1182
|
+
/** @hidden @internal */
|
|
1183
|
+
showContextMenu(node: TabNode | TabSetNode | BorderNode, event: React.MouseEvent<HTMLElement, MouseEvent>) {
|
|
1184
|
+
if (this.props.onContextMenu) {
|
|
1185
|
+
this.props.onContextMenu(node, event);
|
|
1186
|
+
}
|
|
1187
|
+
}
|
|
1188
|
+
|
|
1189
|
+
/** @hidden @internal */
|
|
1190
|
+
auxMouseClick(node: TabNode | TabSetNode | BorderNode, event: React.MouseEvent<HTMLElement, MouseEvent>) {
|
|
1191
|
+
if (this.props.onAuxMouseClick) {
|
|
1192
|
+
this.props.onAuxMouseClick(node, event);
|
|
1193
|
+
}
|
|
1194
|
+
}
|
|
1035
1195
|
}
|
|
1036
1196
|
|
|
1197
|
+
// wrapper round the drag rect renderer that can call
|
|
1198
|
+
// a method once the rendering is written to the dom
|
|
1199
|
+
|
|
1200
|
+
/** @hidden @internal */
|
|
1201
|
+
interface IDragRectRenderWrapper {
|
|
1202
|
+
onRendered?: () => void;
|
|
1203
|
+
children: React.ReactNode;
|
|
1204
|
+
}
|
|
1205
|
+
|
|
1206
|
+
/** @hidden @internal */
|
|
1207
|
+
const DragRectRenderWrapper = (props: IDragRectRenderWrapper) => {
|
|
1208
|
+
React.useEffect(() => {
|
|
1209
|
+
props.onRendered?.();
|
|
1210
|
+
}, [props]);
|
|
1211
|
+
|
|
1212
|
+
return (<React.Fragment>
|
|
1213
|
+
{props.children}
|
|
1214
|
+
</React.Fragment>)
|
|
1215
|
+
}
|
|
1037
1216
|
export default Layout;
|