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
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "flexlayout-react",
3
- "version": "0.5.18",
3
+ "version": "0.6.0",
4
4
  "description": "A multi-tab docking layout manager",
5
5
  "main": "lib/index.js",
6
6
  "types": "./declarations/index.d.ts",
@@ -20,14 +20,16 @@
20
20
  ],
21
21
  "scripts": {
22
22
  "start": "webpack serve",
23
- "build": "npm run build:commonjs && npm run build:umd && npm run build:umd:min && npm run css",
23
+ "build": " npm run css && npm run test && npm run build:commonjs && npm run build:umd && npm run build:umd:min",
24
24
  "build:commonjs": "tsc -p tsconfig2.json",
25
25
  "build:umd": "webpack --mode=development --config webpack_build.config.js",
26
26
  "build:umd:min": "webpack --mode=production --config webpack_build_min.config.js",
27
- "test": "jasmine",
28
27
  "lint:eslint": "eslint src/*",
29
28
  "doc": "typedoc --out typedoc --exclude \"**/examples/**/*.tsx\" --excludeInternal --disableSources --excludePrivate --excludeProtected --readme none ./src",
30
- "css": "sass style/gray.scss style/gray.css && sass style/light.scss style/light.css && sass style/dark.scss style/dark.css"
29
+ "css": "sass style/gray.scss style/gray.css && sass style/light.scss style/light.css && sass style/light.scss test/style/light.css && sass style/dark.scss style/dark.css",
30
+ "cypress": "cypress open-ct",
31
+ "cypress-firefox": "cypress open-ct --browser firefox",
32
+ "test": "cypress run-ct"
31
33
  },
32
34
  "author": "Caplin Systems Ltd",
33
35
  "repository": "https://github.com/caplin/FlexLayout",
@@ -40,14 +42,17 @@
40
42
  "extends": "react-app"
41
43
  },
42
44
  "devDependencies": {
43
- "@types/jasmine": "^3.7.2",
45
+ "@cypress/react": "^5.10.2",
46
+ "@cypress/webpack-dev-server": "^1.7.0",
44
47
  "@types/node": "^15.0.2",
45
48
  "@types/react": "^17.0.5",
46
49
  "@types/react-dom": "^17.0.4",
47
50
  "@types/uuid": "^8.3.1",
48
51
  "awesome-typescript-loader": "^5.2.0",
49
- "jasmine": "^3.7.0",
52
+ "cypress": "^8.7.0",
50
53
  "prettier": "^2.3.0",
54
+ "react": "^17.0.2",
55
+ "react-dom": "^17.0.2",
51
56
  "react-scripts": "4.0.3",
52
57
  "sass": "^1.32.12",
53
58
  "source-map-loader": "^1.1.2",
package/src/I18nLabel.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  export enum I18nLabel {
2
2
  Close_Tab = "Close",
3
3
  Close_Tabset = "Close tabset",
4
- Move_Tab = "Move: ",
4
+ Move_Tab = "Move: ", // no longer used
5
5
  Move_Tabset = "Move tabset",
6
6
  Maximize = "Maximize tabset",
7
7
  Restore = "Restore tabset",
package/src/PopupMenu.tsx CHANGED
@@ -1,21 +1,28 @@
1
1
  import * as React from "react";
2
2
  import * as ReactDOM from "react-dom";
3
+ import { DragDrop } from ".";
3
4
  import TabNode from "./model/TabNode";
5
+ import { CLASSES } from "./Types";
6
+ import { ILayoutCallbacks } from "./view/Layout";
7
+ import { TabButtonStamp } from "./view/TabButtonStamp";
4
8
 
5
9
  /** @hidden @internal */
6
10
  export function showPopup(
7
- layoutDiv: HTMLDivElement,
8
11
  triggerElement: Element,
9
12
  items: { index: number; node: TabNode }[],
10
13
  onSelect: (item: { index: number; node: TabNode }) => void,
11
- classNameMapper: (defaultClassName: string) => string
14
+ layout: ILayoutCallbacks,
15
+ iconFactory?: (node: TabNode) => React.ReactNode | undefined,
16
+ titleFactory?: (node: TabNode) => React.ReactNode | undefined,
12
17
  ) {
18
+ const layoutDiv = layout.getRootDiv();
19
+ const classNameMapper = layout.getClassName;
13
20
  const currentDocument = triggerElement.ownerDocument;
14
21
  const triggerRect = triggerElement.getBoundingClientRect();
15
22
  const layoutRect = layoutDiv.getBoundingClientRect();
16
23
 
17
24
  const elm = currentDocument.createElement("div");
18
- elm.className = classNameMapper("flexlayout__popup_menu_container");
25
+ elm.className = classNameMapper(CLASSES.FLEXLAYOUT__POPUP_MENU_CONTAINER);
19
26
  if (triggerRect.left < layoutRect.left + layoutRect.width / 2) {
20
27
  elm.style.left = triggerRect.left - layoutRect.left + "px";
21
28
  } else {
@@ -27,27 +34,40 @@ export function showPopup(
27
34
  } else {
28
35
  elm.style.bottom = layoutRect.bottom - triggerRect.bottom + "px";
29
36
  }
37
+ DragDrop.instance.addGlass(() => onHide());
38
+ DragDrop.instance.setGlassCursorOverride("default");
39
+
30
40
  layoutDiv.appendChild(elm);
31
41
 
32
42
  const onHide = () => {
43
+ DragDrop.instance.hideGlass();
33
44
  layoutDiv.removeChild(elm);
34
45
  ReactDOM.unmountComponentAtNode(elm);
35
- elm.removeEventListener("mouseup", onElementMouseUp);
36
- currentDocument.removeEventListener("mouseup", onDocMouseUp);
46
+ elm.removeEventListener("mousedown", onElementMouseDown);
47
+ currentDocument.removeEventListener("mousedown", onDocMouseDown);
37
48
  };
38
49
 
39
- const onElementMouseUp = (event: Event) => {
50
+ const onElementMouseDown = (event: Event) => {
40
51
  event.stopPropagation();
41
52
  };
42
53
 
43
- const onDocMouseUp = (event: Event) => {
54
+ const onDocMouseDown = (event: Event) => {
44
55
  onHide();
45
56
  };
46
57
 
47
- elm.addEventListener("mouseup", onElementMouseUp);
48
- currentDocument.addEventListener("mouseup", onDocMouseUp);
58
+ elm.addEventListener("mousedown", onElementMouseDown);
59
+ currentDocument.addEventListener("mousedown", onDocMouseDown);
49
60
 
50
- ReactDOM.render(<PopupMenu currentDocument={currentDocument} onSelect={onSelect} onHide={onHide} items={items} classNameMapper={classNameMapper} />, elm);
61
+ ReactDOM.render(<PopupMenu
62
+ currentDocument={currentDocument}
63
+ onSelect={onSelect}
64
+ onHide={onHide}
65
+ items={items}
66
+ classNameMapper={classNameMapper}
67
+ layout={layout}
68
+ iconFactory={iconFactory}
69
+ titleFactory={titleFactory}
70
+ />, elm);
51
71
  }
52
72
 
53
73
  /** @hidden @internal */
@@ -57,11 +77,14 @@ interface IPopupMenuProps {
57
77
  onHide: () => void;
58
78
  onSelect: (item: { index: number; node: TabNode }) => void;
59
79
  classNameMapper: (defaultClassName: string) => string;
80
+ layout: ILayoutCallbacks;
81
+ iconFactory?: (node: TabNode) => React.ReactNode | undefined;
82
+ titleFactory?: (node: TabNode) => React.ReactNode | undefined;
60
83
  }
61
84
 
62
85
  /** @hidden @internal */
63
86
  const PopupMenu = (props: IPopupMenuProps) => {
64
- const { items, onHide, onSelect, classNameMapper } = props;
87
+ const { items, onHide, onSelect, classNameMapper, layout, iconFactory, titleFactory} = props;
65
88
 
66
89
  const onItemClick = (item: { index: number; node: TabNode }, event: React.MouseEvent<HTMLDivElement, MouseEvent>) => {
67
90
  onSelect(item);
@@ -69,11 +92,27 @@ const PopupMenu = (props: IPopupMenuProps) => {
69
92
  event.stopPropagation();
70
93
  };
71
94
 
72
- const itemElements = items.map((item) => (
73
- <div key={item.index} className={classNameMapper("flexlayout__popup_menu_item")} onClick={(event) => onItemClick(item, event)} title={item.node.getHelpText()}>
74
- {item.node._getRenderedName()}
95
+ const itemElements = items.map((item, i) => (
96
+ <div key={item.index}
97
+ className={classNameMapper(CLASSES.FLEXLAYOUT__POPUP_MENU_ITEM)}
98
+ data-layout-path={"/popup-menu/tb" + i}
99
+ onClick={(event) => onItemClick(item, event)}
100
+ title={item.node.getHelpText()} >
101
+ {item.node.getModel().isLegacyOverflowMenu() ?
102
+ item.node._getNameForOverflowMenu() :
103
+ <TabButtonStamp
104
+ node={item.node}
105
+ layout={layout}
106
+ iconFactory={iconFactory}
107
+ titleFactory={titleFactory}
108
+ />}
75
109
  </div>
76
110
  ));
77
111
 
78
- return <div className={classNameMapper("flexlayout__popup_menu")}>{itemElements}</div>;
112
+ return (
113
+ <div className={classNameMapper(CLASSES.FLEXLAYOUT__POPUP_MENU)}
114
+ data-layout-path="/popup-menu"
115
+ >
116
+ {itemElements}
117
+ </div>);
79
118
  };
package/src/Types.ts CHANGED
@@ -9,6 +9,8 @@ export enum CLASSES {
9
9
  FLEXLAYOUT__BORDER_BUTTON_TRAILING = "flexlayout__border_button_trailing",
10
10
  FLEXLAYOUT__BORDER_BUTTON__SELECTED = "flexlayout__border_button--selected",
11
11
  FLEXLAYOUT__BORDER_BUTTON__UNSELECTED = "flexlayout__border_button--unselected",
12
+ FLEXLAYOUT__BORDER_TOOLBAR_BUTTON_OVERFLOW = "flexlayout__border_toolbar_button_overflow",
13
+ FLEXLAYOUT__BORDER_TOOLBAR_BUTTON_OVERFLOW_ = "flexlayout__border_toolbar_button_overflow_",
12
14
 
13
15
  FLEXLAYOUT__BORDER_INNER = "flexlayout__border_inner",
14
16
  FLEXLAYOUT__BORDER_INNER_ = "flexlayout__border_inner_",
@@ -34,6 +36,7 @@ export enum CLASSES {
34
36
  FLEXLAYOUT__LAYOUT = "flexlayout__layout",
35
37
 
36
38
  FLEXLAYOUT__OUTLINE_RECT = "flexlayout__outline_rect",
39
+ FLEXLAYOUT__OUTLINE_RECT_EDGE = "flexlayout__outline_rect_edge",
37
40
 
38
41
  FLEXLAYOUT__SPLITTER = "flexlayout__splitter",
39
42
  FLEXLAYOUT__SPLITTER_EXTRA = "flexlayout__splitter_extra",
@@ -67,6 +70,7 @@ export enum CLASSES {
67
70
  FLEXLAYOUT__TAB_BUTTON_OVERFLOW = "flexlayout__tab_button_overflow",
68
71
  FLEXLAYOUT__TAB_BUTTON_TEXTBOX = "flexlayout__tab_button_textbox",
69
72
  FLEXLAYOUT__TAB_BUTTON_TRAILING = "flexlayout__tab_button_trailing",
73
+ FLEXLAYOUT__TAB_BUTTON_STAMP = "flexlayout__tab_button_stamp",
70
74
 
71
75
  FLEXLAYOUT__TAB_FLOATING = "flexlayout__tab_floating",
72
76
  FLEXLAYOUT__TAB_FLOATING_INNER = "flexlayout__tab_floating_inner",
@@ -78,4 +82,7 @@ export enum CLASSES {
78
82
  FLEXLAYOUT__TAB_TOOLBAR_STICKY_BUTTONS_CONTAINER = "flexlayout__tab_toolbar_sticky_buttons_container",
79
83
  FLEXLAYOUT__TAB_TOOLBAR_BUTTON_CLOSE = "flexlayout__tab_toolbar_button-close",
80
84
 
85
+ FLEXLAYOUT__POPUP_MENU_CONTAINER = "flexlayout__popup_menu_container",
86
+ FLEXLAYOUT__POPUP_MENU_ITEM = "flexlayout__popup_menu_item",
87
+ FLEXLAYOUT__POPUP_MENU = "flexlayout__popup_menu",
81
88
  }
@@ -4,6 +4,7 @@ import DockLocation from "../DockLocation";
4
4
  import DropInfo from "../DropInfo";
5
5
  import Orientation from "../Orientation";
6
6
  import Rect from "../Rect";
7
+ import { CLASSES } from "../Types";
7
8
  import IDraggable from "./IDraggable";
8
9
  import IDropTarget from "./IDropTarget";
9
10
  import { IJsonBorderNode } from "./IJsonModel";
@@ -309,18 +310,18 @@ class BorderNode extends Node implements IDropTarget {
309
310
  childCenter = childRect.x + childRect.width / 2;
310
311
  if (x >= pos && x < childCenter) {
311
312
  const outlineRect = new Rect(childRect.x - 2, childY, 3, childHeight);
312
- dropInfo = new DropInfo(this, outlineRect, dockLocation, i, "flexlayout__outline_rect");
313
+ dropInfo = new DropInfo(this, outlineRect, dockLocation, i, CLASSES.FLEXLAYOUT__OUTLINE_RECT);
313
314
  break;
314
315
  }
315
316
  pos = childCenter;
316
317
  }
317
318
  if (dropInfo == null) {
318
319
  const outlineRect = new Rect(childRect.getRight() - 2, childY, 3, childHeight);
319
- dropInfo = new DropInfo(this, outlineRect, dockLocation, this._children.length, "flexlayout__outline_rect");
320
+ dropInfo = new DropInfo(this, outlineRect, dockLocation, this._children.length, CLASSES.FLEXLAYOUT__OUTLINE_RECT);
320
321
  }
321
322
  } else {
322
323
  const outlineRect = new Rect(this._tabHeaderRect!.x + 1, this._tabHeaderRect!.y + 2, 3, 18);
323
- dropInfo = new DropInfo(this, outlineRect, dockLocation, 0, "flexlayout__outline_rect");
324
+ dropInfo = new DropInfo(this, outlineRect, dockLocation, 0, CLASSES.FLEXLAYOUT__OUTLINE_RECT);
324
325
  }
325
326
  } else {
326
327
  if (this._children.length > 0) {
@@ -337,18 +338,18 @@ class BorderNode extends Node implements IDropTarget {
337
338
  childCenter = childRect.y + childRect.height / 2;
338
339
  if (y >= pos && y < childCenter) {
339
340
  const outlineRect = new Rect(childX, childRect.y - 2, childWidth, 3);
340
- dropInfo = new DropInfo(this, outlineRect, dockLocation, i, "flexlayout__outline_rect");
341
+ dropInfo = new DropInfo(this, outlineRect, dockLocation, i, CLASSES.FLEXLAYOUT__OUTLINE_RECT);
341
342
  break;
342
343
  }
343
344
  pos = childCenter;
344
345
  }
345
346
  if (dropInfo == null) {
346
347
  const outlineRect = new Rect(childX, childRect.getBottom() - 2, childWidth, 3);
347
- dropInfo = new DropInfo(this, outlineRect, dockLocation, this._children.length, "flexlayout__outline_rect");
348
+ dropInfo = new DropInfo(this, outlineRect, dockLocation, this._children.length, CLASSES.FLEXLAYOUT__OUTLINE_RECT);
348
349
  }
349
350
  } else {
350
351
  const outlineRect = new Rect(this._tabHeaderRect!.x + 2, this._tabHeaderRect!.y + 1, 18, 3);
351
- dropInfo = new DropInfo(this, outlineRect, dockLocation, 0, "flexlayout__outline_rect");
352
+ dropInfo = new DropInfo(this, outlineRect, dockLocation, 0, CLASSES.FLEXLAYOUT__OUTLINE_RECT);
352
353
  }
353
354
  }
354
355
  if (!dragNode._canDockInto(dragNode, dropInfo)) {
@@ -356,7 +357,7 @@ class BorderNode extends Node implements IDropTarget {
356
357
  }
357
358
  } else if (this.getSelected() !== -1 && this._contentRect!.contains(x, y)) {
358
359
  const outlineRect = this._contentRect;
359
- dropInfo = new DropInfo(this, outlineRect!, dockLocation, -1, "flexlayout__outline_rect");
360
+ dropInfo = new DropInfo(this, outlineRect!, dockLocation, -1, CLASSES.FLEXLAYOUT__OUTLINE_RECT);
360
361
  if (!dragNode._canDockInto(dragNode, dropInfo)) {
361
362
  return undefined;
362
363
  }
@@ -40,6 +40,8 @@ export interface IGlobalAttributes {
40
40
  borderMinSize?: number; // default: 0
41
41
  borderSize?: number; // default: 200
42
42
  enableEdgeDock?: boolean; // default: true
43
+ enableUseVisibility?: boolean; // default: false
44
+ legacyOverflowMenu?: boolean; // default: false
43
45
  marginInsets?: IInsets; // default: {"top":0,"right":0,"bottom":0,"left":0}
44
46
  rootOrientationVertical?: boolean; // default: false
45
47
  splitterExtra?: number; // default: 0
@@ -108,6 +110,7 @@ export interface ITabSetAttributes {
108
110
  width?: number;
109
111
  }
110
112
  export interface ITabAttributes {
113
+ altName?: string;
111
114
  borderHeight?: number; // default: -1 - inherited from global tabBorderHeight
112
115
  borderWidth?: number; // default: -1 - inherited from global tabBorderWidth
113
116
  className?: string; // - inherited from global tabClassName
@@ -52,6 +52,9 @@ class Model {
52
52
  /** @hidden @internal */
53
53
  private static _createAttributeDefinitions(): AttributeDefinitions {
54
54
  const attributeDefinitions = new AttributeDefinitions();
55
+
56
+ attributeDefinitions.add("legacyOverflowMenu", false).setType(Attribute.BOOLEAN);
57
+
55
58
  // splitter
56
59
  attributeDefinitions.add("splitterSize", -1).setType(Attribute.NUMBER);
57
60
  attributeDefinitions.add("splitterExtra", 0).setType(Attribute.NUMBER);
@@ -59,6 +62,7 @@ class Model {
59
62
  attributeDefinitions.add("rootOrientationVertical", false).setType(Attribute.BOOLEAN);
60
63
  attributeDefinitions.add("marginInsets", { top: 0, right: 0, bottom: 0, left: 0 })
61
64
  .setType("IInsets");
65
+ attributeDefinitions.add("enableUseVisibility", false).setType(Attribute.BOOLEAN);
62
66
 
63
67
  // tab
64
68
  attributeDefinitions.add("tabEnableClose", true).setType(Attribute.BOOLEAN);
@@ -201,6 +205,10 @@ class Model {
201
205
  return this._attributes.rootOrientationVertical as boolean;
202
206
  }
203
207
 
208
+ isUseVisibility() {
209
+ return this._attributes.enableUseVisibility as boolean;
210
+ }
211
+
204
212
  /**
205
213
  * Gets the
206
214
  * @returns {BorderSet|*}
@@ -281,13 +289,17 @@ class Model {
281
289
  const node = this._idMap[action.data.node];
282
290
 
283
291
  if (node instanceof TabSetNode) {
284
- // first delete all child tabs
292
+ // first delete all child tabs that are closeable
285
293
  const children = [...node.getChildren()];
286
294
  children.forEach((child, i) => {
287
- (child as TabNode)._delete();
295
+ if ((child as TabNode).isEnableClose()) {
296
+ (child as TabNode)._delete();
297
+ }
288
298
  });
289
299
 
290
- node._delete();
300
+ if (node.getChildren().length === 0) {
301
+ node._delete();
302
+ }
291
303
  this._tidy();
292
304
  }
293
305
  break;
@@ -439,6 +451,10 @@ class Model {
439
451
  return splitterSize;
440
452
  }
441
453
 
454
+ isLegacyOverflowMenu() {
455
+ return this._attributes.legacyOverflowMenu as boolean;
456
+ }
457
+
442
458
  getSplitterExtra() {
443
459
  return this._attributes.splitterExtra as number;
444
460
  }
@@ -5,6 +5,7 @@ import DockLocation from "../DockLocation";
5
5
  import DropInfo from "../DropInfo";
6
6
  import Orientation from "../Orientation";
7
7
  import Rect from "../Rect";
8
+ import { CLASSES } from "../Types";
8
9
  import BorderNode from "./BorderNode";
9
10
  import IDraggable from "./IDraggable";
10
11
  import IDropTarget from "./IDropTarget";
@@ -381,7 +382,9 @@ class RowNode extends Node implements IDropTarget {
381
382
  // add tabset into empty root
382
383
  if (this === this._model.getRoot() && this._children.length === 0) {
383
384
  const callback = this._model._getOnCreateTabSet();
384
- const child = new TabSetNode(this._model, callback ? callback() : {});
385
+ let attrs = callback ? callback() : {};
386
+ attrs = {...attrs, selected: -1};
387
+ const child = new TabSetNode(this._model, attrs);
385
388
  this._model._setActiveTabset(child);
386
389
  this._addChild(child);
387
390
  }
@@ -404,24 +407,24 @@ class RowNode extends Node implements IDropTarget {
404
407
  const dockLocation = DockLocation.LEFT;
405
408
  const outlineRect = dockLocation.getDockRect(this._rect);
406
409
  outlineRect.width = outlineRect.width / 2;
407
- dropInfo = new DropInfo(this, outlineRect, dockLocation, -1, "flexlayout__outline_rect_edge");
410
+ dropInfo = new DropInfo(this, outlineRect, dockLocation, -1, CLASSES.FLEXLAYOUT__OUTLINE_RECT_EDGE);
408
411
  } else if (x > this._rect.getRight() - margin && yy > h / 2 - half && yy < h / 2 + half) {
409
412
  const dockLocation = DockLocation.RIGHT;
410
413
  const outlineRect = dockLocation.getDockRect(this._rect);
411
414
  outlineRect.width = outlineRect.width / 2;
412
415
  outlineRect.x += outlineRect.width;
413
- dropInfo = new DropInfo(this, outlineRect, dockLocation, -1, "flexlayout__outline_rect_edge");
416
+ dropInfo = new DropInfo(this, outlineRect, dockLocation, -1, CLASSES.FLEXLAYOUT__OUTLINE_RECT_EDGE);
414
417
  } else if (y < this._rect.y + margin && xx > w / 2 - half && xx < w / 2 + half) {
415
418
  const dockLocation = DockLocation.TOP;
416
419
  const outlineRect = dockLocation.getDockRect(this._rect);
417
420
  outlineRect.height = outlineRect.height / 2;
418
- dropInfo = new DropInfo(this, outlineRect, dockLocation, -1, "flexlayout__outline_rect_edge");
421
+ dropInfo = new DropInfo(this, outlineRect, dockLocation, -1, CLASSES.FLEXLAYOUT__OUTLINE_RECT_EDGE);
419
422
  } else if (y > this._rect.getBottom() - margin && xx > w / 2 - half && xx < w / 2 + half) {
420
423
  const dockLocation = DockLocation.BOTTOM;
421
424
  const outlineRect = dockLocation.getDockRect(this._rect);
422
425
  outlineRect.height = outlineRect.height / 2;
423
426
  outlineRect.y += outlineRect.height;
424
- dropInfo = new DropInfo(this, outlineRect, dockLocation, -1, "flexlayout__outline_rect_edge");
427
+ dropInfo = new DropInfo(this, outlineRect, dockLocation, -1, CLASSES.FLEXLAYOUT__OUTLINE_RECT_EDGE);
425
428
  }
426
429
 
427
430
  if (dropInfo !== undefined) {
@@ -26,6 +26,7 @@ class TabNode extends Node implements IDraggable {
26
26
  attributeDefinitions.add("id", undefined).setType(Attribute.STRING);
27
27
 
28
28
  attributeDefinitions.add("name", "[Unnamed Tab]").setType(Attribute.STRING);
29
+ attributeDefinitions.add("altName", undefined).setType(Attribute.STRING);
29
30
  attributeDefinitions.add("helpText", undefined).setType(Attribute.STRING);
30
31
  attributeDefinitions.add("component", undefined).setType(Attribute.STRING);
31
32
  attributeDefinitions.add("config", undefined).setType("any");
@@ -84,7 +85,11 @@ class TabNode extends Node implements IDraggable {
84
85
  }
85
86
 
86
87
  /** @hidden @internal */
87
- _getRenderedName() {
88
+ _getNameForOverflowMenu() {
89
+ const altName = this._getAttr("altName") as string;
90
+ if (altName !== undefined) {
91
+ return altName;
92
+ }
88
93
  return this._renderedName;
89
94
  }
90
95
 
@@ -4,6 +4,7 @@ import DockLocation from "../DockLocation";
4
4
  import DropInfo from "../DropInfo";
5
5
  import Orientation from "../Orientation";
6
6
  import Rect from "../Rect";
7
+ import { CLASSES } from "../Types";
7
8
  import BorderNode from "./BorderNode";
8
9
  import IDraggable from "./IDraggable";
9
10
  import IDropTarget from "./IDropTarget";
@@ -27,6 +28,9 @@ class TabSetNode extends Node implements IDraggable, IDropTarget {
27
28
  newLayoutNode._addChild(child);
28
29
  });
29
30
  }
31
+ if (newLayoutNode._children.length === 0) {
32
+ newLayoutNode._setSelected(-1);
33
+ }
30
34
 
31
35
  if (json.maximized && json.maximized === true) {
32
36
  model._setMaximizedTabset(newLayoutNode);
@@ -268,11 +272,11 @@ class TabSetNode extends Node implements IDraggable, IDropTarget {
268
272
  if (dragNode === this) {
269
273
  const dockLocation = DockLocation.CENTER;
270
274
  const outlineRect = this._tabHeaderRect;
271
- dropInfo = new DropInfo(this, outlineRect!, dockLocation, -1, "flexlayout__outline_rect");
275
+ dropInfo = new DropInfo(this, outlineRect!, dockLocation, -1, CLASSES.FLEXLAYOUT__OUTLINE_RECT);
272
276
  } else if (this._contentRect!.contains(x, y)) {
273
277
  const dockLocation = DockLocation.getLocation(this._contentRect!, x, y);
274
278
  const outlineRect = dockLocation.getDockRect(this._rect);
275
- dropInfo = new DropInfo(this, outlineRect, dockLocation, -1, "flexlayout__outline_rect");
279
+ dropInfo = new DropInfo(this, outlineRect, dockLocation, -1, CLASSES.FLEXLAYOUT__OUTLINE_RECT);
276
280
  } else if (this._tabHeaderRect != null && this._tabHeaderRect.contains(x, y)) {
277
281
  let r: Rect;
278
282
  let yy: number;
@@ -296,7 +300,7 @@ class TabSetNode extends Node implements IDraggable, IDropTarget {
296
300
  if (x >= p && x < childCenter) {
297
301
  const dockLocation = DockLocation.CENTER;
298
302
  const outlineRect = new Rect(r.x - 2, yy, 3, h);
299
- dropInfo = new DropInfo(this, outlineRect, dockLocation, i, "flexlayout__outline_rect");
303
+ dropInfo = new DropInfo(this, outlineRect, dockLocation, i, CLASSES.FLEXLAYOUT__OUTLINE_RECT);
300
304
  break;
301
305
  }
302
306
  p = childCenter;
@@ -305,7 +309,7 @@ class TabSetNode extends Node implements IDraggable, IDropTarget {
305
309
  if (dropInfo == null) {
306
310
  const dockLocation = DockLocation.CENTER;
307
311
  const outlineRect = new Rect(r.getRight() - 2, yy, 3, h);
308
- dropInfo = new DropInfo(this, outlineRect, dockLocation, this._children.length, "flexlayout__outline_rect");
312
+ dropInfo = new DropInfo(this, outlineRect, dockLocation, this._children.length, CLASSES.FLEXLAYOUT__OUTLINE_RECT);
309
313
  }
310
314
  }
311
315
 
@@ -3,9 +3,10 @@ import { I18nLabel } from "..";
3
3
  import Actions from "../model/Actions";
4
4
  import TabNode from "../model/TabNode";
5
5
  import Rect from "../Rect";
6
- import { IIcons, ILayoutCallbacks, ITitleObject } from "./Layout";
6
+ import { IIcons, ILayoutCallbacks } from "./Layout";
7
7
  import { ICloseType } from "../model/ICloseType";
8
8
  import { CLASSES } from "../Types";
9
+ import { getRenderStateEx, isAuxMouseEvent } from "./Utils";
9
10
 
10
11
  /** @hidden @internal */
11
12
  export interface IBorderButtonProps {
@@ -16,16 +17,28 @@ export interface IBorderButtonProps {
16
17
  iconFactory?: (node: TabNode) => React.ReactNode | undefined;
17
18
  titleFactory?: (node: TabNode) => React.ReactNode | undefined;
18
19
  icons?: IIcons;
20
+ path: string;
19
21
  }
20
22
 
21
23
  /** @hidden @internal */
22
24
  export const BorderButton = (props: IBorderButtonProps) => {
23
- const { layout, node, selected, border, iconFactory, titleFactory, icons } = props;
25
+ const { layout, node, selected, border, iconFactory, titleFactory, icons, path } = props;
24
26
  const selfRef = React.useRef<HTMLDivElement | null>(null);
25
27
 
26
28
  const onMouseDown = (event: React.MouseEvent<HTMLDivElement, MouseEvent> | React.TouchEvent<HTMLDivElement>) => {
27
- const message = layout.i18nName(I18nLabel.Move_Tab, node.getName());
28
- props.layout.dragStart(event, message, node, node.isEnableDrag(), onClick, (event2: Event) => undefined);
29
+ if (!isAuxMouseEvent(event)) {
30
+ props.layout.dragStart(event, undefined, node, node.isEnableDrag(), onClick, (event2: Event) => undefined);
31
+ }
32
+ };
33
+
34
+ const onAuxMouseClick = (event: React.MouseEvent<HTMLDivElement, MouseEvent>) => {
35
+ if (isAuxMouseEvent(event)) {
36
+ layout.auxMouseClick(node, event);
37
+ }
38
+ };
39
+
40
+ const onContextMenu = (event: React.MouseEvent<HTMLDivElement, MouseEvent>) => {
41
+ layout.showContextMenu(node, event);
29
42
  };
30
43
 
31
44
  const onClick = () => {
@@ -82,57 +95,39 @@ export const BorderButton = (props: IBorderButtonProps) => {
82
95
  classNames += " " + node.getClassName();
83
96
  }
84
97
 
85
- let leadingContent = iconFactory ? iconFactory(node) : undefined;
86
- let titleContent: React.ReactNode = node.getName();
87
- let name = node.getName();
88
-
89
- function isTitleObject(obj: any): obj is ITitleObject {
90
- return obj.titleContent !== undefined
91
- }
92
-
93
- if (titleFactory !== undefined) {
94
- const titleObj = titleFactory(node);
95
- if (titleObj !== undefined) {
96
- if (typeof titleObj === "string") {
97
- titleContent = titleObj as string;
98
- name = titleObj as string;
99
- } else if (isTitleObject(titleObj)) {
100
- titleContent = titleObj.titleContent;
101
- name = titleObj.name;
102
- } else {
103
- titleContent = titleObj;
104
- }
105
- }
106
- }
107
-
108
- if (typeof leadingContent === undefined && typeof node.getIcon() !== undefined) {
109
- leadingContent = <img src={node.getIcon()} alt="leadingContent" />;
110
- }
111
-
112
- let buttons: any[] = [];
113
-
114
- // allow customization of leading contents (icon) and contents
115
- const renderState = { leading: leadingContent, content: titleContent, name, buttons };
116
- layout.customizeTab(node, renderState);
117
- node._setRenderedName(renderState.name);
98
+ const renderState = getRenderStateEx(layout, node, iconFactory, titleFactory);
118
99
 
119
- const content = <div className={cm(CLASSES.FLEXLAYOUT__BORDER_BUTTON_CONTENT)}>{renderState.content}</div>;
120
- const leading = <div className={cm(CLASSES.FLEXLAYOUT__BORDER_BUTTON_LEADING)}>{renderState.leading}</div>;
100
+ const content = renderState.content ? (<div className={cm(CLASSES.FLEXLAYOUT__BORDER_BUTTON_CONTENT)}>{renderState.content}</div>) : null;
101
+ const leading = renderState.leading ? (<div className={cm(CLASSES.FLEXLAYOUT__BORDER_BUTTON_LEADING)}>{renderState.leading}</div>) : null;
121
102
 
122
103
  if (node.isEnableClose()) {
123
104
  const closeTitle = layout.i18nName(I18nLabel.Close_Tab);
124
- buttons.push(
125
- <div key="close" title={closeTitle} className={cm(CLASSES.FLEXLAYOUT__BORDER_BUTTON_TRAILING)} onMouseDown={onCloseMouseDown} onClick={onClose} onTouchStart={onCloseMouseDown}>
105
+ renderState.buttons.push(
106
+ <div
107
+ key="close"
108
+ data-layout-path={path + "/button/close"}
109
+ title={closeTitle}
110
+ className={cm(CLASSES.FLEXLAYOUT__BORDER_BUTTON_TRAILING)}
111
+ onMouseDown={onCloseMouseDown}
112
+ onClick={onClose}
113
+ onTouchStart={onCloseMouseDown}>
126
114
  {icons?.close}
127
115
  </div>
128
116
  );
129
117
  }
130
118
 
131
119
  return (
132
- <div ref={selfRef} style={{}} className={classNames} onMouseDown={onMouseDown} onTouchStart={onMouseDown} title={node.getHelpText()}>
120
+ <div ref={selfRef} style={{}} className={classNames}
121
+ data-layout-path={path}
122
+ onMouseDown={onMouseDown}
123
+ onClick={onAuxMouseClick}
124
+ onAuxClick={onAuxMouseClick}
125
+ onContextMenu={onContextMenu}
126
+ onTouchStart={onMouseDown}
127
+ title={node.getHelpText()}>
133
128
  {leading}
134
129
  {content}
135
- {buttons}
130
+ {renderState.buttons}
136
131
  </div>
137
132
  );
138
133
  };