flexlayout-react 0.7.15 → 0.8.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/ChangeLog.txt +28 -0
- package/README.md +157 -330
- package/Screenshot_light.png +0 -0
- package/Screenshot_rounded.png +0 -0
- package/declarations/Attribute.d.ts +1 -1
- package/declarations/AttributeDefinitions.d.ts +1 -1
- package/declarations/DockLocation.d.ts +12 -12
- package/declarations/DropInfo.d.ts +12 -12
- package/declarations/I18nLabel.d.ts +12 -14
- package/declarations/Orientation.d.ts +7 -7
- package/declarations/PopupMenu.d.ts +1 -1
- package/declarations/Rect.d.ts +41 -28
- package/declarations/Types.d.ts +95 -79
- package/declarations/examples/demo/Utils.d.ts +4 -0
- package/declarations/index.d.ts +21 -22
- package/declarations/model/Action.d.ts +5 -5
- package/declarations/model/Actions.d.ts +127 -110
- package/declarations/model/BorderNode.d.ts +30 -34
- package/declarations/model/BorderSet.d.ts +3 -4
- package/declarations/model/ICloseType.d.ts +5 -5
- package/declarations/model/IDraggable.d.ts +2 -2
- package/declarations/model/IDropTarget.d.ts +2 -2
- package/declarations/model/IJsonModel.d.ts +811 -149
- package/declarations/model/LayoutWindow.d.ts +28 -0
- package/declarations/model/Model.d.ts +91 -86
- package/declarations/model/Node.d.ts +17 -17
- package/declarations/model/RowNode.d.ts +10 -11
- package/declarations/model/TabNode.d.ts +44 -37
- package/declarations/model/TabSetNode.d.ts +44 -41
- package/declarations/model/Utils.d.ts +1 -1
- package/declarations/model/WindowLayout.d.ts +24 -0
- package/declarations/src/Attribute.d.ts +1 -0
- package/declarations/src/AttributeDefinitions.d.ts +1 -0
- package/declarations/src/DockLocation.d.ts +12 -0
- package/declarations/src/DropInfo.d.ts +12 -0
- package/declarations/src/I18nLabel.d.ts +10 -0
- package/declarations/src/Orientation.d.ts +7 -0
- package/declarations/src/PopupMenu.d.ts +1 -0
- package/declarations/src/Rect.d.ts +31 -0
- package/declarations/src/Types.d.ts +92 -0
- package/declarations/src/index.d.ts +20 -0
- package/declarations/src/model/Action.d.ts +5 -0
- package/declarations/src/model/Actions.d.ts +110 -0
- package/declarations/src/model/BorderNode.d.ts +28 -0
- package/declarations/src/model/BorderSet.d.ts +3 -0
- package/declarations/src/model/ICloseType.d.ts +5 -0
- package/declarations/src/model/IDraggable.d.ts +2 -0
- package/declarations/src/model/IDropTarget.d.ts +2 -0
- package/declarations/src/model/IJsonModel.d.ts +153 -0
- package/declarations/src/model/Model.d.ts +98 -0
- package/declarations/src/model/Node.d.ts +16 -0
- package/declarations/src/model/RowNode.d.ts +11 -0
- package/declarations/src/model/TabNode.d.ts +36 -0
- package/declarations/src/model/TabSetNode.d.ts +37 -0
- package/declarations/src/model/Utils.d.ts +1 -0
- package/declarations/src/view/BorderButton.d.ts +1 -0
- package/declarations/src/view/BorderTab.d.ts +2 -0
- package/declarations/src/view/BorderTabSet.d.ts +1 -0
- package/declarations/src/view/DragContainer.d.ts +1 -0
- package/declarations/src/view/ErrorBoundary.d.ts +1 -0
- package/declarations/src/view/FloatingWindow.d.ts +1 -0
- package/declarations/src/view/Icons.d.ts +7 -0
- package/declarations/src/view/Layout.d.ts +113 -0
- package/declarations/src/view/Overlay.d.ts +1 -0
- package/declarations/src/view/PopupMenu.d.ts +1 -0
- package/declarations/src/view/Row.d.ts +1 -0
- package/declarations/src/view/Splitter.d.ts +1 -0
- package/declarations/src/view/Tab.d.ts +1 -0
- package/declarations/src/view/TabButton.d.ts +1 -0
- package/declarations/src/view/TabButtonStamp.d.ts +1 -0
- package/declarations/src/view/TabOverflowHook.d.ts +1 -0
- package/declarations/src/view/TabSet.d.ts +1 -0
- package/declarations/src/view/Utils.d.ts +4 -0
- package/declarations/view/BorderButton.d.ts +1 -1
- package/declarations/view/BorderTab.d.ts +2 -0
- package/declarations/view/BorderTabSet.d.ts +1 -1
- package/declarations/view/DragContainer.d.ts +1 -0
- package/declarations/view/ErrorBoundary.d.ts +1 -1
- package/declarations/view/ExtendedResizeObserver.d.ts +23 -0
- package/declarations/view/FloatingWindow.d.ts +1 -1
- package/declarations/view/Icons.d.ts +8 -7
- package/declarations/view/Layout.d.ts +140 -161
- package/declarations/view/Overlay.d.ts +1 -0
- package/declarations/view/PopoutWindow.d.ts +1 -0
- package/declarations/view/PopupMenu.d.ts +1 -0
- package/declarations/view/Row.d.ts +1 -0
- package/declarations/view/SizeTracker.d.ts +10 -0
- package/declarations/view/Splitter.d.ts +1 -1
- package/declarations/view/Tab.d.ts +1 -1
- package/declarations/view/TabButton.d.ts +1 -1
- package/declarations/view/TabButtonStamp.d.ts +1 -1
- package/declarations/view/TabOverflowHook.d.ts +1 -1
- package/declarations/view/TabSet.d.ts +1 -1
- package/declarations/view/Utils.d.ts +11 -1
- package/dist/bundles/demo.js +232052 -0
- package/dist/bundles/demo.js.map +1 -0
- package/dist/flexlayout.js +122 -92
- package/dist/flexlayout_min.js +1 -1
- package/lib/Attribute.js +42 -31
- package/lib/Attribute.js.map +1 -1
- package/lib/AttributeDefinitions.js +131 -108
- package/lib/AttributeDefinitions.js.map +1 -1
- package/lib/DockLocation.js +120 -124
- package/lib/DockLocation.js.map +1 -1
- package/lib/DropInfo.js +9 -13
- package/lib/DropInfo.js.map +1 -1
- package/lib/I18nLabel.js +13 -18
- package/lib/I18nLabel.js.map +1 -1
- package/lib/Orientation.js +22 -26
- package/lib/Orientation.js.map +1 -1
- package/lib/Rect.js +104 -72
- package/lib/Rect.js.map +1 -1
- package/lib/Types.js +96 -83
- package/lib/Types.js.map +1 -1
- package/lib/index.js +21 -38
- package/lib/index.js.map +1 -1
- package/lib/model/Action.js +6 -10
- package/lib/model/Action.js.map +1 -1
- package/lib/model/Actions.js +169 -155
- package/lib/model/Actions.js.map +1 -1
- package/lib/model/BorderNode.js +385 -406
- package/lib/model/BorderNode.js.map +1 -1
- package/lib/model/BorderSet.js +66 -121
- package/lib/model/BorderSet.js.map +1 -1
- package/lib/model/ICloseType.js +6 -9
- package/lib/model/ICloseType.js.map +1 -1
- package/lib/model/IDraggable.js +1 -2
- package/lib/model/IDropTarget.js +1 -2
- package/lib/model/IJsonModel.js +1 -2
- package/lib/model/LayoutWindow.js +83 -0
- package/lib/model/LayoutWindow.js.map +1 -0
- package/lib/model/Model.js +614 -496
- package/lib/model/Model.js.map +1 -1
- package/lib/model/Node.js +217 -228
- package/lib/model/Node.js.map +1 -1
- package/lib/model/RowNode.js +491 -504
- package/lib/model/RowNode.js.map +1 -1
- package/lib/model/TabNode.js +289 -184
- package/lib/model/TabNode.js.map +1 -1
- package/lib/model/TabSetNode.js +459 -446
- package/lib/model/TabSetNode.js.map +1 -1
- package/lib/model/Utils.js +47 -82
- package/lib/model/Utils.js.map +1 -1
- package/lib/view/BorderButton.js +129 -138
- package/lib/view/BorderButton.js.map +1 -1
- package/lib/view/BorderTab.js +47 -0
- package/lib/view/BorderTab.js.map +1 -0
- package/lib/view/BorderTabSet.js +134 -128
- package/lib/view/BorderTabSet.js.map +1 -1
- package/lib/view/DragContainer.js +16 -0
- package/lib/view/DragContainer.js.map +1 -0
- package/lib/view/ErrorBoundary.js +23 -27
- package/lib/view/ErrorBoundary.js.map +1 -1
- package/lib/view/Icons.js +40 -45
- package/lib/view/Icons.js.map +1 -1
- package/lib/view/Layout.js +919 -907
- package/lib/view/Layout.js.map +1 -1
- package/lib/view/Overlay.js +9 -0
- package/lib/view/Overlay.js.map +1 -0
- package/lib/view/PopoutWindow.js +129 -0
- package/lib/view/PopoutWindow.js.map +1 -0
- package/lib/view/PopupMenu.js +71 -0
- package/lib/view/PopupMenu.js.map +1 -0
- package/lib/view/Row.js +45 -0
- package/lib/view/Row.js.map +1 -0
- package/lib/view/SizeTracker.js +11 -0
- package/lib/view/SizeTracker.js.map +1 -0
- package/lib/view/Splitter.js +191 -147
- package/lib/view/Splitter.js.map +1 -1
- package/lib/view/Tab.js +86 -60
- package/lib/view/Tab.js.map +1 -1
- package/lib/view/TabButton.js +127 -135
- package/lib/view/TabButton.js.map +1 -1
- package/lib/view/TabButtonStamp.js +16 -21
- package/lib/view/TabButtonStamp.js.map +1 -1
- package/lib/view/TabOverflowHook.js +150 -149
- package/lib/view/TabOverflowHook.js.map +1 -1
- package/lib/view/TabSet.js +272 -234
- package/lib/view/TabSet.js.map +1 -1
- package/lib/view/Utils.js +126 -68
- package/lib/view/Utils.js.map +1 -1
- package/package.json +36 -30
- package/src/Attribute.ts +23 -0
- package/src/AttributeDefinitions.ts +38 -15
- package/src/DockLocation.ts +13 -13
- package/src/I18nLabel.ts +7 -9
- package/src/Rect.ts +53 -1
- package/src/Types.ts +16 -0
- package/src/index.ts +1 -2
- package/src/model/Actions.ts +49 -29
- package/src/model/BorderNode.ts +208 -214
- package/src/model/BorderSet.ts +42 -91
- package/src/model/IJsonModel.ts +883 -103
- package/src/model/LayoutWindow.ts +121 -0
- package/src/model/Model.ts +488 -366
- package/src/model/Node.ts +98 -111
- package/src/model/RowNode.ts +323 -319
- package/src/model/TabNode.ts +294 -110
- package/src/model/TabSetNode.ts +303 -242
- package/src/model/Utils.ts +6 -32
- package/src/view/BorderButton.tsx +36 -52
- package/src/view/BorderTab.tsx +70 -0
- package/src/view/BorderTabSet.tsx +64 -52
- package/src/view/DragContainer.tsx +32 -0
- package/src/view/Icons.tsx +6 -0
- package/src/view/Layout.tsx +1053 -1046
- package/src/view/Overlay.tsx +22 -0
- package/src/view/PopoutWindow.tsx +152 -0
- package/src/{PopupMenu.tsx → view/PopupMenu.tsx} +36 -31
- package/src/view/Row.tsx +68 -0
- package/src/view/SizeTracker.tsx +20 -0
- package/src/view/Splitter.tsx +167 -112
- package/src/view/Tab.tsx +76 -42
- package/src/view/TabButton.tsx +39 -54
- package/src/view/TabButtonStamp.tsx +5 -9
- package/src/view/TabOverflowHook.tsx +14 -9
- package/src/view/TabSet.tsx +221 -176
- package/src/view/Utils.tsx +119 -39
- package/style/_base.scss +140 -34
- package/style/dark.css +140 -35
- package/style/dark.css.map +1 -1
- package/style/dark.scss +3 -1
- package/style/gray.css +139 -34
- package/style/gray.css.map +1 -1
- package/style/gray.scss +2 -0
- package/style/light.css +141 -36
- package/style/light.css.map +1 -1
- package/style/light.scss +4 -2
- package/style/rounded.css +697 -0
- package/style/rounded.css.map +1 -0
- package/style/rounded.scss +194 -0
- package/style/underline.css +139 -34
- package/style/underline.css.map +1 -1
- package/style/underline.scss +2 -0
- package/cypress.config.ts +0 -16
- package/lib/DragDrop.js +0 -316
- package/lib/DragDrop.js.map +0 -1
- package/lib/PopupMenu.js +0 -68
- package/lib/PopupMenu.js.map +0 -1
- package/lib/model/SplitterNode.js +0 -72
- package/lib/model/SplitterNode.js.map +0 -1
- package/lib/view/FloatingWindow.js +0 -123
- package/lib/view/FloatingWindow.js.map +0 -1
- package/lib/view/FloatingWindowTab.js +0 -19
- package/lib/view/FloatingWindowTab.js.map +0 -1
- package/lib/view/TabFloating.js +0 -66
- package/lib/view/TabFloating.js.map +0 -1
- package/src/DragDrop.ts +0 -392
- package/src/model/SplitterNode.ts +0 -78
- package/src/view/FloatingWindow.tsx +0 -140
- package/src/view/FloatingWindowTab.tsx +0 -29
- package/src/view/TabFloating.tsx +0 -101
package/src/view/Layout.tsx
CHANGED
|
@@ -1,1243 +1,912 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
2
|
import { createPortal } from "react-dom";
|
|
3
|
+
import { createRoot } from "react-dom/client";
|
|
3
4
|
import { DockLocation } from "../DockLocation";
|
|
4
|
-
import { DragDrop } from "../DragDrop";
|
|
5
5
|
import { DropInfo } from "../DropInfo";
|
|
6
6
|
import { I18nLabel } from "../I18nLabel";
|
|
7
|
+
import { Orientation } from "../Orientation";
|
|
8
|
+
import { Rect } from "../Rect";
|
|
9
|
+
import { CLASSES } from "../Types";
|
|
7
10
|
import { Action } from "../model/Action";
|
|
8
11
|
import { Actions } from "../model/Actions";
|
|
9
12
|
import { BorderNode } from "../model/BorderNode";
|
|
10
|
-
import { BorderSet } from "../model/BorderSet";
|
|
11
13
|
import { IDraggable } from "../model/IDraggable";
|
|
12
|
-
import {
|
|
14
|
+
import { IJsonTabNode } from "../model/IJsonModel";
|
|
15
|
+
import { Model } from "../model/Model";
|
|
13
16
|
import { Node } from "../model/Node";
|
|
14
|
-
import { RowNode } from "../model/RowNode";
|
|
15
|
-
import { SplitterNode } from "../model/SplitterNode";
|
|
16
17
|
import { TabNode } from "../model/TabNode";
|
|
17
18
|
import { TabSetNode } from "../model/TabSetNode";
|
|
18
|
-
import {
|
|
19
|
-
import { CLASSES } from "../Types";
|
|
19
|
+
import { BorderTab } from "./BorderTab";
|
|
20
20
|
import { BorderTabSet } from "./BorderTabSet";
|
|
21
|
-
import {
|
|
21
|
+
import { DragContainer } from "./DragContainer";
|
|
22
|
+
import { ErrorBoundary } from "./ErrorBoundary";
|
|
23
|
+
import { PopoutWindow } from "./PopoutWindow";
|
|
24
|
+
import { AsterickIcon, CloseIcon, EdgeIcon, MaximizeIcon, OverflowIcon, PopoutIcon, RestoreIcon } from "./Icons";
|
|
25
|
+
import { Overlay } from "./Overlay";
|
|
26
|
+
import { Row } from "./Row";
|
|
22
27
|
import { Tab } from "./Tab";
|
|
23
|
-
import {
|
|
24
|
-
import {
|
|
25
|
-
import { FloatingWindowTab } from "./FloatingWindowTab";
|
|
26
|
-
import { TabFloating } from "./TabFloating";
|
|
27
|
-
import { IJsonTabNode } from "../model/IJsonModel";
|
|
28
|
-
import { Orientation } from "../Orientation";
|
|
29
|
-
import { CloseIcon, EdgeIcon, MaximizeIcon, OverflowIcon, PopoutIcon, RestoreIcon } from "./Icons";
|
|
28
|
+
import { copyInlineStyles, enablePointerOnIFrames, isDesktop, isSafari } from "./Utils";
|
|
29
|
+
import { LayoutWindow } from "../model/LayoutWindow";
|
|
30
30
|
import { TabButtonStamp } from "./TabButtonStamp";
|
|
31
|
-
|
|
32
|
-
export type CustomDragCallback = (dragging: TabNode | IJsonTabNode, over: TabNode, x: number, y: number, location: DockLocation) => void;
|
|
33
|
-
export type DragRectRenderCallback = (content: React.ReactElement | undefined, node?: Node, json?: IJsonTabNode) => React.ReactElement | undefined;
|
|
34
|
-
export type FloatingTabPlaceholderRenderCallback = (dockPopout: () => void, showPopout: () => void) => React.ReactElement | undefined;
|
|
35
|
-
export type NodeMouseEvent = (node: TabNode | TabSetNode | BorderNode, event: React.MouseEvent<HTMLElement, MouseEvent>) => void;
|
|
36
|
-
export type ShowOverflowMenuCallback = (
|
|
37
|
-
node: TabSetNode | BorderNode,
|
|
38
|
-
mouseEvent: React.MouseEvent<HTMLElement, MouseEvent>,
|
|
39
|
-
items: { index: number; node: TabNode }[],
|
|
40
|
-
onSelect: (item: { index: number; node: TabNode }) => void,
|
|
41
|
-
) => void;
|
|
42
|
-
export type TabSetPlaceHolderCallback = (node: TabSetNode) => React.ReactNode;
|
|
43
|
-
export type IconFactory = (node: TabNode) => React.ReactNode;
|
|
44
|
-
export type TitleFactory = (node: TabNode) => ITitleObject | React.ReactNode;
|
|
31
|
+
import { SizeTracker } from "./SizeTracker";
|
|
45
32
|
|
|
46
33
|
export interface ILayoutProps {
|
|
34
|
+
/** the model for this layout */
|
|
47
35
|
model: Model;
|
|
36
|
+
/** factory function for creating the tab components */
|
|
48
37
|
factory: (node: TabNode) => React.ReactNode;
|
|
49
|
-
|
|
50
|
-
fontFamily?: string;
|
|
51
|
-
iconFactory?: IconFactory;
|
|
52
|
-
titleFactory?: TitleFactory;
|
|
38
|
+
/** object mapping keys among close, maximize, restore, more, popout to React nodes to use in place of the default icons, can alternatively return functions for creating the React nodes */
|
|
53
39
|
icons?: IIcons;
|
|
40
|
+
/** function called whenever the layout generates an action to update the model (allows for intercepting actions before they are dispatched to the model, for example, asking the user to confirm a tab close.) Returning undefined from the function will halt the action, otherwise return the action to continue */
|
|
54
41
|
onAction?: (action: Action) => Action | undefined;
|
|
42
|
+
/** function called when rendering a tab, allows leading (icon), content section, buttons and name used in overflow menu to be customized */
|
|
55
43
|
onRenderTab?: (
|
|
56
44
|
node: TabNode,
|
|
57
45
|
renderValues: ITabRenderValues, // change the values in this object as required
|
|
58
46
|
) => void;
|
|
47
|
+
/** function called when rendering a tabset, allows header and buttons to be customized */
|
|
59
48
|
onRenderTabSet?: (
|
|
60
49
|
tabSetNode: TabSetNode | BorderNode,
|
|
61
50
|
renderValues: ITabSetRenderValues, // change the values in this object as required
|
|
62
51
|
) => void;
|
|
52
|
+
/** function called when model has changed */
|
|
63
53
|
onModelChange?: (model: Model, action: Action) => void;
|
|
64
|
-
|
|
65
|
-
|
|
54
|
+
/** function called when an external object (not a tab) gets dragged onto the layout, with a single dragenter argument. Should return either undefined to reject the drag/drop or an object with keys dragText, jsonDrop, to create a tab via drag (similar to a call to addTabToTabSet). Function onDropis passed the added tabNodeand thedrop DragEvent`, unless the drag was canceled. */
|
|
55
|
+
onExternalDrag?: (event: React.DragEvent<HTMLElement>) => undefined | {
|
|
66
56
|
json: any,
|
|
67
|
-
onDrop?: (node?: Node, event?:
|
|
57
|
+
onDrop?: (node?: Node, event?: React.DragEvent<HTMLElement>) => void
|
|
68
58
|
};
|
|
59
|
+
/** function called with default css class name, return value is class name that will be used. Mainly for use with css modules. */
|
|
69
60
|
classNameMapper?: (defaultClassName: string) => string;
|
|
61
|
+
/** function called for each I18nLabel to allow user translation, currently used for tab and tabset move messages, return undefined to use default values */
|
|
70
62
|
i18nMapper?: (id: I18nLabel, param?: string) => string | undefined;
|
|
63
|
+
/** if left undefined will do simple check based on userAgent */
|
|
71
64
|
supportsPopout?: boolean | undefined;
|
|
65
|
+
/** URL of popout window relative to origin, defaults to popout.html */
|
|
72
66
|
popoutURL?: string | undefined;
|
|
67
|
+
/** boolean value, defaults to false, resize tabs as splitters are dragged. Warning: this can cause resizing to become choppy when tabs are slow to draw */
|
|
73
68
|
realtimeResize?: boolean | undefined;
|
|
74
|
-
|
|
75
|
-
x: number,
|
|
76
|
-
y: number,
|
|
77
|
-
width: number,
|
|
78
|
-
height: number,
|
|
79
|
-
callback: CustomDragCallback,
|
|
80
|
-
// Called once when `callback` is not going to be called anymore (user canceled the drag, moved mouse and you returned a different callback, etc)
|
|
81
|
-
invalidated?: () => void,
|
|
82
|
-
cursor?: string | undefined
|
|
83
|
-
};
|
|
69
|
+
/** callback for rendering the drag rectangles */
|
|
84
70
|
onRenderDragRect?: DragRectRenderCallback;
|
|
85
|
-
|
|
71
|
+
/** callback for handling context actions on tabs and tabsets */
|
|
86
72
|
onContextMenu?: NodeMouseEvent;
|
|
73
|
+
/** callback for handling mouse clicks on tabs and tabsets with alt, meta, shift keys, also handles center mouse clicks */
|
|
87
74
|
onAuxMouseClick?: NodeMouseEvent;
|
|
75
|
+
/** callback for handling the display of the tab overflow menu */
|
|
88
76
|
onShowOverflowMenu?: ShowOverflowMenuCallback;
|
|
77
|
+
/** callback for rendering a placeholder when a tabset is empty */
|
|
89
78
|
onTabSetPlaceHolder?: TabSetPlaceHolderCallback;
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
size?: string;
|
|
93
|
-
family?: string;
|
|
94
|
-
style?: string;
|
|
95
|
-
weight?: string;
|
|
79
|
+
/** Name given to popout windows, defaults to 'Popout Window' */
|
|
80
|
+
popoutWindowName?: string;
|
|
96
81
|
}
|
|
97
82
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
overflowPosition: number | undefined;
|
|
107
|
-
}
|
|
83
|
+
/**
|
|
84
|
+
* A React component that hosts a multi-tabbed layout
|
|
85
|
+
*/
|
|
86
|
+
export class Layout extends React.Component<ILayoutProps> {
|
|
87
|
+
/** @internal */
|
|
88
|
+
private selfRef: React.RefObject<LayoutInternal>;
|
|
89
|
+
/** @internal */
|
|
90
|
+
private revision: number; // so LayoutInternal knows this is a parent render (used for optimization)
|
|
108
91
|
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
}
|
|
92
|
+
/** @internal */
|
|
93
|
+
constructor(props: ILayoutProps) {
|
|
94
|
+
super(props);
|
|
95
|
+
this.selfRef = React.createRef<LayoutInternal>();
|
|
96
|
+
this.revision = 0;
|
|
97
|
+
}
|
|
115
98
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
}
|
|
99
|
+
/** re-render the layout */
|
|
100
|
+
redraw() {
|
|
101
|
+
this.selfRef.current!.redraw("parent " + this.revision);
|
|
102
|
+
}
|
|
120
103
|
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
}
|
|
104
|
+
/**
|
|
105
|
+
* Adds a new tab to the given tabset
|
|
106
|
+
* @param tabsetId the id of the tabset where the new tab will be added
|
|
107
|
+
* @param json the json for the new tab node
|
|
108
|
+
* @returns the added tab node or undefined
|
|
109
|
+
*/
|
|
110
|
+
addTabToTabSet(tabsetId: string, json: IJsonTabNode): TabNode | undefined {
|
|
111
|
+
return this.selfRef.current!.addTabToTabSet(tabsetId, json);
|
|
112
|
+
}
|
|
131
113
|
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
114
|
+
/**
|
|
115
|
+
* Adds a new tab by dragging an item to the drop location, must be called from within an HTML
|
|
116
|
+
* drag start handler. You can use the setDragComponent() method to set the drag image before calling this
|
|
117
|
+
* method.
|
|
118
|
+
* @param event the drag start event
|
|
119
|
+
* @param json the json for the new tab node
|
|
120
|
+
* @param onDrop a callback to call when the drag is complete
|
|
121
|
+
*/
|
|
122
|
+
addTabWithDragAndDrop(event: DragEvent, json: IJsonTabNode, onDrop?: (node?: Node, event?: React.DragEvent<HTMLElement>) => void) {
|
|
123
|
+
this.selfRef.current!.addTabWithDragAndDrop(event, json, onDrop);
|
|
124
|
+
}
|
|
141
125
|
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
}
|
|
126
|
+
/**
|
|
127
|
+
* Move a tab/tabset using drag and drop, must be called from within an HTML
|
|
128
|
+
* drag start handler
|
|
129
|
+
* @param event the drag start event
|
|
130
|
+
* @param node the tab or tabset to drag
|
|
131
|
+
*/
|
|
132
|
+
moveTabWithDragAndDrop(event: DragEvent, node: (TabNode | TabSetNode)) {
|
|
133
|
+
this.selfRef.current!.moveTabWithDragAndDrop(event, node);
|
|
134
|
+
}
|
|
151
135
|
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
136
|
+
/**
|
|
137
|
+
* Adds a new tab to the active tabset (if there is one)
|
|
138
|
+
* @param json the json for the new tab node
|
|
139
|
+
* @returns the added tab node or undefined
|
|
140
|
+
*/
|
|
141
|
+
addTabToActiveTabSet(json: IJsonTabNode): TabNode | undefined {
|
|
142
|
+
return this.selfRef.current!.addTabToActiveTabSet(json);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Sets the drag image from a react component for a drag event
|
|
147
|
+
* @param event the drag event
|
|
148
|
+
* @param component the react component to be used for the drag image
|
|
149
|
+
* @param x the x position of the drag cursor on the image
|
|
150
|
+
* @param y the x position of the drag cursor on the image
|
|
151
|
+
*/
|
|
152
|
+
setDragComponent(event: DragEvent, component: React.ReactNode, x: number, y: number) {
|
|
153
|
+
this.selfRef.current!.setDragComponent(event, component, x, y);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/** Get the root div element of the layout */
|
|
157
|
+
getRootDiv() {
|
|
158
|
+
return this.selfRef.current!.getRootDiv();
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/** @internal */
|
|
162
|
+
render() {
|
|
163
|
+
return (<LayoutInternal ref={this.selfRef} {...this.props} renderRevision={this.revision++} />)
|
|
164
|
+
}
|
|
162
165
|
}
|
|
163
166
|
|
|
164
167
|
/** @internal */
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
getCurrentDocument(): Document | undefined;
|
|
172
|
-
getClassName(defaultClassName: string): string;
|
|
173
|
-
doAction(action: Action): Node | undefined;
|
|
174
|
-
getDomRect(): DOMRect | undefined;
|
|
175
|
-
getRootDiv(): HTMLDivElement | null;
|
|
176
|
-
dragStart(
|
|
177
|
-
event: Event | React.MouseEvent<HTMLDivElement, MouseEvent> | React.TouchEvent<HTMLDivElement> | React.DragEvent<HTMLDivElement> | undefined,
|
|
178
|
-
dragDivText: string | undefined,
|
|
179
|
-
node: Node & IDraggable,
|
|
180
|
-
allowDrag: boolean,
|
|
181
|
-
onClick?: (event: Event) => void,
|
|
182
|
-
onDoubleClick?: (event: Event) => void
|
|
183
|
-
): void;
|
|
184
|
-
customizeTab(
|
|
185
|
-
tabNode: TabNode,
|
|
186
|
-
renderValues: ITabRenderValues,
|
|
187
|
-
): void;
|
|
188
|
-
customizeTabSet(
|
|
189
|
-
tabSetNode: TabSetNode | BorderNode,
|
|
190
|
-
renderValues: ITabSetRenderValues,
|
|
191
|
-
): void;
|
|
192
|
-
styleFont: (style: Record<string, string>) => Record<string, string>;
|
|
193
|
-
setEditingTab(tabNode?: TabNode): void;
|
|
194
|
-
getEditingTab(): TabNode | undefined;
|
|
195
|
-
getOnRenderFloatingTabPlaceholder(): FloatingTabPlaceholderRenderCallback | undefined;
|
|
196
|
-
showContextMenu(node: TabNode | TabSetNode | BorderNode, event: React.MouseEvent<HTMLElement, MouseEvent>): void;
|
|
197
|
-
auxMouseClick(node: TabNode | TabSetNode | BorderNode, event: React.MouseEvent<HTMLElement, MouseEvent>): void;
|
|
198
|
-
showPortal: (portal: React.ReactNode, portalDiv: HTMLDivElement) => void;
|
|
199
|
-
hidePortal: () => void;
|
|
200
|
-
getShowOverflowMenu(): ShowOverflowMenuCallback | undefined;
|
|
201
|
-
getTabSetPlaceHolderCallback(): TabSetPlaceHolderCallback | undefined;
|
|
168
|
+
interface ILayoutInternalProps extends ILayoutProps {
|
|
169
|
+
renderRevision: number;
|
|
170
|
+
|
|
171
|
+
// used only for popout windows:
|
|
172
|
+
windowId?: string;
|
|
173
|
+
mainLayout?: LayoutInternal;
|
|
202
174
|
}
|
|
203
175
|
|
|
204
|
-
// Popout windows work in latest browsers based on webkit (Chrome, Opera, Safari, latest Edge) and Firefox. They do
|
|
205
|
-
// not work on any version if IE or the original Edge browser
|
|
206
|
-
// Assume any recent desktop browser not IE or original Edge will work
|
|
207
176
|
/** @internal */
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
177
|
+
interface ILayoutInternalState {
|
|
178
|
+
rect: Rect;
|
|
179
|
+
editingTab?: TabNode;
|
|
180
|
+
portal?: React.ReactPortal;
|
|
181
|
+
showEdges: boolean;
|
|
182
|
+
showOverlay: boolean;
|
|
183
|
+
calculatedBorderBarSize: number;
|
|
184
|
+
layoutRevision: number;
|
|
185
|
+
forceRevision: number;
|
|
186
|
+
showHiddenBorder: DockLocation;
|
|
187
|
+
}
|
|
214
188
|
|
|
215
|
-
/**
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
export class Layout extends React.Component<ILayoutProps, ILayoutState> {
|
|
189
|
+
/** @internal */
|
|
190
|
+
export class LayoutInternal extends React.Component<ILayoutInternalProps, ILayoutInternalState> {
|
|
191
|
+
public static dragState: DragState | undefined = undefined;
|
|
219
192
|
|
|
220
|
-
/** @internal */
|
|
221
193
|
private selfRef: React.RefObject<HTMLDivElement>;
|
|
222
|
-
|
|
223
|
-
private findHeaderBarSizeRef: React.RefObject<HTMLDivElement>;
|
|
224
|
-
/** @internal */
|
|
225
|
-
private findTabBarSizeRef: React.RefObject<HTMLDivElement>;
|
|
226
|
-
/** @internal */
|
|
194
|
+
private moveablesRef: React.RefObject<HTMLDivElement>;
|
|
227
195
|
private findBorderBarSizeRef: React.RefObject<HTMLDivElement>;
|
|
228
|
-
|
|
196
|
+
private mainRef: React.RefObject<HTMLDivElement>;
|
|
229
197
|
private previousModel?: Model;
|
|
230
|
-
|
|
231
|
-
private
|
|
232
|
-
|
|
233
|
-
/** @internal */
|
|
234
|
-
// private start: number = 0;
|
|
235
|
-
/** @internal */
|
|
236
|
-
// private layoutTime: number = 0;
|
|
237
|
-
|
|
238
|
-
/** @internal */
|
|
239
|
-
private tabIds: string[];
|
|
240
|
-
/** @internal */
|
|
241
|
-
private newTabJson: IJsonTabNode | undefined;
|
|
242
|
-
/** @internal */
|
|
243
|
-
private firstMove: boolean = false;
|
|
244
|
-
/** @internal */
|
|
245
|
-
private dragNode?: Node & IDraggable;
|
|
246
|
-
/** @internal */
|
|
247
|
-
private dragDiv?: HTMLDivElement;
|
|
248
|
-
/** @internal */
|
|
249
|
-
private dragRectRendered: boolean = true;
|
|
250
|
-
/** @internal */
|
|
251
|
-
private dragDivText: string | undefined = undefined;
|
|
252
|
-
/** @internal */
|
|
198
|
+
private orderedIds: string[];
|
|
199
|
+
private moveableElementMap = new Map<string, HTMLElement>();
|
|
253
200
|
private dropInfo: DropInfo | undefined;
|
|
254
|
-
|
|
255
|
-
private customDrop: ICustomDropDestination | undefined;
|
|
256
|
-
/** @internal */
|
|
257
|
-
private outlineDiv?: HTMLDivElement;
|
|
258
|
-
/** @internal */
|
|
259
|
-
private edgeRectLength = 100;
|
|
260
|
-
/** @internal */
|
|
261
|
-
private edgeRectWidth = 10;
|
|
262
|
-
/** @internal */
|
|
263
|
-
private fnNewNodeDropped?: (node?: Node, event?: Event) => void;
|
|
264
|
-
/** @internal */
|
|
201
|
+
private outlineDiv?: HTMLElement;
|
|
265
202
|
private currentDocument?: Document;
|
|
266
|
-
/** @internal */
|
|
267
203
|
private currentWindow?: Window;
|
|
268
|
-
/** @internal */
|
|
269
204
|
private supportsPopout: boolean;
|
|
270
|
-
/** @internal */
|
|
271
205
|
private popoutURL: string;
|
|
272
|
-
/** @internal */
|
|
273
206
|
private icons: IIcons;
|
|
274
|
-
/** @internal */
|
|
275
207
|
private resizeObserver?: ResizeObserver;
|
|
276
208
|
|
|
277
|
-
|
|
209
|
+
private dragEnterCount: number = 0;
|
|
210
|
+
private dragging: boolean = false;
|
|
211
|
+
private windowId: string;
|
|
212
|
+
private layoutWindow: LayoutWindow;
|
|
213
|
+
private mainLayout: LayoutInternal;
|
|
214
|
+
private isMainWindow: boolean;
|
|
215
|
+
private isDraggingOverWindow: boolean;
|
|
216
|
+
private styleObserver: MutationObserver | undefined;
|
|
217
|
+
private popoutWindowName: string;
|
|
218
|
+
// private renderCount: any;
|
|
219
|
+
|
|
220
|
+
constructor(props: ILayoutInternalProps) {
|
|
278
221
|
super(props);
|
|
279
|
-
|
|
280
|
-
this.
|
|
222
|
+
|
|
223
|
+
this.orderedIds = [];
|
|
281
224
|
this.selfRef = React.createRef<HTMLDivElement>();
|
|
282
|
-
this.
|
|
283
|
-
this.
|
|
225
|
+
this.moveablesRef = React.createRef<HTMLDivElement>();
|
|
226
|
+
this.mainRef = React.createRef<HTMLDivElement>();
|
|
284
227
|
this.findBorderBarSizeRef = React.createRef<HTMLDivElement>();
|
|
228
|
+
|
|
285
229
|
this.supportsPopout = props.supportsPopout !== undefined ? props.supportsPopout : defaultSupportsPopout;
|
|
286
230
|
this.popoutURL = props.popoutURL ? props.popoutURL : "popout.html";
|
|
287
231
|
this.icons = { ...defaultIcons, ...props.icons };
|
|
232
|
+
this.windowId = props.windowId ? props.windowId : Model.MAIN_WINDOW_ID;
|
|
233
|
+
this.mainLayout = this.props.mainLayout ? this.props.mainLayout : this;
|
|
234
|
+
this.isDraggingOverWindow = false;
|
|
235
|
+
this.layoutWindow = this.props.model.getwindowsMap().get(this.windowId)!;
|
|
236
|
+
this.layoutWindow.layout = this;
|
|
237
|
+
this.popoutWindowName = this.props.popoutWindowName || "Popout Window";
|
|
238
|
+
// this.renderCount = 0;
|
|
288
239
|
|
|
289
240
|
this.state = {
|
|
290
|
-
rect:
|
|
291
|
-
calculatedHeaderBarSize: 25,
|
|
292
|
-
calculatedTabBarSize: 26,
|
|
293
|
-
calculatedBorderBarSize: 30,
|
|
241
|
+
rect: Rect.empty(),
|
|
294
242
|
editingTab: undefined,
|
|
295
|
-
showHiddenBorder: DockLocation.CENTER,
|
|
296
243
|
showEdges: false,
|
|
244
|
+
showOverlay: false,
|
|
245
|
+
calculatedBorderBarSize: 29,
|
|
246
|
+
layoutRevision: 0,
|
|
247
|
+
forceRevision: 0,
|
|
248
|
+
showHiddenBorder: DockLocation.CENTER
|
|
297
249
|
};
|
|
298
250
|
|
|
299
|
-
this.
|
|
300
|
-
}
|
|
301
|
-
|
|
302
|
-
/** @internal */
|
|
303
|
-
styleFont(style: Record<string, string>): Record<string, string> {
|
|
304
|
-
if (this.props.font) {
|
|
305
|
-
if (this.selfRef.current) {
|
|
306
|
-
if (this.props.font.size) {
|
|
307
|
-
this.selfRef.current.style.setProperty("--font-size", this.props.font.size);
|
|
308
|
-
}
|
|
309
|
-
if (this.props.font.family) {
|
|
310
|
-
this.selfRef.current.style.setProperty("--font-family", this.props.font.family);
|
|
311
|
-
}
|
|
312
|
-
}
|
|
313
|
-
if (this.props.font.style) {
|
|
314
|
-
style.fontStyle = this.props.font.style;
|
|
315
|
-
}
|
|
316
|
-
if (this.props.font.weight) {
|
|
317
|
-
style.fontWeight = this.props.font.weight;
|
|
318
|
-
}
|
|
319
|
-
}
|
|
320
|
-
return style;
|
|
321
|
-
}
|
|
322
|
-
|
|
323
|
-
/** @internal */
|
|
324
|
-
onModelChange = (action: Action) => {
|
|
325
|
-
this.forceUpdate();
|
|
326
|
-
if (this.props.onModelChange) {
|
|
327
|
-
this.props.onModelChange(this.props.model, action);
|
|
328
|
-
}
|
|
329
|
-
};
|
|
330
|
-
|
|
331
|
-
/** @internal */
|
|
332
|
-
doAction(action: Action): Node | undefined {
|
|
333
|
-
if (this.props.onAction !== undefined) {
|
|
334
|
-
const outcome = this.props.onAction(action);
|
|
335
|
-
if (outcome !== undefined) {
|
|
336
|
-
return this.props.model.doAction(outcome);
|
|
337
|
-
}
|
|
338
|
-
return undefined;
|
|
339
|
-
} else {
|
|
340
|
-
return this.props.model.doAction(action);
|
|
341
|
-
}
|
|
251
|
+
this.isMainWindow = this.windowId === Model.MAIN_WINDOW_ID;
|
|
342
252
|
}
|
|
343
253
|
|
|
344
|
-
/** @internal */
|
|
345
254
|
componentDidMount() {
|
|
346
255
|
this.updateRect();
|
|
347
|
-
this.updateLayoutMetrics();
|
|
348
256
|
|
|
349
|
-
|
|
350
|
-
this.currentDocument = (this.selfRef.current as HTMLDivElement).ownerDocument;
|
|
257
|
+
this.currentDocument = (this.selfRef.current as HTMLElement).ownerDocument;
|
|
351
258
|
this.currentWindow = this.currentDocument.defaultView!;
|
|
259
|
+
|
|
260
|
+
this.layoutWindow.window = this.currentWindow;
|
|
261
|
+
this.layoutWindow.toScreenRectFunction = (r) => this.getScreenRect(r);
|
|
262
|
+
|
|
352
263
|
this.resizeObserver = new ResizeObserver(entries => {
|
|
353
|
-
|
|
264
|
+
requestAnimationFrame(() => {
|
|
265
|
+
this.updateRect();
|
|
266
|
+
});
|
|
354
267
|
});
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
this.resizeObserver.observe(selfRefCurr);
|
|
268
|
+
if (this.selfRef.current) {
|
|
269
|
+
this.resizeObserver.observe(this.selfRef.current);
|
|
358
270
|
}
|
|
271
|
+
|
|
272
|
+
if (this.isMainWindow) {
|
|
273
|
+
this.props.model.addChangeListener(this.onModelChange);
|
|
274
|
+
this.updateLayoutMetrics();
|
|
275
|
+
} else {
|
|
276
|
+
// since resizeObserver doesn't always work as expected when observing element in another document
|
|
277
|
+
this.currentWindow.addEventListener("resize", () => {
|
|
278
|
+
this.updateRect();
|
|
279
|
+
});
|
|
280
|
+
|
|
281
|
+
const sourceElement = this.props.mainLayout!.getRootDiv()!;
|
|
282
|
+
const targetElement = this.selfRef.current!;
|
|
283
|
+
|
|
284
|
+
copyInlineStyles(sourceElement, targetElement);
|
|
285
|
+
|
|
286
|
+
this.styleObserver = new MutationObserver(() => {
|
|
287
|
+
const changed = copyInlineStyles(sourceElement, targetElement);
|
|
288
|
+
if (changed) {
|
|
289
|
+
this.redraw("mutation observer");
|
|
290
|
+
}
|
|
291
|
+
});
|
|
292
|
+
|
|
293
|
+
// Observe changes to the source element's style attribute
|
|
294
|
+
this.styleObserver.observe(sourceElement, { attributeFilter: ['style'] });
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
// allow tabs to overlay when hidden
|
|
298
|
+
document.addEventListener('visibilitychange', () => {
|
|
299
|
+
for (const [_, layoutWindow] of this.props.model.getwindowsMap()) {
|
|
300
|
+
const layout = layoutWindow.layout;
|
|
301
|
+
if (layout) {
|
|
302
|
+
this.redraw("visibility change");
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
});
|
|
359
306
|
}
|
|
360
307
|
|
|
361
|
-
/** @internal */
|
|
362
308
|
componentDidUpdate() {
|
|
363
|
-
this.
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
309
|
+
this.currentDocument = (this.selfRef.current as HTMLElement).ownerDocument;
|
|
310
|
+
this.currentWindow = this.currentDocument.defaultView!;
|
|
311
|
+
if (this.isMainWindow) {
|
|
312
|
+
if (this.props.model !== this.previousModel) {
|
|
313
|
+
if (this.previousModel !== undefined) {
|
|
314
|
+
this.previousModel.removeChangeListener(this.onModelChange); // stop listening to old model
|
|
315
|
+
}
|
|
316
|
+
this.props.model.getwindowsMap().get(this.windowId)!.layout = this;
|
|
317
|
+
this.props.model.addChangeListener(this.onModelChange);
|
|
318
|
+
this.layoutWindow = this.props.model.getwindowsMap().get(this.windowId)!;
|
|
319
|
+
this.layoutWindow.layout = this;
|
|
320
|
+
this.layoutWindow.toScreenRectFunction = (r) => this.getScreenRect(r);
|
|
321
|
+
this.previousModel = this.props.model;
|
|
322
|
+
this.tidyMoveablesMap();
|
|
367
323
|
}
|
|
368
|
-
|
|
369
|
-
this.
|
|
324
|
+
|
|
325
|
+
this.updateLayoutMetrics();
|
|
370
326
|
}
|
|
371
|
-
// console.log("Layout time: " + this.layoutTime + "ms Render time: " + (Date.now() - this.start) + "ms");
|
|
372
327
|
}
|
|
373
328
|
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
domRect = this.getDomRect();
|
|
378
|
-
}
|
|
379
|
-
if (!domRect) {
|
|
380
|
-
// no dom rect available, return.
|
|
381
|
-
return;
|
|
329
|
+
componentWillUnmount() {
|
|
330
|
+
if (this.selfRef.current) {
|
|
331
|
+
this.resizeObserver?.unobserve(this.selfRef.current);
|
|
382
332
|
}
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
333
|
+
this.styleObserver?.disconnect();
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
render() {
|
|
337
|
+
// console.log("render", this.windowId, this.state.revision, this.renderCount++);
|
|
338
|
+
// first render will be used to find the size (via selfRef)
|
|
339
|
+
if (!this.selfRef.current) {
|
|
340
|
+
return (
|
|
341
|
+
<div ref={this.selfRef} className={this.getClassName(CLASSES.FLEXLAYOUT__LAYOUT)}>
|
|
342
|
+
<div ref={this.moveablesRef} key="__moveables__" className={this.getClassName(CLASSES.FLEXLAYOUT__LAYOUT_MOVEABLES)}></div>
|
|
343
|
+
{this.renderMetricsElements()}
|
|
344
|
+
</div>
|
|
345
|
+
);
|
|
386
346
|
}
|
|
387
|
-
};
|
|
388
347
|
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
348
|
+
const model = this.props.model;
|
|
349
|
+
model.getRoot(this.windowId).calcMinMaxSize();
|
|
350
|
+
model.getRoot(this.windowId).setPaths("");
|
|
351
|
+
model.getBorderSet().setPaths();
|
|
352
|
+
|
|
353
|
+
const inner = this.renderLayout();
|
|
354
|
+
const outer = this.renderBorders(inner);
|
|
355
|
+
|
|
356
|
+
const tabs = this.renderTabs();
|
|
357
|
+
const reorderedTabs = this.reorderComponents(tabs, this.orderedIds);
|
|
358
|
+
|
|
359
|
+
let floatingWindows = null;
|
|
360
|
+
let tabMoveables = null;
|
|
361
|
+
let tabStamps = null;
|
|
362
|
+
let metricElements = null;
|
|
363
|
+
|
|
364
|
+
if (this.isMainWindow) {
|
|
365
|
+
floatingWindows = this.renderWindows();
|
|
366
|
+
metricElements = this.renderMetricsElements();
|
|
367
|
+
tabMoveables = this.renderTabMoveables();
|
|
368
|
+
tabStamps = <div key="__tabStamps__" className={this.getClassName(CLASSES.FLEXLAYOUT__LAYOUT_TAB_STAMPS)}>
|
|
369
|
+
{this.renderTabStamps()}
|
|
370
|
+
</div>;
|
|
396
371
|
}
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
this.
|
|
372
|
+
|
|
373
|
+
return (
|
|
374
|
+
<div
|
|
375
|
+
ref={this.selfRef}
|
|
376
|
+
className={this.getClassName(CLASSES.FLEXLAYOUT__LAYOUT)}
|
|
377
|
+
onDragEnter={this.onDragEnterRaw}
|
|
378
|
+
onDragLeave={this.onDragLeaveRaw}
|
|
379
|
+
onDragOver={this.onDragOver}
|
|
380
|
+
onDrop={this.onDrop}
|
|
381
|
+
>
|
|
382
|
+
<div ref={this.moveablesRef} key="__moveables__" className={this.getClassName(CLASSES.FLEXLAYOUT__LAYOUT_MOVEABLES)}></div>
|
|
383
|
+
{metricElements}
|
|
384
|
+
<Overlay key="__overlay__" layout={this} show={this.state.showOverlay} />
|
|
385
|
+
{outer}
|
|
386
|
+
{reorderedTabs}
|
|
387
|
+
{tabMoveables}
|
|
388
|
+
{tabStamps}
|
|
389
|
+
{this.state.portal}
|
|
390
|
+
{floatingWindows}
|
|
391
|
+
</div>
|
|
392
|
+
);
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
renderBorders(
|
|
396
|
+
inner: React.ReactNode
|
|
397
|
+
) {
|
|
398
|
+
const classMain = this.getClassName(CLASSES.FLEXLAYOUT__LAYOUT_MAIN);
|
|
399
|
+
const borders = this.props.model.getBorderSet().getBorderMap()
|
|
400
|
+
if (this.isMainWindow && borders.size > 0) {
|
|
401
|
+
inner = (
|
|
402
|
+
<div className={classMain} ref={this.mainRef}>
|
|
403
|
+
{inner}
|
|
404
|
+
</div>);
|
|
405
|
+
const borderSetComponents = new Map<DockLocation, React.ReactNode>();
|
|
406
|
+
const borderSetContentComponents = new Map<DockLocation, React.ReactNode>();
|
|
407
|
+
for (const [_, location] of DockLocation.values) {
|
|
408
|
+
const border = borders.get(location);
|
|
409
|
+
const showBorder = border && (
|
|
410
|
+
!border.isAutoHide() ||
|
|
411
|
+
(border.isAutoHide() && (border.getChildren().length > 0 || this.state.showHiddenBorder === location)));
|
|
412
|
+
if (showBorder) {
|
|
413
|
+
borderSetComponents.set(location, <BorderTabSet layout={this} border={border} size={this.state.calculatedBorderBarSize} />);
|
|
414
|
+
borderSetContentComponents.set(location, <BorderTab layout={this} border={border} show={border.getSelected() !== -1} />);
|
|
415
|
+
}
|
|
401
416
|
}
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
const
|
|
405
|
-
|
|
406
|
-
|
|
417
|
+
|
|
418
|
+
const classBorderOuter = this.getClassName(CLASSES.FLEXLAYOUT__LAYOUT_BORDER_CONTAINER);
|
|
419
|
+
const classBorderInner = this.getClassName(CLASSES.FLEXLAYOUT__LAYOUT_BORDER_CONTAINER_INNER);
|
|
420
|
+
|
|
421
|
+
if (this.props.model.getBorderSet().getLayoutHorizontal()) {
|
|
422
|
+
const innerWithBorderTabs = (
|
|
423
|
+
<div className={classBorderInner} style={{ flexDirection: "column" }}>
|
|
424
|
+
{borderSetContentComponents.get(DockLocation.TOP)}
|
|
425
|
+
<div className={classBorderInner} style={{ flexDirection: "row" }}>
|
|
426
|
+
{borderSetContentComponents.get(DockLocation.LEFT)}
|
|
427
|
+
{inner}
|
|
428
|
+
{borderSetContentComponents.get(DockLocation.RIGHT)}
|
|
429
|
+
</div>
|
|
430
|
+
{borderSetContentComponents.get(DockLocation.BOTTOM)}
|
|
431
|
+
</div>
|
|
432
|
+
);
|
|
433
|
+
return (
|
|
434
|
+
<div className={classBorderOuter} style={{ flexDirection: "column" }}>
|
|
435
|
+
{borderSetComponents.get(DockLocation.TOP)}
|
|
436
|
+
<div className={classBorderInner} style={{ flexDirection: "row" }}>
|
|
437
|
+
{borderSetComponents.get(DockLocation.LEFT)}
|
|
438
|
+
{innerWithBorderTabs}
|
|
439
|
+
{borderSetComponents.get(DockLocation.RIGHT)}
|
|
440
|
+
</div>
|
|
441
|
+
{borderSetComponents.get(DockLocation.BOTTOM)}
|
|
442
|
+
</div>
|
|
443
|
+
);
|
|
444
|
+
} else {
|
|
445
|
+
const innerWithBorderTabs = (
|
|
446
|
+
<div className={classBorderInner} style={{ flexDirection: "row" }}>
|
|
447
|
+
{borderSetContentComponents.get(DockLocation.LEFT)}
|
|
448
|
+
<div className={classBorderInner} style={{ flexDirection: "column" }}>
|
|
449
|
+
{borderSetContentComponents.get(DockLocation.TOP)}
|
|
450
|
+
{inner}
|
|
451
|
+
{borderSetContentComponents.get(DockLocation.BOTTOM)}
|
|
452
|
+
</div>
|
|
453
|
+
{borderSetContentComponents.get(DockLocation.RIGHT)}
|
|
454
|
+
</div>
|
|
455
|
+
);
|
|
456
|
+
|
|
457
|
+
return (
|
|
458
|
+
<div className={classBorderOuter} style={{ flexDirection: "row" }}>
|
|
459
|
+
{borderSetComponents.get(DockLocation.LEFT)}
|
|
460
|
+
<div className={classBorderInner} style={{ flexDirection: "column" }}>
|
|
461
|
+
{borderSetComponents.get(DockLocation.TOP)}
|
|
462
|
+
{innerWithBorderTabs}
|
|
463
|
+
{borderSetComponents.get(DockLocation.BOTTOM)}
|
|
464
|
+
</div>
|
|
465
|
+
{borderSetComponents.get(DockLocation.RIGHT)}
|
|
466
|
+
</div>
|
|
467
|
+
);
|
|
407
468
|
}
|
|
408
|
-
}
|
|
409
|
-
};
|
|
410
469
|
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
470
|
+
} else { // no borders
|
|
471
|
+
return (
|
|
472
|
+
<div className={classMain} ref={this.mainRef} style={{ position: "absolute", top: 0, left: 0, bottom: 0, right: 0, display: "flex" }}>
|
|
473
|
+
{inner}
|
|
474
|
+
</div>
|
|
475
|
+
);
|
|
417
476
|
}
|
|
418
|
-
};
|
|
419
|
-
|
|
420
|
-
/** @internal */
|
|
421
|
-
getCurrentDocument() {
|
|
422
|
-
return this.currentDocument;
|
|
423
477
|
}
|
|
424
478
|
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
479
|
+
renderLayout() {
|
|
480
|
+
return (
|
|
481
|
+
<>
|
|
482
|
+
<Row key="__row__" layout={this} node={this.props.model.getRoot(this.windowId)} />
|
|
483
|
+
{this.renderEdgeIndicators()}
|
|
484
|
+
</>
|
|
485
|
+
);
|
|
428
486
|
}
|
|
429
487
|
|
|
430
|
-
|
|
431
|
-
getRootDiv() {
|
|
432
|
-
return this.selfRef.current;
|
|
433
|
-
}
|
|
434
|
-
|
|
435
|
-
/** @internal */
|
|
436
|
-
isSupportsPopout() {
|
|
437
|
-
return this.supportsPopout;
|
|
438
|
-
}
|
|
439
|
-
|
|
440
|
-
/** @internal */
|
|
441
|
-
isRealtimeResize() {
|
|
442
|
-
return this.props.realtimeResize ?? false;
|
|
443
|
-
}
|
|
444
|
-
|
|
445
|
-
/** @internal */
|
|
446
|
-
onTabDrag(...args: Parameters<Required<ILayoutProps>['onTabDrag']>) {
|
|
447
|
-
return this.props.onTabDrag?.(...args);
|
|
448
|
-
}
|
|
449
|
-
|
|
450
|
-
/** @internal */
|
|
451
|
-
getPopoutURL() {
|
|
452
|
-
return this.popoutURL;
|
|
453
|
-
}
|
|
454
|
-
|
|
455
|
-
/** @internal */
|
|
456
|
-
componentWillUnmount() {
|
|
457
|
-
const selfRefCurr = this.selfRef.current;
|
|
458
|
-
if (selfRefCurr) {
|
|
459
|
-
this.resizeObserver?.unobserve(selfRefCurr);
|
|
460
|
-
}
|
|
461
|
-
}
|
|
462
|
-
|
|
463
|
-
/** @internal */
|
|
464
|
-
setEditingTab(tabNode?: TabNode) {
|
|
465
|
-
this.setState({ editingTab: tabNode });
|
|
466
|
-
}
|
|
467
|
-
|
|
468
|
-
/** @internal */
|
|
469
|
-
getEditingTab() {
|
|
470
|
-
return this.state.editingTab;
|
|
471
|
-
}
|
|
472
|
-
|
|
473
|
-
/** @internal */
|
|
474
|
-
render() {
|
|
475
|
-
// first render will be used to find the size (via selfRef)
|
|
476
|
-
if (!this.selfRef.current) {
|
|
477
|
-
return (
|
|
478
|
-
<div ref={this.selfRef} className={this.getClassName(CLASSES.FLEXLAYOUT__LAYOUT)}>
|
|
479
|
-
{this.metricsElements()}
|
|
480
|
-
</div>
|
|
481
|
-
);
|
|
482
|
-
}
|
|
483
|
-
|
|
484
|
-
this.props.model._setPointerFine(window && window.matchMedia && window.matchMedia("(pointer: fine)").matches);
|
|
485
|
-
// this.start = Date.now();
|
|
486
|
-
const borderComponents: React.ReactNode[] = [];
|
|
487
|
-
const tabSetComponents: React.ReactNode[] = [];
|
|
488
|
-
const floatingWindows: React.ReactNode[] = [];
|
|
489
|
-
const tabComponents: Record<string, React.ReactNode> = {};
|
|
490
|
-
const splitterComponents: React.ReactNode[] = [];
|
|
491
|
-
|
|
492
|
-
const metrics: ILayoutMetrics = {
|
|
493
|
-
headerBarSize: this.state.calculatedHeaderBarSize,
|
|
494
|
-
tabBarSize: this.state.calculatedTabBarSize,
|
|
495
|
-
borderBarSize: this.state.calculatedBorderBarSize
|
|
496
|
-
};
|
|
497
|
-
this.props.model._setShowHiddenBorder(this.state.showHiddenBorder);
|
|
498
|
-
|
|
499
|
-
this.centerRect = this.props.model._layout(this.state.rect, metrics);
|
|
500
|
-
|
|
501
|
-
this.renderBorder(this.props.model.getBorderSet(), borderComponents, tabComponents, floatingWindows, splitterComponents);
|
|
502
|
-
this.renderChildren("", this.props.model.getRoot(), tabSetComponents, tabComponents, floatingWindows, splitterComponents);
|
|
503
|
-
|
|
504
|
-
const nextTopIds: string[] = [];
|
|
505
|
-
const nextTopIdsMap: Record<string, string> = {};
|
|
506
|
-
|
|
507
|
-
// Keep any previous tabs in the same DOM order as before, removing any that have been deleted
|
|
508
|
-
for (const t of this.tabIds) {
|
|
509
|
-
if (tabComponents[t]) {
|
|
510
|
-
nextTopIds.push(t);
|
|
511
|
-
nextTopIdsMap[t] = t;
|
|
512
|
-
}
|
|
513
|
-
}
|
|
514
|
-
this.tabIds = nextTopIds;
|
|
515
|
-
|
|
516
|
-
// Add tabs that have been added to the DOM
|
|
517
|
-
for (const t of Object.keys(tabComponents)) {
|
|
518
|
-
if (!nextTopIdsMap[t]) {
|
|
519
|
-
this.tabIds.push(t);
|
|
520
|
-
}
|
|
521
|
-
}
|
|
522
|
-
|
|
488
|
+
renderEdgeIndicators() {
|
|
523
489
|
const edges: React.ReactNode[] = [];
|
|
524
490
|
const arrowIcon = this.icons.edgeArrow;
|
|
525
491
|
if (this.state.showEdges) {
|
|
526
|
-
const r = this.
|
|
527
|
-
const length =
|
|
528
|
-
const width =
|
|
529
|
-
const offset =
|
|
492
|
+
const r = this.props.model.getRoot(this.windowId).getRect();
|
|
493
|
+
const length = edgeRectLength;
|
|
494
|
+
const width = edgeRectWidth;
|
|
495
|
+
const offset = edgeRectLength / 2;
|
|
530
496
|
const className = this.getClassName(CLASSES.FLEXLAYOUT__EDGE_RECT);
|
|
531
497
|
const radius = 50;
|
|
532
|
-
edges.push(<div key="North" style={{ top:
|
|
533
|
-
<div style={{transform: "rotate(180deg)"}}>
|
|
498
|
+
edges.push(<div key="North" style={{ top: 0, left: r.width / 2 - offset, width: length, height: width, borderBottomLeftRadius: radius, borderBottomRightRadius: radius }} className={className + " " + this.getClassName(CLASSES.FLEXLAYOUT__EDGE_RECT_TOP)}>
|
|
499
|
+
<div style={{ transform: "rotate(180deg)" }}>
|
|
534
500
|
{arrowIcon}
|
|
535
501
|
</div>
|
|
536
502
|
</div>);
|
|
537
|
-
edges.push(<div key="West" style={{ top: r.
|
|
538
|
-
<div style={{transform: "rotate(90deg)"}}>
|
|
503
|
+
edges.push(<div key="West" style={{ top: r.height / 2 - offset, left: 0, width: width, height: length, borderTopRightRadius: radius, borderBottomRightRadius: radius }} className={className + " " + this.getClassName(CLASSES.FLEXLAYOUT__EDGE_RECT_LEFT)}>
|
|
504
|
+
<div style={{ transform: "rotate(90deg)" }}>
|
|
539
505
|
{arrowIcon}
|
|
540
506
|
</div>
|
|
541
507
|
</div>);
|
|
542
|
-
edges.push(<div key="South" style={{ top: r.
|
|
508
|
+
edges.push(<div key="South" style={{ top: r.height - width, left: r.width / 2 - offset, width: length, height: width, borderTopLeftRadius: radius, borderTopRightRadius: radius }} className={className + " " + this.getClassName(CLASSES.FLEXLAYOUT__EDGE_RECT_BOTTOM)}>
|
|
543
509
|
<div>
|
|
544
510
|
{arrowIcon}
|
|
545
511
|
</div>
|
|
546
512
|
</div>);
|
|
547
|
-
edges.push(<div key="East" style={{ top: r.
|
|
548
|
-
<div style={{transform: "rotate(-90deg)"}}>
|
|
513
|
+
edges.push(<div key="East" style={{ top: r.height / 2 - offset, left: r.width - width, width: width, height: length, borderTopLeftRadius: radius, borderBottomLeftRadius: radius }} className={className + " " + this.getClassName(CLASSES.FLEXLAYOUT__EDGE_RECT_RIGHT)}>
|
|
514
|
+
<div style={{ transform: "rotate(-90deg)" }}>
|
|
549
515
|
{arrowIcon}
|
|
550
516
|
</div>
|
|
551
517
|
</div>);
|
|
552
518
|
}
|
|
553
519
|
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
return (
|
|
557
|
-
<div ref={this.selfRef} className={this.getClassName(CLASSES.FLEXLAYOUT__LAYOUT)} onDragEnter={this.props.onExternalDrag ? this.onDragEnter : undefined}>
|
|
558
|
-
{tabSetComponents}
|
|
559
|
-
{this.tabIds.map((t) => {
|
|
560
|
-
return tabComponents[t];
|
|
561
|
-
})}
|
|
562
|
-
{borderComponents}
|
|
563
|
-
{splitterComponents}
|
|
564
|
-
{edges}
|
|
565
|
-
{floatingWindows}
|
|
566
|
-
{this.metricsElements()}
|
|
567
|
-
{this.state.portal}
|
|
568
|
-
</div>
|
|
569
|
-
);
|
|
570
|
-
}
|
|
571
|
-
|
|
572
|
-
/** @internal */
|
|
573
|
-
metricsElements() {
|
|
574
|
-
// used to measure the tab and border tab sizes
|
|
575
|
-
const fontStyle = this.styleFont({ visibility: "hidden" });
|
|
576
|
-
return (
|
|
577
|
-
<React.Fragment>
|
|
578
|
-
<div key="findHeaderBarSize" ref={this.findHeaderBarSizeRef} style={fontStyle} className={this.getClassName(CLASSES.FLEXLAYOUT__TABSET_HEADER_SIZER)}>
|
|
579
|
-
FindHeaderBarSize
|
|
580
|
-
</div>
|
|
581
|
-
<div key="findTabBarSize" ref={this.findTabBarSizeRef} style={fontStyle} className={this.getClassName(CLASSES.FLEXLAYOUT__TABSET_SIZER)}>
|
|
582
|
-
FindTabBarSize
|
|
583
|
-
</div>
|
|
584
|
-
<div key="findBorderBarSize" ref={this.findBorderBarSizeRef} style={fontStyle} className={this.getClassName(CLASSES.FLEXLAYOUT__BORDER_SIZER)}>
|
|
585
|
-
FindBorderBarSize
|
|
586
|
-
</div>
|
|
587
|
-
</React.Fragment>
|
|
588
|
-
);
|
|
520
|
+
return edges;
|
|
589
521
|
}
|
|
590
522
|
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
this.
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
// catch incase it was a model change
|
|
598
|
-
}
|
|
599
|
-
};
|
|
600
|
-
|
|
601
|
-
/** @internal */
|
|
602
|
-
onSetWindow = (id: string, window: Window) => {
|
|
603
|
-
(this.props.model.getNodeById(id) as TabNode)._setWindow(window);
|
|
604
|
-
};
|
|
605
|
-
|
|
606
|
-
/** @internal */
|
|
607
|
-
renderBorder(borderSet: BorderSet, borderComponents: React.ReactNode[], tabComponents: Record<string, React.ReactNode>, floatingWindows: React.ReactNode[], splitterComponents: React.ReactNode[]) {
|
|
608
|
-
for (const border of borderSet.getBorders()) {
|
|
609
|
-
const borderPath = `/border/${border.getLocation().getName()}`;
|
|
610
|
-
if (border.isShowing()) {
|
|
611
|
-
borderComponents.push(
|
|
612
|
-
<BorderTabSet
|
|
613
|
-
key={`border_${border.getLocation().getName()}`}
|
|
614
|
-
path={borderPath}
|
|
615
|
-
border={border}
|
|
616
|
-
layout={this}
|
|
617
|
-
iconFactory={this.props.iconFactory}
|
|
618
|
-
titleFactory={this.props.titleFactory}
|
|
619
|
-
icons={this.icons}
|
|
620
|
-
/>
|
|
621
|
-
);
|
|
622
|
-
const drawChildren = border._getDrawChildren();
|
|
623
|
-
let i = 0;
|
|
624
|
-
let tabCount = 0;
|
|
625
|
-
for (const child of drawChildren) {
|
|
626
|
-
if (child instanceof SplitterNode) {
|
|
627
|
-
let path = borderPath + "/s";
|
|
628
|
-
splitterComponents.push(<Splitter key={child.getId()} layout={this} node={child} path={path} />);
|
|
629
|
-
} else if (child instanceof TabNode) {
|
|
630
|
-
let path = borderPath + "/t" + tabCount++;
|
|
631
|
-
if (this.supportsPopout && child.isFloating()) {
|
|
632
|
-
const rect = this._getScreenRect(child);
|
|
633
|
-
|
|
634
|
-
const tabBorderWidth = child._getAttr("borderWidth");
|
|
635
|
-
const tabBorderHeight = child._getAttr("borderHeight");
|
|
636
|
-
if (rect) {
|
|
637
|
-
if (tabBorderWidth !== -1 && border.getLocation().getOrientation() === Orientation.HORZ) {
|
|
638
|
-
rect.width = tabBorderWidth;
|
|
639
|
-
} else if (tabBorderHeight !== -1 && border.getLocation().getOrientation() === Orientation.VERT) {
|
|
640
|
-
rect.height = tabBorderHeight;
|
|
641
|
-
}
|
|
642
|
-
}
|
|
643
|
-
|
|
644
|
-
floatingWindows.push(
|
|
645
|
-
<FloatingWindow
|
|
646
|
-
key={child.getId()}
|
|
647
|
-
url={this.popoutURL}
|
|
648
|
-
rect={rect}
|
|
649
|
-
title={child.getName()}
|
|
650
|
-
id={child.getId()}
|
|
651
|
-
onSetWindow={this.onSetWindow}
|
|
652
|
-
onCloseWindow={this.onCloseWindow}
|
|
653
|
-
>
|
|
654
|
-
<FloatingWindowTab layout={this} node={child} factory={this.props.factory} />
|
|
655
|
-
</FloatingWindow>
|
|
656
|
-
);
|
|
657
|
-
tabComponents[child.getId()] = <TabFloating key={child.getId()}
|
|
658
|
-
layout={this}
|
|
659
|
-
path={path}
|
|
660
|
-
node={child}
|
|
661
|
-
selected={i === border.getSelected()
|
|
662
|
-
} />;
|
|
663
|
-
} else {
|
|
664
|
-
tabComponents[child.getId()] = <Tab key={child.getId()}
|
|
665
|
-
layout={this}
|
|
666
|
-
path={path}
|
|
667
|
-
node={child}
|
|
668
|
-
selected={i === border.getSelected()}
|
|
669
|
-
factory={this.props.factory} />;
|
|
670
|
-
}
|
|
671
|
-
}
|
|
672
|
-
i++;
|
|
673
|
-
}
|
|
674
|
-
}
|
|
675
|
-
}
|
|
676
|
-
}
|
|
523
|
+
renderWindows() {
|
|
524
|
+
const floatingWindows: React.ReactNode[] = [];
|
|
525
|
+
if (this.supportsPopout) {
|
|
526
|
+
const windows = this.props.model.getwindowsMap();
|
|
527
|
+
let i = 1;
|
|
528
|
+
for (const [windowId, layoutWindow] of windows) {
|
|
677
529
|
|
|
678
|
-
|
|
679
|
-
renderChildren(path: string, node: RowNode | TabSetNode, tabSetComponents: React.ReactNode[], tabComponents: Record<string, React.ReactNode>, floatingWindows: React.ReactNode[], splitterComponents: React.ReactNode[]) {
|
|
680
|
-
const drawChildren = node._getDrawChildren();
|
|
681
|
-
let splitterCount = 0;
|
|
682
|
-
let tabCount = 0;
|
|
683
|
-
let rowCount = 0;
|
|
684
|
-
|
|
685
|
-
for (const child of drawChildren!) {
|
|
686
|
-
if (child instanceof SplitterNode) {
|
|
687
|
-
const newPath = path + "/s" + (splitterCount++);
|
|
688
|
-
splitterComponents.push(<Splitter key={child.getId()} layout={this} path={newPath} node={child} />);
|
|
689
|
-
} else if (child instanceof TabSetNode) {
|
|
690
|
-
const newPath = path + "/ts" + (rowCount++);
|
|
691
|
-
tabSetComponents.push(<TabSet key={child.getId()} layout={this} path={newPath} node={child} iconFactory={this.props.iconFactory} titleFactory={this.props.titleFactory} icons={this.icons} />);
|
|
692
|
-
this.renderChildren(newPath, child, tabSetComponents, tabComponents, floatingWindows, splitterComponents);
|
|
693
|
-
} else if (child instanceof TabNode) {
|
|
694
|
-
const newPath = path + "/t" + (tabCount++);
|
|
695
|
-
const selectedTab = child.getParent()!.getChildren()[(child.getParent() as TabSetNode).getSelected()];
|
|
696
|
-
if (selectedTab === undefined) {
|
|
697
|
-
// this should not happen!
|
|
698
|
-
console.warn("undefined selectedTab should not happen");
|
|
699
|
-
}
|
|
700
|
-
if (this.supportsPopout && child.isFloating()) {
|
|
701
|
-
const rect = this._getScreenRect(child);
|
|
530
|
+
if (windowId !== Model.MAIN_WINDOW_ID) {
|
|
702
531
|
floatingWindows.push(
|
|
703
|
-
<
|
|
704
|
-
key={
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
532
|
+
<PopoutWindow
|
|
533
|
+
key={windowId}
|
|
534
|
+
layout={this}
|
|
535
|
+
title={this.popoutWindowName + " " + i}
|
|
536
|
+
layoutWindow={layoutWindow}
|
|
537
|
+
url={this.popoutURL + "?id=" + windowId}
|
|
709
538
|
onSetWindow={this.onSetWindow}
|
|
710
539
|
onCloseWindow={this.onCloseWindow}
|
|
711
540
|
>
|
|
712
|
-
<
|
|
713
|
-
</
|
|
541
|
+
<LayoutInternal {...this.props} windowId={windowId} mainLayout={this} />
|
|
542
|
+
</PopoutWindow>
|
|
714
543
|
);
|
|
715
|
-
|
|
716
|
-
} else {
|
|
717
|
-
tabComponents[child.getId()] = <Tab key={child.getId()} layout={this} path={newPath} node={child} selected={child === selectedTab} factory={this.props.factory} />;
|
|
544
|
+
i++;
|
|
718
545
|
}
|
|
719
|
-
} else {
|
|
720
|
-
// is row
|
|
721
|
-
const newPath = path + ((child.getOrientation() === Orientation.HORZ) ? "/r" : "/c") + (rowCount++);
|
|
722
|
-
this.renderChildren(newPath, child as RowNode, tabSetComponents, tabComponents, floatingWindows, splitterComponents);
|
|
723
546
|
}
|
|
724
547
|
}
|
|
548
|
+
return floatingWindows;
|
|
725
549
|
}
|
|
726
550
|
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
551
|
+
renderTabMoveables() {
|
|
552
|
+
const tabMoveables: React.ReactNode[] = [];
|
|
553
|
+
|
|
554
|
+
this.props.model.visitNodes((node) => {
|
|
555
|
+
if (node instanceof TabNode) {
|
|
556
|
+
const child = node as TabNode;
|
|
557
|
+
const element = this.getMoveableElement(child.getId());
|
|
558
|
+
child.setMoveableElement(element);
|
|
559
|
+
const selected = child.isSelected();
|
|
560
|
+
const rect = (child.getParent() as BorderNode | TabSetNode).getContentRect();
|
|
561
|
+
|
|
562
|
+
// only render first time if size >0
|
|
563
|
+
const renderTab = child.isRendered() ||
|
|
564
|
+
((selected || !child.isEnableRenderOnDemand()) && (rect.width > 0 && rect.height > 0));
|
|
565
|
+
|
|
566
|
+
if (renderTab) {
|
|
567
|
+
// console.log("rendertab", child.getName(), this.props.renderRevision);
|
|
568
|
+
const key = child.getId() + (child.isEnableWindowReMount() ? child.getWindowId() : "");
|
|
569
|
+
tabMoveables.push(createPortal(
|
|
570
|
+
<SizeTracker rect={rect} selected={child.isSelected()} forceRevision={this.state.forceRevision} tabsRevision={this.props.renderRevision} key={key}>
|
|
571
|
+
<ErrorBoundary message={this.i18nName(I18nLabel.Error_rendering_component)}>
|
|
572
|
+
{this.props.factory(child)}
|
|
573
|
+
</ErrorBoundary>
|
|
574
|
+
</SizeTracker>
|
|
575
|
+
, element, key));
|
|
576
|
+
|
|
577
|
+
child.setRendered(renderTab);
|
|
578
|
+
}
|
|
579
|
+
}
|
|
580
|
+
});
|
|
756
581
|
|
|
757
|
-
|
|
758
|
-
* Adds a new tab to the active tabset (if there is one)
|
|
759
|
-
* @param json the json for the new tab node
|
|
760
|
-
* @returns the added tab node or undefined
|
|
761
|
-
*/
|
|
762
|
-
addTabToActiveTabSet(json: IJsonTabNode) : TabNode | undefined {
|
|
763
|
-
const tabsetNode = this.props.model.getActiveTabset();
|
|
764
|
-
if (tabsetNode !== undefined) {
|
|
765
|
-
const node = this.doAction(Actions.addNode(json, tabsetNode.getId(), DockLocation.CENTER, -1));
|
|
766
|
-
return node as TabNode;
|
|
767
|
-
}
|
|
768
|
-
return undefined;
|
|
582
|
+
return tabMoveables;
|
|
769
583
|
}
|
|
770
584
|
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
* @param dragText the text to show on the drag panel
|
|
774
|
-
* @param json the json for the new tab node
|
|
775
|
-
* @param onDrop a callback to call when the drag is complete (node and event will be undefined if the drag was cancelled)
|
|
776
|
-
*/
|
|
777
|
-
addTabWithDragAndDrop(dragText: string | undefined, json: IJsonTabNode, onDrop?: (node?: Node, event?: Event) => void) {
|
|
778
|
-
this.fnNewNodeDropped = onDrop;
|
|
779
|
-
this.newTabJson = json;
|
|
780
|
-
this.dragStart(undefined, dragText, TabNode._fromJson(json, this.props.model, false), true, undefined, undefined);
|
|
781
|
-
}
|
|
585
|
+
renderTabStamps() {
|
|
586
|
+
const tabStamps: React.ReactNode[] = [];
|
|
782
587
|
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
* @param dragText the text to show on the drag panel
|
|
787
|
-
*/
|
|
788
|
-
moveTabWithDragAndDrop(node: (TabNode | TabSetNode), dragText?: string) {
|
|
789
|
-
this.dragStart(undefined, dragText, node, true, undefined, undefined);
|
|
790
|
-
}
|
|
588
|
+
this.props.model.visitNodes((node) => {
|
|
589
|
+
if (node instanceof TabNode) {
|
|
590
|
+
const child = node as TabNode;
|
|
791
591
|
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
* mouse down on the panel
|
|
795
|
-
*
|
|
796
|
-
* @param dragText the text to show on the drag panel
|
|
797
|
-
* @param json the json for the new tab node
|
|
798
|
-
* @param onDrop a callback to call when the drag is complete (node and event will be undefined if the drag was cancelled)
|
|
799
|
-
*/
|
|
800
|
-
addTabWithDragAndDropIndirect(dragText: string | undefined, json: IJsonTabNode, onDrop?: (node?: Node, event?: Event) => void) {
|
|
801
|
-
this.fnNewNodeDropped = onDrop;
|
|
802
|
-
this.newTabJson = json;
|
|
803
|
-
|
|
804
|
-
DragDrop.instance.addGlass(this.onCancelAdd);
|
|
805
|
-
|
|
806
|
-
this.dragDivText = dragText;
|
|
807
|
-
this.dragDiv = this.currentDocument!.createElement("div");
|
|
808
|
-
this.dragDiv.className = this.getClassName(CLASSES.FLEXLAYOUT__DRAG_RECT);
|
|
809
|
-
this.dragDiv.addEventListener("mousedown", this.onDragDivMouseDown);
|
|
810
|
-
this.dragDiv.addEventListener("touchstart", this.onDragDivMouseDown, { passive: false });
|
|
811
|
-
|
|
812
|
-
this.dragRectRender(this.dragDivText, undefined, this.newTabJson, () => {
|
|
813
|
-
if (this.dragDiv) {
|
|
814
|
-
// now it's been rendered into the dom it can be centered
|
|
815
|
-
this.dragDiv.style.visibility = "visible";
|
|
816
|
-
const domRect = this.dragDiv.getBoundingClientRect();
|
|
817
|
-
const r = new Rect(0, 0, domRect?.width, domRect?.height);
|
|
818
|
-
r.centerInRect(this.state.rect);
|
|
819
|
-
this.dragDiv.setAttribute("data-layout-path", "/drag-rectangle");
|
|
820
|
-
this.dragDiv.style.left = r.x + "px";
|
|
821
|
-
this.dragDiv.style.top = r.y + "px";
|
|
592
|
+
// what the tab should look like when dragged (since images need to have been loaded before drag image can be taken)
|
|
593
|
+
tabStamps.push(<DragContainer key={child.getId()} layout={this} node={child} />)
|
|
822
594
|
}
|
|
823
595
|
});
|
|
824
596
|
|
|
825
|
-
|
|
826
|
-
rootdiv!.appendChild(this.dragDiv);
|
|
597
|
+
return tabStamps;
|
|
827
598
|
}
|
|
828
599
|
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
this.hidePortal();
|
|
837
|
-
if (this.fnNewNodeDropped != null) {
|
|
838
|
-
this.fnNewNodeDropped();
|
|
839
|
-
this.fnNewNodeDropped = undefined;
|
|
840
|
-
}
|
|
841
|
-
|
|
842
|
-
try {
|
|
843
|
-
this.customDrop?.invalidated?.()
|
|
844
|
-
} catch (e) {
|
|
845
|
-
console.error(e)
|
|
846
|
-
}
|
|
600
|
+
renderTabs() {
|
|
601
|
+
const tabs = new Map<string, React.ReactNode>();
|
|
602
|
+
this.props.model.visitWindowNodes(this.windowId, (node) => {
|
|
603
|
+
if (node instanceof TabNode) {
|
|
604
|
+
const child = node as TabNode;
|
|
605
|
+
const selected = child.isSelected();
|
|
606
|
+
const path = child.getPath();
|
|
847
607
|
|
|
848
|
-
|
|
849
|
-
this.newTabJson = undefined;
|
|
850
|
-
this.customDrop = undefined;
|
|
851
|
-
};
|
|
608
|
+
const renderTab = child.isRendered() || selected || !child.isEnableRenderOnDemand();
|
|
852
609
|
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
const rootdiv = this.selfRef.current;
|
|
610
|
+
if (renderTab) {
|
|
611
|
+
// const rect = (child.getParent() as BorderNode | TabSetNode).getContentRect();
|
|
612
|
+
// const key = child.getId();
|
|
857
613
|
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
614
|
+
tabs.set(child.getId(), (
|
|
615
|
+
// <SizeTracker rect={rect} forceRevision={this.state.forceRevision} key={key}>
|
|
616
|
+
<Tab
|
|
617
|
+
key={child.getId()}
|
|
618
|
+
layout={this}
|
|
619
|
+
path={path}
|
|
620
|
+
node={child}
|
|
621
|
+
selected={selected} />
|
|
622
|
+
// </SizeTracker>
|
|
623
|
+
));
|
|
624
|
+
}
|
|
863
625
|
}
|
|
626
|
+
});
|
|
627
|
+
return tabs;
|
|
628
|
+
}
|
|
864
629
|
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
630
|
+
renderMetricsElements() {
|
|
631
|
+
return (
|
|
632
|
+
<div key="findBorderBarSize" ref={this.findBorderBarSizeRef} className={this.getClassName(CLASSES.FLEXLAYOUT__BORDER_SIZER)}>
|
|
633
|
+
FindBorderBarSize
|
|
634
|
+
</div>
|
|
635
|
+
);
|
|
636
|
+
}
|
|
637
|
+
|
|
638
|
+
checkForBorderToShow(x: number, y: number) {
|
|
639
|
+
const r = this.getBoundingClientRect(this.mainRef.current!);
|
|
640
|
+
const c = r.getCenter();
|
|
641
|
+
const margin = edgeRectWidth;
|
|
642
|
+
const offset = edgeRectLength / 2;
|
|
871
643
|
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
this.fnNewNodeDropped = undefined;
|
|
644
|
+
let overEdge = false;
|
|
645
|
+
if (this.props.model.isEnableEdgeDock() && this.state.showHiddenBorder === DockLocation.CENTER) {
|
|
646
|
+
if ((y > c.y - offset && y < c.y + offset) ||
|
|
647
|
+
(x > c.x - offset && x < c.x + offset)) {
|
|
648
|
+
overEdge = true;
|
|
878
649
|
}
|
|
650
|
+
}
|
|
879
651
|
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
652
|
+
let location = DockLocation.CENTER;
|
|
653
|
+
if (!overEdge) {
|
|
654
|
+
if (x <= r.x + margin) {
|
|
655
|
+
location = DockLocation.LEFT;
|
|
656
|
+
} else if (x >= r.getRight() - margin) {
|
|
657
|
+
location = DockLocation.RIGHT;
|
|
658
|
+
} else if (y <= r.y + margin) {
|
|
659
|
+
location = DockLocation.TOP;
|
|
660
|
+
} else if (y >= r.getBottom() - margin) {
|
|
661
|
+
location = DockLocation.BOTTOM;
|
|
884
662
|
}
|
|
663
|
+
}
|
|
885
664
|
|
|
886
|
-
|
|
887
|
-
this.
|
|
888
|
-
this.customDrop = undefined;
|
|
665
|
+
if (location !== this.state.showHiddenBorder) {
|
|
666
|
+
this.setState({ showHiddenBorder: location });
|
|
889
667
|
}
|
|
890
|
-
|
|
668
|
+
}
|
|
891
669
|
|
|
670
|
+
updateLayoutMetrics = () => {
|
|
671
|
+
if (this.findBorderBarSizeRef.current) {
|
|
672
|
+
const borderBarSize = this.findBorderBarSizeRef.current.getBoundingClientRect().height;
|
|
673
|
+
if (borderBarSize !== this.state.calculatedBorderBarSize) {
|
|
674
|
+
this.setState({ calculatedBorderBarSize: borderBarSize });
|
|
675
|
+
}
|
|
676
|
+
}
|
|
892
677
|
};
|
|
893
678
|
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
this.
|
|
898
|
-
|
|
679
|
+
tidyMoveablesMap() {
|
|
680
|
+
// console.log("tidyMoveablesMap");
|
|
681
|
+
const tabs = new Map<string, TabNode>();
|
|
682
|
+
this.props.model.visitNodes((node, _) => {
|
|
683
|
+
if (node instanceof TabNode) {
|
|
684
|
+
tabs.set(node.getId(), node);
|
|
685
|
+
}
|
|
686
|
+
});
|
|
899
687
|
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
onClick?: (event: Event) => void,
|
|
907
|
-
onDoubleClick?: (event: Event) => void
|
|
908
|
-
) => {
|
|
909
|
-
if (!allowDrag) {
|
|
910
|
-
DragDrop.instance.startDrag(
|
|
911
|
-
event,
|
|
912
|
-
undefined,
|
|
913
|
-
undefined,
|
|
914
|
-
undefined,
|
|
915
|
-
undefined,
|
|
916
|
-
onClick,
|
|
917
|
-
onDoubleClick,
|
|
918
|
-
this.currentDocument,
|
|
919
|
-
this.selfRef.current ?? undefined
|
|
920
|
-
);
|
|
921
|
-
} else {
|
|
922
|
-
this.dragNode = node;
|
|
923
|
-
this.dragDivText = dragDivText;
|
|
924
|
-
DragDrop.instance.startDrag(
|
|
925
|
-
event,
|
|
926
|
-
this.onDragStart,
|
|
927
|
-
this.onDragMove,
|
|
928
|
-
this.onDragEnd,
|
|
929
|
-
this.onCancelDrag,
|
|
930
|
-
onClick,
|
|
931
|
-
onDoubleClick,
|
|
932
|
-
this.currentDocument,
|
|
933
|
-
this.selfRef.current ?? undefined
|
|
934
|
-
);
|
|
688
|
+
for (const [nodeId, element] of this.moveableElementMap) {
|
|
689
|
+
if (!tabs.has(nodeId)) {
|
|
690
|
+
// console.log("delete", nodeId);
|
|
691
|
+
element.remove(); // remove from dom
|
|
692
|
+
this.moveableElementMap.delete(nodeId); // remove map entry
|
|
693
|
+
}
|
|
935
694
|
}
|
|
936
|
-
}
|
|
695
|
+
}
|
|
937
696
|
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
697
|
+
reorderComponents(components: Map<string, React.ReactNode>, ids: string[]) {
|
|
698
|
+
const nextIds: string[] = [];
|
|
699
|
+
const nextIdsSet = new Set<string>();
|
|
941
700
|
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
if (
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
layout={this}
|
|
949
|
-
iconFactory={this.props.iconFactory}
|
|
950
|
-
titleFactory={this.props.titleFactory}
|
|
951
|
-
/>);
|
|
701
|
+
let reordered: React.ReactNode[] = [];
|
|
702
|
+
// Keep any previous tabs in the same DOM order as before, removing any that have been deleted
|
|
703
|
+
for (const id of ids) {
|
|
704
|
+
if (components.get(id)) {
|
|
705
|
+
nextIds.push(id);
|
|
706
|
+
nextIdsSet.add(id);
|
|
952
707
|
}
|
|
953
708
|
}
|
|
709
|
+
ids.splice(0, ids.length, ...nextIds);
|
|
954
710
|
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
if (
|
|
958
|
-
|
|
711
|
+
// Add tabs that have been added to the DOM
|
|
712
|
+
for (const [id, _] of components) {
|
|
713
|
+
if (!nextIdsSet.has(id)) {
|
|
714
|
+
ids.push(id);
|
|
959
715
|
}
|
|
960
716
|
}
|
|
961
717
|
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
if (dragDiv) {
|
|
966
|
-
dragDiv.style.visibility = "hidden";
|
|
967
|
-
this.showPortal(
|
|
968
|
-
<DragRectRenderWrapper
|
|
969
|
-
// wait for it to be rendered
|
|
970
|
-
onRendered={() => {
|
|
971
|
-
this.dragRectRendered = true;
|
|
972
|
-
onRendered?.();
|
|
973
|
-
}}>
|
|
974
|
-
{content}
|
|
975
|
-
</DragRectRenderWrapper>,
|
|
976
|
-
dragDiv,
|
|
977
|
-
);
|
|
978
|
-
}
|
|
979
|
-
};
|
|
718
|
+
reordered = ids.map((id) => {
|
|
719
|
+
return components.get(id);
|
|
720
|
+
});
|
|
980
721
|
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
const portal = createPortal(control, element) as React.ReactPortal;
|
|
984
|
-
this.setState({ portal });
|
|
985
|
-
};
|
|
722
|
+
return reordered;
|
|
723
|
+
}
|
|
986
724
|
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
this.
|
|
725
|
+
onModelChange = (action: Action) => {
|
|
726
|
+
this.redrawInternal("model change");
|
|
727
|
+
if (this.props.onModelChange) {
|
|
728
|
+
this.props.onModelChange(this.props.model, action);
|
|
729
|
+
}
|
|
990
730
|
};
|
|
991
731
|
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
this.
|
|
995
|
-
|
|
996
|
-
const rootdiv = this.selfRef.current;
|
|
997
|
-
this.outlineDiv = this.currentDocument!.createElement("div");
|
|
998
|
-
this.outlineDiv.className = this.getClassName(CLASSES.FLEXLAYOUT__OUTLINE_RECT);
|
|
999
|
-
this.outlineDiv.style.visibility = "hidden";
|
|
1000
|
-
if (rootdiv) {
|
|
1001
|
-
rootdiv.appendChild(this.outlineDiv);
|
|
1002
|
-
}
|
|
732
|
+
redraw(type?: string) {
|
|
733
|
+
// console.log("redraw", this.windowId, type);
|
|
734
|
+
this.mainLayout.setState((state, props) => { return { forceRevision: state.forceRevision + 1 } });
|
|
735
|
+
}
|
|
1003
736
|
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
this.dragRectRender(this.dragDivText, this.dragNode, this.newTabJson);
|
|
737
|
+
redrawInternal(type: string) {
|
|
738
|
+
// console.log("redrawInternal", this.windowId, type);
|
|
739
|
+
this.mainLayout.setState((state, props) => { return { layoutRevision: state.layoutRevision + 1 } });
|
|
740
|
+
}
|
|
1009
741
|
|
|
1010
|
-
|
|
1011
|
-
|
|
742
|
+
doAction(action: Action): Node | undefined {
|
|
743
|
+
if (this.props.onAction !== undefined) {
|
|
744
|
+
const outcome = this.props.onAction(action);
|
|
745
|
+
if (outcome !== undefined) {
|
|
746
|
+
return this.props.model.doAction(outcome);
|
|
1012
747
|
}
|
|
748
|
+
return undefined;
|
|
749
|
+
} else {
|
|
750
|
+
return this.props.model.doAction(action);
|
|
1013
751
|
}
|
|
1014
|
-
|
|
1015
|
-
if (this.props.model.getMaximizedTabset() === undefined) {
|
|
1016
|
-
this.setState({ showEdges: this.props.model.isEnableEdgeDock() });
|
|
1017
|
-
}
|
|
752
|
+
}
|
|
1018
753
|
|
|
1019
|
-
|
|
1020
|
-
|
|
754
|
+
updateRect = () => {
|
|
755
|
+
const rect = this.getDomRect()
|
|
756
|
+
if (!rect.equals(this.state.rect) && rect.width !== 0 && rect.height !== 0) {
|
|
757
|
+
// console.log("updateRect", rect.floor());
|
|
758
|
+
this.setState({ rect });
|
|
759
|
+
if (this.windowId !== Model.MAIN_WINDOW_ID) {
|
|
760
|
+
this.redrawInternal("rect updated");
|
|
761
|
+
}
|
|
1021
762
|
}
|
|
1022
|
-
this.firstMove = true;
|
|
1023
|
-
|
|
1024
|
-
return true;
|
|
1025
763
|
};
|
|
1026
764
|
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
if (
|
|
1030
|
-
|
|
1031
|
-
if (this.outlineDiv) {
|
|
1032
|
-
this.outlineDiv.style.transition = `top ${speed}s, left ${speed}s, width ${speed}s, height ${speed}s`;
|
|
1033
|
-
}
|
|
765
|
+
getBoundingClientRect(div: HTMLElement): Rect {
|
|
766
|
+
const layoutRect = this.getDomRect();
|
|
767
|
+
if (layoutRect) {
|
|
768
|
+
return Rect.getBoundingClientRect(div).relativeTo(layoutRect);
|
|
1034
769
|
}
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
const pos = {
|
|
1038
|
-
x: event.clientX - (clientRect?.left ?? 0),
|
|
1039
|
-
y: event.clientY - (clientRect?.top ?? 0),
|
|
1040
|
-
};
|
|
770
|
+
return Rect.empty();
|
|
771
|
+
}
|
|
1041
772
|
|
|
1042
|
-
|
|
773
|
+
getMoveableContainer() {
|
|
774
|
+
return this.moveablesRef.current;
|
|
775
|
+
}
|
|
1043
776
|
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
if (this.dragDiv) {
|
|
1053
|
-
this.dragDiv.style.left = newLeft + "px";
|
|
1054
|
-
this.dragDiv.style.top = pos.y + 5 + "px";
|
|
1055
|
-
if (this.dragRectRendered && this.dragDiv.style.visibility === "hidden") {
|
|
1056
|
-
// make visible once the drag rect has been rendered
|
|
1057
|
-
this.dragDiv.style.visibility = "visible";
|
|
1058
|
-
}
|
|
777
|
+
getMoveableElement(id: string) {
|
|
778
|
+
let moveableElement = this.moveableElementMap.get(id);
|
|
779
|
+
if (moveableElement === undefined) {
|
|
780
|
+
moveableElement = document.createElement("div");
|
|
781
|
+
this.moveablesRef.current!.appendChild(moveableElement);
|
|
782
|
+
moveableElement.className = CLASSES.FLEXLAYOUT__TAB_MOVEABLE;
|
|
783
|
+
this.moveableElementMap.set(id, moveableElement);
|
|
1059
784
|
}
|
|
785
|
+
return moveableElement;
|
|
786
|
+
}
|
|
1060
787
|
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
this.outlineDiv.style.visibility = "visible";
|
|
1071
|
-
}
|
|
1072
|
-
}
|
|
788
|
+
getMainLayout() {
|
|
789
|
+
return this.mainLayout;
|
|
790
|
+
}
|
|
791
|
+
|
|
792
|
+
getClassName = (defaultClassName: string) => {
|
|
793
|
+
if (this.props.classNameMapper === undefined) {
|
|
794
|
+
return defaultClassName;
|
|
795
|
+
} else {
|
|
796
|
+
return this.props.classNameMapper(defaultClassName);
|
|
1073
797
|
}
|
|
1074
798
|
};
|
|
1075
799
|
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
}
|
|
800
|
+
getCurrentDocument() {
|
|
801
|
+
return this.currentDocument;
|
|
802
|
+
}
|
|
803
|
+
|
|
804
|
+
getDomRect() {
|
|
805
|
+
if (this.selfRef.current) {
|
|
806
|
+
return Rect.fromDomRect(this.selfRef.current.getBoundingClientRect());
|
|
807
|
+
} else {
|
|
808
|
+
return Rect.empty();
|
|
1086
809
|
}
|
|
1087
|
-
|
|
1088
|
-
this.hidePortal();
|
|
810
|
+
}
|
|
1089
811
|
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
if (this.dropInfo) {
|
|
1094
|
-
if (this.customDrop) {
|
|
1095
|
-
this.newTabJson = undefined;
|
|
1096
|
-
|
|
1097
|
-
try {
|
|
1098
|
-
const { callback, dragging, over, x, y, location } = this.customDrop;
|
|
1099
|
-
callback(dragging, over, x, y, location);
|
|
1100
|
-
if (this.fnNewNodeDropped != null) {
|
|
1101
|
-
this.fnNewNodeDropped();
|
|
1102
|
-
this.fnNewNodeDropped = undefined;
|
|
1103
|
-
}
|
|
1104
|
-
} catch (e) {
|
|
1105
|
-
console.error(e)
|
|
1106
|
-
}
|
|
1107
|
-
} else if (this.newTabJson !== undefined) {
|
|
1108
|
-
const newNode = this.doAction(Actions.addNode(this.newTabJson, this.dropInfo.node.getId(), this.dropInfo.location, this.dropInfo.index));
|
|
812
|
+
getWindowId() {
|
|
813
|
+
return this.windowId;
|
|
814
|
+
}
|
|
1109
815
|
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
}
|
|
1114
|
-
this.newTabJson = undefined;
|
|
1115
|
-
} else if (this.dragNode !== undefined) {
|
|
1116
|
-
this.doAction(Actions.moveNode(this.dragNode.getId(), this.dropInfo.node.getId(), this.dropInfo.location, this.dropInfo.index));
|
|
1117
|
-
}
|
|
1118
|
-
}
|
|
1119
|
-
this.setState({ showHiddenBorder: DockLocation.CENTER });
|
|
1120
|
-
};
|
|
816
|
+
getRootDiv() {
|
|
817
|
+
return this.selfRef.current;
|
|
818
|
+
}
|
|
1121
819
|
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
const currentCallback = this.customDrop?.callback;
|
|
1126
|
-
this.customDrop = undefined;
|
|
1127
|
-
|
|
1128
|
-
const dragging = this.newTabJson || (this.dragNode instanceof TabNode ? this.dragNode : undefined);
|
|
1129
|
-
if (dragging && (dropInfo.node instanceof TabSetNode || dropInfo.node instanceof BorderNode) && dropInfo.index === -1) {
|
|
1130
|
-
const selected = dropInfo.node.getSelectedNode() as TabNode | undefined;
|
|
1131
|
-
const tabRect = selected?.getRect();
|
|
1132
|
-
|
|
1133
|
-
if (selected && tabRect?.contains(pos.x, pos.y)) {
|
|
1134
|
-
let customDrop: ICustomDropDestination | undefined = undefined;
|
|
1135
|
-
|
|
1136
|
-
try {
|
|
1137
|
-
const dest = this.onTabDrag(dragging, selected, pos.x - tabRect.x, pos.y - tabRect.y, dropInfo.location, () => this.onDragMove(event));
|
|
1138
|
-
|
|
1139
|
-
if (dest) {
|
|
1140
|
-
customDrop = {
|
|
1141
|
-
rect: new Rect(dest.x + tabRect.x, dest.y + tabRect.y, dest.width, dest.height),
|
|
1142
|
-
callback: dest.callback,
|
|
1143
|
-
invalidated: dest.invalidated,
|
|
1144
|
-
dragging: dragging,
|
|
1145
|
-
over: selected,
|
|
1146
|
-
x: pos.x - tabRect.x,
|
|
1147
|
-
y: pos.y - tabRect.y,
|
|
1148
|
-
location: dropInfo.location,
|
|
1149
|
-
cursor: dest.cursor
|
|
1150
|
-
};
|
|
1151
|
-
}
|
|
1152
|
-
} catch (e) {
|
|
1153
|
-
console.error(e);
|
|
1154
|
-
}
|
|
820
|
+
getMainElement() {
|
|
821
|
+
return this.mainRef.current;
|
|
822
|
+
}
|
|
1155
823
|
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
824
|
+
getFactory() {
|
|
825
|
+
return this.props.factory;
|
|
826
|
+
}
|
|
1159
827
|
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
828
|
+
isSupportsPopout() {
|
|
829
|
+
return this.supportsPopout;
|
|
830
|
+
}
|
|
1163
831
|
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
if (this.customDrop) {
|
|
1168
|
-
this.customDrop.rect.positionElement(this.outlineDiv);
|
|
1169
|
-
} else {
|
|
1170
|
-
dropInfo.rect.positionElement(this.outlineDiv);
|
|
1171
|
-
}
|
|
1172
|
-
}
|
|
832
|
+
isRealtimeResize() {
|
|
833
|
+
return this.props.realtimeResize ?? false;
|
|
834
|
+
}
|
|
1173
835
|
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
}
|
|
836
|
+
getPopoutURL() {
|
|
837
|
+
return this.popoutURL;
|
|
838
|
+
}
|
|
1178
839
|
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
} catch (e) {
|
|
1182
|
-
console.error(e);
|
|
1183
|
-
}
|
|
840
|
+
setEditingTab(tabNode?: TabNode) {
|
|
841
|
+
this.setState({ editingTab: tabNode });
|
|
1184
842
|
}
|
|
1185
843
|
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
// DragDrop keeps track of number of dragenters minus the number of
|
|
1189
|
-
// dragleaves. Only start a new drag if there isn't one already.
|
|
1190
|
-
if (DragDrop.instance.isDragging())
|
|
1191
|
-
return;
|
|
1192
|
-
const drag = this.props.onExternalDrag!(event);
|
|
1193
|
-
if (drag) {
|
|
1194
|
-
// Mimic addTabWithDragAndDrop, but pass in DragEvent
|
|
1195
|
-
this.fnNewNodeDropped = drag.onDrop;
|
|
1196
|
-
this.newTabJson = drag.json;
|
|
1197
|
-
this.dragStart(event, drag.dragText, TabNode._fromJson(drag.json, this.props.model, false), true, undefined, undefined);
|
|
1198
|
-
}
|
|
844
|
+
getEditingTab() {
|
|
845
|
+
return this.state.editingTab;
|
|
1199
846
|
}
|
|
1200
847
|
|
|
848
|
+
getModel() {
|
|
849
|
+
return this.props.model;
|
|
850
|
+
}
|
|
1201
851
|
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
const c = r.getCenter();
|
|
1206
|
-
const margin = this.edgeRectWidth;
|
|
1207
|
-
const offset = this.edgeRectLength / 2;
|
|
852
|
+
onCloseWindow = (windowLayout: LayoutWindow) => {
|
|
853
|
+
this.doAction(Actions.closeWindow(windowLayout.windowId));
|
|
854
|
+
};
|
|
1208
855
|
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
if ((y > c.y - offset && y < c.y + offset) ||
|
|
1212
|
-
(x > c.x - offset && x < c.x + offset)) {
|
|
1213
|
-
overEdge = true;
|
|
1214
|
-
}
|
|
1215
|
-
}
|
|
856
|
+
onSetWindow = (windowLayout: LayoutWindow, window: Window) => {
|
|
857
|
+
};
|
|
1216
858
|
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
859
|
+
getScreenRect(inRect: Rect) {
|
|
860
|
+
const rect = inRect.clone();
|
|
861
|
+
const layoutRect = this.getDomRect();
|
|
862
|
+
// Note: outerHeight can be less than innerHeight when window is zoomed, so cannot use
|
|
863
|
+
// const navHeight = Math.min(65, this.currentWindow!.outerHeight - this.currentWindow!.innerHeight);
|
|
864
|
+
// const navWidth = Math.min(65, this.currentWindow!.outerWidth - this.currentWindow!.innerWidth);
|
|
865
|
+
const navHeight = 60;
|
|
866
|
+
const navWidth = 2;
|
|
867
|
+
// console.log(rect.y, this.currentWindow!.screenX,layoutRect.y);
|
|
868
|
+
rect.x = this.currentWindow!.screenX + this.currentWindow!.scrollX + navWidth / 2 + layoutRect.x + rect.x;
|
|
869
|
+
rect.y = this.currentWindow!.screenY + this.currentWindow!.scrollY + (navHeight - navWidth / 2) + layoutRect.y + rect.y;
|
|
870
|
+
rect.height += navHeight;
|
|
871
|
+
rect.width += navWidth;
|
|
872
|
+
return rect;
|
|
873
|
+
}
|
|
874
|
+
|
|
875
|
+
addTabToTabSet(tabsetId: string, json: IJsonTabNode): TabNode | undefined {
|
|
876
|
+
const tabsetNode = this.props.model.getNodeById(tabsetId);
|
|
877
|
+
if (tabsetNode !== undefined) {
|
|
878
|
+
const node = this.doAction(Actions.addNode(json, tabsetId, DockLocation.CENTER, -1));
|
|
879
|
+
return node as TabNode;
|
|
1228
880
|
}
|
|
881
|
+
return undefined;
|
|
882
|
+
}
|
|
1229
883
|
|
|
1230
|
-
|
|
1231
|
-
|
|
884
|
+
addTabToActiveTabSet(json: IJsonTabNode): TabNode | undefined {
|
|
885
|
+
const tabsetNode = this.props.model.getActiveTabset(this.windowId);
|
|
886
|
+
if (tabsetNode !== undefined) {
|
|
887
|
+
const node = this.doAction(Actions.addNode(json, tabsetNode.getId(), DockLocation.CENTER, -1));
|
|
888
|
+
return node as TabNode;
|
|
1232
889
|
}
|
|
890
|
+
return undefined;
|
|
1233
891
|
}
|
|
1234
892
|
|
|
1235
|
-
|
|
893
|
+
showControlInPortal = (control: React.ReactNode, element: HTMLElement) => {
|
|
894
|
+
const portal = createPortal(control, element) as React.ReactPortal;
|
|
895
|
+
this.setState({ portal });
|
|
896
|
+
};
|
|
897
|
+
|
|
898
|
+
hideControlInPortal = () => {
|
|
899
|
+
this.setState({ portal: undefined });
|
|
900
|
+
};
|
|
901
|
+
|
|
902
|
+
getIcons = () => {
|
|
903
|
+
return this.icons;
|
|
904
|
+
};
|
|
905
|
+
|
|
1236
906
|
maximize(tabsetNode: TabSetNode) {
|
|
1237
|
-
this.doAction(Actions.maximizeToggle(tabsetNode.getId()));
|
|
907
|
+
this.doAction(Actions.maximizeToggle(tabsetNode.getId(), this.getWindowId()));
|
|
1238
908
|
}
|
|
1239
909
|
|
|
1240
|
-
/** @internal */
|
|
1241
910
|
customizeTab(
|
|
1242
911
|
tabNode: TabNode,
|
|
1243
912
|
renderValues: ITabRenderValues,
|
|
@@ -1247,7 +916,6 @@ export class Layout extends React.Component<ILayoutProps, ILayoutState> {
|
|
|
1247
916
|
}
|
|
1248
917
|
}
|
|
1249
918
|
|
|
1250
|
-
/** @internal */
|
|
1251
919
|
customizeTabSet(
|
|
1252
920
|
tabSetNode: TabSetNode | BorderNode,
|
|
1253
921
|
renderValues: ITabSetRenderValues,
|
|
@@ -1257,7 +925,6 @@ export class Layout extends React.Component<ILayoutProps, ILayoutState> {
|
|
|
1257
925
|
}
|
|
1258
926
|
}
|
|
1259
927
|
|
|
1260
|
-
/** @internal */
|
|
1261
928
|
i18nName(id: I18nLabel, param?: string) {
|
|
1262
929
|
let message;
|
|
1263
930
|
if (this.props.i18nMapper) {
|
|
@@ -1269,51 +936,391 @@ export class Layout extends React.Component<ILayoutProps, ILayoutState> {
|
|
|
1269
936
|
return message;
|
|
1270
937
|
}
|
|
1271
938
|
|
|
1272
|
-
/** @internal */
|
|
1273
|
-
getOnRenderFloatingTabPlaceholder() {
|
|
1274
|
-
return this.props.onRenderFloatingTabPlaceholder;
|
|
1275
|
-
}
|
|
1276
|
-
|
|
1277
|
-
/** @internal */
|
|
1278
939
|
getShowOverflowMenu() {
|
|
1279
940
|
return this.props.onShowOverflowMenu;
|
|
1280
941
|
}
|
|
1281
942
|
|
|
1282
|
-
/** @internal */
|
|
1283
943
|
getTabSetPlaceHolderCallback() {
|
|
1284
944
|
return this.props.onTabSetPlaceHolder;
|
|
1285
945
|
}
|
|
1286
|
-
|
|
946
|
+
|
|
1287
947
|
showContextMenu(node: TabNode | TabSetNode | BorderNode, event: React.MouseEvent<HTMLElement, MouseEvent>) {
|
|
1288
948
|
if (this.props.onContextMenu) {
|
|
1289
949
|
this.props.onContextMenu(node, event);
|
|
1290
950
|
}
|
|
1291
951
|
}
|
|
1292
952
|
|
|
1293
|
-
/** @internal */
|
|
1294
953
|
auxMouseClick(node: TabNode | TabSetNode | BorderNode, event: React.MouseEvent<HTMLElement, MouseEvent>) {
|
|
1295
954
|
if (this.props.onAuxMouseClick) {
|
|
1296
955
|
this.props.onAuxMouseClick(node, event);
|
|
1297
956
|
}
|
|
1298
957
|
}
|
|
958
|
+
|
|
959
|
+
public showOverlay(show: boolean) {
|
|
960
|
+
this.setState({ showOverlay: show });
|
|
961
|
+
enablePointerOnIFrames(!show, this.currentDocument!);
|
|
962
|
+
}
|
|
963
|
+
|
|
964
|
+
|
|
965
|
+
|
|
966
|
+
// *************************** Start Drag Drop *************************************
|
|
967
|
+
|
|
968
|
+
addTabWithDragAndDrop(event: DragEvent, json: IJsonTabNode, onDrop?: (node?: Node, event?: React.DragEvent<HTMLElement>) => void) {
|
|
969
|
+
const tempNode = TabNode.fromJson(json, this.props.model, false);
|
|
970
|
+
LayoutInternal.dragState = new DragState(this.mainLayout, DragSource.Add, tempNode, json, onDrop);
|
|
971
|
+
}
|
|
972
|
+
|
|
973
|
+
moveTabWithDragAndDrop(event: DragEvent, node: (TabNode | TabSetNode)) {
|
|
974
|
+
this.setDragNode(event, node);
|
|
975
|
+
}
|
|
976
|
+
|
|
977
|
+
public setDragNode = (event: DragEvent, node: Node & IDraggable) => {
|
|
978
|
+
LayoutInternal.dragState = new DragState(this.mainLayout, DragSource.Internal, node, undefined, undefined);
|
|
979
|
+
// Note: can only set (very) limited types on android! so cannot set json
|
|
980
|
+
// Note: must set text/plain for android to allow drag,
|
|
981
|
+
// so just set a simple message indicating its a flexlayout drag (this is not used anywhere else)
|
|
982
|
+
event.dataTransfer!.setData('text/plain', "--flexlayout--");
|
|
983
|
+
event.dataTransfer!.effectAllowed = "copyMove";
|
|
984
|
+
event.dataTransfer!.dropEffect = "move";
|
|
985
|
+
|
|
986
|
+
this.dragEnterCount = 0;
|
|
987
|
+
|
|
988
|
+
if (node instanceof TabSetNode) {
|
|
989
|
+
let rendered = false;
|
|
990
|
+
let content = this.i18nName(I18nLabel.Move_Tabset);
|
|
991
|
+
if (node.getChildren().length > 0) {
|
|
992
|
+
content = this.i18nName(I18nLabel.Move_Tabs).replace("?", String(node.getChildren().length));
|
|
993
|
+
}
|
|
994
|
+
if (this.props.onRenderDragRect) {
|
|
995
|
+
const dragComponent = this.props.onRenderDragRect(content, node, undefined);
|
|
996
|
+
if (dragComponent) {
|
|
997
|
+
this.setDragComponent(event, dragComponent, 10, 10);
|
|
998
|
+
rendered = true;
|
|
999
|
+
}
|
|
1000
|
+
}
|
|
1001
|
+
if (!rendered) {
|
|
1002
|
+
this.setDragComponent(event, content, 10, 10);
|
|
1003
|
+
}
|
|
1004
|
+
} else {
|
|
1005
|
+
const element = event.target as HTMLElement;
|
|
1006
|
+
const rect = element.getBoundingClientRect();
|
|
1007
|
+
const offsetX = event.clientX - rect.left;
|
|
1008
|
+
const offsetY = event.clientY - rect.top;
|
|
1009
|
+
const parentNode = node?.getParent();
|
|
1010
|
+
const isInVerticalBorder = parentNode instanceof BorderNode && (parentNode as BorderNode).getOrientation() === Orientation.HORZ;
|
|
1011
|
+
const x = isInVerticalBorder ? 10 : offsetX;
|
|
1012
|
+
const y = isInVerticalBorder ? 10 : offsetY;
|
|
1013
|
+
|
|
1014
|
+
let rendered = false;
|
|
1015
|
+
if (this.props.onRenderDragRect) {
|
|
1016
|
+
const content = <TabButtonStamp key={node.getId()} layout={this} node={node as TabNode} />;
|
|
1017
|
+
const dragComponent = this.props.onRenderDragRect(content, node, undefined);
|
|
1018
|
+
if (dragComponent) {
|
|
1019
|
+
this.setDragComponent(event, dragComponent, x, y);
|
|
1020
|
+
rendered = true;
|
|
1021
|
+
}
|
|
1022
|
+
}
|
|
1023
|
+
if (!rendered) {
|
|
1024
|
+
if (isSafari()) { // safari doesnt render the offscreen tabstamps
|
|
1025
|
+
this.setDragComponent(event, <TabButtonStamp node={node as TabNode} layout={this}/>, x,y);
|
|
1026
|
+
} else {
|
|
1027
|
+
event.dataTransfer!.setDragImage((node as TabNode).getTabStamp()!, x, y);
|
|
1028
|
+
}
|
|
1029
|
+
}
|
|
1030
|
+
}
|
|
1031
|
+
};
|
|
1032
|
+
|
|
1033
|
+
|
|
1034
|
+
|
|
1035
|
+
public setDragComponent(event: DragEvent, component: React.ReactNode, x: number, y: number) {
|
|
1036
|
+
let dragElement: JSX.Element = (
|
|
1037
|
+
<div style={{ position: "unset" }}
|
|
1038
|
+
className={this.getClassName(CLASSES.FLEXLAYOUT__LAYOUT) + " " + this.getClassName(CLASSES.FLEXLAYOUT__DRAG_RECT)}>
|
|
1039
|
+
{component}
|
|
1040
|
+
</div>
|
|
1041
|
+
);
|
|
1042
|
+
|
|
1043
|
+
const tempDiv = this.currentDocument!.createElement('div');
|
|
1044
|
+
tempDiv.setAttribute("data-layout-path", "/drag-rectangle");
|
|
1045
|
+
tempDiv.style.position = "absolute";
|
|
1046
|
+
tempDiv.style.left = "-10000px";
|
|
1047
|
+
tempDiv.style.top = "-10000px";
|
|
1048
|
+
this.currentDocument!.body.appendChild(tempDiv);
|
|
1049
|
+
createRoot(tempDiv).render(dragElement);
|
|
1050
|
+
|
|
1051
|
+
event.dataTransfer!.setDragImage(tempDiv, x, y);
|
|
1052
|
+
setTimeout(() => {
|
|
1053
|
+
this.currentDocument!.body.removeChild(tempDiv!);
|
|
1054
|
+
}, 0);
|
|
1055
|
+
}
|
|
1056
|
+
|
|
1057
|
+
setDraggingOverWindow(overWindow: boolean) {
|
|
1058
|
+
// console.log("setDraggingOverWindow", overWindow);
|
|
1059
|
+
if (this.isDraggingOverWindow !== overWindow) {
|
|
1060
|
+
if (this.outlineDiv) {
|
|
1061
|
+
this.outlineDiv!.style.visibility = overWindow ? "hidden" : "visible";
|
|
1062
|
+
}
|
|
1063
|
+
|
|
1064
|
+
if (overWindow) {
|
|
1065
|
+
this.setState({ showEdges: false });
|
|
1066
|
+
} else {
|
|
1067
|
+
// add edge indicators
|
|
1068
|
+
if (this.props.model.getMaximizedTabset(this.windowId) === undefined) {
|
|
1069
|
+
this.setState({ showEdges: this.props.model.isEnableEdgeDock() });
|
|
1070
|
+
}
|
|
1071
|
+
}
|
|
1072
|
+
|
|
1073
|
+
this.isDraggingOverWindow = overWindow;
|
|
1074
|
+
}
|
|
1075
|
+
}
|
|
1076
|
+
|
|
1077
|
+
onDragEnterRaw = (event: React.DragEvent<HTMLElement>) => {
|
|
1078
|
+
this.dragEnterCount++;
|
|
1079
|
+
if (this.dragEnterCount === 1) {
|
|
1080
|
+
this.onDragEnter(event);
|
|
1081
|
+
}
|
|
1082
|
+
}
|
|
1083
|
+
|
|
1084
|
+
onDragLeaveRaw = (event: React.DragEvent<HTMLElement>) => {
|
|
1085
|
+
this.dragEnterCount--;
|
|
1086
|
+
if (this.dragEnterCount === 0) {
|
|
1087
|
+
this.onDragLeave(event);
|
|
1088
|
+
}
|
|
1089
|
+
}
|
|
1090
|
+
|
|
1091
|
+
clearDragMain() {
|
|
1092
|
+
// console.log("clear drag main");
|
|
1093
|
+
LayoutInternal.dragState = undefined;
|
|
1094
|
+
if (this.windowId === Model.MAIN_WINDOW_ID) {
|
|
1095
|
+
this.isDraggingOverWindow = false;
|
|
1096
|
+
}
|
|
1097
|
+
for (const [, layoutWindow] of this.props.model.getwindowsMap()) {
|
|
1098
|
+
// console.log(layoutWindow);
|
|
1099
|
+
layoutWindow.layout!.clearDragLocal();
|
|
1100
|
+
}
|
|
1101
|
+
}
|
|
1102
|
+
|
|
1103
|
+
clearDragLocal() {
|
|
1104
|
+
// console.log("clear drag local", this.windowId);
|
|
1105
|
+
this.setState({ showEdges: false });
|
|
1106
|
+
this.showOverlay(false);
|
|
1107
|
+
this.dragEnterCount = 0;
|
|
1108
|
+
this.dragging = false;
|
|
1109
|
+
if (this.outlineDiv) {
|
|
1110
|
+
this.selfRef.current!.removeChild(this.outlineDiv);
|
|
1111
|
+
this.outlineDiv = undefined;
|
|
1112
|
+
}
|
|
1113
|
+
}
|
|
1114
|
+
|
|
1115
|
+
onDragEnter = (event: React.DragEvent<HTMLElement>) => {
|
|
1116
|
+
// console.log("onDragEnter", this.windowId, this.dragEnterCount);
|
|
1117
|
+
|
|
1118
|
+
if (!LayoutInternal.dragState && this.props.onExternalDrag) { // not internal dragging
|
|
1119
|
+
const externalDrag = this.props.onExternalDrag!(event);
|
|
1120
|
+
if (externalDrag) {
|
|
1121
|
+
const tempNode = TabNode.fromJson(externalDrag.json, this.props.model, false);
|
|
1122
|
+
LayoutInternal.dragState = new DragState(this.mainLayout, DragSource.External, tempNode, externalDrag.json, externalDrag.onDrop);
|
|
1123
|
+
}
|
|
1124
|
+
}
|
|
1125
|
+
|
|
1126
|
+
if (LayoutInternal.dragState) {
|
|
1127
|
+
if (this.windowId !== Model.MAIN_WINDOW_ID && LayoutInternal.dragState.mainLayout === this.mainLayout) {
|
|
1128
|
+
LayoutInternal.dragState.mainLayout.setDraggingOverWindow(true);
|
|
1129
|
+
}
|
|
1130
|
+
|
|
1131
|
+
if (LayoutInternal.dragState.mainLayout !== this.mainLayout) {
|
|
1132
|
+
return; // drag not by this layout or its popouts
|
|
1133
|
+
}
|
|
1134
|
+
|
|
1135
|
+
event.preventDefault();
|
|
1136
|
+
|
|
1137
|
+
this.dropInfo = undefined;
|
|
1138
|
+
const rootdiv = this.selfRef.current;
|
|
1139
|
+
this.outlineDiv = this.currentDocument!.createElement("div");
|
|
1140
|
+
this.outlineDiv.className = this.getClassName(CLASSES.FLEXLAYOUT__OUTLINE_RECT);
|
|
1141
|
+
this.outlineDiv.style.visibility = "hidden";
|
|
1142
|
+
const speed = this.props.model.getAttribute("tabDragSpeed") as number;
|
|
1143
|
+
this.outlineDiv.style.transition = `top ${speed}s, left ${speed}s, width ${speed}s, height ${speed}s`;
|
|
1144
|
+
|
|
1145
|
+
rootdiv!.appendChild(this.outlineDiv);
|
|
1146
|
+
|
|
1147
|
+
this.dragging = true;
|
|
1148
|
+
this.showOverlay(true);
|
|
1149
|
+
// add edge indicators
|
|
1150
|
+
if (!this.isDraggingOverWindow && this.props.model.getMaximizedTabset(this.windowId) === undefined) {
|
|
1151
|
+
this.setState({ showEdges: this.props.model.isEnableEdgeDock() });
|
|
1152
|
+
}
|
|
1153
|
+
|
|
1154
|
+
const clientRect = this.selfRef.current?.getBoundingClientRect()!;
|
|
1155
|
+
const r = new Rect(
|
|
1156
|
+
event.clientX - (clientRect.left),
|
|
1157
|
+
event.clientY - (clientRect.top),
|
|
1158
|
+
1, 1
|
|
1159
|
+
);
|
|
1160
|
+
r.positionElement(this.outlineDiv);
|
|
1161
|
+
}
|
|
1162
|
+
}
|
|
1163
|
+
|
|
1164
|
+
onDragOver = (event: React.DragEvent<HTMLElement>) => {
|
|
1165
|
+
if (this.dragging && !this.isDraggingOverWindow) {
|
|
1166
|
+
// console.log("onDragOver");
|
|
1167
|
+
|
|
1168
|
+
event.preventDefault();
|
|
1169
|
+
const clientRect = this.selfRef.current?.getBoundingClientRect();
|
|
1170
|
+
const pos = {
|
|
1171
|
+
x: event.clientX - (clientRect?.left ?? 0),
|
|
1172
|
+
y: event.clientY - (clientRect?.top ?? 0),
|
|
1173
|
+
};
|
|
1174
|
+
|
|
1175
|
+
this.checkForBorderToShow(pos.x, pos.y);
|
|
1176
|
+
|
|
1177
|
+
let dropInfo = this.props.model.findDropTargetNode(this.windowId, LayoutInternal.dragState!.dragNode!, pos.x, pos.y);
|
|
1178
|
+
if (dropInfo) {
|
|
1179
|
+
this.dropInfo = dropInfo;
|
|
1180
|
+
if (this.outlineDiv) {
|
|
1181
|
+
this.outlineDiv.className = this.getClassName(dropInfo.className);
|
|
1182
|
+
dropInfo.rect.positionElement(this.outlineDiv);
|
|
1183
|
+
this.outlineDiv.style.visibility = "visible";
|
|
1184
|
+
}
|
|
1185
|
+
}
|
|
1186
|
+
}
|
|
1187
|
+
}
|
|
1188
|
+
|
|
1189
|
+
onDragLeave = (event: React.DragEvent<HTMLElement>) => {
|
|
1190
|
+
// console.log("onDragLeave", this.windowId, this.dragging);
|
|
1191
|
+
if (this.dragging) {
|
|
1192
|
+
if (this.windowId !== Model.MAIN_WINDOW_ID) {
|
|
1193
|
+
LayoutInternal.dragState!.mainLayout.setDraggingOverWindow(false);
|
|
1194
|
+
}
|
|
1195
|
+
|
|
1196
|
+
this.clearDragLocal();
|
|
1197
|
+
}
|
|
1198
|
+
}
|
|
1199
|
+
|
|
1200
|
+
onDrop = (event: React.DragEvent<HTMLElement>) => {
|
|
1201
|
+
// console.log("ondrop", this.windowId, this.dragging, Layout.dragState);
|
|
1202
|
+
|
|
1203
|
+
if (this.dragging) {
|
|
1204
|
+
event.preventDefault();
|
|
1205
|
+
|
|
1206
|
+
const dragState = LayoutInternal.dragState!;
|
|
1207
|
+
if (this.dropInfo) {
|
|
1208
|
+
if (dragState.dragJson !== undefined) {
|
|
1209
|
+
const newNode = this.doAction(Actions.addNode(dragState.dragJson, this.dropInfo.node.getId(), this.dropInfo.location, this.dropInfo.index));
|
|
1210
|
+
|
|
1211
|
+
if (dragState.fnNewNodeDropped !== undefined) {
|
|
1212
|
+
dragState.fnNewNodeDropped(newNode, event);
|
|
1213
|
+
}
|
|
1214
|
+
} else if (dragState.dragNode !== undefined) {
|
|
1215
|
+
this.doAction(Actions.moveNode(dragState.dragNode.getId(), this.dropInfo.node.getId(), this.dropInfo.location, this.dropInfo.index));
|
|
1216
|
+
}
|
|
1217
|
+
}
|
|
1218
|
+
|
|
1219
|
+
this.mainLayout.clearDragMain();
|
|
1220
|
+
}
|
|
1221
|
+
this.dragEnterCount = 0; // must set to zero here ref sublayouts
|
|
1222
|
+
}
|
|
1223
|
+
|
|
1224
|
+
// *************************** End Drag Drop *************************************
|
|
1299
1225
|
}
|
|
1300
1226
|
|
|
1301
|
-
|
|
1302
|
-
// a method once the rendering is written to the dom
|
|
1227
|
+
export const FlexLayoutVersion = "0.8.1";
|
|
1303
1228
|
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1229
|
+
export type DragRectRenderCallback = (
|
|
1230
|
+
content: React.ReactNode | undefined,
|
|
1231
|
+
node?: Node,
|
|
1232
|
+
json?: IJsonTabNode
|
|
1233
|
+
) => React.ReactNode | undefined;
|
|
1234
|
+
|
|
1235
|
+
export type NodeMouseEvent = (
|
|
1236
|
+
node: TabNode | TabSetNode | BorderNode,
|
|
1237
|
+
event: React.MouseEvent<HTMLElement, MouseEvent>
|
|
1238
|
+
) => void;
|
|
1239
|
+
|
|
1240
|
+
export type ShowOverflowMenuCallback = (
|
|
1241
|
+
node: TabSetNode | BorderNode,
|
|
1242
|
+
mouseEvent: React.MouseEvent<HTMLElement, MouseEvent>,
|
|
1243
|
+
items: { index: number; node: TabNode }[],
|
|
1244
|
+
onSelect: (item: { index: number; node: TabNode }) => void,
|
|
1245
|
+
) => void;
|
|
1246
|
+
|
|
1247
|
+
export type TabSetPlaceHolderCallback = (node: TabSetNode) => React.ReactNode;
|
|
1248
|
+
|
|
1249
|
+
export interface ITabSetRenderValues {
|
|
1250
|
+
/** components that will be added after the tabs */
|
|
1251
|
+
stickyButtons: React.ReactNode[];
|
|
1252
|
+
/** components that will be added at the end of the tabset */
|
|
1253
|
+
buttons: React.ReactNode[];
|
|
1254
|
+
/** position to insert overflow button within [...stickyButtons, ...buttons]
|
|
1255
|
+
* if left undefined position will be after the sticky buttons (if any)
|
|
1256
|
+
*/
|
|
1257
|
+
overflowPosition: number | undefined;
|
|
1308
1258
|
}
|
|
1309
1259
|
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
React.
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1260
|
+
export interface ITabRenderValues {
|
|
1261
|
+
/** the icon or other leading component */
|
|
1262
|
+
leading: React.ReactNode;
|
|
1263
|
+
/** the main tab text/component */
|
|
1264
|
+
content: React.ReactNode;
|
|
1265
|
+
/** a set of react components to add to the tab after the content */
|
|
1266
|
+
buttons: React.ReactNode[];
|
|
1267
|
+
}
|
|
1268
|
+
|
|
1269
|
+
export interface IIcons {
|
|
1270
|
+
close?: (React.ReactNode | ((tabNode: TabNode) => React.ReactNode));
|
|
1271
|
+
closeTabset?: (React.ReactNode | ((tabSetNode: TabSetNode) => React.ReactNode));
|
|
1272
|
+
popout?: (React.ReactNode | ((tabNode: TabNode) => React.ReactNode));
|
|
1273
|
+
maximize?: (React.ReactNode | ((tabSetNode: TabSetNode) => React.ReactNode));
|
|
1274
|
+
restore?: (React.ReactNode | ((tabSetNode: TabSetNode) => React.ReactNode));
|
|
1275
|
+
more?: (React.ReactNode | ((tabSetNode: (TabSetNode | BorderNode), hiddenTabs: { node: TabNode; index: number }[]) => React.ReactNode));
|
|
1276
|
+
edgeArrow?: React.ReactNode;
|
|
1277
|
+
activeTabset?: (React.ReactNode | ((tabSetNode: TabSetNode) => React.ReactNode));
|
|
1278
|
+
}
|
|
1279
|
+
|
|
1280
|
+
const defaultIcons = {
|
|
1281
|
+
close: <CloseIcon />,
|
|
1282
|
+
closeTabset: <CloseIcon />,
|
|
1283
|
+
popout: <PopoutIcon />,
|
|
1284
|
+
maximize: <MaximizeIcon />,
|
|
1285
|
+
restore: <RestoreIcon />,
|
|
1286
|
+
more: <OverflowIcon />,
|
|
1287
|
+
edgeArrow: <EdgeIcon />,
|
|
1288
|
+
activeTabset: <AsterickIcon />
|
|
1289
|
+
};
|
|
1290
|
+
|
|
1291
|
+
enum DragSource {
|
|
1292
|
+
Internal = "internal",
|
|
1293
|
+
External = "external",
|
|
1294
|
+
Add = "add"
|
|
1319
1295
|
}
|
|
1296
|
+
|
|
1297
|
+
/** @internal */
|
|
1298
|
+
const defaultSupportsPopout: boolean = isDesktop();
|
|
1299
|
+
|
|
1300
|
+
/** @internal */
|
|
1301
|
+
const edgeRectLength = 100;
|
|
1302
|
+
/** @internal */
|
|
1303
|
+
const edgeRectWidth = 10;
|
|
1304
|
+
|
|
1305
|
+
// global layout drag state
|
|
1306
|
+
class DragState {
|
|
1307
|
+
public readonly mainLayout: LayoutInternal;
|
|
1308
|
+
public readonly dragSource: DragSource;
|
|
1309
|
+
public readonly dragNode: Node & IDraggable | undefined;
|
|
1310
|
+
public readonly dragJson: IJsonTabNode | undefined;
|
|
1311
|
+
public readonly fnNewNodeDropped: ((node?: Node, event?: React.DragEvent<HTMLElement>) => void) | undefined;
|
|
1312
|
+
|
|
1313
|
+
public constructor(
|
|
1314
|
+
mainLayout: LayoutInternal,
|
|
1315
|
+
dragSource: DragSource,
|
|
1316
|
+
dragNode: Node & IDraggable | undefined,
|
|
1317
|
+
dragJson: IJsonTabNode | undefined,
|
|
1318
|
+
fnNewNodeDropped: ((node?: Node, event?: React.DragEvent<HTMLElement>) => void) | undefined
|
|
1319
|
+
) {
|
|
1320
|
+
this.mainLayout = mainLayout;
|
|
1321
|
+
this.dragSource = dragSource;
|
|
1322
|
+
this.dragNode = dragNode;
|
|
1323
|
+
this.dragJson = dragJson;
|
|
1324
|
+
this.fnNewNodeDropped = fnNewNodeDropped;
|
|
1325
|
+
}
|
|
1326
|
+
}
|