flexlayout-react 0.5.18 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (92) hide show
  1. package/ChangeLog.txt +27 -0
  2. package/README.md +126 -108
  3. package/declarations/Types.d.ts +8 -1
  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 +8 -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 +53 -17
  12. package/dist/flexlayout_min.js +1 -1
  13. package/lib/PopupMenu.js +22 -12
  14. package/lib/PopupMenu.js.map +1 -1
  15. package/lib/Types.js +7 -0
  16. package/lib/Types.js.map +1 -1
  17. package/lib/model/BorderNode.js +8 -7
  18. package/lib/model/BorderNode.js.map +1 -1
  19. package/lib/model/Model.js +15 -3
  20. package/lib/model/Model.js.map +1 -1
  21. package/lib/model/RowNode.js +19 -5
  22. package/lib/model/RowNode.js.map +1 -1
  23. package/lib/model/TabNode.js +6 -1
  24. package/lib/model/TabNode.js.map +1 -1
  25. package/lib/model/TabSetNode.js +8 -4
  26. package/lib/model/TabSetNode.js.map +1 -1
  27. package/lib/view/BorderButton.js +19 -38
  28. package/lib/view/BorderButton.js.map +1 -1
  29. package/lib/view/BorderTabSet.js +19 -8
  30. package/lib/view/BorderTabSet.js.map +1 -1
  31. package/lib/view/FloatingWindow.js +13 -5
  32. package/lib/view/FloatingWindow.js.map +1 -1
  33. package/lib/view/Icons.js +36 -0
  34. package/lib/view/Icons.js.map +1 -0
  35. package/lib/view/Layout.js +148 -71
  36. package/lib/view/Layout.js.map +1 -1
  37. package/lib/view/MenuTabButton.js +22 -0
  38. package/lib/view/MenuTabButton.js.map +1 -0
  39. package/lib/view/Splitter.js +3 -3
  40. package/lib/view/Splitter.js.map +1 -1
  41. package/lib/view/Tab.js +9 -6
  42. package/lib/view/Tab.js.map +1 -1
  43. package/lib/view/TabButton.js +20 -44
  44. package/lib/view/TabButton.js.map +1 -1
  45. package/lib/view/TabButtonStamp.js +22 -0
  46. package/lib/view/TabButtonStamp.js.map +1 -0
  47. package/lib/view/TabFloating.js +29 -15
  48. package/lib/view/TabFloating.js.map +1 -1
  49. package/lib/view/TabOverflowHook.js +1 -1
  50. package/lib/view/TabSet.js +40 -25
  51. package/lib/view/TabSet.js.map +1 -1
  52. package/lib/view/Utils.js +61 -0
  53. package/lib/view/Utils.js.map +1 -0
  54. package/package.json +11 -6
  55. package/src/I18nLabel.ts +1 -1
  56. package/src/PopupMenu.tsx +54 -15
  57. package/src/Types.ts +7 -0
  58. package/src/model/BorderNode.ts +8 -7
  59. package/src/model/IJsonModel.ts +3 -0
  60. package/src/model/Model.ts +19 -3
  61. package/src/model/RowNode.ts +8 -5
  62. package/src/model/TabNode.ts +6 -1
  63. package/src/model/TabSetNode.ts +8 -4
  64. package/src/view/BorderButton.tsx +38 -43
  65. package/src/view/BorderTabSet.tsx +34 -7
  66. package/src/view/FloatingWindow.tsx +14 -6
  67. package/src/view/Icons.tsx +36 -0
  68. package/src/view/Layout.tsx +179 -88
  69. package/src/view/Splitter.tsx +4 -1
  70. package/src/view/Tab.tsx +17 -6
  71. package/src/view/TabButton.tsx +42 -55
  72. package/src/view/TabButtonStamp.tsx +47 -0
  73. package/src/view/TabFloating.tsx +47 -23
  74. package/src/view/TabOverflowHook.tsx +1 -1
  75. package/src/view/TabSet.tsx +71 -22
  76. package/src/view/Utils.tsx +71 -0
  77. package/style/_base.scss +146 -92
  78. package/style/dark.css +157 -129
  79. package/style/dark.css.map +1 -1
  80. package/style/dark.scss +31 -21
  81. package/style/gray.css +157 -129
  82. package/style/gray.css.map +1 -1
  83. package/style/gray.scss +30 -23
  84. package/style/light.css +157 -129
  85. package/style/light.css.map +1 -1
  86. package/style/light.scss +30 -20
  87. package/images/close.png +0 -0
  88. package/images/maximize.png +0 -0
  89. package/images/more.png +0 -0
  90. package/images/more2.png +0 -0
  91. package/images/popout.png +0 -0
  92. package/images/restore.png +0 -0
@@ -25,9 +25,14 @@ 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;
34
+ export type FloatingTabPlaceholderRenderCallback = (dockPopout: () => void, showPopout: () => void) => React.ReactElement | undefined;
35
+ export type NodeMouseEvent = (node: TabNode | TabSetNode | BorderNode, event: React.MouseEvent<HTMLElement, MouseEvent>) => void;
31
36
 
32
37
  export interface ILayoutProps {
33
38
  model: Model;
@@ -36,16 +41,15 @@ export interface ILayoutProps {
36
41
  fontFamily?: string;
37
42
  iconFactory?: (node: TabNode) => React.ReactNode | undefined;
38
43
  titleFactory?: (node: TabNode) => ITitleObject | React.ReactNode | undefined;
39
- closeIcon?: React.ReactNode;
40
44
  icons?: IIcons;
41
45
  onAction?: (action: Action) => Action | undefined;
42
46
  onRenderTab?: (
43
47
  node: TabNode,
44
- renderValues: ITabRenderValues,
48
+ renderValues: ITabRenderValues, // change the values in this object as required
45
49
  ) => void;
46
50
  onRenderTabSet?: (
47
51
  tabSetNode: TabSetNode | BorderNode,
48
- renderValues: ITabSetRenderValues,
52
+ renderValues: ITabSetRenderValues, // change the values in this object as required
49
53
  ) => void;
50
54
  onModelChange?: (model: Model) => void;
51
55
  onExternalDrag?: (event: React.DragEvent<HTMLDivElement>) => undefined | {
@@ -69,6 +73,9 @@ export interface ILayoutProps {
69
73
  cursor?: string | undefined
70
74
  };
71
75
  onRenderDragRect?: DragRectRenderCallback;
76
+ onRenderFloatingTabPlaceholder?: FloatingTabPlaceholderRenderCallback;
77
+ onContextMenu?: NodeMouseEvent;
78
+ onAuxMouseClick?: NodeMouseEvent;
72
79
  }
73
80
  export interface IFontValues {
74
81
  size?: string;
@@ -114,6 +121,15 @@ export interface IIcons {
114
121
  more?: React.ReactNode;
115
122
  }
116
123
 
124
+ const defaultIcons = {
125
+ close: <CloseIcon/>,
126
+ closeTabset: <CloseIcon/>,
127
+ popout: <PopoutIcon/>,
128
+ maximize: <MaximizeIcon/>,
129
+ restore: <RestoreIcon/>,
130
+ more: <OverflowIcon/>,
131
+ };
132
+
117
133
  export interface ICustomDropDestination {
118
134
  rect: Rect;
119
135
  callback: CustomDragCallback;
@@ -140,7 +156,7 @@ export interface ILayoutCallbacks {
140
156
  getRootDiv(): HTMLDivElement;
141
157
  dragStart(
142
158
  event: Event | React.MouseEvent<HTMLDivElement, MouseEvent> | React.TouchEvent<HTMLDivElement> | React.DragEvent<HTMLDivElement> | undefined,
143
- dragDivText: string,
159
+ dragDivText: string | undefined,
144
160
  node: Node & IDraggable,
145
161
  allowDrag: boolean,
146
162
  onClick?: (event: Event) => void,
@@ -157,7 +173,9 @@ export interface ILayoutCallbacks {
157
173
  styleFont: (style: Record<string, string>) => Record<string, string>;
158
174
  setEditingTab(tabNode?: TabNode): void;
159
175
  getEditingTab(): TabNode | undefined;
160
-
176
+ getOnRenderFloatingTabPlaceholder(): FloatingTabPlaceholderRenderCallback | undefined;
177
+ showContextMenu(node: TabNode | TabSetNode | BorderNode, event: React.MouseEvent<HTMLElement, MouseEvent>): void;
178
+ auxMouseClick(node: TabNode | TabSetNode | BorderNode, event: React.MouseEvent<HTMLElement, MouseEvent>): void;
161
179
  }
162
180
 
163
181
  // Popout windows work in latest browsers based on webkit (Chrome, Opera, Safari, latest Edge) and Firefox. They do
@@ -207,7 +225,7 @@ export class Layout extends React.Component<ILayoutProps, ILayoutState> {
207
225
  /** @hidden @internal */
208
226
  private dragRectRendered: boolean = true;
209
227
  /** @hidden @internal */
210
- private dragDivText: string = "";
228
+ private dragDivText: string | undefined = undefined;
211
229
  /** @hidden @internal */
212
230
  private dropInfo: DropInfo | undefined;
213
231
  /** @hidden @internal */
@@ -256,8 +274,7 @@ export class Layout extends React.Component<ILayoutProps, ILayoutState> {
256
274
  this.findBorderBarSizeRef = React.createRef<HTMLDivElement>();
257
275
  this.supportsPopout = props.supportsPopout !== undefined ? props.supportsPopout : defaultSupportsPopout;
258
276
  this.popoutURL = props.popoutURL ? props.popoutURL : "popout.html";
259
- // For backwards compatibility, prop closeIcon sets prop icons.close:
260
- this.icons = props.closeIcon ? Object.assign({ close: props.closeIcon }, props.icons) : props.icons;
277
+ this.icons = {...defaultIcons, ...props.icons};
261
278
  this.firstRender = true;
262
279
 
263
280
  this.state = {
@@ -275,11 +292,13 @@ export class Layout extends React.Component<ILayoutProps, ILayoutState> {
275
292
  /** @hidden @internal */
276
293
  styleFont(style: Record<string, string>): Record<string, string> {
277
294
  if (this.props.font) {
278
- if (this.props.font.size) {
279
- style.fontSize = this.props.font.size;
280
- }
281
- if (this.props.font.family) {
282
- style.fontFamily = this.props.font.family;
295
+ if (this.selfRef.current) {
296
+ if (this.props.font.size) {
297
+ this.selfRef.current.style.setProperty("--font-size", this.props.font.size);
298
+ }
299
+ if (this.props.font.family) {
300
+ this.selfRef.current.style.setProperty("--font-family", this.props.font.family);
301
+ }
283
302
  }
284
303
  if (this.props.font.style) {
285
304
  style.fontStyle = this.props.font.style;
@@ -451,14 +470,14 @@ export class Layout extends React.Component<ILayoutProps, ILayoutState> {
451
470
  const metrics: ILayoutMetrics = {
452
471
  headerBarSize: this.state.calculatedHeaderBarSize,
453
472
  tabBarSize: this.state.calculatedTabBarSize,
454
- borderBarSize: this.state.calculatedBorderBarSize,
473
+ borderBarSize: this.state.calculatedBorderBarSize
455
474
  };
456
475
  this.props.model._setShowHiddenBorder(this.state.showHiddenBorder);
457
476
 
458
477
  this.centerRect = this.props.model._layout(this.state.rect, metrics);
459
478
 
460
479
  this.renderBorder(this.props.model.getBorderSet(), borderComponents, tabComponents, floatingWindows, splitterComponents);
461
- this.renderChildren(this.props.model.getRoot(), tabSetComponents, tabComponents, floatingWindows, splitterComponents);
480
+ this.renderChildren("", this.props.model.getRoot(), tabSetComponents, tabComponents, floatingWindows, splitterComponents);
462
481
 
463
482
  if (this.edgesShown) {
464
483
  this.repositionEdges(this.state.rect)
@@ -536,10 +555,12 @@ export class Layout extends React.Component<ILayoutProps, ILayoutState> {
536
555
  /** @hidden @internal */
537
556
  renderBorder(borderSet: BorderSet, borderComponents: React.ReactNode[], tabComponents: Record<string, React.ReactNode>, floatingWindows: React.ReactNode[], splitterComponents: React.ReactNode[]) {
538
557
  for (const border of borderSet.getBorders()) {
558
+ const borderPath = `/border/${border.getLocation().getName()}`;
539
559
  if (border.isShowing()) {
540
560
  borderComponents.push(
541
561
  <BorderTabSet
542
- key={"border_" + border.getLocation().getName()}
562
+ key={`border_${border.getLocation().getName()}`}
563
+ path={borderPath}
543
564
  border={border}
544
565
  layout={this}
545
566
  iconFactory={this.props.iconFactory}
@@ -549,10 +570,13 @@ export class Layout extends React.Component<ILayoutProps, ILayoutState> {
549
570
  );
550
571
  const drawChildren = border._getDrawChildren();
551
572
  let i = 0;
573
+ let tabCount = 0;
552
574
  for (const child of drawChildren) {
553
575
  if (child instanceof SplitterNode) {
554
- splitterComponents.push(<Splitter key={child.getId()} layout={this} node={child} />);
576
+ let path = borderPath + "/s";
577
+ splitterComponents.push(<Splitter key={child.getId()} layout={this} node={child} path={path} />);
555
578
  } else if (child instanceof TabNode) {
579
+ let path = borderPath + "/t" + tabCount++;
556
580
  if (this.supportsPopout && child.isFloating()) {
557
581
  const rect = this._getScreenRect(child);
558
582
  floatingWindows.push(
@@ -568,9 +592,19 @@ export class Layout extends React.Component<ILayoutProps, ILayoutState> {
568
592
  <FloatingWindowTab layout={this} node={child} factory={this.props.factory} />
569
593
  </FloatingWindow>
570
594
  );
571
- tabComponents[child.getId()] = <TabFloating key={child.getId()} layout={this} node={child} selected={i === border.getSelected()} />;
595
+ tabComponents[child.getId()] = <TabFloating key={child.getId()}
596
+ layout={this}
597
+ path={path}
598
+ node={child}
599
+ selected={i === border.getSelected()
600
+ } />;
572
601
  } else {
573
- tabComponents[child.getId()] = <Tab key={child.getId()} layout={this} node={child} selected={i === border.getSelected()} factory={this.props.factory} />;
602
+ tabComponents[child.getId()] = <Tab key={child.getId()}
603
+ layout={this}
604
+ path={path}
605
+ node={child}
606
+ selected={i === border.getSelected()}
607
+ factory={this.props.factory} />;
574
608
  }
575
609
  }
576
610
  i++;
@@ -580,16 +614,22 @@ export class Layout extends React.Component<ILayoutProps, ILayoutState> {
580
614
  }
581
615
 
582
616
  /** @hidden @internal */
583
- renderChildren(node: RowNode | TabSetNode, tabSetComponents: React.ReactNode[], tabComponents: Record<string, React.ReactNode>, floatingWindows: React.ReactNode[], splitterComponents: React.ReactNode[]) {
617
+ renderChildren(path: string, node: RowNode | TabSetNode, tabSetComponents: React.ReactNode[], tabComponents: Record<string, React.ReactNode>, floatingWindows: React.ReactNode[], splitterComponents: React.ReactNode[]) {
584
618
  const drawChildren = node._getDrawChildren();
619
+ let splitterCount = 0;
620
+ let tabCount = 0;
621
+ let rowCount = 0;
585
622
 
586
623
  for (const child of drawChildren!) {
587
624
  if (child instanceof SplitterNode) {
588
- splitterComponents.push(<Splitter key={child.getId()} layout={this} node={child} />);
625
+ const newPath = path + "/s" + (splitterCount++);
626
+ splitterComponents.push(<Splitter key={child.getId()} layout={this} path={newPath} node={child} />);
589
627
  } else if (child instanceof TabSetNode) {
590
- tabSetComponents.push(<TabSet key={child.getId()} layout={this} node={child} iconFactory={this.props.iconFactory} titleFactory={this.props.titleFactory} icons={this.icons} />);
591
- this.renderChildren(child, tabSetComponents, tabComponents, floatingWindows, splitterComponents);
628
+ const newPath = path + "/ts" + (rowCount++);
629
+ tabSetComponents.push(<TabSet key={child.getId()} layout={this} path={newPath} node={child} iconFactory={this.props.iconFactory} titleFactory={this.props.titleFactory} icons={this.icons} />);
630
+ this.renderChildren(newPath, child, tabSetComponents, tabComponents, floatingWindows, splitterComponents);
592
631
  } else if (child instanceof TabNode) {
632
+ const newPath = path + "/t" + (tabCount++);
593
633
  const selectedTab = child.getParent()!.getChildren()[(child.getParent() as TabSetNode).getSelected()];
594
634
  if (selectedTab === undefined) {
595
635
  // this should not happen!
@@ -610,13 +650,14 @@ export class Layout extends React.Component<ILayoutProps, ILayoutState> {
610
650
  <FloatingWindowTab layout={this} node={child} factory={this.props.factory} />
611
651
  </FloatingWindow>
612
652
  );
613
- tabComponents[child.getId()] = <TabFloating key={child.getId()} layout={this} node={child} selected={child === selectedTab} />;
653
+ tabComponents[child.getId()] = <TabFloating key={child.getId()} layout={this} path={newPath} node={child} selected={child === selectedTab} />;
614
654
  } else {
615
- tabComponents[child.getId()] = <Tab key={child.getId()} layout={this} node={child} selected={child === selectedTab} factory={this.props.factory} />;
655
+ tabComponents[child.getId()] = <Tab key={child.getId()} layout={this} path={newPath} node={child} selected={child === selectedTab} factory={this.props.factory} />;
616
656
  }
617
657
  } else {
618
658
  // is row
619
- this.renderChildren(child as RowNode, tabSetComponents, tabComponents, floatingWindows, splitterComponents);
659
+ const newPath = path + ((child.getOrientation() === Orientation.HORZ) ? "/r" : "/c") + (rowCount++);
660
+ this.renderChildren(newPath, child as RowNode, tabSetComponents, tabComponents, floatingWindows, splitterComponents);
620
661
  }
621
662
  }
622
663
  }
@@ -661,7 +702,7 @@ export class Layout extends React.Component<ILayoutProps, ILayoutState> {
661
702
  * @param json the json for the new tab node
662
703
  * @param onDrop a callback to call when the drag is complete (node and event will be undefined if the drag was cancelled)
663
704
  */
664
- addTabWithDragAndDrop(dragText: string, json: IJsonTabNode, onDrop?: (node?: Node, event?: Event) => void) {
705
+ addTabWithDragAndDrop(dragText: string | undefined, json: IJsonTabNode, onDrop?: (node?: Node, event?: Event) => void) {
665
706
  this.fnNewNodeDropped = onDrop;
666
707
  this.newTabJson = json;
667
708
  this.dragStart(undefined, dragText, TabNode._fromJson(json, this.props.model, false), true, undefined, undefined);
@@ -675,7 +716,7 @@ export class Layout extends React.Component<ILayoutProps, ILayoutState> {
675
716
  * @param json the json for the new tab node
676
717
  * @param onDrop a callback to call when the drag is complete (node and event will be undefined if the drag was cancelled)
677
718
  */
678
- addTabWithDragAndDropIndirect(dragText: string, json: IJsonTabNode, onDrop?: (node?: Node, event?: Event) => void) {
719
+ addTabWithDragAndDropIndirect(dragText: string | undefined, json: IJsonTabNode, onDrop?: (node?: Node, event?: Event) => void) {
679
720
  this.fnNewNodeDropped = onDrop;
680
721
  this.newTabJson = json;
681
722
 
@@ -683,7 +724,7 @@ export class Layout extends React.Component<ILayoutProps, ILayoutState> {
683
724
 
684
725
  this.dragDivText = dragText;
685
726
  this.dragDiv = this.currentDocument!.createElement("div");
686
- this.dragDiv.className = this.getClassName("flexlayout__drag_rect");
727
+ this.dragDiv.className = this.getClassName(CLASSES.FLEXLAYOUT__DRAG_RECT);
687
728
  this.dragDiv.addEventListener("mousedown", this.onDragDivMouseDown);
688
729
  this.dragDiv.addEventListener("touchstart", this.onDragDivMouseDown);
689
730
 
@@ -694,6 +735,7 @@ export class Layout extends React.Component<ILayoutProps, ILayoutState> {
694
735
  const domRect = this.dragDiv.getBoundingClientRect();
695
736
  const r = new Rect(0, 0, domRect?.width, domRect?.height);
696
737
  r.centerInRect(this.state.rect);
738
+ this.dragDiv.setAttribute("data-layout-path", "/drag-rectangle");
697
739
  this.dragDiv.style.left = r.x + "px";
698
740
  this.dragDiv.style.top = r.y + "px";
699
741
  }
@@ -767,7 +809,7 @@ export class Layout extends React.Component<ILayoutProps, ILayoutState> {
767
809
  /** @hidden @internal */
768
810
  dragStart = (
769
811
  event: Event | React.MouseEvent<HTMLDivElement, MouseEvent> | React.TouchEvent<HTMLDivElement> | React.DragEvent<HTMLDivElement> | undefined,
770
- dragDivText: string,
812
+ dragDivText: string | undefined,
771
813
  node: Node & IDraggable,
772
814
  allowDrag: boolean,
773
815
  onClick?: (event: Event) => void,
@@ -783,11 +825,24 @@ export class Layout extends React.Component<ILayoutProps, ILayoutState> {
783
825
  };
784
826
 
785
827
  /** @hidden @internal */
786
- dragRectRender = (text: String, node?: Node, json?: IJsonTabNode, onRendered?: () => void) => {
787
- let content: React.ReactElement | undefined = <div style={{ whiteSpace: "pre" }}>{text.replace("<br>", "\n")}</div>;
828
+ dragRectRender = (text: String | undefined, node?: Node, json?: IJsonTabNode, onRendered?: () => void) => {
829
+ let content: React.ReactElement | undefined;
830
+
831
+ if (text !== undefined) {
832
+ content = <div style={{ whiteSpace: "pre" }}>{text.replace("<br>", "\n")}</div>;
833
+ } else {
834
+ if (node && node instanceof TabNode) {
835
+ content = (<TabButtonStamp
836
+ node={node}
837
+ layout={this}
838
+ iconFactory={this.props.iconFactory}
839
+ titleFactory={this.props.titleFactory}
840
+ />);
841
+ }
842
+ }
788
843
 
789
844
  if (this.props.onRenderDragRect !== undefined) {
790
- const customContent = this.props.onRenderDragRect(text, node, json);
845
+ const customContent = this.props.onRenderDragRect(content, node, json);
791
846
  if (customContent !== undefined) {
792
847
  content = customContent;
793
848
  }
@@ -820,6 +875,7 @@ export class Layout extends React.Component<ILayoutProps, ILayoutState> {
820
875
  if (this.dragDiv == null) {
821
876
  this.dragDiv = this.currentDocument!.createElement("div");
822
877
  this.dragDiv.className = this.getClassName(CLASSES.FLEXLAYOUT__DRAG_RECT);
878
+ this.dragDiv.setAttribute("data-layout-path", "/drag-rectangle");
823
879
  this.dragRectRender(this.dragDivText, this.dragNode, this.newTabJson);
824
880
 
825
881
  rootdiv.appendChild(this.dragDiv);
@@ -867,62 +923,13 @@ export class Layout extends React.Component<ILayoutProps, ILayoutState> {
867
923
 
868
924
  let dropInfo = this.props.model._findDropTargetNode(this.dragNode!, pos.x, pos.y);
869
925
  if (dropInfo) {
870
- let invalidated = this.customDrop?.invalidated;
871
- const currentCallback = this.customDrop?.callback;
872
- this.customDrop = undefined;
873
-
874
- const dragging = this.newTabJson || (this.dragNode instanceof TabNode ? this.dragNode : undefined);
875
- if (dragging && (dropInfo.node instanceof TabSetNode || dropInfo.node instanceof BorderNode) && dropInfo.index === -1) {
876
- const selected = dropInfo.node.getSelectedNode() as TabNode | undefined;
877
- const tabRect = selected?.getRect()
878
-
879
- if (selected && tabRect?.contains(pos.x, pos.y)) {
880
- let customDrop: ICustomDropDestination | undefined = undefined;
881
-
882
- try {
883
- const dest = this.onTabDrag(dragging, selected, pos.x - tabRect.x, pos.y - tabRect.y, dropInfo.location, () => this.onDragMove(event));
884
-
885
- if (dest) {
886
- customDrop = {
887
- rect: new Rect(dest.x + tabRect.x, dest.y + tabRect.y, dest.width, dest.height),
888
- callback: dest.callback,
889
- invalidated: dest.invalidated,
890
- dragging: dragging,
891
- over: selected,
892
- x: pos.x - tabRect.x,
893
- y: pos.y - tabRect.y,
894
- location: dropInfo.location,
895
- cursor: dest.cursor
896
- };
897
- }
898
- } catch (e) {
899
- console.error(e)
900
- }
901
-
902
- if (customDrop?.callback === currentCallback) {
903
- invalidated = undefined;
904
- }
905
-
906
- this.customDrop = customDrop;
907
- }
908
- }
909
-
910
- this.dropInfo = dropInfo;
911
- this.outlineDiv!.className = this.getClassName(this.customDrop ? "flexlayout__outline_rect" : dropInfo.className);
912
-
913
- if (this.customDrop) {
914
- this.customDrop.rect.positionElement(this.outlineDiv!);
926
+ if (this.props.onTabDrag) {
927
+ this.handleCustomTabDrag(dropInfo, pos, event);
915
928
  } else {
929
+ this.dropInfo = dropInfo;
930
+ this.outlineDiv!.className = this.getClassName(dropInfo.className);
916
931
  dropInfo.rect.positionElement(this.outlineDiv!);
917
- }
918
-
919
- DragDrop.instance.setGlassCursorOverride(this.customDrop?.cursor);
920
- this.outlineDiv!.style.visibility = "visible";
921
-
922
- try {
923
- invalidated?.();
924
- } catch (e) {
925
- console.error(e);
932
+ this.outlineDiv!.style.visibility = "visible";
926
933
  }
927
934
  }
928
935
  };
@@ -943,6 +950,10 @@ export class Layout extends React.Component<ILayoutProps, ILayoutState> {
943
950
  try {
944
951
  const { callback, dragging, over, x, y, location } = this.customDrop;
945
952
  callback(dragging, over, x, y, location);
953
+ if (this.fnNewNodeDropped != null) {
954
+ this.fnNewNodeDropped();
955
+ this.fnNewNodeDropped = undefined;
956
+ }
946
957
  } catch (e) {
947
958
  console.error(e)
948
959
  }
@@ -961,6 +972,67 @@ export class Layout extends React.Component<ILayoutProps, ILayoutState> {
961
972
  this.setState({ showHiddenBorder: DockLocation.CENTER });
962
973
  };
963
974
 
975
+ /** @hidden @internal */
976
+ private handleCustomTabDrag(dropInfo: DropInfo, pos: { x: number; y: number; }, event: React.MouseEvent<Element, MouseEvent>) {
977
+ let invalidated = this.customDrop?.invalidated;
978
+ const currentCallback = this.customDrop?.callback;
979
+ this.customDrop = undefined;
980
+
981
+ const dragging = this.newTabJson || (this.dragNode instanceof TabNode ? this.dragNode : undefined);
982
+ if (dragging && (dropInfo.node instanceof TabSetNode || dropInfo.node instanceof BorderNode) && dropInfo.index === -1) {
983
+ const selected = dropInfo.node.getSelectedNode() as TabNode | undefined;
984
+ const tabRect = selected?.getRect();
985
+
986
+ if (selected && tabRect?.contains(pos.x, pos.y)) {
987
+ let customDrop: ICustomDropDestination | undefined = undefined;
988
+
989
+ try {
990
+ const dest = this.onTabDrag(dragging, selected, pos.x - tabRect.x, pos.y - tabRect.y, dropInfo.location, () => this.onDragMove(event));
991
+
992
+ if (dest) {
993
+ customDrop = {
994
+ rect: new Rect(dest.x + tabRect.x, dest.y + tabRect.y, dest.width, dest.height),
995
+ callback: dest.callback,
996
+ invalidated: dest.invalidated,
997
+ dragging: dragging,
998
+ over: selected,
999
+ x: pos.x - tabRect.x,
1000
+ y: pos.y - tabRect.y,
1001
+ location: dropInfo.location,
1002
+ cursor: dest.cursor
1003
+ };
1004
+ }
1005
+ } catch (e) {
1006
+ console.error(e);
1007
+ }
1008
+
1009
+ if (customDrop?.callback === currentCallback) {
1010
+ invalidated = undefined;
1011
+ }
1012
+
1013
+ this.customDrop = customDrop;
1014
+ }
1015
+ }
1016
+
1017
+ this.dropInfo = dropInfo;
1018
+ this.outlineDiv!.className = this.getClassName(this.customDrop ? CLASSES.FLEXLAYOUT__OUTLINE_RECT : dropInfo.className);
1019
+
1020
+ if (this.customDrop) {
1021
+ this.customDrop.rect.positionElement(this.outlineDiv!);
1022
+ } else {
1023
+ dropInfo.rect.positionElement(this.outlineDiv!);
1024
+ }
1025
+
1026
+ DragDrop.instance.setGlassCursorOverride(this.customDrop?.cursor);
1027
+ this.outlineDiv!.style.visibility = "visible";
1028
+
1029
+ try {
1030
+ invalidated?.();
1031
+ } catch (e) {
1032
+ console.error(e);
1033
+ }
1034
+ }
1035
+
964
1036
  /** @hidden @internal */
965
1037
  onDragEnter(event: React.DragEvent<HTMLDivElement>) {
966
1038
  // DragDrop keeps track of number of dragenters minus the number of
@@ -1125,6 +1197,25 @@ export class Layout extends React.Component<ILayoutProps, ILayoutState> {
1125
1197
  }
1126
1198
  return message;
1127
1199
  }
1200
+
1201
+ /** @hidden @internal */
1202
+ getOnRenderFloatingTabPlaceholder() {
1203
+ return this.props.onRenderFloatingTabPlaceholder;
1204
+ }
1205
+
1206
+ /** @hidden @internal */
1207
+ showContextMenu(node: TabNode | TabSetNode | BorderNode, event: React.MouseEvent<HTMLElement, MouseEvent>) {
1208
+ if (this.props.onContextMenu) {
1209
+ this.props.onContextMenu(node, event);
1210
+ }
1211
+ }
1212
+
1213
+ /** @hidden @internal */
1214
+ auxMouseClick(node: TabNode | TabSetNode | BorderNode, event: React.MouseEvent<HTMLElement, MouseEvent>) {
1215
+ if (this.props.onAuxMouseClick) {
1216
+ this.props.onAuxMouseClick(node, event);
1217
+ }
1218
+ }
1128
1219
  }
1129
1220
 
1130
1221
  // wrapper round the drag rect renderer that can call
@@ -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
+