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.
Files changed (83) hide show
  1. package/ChangeLog.txt +22 -0
  2. package/README.md +61 -56
  3. package/declarations/Types.d.ts +1 -0
  4. package/declarations/model/IJsonModel.d.ts +3 -0
  5. package/declarations/model/Model.d.ts +2 -0
  6. package/declarations/view/Icons.d.ts +6 -0
  7. package/declarations/view/Layout.d.ts +6 -4
  8. package/declarations/view/MenuTabButton.d.ts +1 -0
  9. package/declarations/view/TabButtonStamp.d.ts +1 -0
  10. package/declarations/view/Utils.d.ts +1 -0
  11. package/dist/flexlayout.js +50 -14
  12. package/dist/flexlayout_min.js +1 -1
  13. package/lib/PopupMenu.js +11 -7
  14. package/lib/PopupMenu.js.map +1 -1
  15. package/lib/Types.js +1 -0
  16. package/lib/Types.js.map +1 -1
  17. package/lib/model/Model.js +8 -0
  18. package/lib/model/Model.js.map +1 -1
  19. package/lib/model/TabNode.js +6 -1
  20. package/lib/model/TabNode.js.map +1 -1
  21. package/lib/view/BorderButton.js +11 -41
  22. package/lib/view/BorderButton.js.map +1 -1
  23. package/lib/view/BorderTabSet.js +7 -7
  24. package/lib/view/BorderTabSet.js.map +1 -1
  25. package/lib/view/FloatingWindow.js +13 -5
  26. package/lib/view/FloatingWindow.js.map +1 -1
  27. package/lib/view/Icons.js +36 -0
  28. package/lib/view/Icons.js.map +1 -0
  29. package/lib/view/Layout.js +80 -25
  30. package/lib/view/Layout.js.map +1 -1
  31. package/lib/view/MenuTabButton.js +22 -0
  32. package/lib/view/MenuTabButton.js.map +1 -0
  33. package/lib/view/Splitter.js +3 -3
  34. package/lib/view/Splitter.js.map +1 -1
  35. package/lib/view/Tab.js +9 -6
  36. package/lib/view/Tab.js.map +1 -1
  37. package/lib/view/TabButton.js +13 -46
  38. package/lib/view/TabButton.js.map +1 -1
  39. package/lib/view/TabButtonStamp.js +22 -0
  40. package/lib/view/TabButtonStamp.js.map +1 -0
  41. package/lib/view/TabFloating.js +7 -5
  42. package/lib/view/TabFloating.js.map +1 -1
  43. package/lib/view/TabOverflowHook.js +1 -1
  44. package/lib/view/TabSet.js +15 -25
  45. package/lib/view/TabSet.js.map +1 -1
  46. package/lib/view/Utils.js +61 -0
  47. package/lib/view/Utils.js.map +1 -0
  48. package/package.json +11 -6
  49. package/src/I18nLabel.ts +1 -1
  50. package/src/PopupMenu.tsx +30 -10
  51. package/src/Types.ts +1 -0
  52. package/src/model/IJsonModel.ts +3 -0
  53. package/src/model/Model.ts +12 -0
  54. package/src/model/TabNode.ts +6 -1
  55. package/src/view/BorderButton.tsx +19 -43
  56. package/src/view/BorderTabSet.tsx +14 -4
  57. package/src/view/FloatingWindow.tsx +14 -6
  58. package/src/view/Icons.tsx +36 -0
  59. package/src/view/Layout.tsx +108 -41
  60. package/src/view/Splitter.tsx +4 -1
  61. package/src/view/Tab.tsx +17 -6
  62. package/src/view/TabButton.tsx +27 -55
  63. package/src/view/TabButtonStamp.tsx +47 -0
  64. package/src/view/TabFloating.tsx +12 -5
  65. package/src/view/TabOverflowHook.tsx +1 -1
  66. package/src/view/TabSet.tsx +27 -17
  67. package/src/view/Utils.tsx +71 -0
  68. package/style/_base.scss +82 -52
  69. package/style/dark.css +82 -76
  70. package/style/dark.css.map +1 -1
  71. package/style/dark.scss +15 -5
  72. package/style/gray.css +79 -73
  73. package/style/gray.css.map +1 -1
  74. package/style/gray.scss +10 -3
  75. package/style/light.css +83 -77
  76. package/style/light.css.map +1 -1
  77. package/style/light.scss +16 -6
  78. package/images/close.png +0 -0
  79. package/images/maximize.png +0 -0
  80. package/images/more.png +0 -0
  81. package/images/more2.png +0 -0
  82. package/images/popout.png +0 -0
  83. package/images/restore.png +0 -0
@@ -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 = (text: String, node?: Node, json?: IJsonTabNode) => React.ReactElement | undefined;
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>) : void;
167
- auxMouseClick(node: TabNode | TabSetNode | BorderNode, event: React.MouseEvent<HTMLElement, MouseEvent>) : void;
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
- // For backwards compatibility, prop closeIcon sets prop icons.close:
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.props.font.size) {
286
- style.fontSize = this.props.font.size;
287
- }
288
- if (this.props.font.family) {
289
- style.fontFamily = this.props.font.family;
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={"border_" + border.getLocation().getName()}
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
- splitterComponents.push(<Splitter key={child.getId()} layout={this} node={child} />);
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()} layout={this} node={child} selected={i === border.getSelected()} />;
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()} layout={this} node={child} selected={i === border.getSelected()} factory={this.props.factory} />;
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
- splitterComponents.push(<Splitter key={child.getId()} layout={this} node={child} />);
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
- tabSetComponents.push(<TabSet key={child.getId()} layout={this} node={child} iconFactory={this.props.iconFactory} titleFactory={this.props.titleFactory} icons={this.icons} />);
598
- this.renderChildren(child, tabSetComponents, tabComponents, floatingWindows, splitterComponents);
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
- this.renderChildren(child as RowNode, tabSetComponents, tabComponents, floatingWindows, splitterComponents);
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 = <div style={{ whiteSpace: "pre" }}>{text.replace("<br>", "\n")}</div>;
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(text, node, json);
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
- ReactDOM.render(<DragRectRenderWrapper
807
- // wait for it to be rendered
808
- onRendered={() => {
809
- this.dragRectRendered = true;
810
- onRendered?.();
811
- }}
812
- >
813
- {content}
814
- </DragRectRenderWrapper>, this.dragDiv!);
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
 
@@ -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
- display: selected ? "block" : "none",
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.display = "none";
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 className={className} onMouseDown={onMouseDown} onTouchStart={onMouseDown} style={style}>
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
+
@@ -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, ITitleObject } from "./Layout";
7
+ import { IIcons, ILayoutCallbacks } from "./Layout";
8
8
  import { ICloseType } from "../model/ICloseType";
9
9
  import { CLASSES } from "../Types";
10
- import { isAuxMouseEvent } from "./TabSet";
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, show, selected, iconFactory, titleFactory, icons } = props;
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
- const message = layout.i18nName(I18nLabel.Move_Tab, node.getName());
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 = contentRef.current!.getBoundingClientRect().width;
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
- let leadingContent = iconFactory ? iconFactory(node) : undefined;
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 ref={contentRef} className={cm(CLASSES.FLEXLAYOUT__TAB_BUTTON_CONTENT)}>
151
+ let content = renderState.content ? (
152
+ <div className={cm(CLASSES.FLEXLAYOUT__TAB_BUTTON_CONTENT)}>
187
153
  {renderState.content}
188
- </div>
189
- );
190
- const leading = <div className={cm(CLASSES.FLEXLAYOUT__TAB_BUTTON_LEADING)}>{renderState.leading}</div>;
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 key="close" title={closeTitle} className={cm(CLASSES.FLEXLAYOUT__TAB_BUTTON_TRAILING)} onMouseDown={onCloseMouseDown} onClick={onClose} onTouchStart={onCloseMouseDown}>
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
- style={{
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
+ };