flexlayout-react 0.5.19 → 0.6.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/ChangeLog.txt +22 -0
- package/README.md +61 -56
- package/declarations/Types.d.ts +1 -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 +6 -4
- 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 +50 -14
- package/dist/flexlayout_min.js +1 -1
- package/lib/PopupMenu.js +11 -7
- package/lib/PopupMenu.js.map +1 -1
- package/lib/Types.js +1 -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 +11 -41
- package/lib/view/BorderButton.js.map +1 -1
- package/lib/view/BorderTabSet.js +7 -7
- 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 +80 -25
- 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/Splitter.js +3 -3
- package/lib/view/Splitter.js.map +1 -1
- package/lib/view/Tab.js +9 -6
- 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 +7 -5
- package/lib/view/TabFloating.js.map +1 -1
- package/lib/view/TabOverflowHook.js +1 -1
- package/lib/view/TabSet.js +15 -25
- 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 +11 -6
- package/src/I18nLabel.ts +1 -1
- package/src/PopupMenu.tsx +30 -10
- package/src/Types.ts +1 -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 +19 -43
- package/src/view/BorderTabSet.tsx +14 -4
- package/src/view/FloatingWindow.tsx +14 -6
- package/src/view/Icons.tsx +36 -0
- package/src/view/Layout.tsx +108 -41
- package/src/view/Splitter.tsx +4 -1
- package/src/view/Tab.tsx +17 -6
- package/src/view/TabButton.tsx +27 -55
- package/src/view/TabButtonStamp.tsx +47 -0
- package/src/view/TabFloating.tsx +12 -5
- package/src/view/TabOverflowHook.tsx +1 -1
- package/src/view/TabSet.tsx +27 -17
- package/src/view/Utils.tsx +71 -0
- package/style/_base.scss +82 -52
- package/style/dark.css +82 -76
- package/style/dark.css.map +1 -1
- package/style/dark.scss +15 -5
- package/style/gray.css +79 -73
- package/style/gray.css.map +1 -1
- package/style/gray.scss +10 -3
- package/style/light.css +83 -77
- 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
package/src/view/Layout.tsx
CHANGED
|
@@ -25,9 +25,12 @@ import { FloatingWindow } from "./FloatingWindow";
|
|
|
25
25
|
import { FloatingWindowTab } from "./FloatingWindowTab";
|
|
26
26
|
import { TabFloating } from "./TabFloating";
|
|
27
27
|
import { IJsonTabNode } from "../model/IJsonModel";
|
|
28
|
+
import { Orientation } from "..";
|
|
29
|
+
import { CloseIcon, MaximizeIcon, OverflowIcon, PopoutIcon, RestoreIcon } from "./Icons";
|
|
30
|
+
import { TabButtonStamp } from "./TabButtonStamp";
|
|
28
31
|
|
|
29
32
|
export type CustomDragCallback = (dragging: TabNode | IJsonTabNode, over: TabNode, x: number, y: number, location: DockLocation) => void;
|
|
30
|
-
export type DragRectRenderCallback = (
|
|
33
|
+
export type DragRectRenderCallback = (content: React.ReactElement | undefined, node?: Node, json?: IJsonTabNode) => React.ReactElement | undefined;
|
|
31
34
|
export type FloatingTabPlaceholderRenderCallback = (dockPopout: () => void, showPopout: () => void) => React.ReactElement | undefined;
|
|
32
35
|
export type NodeMouseEvent = (node: TabNode | TabSetNode | BorderNode, event: React.MouseEvent<HTMLElement, MouseEvent>) => void;
|
|
33
36
|
|
|
@@ -38,7 +41,6 @@ export interface ILayoutProps {
|
|
|
38
41
|
fontFamily?: string;
|
|
39
42
|
iconFactory?: (node: TabNode) => React.ReactNode | undefined;
|
|
40
43
|
titleFactory?: (node: TabNode) => ITitleObject | React.ReactNode | undefined;
|
|
41
|
-
closeIcon?: React.ReactNode;
|
|
42
44
|
icons?: IIcons;
|
|
43
45
|
onAction?: (action: Action) => Action | undefined;
|
|
44
46
|
onRenderTab?: (
|
|
@@ -108,6 +110,7 @@ export interface ILayoutState {
|
|
|
108
110
|
calculatedBorderBarSize: number;
|
|
109
111
|
editingTab?: TabNode;
|
|
110
112
|
showHiddenBorder: DockLocation;
|
|
113
|
+
portal?: React.ReactNode;
|
|
111
114
|
}
|
|
112
115
|
|
|
113
116
|
export interface IIcons {
|
|
@@ -119,6 +122,15 @@ export interface IIcons {
|
|
|
119
122
|
more?: React.ReactNode;
|
|
120
123
|
}
|
|
121
124
|
|
|
125
|
+
const defaultIcons = {
|
|
126
|
+
close: <CloseIcon/>,
|
|
127
|
+
closeTabset: <CloseIcon/>,
|
|
128
|
+
popout: <PopoutIcon/>,
|
|
129
|
+
maximize: <MaximizeIcon/>,
|
|
130
|
+
restore: <RestoreIcon/>,
|
|
131
|
+
more: <OverflowIcon/>,
|
|
132
|
+
};
|
|
133
|
+
|
|
122
134
|
export interface ICustomDropDestination {
|
|
123
135
|
rect: Rect;
|
|
124
136
|
callback: CustomDragCallback;
|
|
@@ -145,7 +157,7 @@ export interface ILayoutCallbacks {
|
|
|
145
157
|
getRootDiv(): HTMLDivElement;
|
|
146
158
|
dragStart(
|
|
147
159
|
event: Event | React.MouseEvent<HTMLDivElement, MouseEvent> | React.TouchEvent<HTMLDivElement> | React.DragEvent<HTMLDivElement> | undefined,
|
|
148
|
-
dragDivText: string,
|
|
160
|
+
dragDivText: string | undefined,
|
|
149
161
|
node: Node & IDraggable,
|
|
150
162
|
allowDrag: boolean,
|
|
151
163
|
onClick?: (event: Event) => void,
|
|
@@ -163,8 +175,10 @@ export interface ILayoutCallbacks {
|
|
|
163
175
|
setEditingTab(tabNode?: TabNode): void;
|
|
164
176
|
getEditingTab(): TabNode | undefined;
|
|
165
177
|
getOnRenderFloatingTabPlaceholder(): FloatingTabPlaceholderRenderCallback | undefined;
|
|
166
|
-
showContextMenu(node: TabNode | TabSetNode | BorderNode, event: React.MouseEvent<HTMLElement, MouseEvent>)
|
|
167
|
-
auxMouseClick(node: TabNode | TabSetNode | BorderNode, event: React.MouseEvent<HTMLElement, MouseEvent>)
|
|
178
|
+
showContextMenu(node: TabNode | TabSetNode | BorderNode, event: React.MouseEvent<HTMLElement, MouseEvent>): void;
|
|
179
|
+
auxMouseClick(node: TabNode | TabSetNode | BorderNode, event: React.MouseEvent<HTMLElement, MouseEvent>): void;
|
|
180
|
+
showPortal: (portal: React.ReactNode, portalDiv: HTMLDivElement) => void;
|
|
181
|
+
hidePortal: () => void;
|
|
168
182
|
}
|
|
169
183
|
|
|
170
184
|
// Popout windows work in latest browsers based on webkit (Chrome, Opera, Safari, latest Edge) and Firefox. They do
|
|
@@ -214,7 +228,7 @@ export class Layout extends React.Component<ILayoutProps, ILayoutState> {
|
|
|
214
228
|
/** @hidden @internal */
|
|
215
229
|
private dragRectRendered: boolean = true;
|
|
216
230
|
/** @hidden @internal */
|
|
217
|
-
private dragDivText: string =
|
|
231
|
+
private dragDivText: string | undefined = undefined;
|
|
218
232
|
/** @hidden @internal */
|
|
219
233
|
private dropInfo: DropInfo | undefined;
|
|
220
234
|
/** @hidden @internal */
|
|
@@ -263,8 +277,7 @@ export class Layout extends React.Component<ILayoutProps, ILayoutState> {
|
|
|
263
277
|
this.findBorderBarSizeRef = React.createRef<HTMLDivElement>();
|
|
264
278
|
this.supportsPopout = props.supportsPopout !== undefined ? props.supportsPopout : defaultSupportsPopout;
|
|
265
279
|
this.popoutURL = props.popoutURL ? props.popoutURL : "popout.html";
|
|
266
|
-
|
|
267
|
-
this.icons = props.closeIcon ? Object.assign({ close: props.closeIcon }, props.icons) : props.icons;
|
|
280
|
+
this.icons = {...defaultIcons, ...props.icons};
|
|
268
281
|
this.firstRender = true;
|
|
269
282
|
|
|
270
283
|
this.state = {
|
|
@@ -282,11 +295,13 @@ export class Layout extends React.Component<ILayoutProps, ILayoutState> {
|
|
|
282
295
|
/** @hidden @internal */
|
|
283
296
|
styleFont(style: Record<string, string>): Record<string, string> {
|
|
284
297
|
if (this.props.font) {
|
|
285
|
-
if (this.
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
298
|
+
if (this.selfRef.current) {
|
|
299
|
+
if (this.props.font.size) {
|
|
300
|
+
this.selfRef.current.style.setProperty("--font-size", this.props.font.size);
|
|
301
|
+
}
|
|
302
|
+
if (this.props.font.family) {
|
|
303
|
+
this.selfRef.current.style.setProperty("--font-family", this.props.font.family);
|
|
304
|
+
}
|
|
290
305
|
}
|
|
291
306
|
if (this.props.font.style) {
|
|
292
307
|
style.fontStyle = this.props.font.style;
|
|
@@ -458,14 +473,14 @@ export class Layout extends React.Component<ILayoutProps, ILayoutState> {
|
|
|
458
473
|
const metrics: ILayoutMetrics = {
|
|
459
474
|
headerBarSize: this.state.calculatedHeaderBarSize,
|
|
460
475
|
tabBarSize: this.state.calculatedTabBarSize,
|
|
461
|
-
borderBarSize: this.state.calculatedBorderBarSize
|
|
476
|
+
borderBarSize: this.state.calculatedBorderBarSize
|
|
462
477
|
};
|
|
463
478
|
this.props.model._setShowHiddenBorder(this.state.showHiddenBorder);
|
|
464
479
|
|
|
465
480
|
this.centerRect = this.props.model._layout(this.state.rect, metrics);
|
|
466
481
|
|
|
467
482
|
this.renderBorder(this.props.model.getBorderSet(), borderComponents, tabComponents, floatingWindows, splitterComponents);
|
|
468
|
-
this.renderChildren(this.props.model.getRoot(), tabSetComponents, tabComponents, floatingWindows, splitterComponents);
|
|
483
|
+
this.renderChildren("", this.props.model.getRoot(), tabSetComponents, tabComponents, floatingWindows, splitterComponents);
|
|
469
484
|
|
|
470
485
|
if (this.edgesShown) {
|
|
471
486
|
this.repositionEdges(this.state.rect)
|
|
@@ -502,6 +517,7 @@ export class Layout extends React.Component<ILayoutProps, ILayoutState> {
|
|
|
502
517
|
{splitterComponents}
|
|
503
518
|
{floatingWindows}
|
|
504
519
|
{this.metricsElements()}
|
|
520
|
+
{this.state.portal}
|
|
505
521
|
</div>
|
|
506
522
|
);
|
|
507
523
|
}
|
|
@@ -543,10 +559,12 @@ export class Layout extends React.Component<ILayoutProps, ILayoutState> {
|
|
|
543
559
|
/** @hidden @internal */
|
|
544
560
|
renderBorder(borderSet: BorderSet, borderComponents: React.ReactNode[], tabComponents: Record<string, React.ReactNode>, floatingWindows: React.ReactNode[], splitterComponents: React.ReactNode[]) {
|
|
545
561
|
for (const border of borderSet.getBorders()) {
|
|
562
|
+
const borderPath = `/border/${border.getLocation().getName()}`;
|
|
546
563
|
if (border.isShowing()) {
|
|
547
564
|
borderComponents.push(
|
|
548
565
|
<BorderTabSet
|
|
549
|
-
key={
|
|
566
|
+
key={`border_${border.getLocation().getName()}`}
|
|
567
|
+
path={borderPath}
|
|
550
568
|
border={border}
|
|
551
569
|
layout={this}
|
|
552
570
|
iconFactory={this.props.iconFactory}
|
|
@@ -556,10 +574,13 @@ export class Layout extends React.Component<ILayoutProps, ILayoutState> {
|
|
|
556
574
|
);
|
|
557
575
|
const drawChildren = border._getDrawChildren();
|
|
558
576
|
let i = 0;
|
|
577
|
+
let tabCount = 0;
|
|
559
578
|
for (const child of drawChildren) {
|
|
560
579
|
if (child instanceof SplitterNode) {
|
|
561
|
-
|
|
580
|
+
let path = borderPath + "/s";
|
|
581
|
+
splitterComponents.push(<Splitter key={child.getId()} layout={this} node={child} path={path} />);
|
|
562
582
|
} else if (child instanceof TabNode) {
|
|
583
|
+
let path = borderPath + "/t" + tabCount++;
|
|
563
584
|
if (this.supportsPopout && child.isFloating()) {
|
|
564
585
|
const rect = this._getScreenRect(child);
|
|
565
586
|
floatingWindows.push(
|
|
@@ -575,9 +596,19 @@ export class Layout extends React.Component<ILayoutProps, ILayoutState> {
|
|
|
575
596
|
<FloatingWindowTab layout={this} node={child} factory={this.props.factory} />
|
|
576
597
|
</FloatingWindow>
|
|
577
598
|
);
|
|
578
|
-
tabComponents[child.getId()] = <TabFloating key={child.getId()}
|
|
599
|
+
tabComponents[child.getId()] = <TabFloating key={child.getId()}
|
|
600
|
+
layout={this}
|
|
601
|
+
path={path}
|
|
602
|
+
node={child}
|
|
603
|
+
selected={i === border.getSelected()
|
|
604
|
+
} />;
|
|
579
605
|
} else {
|
|
580
|
-
tabComponents[child.getId()] = <Tab key={child.getId()}
|
|
606
|
+
tabComponents[child.getId()] = <Tab key={child.getId()}
|
|
607
|
+
layout={this}
|
|
608
|
+
path={path}
|
|
609
|
+
node={child}
|
|
610
|
+
selected={i === border.getSelected()}
|
|
611
|
+
factory={this.props.factory} />;
|
|
581
612
|
}
|
|
582
613
|
}
|
|
583
614
|
i++;
|
|
@@ -587,16 +618,22 @@ export class Layout extends React.Component<ILayoutProps, ILayoutState> {
|
|
|
587
618
|
}
|
|
588
619
|
|
|
589
620
|
/** @hidden @internal */
|
|
590
|
-
renderChildren(node: RowNode | TabSetNode, tabSetComponents: React.ReactNode[], tabComponents: Record<string, React.ReactNode>, floatingWindows: React.ReactNode[], splitterComponents: React.ReactNode[]) {
|
|
621
|
+
renderChildren(path: string, node: RowNode | TabSetNode, tabSetComponents: React.ReactNode[], tabComponents: Record<string, React.ReactNode>, floatingWindows: React.ReactNode[], splitterComponents: React.ReactNode[]) {
|
|
591
622
|
const drawChildren = node._getDrawChildren();
|
|
623
|
+
let splitterCount = 0;
|
|
624
|
+
let tabCount = 0;
|
|
625
|
+
let rowCount = 0;
|
|
592
626
|
|
|
593
627
|
for (const child of drawChildren!) {
|
|
594
628
|
if (child instanceof SplitterNode) {
|
|
595
|
-
|
|
629
|
+
const newPath = path + "/s" + (splitterCount++);
|
|
630
|
+
splitterComponents.push(<Splitter key={child.getId()} layout={this} path={newPath} node={child} />);
|
|
596
631
|
} else if (child instanceof TabSetNode) {
|
|
597
|
-
|
|
598
|
-
|
|
632
|
+
const newPath = path + "/ts" + (rowCount++);
|
|
633
|
+
tabSetComponents.push(<TabSet key={child.getId()} layout={this} path={newPath} node={child} iconFactory={this.props.iconFactory} titleFactory={this.props.titleFactory} icons={this.icons} />);
|
|
634
|
+
this.renderChildren(newPath, child, tabSetComponents, tabComponents, floatingWindows, splitterComponents);
|
|
599
635
|
} else if (child instanceof TabNode) {
|
|
636
|
+
const newPath = path + "/t" + (tabCount++);
|
|
600
637
|
const selectedTab = child.getParent()!.getChildren()[(child.getParent() as TabSetNode).getSelected()];
|
|
601
638
|
if (selectedTab === undefined) {
|
|
602
639
|
// this should not happen!
|
|
@@ -617,13 +654,14 @@ export class Layout extends React.Component<ILayoutProps, ILayoutState> {
|
|
|
617
654
|
<FloatingWindowTab layout={this} node={child} factory={this.props.factory} />
|
|
618
655
|
</FloatingWindow>
|
|
619
656
|
);
|
|
620
|
-
tabComponents[child.getId()] = <TabFloating key={child.getId()} layout={this} node={child} selected={child === selectedTab} />;
|
|
657
|
+
tabComponents[child.getId()] = <TabFloating key={child.getId()} layout={this} path={newPath} node={child} selected={child === selectedTab} />;
|
|
621
658
|
} else {
|
|
622
|
-
tabComponents[child.getId()] = <Tab key={child.getId()} layout={this} node={child} selected={child === selectedTab} factory={this.props.factory} />;
|
|
659
|
+
tabComponents[child.getId()] = <Tab key={child.getId()} layout={this} path={newPath} node={child} selected={child === selectedTab} factory={this.props.factory} />;
|
|
623
660
|
}
|
|
624
661
|
} else {
|
|
625
662
|
// is row
|
|
626
|
-
|
|
663
|
+
const newPath = path + ((child.getOrientation() === Orientation.HORZ) ? "/r" : "/c") + (rowCount++);
|
|
664
|
+
this.renderChildren(newPath, child as RowNode, tabSetComponents, tabComponents, floatingWindows, splitterComponents);
|
|
627
665
|
}
|
|
628
666
|
}
|
|
629
667
|
}
|
|
@@ -668,7 +706,7 @@ export class Layout extends React.Component<ILayoutProps, ILayoutState> {
|
|
|
668
706
|
* @param json the json for the new tab node
|
|
669
707
|
* @param onDrop a callback to call when the drag is complete (node and event will be undefined if the drag was cancelled)
|
|
670
708
|
*/
|
|
671
|
-
addTabWithDragAndDrop(dragText: string, json: IJsonTabNode, onDrop?: (node?: Node, event?: Event) => void) {
|
|
709
|
+
addTabWithDragAndDrop(dragText: string | undefined, json: IJsonTabNode, onDrop?: (node?: Node, event?: Event) => void) {
|
|
672
710
|
this.fnNewNodeDropped = onDrop;
|
|
673
711
|
this.newTabJson = json;
|
|
674
712
|
this.dragStart(undefined, dragText, TabNode._fromJson(json, this.props.model, false), true, undefined, undefined);
|
|
@@ -682,7 +720,7 @@ export class Layout extends React.Component<ILayoutProps, ILayoutState> {
|
|
|
682
720
|
* @param json the json for the new tab node
|
|
683
721
|
* @param onDrop a callback to call when the drag is complete (node and event will be undefined if the drag was cancelled)
|
|
684
722
|
*/
|
|
685
|
-
addTabWithDragAndDropIndirect(dragText: string, json: IJsonTabNode, onDrop?: (node?: Node, event?: Event) => void) {
|
|
723
|
+
addTabWithDragAndDropIndirect(dragText: string | undefined, json: IJsonTabNode, onDrop?: (node?: Node, event?: Event) => void) {
|
|
686
724
|
this.fnNewNodeDropped = onDrop;
|
|
687
725
|
this.newTabJson = json;
|
|
688
726
|
|
|
@@ -701,6 +739,7 @@ export class Layout extends React.Component<ILayoutProps, ILayoutState> {
|
|
|
701
739
|
const domRect = this.dragDiv.getBoundingClientRect();
|
|
702
740
|
const r = new Rect(0, 0, domRect?.width, domRect?.height);
|
|
703
741
|
r.centerInRect(this.state.rect);
|
|
742
|
+
this.dragDiv.setAttribute("data-layout-path", "/drag-rectangle");
|
|
704
743
|
this.dragDiv.style.left = r.x + "px";
|
|
705
744
|
this.dragDiv.style.top = r.y + "px";
|
|
706
745
|
}
|
|
@@ -715,6 +754,7 @@ export class Layout extends React.Component<ILayoutProps, ILayoutState> {
|
|
|
715
754
|
const rootdiv = this.selfRef.current;
|
|
716
755
|
rootdiv!.removeChild(this.dragDiv!);
|
|
717
756
|
this.dragDiv = undefined;
|
|
757
|
+
this.hidePortal();
|
|
718
758
|
if (this.fnNewNodeDropped != null) {
|
|
719
759
|
this.fnNewNodeDropped();
|
|
720
760
|
this.fnNewNodeDropped = undefined;
|
|
@@ -745,6 +785,7 @@ export class Layout extends React.Component<ILayoutProps, ILayoutState> {
|
|
|
745
785
|
} catch (e) { }
|
|
746
786
|
|
|
747
787
|
this.dragDiv = undefined;
|
|
788
|
+
this.hidePortal();
|
|
748
789
|
this.hideEdges(rootdiv);
|
|
749
790
|
if (this.fnNewNodeDropped != null) {
|
|
750
791
|
this.fnNewNodeDropped();
|
|
@@ -774,7 +815,7 @@ export class Layout extends React.Component<ILayoutProps, ILayoutState> {
|
|
|
774
815
|
/** @hidden @internal */
|
|
775
816
|
dragStart = (
|
|
776
817
|
event: Event | React.MouseEvent<HTMLDivElement, MouseEvent> | React.TouchEvent<HTMLDivElement> | React.DragEvent<HTMLDivElement> | undefined,
|
|
777
|
-
dragDivText: string,
|
|
818
|
+
dragDivText: string | undefined,
|
|
778
819
|
node: Node & IDraggable,
|
|
779
820
|
allowDrag: boolean,
|
|
780
821
|
onClick?: (event: Event) => void,
|
|
@@ -790,11 +831,24 @@ export class Layout extends React.Component<ILayoutProps, ILayoutState> {
|
|
|
790
831
|
};
|
|
791
832
|
|
|
792
833
|
/** @hidden @internal */
|
|
793
|
-
dragRectRender = (text: String, node?: Node, json?: IJsonTabNode, onRendered?: () => void) => {
|
|
794
|
-
let content: React.ReactElement | undefined
|
|
834
|
+
dragRectRender = (text: String | undefined, node?: Node, json?: IJsonTabNode, onRendered?: () => void) => {
|
|
835
|
+
let content: React.ReactElement | undefined;
|
|
836
|
+
|
|
837
|
+
if (text !== undefined) {
|
|
838
|
+
content = <div style={{ whiteSpace: "pre" }}>{text.replace("<br>", "\n")}</div>;
|
|
839
|
+
} else {
|
|
840
|
+
if (node && node instanceof TabNode) {
|
|
841
|
+
content = (<TabButtonStamp
|
|
842
|
+
node={node}
|
|
843
|
+
layout={this}
|
|
844
|
+
iconFactory={this.props.iconFactory}
|
|
845
|
+
titleFactory={this.props.titleFactory}
|
|
846
|
+
/>);
|
|
847
|
+
}
|
|
848
|
+
}
|
|
795
849
|
|
|
796
850
|
if (this.props.onRenderDragRect !== undefined) {
|
|
797
|
-
const customContent = this.props.onRenderDragRect(
|
|
851
|
+
const customContent = this.props.onRenderDragRect(content, node, json);
|
|
798
852
|
if (customContent !== undefined) {
|
|
799
853
|
content = customContent;
|
|
800
854
|
}
|
|
@@ -803,15 +857,25 @@ export class Layout extends React.Component<ILayoutProps, ILayoutState> {
|
|
|
803
857
|
// hide div until the render is complete
|
|
804
858
|
this.dragDiv!.style.visibility = "hidden";
|
|
805
859
|
this.dragRectRendered = false;
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
860
|
+
this.showPortal(
|
|
861
|
+
<DragRectRenderWrapper
|
|
862
|
+
// wait for it to be rendered
|
|
863
|
+
onRendered={() => {
|
|
864
|
+
this.dragRectRendered = true;
|
|
865
|
+
onRendered?.();
|
|
866
|
+
}}>
|
|
867
|
+
{content}
|
|
868
|
+
</DragRectRenderWrapper>,
|
|
869
|
+
this.dragDiv!);
|
|
870
|
+
};
|
|
871
|
+
|
|
872
|
+
showPortal = (control: React.ReactNode, element: HTMLElement) => {
|
|
873
|
+
const portal = ReactDOM.createPortal(control, element);
|
|
874
|
+
this.setState({portal});
|
|
875
|
+
};
|
|
876
|
+
|
|
877
|
+
hidePortal = () => {
|
|
878
|
+
this.setState({portal: undefined});
|
|
815
879
|
};
|
|
816
880
|
|
|
817
881
|
/** @hidden @internal */
|
|
@@ -827,6 +891,7 @@ export class Layout extends React.Component<ILayoutProps, ILayoutState> {
|
|
|
827
891
|
if (this.dragDiv == null) {
|
|
828
892
|
this.dragDiv = this.currentDocument!.createElement("div");
|
|
829
893
|
this.dragDiv.className = this.getClassName(CLASSES.FLEXLAYOUT__DRAG_RECT);
|
|
894
|
+
this.dragDiv.setAttribute("data-layout-path", "/drag-rectangle");
|
|
830
895
|
this.dragRectRender(this.dragDivText, this.dragNode, this.newTabJson);
|
|
831
896
|
|
|
832
897
|
rootdiv.appendChild(this.dragDiv);
|
|
@@ -891,6 +956,8 @@ export class Layout extends React.Component<ILayoutProps, ILayoutState> {
|
|
|
891
956
|
rootdiv.removeChild(this.outlineDiv!);
|
|
892
957
|
rootdiv.removeChild(this.dragDiv!);
|
|
893
958
|
this.dragDiv = undefined;
|
|
959
|
+
this.hidePortal();
|
|
960
|
+
|
|
894
961
|
this.hideEdges(rootdiv);
|
|
895
962
|
DragDrop.instance.hideGlass();
|
|
896
963
|
|
package/src/view/Splitter.tsx
CHANGED
|
@@ -13,11 +13,12 @@ import { ILayoutCallbacks } from "./Layout";
|
|
|
13
13
|
export interface ISplitterProps {
|
|
14
14
|
layout: ILayoutCallbacks;
|
|
15
15
|
node: SplitterNode;
|
|
16
|
+
path: string;
|
|
16
17
|
}
|
|
17
18
|
|
|
18
19
|
/** @hidden @internal */
|
|
19
20
|
export const Splitter = (props: ISplitterProps) => {
|
|
20
|
-
const { layout, node } = props;
|
|
21
|
+
const { layout, node, path } = props;
|
|
21
22
|
|
|
22
23
|
const pBounds = React.useRef<number[]>([]);
|
|
23
24
|
const outlineDiv = React.useRef<HTMLDivElement | undefined>(undefined);
|
|
@@ -132,6 +133,7 @@ export const Splitter = (props: ISplitterProps) => {
|
|
|
132
133
|
if (extra === 0) {
|
|
133
134
|
return (<div
|
|
134
135
|
style={style}
|
|
136
|
+
data-layout-path={path}
|
|
135
137
|
className={className}
|
|
136
138
|
onTouchStart={onMouseDown}
|
|
137
139
|
onMouseDown={onMouseDown}>
|
|
@@ -156,6 +158,7 @@ export const Splitter = (props: ISplitterProps) => {
|
|
|
156
158
|
return (
|
|
157
159
|
<div
|
|
158
160
|
style={style}
|
|
161
|
+
data-layout-path={path}
|
|
159
162
|
className={className}>
|
|
160
163
|
<div
|
|
161
164
|
style={style2}
|
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 {
|
|
@@ -15,11 +16,12 @@ export interface ITabProps {
|
|
|
15
16
|
selected: boolean;
|
|
16
17
|
node: TabNode;
|
|
17
18
|
factory: (node: TabNode) => React.ReactNode;
|
|
19
|
+
path: string;
|
|
18
20
|
}
|
|
19
21
|
|
|
20
22
|
/** @hidden @internal */
|
|
21
23
|
export const Tab = (props: ITabProps) => {
|
|
22
|
-
const { layout, selected, node, factory } = props;
|
|
24
|
+
const { layout, selected, node, factory, path } = props;
|
|
23
25
|
const [renderComponent, setRenderComponent] = React.useState<boolean>(!props.node.isEnableRenderOnDemand() || props.selected);
|
|
24
26
|
|
|
25
27
|
React.useLayoutEffect(() => {
|
|
@@ -40,15 +42,17 @@ export const Tab = (props: ITabProps) => {
|
|
|
40
42
|
};
|
|
41
43
|
|
|
42
44
|
const cm = layout.getClassName;
|
|
45
|
+
const useVisibility = node.getModel().isUseVisibility();
|
|
43
46
|
|
|
44
47
|
const parentNode = node.getParent() as TabSetNode | BorderNode;
|
|
45
|
-
const style: Record<string, any> = node._styleWithPosition(
|
|
46
|
-
|
|
47
|
-
|
|
48
|
+
const style: Record<string, any> = node._styleWithPosition();
|
|
49
|
+
if (!selected) {
|
|
50
|
+
hideElement(style, useVisibility);
|
|
51
|
+
}
|
|
48
52
|
|
|
49
53
|
if (parentNode instanceof TabSetNode) {
|
|
50
54
|
if (node.getModel().getMaximizedTabset() !== undefined && !parentNode.isMaximized()) {
|
|
51
|
-
style
|
|
55
|
+
hideElement(style, useVisibility);
|
|
52
56
|
}
|
|
53
57
|
}
|
|
54
58
|
|
|
@@ -64,10 +68,17 @@ export const Tab = (props: ITabProps) => {
|
|
|
64
68
|
}
|
|
65
69
|
|
|
66
70
|
return (
|
|
67
|
-
<div
|
|
71
|
+
<div
|
|
72
|
+
className={className}
|
|
73
|
+
data-layout-path={path}
|
|
74
|
+
onMouseDown={onMouseDown}
|
|
75
|
+
onTouchStart={onMouseDown}
|
|
76
|
+
style={style}>
|
|
68
77
|
<ErrorBoundary message={props.layout.i18nName(I18nLabel.Error_rendering_component)}>
|
|
69
78
|
<Fragment>{child}</Fragment>
|
|
70
79
|
</ErrorBoundary>
|
|
71
80
|
</div>
|
|
72
81
|
);
|
|
73
82
|
};
|
|
83
|
+
|
|
84
|
+
|
package/src/view/TabButton.tsx
CHANGED
|
@@ -4,26 +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
20
|
icons?: IIcons;
|
|
21
|
+
path: string;
|
|
22
22
|
}
|
|
23
23
|
|
|
24
24
|
/** @hidden @internal */
|
|
25
25
|
export const TabButton = (props: ITabButtonProps) => {
|
|
26
|
-
const { layout, node,
|
|
26
|
+
const { layout, node, selected, iconFactory, titleFactory, icons, path } = props;
|
|
27
27
|
const selfRef = React.useRef<HTMLDivElement | null>(null);
|
|
28
28
|
const contentRef = React.useRef<HTMLInputElement | null>(null);
|
|
29
29
|
const contentWidth = React.useRef<number>(0);
|
|
@@ -31,8 +31,7 @@ export const TabButton = (props: ITabButtonProps) => {
|
|
|
31
31
|
const onMouseDown = (event: React.MouseEvent<HTMLDivElement, MouseEvent> | React.TouchEvent<HTMLDivElement>) => {
|
|
32
32
|
|
|
33
33
|
if (!isAuxMouseEvent(event) && !layout.getEditingTab()) {
|
|
34
|
-
|
|
35
|
-
layout.dragStart(event, message, node, node.isEnableDrag(), onClick, onDoubleClick);
|
|
34
|
+
layout.dragStart(event, undefined, node, node.isEnableDrag(), onClick, onDoubleClick);
|
|
36
35
|
}
|
|
37
36
|
};
|
|
38
37
|
|
|
@@ -55,7 +54,7 @@ export const TabButton = (props: ITabButtonProps) => {
|
|
|
55
54
|
layout.setEditingTab(node);
|
|
56
55
|
layout.getCurrentDocument()!.body.addEventListener("mousedown", onEndEdit);
|
|
57
56
|
layout.getCurrentDocument()!.body.addEventListener("touchstart", onEndEdit);
|
|
58
|
-
}
|
|
57
|
+
}
|
|
59
58
|
// else {
|
|
60
59
|
// const parentNode = node.getParent() as TabSetNode;
|
|
61
60
|
// if (parentNode.canMaximize()) {
|
|
@@ -110,7 +109,7 @@ export const TabButton = (props: ITabButtonProps) => {
|
|
|
110
109
|
const layoutRect = layout.getDomRect();
|
|
111
110
|
const r = selfRef.current!.getBoundingClientRect();
|
|
112
111
|
node._setTabRect(new Rect(r.left - layoutRect.left, r.top - layoutRect.top, r.width, r.height));
|
|
113
|
-
contentWidth.current =
|
|
112
|
+
contentWidth.current = selfRef.current!.getBoundingClientRect().width;
|
|
114
113
|
};
|
|
115
114
|
|
|
116
115
|
const onTextBoxMouseDown = (event: React.MouseEvent<HTMLInputElement> | React.TouchEvent<HTMLInputElement>) => {
|
|
@@ -147,55 +146,24 @@ export const TabButton = (props: ITabButtonProps) => {
|
|
|
147
146
|
classNames += " " + node.getClassName();
|
|
148
147
|
}
|
|
149
148
|
|
|
150
|
-
|
|
151
|
-
let titleContent: React.ReactNode = node.getName();
|
|
152
|
-
let name = node.getName();
|
|
153
|
-
|
|
154
|
-
function isTitleObject(obj: any): obj is ITitleObject {
|
|
155
|
-
return obj.titleContent !== undefined
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
if (titleFactory !== undefined) {
|
|
159
|
-
const titleObj = titleFactory(node);
|
|
160
|
-
if (titleObj !== undefined) {
|
|
161
|
-
if (typeof titleObj === "string") {
|
|
162
|
-
titleContent = titleObj as string;
|
|
163
|
-
name = titleObj as string;
|
|
164
|
-
} else if (isTitleObject(titleObj)) {
|
|
165
|
-
titleContent = titleObj.titleContent;
|
|
166
|
-
name = titleObj.name;
|
|
167
|
-
} else {
|
|
168
|
-
titleContent = titleObj;
|
|
169
|
-
}
|
|
170
|
-
}
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
if (typeof leadingContent === undefined && typeof node.getIcon() !== undefined) {
|
|
174
|
-
leadingContent = <img src={node.getIcon()} alt="leadingContent" />;
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
let buttons: any[] = [];
|
|
178
|
-
|
|
179
|
-
// allow customization of leading contents (icon) and contents
|
|
180
|
-
const renderState = { leading: leadingContent, content: titleContent, name, buttons };
|
|
181
|
-
layout.customizeTab(node, renderState);
|
|
182
|
-
|
|
183
|
-
node._setRenderedName(renderState.name);
|
|
149
|
+
const renderState = getRenderStateEx(layout, node, iconFactory, titleFactory);
|
|
184
150
|
|
|
185
|
-
let content = (
|
|
186
|
-
<div
|
|
151
|
+
let content = renderState.content ? (
|
|
152
|
+
<div className={cm(CLASSES.FLEXLAYOUT__TAB_BUTTON_CONTENT)}>
|
|
187
153
|
{renderState.content}
|
|
188
|
-
</div>
|
|
189
|
-
|
|
190
|
-
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;
|
|
191
160
|
|
|
192
161
|
if (layout.getEditingTab() === node) {
|
|
193
|
-
const contentStyle = { width: contentWidth + "px" };
|
|
194
162
|
content = (
|
|
195
163
|
<input
|
|
196
|
-
style={contentStyle}
|
|
197
164
|
ref={contentRef}
|
|
198
165
|
className={cm(CLASSES.FLEXLAYOUT__TAB_BUTTON_TEXTBOX)}
|
|
166
|
+
data-layout-path={path + "/textbox"}
|
|
199
167
|
type="text"
|
|
200
168
|
autoFocus={true}
|
|
201
169
|
defaultValue={node.getName()}
|
|
@@ -208,8 +176,14 @@ export const TabButton = (props: ITabButtonProps) => {
|
|
|
208
176
|
|
|
209
177
|
if (node.isEnableClose()) {
|
|
210
178
|
const closeTitle = layout.i18nName(I18nLabel.Close_Tab);
|
|
211
|
-
buttons.push(
|
|
212
|
-
<div
|
|
179
|
+
renderState.buttons.push(
|
|
180
|
+
<div
|
|
181
|
+
key="close"
|
|
182
|
+
data-layout-path={path + "/button/close"}
|
|
183
|
+
title={closeTitle}
|
|
184
|
+
className={cm(CLASSES.FLEXLAYOUT__TAB_BUTTON_TRAILING)}
|
|
185
|
+
onMouseDown={onCloseMouseDown} onClick={onClose}
|
|
186
|
+
onTouchStart={onCloseMouseDown}>
|
|
213
187
|
{icons?.close}
|
|
214
188
|
</div>
|
|
215
189
|
);
|
|
@@ -218,9 +192,7 @@ export const TabButton = (props: ITabButtonProps) => {
|
|
|
218
192
|
return (
|
|
219
193
|
<div
|
|
220
194
|
ref={selfRef}
|
|
221
|
-
|
|
222
|
-
visibility: show ? "visible" : "hidden",
|
|
223
|
-
}}
|
|
195
|
+
data-layout-path={path}
|
|
224
196
|
className={classNames}
|
|
225
197
|
onMouseDown={onMouseDown}
|
|
226
198
|
onClick={onAuxMouseClick}
|
|
@@ -231,7 +203,7 @@ export const TabButton = (props: ITabButtonProps) => {
|
|
|
231
203
|
>
|
|
232
204
|
{leading}
|
|
233
205
|
{content}
|
|
234
|
-
{buttons}
|
|
206
|
+
{renderState.buttons}
|
|
235
207
|
</div>
|
|
236
208
|
);
|
|
237
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
|
+
};
|