flexlayout-react 0.5.20 → 0.6.2
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 +22 -0
- package/README.md +9 -13
- package/declarations/Types.d.ts +2 -0
- package/declarations/model/IJsonModel.d.ts +3 -0
- package/declarations/model/Model.d.ts +2 -0
- package/declarations/view/Icons.d.ts +6 -0
- package/declarations/view/Layout.d.ts +23 -10
- package/declarations/view/MenuTabButton.d.ts +1 -0
- package/declarations/view/TabButtonStamp.d.ts +1 -0
- package/declarations/view/Utils.d.ts +1 -0
- package/dist/flexlayout.js +49 -13
- package/dist/flexlayout_min.js +1 -1
- package/lib/PopupMenu.js +10 -6
- package/lib/PopupMenu.js.map +1 -1
- package/lib/Types.js +2 -0
- package/lib/Types.js.map +1 -1
- package/lib/model/Model.js +8 -0
- package/lib/model/Model.js.map +1 -1
- package/lib/model/TabNode.js +6 -1
- package/lib/model/TabNode.js.map +1 -1
- package/lib/view/BorderButton.js +9 -39
- package/lib/view/BorderButton.js.map +1 -1
- package/lib/view/BorderTabSet.js +21 -8
- package/lib/view/BorderTabSet.js.map +1 -1
- package/lib/view/FloatingWindow.js +13 -5
- package/lib/view/FloatingWindow.js.map +1 -1
- package/lib/view/Icons.js +36 -0
- package/lib/view/Icons.js.map +1 -0
- package/lib/view/Layout.js +58 -13
- package/lib/view/Layout.js.map +1 -1
- package/lib/view/MenuTabButton.js +22 -0
- package/lib/view/MenuTabButton.js.map +1 -0
- package/lib/view/Tab.js +7 -4
- package/lib/view/Tab.js.map +1 -1
- package/lib/view/TabButton.js +13 -46
- package/lib/view/TabButton.js.map +1 -1
- package/lib/view/TabButtonStamp.js +22 -0
- package/lib/view/TabButtonStamp.js.map +1 -0
- package/lib/view/TabFloating.js +5 -3
- package/lib/view/TabFloating.js.map +1 -1
- package/lib/view/TabOverflowHook.js +3 -1
- package/lib/view/TabOverflowHook.js.map +1 -1
- package/lib/view/TabSet.js +29 -24
- package/lib/view/TabSet.js.map +1 -1
- package/lib/view/Utils.js +61 -0
- package/lib/view/Utils.js.map +1 -0
- package/package.json +3 -3
- package/src/I18nLabel.ts +1 -1
- package/src/PopupMenu.tsx +25 -8
- package/src/Types.ts +2 -0
- package/src/model/IJsonModel.ts +3 -0
- package/src/model/Model.ts +12 -0
- package/src/model/TabNode.ts +6 -1
- package/src/view/BorderButton.tsx +8 -41
- package/src/view/BorderTabSet.tsx +28 -8
- package/src/view/FloatingWindow.tsx +14 -6
- package/src/view/Icons.tsx +36 -0
- package/src/view/Layout.tsx +88 -34
- package/src/view/Tab.tsx +9 -4
- package/src/view/TabButton.tsx +18 -55
- package/src/view/TabButtonStamp.tsx +47 -0
- package/src/view/TabFloating.tsx +5 -3
- package/src/view/TabOverflowHook.tsx +3 -2
- package/src/view/TabSet.tsx +33 -21
- package/src/view/Utils.tsx +71 -0
- package/style/_base.scss +81 -46
- package/style/dark.css +79 -68
- package/style/dark.css.map +1 -1
- package/style/dark.scss +15 -5
- package/style/gray.css +76 -65
- package/style/gray.css.map +1 -1
- package/style/gray.scss +10 -3
- package/style/light.css +80 -69
- package/style/light.css.map +1 -1
- package/style/light.scss +16 -6
- package/images/close.png +0 -0
- package/images/maximize.png +0 -0
- package/images/more.png +0 -0
- package/images/more2.png +0 -0
- package/images/popout.png +0 -0
- package/images/restore.png +0 -0
|
@@ -10,7 +10,7 @@ import { I18nLabel } from "../I18nLabel";
|
|
|
10
10
|
import { useTabOverflow } from "./TabOverflowHook";
|
|
11
11
|
import Orientation from "../Orientation";
|
|
12
12
|
import { CLASSES } from "../Types";
|
|
13
|
-
import { isAuxMouseEvent } from "./
|
|
13
|
+
import { isAuxMouseEvent } from "./Utils";
|
|
14
14
|
|
|
15
15
|
/** @hidden @internal */
|
|
16
16
|
export interface IBorderTabSetProps {
|
|
@@ -18,7 +18,7 @@ export interface IBorderTabSetProps {
|
|
|
18
18
|
layout: ILayoutCallbacks;
|
|
19
19
|
iconFactory?: (node: TabNode) => React.ReactNode | undefined;
|
|
20
20
|
titleFactory?: (node: TabNode) => React.ReactNode | undefined;
|
|
21
|
-
icons
|
|
21
|
+
icons: IIcons;
|
|
22
22
|
path: string;
|
|
23
23
|
}
|
|
24
24
|
|
|
@@ -47,8 +47,18 @@ export const BorderTabSet = (props: IBorderTabSetProps) => {
|
|
|
47
47
|
};
|
|
48
48
|
|
|
49
49
|
const onOverflowClick = (event: React.MouseEvent<HTMLElement, MouseEvent>) => {
|
|
50
|
-
const
|
|
51
|
-
|
|
50
|
+
const callback = layout.getShowOverflowMenu();
|
|
51
|
+
if (callback !== undefined) {
|
|
52
|
+
callback( border, event, hiddenTabs, onOverflowItemSelect);
|
|
53
|
+
} else {
|
|
54
|
+
const element = overflowbuttonRef.current!;
|
|
55
|
+
showPopup( element,
|
|
56
|
+
hiddenTabs,
|
|
57
|
+
onOverflowItemSelect,
|
|
58
|
+
layout,
|
|
59
|
+
iconFactory,
|
|
60
|
+
titleFactory);
|
|
61
|
+
}
|
|
52
62
|
event.stopPropagation();
|
|
53
63
|
};
|
|
54
64
|
|
|
@@ -108,18 +118,26 @@ export const BorderTabSet = (props: IBorderTabSetProps) => {
|
|
|
108
118
|
|
|
109
119
|
if (hiddenTabs.length > 0) {
|
|
110
120
|
const overflowTitle = layout.i18nName(I18nLabel.Overflow_Menu_Tooltip);
|
|
121
|
+
let overflowContent;
|
|
122
|
+
if (typeof icons.more === "function") {
|
|
123
|
+
overflowContent = icons.more(border, hiddenTabs);
|
|
124
|
+
} else {
|
|
125
|
+
overflowContent = (<>
|
|
126
|
+
{icons.more}
|
|
127
|
+
<div className={cm(CLASSES.FLEXLAYOUT__TAB_BUTTON_OVERFLOW_COUNT)}>{hiddenTabs.length}</div>
|
|
128
|
+
</>);
|
|
129
|
+
}
|
|
111
130
|
buttons.push(
|
|
112
131
|
<button
|
|
113
132
|
key="overflowbutton"
|
|
114
133
|
ref={overflowbuttonRef}
|
|
115
|
-
className={cm(CLASSES.FLEXLAYOUT__BORDER_TOOLBAR_BUTTON_OVERFLOW) + " " + cm(CLASSES.FLEXLAYOUT__BORDER_TOOLBAR_BUTTON_OVERFLOW_ + border.getLocation().getName())}
|
|
134
|
+
className={cm(CLASSES.FLEXLAYOUT__BORDER_TOOLBAR_BUTTON) + " " + cm(CLASSES.FLEXLAYOUT__BORDER_TOOLBAR_BUTTON_OVERFLOW) + " " + cm(CLASSES.FLEXLAYOUT__BORDER_TOOLBAR_BUTTON_OVERFLOW_ + border.getLocation().getName())}
|
|
116
135
|
title={overflowTitle}
|
|
117
136
|
onClick={onOverflowClick}
|
|
118
137
|
onMouseDown={onInterceptMouseDown}
|
|
119
138
|
onTouchStart={onInterceptMouseDown}
|
|
120
139
|
>
|
|
121
|
-
{
|
|
122
|
-
{hiddenTabs.length}
|
|
140
|
+
{overflowContent}
|
|
123
141
|
</button>
|
|
124
142
|
);
|
|
125
143
|
}
|
|
@@ -137,7 +155,9 @@ export const BorderTabSet = (props: IBorderTabSetProps) => {
|
|
|
137
155
|
onClick={onFloatTab}
|
|
138
156
|
onMouseDown={onInterceptMouseDown}
|
|
139
157
|
onTouchStart={onInterceptMouseDown}
|
|
140
|
-
|
|
158
|
+
>
|
|
159
|
+
{(typeof icons.popout === "function") ? icons.popout(selectedTabNode) : icons.popout}
|
|
160
|
+
</button>
|
|
141
161
|
);
|
|
142
162
|
}
|
|
143
163
|
}
|
|
@@ -16,7 +16,7 @@ export interface IFloatingWindowProps {
|
|
|
16
16
|
interface IStyleSheet {
|
|
17
17
|
href: string | null;
|
|
18
18
|
type: string;
|
|
19
|
-
rules: string[];
|
|
19
|
+
rules: string[] | null;
|
|
20
20
|
}
|
|
21
21
|
|
|
22
22
|
/** @hidden @internal */
|
|
@@ -31,17 +31,23 @@ export const FloatingWindow = (props: React.PropsWithChildren<IFloatingWindowPro
|
|
|
31
31
|
// the floating window. window.document.styleSheets is mutable and we can't guarantee
|
|
32
32
|
// the styles will exist when 'popoutWindow.load' is called below.
|
|
33
33
|
const styles = Array.from(window.document.styleSheets).reduce((result, styleSheet) => {
|
|
34
|
+
let rules: CSSRuleList | undefined = undefined;
|
|
35
|
+
try {
|
|
36
|
+
rules = styleSheet.cssRules;
|
|
37
|
+
} catch (e) {
|
|
38
|
+
// styleSheet.cssRules can throw security exception
|
|
39
|
+
}
|
|
40
|
+
|
|
34
41
|
try {
|
|
35
42
|
return [
|
|
36
43
|
...result,
|
|
37
44
|
{
|
|
38
45
|
href: styleSheet.href,
|
|
39
46
|
type: styleSheet.type,
|
|
40
|
-
rules: Array.from(
|
|
47
|
+
rules: rules ? Array.from(rules).map(rule => rule.cssText) : null,
|
|
41
48
|
}
|
|
42
49
|
];
|
|
43
50
|
} catch (e) {
|
|
44
|
-
// styleSheet.cssRules can throw security exception
|
|
45
51
|
return result;
|
|
46
52
|
}
|
|
47
53
|
}, [] as IStyleSheet[]);
|
|
@@ -113,9 +119,11 @@ function copyStyles(doc: Document, styleSheets: IStyleSheet[]): Promise<boolean[
|
|
|
113
119
|
})
|
|
114
120
|
);
|
|
115
121
|
} else {
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
122
|
+
if (styleSheet.rules) {
|
|
123
|
+
const style = doc.createElement("style");
|
|
124
|
+
styleSheet.rules.forEach(rule => style.appendChild(doc.createTextNode(rule)));
|
|
125
|
+
head.appendChild(style);
|
|
126
|
+
}
|
|
119
127
|
}
|
|
120
128
|
});
|
|
121
129
|
return Promise.all(promises);
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
|
|
3
|
+
const style = {width:"1em", height:"1em", display: "flex", alignItems:"center"};
|
|
4
|
+
|
|
5
|
+
export const CloseIcon = () => {
|
|
6
|
+
return (
|
|
7
|
+
<svg xmlns="http://www.w3.org/2000/svg" style={style} viewBox="0 0 24 24" >
|
|
8
|
+
<path fill="none" d="M0 0h24v24H0z"/>
|
|
9
|
+
<path stroke="gray" fill="gray" d="M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z" />
|
|
10
|
+
</svg>
|
|
11
|
+
);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export const MaximizeIcon = () => {
|
|
15
|
+
return (
|
|
16
|
+
<svg xmlns="http://www.w3.org/2000/svg" style={style} viewBox="0 0 24 24" fill="gray"><path d="M0 0h24v24H0z" fill="none"/><path stroke="gray" d="M7 14H5v5h5v-2H7v-3zm-2-4h2V7h3V5H5v5zm12 7h-3v2h5v-5h-2v3zM14 5v2h3v3h2V5h-5z"/></svg>
|
|
17
|
+
);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export const OverflowIcon = () => {
|
|
21
|
+
return (
|
|
22
|
+
<svg xmlns="http://www.w3.org/2000/svg" style={style} viewBox="0 0 24 24" fill="gray"><path d="M0 0h24v24H0z" fill="none"/><path stroke="gray" d="M7 10l5 5 5-5z"/></svg>
|
|
23
|
+
);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export const PopoutIcon = () => {
|
|
27
|
+
return (
|
|
28
|
+
<svg xmlns="http://www.w3.org/2000/svg" style={style} viewBox="0 0 24 24" fill="gray"><path d="M0 0h24v24H0z" fill="none"/><path stroke="gray" d="M9 5v2h6.59L4 18.59 5.41 20 17 8.41V15h2V5z"/></svg>
|
|
29
|
+
);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export const RestoreIcon = () => {
|
|
33
|
+
return (
|
|
34
|
+
<svg xmlns="http://www.w3.org/2000/svg" style={style} viewBox="0 0 24 24" fill="gray"><path d="M0 0h24v24H0z" fill="none"/><path stroke="gray" d="M5 16h3v3h2v-5H5v2zm3-8H5v2h5V5H8v3zm6 11h2v-3h3v-2h-5v5zm2-11V5h-2v5h5V8h-3z"/></svg>
|
|
35
|
+
);
|
|
36
|
+
}
|
package/src/view/Layout.tsx
CHANGED
|
@@ -26,11 +26,19 @@ import { FloatingWindowTab } from "./FloatingWindowTab";
|
|
|
26
26
|
import { TabFloating } from "./TabFloating";
|
|
27
27
|
import { IJsonTabNode } from "../model/IJsonModel";
|
|
28
28
|
import { Orientation } from "..";
|
|
29
|
+
import { CloseIcon, MaximizeIcon, OverflowIcon, PopoutIcon, RestoreIcon } from "./Icons";
|
|
30
|
+
import { TabButtonStamp } from "./TabButtonStamp";
|
|
29
31
|
|
|
30
32
|
export type CustomDragCallback = (dragging: TabNode | IJsonTabNode, over: TabNode, x: number, y: number, location: DockLocation) => void;
|
|
31
|
-
export type DragRectRenderCallback = (
|
|
33
|
+
export type DragRectRenderCallback = (content: React.ReactElement | undefined, node?: Node, json?: IJsonTabNode) => React.ReactElement | undefined;
|
|
32
34
|
export type FloatingTabPlaceholderRenderCallback = (dockPopout: () => void, showPopout: () => void) => React.ReactElement | undefined;
|
|
33
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;
|
|
34
42
|
|
|
35
43
|
export interface ILayoutProps {
|
|
36
44
|
model: Model;
|
|
@@ -39,7 +47,6 @@ export interface ILayoutProps {
|
|
|
39
47
|
fontFamily?: string;
|
|
40
48
|
iconFactory?: (node: TabNode) => React.ReactNode | undefined;
|
|
41
49
|
titleFactory?: (node: TabNode) => ITitleObject | React.ReactNode | undefined;
|
|
42
|
-
closeIcon?: React.ReactNode;
|
|
43
50
|
icons?: IIcons;
|
|
44
51
|
onAction?: (action: Action) => Action | undefined;
|
|
45
52
|
onRenderTab?: (
|
|
@@ -75,6 +82,7 @@ export interface ILayoutProps {
|
|
|
75
82
|
onRenderFloatingTabPlaceholder?: FloatingTabPlaceholderRenderCallback;
|
|
76
83
|
onContextMenu?: NodeMouseEvent;
|
|
77
84
|
onAuxMouseClick?: NodeMouseEvent;
|
|
85
|
+
onShowOverflowMenu?: ShowOverflowMenuCallback;
|
|
78
86
|
}
|
|
79
87
|
export interface IFontValues {
|
|
80
88
|
size?: string;
|
|
@@ -109,17 +117,27 @@ export interface ILayoutState {
|
|
|
109
117
|
calculatedBorderBarSize: number;
|
|
110
118
|
editingTab?: TabNode;
|
|
111
119
|
showHiddenBorder: DockLocation;
|
|
120
|
+
portal?: React.ReactNode;
|
|
112
121
|
}
|
|
113
122
|
|
|
114
123
|
export interface IIcons {
|
|
115
|
-
close?: React.ReactNode;
|
|
116
|
-
closeTabset?: React.ReactNode;
|
|
117
|
-
popout?: React.ReactNode;
|
|
118
|
-
maximize?: React.ReactNode;
|
|
119
|
-
restore?: React.ReactNode;
|
|
120
|
-
more?: React.ReactNode;
|
|
124
|
+
close?: (React.ReactNode | ((tabNode: TabNode) => React.ReactNode));
|
|
125
|
+
closeTabset?: (React.ReactNode | ((tabSetNode: TabSetNode) => React.ReactNode));
|
|
126
|
+
popout?: (React.ReactNode | ((tabNode: TabNode) => React.ReactNode));
|
|
127
|
+
maximize?: (React.ReactNode | ((tabSetNode: TabSetNode) => React.ReactNode));
|
|
128
|
+
restore?: (React.ReactNode | ((tabSetNode: TabSetNode) => React.ReactNode));
|
|
129
|
+
more?: (React.ReactNode | ((tabSetNode: (TabSetNode | BorderNode), hiddenTabs: { node: TabNode; index: number }[]) => React.ReactNode));
|
|
121
130
|
}
|
|
122
131
|
|
|
132
|
+
const defaultIcons = {
|
|
133
|
+
close: <CloseIcon/>,
|
|
134
|
+
closeTabset: <CloseIcon/>,
|
|
135
|
+
popout: <PopoutIcon/>,
|
|
136
|
+
maximize: <MaximizeIcon/>,
|
|
137
|
+
restore: <RestoreIcon/>,
|
|
138
|
+
more: <OverflowIcon/>,
|
|
139
|
+
};
|
|
140
|
+
|
|
123
141
|
export interface ICustomDropDestination {
|
|
124
142
|
rect: Rect;
|
|
125
143
|
callback: CustomDragCallback;
|
|
@@ -146,7 +164,7 @@ export interface ILayoutCallbacks {
|
|
|
146
164
|
getRootDiv(): HTMLDivElement;
|
|
147
165
|
dragStart(
|
|
148
166
|
event: Event | React.MouseEvent<HTMLDivElement, MouseEvent> | React.TouchEvent<HTMLDivElement> | React.DragEvent<HTMLDivElement> | undefined,
|
|
149
|
-
dragDivText: string,
|
|
167
|
+
dragDivText: string | undefined,
|
|
150
168
|
node: Node & IDraggable,
|
|
151
169
|
allowDrag: boolean,
|
|
152
170
|
onClick?: (event: Event) => void,
|
|
@@ -166,6 +184,9 @@ export interface ILayoutCallbacks {
|
|
|
166
184
|
getOnRenderFloatingTabPlaceholder(): FloatingTabPlaceholderRenderCallback | undefined;
|
|
167
185
|
showContextMenu(node: TabNode | TabSetNode | BorderNode, event: React.MouseEvent<HTMLElement, MouseEvent>): void;
|
|
168
186
|
auxMouseClick(node: TabNode | TabSetNode | BorderNode, event: React.MouseEvent<HTMLElement, MouseEvent>): void;
|
|
187
|
+
showPortal: (portal: React.ReactNode, portalDiv: HTMLDivElement) => void;
|
|
188
|
+
hidePortal: () => void;
|
|
189
|
+
getShowOverflowMenu(): ShowOverflowMenuCallback | undefined;
|
|
169
190
|
}
|
|
170
191
|
|
|
171
192
|
// Popout windows work in latest browsers based on webkit (Chrome, Opera, Safari, latest Edge) and Firefox. They do
|
|
@@ -215,7 +236,7 @@ export class Layout extends React.Component<ILayoutProps, ILayoutState> {
|
|
|
215
236
|
/** @hidden @internal */
|
|
216
237
|
private dragRectRendered: boolean = true;
|
|
217
238
|
/** @hidden @internal */
|
|
218
|
-
private dragDivText: string =
|
|
239
|
+
private dragDivText: string | undefined = undefined;
|
|
219
240
|
/** @hidden @internal */
|
|
220
241
|
private dropInfo: DropInfo | undefined;
|
|
221
242
|
/** @hidden @internal */
|
|
@@ -248,7 +269,7 @@ export class Layout extends React.Component<ILayoutProps, ILayoutState> {
|
|
|
248
269
|
/** @hidden @internal */
|
|
249
270
|
private popoutURL: string;
|
|
250
271
|
/** @hidden @internal */
|
|
251
|
-
private icons
|
|
272
|
+
private icons: IIcons;
|
|
252
273
|
/** @hidden @internal */
|
|
253
274
|
private firstRender: boolean;
|
|
254
275
|
/** @hidden @internal */
|
|
@@ -264,8 +285,7 @@ export class Layout extends React.Component<ILayoutProps, ILayoutState> {
|
|
|
264
285
|
this.findBorderBarSizeRef = React.createRef<HTMLDivElement>();
|
|
265
286
|
this.supportsPopout = props.supportsPopout !== undefined ? props.supportsPopout : defaultSupportsPopout;
|
|
266
287
|
this.popoutURL = props.popoutURL ? props.popoutURL : "popout.html";
|
|
267
|
-
|
|
268
|
-
this.icons = props.closeIcon ? Object.assign({ close: props.closeIcon }, props.icons) : props.icons;
|
|
288
|
+
this.icons = {...defaultIcons, ...props.icons};
|
|
269
289
|
this.firstRender = true;
|
|
270
290
|
|
|
271
291
|
this.state = {
|
|
@@ -283,11 +303,13 @@ export class Layout extends React.Component<ILayoutProps, ILayoutState> {
|
|
|
283
303
|
/** @hidden @internal */
|
|
284
304
|
styleFont(style: Record<string, string>): Record<string, string> {
|
|
285
305
|
if (this.props.font) {
|
|
286
|
-
if (this.
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
306
|
+
if (this.selfRef.current) {
|
|
307
|
+
if (this.props.font.size) {
|
|
308
|
+
this.selfRef.current.style.setProperty("--font-size", this.props.font.size);
|
|
309
|
+
}
|
|
310
|
+
if (this.props.font.family) {
|
|
311
|
+
this.selfRef.current.style.setProperty("--font-family", this.props.font.family);
|
|
312
|
+
}
|
|
291
313
|
}
|
|
292
314
|
if (this.props.font.style) {
|
|
293
315
|
style.fontStyle = this.props.font.style;
|
|
@@ -459,7 +481,7 @@ export class Layout extends React.Component<ILayoutProps, ILayoutState> {
|
|
|
459
481
|
const metrics: ILayoutMetrics = {
|
|
460
482
|
headerBarSize: this.state.calculatedHeaderBarSize,
|
|
461
483
|
tabBarSize: this.state.calculatedTabBarSize,
|
|
462
|
-
borderBarSize: this.state.calculatedBorderBarSize
|
|
484
|
+
borderBarSize: this.state.calculatedBorderBarSize
|
|
463
485
|
};
|
|
464
486
|
this.props.model._setShowHiddenBorder(this.state.showHiddenBorder);
|
|
465
487
|
|
|
@@ -503,6 +525,7 @@ export class Layout extends React.Component<ILayoutProps, ILayoutState> {
|
|
|
503
525
|
{splitterComponents}
|
|
504
526
|
{floatingWindows}
|
|
505
527
|
{this.metricsElements()}
|
|
528
|
+
{this.state.portal}
|
|
506
529
|
</div>
|
|
507
530
|
);
|
|
508
531
|
}
|
|
@@ -691,7 +714,7 @@ export class Layout extends React.Component<ILayoutProps, ILayoutState> {
|
|
|
691
714
|
* @param json the json for the new tab node
|
|
692
715
|
* @param onDrop a callback to call when the drag is complete (node and event will be undefined if the drag was cancelled)
|
|
693
716
|
*/
|
|
694
|
-
addTabWithDragAndDrop(dragText: string, json: IJsonTabNode, onDrop?: (node?: Node, event?: Event) => void) {
|
|
717
|
+
addTabWithDragAndDrop(dragText: string | undefined, json: IJsonTabNode, onDrop?: (node?: Node, event?: Event) => void) {
|
|
695
718
|
this.fnNewNodeDropped = onDrop;
|
|
696
719
|
this.newTabJson = json;
|
|
697
720
|
this.dragStart(undefined, dragText, TabNode._fromJson(json, this.props.model, false), true, undefined, undefined);
|
|
@@ -705,7 +728,7 @@ export class Layout extends React.Component<ILayoutProps, ILayoutState> {
|
|
|
705
728
|
* @param json the json for the new tab node
|
|
706
729
|
* @param onDrop a callback to call when the drag is complete (node and event will be undefined if the drag was cancelled)
|
|
707
730
|
*/
|
|
708
|
-
addTabWithDragAndDropIndirect(dragText: string, json: IJsonTabNode, onDrop?: (node?: Node, event?: Event) => void) {
|
|
731
|
+
addTabWithDragAndDropIndirect(dragText: string | undefined, json: IJsonTabNode, onDrop?: (node?: Node, event?: Event) => void) {
|
|
709
732
|
this.fnNewNodeDropped = onDrop;
|
|
710
733
|
this.newTabJson = json;
|
|
711
734
|
|
|
@@ -739,6 +762,7 @@ export class Layout extends React.Component<ILayoutProps, ILayoutState> {
|
|
|
739
762
|
const rootdiv = this.selfRef.current;
|
|
740
763
|
rootdiv!.removeChild(this.dragDiv!);
|
|
741
764
|
this.dragDiv = undefined;
|
|
765
|
+
this.hidePortal();
|
|
742
766
|
if (this.fnNewNodeDropped != null) {
|
|
743
767
|
this.fnNewNodeDropped();
|
|
744
768
|
this.fnNewNodeDropped = undefined;
|
|
@@ -769,6 +793,7 @@ export class Layout extends React.Component<ILayoutProps, ILayoutState> {
|
|
|
769
793
|
} catch (e) { }
|
|
770
794
|
|
|
771
795
|
this.dragDiv = undefined;
|
|
796
|
+
this.hidePortal();
|
|
772
797
|
this.hideEdges(rootdiv);
|
|
773
798
|
if (this.fnNewNodeDropped != null) {
|
|
774
799
|
this.fnNewNodeDropped();
|
|
@@ -798,7 +823,7 @@ export class Layout extends React.Component<ILayoutProps, ILayoutState> {
|
|
|
798
823
|
/** @hidden @internal */
|
|
799
824
|
dragStart = (
|
|
800
825
|
event: Event | React.MouseEvent<HTMLDivElement, MouseEvent> | React.TouchEvent<HTMLDivElement> | React.DragEvent<HTMLDivElement> | undefined,
|
|
801
|
-
dragDivText: string,
|
|
826
|
+
dragDivText: string | undefined,
|
|
802
827
|
node: Node & IDraggable,
|
|
803
828
|
allowDrag: boolean,
|
|
804
829
|
onClick?: (event: Event) => void,
|
|
@@ -814,11 +839,24 @@ export class Layout extends React.Component<ILayoutProps, ILayoutState> {
|
|
|
814
839
|
};
|
|
815
840
|
|
|
816
841
|
/** @hidden @internal */
|
|
817
|
-
dragRectRender = (text: String, node?: Node, json?: IJsonTabNode, onRendered?: () => void) => {
|
|
818
|
-
let content: React.ReactElement | undefined
|
|
842
|
+
dragRectRender = (text: String | undefined, node?: Node, json?: IJsonTabNode, onRendered?: () => void) => {
|
|
843
|
+
let content: React.ReactElement | undefined;
|
|
844
|
+
|
|
845
|
+
if (text !== undefined) {
|
|
846
|
+
content = <div style={{ whiteSpace: "pre" }}>{text.replace("<br>", "\n")}</div>;
|
|
847
|
+
} else {
|
|
848
|
+
if (node && node instanceof TabNode) {
|
|
849
|
+
content = (<TabButtonStamp
|
|
850
|
+
node={node}
|
|
851
|
+
layout={this}
|
|
852
|
+
iconFactory={this.props.iconFactory}
|
|
853
|
+
titleFactory={this.props.titleFactory}
|
|
854
|
+
/>);
|
|
855
|
+
}
|
|
856
|
+
}
|
|
819
857
|
|
|
820
858
|
if (this.props.onRenderDragRect !== undefined) {
|
|
821
|
-
const customContent = this.props.onRenderDragRect(
|
|
859
|
+
const customContent = this.props.onRenderDragRect(content, node, json);
|
|
822
860
|
if (customContent !== undefined) {
|
|
823
861
|
content = customContent;
|
|
824
862
|
}
|
|
@@ -827,15 +865,25 @@ export class Layout extends React.Component<ILayoutProps, ILayoutState> {
|
|
|
827
865
|
// hide div until the render is complete
|
|
828
866
|
this.dragDiv!.style.visibility = "hidden";
|
|
829
867
|
this.dragRectRendered = false;
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
868
|
+
this.showPortal(
|
|
869
|
+
<DragRectRenderWrapper
|
|
870
|
+
// wait for it to be rendered
|
|
871
|
+
onRendered={() => {
|
|
872
|
+
this.dragRectRendered = true;
|
|
873
|
+
onRendered?.();
|
|
874
|
+
}}>
|
|
875
|
+
{content}
|
|
876
|
+
</DragRectRenderWrapper>,
|
|
877
|
+
this.dragDiv!);
|
|
878
|
+
};
|
|
879
|
+
|
|
880
|
+
showPortal = (control: React.ReactNode, element: HTMLElement) => {
|
|
881
|
+
const portal = ReactDOM.createPortal(control, element);
|
|
882
|
+
this.setState({portal});
|
|
883
|
+
};
|
|
884
|
+
|
|
885
|
+
hidePortal = () => {
|
|
886
|
+
this.setState({portal: undefined});
|
|
839
887
|
};
|
|
840
888
|
|
|
841
889
|
/** @hidden @internal */
|
|
@@ -916,6 +964,8 @@ export class Layout extends React.Component<ILayoutProps, ILayoutState> {
|
|
|
916
964
|
rootdiv.removeChild(this.outlineDiv!);
|
|
917
965
|
rootdiv.removeChild(this.dragDiv!);
|
|
918
966
|
this.dragDiv = undefined;
|
|
967
|
+
this.hidePortal();
|
|
968
|
+
|
|
919
969
|
this.hideEdges(rootdiv);
|
|
920
970
|
DragDrop.instance.hideGlass();
|
|
921
971
|
|
|
@@ -1179,6 +1229,10 @@ export class Layout extends React.Component<ILayoutProps, ILayoutState> {
|
|
|
1179
1229
|
return this.props.onRenderFloatingTabPlaceholder;
|
|
1180
1230
|
}
|
|
1181
1231
|
|
|
1232
|
+
/** @hidden @internal */
|
|
1233
|
+
getShowOverflowMenu() {
|
|
1234
|
+
return this.props.onShowOverflowMenu;
|
|
1235
|
+
}
|
|
1182
1236
|
/** @hidden @internal */
|
|
1183
1237
|
showContextMenu(node: TabNode | TabSetNode | BorderNode, event: React.MouseEvent<HTMLElement, MouseEvent>) {
|
|
1184
1238
|
if (this.props.onContextMenu) {
|
package/src/view/Tab.tsx
CHANGED
|
@@ -8,6 +8,7 @@ import { ILayoutCallbacks } from "./Layout";
|
|
|
8
8
|
import { ErrorBoundary } from "./ErrorBoundary";
|
|
9
9
|
import { I18nLabel } from "../I18nLabel";
|
|
10
10
|
import { BorderNode } from "..";
|
|
11
|
+
import { hideElement } from "./Utils";
|
|
11
12
|
|
|
12
13
|
/** @hidden @internal */
|
|
13
14
|
export interface ITabProps {
|
|
@@ -41,15 +42,17 @@ export const Tab = (props: ITabProps) => {
|
|
|
41
42
|
};
|
|
42
43
|
|
|
43
44
|
const cm = layout.getClassName;
|
|
45
|
+
const useVisibility = node.getModel().isUseVisibility();
|
|
44
46
|
|
|
45
47
|
const parentNode = node.getParent() as TabSetNode | BorderNode;
|
|
46
|
-
const style: Record<string, any> = node._styleWithPosition(
|
|
47
|
-
|
|
48
|
-
|
|
48
|
+
const style: Record<string, any> = node._styleWithPosition();
|
|
49
|
+
if (!selected) {
|
|
50
|
+
hideElement(style, useVisibility);
|
|
51
|
+
}
|
|
49
52
|
|
|
50
53
|
if (parentNode instanceof TabSetNode) {
|
|
51
54
|
if (node.getModel().getMaximizedTabset() !== undefined && !parentNode.isMaximized()) {
|
|
52
|
-
style
|
|
55
|
+
hideElement(style, useVisibility);
|
|
53
56
|
}
|
|
54
57
|
}
|
|
55
58
|
|
|
@@ -77,3 +80,5 @@ export const Tab = (props: ITabProps) => {
|
|
|
77
80
|
</div>
|
|
78
81
|
);
|
|
79
82
|
};
|
|
83
|
+
|
|
84
|
+
|
package/src/view/TabButton.tsx
CHANGED
|
@@ -4,27 +4,26 @@ import Actions from "../model/Actions";
|
|
|
4
4
|
import TabNode from "../model/TabNode";
|
|
5
5
|
import TabSetNode from "../model/TabSetNode";
|
|
6
6
|
import Rect from "../Rect";
|
|
7
|
-
import { IIcons, ILayoutCallbacks
|
|
7
|
+
import { IIcons, ILayoutCallbacks } from "./Layout";
|
|
8
8
|
import { ICloseType } from "../model/ICloseType";
|
|
9
9
|
import { CLASSES } from "../Types";
|
|
10
|
-
import { isAuxMouseEvent } from "./
|
|
10
|
+
import { getRenderStateEx, isAuxMouseEvent } from "./Utils";
|
|
11
11
|
|
|
12
12
|
/** @hidden @internal */
|
|
13
13
|
export interface ITabButtonProps {
|
|
14
14
|
layout: ILayoutCallbacks;
|
|
15
15
|
node: TabNode;
|
|
16
|
-
show: boolean;
|
|
17
16
|
selected: boolean;
|
|
18
17
|
height: number;
|
|
19
18
|
iconFactory?: (node: TabNode) => React.ReactNode | undefined;
|
|
20
19
|
titleFactory?: (node: TabNode) => React.ReactNode | undefined;
|
|
21
|
-
icons
|
|
20
|
+
icons: IIcons;
|
|
22
21
|
path: string;
|
|
23
22
|
}
|
|
24
23
|
|
|
25
24
|
/** @hidden @internal */
|
|
26
25
|
export const TabButton = (props: ITabButtonProps) => {
|
|
27
|
-
const { layout, node,
|
|
26
|
+
const { layout, node, selected, iconFactory, titleFactory, icons, path } = props;
|
|
28
27
|
const selfRef = React.useRef<HTMLDivElement | null>(null);
|
|
29
28
|
const contentRef = React.useRef<HTMLInputElement | null>(null);
|
|
30
29
|
const contentWidth = React.useRef<number>(0);
|
|
@@ -32,8 +31,7 @@ export const TabButton = (props: ITabButtonProps) => {
|
|
|
32
31
|
const onMouseDown = (event: React.MouseEvent<HTMLDivElement, MouseEvent> | React.TouchEvent<HTMLDivElement>) => {
|
|
33
32
|
|
|
34
33
|
if (!isAuxMouseEvent(event) && !layout.getEditingTab()) {
|
|
35
|
-
|
|
36
|
-
layout.dragStart(event, message, node, node.isEnableDrag(), onClick, onDoubleClick);
|
|
34
|
+
layout.dragStart(event, undefined, node, node.isEnableDrag(), onClick, onDoubleClick);
|
|
37
35
|
}
|
|
38
36
|
};
|
|
39
37
|
|
|
@@ -111,7 +109,7 @@ export const TabButton = (props: ITabButtonProps) => {
|
|
|
111
109
|
const layoutRect = layout.getDomRect();
|
|
112
110
|
const r = selfRef.current!.getBoundingClientRect();
|
|
113
111
|
node._setTabRect(new Rect(r.left - layoutRect.left, r.top - layoutRect.top, r.width, r.height));
|
|
114
|
-
contentWidth.current =
|
|
112
|
+
contentWidth.current = selfRef.current!.getBoundingClientRect().width;
|
|
115
113
|
};
|
|
116
114
|
|
|
117
115
|
const onTextBoxMouseDown = (event: React.MouseEvent<HTMLInputElement> | React.TouchEvent<HTMLInputElement>) => {
|
|
@@ -148,53 +146,21 @@ export const TabButton = (props: ITabButtonProps) => {
|
|
|
148
146
|
classNames += " " + node.getClassName();
|
|
149
147
|
}
|
|
150
148
|
|
|
151
|
-
|
|
152
|
-
let titleContent: React.ReactNode = node.getName();
|
|
153
|
-
let name = node.getName();
|
|
149
|
+
const renderState = getRenderStateEx(layout, node, iconFactory, titleFactory);
|
|
154
150
|
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
if (titleFactory !== undefined) {
|
|
160
|
-
const titleObj = titleFactory(node);
|
|
161
|
-
if (titleObj !== undefined) {
|
|
162
|
-
if (typeof titleObj === "string") {
|
|
163
|
-
titleContent = titleObj as string;
|
|
164
|
-
name = titleObj as string;
|
|
165
|
-
} else if (isTitleObject(titleObj)) {
|
|
166
|
-
titleContent = titleObj.titleContent;
|
|
167
|
-
name = titleObj.name;
|
|
168
|
-
} else {
|
|
169
|
-
titleContent = titleObj;
|
|
170
|
-
}
|
|
171
|
-
}
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
if (leadingContent === undefined && node.getIcon() !== undefined) {
|
|
175
|
-
leadingContent = <img src={node.getIcon()} alt="leadingContent" />;
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
let buttons: any[] = [];
|
|
179
|
-
|
|
180
|
-
// allow customization of leading contents (icon) and contents
|
|
181
|
-
const renderState = { leading: leadingContent, content: titleContent, name, buttons };
|
|
182
|
-
layout.customizeTab(node, renderState);
|
|
183
|
-
|
|
184
|
-
node._setRenderedName(renderState.name);
|
|
185
|
-
|
|
186
|
-
let content = (
|
|
187
|
-
<div ref={contentRef} className={cm(CLASSES.FLEXLAYOUT__TAB_BUTTON_CONTENT)}>
|
|
151
|
+
let content = renderState.content ? (
|
|
152
|
+
<div className={cm(CLASSES.FLEXLAYOUT__TAB_BUTTON_CONTENT)}>
|
|
188
153
|
{renderState.content}
|
|
189
|
-
</div>
|
|
190
|
-
|
|
191
|
-
const leading =
|
|
154
|
+
</div>) : null;
|
|
155
|
+
|
|
156
|
+
const leading = renderState.leading ? (
|
|
157
|
+
<div className={cm(CLASSES.FLEXLAYOUT__TAB_BUTTON_LEADING)}>
|
|
158
|
+
{renderState.leading}
|
|
159
|
+
</div>) : null;
|
|
192
160
|
|
|
193
161
|
if (layout.getEditingTab() === node) {
|
|
194
|
-
const contentStyle = { width: contentWidth + "px" };
|
|
195
162
|
content = (
|
|
196
163
|
<input
|
|
197
|
-
style={contentStyle}
|
|
198
164
|
ref={contentRef}
|
|
199
165
|
className={cm(CLASSES.FLEXLAYOUT__TAB_BUTTON_TEXTBOX)}
|
|
200
166
|
data-layout-path={path + "/textbox"}
|
|
@@ -210,7 +176,7 @@ export const TabButton = (props: ITabButtonProps) => {
|
|
|
210
176
|
|
|
211
177
|
if (node.isEnableClose()) {
|
|
212
178
|
const closeTitle = layout.i18nName(I18nLabel.Close_Tab);
|
|
213
|
-
buttons.push(
|
|
179
|
+
renderState.buttons.push(
|
|
214
180
|
<div
|
|
215
181
|
key="close"
|
|
216
182
|
data-layout-path={path + "/button/close"}
|
|
@@ -218,7 +184,7 @@ export const TabButton = (props: ITabButtonProps) => {
|
|
|
218
184
|
className={cm(CLASSES.FLEXLAYOUT__TAB_BUTTON_TRAILING)}
|
|
219
185
|
onMouseDown={onCloseMouseDown} onClick={onClose}
|
|
220
186
|
onTouchStart={onCloseMouseDown}>
|
|
221
|
-
{icons
|
|
187
|
+
{(typeof icons.close === "function") ? icons.close(node) : icons.close}
|
|
222
188
|
</div>
|
|
223
189
|
);
|
|
224
190
|
}
|
|
@@ -227,9 +193,6 @@ export const TabButton = (props: ITabButtonProps) => {
|
|
|
227
193
|
<div
|
|
228
194
|
ref={selfRef}
|
|
229
195
|
data-layout-path={path}
|
|
230
|
-
style={{
|
|
231
|
-
visibility: show ? "visible" : "hidden",
|
|
232
|
-
}}
|
|
233
196
|
className={classNames}
|
|
234
197
|
onMouseDown={onMouseDown}
|
|
235
198
|
onClick={onAuxMouseClick}
|
|
@@ -240,7 +203,7 @@ export const TabButton = (props: ITabButtonProps) => {
|
|
|
240
203
|
>
|
|
241
204
|
{leading}
|
|
242
205
|
{content}
|
|
243
|
-
{buttons}
|
|
206
|
+
{renderState.buttons}
|
|
244
207
|
</div>
|
|
245
208
|
);
|
|
246
209
|
};
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import TabNode from "../model/TabNode";
|
|
3
|
+
import { ILayoutCallbacks } from "./Layout";
|
|
4
|
+
import { CLASSES } from "../Types";
|
|
5
|
+
import { getRenderStateEx } from "./Utils";
|
|
6
|
+
|
|
7
|
+
/** @hidden @internal */
|
|
8
|
+
export interface ITabButtonStampProps {
|
|
9
|
+
node: TabNode;
|
|
10
|
+
layout: ILayoutCallbacks;
|
|
11
|
+
iconFactory?: (node: TabNode) => React.ReactNode | undefined;
|
|
12
|
+
titleFactory?: (node: TabNode) => React.ReactNode | undefined;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/** @hidden @internal */
|
|
16
|
+
export const TabButtonStamp = (props: ITabButtonStampProps) => {
|
|
17
|
+
const { layout, node, iconFactory, titleFactory } = props;
|
|
18
|
+
const selfRef = React.useRef<HTMLDivElement | null>(null);
|
|
19
|
+
|
|
20
|
+
const cm = layout.getClassName;
|
|
21
|
+
|
|
22
|
+
let classNames = cm(CLASSES.FLEXLAYOUT__TAB_BUTTON_STAMP);
|
|
23
|
+
|
|
24
|
+
const renderState = getRenderStateEx(layout, node, iconFactory, titleFactory);
|
|
25
|
+
|
|
26
|
+
let content = renderState.content ? (
|
|
27
|
+
<div className={cm(CLASSES.FLEXLAYOUT__TAB_BUTTON_CONTENT)}>
|
|
28
|
+
{renderState.content}
|
|
29
|
+
</div>)
|
|
30
|
+
: node._getNameForOverflowMenu();
|
|
31
|
+
|
|
32
|
+
const leading = renderState.leading ? (
|
|
33
|
+
<div className={cm(CLASSES.FLEXLAYOUT__TAB_BUTTON_LEADING)}>
|
|
34
|
+
{renderState.leading}
|
|
35
|
+
</div>) : null;
|
|
36
|
+
|
|
37
|
+
return (
|
|
38
|
+
<div
|
|
39
|
+
ref={selfRef}
|
|
40
|
+
className={classNames}
|
|
41
|
+
title={node.getHelpText()}
|
|
42
|
+
>
|
|
43
|
+
{leading}
|
|
44
|
+
{content}
|
|
45
|
+
</div>
|
|
46
|
+
);
|
|
47
|
+
};
|