flexlayout-react 0.7.14 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/ChangeLog.txt +26 -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 -6
- package/declarations/view/Layout.d.ts +139 -160
- 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 +457 -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 +124 -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 -40
- package/lib/view/Icons.js.map +1 -1
- package/lib/view/Layout.js +918 -901
- 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 +122 -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 +267 -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 +300 -242
- package/src/model/Utils.ts +6 -32
- package/src/view/BorderButton.tsx +32 -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 +13 -0
- package/src/view/Layout.tsx +1071 -1047
- 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 +36 -55
- package/src/view/TabButtonStamp.tsx +5 -9
- package/src/view/TabOverflowHook.tsx +14 -9
- package/src/view/TabSet.tsx +217 -176
- package/src/view/Utils.tsx +119 -39
- package/style/_base.scss +143 -35
- package/style/dark.css +685 -577
- package/style/dark.css.map +1 -1
- package/style/dark.scss +4 -1
- package/style/gray.css +668 -560
- package/style/gray.css.map +1 -1
- package/style/gray.scss +4 -1
- package/style/light.css +669 -561
- package/style/light.css.map +1 -1
- package/style/light.scss +6 -3
- package/style/rounded.css +697 -0
- package/style/rounded.css.map +1 -0
- package/style/rounded.scss +194 -0
- package/style/underline.css +690 -582
- package/style/underline.css.map +1 -1
- package/style/underline.scss +4 -1
- 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,732 +1,878 @@
|
|
|
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, 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
|
-
|
|
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
|
+
}
|
|
140
125
|
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
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
|
+
}
|
|
149
135
|
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
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
|
+
}
|
|
160
165
|
}
|
|
161
166
|
|
|
162
167
|
/** @internal */
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
getCurrentDocument(): Document | undefined;
|
|
170
|
-
getClassName(defaultClassName: string): string;
|
|
171
|
-
doAction(action: Action): Node | undefined;
|
|
172
|
-
getDomRect(): DOMRect | undefined;
|
|
173
|
-
getRootDiv(): HTMLDivElement | null;
|
|
174
|
-
dragStart(
|
|
175
|
-
event: Event | React.MouseEvent<HTMLDivElement, MouseEvent> | React.TouchEvent<HTMLDivElement> | React.DragEvent<HTMLDivElement> | undefined,
|
|
176
|
-
dragDivText: string | undefined,
|
|
177
|
-
node: Node & IDraggable,
|
|
178
|
-
allowDrag: boolean,
|
|
179
|
-
onClick?: (event: Event) => void,
|
|
180
|
-
onDoubleClick?: (event: Event) => void
|
|
181
|
-
): void;
|
|
182
|
-
customizeTab(
|
|
183
|
-
tabNode: TabNode,
|
|
184
|
-
renderValues: ITabRenderValues,
|
|
185
|
-
): void;
|
|
186
|
-
customizeTabSet(
|
|
187
|
-
tabSetNode: TabSetNode | BorderNode,
|
|
188
|
-
renderValues: ITabSetRenderValues,
|
|
189
|
-
): void;
|
|
190
|
-
styleFont: (style: Record<string, string>) => Record<string, string>;
|
|
191
|
-
setEditingTab(tabNode?: TabNode): void;
|
|
192
|
-
getEditingTab(): TabNode | undefined;
|
|
193
|
-
getOnRenderFloatingTabPlaceholder(): FloatingTabPlaceholderRenderCallback | undefined;
|
|
194
|
-
showContextMenu(node: TabNode | TabSetNode | BorderNode, event: React.MouseEvent<HTMLElement, MouseEvent>): void;
|
|
195
|
-
auxMouseClick(node: TabNode | TabSetNode | BorderNode, event: React.MouseEvent<HTMLElement, MouseEvent>): void;
|
|
196
|
-
showPortal: (portal: React.ReactNode, portalDiv: HTMLDivElement) => void;
|
|
197
|
-
hidePortal: () => void;
|
|
198
|
-
getShowOverflowMenu(): ShowOverflowMenuCallback | undefined;
|
|
199
|
-
getTabSetPlaceHolderCallback(): TabSetPlaceHolderCallback | undefined;
|
|
168
|
+
interface ILayoutInternalProps extends ILayoutProps {
|
|
169
|
+
renderRevision: number;
|
|
170
|
+
|
|
171
|
+
// used only for popout windows:
|
|
172
|
+
windowId?: string;
|
|
173
|
+
mainLayout?: LayoutInternal;
|
|
200
174
|
}
|
|
201
175
|
|
|
202
|
-
// Popout windows work in latest browsers based on webkit (Chrome, Opera, Safari, latest Edge) and Firefox. They do
|
|
203
|
-
// not work on any version if IE or the original Edge browser
|
|
204
|
-
// Assume any recent desktop browser not IE or original Edge will work
|
|
205
176
|
/** @internal */
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
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
|
+
}
|
|
212
188
|
|
|
213
|
-
/**
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
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;
|
|
217
192
|
|
|
218
|
-
/** @internal */
|
|
219
193
|
private selfRef: React.RefObject<HTMLDivElement>;
|
|
220
|
-
|
|
221
|
-
private findHeaderBarSizeRef: React.RefObject<HTMLDivElement>;
|
|
222
|
-
/** @internal */
|
|
223
|
-
private findTabBarSizeRef: React.RefObject<HTMLDivElement>;
|
|
224
|
-
/** @internal */
|
|
194
|
+
private moveablesRef: React.RefObject<HTMLDivElement>;
|
|
225
195
|
private findBorderBarSizeRef: React.RefObject<HTMLDivElement>;
|
|
226
|
-
|
|
196
|
+
private mainRef: React.RefObject<HTMLDivElement>;
|
|
227
197
|
private previousModel?: Model;
|
|
228
|
-
|
|
229
|
-
private
|
|
230
|
-
|
|
231
|
-
/** @internal */
|
|
232
|
-
// private start: number = 0;
|
|
233
|
-
/** @internal */
|
|
234
|
-
// private layoutTime: number = 0;
|
|
235
|
-
|
|
236
|
-
/** @internal */
|
|
237
|
-
private tabIds: string[];
|
|
238
|
-
/** @internal */
|
|
239
|
-
private newTabJson: IJsonTabNode | undefined;
|
|
240
|
-
/** @internal */
|
|
241
|
-
private firstMove: boolean = false;
|
|
242
|
-
/** @internal */
|
|
243
|
-
private dragNode?: Node & IDraggable;
|
|
244
|
-
/** @internal */
|
|
245
|
-
private dragDiv?: HTMLDivElement;
|
|
246
|
-
/** @internal */
|
|
247
|
-
private dragRectRendered: boolean = true;
|
|
248
|
-
/** @internal */
|
|
249
|
-
private dragDivText: string | undefined = undefined;
|
|
250
|
-
/** @internal */
|
|
198
|
+
private orderedIds: string[];
|
|
199
|
+
private moveableElementMap = new Map<string, HTMLElement>();
|
|
251
200
|
private dropInfo: DropInfo | undefined;
|
|
252
|
-
|
|
253
|
-
private customDrop: ICustomDropDestination | undefined;
|
|
254
|
-
/** @internal */
|
|
255
|
-
private outlineDiv?: HTMLDivElement;
|
|
256
|
-
/** @internal */
|
|
257
|
-
private edgeRectLength = 100;
|
|
258
|
-
/** @internal */
|
|
259
|
-
private edgeRectWidth = 10;
|
|
260
|
-
/** @internal */
|
|
261
|
-
private fnNewNodeDropped?: (node?: Node, event?: Event) => void;
|
|
262
|
-
/** @internal */
|
|
201
|
+
private outlineDiv?: HTMLElement;
|
|
263
202
|
private currentDocument?: Document;
|
|
264
|
-
/** @internal */
|
|
265
203
|
private currentWindow?: Window;
|
|
266
|
-
/** @internal */
|
|
267
204
|
private supportsPopout: boolean;
|
|
268
|
-
/** @internal */
|
|
269
205
|
private popoutURL: string;
|
|
270
|
-
/** @internal */
|
|
271
206
|
private icons: IIcons;
|
|
272
|
-
/** @internal */
|
|
273
207
|
private resizeObserver?: ResizeObserver;
|
|
274
208
|
|
|
275
|
-
|
|
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) {
|
|
276
221
|
super(props);
|
|
277
|
-
|
|
278
|
-
this.
|
|
222
|
+
|
|
223
|
+
this.orderedIds = [];
|
|
279
224
|
this.selfRef = React.createRef<HTMLDivElement>();
|
|
280
|
-
this.
|
|
281
|
-
this.
|
|
225
|
+
this.moveablesRef = React.createRef<HTMLDivElement>();
|
|
226
|
+
this.mainRef = React.createRef<HTMLDivElement>();
|
|
282
227
|
this.findBorderBarSizeRef = React.createRef<HTMLDivElement>();
|
|
228
|
+
|
|
283
229
|
this.supportsPopout = props.supportsPopout !== undefined ? props.supportsPopout : defaultSupportsPopout;
|
|
284
230
|
this.popoutURL = props.popoutURL ? props.popoutURL : "popout.html";
|
|
285
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;
|
|
286
239
|
|
|
287
240
|
this.state = {
|
|
288
|
-
rect:
|
|
289
|
-
calculatedHeaderBarSize: 25,
|
|
290
|
-
calculatedTabBarSize: 26,
|
|
291
|
-
calculatedBorderBarSize: 30,
|
|
241
|
+
rect: Rect.empty(),
|
|
292
242
|
editingTab: undefined,
|
|
293
|
-
showHiddenBorder: DockLocation.CENTER,
|
|
294
243
|
showEdges: false,
|
|
244
|
+
showOverlay: false,
|
|
245
|
+
calculatedBorderBarSize: 29,
|
|
246
|
+
layoutRevision: 0,
|
|
247
|
+
forceRevision: 0,
|
|
248
|
+
showHiddenBorder: DockLocation.CENTER
|
|
295
249
|
};
|
|
296
250
|
|
|
297
|
-
this.
|
|
298
|
-
}
|
|
299
|
-
|
|
300
|
-
/** @internal */
|
|
301
|
-
styleFont(style: Record<string, string>): Record<string, string> {
|
|
302
|
-
if (this.props.font) {
|
|
303
|
-
if (this.selfRef.current) {
|
|
304
|
-
if (this.props.font.size) {
|
|
305
|
-
this.selfRef.current.style.setProperty("--font-size", this.props.font.size);
|
|
306
|
-
}
|
|
307
|
-
if (this.props.font.family) {
|
|
308
|
-
this.selfRef.current.style.setProperty("--font-family", this.props.font.family);
|
|
309
|
-
}
|
|
310
|
-
}
|
|
311
|
-
if (this.props.font.style) {
|
|
312
|
-
style.fontStyle = this.props.font.style;
|
|
313
|
-
}
|
|
314
|
-
if (this.props.font.weight) {
|
|
315
|
-
style.fontWeight = this.props.font.weight;
|
|
316
|
-
}
|
|
317
|
-
}
|
|
318
|
-
return style;
|
|
319
|
-
}
|
|
320
|
-
|
|
321
|
-
/** @internal */
|
|
322
|
-
onModelChange = (action: Action) => {
|
|
323
|
-
this.forceUpdate();
|
|
324
|
-
if (this.props.onModelChange) {
|
|
325
|
-
this.props.onModelChange(this.props.model, action);
|
|
326
|
-
}
|
|
327
|
-
};
|
|
328
|
-
|
|
329
|
-
/** @internal */
|
|
330
|
-
doAction(action: Action): Node | undefined {
|
|
331
|
-
if (this.props.onAction !== undefined) {
|
|
332
|
-
const outcome = this.props.onAction(action);
|
|
333
|
-
if (outcome !== undefined) {
|
|
334
|
-
return this.props.model.doAction(outcome);
|
|
335
|
-
}
|
|
336
|
-
return undefined;
|
|
337
|
-
} else {
|
|
338
|
-
return this.props.model.doAction(action);
|
|
339
|
-
}
|
|
251
|
+
this.isMainWindow = this.windowId === Model.MAIN_WINDOW_ID;
|
|
340
252
|
}
|
|
341
253
|
|
|
342
|
-
/** @internal */
|
|
343
254
|
componentDidMount() {
|
|
344
255
|
this.updateRect();
|
|
345
|
-
this.updateLayoutMetrics();
|
|
346
256
|
|
|
347
|
-
|
|
348
|
-
this.currentDocument = (this.selfRef.current as HTMLDivElement).ownerDocument;
|
|
257
|
+
this.currentDocument = (this.selfRef.current as HTMLElement).ownerDocument;
|
|
349
258
|
this.currentWindow = this.currentDocument.defaultView!;
|
|
259
|
+
|
|
260
|
+
this.layoutWindow.window = this.currentWindow;
|
|
261
|
+
this.layoutWindow.toScreenRectFunction = (r) => this.getScreenRect(r);
|
|
262
|
+
|
|
350
263
|
this.resizeObserver = new ResizeObserver(entries => {
|
|
351
|
-
|
|
264
|
+
requestAnimationFrame(() => {
|
|
265
|
+
this.updateRect();
|
|
266
|
+
});
|
|
352
267
|
});
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
this.resizeObserver.observe(selfRefCurr);
|
|
268
|
+
if (this.selfRef.current) {
|
|
269
|
+
this.resizeObserver.observe(this.selfRef.current);
|
|
356
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
|
+
});
|
|
357
306
|
}
|
|
358
307
|
|
|
359
|
-
/** @internal */
|
|
360
308
|
componentDidUpdate() {
|
|
361
|
-
this.
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
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();
|
|
365
323
|
}
|
|
366
|
-
|
|
367
|
-
this.
|
|
324
|
+
|
|
325
|
+
this.updateLayoutMetrics();
|
|
368
326
|
}
|
|
369
|
-
// console.log("Layout time: " + this.layoutTime + "ms Render time: " + (Date.now() - this.start) + "ms");
|
|
370
327
|
}
|
|
371
328
|
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
domRect = this.getDomRect();
|
|
376
|
-
}
|
|
377
|
-
if (!domRect) {
|
|
378
|
-
// no dom rect available, return.
|
|
379
|
-
return;
|
|
329
|
+
componentWillUnmount() {
|
|
330
|
+
if (this.selfRef.current) {
|
|
331
|
+
this.resizeObserver?.unobserve(this.selfRef.current);
|
|
380
332
|
}
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
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
|
+
);
|
|
384
346
|
}
|
|
385
|
-
};
|
|
386
347
|
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
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>;
|
|
394
371
|
}
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
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
|
+
}
|
|
399
416
|
}
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
const
|
|
403
|
-
|
|
404
|
-
|
|
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
|
+
);
|
|
405
468
|
}
|
|
406
|
-
}
|
|
407
|
-
};
|
|
408
469
|
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
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
|
+
);
|
|
415
476
|
}
|
|
416
|
-
};
|
|
417
|
-
|
|
418
|
-
/** @internal */
|
|
419
|
-
getCurrentDocument() {
|
|
420
|
-
return this.currentDocument;
|
|
421
477
|
}
|
|
422
478
|
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
479
|
+
renderLayout() {
|
|
480
|
+
return (
|
|
481
|
+
<>
|
|
482
|
+
<Row key="__row__" layout={this} node={this.props.model.getRoot(this.windowId)} />
|
|
483
|
+
{this.renderEdgeIndicators()}
|
|
484
|
+
</>
|
|
485
|
+
);
|
|
426
486
|
}
|
|
427
487
|
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
488
|
+
renderEdgeIndicators() {
|
|
489
|
+
const edges: React.ReactNode[] = [];
|
|
490
|
+
const arrowIcon = this.icons.edgeArrow;
|
|
491
|
+
if (this.state.showEdges) {
|
|
492
|
+
const r = this.props.model.getRoot(this.windowId).getRect();
|
|
493
|
+
const length = edgeRectLength;
|
|
494
|
+
const width = edgeRectWidth;
|
|
495
|
+
const offset = edgeRectLength / 2;
|
|
496
|
+
const className = this.getClassName(CLASSES.FLEXLAYOUT__EDGE_RECT);
|
|
497
|
+
const radius = 50;
|
|
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)" }}>
|
|
500
|
+
{arrowIcon}
|
|
501
|
+
</div>
|
|
502
|
+
</div>);
|
|
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)" }}>
|
|
505
|
+
{arrowIcon}
|
|
506
|
+
</div>
|
|
507
|
+
</div>);
|
|
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)}>
|
|
509
|
+
<div>
|
|
510
|
+
{arrowIcon}
|
|
511
|
+
</div>
|
|
512
|
+
</div>);
|
|
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)" }}>
|
|
515
|
+
{arrowIcon}
|
|
516
|
+
</div>
|
|
517
|
+
</div>);
|
|
518
|
+
}
|
|
432
519
|
|
|
433
|
-
|
|
434
|
-
isSupportsPopout() {
|
|
435
|
-
return this.supportsPopout;
|
|
520
|
+
return edges;
|
|
436
521
|
}
|
|
437
522
|
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
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) {
|
|
442
529
|
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
530
|
+
if (windowId !== Model.MAIN_WINDOW_ID) {
|
|
531
|
+
floatingWindows.push(
|
|
532
|
+
<PopoutWindow
|
|
533
|
+
key={windowId}
|
|
534
|
+
layout={this}
|
|
535
|
+
title={this.popoutWindowName + " " + i}
|
|
536
|
+
layoutWindow={layoutWindow}
|
|
537
|
+
url={this.popoutURL + "?id=" + windowId}
|
|
538
|
+
onSetWindow={this.onSetWindow}
|
|
539
|
+
onCloseWindow={this.onCloseWindow}
|
|
540
|
+
>
|
|
541
|
+
<LayoutInternal {...this.props} windowId={windowId} mainLayout={this} />
|
|
542
|
+
</PopoutWindow>
|
|
543
|
+
);
|
|
544
|
+
i++;
|
|
545
|
+
}
|
|
546
|
+
}
|
|
547
|
+
}
|
|
548
|
+
return floatingWindows;
|
|
446
549
|
}
|
|
447
550
|
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
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
|
+
});
|
|
581
|
+
|
|
582
|
+
return tabMoveables;
|
|
451
583
|
}
|
|
452
584
|
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
585
|
+
renderTabStamps() {
|
|
586
|
+
const tabStamps: React.ReactNode[] = [];
|
|
587
|
+
|
|
588
|
+
this.props.model.visitNodes((node) => {
|
|
589
|
+
if (node instanceof TabNode) {
|
|
590
|
+
const child = node as TabNode;
|
|
591
|
+
|
|
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} />)
|
|
594
|
+
}
|
|
595
|
+
});
|
|
596
|
+
|
|
597
|
+
return tabStamps;
|
|
459
598
|
}
|
|
460
599
|
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
this.
|
|
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();
|
|
607
|
+
|
|
608
|
+
const renderTab = child.isRendered() || selected || !child.isEnableRenderOnDemand();
|
|
609
|
+
|
|
610
|
+
if (renderTab) {
|
|
611
|
+
// const rect = (child.getParent() as BorderNode | TabSetNode).getContentRect();
|
|
612
|
+
// const key = child.getId();
|
|
613
|
+
|
|
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
|
+
}
|
|
625
|
+
}
|
|
626
|
+
});
|
|
627
|
+
return tabs;
|
|
464
628
|
}
|
|
465
629
|
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
630
|
+
renderMetricsElements() {
|
|
631
|
+
return (
|
|
632
|
+
<div key="findBorderBarSize" ref={this.findBorderBarSizeRef} className={this.getClassName(CLASSES.FLEXLAYOUT__BORDER_SIZER)}>
|
|
633
|
+
FindBorderBarSize
|
|
634
|
+
</div>
|
|
635
|
+
);
|
|
469
636
|
}
|
|
470
637
|
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
)
|
|
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;
|
|
643
|
+
|
|
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;
|
|
649
|
+
}
|
|
480
650
|
}
|
|
481
651
|
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
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;
|
|
662
|
+
}
|
|
663
|
+
}
|
|
489
664
|
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
};
|
|
495
|
-
this.props.model._setShowHiddenBorder(this.state.showHiddenBorder);
|
|
665
|
+
if (location !== this.state.showHiddenBorder) {
|
|
666
|
+
this.setState({ showHiddenBorder: location });
|
|
667
|
+
}
|
|
668
|
+
}
|
|
496
669
|
|
|
497
|
-
|
|
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
|
+
}
|
|
677
|
+
};
|
|
498
678
|
|
|
499
|
-
|
|
500
|
-
|
|
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
|
+
});
|
|
501
687
|
|
|
502
|
-
const
|
|
503
|
-
|
|
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
|
+
}
|
|
694
|
+
}
|
|
695
|
+
}
|
|
696
|
+
|
|
697
|
+
reorderComponents(components: Map<string, React.ReactNode>, ids: string[]) {
|
|
698
|
+
const nextIds: string[] = [];
|
|
699
|
+
const nextIdsSet = new Set<string>();
|
|
504
700
|
|
|
701
|
+
let reordered: React.ReactNode[] = [];
|
|
505
702
|
// Keep any previous tabs in the same DOM order as before, removing any that have been deleted
|
|
506
|
-
for (const
|
|
507
|
-
if (
|
|
508
|
-
|
|
509
|
-
|
|
703
|
+
for (const id of ids) {
|
|
704
|
+
if (components.get(id)) {
|
|
705
|
+
nextIds.push(id);
|
|
706
|
+
nextIdsSet.add(id);
|
|
510
707
|
}
|
|
511
708
|
}
|
|
512
|
-
|
|
709
|
+
ids.splice(0, ids.length, ...nextIds);
|
|
513
710
|
|
|
514
711
|
// Add tabs that have been added to the DOM
|
|
515
|
-
for (const
|
|
516
|
-
if (!
|
|
517
|
-
|
|
712
|
+
for (const [id, _] of components) {
|
|
713
|
+
if (!nextIdsSet.has(id)) {
|
|
714
|
+
ids.push(id);
|
|
518
715
|
}
|
|
519
716
|
}
|
|
520
717
|
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
edges.push(<div key="East" style={{ top: r.y + r.height / 2 - offset, left: r.x + r.width - width, width: width, height: length, borderTopLeftRadius: radius, borderBottomLeftRadius: radius }} className={className + " " + this.getClassName(CLASSES.FLEXLAYOUT__EDGE_RECT_RIGHT)}></div>);
|
|
718
|
+
reordered = ids.map((id) => {
|
|
719
|
+
return components.get(id);
|
|
720
|
+
});
|
|
721
|
+
|
|
722
|
+
return reordered;
|
|
723
|
+
}
|
|
724
|
+
|
|
725
|
+
onModelChange = (action: Action) => {
|
|
726
|
+
this.redrawInternal("model change");
|
|
727
|
+
if (this.props.onModelChange) {
|
|
728
|
+
this.props.onModelChange(this.props.model, action);
|
|
533
729
|
}
|
|
730
|
+
};
|
|
534
731
|
|
|
535
|
-
|
|
732
|
+
redraw(type?: string) {
|
|
733
|
+
// console.log("redraw", this.windowId, type);
|
|
734
|
+
this.mainLayout.setState((state, props) => { return { forceRevision: state.forceRevision + 1 } });
|
|
735
|
+
}
|
|
536
736
|
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
{this.tabIds.map((t) => {
|
|
541
|
-
return tabComponents[t];
|
|
542
|
-
})}
|
|
543
|
-
{borderComponents}
|
|
544
|
-
{splitterComponents}
|
|
545
|
-
{edges}
|
|
546
|
-
{floatingWindows}
|
|
547
|
-
{this.metricsElements()}
|
|
548
|
-
{this.state.portal}
|
|
549
|
-
</div>
|
|
550
|
-
);
|
|
737
|
+
redrawInternal(type: string) {
|
|
738
|
+
// console.log("redrawInternal", this.windowId, type);
|
|
739
|
+
this.mainLayout.setState((state, props) => { return { layoutRevision: state.layoutRevision + 1 } });
|
|
551
740
|
}
|
|
552
741
|
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
FindTabBarSize
|
|
564
|
-
</div>
|
|
565
|
-
<div key="findBorderBarSize" ref={this.findBorderBarSizeRef} style={fontStyle} className={this.getClassName(CLASSES.FLEXLAYOUT__BORDER_SIZER)}>
|
|
566
|
-
FindBorderBarSize
|
|
567
|
-
</div>
|
|
568
|
-
</React.Fragment>
|
|
569
|
-
);
|
|
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);
|
|
747
|
+
}
|
|
748
|
+
return undefined;
|
|
749
|
+
} else {
|
|
750
|
+
return this.props.model.doAction(action);
|
|
751
|
+
}
|
|
570
752
|
}
|
|
571
753
|
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
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
|
+
}
|
|
579
762
|
}
|
|
580
763
|
};
|
|
581
764
|
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
(
|
|
585
|
-
|
|
765
|
+
getBoundingClientRect(div: HTMLElement): Rect {
|
|
766
|
+
const layoutRect = this.getDomRect();
|
|
767
|
+
if (layoutRect) {
|
|
768
|
+
return Rect.getBoundingClientRect(div).relativeTo(layoutRect);
|
|
769
|
+
}
|
|
770
|
+
return Rect.empty();
|
|
771
|
+
}
|
|
586
772
|
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
iconFactory={this.props.iconFactory}
|
|
599
|
-
titleFactory={this.props.titleFactory}
|
|
600
|
-
icons={this.icons}
|
|
601
|
-
/>
|
|
602
|
-
);
|
|
603
|
-
const drawChildren = border._getDrawChildren();
|
|
604
|
-
let i = 0;
|
|
605
|
-
let tabCount = 0;
|
|
606
|
-
for (const child of drawChildren) {
|
|
607
|
-
if (child instanceof SplitterNode) {
|
|
608
|
-
let path = borderPath + "/s";
|
|
609
|
-
splitterComponents.push(<Splitter key={child.getId()} layout={this} node={child} path={path} />);
|
|
610
|
-
} else if (child instanceof TabNode) {
|
|
611
|
-
let path = borderPath + "/t" + tabCount++;
|
|
612
|
-
if (this.supportsPopout && child.isFloating()) {
|
|
613
|
-
const rect = this._getScreenRect(child);
|
|
614
|
-
|
|
615
|
-
const tabBorderWidth = child._getAttr("borderWidth");
|
|
616
|
-
const tabBorderHeight = child._getAttr("borderHeight");
|
|
617
|
-
if (rect) {
|
|
618
|
-
if (tabBorderWidth !== -1 && border.getLocation().getOrientation() === Orientation.HORZ) {
|
|
619
|
-
rect.width = tabBorderWidth;
|
|
620
|
-
} else if (tabBorderHeight !== -1 && border.getLocation().getOrientation() === Orientation.VERT) {
|
|
621
|
-
rect.height = tabBorderHeight;
|
|
622
|
-
}
|
|
623
|
-
}
|
|
624
|
-
|
|
625
|
-
floatingWindows.push(
|
|
626
|
-
<FloatingWindow
|
|
627
|
-
key={child.getId()}
|
|
628
|
-
url={this.popoutURL}
|
|
629
|
-
rect={rect}
|
|
630
|
-
title={child.getName()}
|
|
631
|
-
id={child.getId()}
|
|
632
|
-
onSetWindow={this.onSetWindow}
|
|
633
|
-
onCloseWindow={this.onCloseWindow}
|
|
634
|
-
>
|
|
635
|
-
<FloatingWindowTab layout={this} node={child} factory={this.props.factory} />
|
|
636
|
-
</FloatingWindow>
|
|
637
|
-
);
|
|
638
|
-
tabComponents[child.getId()] = <TabFloating key={child.getId()}
|
|
639
|
-
layout={this}
|
|
640
|
-
path={path}
|
|
641
|
-
node={child}
|
|
642
|
-
selected={i === border.getSelected()
|
|
643
|
-
} />;
|
|
644
|
-
} else {
|
|
645
|
-
tabComponents[child.getId()] = <Tab key={child.getId()}
|
|
646
|
-
layout={this}
|
|
647
|
-
path={path}
|
|
648
|
-
node={child}
|
|
649
|
-
selected={i === border.getSelected()}
|
|
650
|
-
factory={this.props.factory} />;
|
|
651
|
-
}
|
|
652
|
-
}
|
|
653
|
-
i++;
|
|
654
|
-
}
|
|
655
|
-
}
|
|
773
|
+
getMoveableContainer() {
|
|
774
|
+
return this.moveablesRef.current;
|
|
775
|
+
}
|
|
776
|
+
|
|
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);
|
|
656
784
|
}
|
|
785
|
+
return moveableElement;
|
|
657
786
|
}
|
|
658
787
|
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
const newPath = path + "/s" + (splitterCount++);
|
|
669
|
-
splitterComponents.push(<Splitter key={child.getId()} layout={this} path={newPath} node={child} />);
|
|
670
|
-
} else if (child instanceof TabSetNode) {
|
|
671
|
-
const newPath = path + "/ts" + (rowCount++);
|
|
672
|
-
tabSetComponents.push(<TabSet key={child.getId()} layout={this} path={newPath} node={child} iconFactory={this.props.iconFactory} titleFactory={this.props.titleFactory} icons={this.icons} />);
|
|
673
|
-
this.renderChildren(newPath, child, tabSetComponents, tabComponents, floatingWindows, splitterComponents);
|
|
674
|
-
} else if (child instanceof TabNode) {
|
|
675
|
-
const newPath = path + "/t" + (tabCount++);
|
|
676
|
-
const selectedTab = child.getParent()!.getChildren()[(child.getParent() as TabSetNode).getSelected()];
|
|
677
|
-
if (selectedTab === undefined) {
|
|
678
|
-
// this should not happen!
|
|
679
|
-
console.warn("undefined selectedTab should not happen");
|
|
680
|
-
}
|
|
681
|
-
if (this.supportsPopout && child.isFloating()) {
|
|
682
|
-
const rect = this._getScreenRect(child);
|
|
683
|
-
floatingWindows.push(
|
|
684
|
-
<FloatingWindow
|
|
685
|
-
key={child.getId()}
|
|
686
|
-
url={this.popoutURL}
|
|
687
|
-
rect={rect}
|
|
688
|
-
title={child.getName()}
|
|
689
|
-
id={child.getId()}
|
|
690
|
-
onSetWindow={this.onSetWindow}
|
|
691
|
-
onCloseWindow={this.onCloseWindow}
|
|
692
|
-
>
|
|
693
|
-
<FloatingWindowTab layout={this} node={child} factory={this.props.factory} />
|
|
694
|
-
</FloatingWindow>
|
|
695
|
-
);
|
|
696
|
-
tabComponents[child.getId()] = <TabFloating key={child.getId()} layout={this} path={newPath} node={child} selected={child === selectedTab} />;
|
|
697
|
-
} else {
|
|
698
|
-
tabComponents[child.getId()] = <Tab key={child.getId()} layout={this} path={newPath} node={child} selected={child === selectedTab} factory={this.props.factory} />;
|
|
699
|
-
}
|
|
700
|
-
} else {
|
|
701
|
-
// is row
|
|
702
|
-
const newPath = path + ((child.getOrientation() === Orientation.HORZ) ? "/r" : "/c") + (rowCount++);
|
|
703
|
-
this.renderChildren(newPath, child as RowNode, tabSetComponents, tabComponents, floatingWindows, splitterComponents);
|
|
704
|
-
}
|
|
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);
|
|
705
797
|
}
|
|
798
|
+
};
|
|
799
|
+
|
|
800
|
+
getCurrentDocument() {
|
|
801
|
+
return this.currentDocument;
|
|
706
802
|
}
|
|
707
803
|
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
if (!bodyRect) {
|
|
714
|
-
return null;
|
|
804
|
+
getDomRect() {
|
|
805
|
+
if (this.selfRef.current) {
|
|
806
|
+
return Rect.fromDomRect(this.selfRef.current.getBoundingClientRect());
|
|
807
|
+
} else {
|
|
808
|
+
return Rect.empty();
|
|
715
809
|
}
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
810
|
+
}
|
|
811
|
+
|
|
812
|
+
getWindowId() {
|
|
813
|
+
return this.windowId;
|
|
814
|
+
}
|
|
815
|
+
|
|
816
|
+
getRootDiv() {
|
|
817
|
+
return this.selfRef.current;
|
|
818
|
+
}
|
|
819
|
+
|
|
820
|
+
getMainElement() {
|
|
821
|
+
return this.mainRef.current;
|
|
822
|
+
}
|
|
823
|
+
|
|
824
|
+
getFactory() {
|
|
825
|
+
return this.props.factory;
|
|
826
|
+
}
|
|
827
|
+
|
|
828
|
+
isSupportsPopout() {
|
|
829
|
+
return this.supportsPopout;
|
|
830
|
+
}
|
|
831
|
+
|
|
832
|
+
isRealtimeResize() {
|
|
833
|
+
return this.props.realtimeResize ?? false;
|
|
834
|
+
}
|
|
835
|
+
|
|
836
|
+
getPopoutURL() {
|
|
837
|
+
return this.popoutURL;
|
|
838
|
+
}
|
|
839
|
+
|
|
840
|
+
setEditingTab(tabNode?: TabNode) {
|
|
841
|
+
this.setState({ editingTab: tabNode });
|
|
842
|
+
}
|
|
843
|
+
|
|
844
|
+
getEditingTab() {
|
|
845
|
+
return this.state.editingTab;
|
|
846
|
+
}
|
|
847
|
+
|
|
848
|
+
getModel() {
|
|
849
|
+
return this.props.model;
|
|
850
|
+
}
|
|
851
|
+
|
|
852
|
+
onCloseWindow = (windowLayout: LayoutWindow) => {
|
|
853
|
+
this.doAction(Actions.closeWindow(windowLayout.windowId));
|
|
854
|
+
};
|
|
855
|
+
|
|
856
|
+
onSetWindow = (windowLayout: LayoutWindow, window: Window) => {
|
|
857
|
+
};
|
|
858
|
+
|
|
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;
|
|
720
872
|
return rect;
|
|
721
873
|
}
|
|
722
874
|
|
|
723
|
-
|
|
724
|
-
* Adds a new tab to the given tabset
|
|
725
|
-
* @param tabsetId the id of the tabset where the new tab will be added
|
|
726
|
-
* @param json the json for the new tab node
|
|
727
|
-
* @returns the added tab node or undefined
|
|
728
|
-
*/
|
|
729
|
-
addTabToTabSet(tabsetId: string, json: IJsonTabNode) : TabNode | undefined {
|
|
875
|
+
addTabToTabSet(tabsetId: string, json: IJsonTabNode): TabNode | undefined {
|
|
730
876
|
const tabsetNode = this.props.model.getNodeById(tabsetId);
|
|
731
877
|
if (tabsetNode !== undefined) {
|
|
732
878
|
const node = this.doAction(Actions.addNode(json, tabsetId, DockLocation.CENTER, -1));
|
|
@@ -735,13 +881,8 @@ export class Layout extends React.Component<ILayoutProps, ILayoutState> {
|
|
|
735
881
|
return undefined;
|
|
736
882
|
}
|
|
737
883
|
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
* @param json the json for the new tab node
|
|
741
|
-
* @returns the added tab node or undefined
|
|
742
|
-
*/
|
|
743
|
-
addTabToActiveTabSet(json: IJsonTabNode) : TabNode | undefined {
|
|
744
|
-
const tabsetNode = this.props.model.getActiveTabset();
|
|
884
|
+
addTabToActiveTabSet(json: IJsonTabNode): TabNode | undefined {
|
|
885
|
+
const tabsetNode = this.props.model.getActiveTabset(this.windowId);
|
|
745
886
|
if (tabsetNode !== undefined) {
|
|
746
887
|
const node = this.doAction(Actions.addNode(json, tabsetNode.getId(), DockLocation.CENTER, -1));
|
|
747
888
|
return node as TabNode;
|
|
@@ -749,552 +890,435 @@ export class Layout extends React.Component<ILayoutProps, ILayoutState> {
|
|
|
749
890
|
return undefined;
|
|
750
891
|
}
|
|
751
892
|
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
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
|
+
|
|
906
|
+
maximize(tabsetNode: TabSetNode) {
|
|
907
|
+
this.doAction(Actions.maximizeToggle(tabsetNode.getId(), this.getWindowId()));
|
|
908
|
+
}
|
|
909
|
+
|
|
910
|
+
customizeTab(
|
|
911
|
+
tabNode: TabNode,
|
|
912
|
+
renderValues: ITabRenderValues,
|
|
913
|
+
) {
|
|
914
|
+
if (this.props.onRenderTab) {
|
|
915
|
+
this.props.onRenderTab(tabNode, renderValues);
|
|
916
|
+
}
|
|
917
|
+
}
|
|
918
|
+
|
|
919
|
+
customizeTabSet(
|
|
920
|
+
tabSetNode: TabSetNode | BorderNode,
|
|
921
|
+
renderValues: ITabSetRenderValues,
|
|
922
|
+
) {
|
|
923
|
+
if (this.props.onRenderTabSet) {
|
|
924
|
+
this.props.onRenderTabSet(tabSetNode, renderValues);
|
|
925
|
+
}
|
|
926
|
+
}
|
|
927
|
+
|
|
928
|
+
i18nName(id: I18nLabel, param?: string) {
|
|
929
|
+
let message;
|
|
930
|
+
if (this.props.i18nMapper) {
|
|
931
|
+
message = this.props.i18nMapper(id, param);
|
|
932
|
+
}
|
|
933
|
+
if (message === undefined) {
|
|
934
|
+
message = id + (param === undefined ? "" : param);
|
|
935
|
+
}
|
|
936
|
+
return message;
|
|
937
|
+
}
|
|
938
|
+
|
|
939
|
+
getShowOverflowMenu() {
|
|
940
|
+
return this.props.onShowOverflowMenu;
|
|
941
|
+
}
|
|
942
|
+
|
|
943
|
+
getTabSetPlaceHolderCallback() {
|
|
944
|
+
return this.props.onTabSetPlaceHolder;
|
|
762
945
|
}
|
|
763
946
|
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
*/
|
|
769
|
-
moveTabWithDragAndDrop(node: (TabNode | TabSetNode), dragText?: string) {
|
|
770
|
-
this.dragStart(undefined, dragText, node, true, undefined, undefined);
|
|
947
|
+
showContextMenu(node: TabNode | TabSetNode | BorderNode, event: React.MouseEvent<HTMLElement, MouseEvent>) {
|
|
948
|
+
if (this.props.onContextMenu) {
|
|
949
|
+
this.props.onContextMenu(node, event);
|
|
950
|
+
}
|
|
771
951
|
}
|
|
772
952
|
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
* @param json the json for the new tab node
|
|
779
|
-
* @param onDrop a callback to call when the drag is complete (node and event will be undefined if the drag was cancelled)
|
|
780
|
-
*/
|
|
781
|
-
addTabWithDragAndDropIndirect(dragText: string | undefined, json: IJsonTabNode, onDrop?: (node?: Node, event?: Event) => void) {
|
|
782
|
-
this.fnNewNodeDropped = onDrop;
|
|
783
|
-
this.newTabJson = json;
|
|
784
|
-
|
|
785
|
-
DragDrop.instance.addGlass(this.onCancelAdd);
|
|
786
|
-
|
|
787
|
-
this.dragDivText = dragText;
|
|
788
|
-
this.dragDiv = this.currentDocument!.createElement("div");
|
|
789
|
-
this.dragDiv.className = this.getClassName(CLASSES.FLEXLAYOUT__DRAG_RECT);
|
|
790
|
-
this.dragDiv.addEventListener("mousedown", this.onDragDivMouseDown);
|
|
791
|
-
this.dragDiv.addEventListener("touchstart", this.onDragDivMouseDown, { passive: false });
|
|
792
|
-
|
|
793
|
-
this.dragRectRender(this.dragDivText, undefined, this.newTabJson, () => {
|
|
794
|
-
if (this.dragDiv) {
|
|
795
|
-
// now it's been rendered into the dom it can be centered
|
|
796
|
-
this.dragDiv.style.visibility = "visible";
|
|
797
|
-
const domRect = this.dragDiv.getBoundingClientRect();
|
|
798
|
-
const r = new Rect(0, 0, domRect?.width, domRect?.height);
|
|
799
|
-
r.centerInRect(this.state.rect);
|
|
800
|
-
this.dragDiv.setAttribute("data-layout-path", "/drag-rectangle");
|
|
801
|
-
this.dragDiv.style.left = r.x + "px";
|
|
802
|
-
this.dragDiv.style.top = r.y + "px";
|
|
803
|
-
}
|
|
804
|
-
});
|
|
953
|
+
auxMouseClick(node: TabNode | TabSetNode | BorderNode, event: React.MouseEvent<HTMLElement, MouseEvent>) {
|
|
954
|
+
if (this.props.onAuxMouseClick) {
|
|
955
|
+
this.props.onAuxMouseClick(node, event);
|
|
956
|
+
}
|
|
957
|
+
}
|
|
805
958
|
|
|
806
|
-
|
|
807
|
-
|
|
959
|
+
public showOverlay(show: boolean) {
|
|
960
|
+
this.setState({ showOverlay: show });
|
|
961
|
+
enablePointerOnIFrames(!show, this.currentDocument!);
|
|
808
962
|
}
|
|
809
963
|
|
|
810
|
-
/** @internal */
|
|
811
|
-
onCancelAdd = () => {
|
|
812
|
-
const rootdiv = this.selfRef.current;
|
|
813
|
-
if (rootdiv && this.dragDiv) {
|
|
814
|
-
rootdiv.removeChild(this.dragDiv);
|
|
815
|
-
}
|
|
816
|
-
this.dragDiv = undefined;
|
|
817
|
-
this.hidePortal();
|
|
818
|
-
if (this.fnNewNodeDropped != null) {
|
|
819
|
-
this.fnNewNodeDropped();
|
|
820
|
-
this.fnNewNodeDropped = undefined;
|
|
821
|
-
}
|
|
822
964
|
|
|
823
|
-
try {
|
|
824
|
-
this.customDrop?.invalidated?.()
|
|
825
|
-
} catch (e) {
|
|
826
|
-
console.error(e)
|
|
827
|
-
}
|
|
828
965
|
|
|
829
|
-
|
|
830
|
-
this.newTabJson = undefined;
|
|
831
|
-
this.customDrop = undefined;
|
|
832
|
-
};
|
|
966
|
+
// *************************** Start Drag Drop *************************************
|
|
833
967
|
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
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
|
+
}
|
|
838
972
|
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
rootdiv.removeChild(outlineDiv);
|
|
843
|
-
} catch (e) {}
|
|
844
|
-
}
|
|
973
|
+
moveTabWithDragAndDrop(event: DragEvent, node: (TabNode | TabSetNode)) {
|
|
974
|
+
this.setDragNode(event, node);
|
|
975
|
+
}
|
|
845
976
|
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
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));
|
|
851
993
|
}
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
this.fnNewNodeDropped = undefined;
|
|
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
|
+
}
|
|
859
1000
|
}
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
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
|
+
}
|
|
865
1029
|
}
|
|
866
|
-
|
|
867
|
-
DragDrop.instance.hideGlass();
|
|
868
|
-
this.newTabJson = undefined;
|
|
869
|
-
this.customDrop = undefined;
|
|
870
1030
|
}
|
|
871
|
-
this.setState({ showHiddenBorder: DockLocation.CENTER });
|
|
872
|
-
|
|
873
1031
|
};
|
|
874
1032
|
|
|
875
|
-
/** @internal */
|
|
876
|
-
onDragDivMouseDown = (event: Event) => {
|
|
877
|
-
event.preventDefault();
|
|
878
|
-
this.dragStart(event, this.dragDivText, TabNode._fromJson(this.newTabJson, this.props.model, false), true, undefined, undefined);
|
|
879
|
-
};
|
|
880
1033
|
|
|
881
|
-
/** @internal */
|
|
882
|
-
dragStart = (
|
|
883
|
-
event: Event | React.MouseEvent<HTMLDivElement, MouseEvent> | React.TouchEvent<HTMLDivElement> | React.DragEvent<HTMLDivElement> | undefined,
|
|
884
|
-
dragDivText: string | undefined,
|
|
885
|
-
node: Node & IDraggable,
|
|
886
|
-
allowDrag: boolean,
|
|
887
|
-
onClick?: (event: Event) => void,
|
|
888
|
-
onDoubleClick?: (event: Event) => void
|
|
889
|
-
) => {
|
|
890
|
-
if (!allowDrag) {
|
|
891
|
-
DragDrop.instance.startDrag(
|
|
892
|
-
event,
|
|
893
|
-
undefined,
|
|
894
|
-
undefined,
|
|
895
|
-
undefined,
|
|
896
|
-
undefined,
|
|
897
|
-
onClick,
|
|
898
|
-
onDoubleClick,
|
|
899
|
-
this.currentDocument,
|
|
900
|
-
this.selfRef.current ?? undefined
|
|
901
|
-
);
|
|
902
|
-
} else {
|
|
903
|
-
this.dragNode = node;
|
|
904
|
-
this.dragDivText = dragDivText;
|
|
905
|
-
DragDrop.instance.startDrag(
|
|
906
|
-
event,
|
|
907
|
-
this.onDragStart,
|
|
908
|
-
this.onDragMove,
|
|
909
|
-
this.onDragEnd,
|
|
910
|
-
this.onCancelDrag,
|
|
911
|
-
onClick,
|
|
912
|
-
onDoubleClick,
|
|
913
|
-
this.currentDocument,
|
|
914
|
-
this.selfRef.current ?? undefined
|
|
915
|
-
);
|
|
916
|
-
}
|
|
917
|
-
};
|
|
918
1034
|
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
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
|
+
);
|
|
922
1042
|
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
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";
|
|
933
1062
|
}
|
|
934
|
-
}
|
|
935
1063
|
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
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
|
+
}
|
|
940
1071
|
}
|
|
941
|
-
}
|
|
942
1072
|
|
|
943
|
-
|
|
944
|
-
this.dragRectRendered = false;
|
|
945
|
-
const dragDiv = this.dragDiv;
|
|
946
|
-
if (dragDiv) {
|
|
947
|
-
dragDiv.style.visibility = "hidden";
|
|
948
|
-
this.showPortal(
|
|
949
|
-
<DragRectRenderWrapper
|
|
950
|
-
// wait for it to be rendered
|
|
951
|
-
onRendered={() => {
|
|
952
|
-
this.dragRectRendered = true;
|
|
953
|
-
onRendered?.();
|
|
954
|
-
}}>
|
|
955
|
-
{content}
|
|
956
|
-
</DragRectRenderWrapper>,
|
|
957
|
-
dragDiv,
|
|
958
|
-
);
|
|
1073
|
+
this.isDraggingOverWindow = overWindow;
|
|
959
1074
|
}
|
|
960
|
-
}
|
|
961
|
-
|
|
962
|
-
/** @internal */
|
|
963
|
-
showPortal = (control: React.ReactNode, element: HTMLElement) => {
|
|
964
|
-
const portal = createPortal(control, element) as React.ReactPortal;
|
|
965
|
-
this.setState({ portal });
|
|
966
|
-
};
|
|
967
|
-
|
|
968
|
-
/** @internal */
|
|
969
|
-
hidePortal = () => {
|
|
970
|
-
this.setState({ portal: undefined });
|
|
971
|
-
};
|
|
1075
|
+
}
|
|
972
1076
|
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
this.
|
|
976
|
-
|
|
977
|
-
const rootdiv = this.selfRef.current;
|
|
978
|
-
this.outlineDiv = this.currentDocument!.createElement("div");
|
|
979
|
-
this.outlineDiv.className = this.getClassName(CLASSES.FLEXLAYOUT__OUTLINE_RECT);
|
|
980
|
-
this.outlineDiv.style.visibility = "hidden";
|
|
981
|
-
if (rootdiv) {
|
|
982
|
-
rootdiv.appendChild(this.outlineDiv);
|
|
1077
|
+
onDragEnterRaw = (event: React.DragEvent<HTMLElement>) => {
|
|
1078
|
+
this.dragEnterCount++;
|
|
1079
|
+
if (this.dragEnterCount === 1) {
|
|
1080
|
+
this.onDragEnter(event);
|
|
983
1081
|
}
|
|
1082
|
+
}
|
|
984
1083
|
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
this.
|
|
989
|
-
|
|
1084
|
+
onDragLeaveRaw = (event: React.DragEvent<HTMLElement>) => {
|
|
1085
|
+
this.dragEnterCount--;
|
|
1086
|
+
if (this.dragEnterCount === 0) {
|
|
1087
|
+
this.onDragLeave(event);
|
|
1088
|
+
}
|
|
1089
|
+
}
|
|
990
1090
|
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
1091
|
+
clearDragMain() {
|
|
1092
|
+
// console.log("clear drag main");
|
|
1093
|
+
LayoutInternal.dragState = undefined;
|
|
1094
|
+
if (this.windowId === Model.MAIN_WINDOW_ID) {
|
|
1095
|
+
this.isDraggingOverWindow = false;
|
|
994
1096
|
}
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
1097
|
+
for (const [, layoutWindow] of this.props.model.getwindowsMap()) {
|
|
1098
|
+
// console.log(layoutWindow);
|
|
1099
|
+
layoutWindow.layout!.clearDragLocal();
|
|
998
1100
|
}
|
|
1101
|
+
}
|
|
999
1102
|
|
|
1000
|
-
|
|
1001
|
-
|
|
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;
|
|
1002
1112
|
}
|
|
1003
|
-
|
|
1113
|
+
}
|
|
1004
1114
|
|
|
1005
|
-
|
|
1006
|
-
|
|
1115
|
+
onDragEnter = (event: React.DragEvent<HTMLElement>) => {
|
|
1116
|
+
// console.log("onDragEnter", this.windowId, this.dragEnterCount);
|
|
1007
1117
|
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
this.outlineDiv.style.transition = `top ${speed}s, left ${speed}s, width ${speed}s, height ${speed}s`;
|
|
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);
|
|
1014
1123
|
}
|
|
1015
1124
|
}
|
|
1016
|
-
this.firstMove = false;
|
|
1017
|
-
const clientRect = this.selfRef.current?.getBoundingClientRect();
|
|
1018
|
-
const pos = {
|
|
1019
|
-
x: event.clientX - (clientRect?.left ?? 0),
|
|
1020
|
-
y: event.clientY - (clientRect?.top ?? 0),
|
|
1021
|
-
};
|
|
1022
|
-
|
|
1023
|
-
this.checkForBorderToShow(pos.x, pos.y);
|
|
1024
1125
|
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
if (newLeft + dragRect.width > (clientRect?.width ?? 0)) {
|
|
1029
|
-
newLeft = (clientRect?.width ?? 0) - dragRect.width;
|
|
1030
|
-
}
|
|
1031
|
-
newLeft = Math.max(0, newLeft);
|
|
1032
|
-
|
|
1033
|
-
if (this.dragDiv) {
|
|
1034
|
-
this.dragDiv.style.left = newLeft + "px";
|
|
1035
|
-
this.dragDiv.style.top = pos.y + 5 + "px";
|
|
1036
|
-
if (this.dragRectRendered && this.dragDiv.style.visibility === "hidden") {
|
|
1037
|
-
// make visible once the drag rect has been rendered
|
|
1038
|
-
this.dragDiv.style.visibility = "visible";
|
|
1126
|
+
if (LayoutInternal.dragState) {
|
|
1127
|
+
if (this.windowId !== Model.MAIN_WINDOW_ID && LayoutInternal.dragState.mainLayout === this.mainLayout) {
|
|
1128
|
+
LayoutInternal.dragState.mainLayout.setDraggingOverWindow(true);
|
|
1039
1129
|
}
|
|
1040
|
-
}
|
|
1041
1130
|
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
if (this.props.onTabDrag) {
|
|
1045
|
-
this.handleCustomTabDrag(dropInfo, pos, event);
|
|
1046
|
-
} else {
|
|
1047
|
-
this.dropInfo = dropInfo;
|
|
1048
|
-
if (this.outlineDiv) {
|
|
1049
|
-
this.outlineDiv.className = this.getClassName(dropInfo.className);
|
|
1050
|
-
dropInfo.rect.positionElement(this.outlineDiv);
|
|
1051
|
-
this.outlineDiv.style.visibility = "visible";
|
|
1052
|
-
}
|
|
1131
|
+
if (LayoutInternal.dragState.mainLayout !== this.mainLayout) {
|
|
1132
|
+
return; // drag not by this layout or its popouts
|
|
1053
1133
|
}
|
|
1054
|
-
}
|
|
1055
|
-
};
|
|
1056
1134
|
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
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() });
|
|
1066
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);
|
|
1067
1161
|
}
|
|
1068
|
-
|
|
1069
|
-
this.hidePortal();
|
|
1162
|
+
}
|
|
1070
1163
|
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
if (this.dropInfo) {
|
|
1075
|
-
if (this.customDrop) {
|
|
1076
|
-
this.newTabJson = undefined;
|
|
1077
|
-
|
|
1078
|
-
try {
|
|
1079
|
-
const { callback, dragging, over, x, y, location } = this.customDrop;
|
|
1080
|
-
callback(dragging, over, x, y, location);
|
|
1081
|
-
if (this.fnNewNodeDropped != null) {
|
|
1082
|
-
this.fnNewNodeDropped();
|
|
1083
|
-
this.fnNewNodeDropped = undefined;
|
|
1084
|
-
}
|
|
1085
|
-
} catch (e) {
|
|
1086
|
-
console.error(e)
|
|
1087
|
-
}
|
|
1088
|
-
} else if (this.newTabJson !== undefined) {
|
|
1089
|
-
const newNode = this.doAction(Actions.addNode(this.newTabJson, this.dropInfo.node.getId(), this.dropInfo.location, this.dropInfo.index));
|
|
1164
|
+
onDragOver = (event: React.DragEvent<HTMLElement>) => {
|
|
1165
|
+
if (this.dragging && !this.isDraggingOverWindow) {
|
|
1166
|
+
// console.log("onDragOver");
|
|
1090
1167
|
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
}
|
|
1097
|
-
this.doAction(Actions.moveNode(this.dragNode.getId(), this.dropInfo.node.getId(), this.dropInfo.location, this.dropInfo.index));
|
|
1098
|
-
}
|
|
1099
|
-
}
|
|
1100
|
-
this.setState({ showHiddenBorder: DockLocation.CENTER });
|
|
1101
|
-
};
|
|
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
|
+
};
|
|
1102
1174
|
|
|
1103
|
-
|
|
1104
|
-
private handleCustomTabDrag(dropInfo: DropInfo, pos: { x: number; y: number; }, event: React.MouseEvent<Element, MouseEvent>) {
|
|
1105
|
-
let invalidated = this.customDrop?.invalidated;
|
|
1106
|
-
const currentCallback = this.customDrop?.callback;
|
|
1107
|
-
this.customDrop = undefined;
|
|
1108
|
-
|
|
1109
|
-
const dragging = this.newTabJson || (this.dragNode instanceof TabNode ? this.dragNode : undefined);
|
|
1110
|
-
if (dragging && (dropInfo.node instanceof TabSetNode || dropInfo.node instanceof BorderNode) && dropInfo.index === -1) {
|
|
1111
|
-
const selected = dropInfo.node.getSelectedNode() as TabNode | undefined;
|
|
1112
|
-
const tabRect = selected?.getRect();
|
|
1113
|
-
|
|
1114
|
-
if (selected && tabRect?.contains(pos.x, pos.y)) {
|
|
1115
|
-
let customDrop: ICustomDropDestination | undefined = undefined;
|
|
1116
|
-
|
|
1117
|
-
try {
|
|
1118
|
-
const dest = this.onTabDrag(dragging, selected, pos.x - tabRect.x, pos.y - tabRect.y, dropInfo.location, () => this.onDragMove(event));
|
|
1119
|
-
|
|
1120
|
-
if (dest) {
|
|
1121
|
-
customDrop = {
|
|
1122
|
-
rect: new Rect(dest.x + tabRect.x, dest.y + tabRect.y, dest.width, dest.height),
|
|
1123
|
-
callback: dest.callback,
|
|
1124
|
-
invalidated: dest.invalidated,
|
|
1125
|
-
dragging: dragging,
|
|
1126
|
-
over: selected,
|
|
1127
|
-
x: pos.x - tabRect.x,
|
|
1128
|
-
y: pos.y - tabRect.y,
|
|
1129
|
-
location: dropInfo.location,
|
|
1130
|
-
cursor: dest.cursor
|
|
1131
|
-
};
|
|
1132
|
-
}
|
|
1133
|
-
} catch (e) {
|
|
1134
|
-
console.error(e);
|
|
1135
|
-
}
|
|
1175
|
+
this.checkForBorderToShow(pos.x, pos.y);
|
|
1136
1176
|
|
|
1137
|
-
|
|
1138
|
-
|
|
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";
|
|
1139
1184
|
}
|
|
1140
|
-
|
|
1141
|
-
this.customDrop = customDrop;
|
|
1142
1185
|
}
|
|
1143
1186
|
}
|
|
1187
|
+
}
|
|
1144
1188
|
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
if (this.
|
|
1149
|
-
|
|
1150
|
-
} else {
|
|
1151
|
-
dropInfo.rect.positionElement(this.outlineDiv);
|
|
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);
|
|
1152
1194
|
}
|
|
1153
|
-
}
|
|
1154
|
-
|
|
1155
|
-
DragDrop.instance.setGlassCursorOverride(this.customDrop?.cursor);
|
|
1156
|
-
if (this.outlineDiv) {
|
|
1157
|
-
this.outlineDiv.style.visibility = "visible";
|
|
1158
|
-
}
|
|
1159
1195
|
|
|
1160
|
-
|
|
1161
|
-
invalidated?.();
|
|
1162
|
-
} catch (e) {
|
|
1163
|
-
console.error(e);
|
|
1164
|
-
}
|
|
1165
|
-
}
|
|
1166
|
-
|
|
1167
|
-
/** @internal */
|
|
1168
|
-
onDragEnter(event: React.DragEvent<HTMLDivElement>) {
|
|
1169
|
-
// DragDrop keeps track of number of dragenters minus the number of
|
|
1170
|
-
// dragleaves. Only start a new drag if there isn't one already.
|
|
1171
|
-
if (DragDrop.instance.isDragging())
|
|
1172
|
-
return;
|
|
1173
|
-
const drag = this.props.onExternalDrag!(event);
|
|
1174
|
-
if (drag) {
|
|
1175
|
-
// Mimic addTabWithDragAndDrop, but pass in DragEvent
|
|
1176
|
-
this.fnNewNodeDropped = drag.onDrop;
|
|
1177
|
-
this.newTabJson = drag.json;
|
|
1178
|
-
this.dragStart(event, drag.dragText, TabNode._fromJson(drag.json, this.props.model, false), true, undefined, undefined);
|
|
1196
|
+
this.clearDragLocal();
|
|
1179
1197
|
}
|
|
1180
1198
|
}
|
|
1181
1199
|
|
|
1200
|
+
onDrop = (event: React.DragEvent<HTMLElement>) => {
|
|
1201
|
+
// console.log("ondrop", this.windowId, this.dragging, Layout.dragState);
|
|
1182
1202
|
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
const r = this.props.model._getOuterInnerRects().outer;
|
|
1186
|
-
const c = r.getCenter();
|
|
1187
|
-
const margin = this.edgeRectWidth;
|
|
1188
|
-
const offset = this.edgeRectLength / 2;
|
|
1203
|
+
if (this.dragging) {
|
|
1204
|
+
event.preventDefault();
|
|
1189
1205
|
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
overEdge = true;
|
|
1195
|
-
}
|
|
1196
|
-
}
|
|
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));
|
|
1197
1210
|
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
} else if (y <= r.y + margin) {
|
|
1205
|
-
location = DockLocation.TOP;
|
|
1206
|
-
} else if (y >= r.getBottom() - margin) {
|
|
1207
|
-
location = DockLocation.BOTTOM;
|
|
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
|
+
}
|
|
1208
1217
|
}
|
|
1209
|
-
}
|
|
1210
1218
|
|
|
1211
|
-
|
|
1212
|
-
this.setState({ showHiddenBorder: location });
|
|
1219
|
+
this.mainLayout.clearDragMain();
|
|
1213
1220
|
}
|
|
1221
|
+
this.dragEnterCount = 0; // must set to zero here ref sublayouts
|
|
1214
1222
|
}
|
|
1215
1223
|
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
this.doAction(Actions.maximizeToggle(tabsetNode.getId()));
|
|
1219
|
-
}
|
|
1224
|
+
// *************************** End Drag Drop *************************************
|
|
1225
|
+
}
|
|
1220
1226
|
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
if (this.props.onRenderTab) {
|
|
1227
|
-
this.props.onRenderTab(tabNode, renderValues);
|
|
1228
|
-
}
|
|
1229
|
-
}
|
|
1227
|
+
export type DragRectRenderCallback = (
|
|
1228
|
+
content: React.ReactNode | undefined,
|
|
1229
|
+
node?: Node,
|
|
1230
|
+
json?: IJsonTabNode
|
|
1231
|
+
) => React.ReactNode | undefined;
|
|
1230
1232
|
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
) {
|
|
1236
|
-
if (this.props.onRenderTabSet) {
|
|
1237
|
-
this.props.onRenderTabSet(tabSetNode, renderValues);
|
|
1238
|
-
}
|
|
1239
|
-
}
|
|
1233
|
+
export type NodeMouseEvent = (
|
|
1234
|
+
node: TabNode | TabSetNode | BorderNode,
|
|
1235
|
+
event: React.MouseEvent<HTMLElement, MouseEvent>
|
|
1236
|
+
) => void;
|
|
1240
1237
|
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
if (message === undefined) {
|
|
1248
|
-
message = id + (param === undefined ? "" : param);
|
|
1249
|
-
}
|
|
1250
|
-
return message;
|
|
1251
|
-
}
|
|
1238
|
+
export type ShowOverflowMenuCallback = (
|
|
1239
|
+
node: TabSetNode | BorderNode,
|
|
1240
|
+
mouseEvent: React.MouseEvent<HTMLElement, MouseEvent>,
|
|
1241
|
+
items: { index: number; node: TabNode }[],
|
|
1242
|
+
onSelect: (item: { index: number; node: TabNode }) => void,
|
|
1243
|
+
) => void;
|
|
1252
1244
|
|
|
1253
|
-
|
|
1254
|
-
getOnRenderFloatingTabPlaceholder() {
|
|
1255
|
-
return this.props.onRenderFloatingTabPlaceholder;
|
|
1256
|
-
}
|
|
1245
|
+
export type TabSetPlaceHolderCallback = (node: TabSetNode) => React.ReactNode;
|
|
1257
1246
|
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1247
|
+
export interface ITabSetRenderValues {
|
|
1248
|
+
/** components that will be added after the tabs */
|
|
1249
|
+
stickyButtons: React.ReactNode[];
|
|
1250
|
+
/** components that will be added at the end of the tabset */
|
|
1251
|
+
buttons: React.ReactNode[];
|
|
1252
|
+
/** position to insert overflow button within [...stickyButtons, ...buttons]
|
|
1253
|
+
* if left undefined position will be after the sticky buttons (if any)
|
|
1254
|
+
*/
|
|
1255
|
+
overflowPosition: number | undefined;
|
|
1256
|
+
}
|
|
1262
1257
|
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
}
|
|
1272
|
-
}
|
|
1258
|
+
export interface ITabRenderValues {
|
|
1259
|
+
/** the icon or other leading component */
|
|
1260
|
+
leading: React.ReactNode;
|
|
1261
|
+
/** the main tab text/component */
|
|
1262
|
+
content: React.ReactNode;
|
|
1263
|
+
/** a set of react components to add to the tab after the content */
|
|
1264
|
+
buttons: React.ReactNode[];
|
|
1265
|
+
}
|
|
1273
1266
|
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1267
|
+
export interface IIcons {
|
|
1268
|
+
close?: (React.ReactNode | ((tabNode: TabNode) => React.ReactNode));
|
|
1269
|
+
closeTabset?: (React.ReactNode | ((tabSetNode: TabSetNode) => React.ReactNode));
|
|
1270
|
+
popout?: (React.ReactNode | ((tabNode: TabNode) => React.ReactNode));
|
|
1271
|
+
maximize?: (React.ReactNode | ((tabSetNode: TabSetNode) => React.ReactNode));
|
|
1272
|
+
restore?: (React.ReactNode | ((tabSetNode: TabSetNode) => React.ReactNode));
|
|
1273
|
+
more?: (React.ReactNode | ((tabSetNode: (TabSetNode | BorderNode), hiddenTabs: { node: TabNode; index: number }[]) => React.ReactNode));
|
|
1274
|
+
edgeArrow?: React.ReactNode;
|
|
1275
|
+
activeTabset?: (React.ReactNode | ((tabSetNode: TabSetNode) => React.ReactNode));
|
|
1280
1276
|
}
|
|
1281
1277
|
|
|
1282
|
-
|
|
1283
|
-
|
|
1278
|
+
const defaultIcons = {
|
|
1279
|
+
close: <CloseIcon />,
|
|
1280
|
+
closeTabset: <CloseIcon />,
|
|
1281
|
+
popout: <PopoutIcon />,
|
|
1282
|
+
maximize: <MaximizeIcon />,
|
|
1283
|
+
restore: <RestoreIcon />,
|
|
1284
|
+
more: <OverflowIcon />,
|
|
1285
|
+
edgeArrow: <EdgeIcon />,
|
|
1286
|
+
activeTabset: <AsterickIcon />
|
|
1287
|
+
};
|
|
1288
|
+
|
|
1289
|
+
enum DragSource {
|
|
1290
|
+
Internal = "internal",
|
|
1291
|
+
External = "external",
|
|
1292
|
+
Add = "add"
|
|
1293
|
+
}
|
|
1284
1294
|
|
|
1285
1295
|
/** @internal */
|
|
1286
|
-
|
|
1287
|
-
onRendered?: () => void;
|
|
1288
|
-
children: React.ReactNode;
|
|
1289
|
-
}
|
|
1296
|
+
const defaultSupportsPopout: boolean = isDesktop();
|
|
1290
1297
|
|
|
1291
1298
|
/** @internal */
|
|
1292
|
-
const
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1299
|
+
const edgeRectLength = 100;
|
|
1300
|
+
/** @internal */
|
|
1301
|
+
const edgeRectWidth = 10;
|
|
1302
|
+
|
|
1303
|
+
// global layout drag state
|
|
1304
|
+
class DragState {
|
|
1305
|
+
public readonly mainLayout: LayoutInternal;
|
|
1306
|
+
public readonly dragSource: DragSource;
|
|
1307
|
+
public readonly dragNode: Node & IDraggable | undefined;
|
|
1308
|
+
public readonly dragJson: IJsonTabNode | undefined;
|
|
1309
|
+
public readonly fnNewNodeDropped: ((node?: Node, event?: React.DragEvent<HTMLElement>) => void) | undefined;
|
|
1310
|
+
|
|
1311
|
+
public constructor(
|
|
1312
|
+
mainLayout: LayoutInternal,
|
|
1313
|
+
dragSource: DragSource,
|
|
1314
|
+
dragNode: Node & IDraggable | undefined,
|
|
1315
|
+
dragJson: IJsonTabNode | undefined,
|
|
1316
|
+
fnNewNodeDropped: ((node?: Node, event?: React.DragEvent<HTMLElement>) => void) | undefined
|
|
1317
|
+
) {
|
|
1318
|
+
this.mainLayout = mainLayout;
|
|
1319
|
+
this.dragSource = dragSource;
|
|
1320
|
+
this.dragNode = dragNode;
|
|
1321
|
+
this.dragJson = dragJson;
|
|
1322
|
+
this.fnNewNodeDropped = fnNewNodeDropped;
|
|
1323
|
+
}
|
|
1324
|
+
}
|