flexlayout-react 0.5.21 → 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 (78) hide show
  1. package/ChangeLog.txt +11 -0
  2. package/README.md +7 -5
  3. package/declarations/Types.d.ts +1 -0
  4. package/declarations/model/IJsonModel.d.ts +2 -0
  5. package/declarations/model/Model.d.ts +1 -0
  6. package/declarations/view/Icons.d.ts +6 -0
  7. package/declarations/view/Layout.d.ts +3 -4
  8. package/declarations/view/MenuTabButton.d.ts +1 -0
  9. package/declarations/view/Tab.d.ts +1 -1
  10. package/declarations/view/TabButtonStamp.d.ts +1 -0
  11. package/declarations/view/Utils.d.ts +1 -0
  12. package/dist/flexlayout.js +48 -12
  13. package/dist/flexlayout_min.js +1 -1
  14. package/lib/PopupMenu.js +9 -4
  15. package/lib/PopupMenu.js.map +1 -1
  16. package/lib/Types.js +1 -0
  17. package/lib/Types.js.map +1 -1
  18. package/lib/model/Model.js +4 -0
  19. package/lib/model/Model.js.map +1 -1
  20. package/lib/model/TabNode.js +6 -1
  21. package/lib/model/TabNode.js.map +1 -1
  22. package/lib/view/BorderButton.js +9 -39
  23. package/lib/view/BorderButton.js.map +1 -1
  24. package/lib/view/BorderTabSet.js +4 -4
  25. package/lib/view/BorderTabSet.js.map +1 -1
  26. package/lib/view/Icons.js +36 -0
  27. package/lib/view/Icons.js.map +1 -0
  28. package/lib/view/Layout.js +41 -11
  29. package/lib/view/Layout.js.map +1 -1
  30. package/lib/view/MenuTabButton.js +22 -0
  31. package/lib/view/MenuTabButton.js.map +1 -0
  32. package/lib/view/Tab.js +4 -12
  33. package/lib/view/Tab.js.map +1 -1
  34. package/lib/view/TabButton.js +11 -42
  35. package/lib/view/TabButton.js.map +1 -1
  36. package/lib/view/TabButtonStamp.js +22 -0
  37. package/lib/view/TabButtonStamp.js.map +1 -0
  38. package/lib/view/TabFloating.js +2 -2
  39. package/lib/view/TabFloating.js.map +1 -1
  40. package/lib/view/TabOverflowHook.js +1 -1
  41. package/lib/view/TabSet.js +7 -18
  42. package/lib/view/TabSet.js.map +1 -1
  43. package/lib/view/Utils.js +61 -0
  44. package/lib/view/Utils.js.map +1 -0
  45. package/package.json +1 -1
  46. package/src/I18nLabel.ts +1 -1
  47. package/src/PopupMenu.tsx +23 -5
  48. package/src/Types.ts +1 -0
  49. package/src/model/IJsonModel.ts +2 -0
  50. package/src/model/Model.ts +7 -0
  51. package/src/model/TabNode.ts +6 -1
  52. package/src/view/BorderButton.tsx +8 -41
  53. package/src/view/BorderTabSet.tsx +10 -3
  54. package/src/view/Icons.tsx +36 -0
  55. package/src/view/Layout.tsx +42 -18
  56. package/src/view/Tab.tsx +2 -7
  57. package/src/view/TabButton.tsx +15 -48
  58. package/src/view/TabButtonStamp.tsx +47 -0
  59. package/src/view/TabFloating.tsx +1 -1
  60. package/src/view/TabOverflowHook.tsx +1 -1
  61. package/src/view/TabSet.tsx +10 -13
  62. package/src/view/Utils.tsx +71 -0
  63. package/style/_base.scss +75 -45
  64. package/style/dark.css +75 -69
  65. package/style/dark.css.map +1 -1
  66. package/style/dark.scss +15 -5
  67. package/style/gray.css +72 -66
  68. package/style/gray.css.map +1 -1
  69. package/style/gray.scss +10 -3
  70. package/style/light.css +76 -70
  71. package/style/light.css.map +1 -1
  72. package/style/light.scss +16 -6
  73. package/images/close.png +0 -0
  74. package/images/maximize.png +0 -0
  75. package/images/more.png +0 -0
  76. package/images/more2.png +0 -0
  77. package/images/popout.png +0 -0
  78. package/images/restore.png +0 -0
@@ -0,0 +1,61 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.isAuxMouseEvent = exports.hideElement = exports.getRenderStateEx = void 0;
4
+ var React = require("react");
5
+ /** @hidden @internal */
6
+ function getRenderStateEx(layout, node, iconFactory, titleFactory) {
7
+ var leadingContent = iconFactory ? iconFactory(node) : undefined;
8
+ var titleContent = node.getName();
9
+ var name = node.getName();
10
+ function isTitleObject(obj) {
11
+ return obj.titleContent !== undefined;
12
+ }
13
+ if (titleFactory !== undefined) {
14
+ var titleObj = titleFactory(node);
15
+ if (titleObj !== undefined) {
16
+ if (typeof titleObj === "string") {
17
+ titleContent = titleObj;
18
+ name = titleObj;
19
+ }
20
+ else if (isTitleObject(titleObj)) {
21
+ titleContent = titleObj.titleContent;
22
+ name = titleObj.name;
23
+ }
24
+ else {
25
+ titleContent = titleObj;
26
+ }
27
+ }
28
+ }
29
+ if (leadingContent === undefined && node.getIcon() !== undefined) {
30
+ leadingContent = React.createElement("img", { style: { width: "1em", height: "1em" }, src: node.getIcon(), alt: "leadingContent" });
31
+ }
32
+ var buttons = [];
33
+ // allow customization of leading contents (icon) and contents
34
+ var renderState = { leading: leadingContent, content: titleContent, name: name, buttons: buttons };
35
+ layout.customizeTab(node, renderState);
36
+ node._setRenderedName(renderState.name);
37
+ return renderState;
38
+ }
39
+ exports.getRenderStateEx = getRenderStateEx;
40
+ /** @hidden @internal */
41
+ function hideElement(style, useVisibility) {
42
+ if (useVisibility) {
43
+ style.visibility = "hidden";
44
+ }
45
+ else {
46
+ style.display = "none";
47
+ }
48
+ }
49
+ exports.hideElement = hideElement;
50
+ /** @hidden @internal */
51
+ function isAuxMouseEvent(event) {
52
+ var auxEvent = false;
53
+ if (event.nativeEvent instanceof MouseEvent) {
54
+ if (event.nativeEvent.button !== 0 || event.ctrlKey || event.altKey || event.metaKey || event.shiftKey) {
55
+ auxEvent = true;
56
+ }
57
+ }
58
+ return auxEvent;
59
+ }
60
+ exports.isAuxMouseEvent = isAuxMouseEvent;
61
+ //# sourceMappingURL=Utils.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Utils.js","sourceRoot":"","sources":["../../src/view/Utils.tsx"],"names":[],"mappings":";;;AAAA,6BAA+B;AAI/B,wBAAwB;AACxB,SAAgB,gBAAgB,CAC5B,MAAwB,EACxB,IAAa,EACb,WAA4D,EAC5D,YAA6D;IAE7D,IAAI,cAAc,GAAG,WAAW,CAAC,CAAC,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;IACjE,IAAI,YAAY,GAAoB,IAAI,CAAC,OAAO,EAAE,CAAC;IACnD,IAAI,IAAI,GAAG,IAAI,CAAC,OAAO,EAAE,CAAC;IAE1B,SAAS,aAAa,CAAC,GAAQ;QAC3B,OAAO,GAAG,CAAC,YAAY,KAAK,SAAS,CAAA;IACzC,CAAC;IAED,IAAI,YAAY,KAAK,SAAS,EAAE;QAC5B,IAAM,QAAQ,GAAG,YAAY,CAAC,IAAI,CAAC,CAAC;QACpC,IAAI,QAAQ,KAAK,SAAS,EAAE;YACxB,IAAI,OAAO,QAAQ,KAAK,QAAQ,EAAE;gBAC9B,YAAY,GAAG,QAAkB,CAAC;gBAClC,IAAI,GAAG,QAAkB,CAAC;aAC7B;iBAAM,IAAI,aAAa,CAAC,QAAQ,CAAC,EAAE;gBAChC,YAAY,GAAG,QAAQ,CAAC,YAAY,CAAC;gBACrC,IAAI,GAAG,QAAQ,CAAC,IAAI,CAAC;aACxB;iBAAM;gBACH,YAAY,GAAG,QAAQ,CAAC;aAC3B;SACJ;KACJ;IAED,IAAI,cAAc,KAAK,SAAS,IAAI,IAAI,CAAC,OAAO,EAAE,KAAK,SAAS,EAAE;QAC9D,cAAc,GAAG,6BAAK,KAAK,EAAE,EAAC,KAAK,EAAC,KAAK,EAAE,MAAM,EAAC,KAAK,EAAC,EAAE,GAAG,EAAE,IAAI,CAAC,OAAO,EAAE,EAAE,GAAG,EAAC,gBAAgB,GAAG,CAAC;KAC1G;IAED,IAAI,OAAO,GAAU,EAAE,CAAC;IAExB,8DAA8D;IAC9D,IAAM,WAAW,GAAG,EAAE,OAAO,EAAE,cAAc,EAAE,OAAO,EAAE,YAAY,EAAE,IAAI,MAAA,EAAE,OAAO,SAAA,EAAE,CAAC;IACtF,MAAM,CAAC,YAAY,CAAC,IAAI,EAAE,WAAW,CAAC,CAAC;IAEvC,IAAI,CAAC,gBAAgB,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC;IAExC,OAAO,WAAW,CAAC;AAEvB,CAAC;AA3CD,4CA2CC;AAED,wBAAwB;AACxB,SAAgB,WAAW,CAAC,KAA0B,EAAE,aAA+B;IACnF,IAAI,aAAa,EAAE;QACf,KAAK,CAAC,UAAU,GAAG,QAAQ,CAAC;KAC/B;SAAM;QACH,KAAK,CAAC,OAAO,GAAG,MAAM,CAAC;KAC1B;AACL,CAAC;AAND,kCAMC;AAGD,wBAAwB;AACxB,SAAgB,eAAe,CAAC,KAAsF;IAClH,IAAI,QAAQ,GAAG,KAAK,CAAC;IACrB,IAAI,KAAK,CAAC,WAAW,YAAY,UAAU,EAAE;QACzC,IAAI,KAAK,CAAC,WAAW,CAAC,MAAM,KAAK,CAAC,IAAI,KAAK,CAAC,OAAO,IAAI,KAAK,CAAC,MAAM,IAAI,KAAK,CAAC,OAAO,IAAI,KAAK,CAAC,QAAQ,EAAE;YACpG,QAAQ,GAAG,IAAI,CAAC;SACnB;KACJ;IACD,OAAO,QAAQ,CAAC;AACpB,CAAC;AARD,0CAQC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "flexlayout-react",
3
- "version": "0.5.21",
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",
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
@@ -3,15 +3,20 @@ import * as ReactDOM from "react-dom";
3
3
  import { DragDrop } from ".";
4
4
  import TabNode from "./model/TabNode";
5
5
  import { CLASSES } from "./Types";
6
+ import { ILayoutCallbacks } from "./view/Layout";
7
+ import { TabButtonStamp } from "./view/TabButtonStamp";
6
8
 
7
9
  /** @hidden @internal */
8
10
  export function showPopup(
9
- layoutDiv: HTMLDivElement,
10
11
  triggerElement: Element,
11
12
  items: { index: number; node: TabNode }[],
12
13
  onSelect: (item: { index: number; node: TabNode }) => void,
13
- classNameMapper: (defaultClassName: string) => string
14
+ layout: ILayoutCallbacks,
15
+ iconFactory?: (node: TabNode) => React.ReactNode | undefined,
16
+ titleFactory?: (node: TabNode) => React.ReactNode | undefined,
14
17
  ) {
18
+ const layoutDiv = layout.getRootDiv();
19
+ const classNameMapper = layout.getClassName;
15
20
  const currentDocument = triggerElement.ownerDocument;
16
21
  const triggerRect = triggerElement.getBoundingClientRect();
17
22
  const layoutRect = layoutDiv.getBoundingClientRect();
@@ -59,6 +64,9 @@ export function showPopup(
59
64
  onHide={onHide}
60
65
  items={items}
61
66
  classNameMapper={classNameMapper}
67
+ layout={layout}
68
+ iconFactory={iconFactory}
69
+ titleFactory={titleFactory}
62
70
  />, elm);
63
71
  }
64
72
 
@@ -69,11 +77,14 @@ interface IPopupMenuProps {
69
77
  onHide: () => void;
70
78
  onSelect: (item: { index: number; node: TabNode }) => void;
71
79
  classNameMapper: (defaultClassName: string) => string;
80
+ layout: ILayoutCallbacks;
81
+ iconFactory?: (node: TabNode) => React.ReactNode | undefined;
82
+ titleFactory?: (node: TabNode) => React.ReactNode | undefined;
72
83
  }
73
84
 
74
85
  /** @hidden @internal */
75
86
  const PopupMenu = (props: IPopupMenuProps) => {
76
- const { items, onHide, onSelect, classNameMapper } = props;
87
+ const { items, onHide, onSelect, classNameMapper, layout, iconFactory, titleFactory} = props;
77
88
 
78
89
  const onItemClick = (item: { index: number; node: TabNode }, event: React.MouseEvent<HTMLDivElement, MouseEvent>) => {
79
90
  onSelect(item);
@@ -86,8 +97,15 @@ const PopupMenu = (props: IPopupMenuProps) => {
86
97
  className={classNameMapper(CLASSES.FLEXLAYOUT__POPUP_MENU_ITEM)}
87
98
  data-layout-path={"/popup-menu/tb" + i}
88
99
  onClick={(event) => onItemClick(item, event)}
89
- title={item.node.getHelpText()}>
90
- {item.node._getRenderedName()}
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
+ />}
91
109
  </div>
92
110
  ));
93
111
 
package/src/Types.ts CHANGED
@@ -70,6 +70,7 @@ export enum CLASSES {
70
70
  FLEXLAYOUT__TAB_BUTTON_OVERFLOW = "flexlayout__tab_button_overflow",
71
71
  FLEXLAYOUT__TAB_BUTTON_TEXTBOX = "flexlayout__tab_button_textbox",
72
72
  FLEXLAYOUT__TAB_BUTTON_TRAILING = "flexlayout__tab_button_trailing",
73
+ FLEXLAYOUT__TAB_BUTTON_STAMP = "flexlayout__tab_button_stamp",
73
74
 
74
75
  FLEXLAYOUT__TAB_FLOATING = "flexlayout__tab_floating",
75
76
  FLEXLAYOUT__TAB_FLOATING_INNER = "flexlayout__tab_floating_inner",
@@ -41,6 +41,7 @@ export interface IGlobalAttributes {
41
41
  borderSize?: number; // default: 200
42
42
  enableEdgeDock?: boolean; // default: true
43
43
  enableUseVisibility?: boolean; // default: false
44
+ legacyOverflowMenu?: boolean; // default: false
44
45
  marginInsets?: IInsets; // default: {"top":0,"right":0,"bottom":0,"left":0}
45
46
  rootOrientationVertical?: boolean; // default: false
46
47
  splitterExtra?: number; // default: 0
@@ -109,6 +110,7 @@ export interface ITabSetAttributes {
109
110
  width?: number;
110
111
  }
111
112
  export interface ITabAttributes {
113
+ altName?: string;
112
114
  borderHeight?: number; // default: -1 - inherited from global tabBorderHeight
113
115
  borderWidth?: number; // default: -1 - inherited from global tabBorderWidth
114
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);
@@ -448,6 +451,10 @@ class Model {
448
451
  return splitterSize;
449
452
  }
450
453
 
454
+ isLegacyOverflowMenu() {
455
+ return this._attributes.legacyOverflowMenu as boolean;
456
+ }
457
+
451
458
  getSplitterExtra() {
452
459
  return this._attributes.splitterExtra as number;
453
460
  }
@@ -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
 
@@ -3,10 +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 { isAuxMouseEvent } from "./TabSet";
9
+ import { getRenderStateEx, isAuxMouseEvent } from "./Utils";
10
10
 
11
11
  /** @hidden @internal */
12
12
  export interface IBorderButtonProps {
@@ -27,8 +27,7 @@ export const BorderButton = (props: IBorderButtonProps) => {
27
27
 
28
28
  const onMouseDown = (event: React.MouseEvent<HTMLDivElement, MouseEvent> | React.TouchEvent<HTMLDivElement>) => {
29
29
  if (!isAuxMouseEvent(event)) {
30
- const message = layout.i18nName(I18nLabel.Move_Tab, node.getName());
31
- props.layout.dragStart(event, message, node, node.isEnableDrag(), onClick, (event2: Event) => undefined);
30
+ props.layout.dragStart(event, undefined, node, node.isEnableDrag(), onClick, (event2: Event) => undefined);
32
31
  }
33
32
  };
34
33
 
@@ -96,46 +95,14 @@ export const BorderButton = (props: IBorderButtonProps) => {
96
95
  classNames += " " + node.getClassName();
97
96
  }
98
97
 
99
- let leadingContent = iconFactory ? iconFactory(node) : undefined;
100
- let titleContent: React.ReactNode = node.getName();
101
- let name = node.getName();
102
-
103
- function isTitleObject(obj: any): obj is ITitleObject {
104
- return obj.titleContent !== undefined
105
- }
106
-
107
- if (titleFactory !== undefined) {
108
- const titleObj = titleFactory(node);
109
- if (titleObj !== undefined) {
110
- if (typeof titleObj === "string") {
111
- titleContent = titleObj as string;
112
- name = titleObj as string;
113
- } else if (isTitleObject(titleObj)) {
114
- titleContent = titleObj.titleContent;
115
- name = titleObj.name;
116
- } else {
117
- titleContent = titleObj;
118
- }
119
- }
120
- }
121
-
122
- if (leadingContent === undefined && node.getIcon() !== undefined) {
123
- leadingContent = <img src={node.getIcon()} alt="leadingContent" />;
124
- }
125
-
126
- let buttons: any[] = [];
127
-
128
- // allow customization of leading contents (icon) and contents
129
- const renderState = { leading: leadingContent, content: titleContent, name, buttons };
130
- layout.customizeTab(node, renderState);
131
- node._setRenderedName(renderState.name);
98
+ const renderState = getRenderStateEx(layout, node, iconFactory, titleFactory);
132
99
 
133
- const content = <div className={cm(CLASSES.FLEXLAYOUT__BORDER_BUTTON_CONTENT)}>{renderState.content}</div>;
134
- 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;
135
102
 
136
103
  if (node.isEnableClose()) {
137
104
  const closeTitle = layout.i18nName(I18nLabel.Close_Tab);
138
- buttons.push(
105
+ renderState.buttons.push(
139
106
  <div
140
107
  key="close"
141
108
  data-layout-path={path + "/button/close"}
@@ -160,7 +127,7 @@ export const BorderButton = (props: IBorderButtonProps) => {
160
127
  title={node.getHelpText()}>
161
128
  {leading}
162
129
  {content}
163
- {buttons}
130
+ {renderState.buttons}
164
131
  </div>
165
132
  );
166
133
  };
@@ -10,7 +10,7 @@ import { I18nLabel } from "../I18nLabel";
10
10
  import { useTabOverflow } from "./TabOverflowHook";
11
11
  import Orientation from "../Orientation";
12
12
  import { CLASSES } from "../Types";
13
- import { isAuxMouseEvent } from "./TabSet";
13
+ import { isAuxMouseEvent } from "./Utils";
14
14
 
15
15
  /** @hidden @internal */
16
16
  export interface IBorderTabSetProps {
@@ -48,7 +48,12 @@ export const BorderTabSet = (props: IBorderTabSetProps) => {
48
48
 
49
49
  const onOverflowClick = (event: React.MouseEvent<HTMLElement, MouseEvent>) => {
50
50
  const element = overflowbuttonRef.current!;
51
- showPopup(layout.getRootDiv(), element, hiddenTabs, onOverflowItemSelect, layout.getClassName);
51
+ showPopup( element,
52
+ hiddenTabs,
53
+ onOverflowItemSelect,
54
+ layout,
55
+ iconFactory,
56
+ titleFactory);
52
57
  event.stopPropagation();
53
58
  };
54
59
 
@@ -137,7 +142,9 @@ export const BorderTabSet = (props: IBorderTabSetProps) => {
137
142
  onClick={onFloatTab}
138
143
  onMouseDown={onInterceptMouseDown}
139
144
  onTouchStart={onInterceptMouseDown}
140
- />
145
+ >
146
+ {icons?.popout}
147
+ </button>
141
148
  );
142
149
  }
143
150
  }
@@ -0,0 +1,36 @@
1
+ import * as React from "react";
2
+
3
+ const style = {width:"1em", height:"1em", display: "flex", alignItems:"center"};
4
+
5
+ export const CloseIcon = () => {
6
+ return (
7
+ <svg xmlns="http://www.w3.org/2000/svg" style={style} viewBox="0 0 24 24" >
8
+ <path fill="none" d="M0 0h24v24H0z"/>
9
+ <path stroke="gray" fill="gray" d="M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z" />
10
+ </svg>
11
+ );
12
+ }
13
+
14
+ export const MaximizeIcon = () => {
15
+ return (
16
+ <svg xmlns="http://www.w3.org/2000/svg" style={style} viewBox="0 0 24 24" fill="gray"><path d="M0 0h24v24H0z" fill="none"/><path stroke="gray" d="M7 14H5v5h5v-2H7v-3zm-2-4h2V7h3V5H5v5zm12 7h-3v2h5v-5h-2v3zM14 5v2h3v3h2V5h-5z"/></svg>
17
+ );
18
+ }
19
+
20
+ export const OverflowIcon = () => {
21
+ return (
22
+ <svg xmlns="http://www.w3.org/2000/svg" style={style} viewBox="0 0 24 24" fill="gray"><path d="M0 0h24v24H0z" fill="none"/><path stroke="gray" d="M7 10l5 5 5-5z"/></svg>
23
+ );
24
+ }
25
+
26
+ export const PopoutIcon = () => {
27
+ return (
28
+ <svg xmlns="http://www.w3.org/2000/svg" style={style} viewBox="0 0 24 24" fill="gray"><path d="M0 0h24v24H0z" fill="none"/><path stroke="gray" d="M9 5v2h6.59L4 18.59 5.41 20 17 8.41V15h2V5z"/></svg>
29
+ );
30
+ }
31
+
32
+ export const RestoreIcon = () => {
33
+ return (
34
+ <svg xmlns="http://www.w3.org/2000/svg" style={style} viewBox="0 0 24 24" fill="gray"><path d="M0 0h24v24H0z" fill="none"/><path stroke="gray" d="M5 16h3v3h2v-5H5v2zm3-8H5v2h5V5H8v3zm6 11h2v-3h3v-2h-5v5zm2-11V5h-2v5h5V8h-3z"/></svg>
35
+ );
36
+ }
@@ -26,9 +26,11 @@ import { FloatingWindowTab } from "./FloatingWindowTab";
26
26
  import { TabFloating } from "./TabFloating";
27
27
  import { IJsonTabNode } from "../model/IJsonModel";
28
28
  import { Orientation } from "..";
29
+ import { CloseIcon, MaximizeIcon, OverflowIcon, PopoutIcon, RestoreIcon } from "./Icons";
30
+ import { TabButtonStamp } from "./TabButtonStamp";
29
31
 
30
32
  export type CustomDragCallback = (dragging: TabNode | IJsonTabNode, over: TabNode, x: number, y: number, location: DockLocation) => void;
31
- export type DragRectRenderCallback = (text: String, node?: Node, json?: IJsonTabNode) => React.ReactElement | undefined;
33
+ export type DragRectRenderCallback = (content: React.ReactElement | undefined, node?: Node, json?: IJsonTabNode) => React.ReactElement | undefined;
32
34
  export type FloatingTabPlaceholderRenderCallback = (dockPopout: () => void, showPopout: () => void) => React.ReactElement | undefined;
33
35
  export type NodeMouseEvent = (node: TabNode | TabSetNode | BorderNode, event: React.MouseEvent<HTMLElement, MouseEvent>) => void;
34
36
 
@@ -39,7 +41,6 @@ export interface ILayoutProps {
39
41
  fontFamily?: string;
40
42
  iconFactory?: (node: TabNode) => React.ReactNode | undefined;
41
43
  titleFactory?: (node: TabNode) => ITitleObject | React.ReactNode | undefined;
42
- closeIcon?: React.ReactNode;
43
44
  icons?: IIcons;
44
45
  onAction?: (action: Action) => Action | undefined;
45
46
  onRenderTab?: (
@@ -120,6 +121,15 @@ export interface IIcons {
120
121
  more?: React.ReactNode;
121
122
  }
122
123
 
124
+ const defaultIcons = {
125
+ close: <CloseIcon/>,
126
+ closeTabset: <CloseIcon/>,
127
+ popout: <PopoutIcon/>,
128
+ maximize: <MaximizeIcon/>,
129
+ restore: <RestoreIcon/>,
130
+ more: <OverflowIcon/>,
131
+ };
132
+
123
133
  export interface ICustomDropDestination {
124
134
  rect: Rect;
125
135
  callback: CustomDragCallback;
@@ -146,7 +156,7 @@ export interface ILayoutCallbacks {
146
156
  getRootDiv(): HTMLDivElement;
147
157
  dragStart(
148
158
  event: Event | React.MouseEvent<HTMLDivElement, MouseEvent> | React.TouchEvent<HTMLDivElement> | React.DragEvent<HTMLDivElement> | undefined,
149
- dragDivText: string,
159
+ dragDivText: string | undefined,
150
160
  node: Node & IDraggable,
151
161
  allowDrag: boolean,
152
162
  onClick?: (event: Event) => void,
@@ -215,7 +225,7 @@ export class Layout extends React.Component<ILayoutProps, ILayoutState> {
215
225
  /** @hidden @internal */
216
226
  private dragRectRendered: boolean = true;
217
227
  /** @hidden @internal */
218
- private dragDivText: string = "";
228
+ private dragDivText: string | undefined = undefined;
219
229
  /** @hidden @internal */
220
230
  private dropInfo: DropInfo | undefined;
221
231
  /** @hidden @internal */
@@ -264,8 +274,7 @@ export class Layout extends React.Component<ILayoutProps, ILayoutState> {
264
274
  this.findBorderBarSizeRef = React.createRef<HTMLDivElement>();
265
275
  this.supportsPopout = props.supportsPopout !== undefined ? props.supportsPopout : defaultSupportsPopout;
266
276
  this.popoutURL = props.popoutURL ? props.popoutURL : "popout.html";
267
- // For backwards compatibility, prop closeIcon sets prop icons.close:
268
- this.icons = props.closeIcon ? Object.assign({ close: props.closeIcon }, props.icons) : props.icons;
277
+ this.icons = {...defaultIcons, ...props.icons};
269
278
  this.firstRender = true;
270
279
 
271
280
  this.state = {
@@ -283,11 +292,13 @@ export class Layout extends React.Component<ILayoutProps, ILayoutState> {
283
292
  /** @hidden @internal */
284
293
  styleFont(style: Record<string, string>): Record<string, string> {
285
294
  if (this.props.font) {
286
- if (this.props.font.size) {
287
- style.fontSize = this.props.font.size;
288
- }
289
- if (this.props.font.family) {
290
- 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
+ }
291
302
  }
292
303
  if (this.props.font.style) {
293
304
  style.fontStyle = this.props.font.style;
@@ -459,7 +470,7 @@ export class Layout extends React.Component<ILayoutProps, ILayoutState> {
459
470
  const metrics: ILayoutMetrics = {
460
471
  headerBarSize: this.state.calculatedHeaderBarSize,
461
472
  tabBarSize: this.state.calculatedTabBarSize,
462
- borderBarSize: this.state.calculatedBorderBarSize,
473
+ borderBarSize: this.state.calculatedBorderBarSize
463
474
  };
464
475
  this.props.model._setShowHiddenBorder(this.state.showHiddenBorder);
465
476
 
@@ -691,7 +702,7 @@ export class Layout extends React.Component<ILayoutProps, ILayoutState> {
691
702
  * @param json the json for the new tab node
692
703
  * @param onDrop a callback to call when the drag is complete (node and event will be undefined if the drag was cancelled)
693
704
  */
694
- addTabWithDragAndDrop(dragText: string, json: IJsonTabNode, onDrop?: (node?: Node, event?: Event) => void) {
705
+ addTabWithDragAndDrop(dragText: string | undefined, json: IJsonTabNode, onDrop?: (node?: Node, event?: Event) => void) {
695
706
  this.fnNewNodeDropped = onDrop;
696
707
  this.newTabJson = json;
697
708
  this.dragStart(undefined, dragText, TabNode._fromJson(json, this.props.model, false), true, undefined, undefined);
@@ -705,7 +716,7 @@ export class Layout extends React.Component<ILayoutProps, ILayoutState> {
705
716
  * @param json the json for the new tab node
706
717
  * @param onDrop a callback to call when the drag is complete (node and event will be undefined if the drag was cancelled)
707
718
  */
708
- addTabWithDragAndDropIndirect(dragText: string, json: IJsonTabNode, onDrop?: (node?: Node, event?: Event) => void) {
719
+ addTabWithDragAndDropIndirect(dragText: string | undefined, json: IJsonTabNode, onDrop?: (node?: Node, event?: Event) => void) {
709
720
  this.fnNewNodeDropped = onDrop;
710
721
  this.newTabJson = json;
711
722
 
@@ -798,7 +809,7 @@ export class Layout extends React.Component<ILayoutProps, ILayoutState> {
798
809
  /** @hidden @internal */
799
810
  dragStart = (
800
811
  event: Event | React.MouseEvent<HTMLDivElement, MouseEvent> | React.TouchEvent<HTMLDivElement> | React.DragEvent<HTMLDivElement> | undefined,
801
- dragDivText: string,
812
+ dragDivText: string | undefined,
802
813
  node: Node & IDraggable,
803
814
  allowDrag: boolean,
804
815
  onClick?: (event: Event) => void,
@@ -814,11 +825,24 @@ export class Layout extends React.Component<ILayoutProps, ILayoutState> {
814
825
  };
815
826
 
816
827
  /** @hidden @internal */
817
- dragRectRender = (text: String, node?: Node, json?: IJsonTabNode, onRendered?: () => void) => {
818
- 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
+ }
819
843
 
820
844
  if (this.props.onRenderDragRect !== undefined) {
821
- const customContent = this.props.onRenderDragRect(text, node, json);
845
+ const customContent = this.props.onRenderDragRect(content, node, json);
822
846
  if (customContent !== undefined) {
823
847
  content = customContent;
824
848
  }
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 {
@@ -80,10 +81,4 @@ export const Tab = (props: ITabProps) => {
80
81
  );
81
82
  };
82
83
 
83
- export function hideElement(style: Record<string, any>, useVisibility: ConstrainBoolean ) {
84
- if (useVisibility) {
85
- style.visibility = "hidden";
86
- } else {
87
- style.display = "none";
88
- }
89
- }
84
+
@@ -4,10 +4,10 @@ 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 {
@@ -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
 
@@ -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,53 +146,21 @@ 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();
149
+ const renderState = getRenderStateEx(layout, node, iconFactory, titleFactory);
153
150
 
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 (leadingContent === undefined && 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);
184
-
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)}
199
166
  data-layout-path={path + "/textbox"}
@@ -209,7 +176,7 @@ export const TabButton = (props: ITabButtonProps) => {
209
176
 
210
177
  if (node.isEnableClose()) {
211
178
  const closeTitle = layout.i18nName(I18nLabel.Close_Tab);
212
- buttons.push(
179
+ renderState.buttons.push(
213
180
  <div
214
181
  key="close"
215
182
  data-layout-path={path + "/button/close"}
@@ -236,7 +203,7 @@ export const TabButton = (props: ITabButtonProps) => {
236
203
  >
237
204
  {leading}
238
205
  {content}
239
- {buttons}
206
+ {renderState.buttons}
240
207
  </div>
241
208
  );
242
209
  };