flexlayout-react 0.8.3 → 0.8.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/ChangeLog.txt +8 -0
- package/declarations/Types.d.ts +4 -1
- package/declarations/model/BorderNode.d.ts +1 -0
- package/declarations/model/IJsonModel.d.ts +29 -1
- package/declarations/model/TabSetNode.d.ts +1 -0
- package/declarations/view/Layout.d.ts +1 -1
- package/dist/flexlayout.js +10 -10
- package/dist/flexlayout_min.js +1 -1
- package/lib/Types.js +3 -0
- package/lib/Types.js.map +1 -1
- package/lib/model/BorderNode.js +4 -0
- package/lib/model/BorderNode.js.map +1 -1
- package/lib/model/Model.js +2 -0
- package/lib/model/Model.js.map +1 -1
- package/lib/model/TabSetNode.js +4 -0
- package/lib/model/TabSetNode.js.map +1 -1
- package/lib/view/BorderTabSet.js +24 -14
- package/lib/view/BorderTabSet.js.map +1 -1
- package/lib/view/Layout.js +1 -1
- package/lib/view/PopupMenu.js +11 -5
- package/lib/view/PopupMenu.js.map +1 -1
- package/lib/view/Splitter.js +2 -2
- package/lib/view/TabOverflowHook.js +226 -116
- package/lib/view/TabOverflowHook.js.map +1 -1
- package/lib/view/TabSet.js +20 -20
- package/lib/view/TabSet.js.map +1 -1
- package/package.json +1 -1
- package/src/Types.ts +5 -1
- package/src/model/BorderNode.ts +7 -0
- package/src/model/IJsonModel.ts +33 -1
- package/src/model/Model.ts +2 -0
- package/src/model/TabSetNode.ts +9 -0
- package/src/view/BorderTabSet.tsx +48 -23
- package/src/view/Layout.tsx +1 -1
- package/src/view/PopupMenu.tsx +31 -19
- package/src/view/Splitter.tsx +2 -2
- package/src/view/TabOverflowHook.tsx +256 -128
- package/src/view/TabSet.tsx +43 -31
- package/style/_base.scss +53 -5
- package/style/dark.css +46 -4
- package/style/dark.css.map +1 -1
- package/style/dark.scss +4 -0
- package/style/gray.css +48 -6
- package/style/gray.css.map +1 -1
- package/style/gray.scss +6 -2
- package/style/light.css +47 -5
- package/style/light.css.map +1 -1
- package/style/light.scss +5 -1
- package/style/rounded.css +69 -10
- package/style/rounded.css.map +1 -1
- package/style/rounded.scss +28 -6
- package/style/underline.css +46 -5
- package/style/underline.css.map +1 -1
- package/style/underline.scss +5 -1
|
@@ -1,174 +1,302 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
|
-
import { TabNode } from "../model/TabNode";
|
|
3
|
-
import { Rect } from "../Rect";
|
|
4
2
|
import { TabSetNode } from "../model/TabSetNode";
|
|
5
3
|
import { BorderNode } from "../model/BorderNode";
|
|
6
4
|
import { Orientation } from "../Orientation";
|
|
5
|
+
import { LayoutInternal } from "./Layout";
|
|
6
|
+
import { TabNode } from "../model/TabNode";
|
|
7
|
+
import { startDrag } from "./Utils";
|
|
7
8
|
|
|
8
9
|
/** @internal */
|
|
9
10
|
export const useTabOverflow = (
|
|
11
|
+
layout: LayoutInternal,
|
|
10
12
|
node: TabSetNode | BorderNode,
|
|
11
13
|
orientation: Orientation,
|
|
12
|
-
|
|
13
|
-
|
|
14
|
+
tabStripRef: React.RefObject<HTMLElement | null>,
|
|
15
|
+
miniScrollRef: React.RefObject<HTMLElement | null>,
|
|
16
|
+
tabClassName: string
|
|
14
17
|
) => {
|
|
15
|
-
const
|
|
16
|
-
const
|
|
17
|
-
const lastRect = React.useRef<Rect>(Rect.empty());
|
|
18
|
-
const selfRef = React.useRef<HTMLDivElement | null>(null);
|
|
18
|
+
const [hiddenTabs, setHiddenTabs] = React.useState<number[]>([]);
|
|
19
|
+
const [isTabOverflow, setTabOverflow] = React.useState<boolean>(false);
|
|
19
20
|
|
|
20
|
-
const
|
|
21
|
-
const
|
|
22
|
-
const
|
|
23
|
-
const
|
|
21
|
+
const selfRef = React.useRef<HTMLDivElement | null>(null);
|
|
22
|
+
const userControlledPositionRef = React.useRef<boolean>(false);
|
|
23
|
+
const updateHiddenTabsTimerRef = React.useRef<NodeJS.Timeout | undefined>(undefined);
|
|
24
|
+
const hiddenTabsRef = React.useRef<number[]>([]);
|
|
25
|
+
const thumbInternalPos = React.useRef<number>(0);
|
|
26
|
+
hiddenTabsRef.current = hiddenTabs;
|
|
24
27
|
|
|
28
|
+
// if node changes (new model) then reset scroll to 0
|
|
29
|
+
React.useEffect(() => {
|
|
30
|
+
if (tabStripRef.current) {
|
|
31
|
+
setScrollPosition(0);
|
|
32
|
+
}
|
|
33
|
+
}, [node]);
|
|
34
|
+
|
|
25
35
|
// if selected node or tabset/border rectangle change then unset usercontrolled (so selected tab will be kept in view)
|
|
26
|
-
React.
|
|
27
|
-
|
|
36
|
+
React.useEffect(() => {
|
|
37
|
+
userControlledPositionRef.current = false;
|
|
28
38
|
}, [node.getSelectedNode(), node.getRect().width, node.getRect().height]);
|
|
29
39
|
|
|
30
|
-
React.
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
40
|
+
React.useEffect(() => {
|
|
41
|
+
checkForOverflow(); // if tabs + sticky buttons length > scroll area => move sticky buttons to right buttons
|
|
42
|
+
|
|
43
|
+
if (!userControlledPositionRef.current) {
|
|
44
|
+
const selectedTab = findSelectedTab();
|
|
45
|
+
if (selectedTab) {
|
|
46
|
+
selectedTab.scrollIntoView();
|
|
47
|
+
}
|
|
34
48
|
}
|
|
49
|
+
|
|
50
|
+
updateHiddenTabs();
|
|
51
|
+
updateScrollMetrics();
|
|
35
52
|
});
|
|
36
53
|
|
|
37
|
-
const
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
}
|
|
42
|
-
instance.addEventListener("wheel", onWheel, { passive: false });
|
|
43
|
-
return () => {
|
|
44
|
-
instance.removeEventListener("wheel", onWheel);
|
|
45
|
-
};
|
|
46
|
-
}, [instance]);
|
|
47
|
-
|
|
48
|
-
// needed to prevent default mouse wheel over tabset/border (cannot do with react event?)
|
|
49
|
-
const onWheel = (event: Event) => {
|
|
50
|
-
event.preventDefault();
|
|
51
|
-
};
|
|
54
|
+
const updateScrollMetrics = () => {
|
|
55
|
+
if (tabStripRef.current && miniScrollRef.current) {
|
|
56
|
+
const t = tabStripRef.current;
|
|
57
|
+
const s = miniScrollRef.current;
|
|
52
58
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
59
|
+
const size = getSize(t);
|
|
60
|
+
const scrollSize = getScrollSize(t);
|
|
61
|
+
const position = getScrollPosition(t);
|
|
62
|
+
|
|
63
|
+
if (scrollSize > size && scrollSize > 0) {
|
|
64
|
+
let thumbSize = size * size / scrollSize;
|
|
65
|
+
let adjust = 0;
|
|
66
|
+
if (thumbSize < 20) {
|
|
67
|
+
adjust = 20 - thumbSize;
|
|
68
|
+
thumbSize = 20;
|
|
69
|
+
}
|
|
70
|
+
const thumbPos = position * (size - adjust) / scrollSize;
|
|
71
|
+
if (orientation === Orientation.HORZ) {
|
|
72
|
+
s.style.width = thumbSize + "px";
|
|
73
|
+
s.style.left = thumbPos + "px";
|
|
74
|
+
} else {
|
|
75
|
+
s.style.height = thumbSize + "px";
|
|
76
|
+
s.style.top = thumbPos + "px";
|
|
77
|
+
}
|
|
78
|
+
s.style.display = "block";
|
|
79
|
+
} else {
|
|
80
|
+
s.style.display = "none";
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
if (orientation === Orientation.HORZ) {
|
|
84
|
+
s.style.bottom = "0px";
|
|
85
|
+
} else {
|
|
86
|
+
s.style.right = "0px";
|
|
87
|
+
}
|
|
58
88
|
}
|
|
59
|
-
}
|
|
89
|
+
}
|
|
60
90
|
|
|
61
|
-
const
|
|
62
|
-
if (
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
91
|
+
const updateHiddenTabs = () => {
|
|
92
|
+
if (updateHiddenTabsTimerRef.current === undefined) {
|
|
93
|
+
// throttle updates to prevent Maximum update depth exceeded error
|
|
94
|
+
updateHiddenTabsTimerRef.current = setTimeout(() => {
|
|
95
|
+
const newHiddenTabs = findHiddenTabs();
|
|
96
|
+
if (!arraysEqual(newHiddenTabs, hiddenTabsRef.current)) {
|
|
97
|
+
setHiddenTabs(newHiddenTabs);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
updateHiddenTabsTimerRef.current = undefined;
|
|
101
|
+
}, 100);
|
|
66
102
|
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
const updateTabRects = () => {
|
|
106
|
+
if (tabStripRef.current) {
|
|
107
|
+
const tabContainer = tabStripRef.current.firstElementChild!;
|
|
108
|
+
|
|
109
|
+
const nodeChildren = node.getChildren();
|
|
110
|
+
let i = 0;
|
|
111
|
+
Array.from(tabContainer.children).forEach((child) => {
|
|
112
|
+
if (child.classList.contains(tabClassName)) {
|
|
113
|
+
const childNode = nodeChildren[i] as TabNode;
|
|
114
|
+
childNode.setTabRect(layout.getBoundingClientRect(child as HTMLElement));
|
|
115
|
+
i++;
|
|
116
|
+
}
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
const onScroll = () => {
|
|
122
|
+
userControlledPositionRef.current = true;
|
|
123
|
+
updateTabRects();
|
|
124
|
+
updateScrollMetrics()
|
|
125
|
+
updateHiddenTabs();
|
|
67
126
|
};
|
|
68
127
|
|
|
69
|
-
const
|
|
128
|
+
const onScrollPointerDown = (event: React.PointerEvent<HTMLElement>) => {
|
|
129
|
+
event.stopPropagation();
|
|
130
|
+
miniScrollRef.current!.setPointerCapture(event.pointerId)
|
|
131
|
+
const r = miniScrollRef.current?.getBoundingClientRect()!;
|
|
70
132
|
if (orientation === Orientation.HORZ) {
|
|
71
|
-
|
|
133
|
+
thumbInternalPos.current = event.clientX - r.x;
|
|
72
134
|
} else {
|
|
73
|
-
|
|
135
|
+
thumbInternalPos.current = event.clientY - r.y;
|
|
74
136
|
}
|
|
75
|
-
|
|
137
|
+
userControlledPositionRef.current = true;
|
|
138
|
+
startDrag(event.currentTarget.ownerDocument, event, onDragMove, onDragEnd, onDragCancel);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
const onDragMove = (x: number, y: number) => {
|
|
142
|
+
if (tabStripRef.current && miniScrollRef.current) {
|
|
143
|
+
const t = tabStripRef.current;
|
|
144
|
+
const s = miniScrollRef.current;
|
|
145
|
+
const size = getSize(t);
|
|
146
|
+
const scrollSize = getScrollSize(t);
|
|
147
|
+
const thumbSize = getSize(s);
|
|
76
148
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
let lastChild = node.getChildren()[node.getChildren().length - 1] as TabNode;
|
|
84
|
-
const stickyButtonsSize = stickyButtonsRef.current === null ? 0 : getSize(stickyButtonsRef.current!.getBoundingClientRect());
|
|
85
|
-
|
|
86
|
-
if (
|
|
87
|
-
firstRender.current === true ||
|
|
88
|
-
(lastHiddenCount.current === 0 && hiddenTabs.length !== 0) ||
|
|
89
|
-
nodeRect.width !== lastRect.current.width || // incase rect changed between first render and second
|
|
90
|
-
nodeRect.height !== lastRect.current.height
|
|
91
|
-
) {
|
|
92
|
-
lastHiddenCount.current = hiddenTabs.length;
|
|
93
|
-
lastRect.current = nodeRect;
|
|
94
|
-
const enabled = node instanceof TabSetNode ? node.isEnableTabStrip() === true : true;
|
|
95
|
-
let endPos = getFar(nodeRect) - stickyButtonsSize;
|
|
96
|
-
if (toolbarRef.current !== null) {
|
|
97
|
-
endPos -= getSize(toolbarRef.current.getBoundingClientRect());
|
|
149
|
+
const r = t.getBoundingClientRect()!;
|
|
150
|
+
let thumb = 0;
|
|
151
|
+
if (orientation === Orientation.HORZ) {
|
|
152
|
+
thumb = x - r.x - thumbInternalPos.current;
|
|
153
|
+
} else {
|
|
154
|
+
thumb = y - r.y - thumbInternalPos.current
|
|
98
155
|
}
|
|
99
|
-
if (enabled && node.getChildren().length > 0) {
|
|
100
|
-
if (hiddenTabs.length === 0 && position === 0 && getFar(lastChild.getTabRect()!) + tabMargin < endPos) {
|
|
101
|
-
return; // nothing to do all tabs are shown in available space
|
|
102
|
-
}
|
|
103
156
|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
const
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
}
|
|
127
|
-
}
|
|
157
|
+
thumb = Math.max(0, Math.min(scrollSize - thumbSize, thumb));
|
|
158
|
+
if (size > 0) {
|
|
159
|
+
const scrollPos = thumb * scrollSize / size;
|
|
160
|
+
setScrollPosition(scrollPos);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
const onDragEnd = () => {
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
const onDragCancel = () => {
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
const findSelectedTab: () => Element | undefined = () => {
|
|
172
|
+
let found: Element | undefined = undefined;
|
|
173
|
+
if (tabStripRef.current) {
|
|
174
|
+
const tabContainer = tabStripRef.current.firstElementChild!;
|
|
175
|
+
|
|
176
|
+
Array.from(tabContainer.children).forEach((child) => {
|
|
177
|
+
if (child.classList.contains(tabClassName + "--selected")) {
|
|
178
|
+
found = child;
|
|
128
179
|
}
|
|
180
|
+
});
|
|
181
|
+
}
|
|
182
|
+
return found;
|
|
183
|
+
};
|
|
129
184
|
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
185
|
+
const checkForOverflow = () => {
|
|
186
|
+
if (tabStripRef.current) {
|
|
187
|
+
const strip = tabStripRef.current;
|
|
188
|
+
const tabContainer = strip.firstElementChild!;
|
|
189
|
+
|
|
190
|
+
const offset = isTabOverflow ? 10 : 0; // prevents flashing, after sticky buttons docked set, must be 10 pixels smaller before unsetting
|
|
191
|
+
const dock = (getSize(tabContainer) + offset) > getSize(tabStripRef.current);
|
|
192
|
+
if (dock !== isTabOverflow) {
|
|
193
|
+
setTabOverflow(dock);
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
const findHiddenTabs: () => number[] = () => {
|
|
199
|
+
const hidden: number[] = [];
|
|
200
|
+
if (tabStripRef.current) {
|
|
201
|
+
const strip = tabStripRef.current;
|
|
202
|
+
const stripRect = strip.getBoundingClientRect();
|
|
203
|
+
const visibleNear = getNear(stripRect) - 1;
|
|
204
|
+
const visibleFar = getFar(stripRect) + 1;
|
|
205
|
+
|
|
206
|
+
const tabContainer = strip.firstElementChild!;
|
|
207
|
+
|
|
208
|
+
let i = 0;
|
|
209
|
+
Array.from(tabContainer.children).forEach((child) => {
|
|
210
|
+
const tabRect = child.getBoundingClientRect();
|
|
211
|
+
|
|
212
|
+
if (child.classList.contains(tabClassName)) {
|
|
213
|
+
if (getNear(tabRect) < visibleNear || getFar(tabRect) > visibleFar) {
|
|
214
|
+
hidden.push(i);
|
|
141
215
|
}
|
|
216
|
+
i++;
|
|
142
217
|
}
|
|
218
|
+
});
|
|
219
|
+
}
|
|
143
220
|
|
|
144
|
-
|
|
221
|
+
return hidden;
|
|
222
|
+
};
|
|
223
|
+
|
|
224
|
+
const onMouseWheel = (event: React.WheelEvent<HTMLElement>) => {
|
|
225
|
+
if (tabStripRef.current) {
|
|
226
|
+
if (node.getChildren().length === 0) return;
|
|
145
227
|
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
228
|
+
let delta = 0;
|
|
229
|
+
if (Math.abs(event.deltaY) > 0) {
|
|
230
|
+
delta = -event.deltaY;
|
|
231
|
+
if (event.deltaMode === 1) {
|
|
232
|
+
// DOM_DELTA_LINE 0x01 The delta values are specified in lines.
|
|
233
|
+
delta *= 40;
|
|
234
|
+
}
|
|
235
|
+
const newPos = getScrollPosition(tabStripRef.current) - delta;
|
|
236
|
+
const maxScroll = getScrollSize(tabStripRef.current) - getSize(tabStripRef.current);
|
|
237
|
+
const p = Math.max(0, Math.min(maxScroll, newPos));
|
|
238
|
+
setScrollPosition(p);
|
|
239
|
+
updateTabRects();
|
|
240
|
+
updateHiddenTabs();
|
|
241
|
+
userControlledPositionRef.current = true;
|
|
242
|
+
event.stopPropagation();
|
|
150
243
|
}
|
|
151
|
-
} else {
|
|
152
|
-
firstRender.current = true;
|
|
153
244
|
}
|
|
154
245
|
};
|
|
155
246
|
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
if (
|
|
160
|
-
|
|
247
|
+
// orientation helpers:
|
|
248
|
+
|
|
249
|
+
const getNear = (rect: DOMRect) => {
|
|
250
|
+
if (orientation === Orientation.HORZ) {
|
|
251
|
+
return rect.x;
|
|
161
252
|
} else {
|
|
162
|
-
|
|
253
|
+
return rect.y;
|
|
163
254
|
}
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
255
|
+
};
|
|
256
|
+
|
|
257
|
+
const getFar = (rect: DOMRect) => {
|
|
258
|
+
if (orientation === Orientation.HORZ) {
|
|
259
|
+
return rect.right;
|
|
260
|
+
} else {
|
|
261
|
+
return rect.bottom;
|
|
167
262
|
}
|
|
168
|
-
setPosition(position + delta);
|
|
169
|
-
userControlledLeft.current = true;
|
|
170
|
-
event.stopPropagation();
|
|
171
263
|
};
|
|
172
264
|
|
|
173
|
-
|
|
265
|
+
const getSize = (elm: Element) => {
|
|
266
|
+
if (orientation === Orientation.HORZ) {
|
|
267
|
+
return elm.clientWidth;
|
|
268
|
+
} else {
|
|
269
|
+
return elm.clientHeight;
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
const getScrollSize = (elm: Element) => {
|
|
274
|
+
if (orientation === Orientation.HORZ) {
|
|
275
|
+
return elm.scrollWidth;
|
|
276
|
+
} else {
|
|
277
|
+
return elm.scrollHeight;
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
const setScrollPosition = (p: number) => {
|
|
282
|
+
if (orientation === Orientation.HORZ) {
|
|
283
|
+
tabStripRef.current!.scrollLeft = p;
|
|
284
|
+
} else {
|
|
285
|
+
tabStripRef.current!.scrollTop = p;
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
const getScrollPosition = (elm: Element) => {
|
|
290
|
+
if (orientation === Orientation.HORZ) {
|
|
291
|
+
return elm.scrollLeft;
|
|
292
|
+
} else {
|
|
293
|
+
return elm.scrollTop;
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
return { selfRef, userControlledPositionRef, onScroll, onScrollPointerDown, hiddenTabs, onMouseWheel, isTabOverflow };
|
|
174
298
|
};
|
|
299
|
+
|
|
300
|
+
function arraysEqual(arr1: number[], arr2: number[]) {
|
|
301
|
+
return arr1.length === arr2.length && arr1.every((val, index) => val === arr2[index]);
|
|
302
|
+
}
|
package/src/view/TabSet.tsx
CHANGED
|
@@ -24,6 +24,7 @@ export const TabSet = (props: ITabSetProps) => {
|
|
|
24
24
|
const { node, layout } = props;
|
|
25
25
|
|
|
26
26
|
const tabStripRef = React.useRef<HTMLDivElement | null>(null);
|
|
27
|
+
const miniScrollRef = React.useRef<HTMLDivElement | null>(null);
|
|
27
28
|
const tabStripInnerRef = React.useRef<HTMLDivElement | null>(null);
|
|
28
29
|
const contentRef = React.useRef<HTMLDivElement | null>(null);
|
|
29
30
|
const buttonBarRef = React.useRef<HTMLDivElement | null>(null);
|
|
@@ -43,22 +44,28 @@ export const TabSet = (props: ITabSetProps) => {
|
|
|
43
44
|
const newContentRect = Rect.getContentRect(contentRef.current!).relativeTo(layout.getDomRect()!);
|
|
44
45
|
if (!node.getContentRect().equals(newContentRect)) {
|
|
45
46
|
node.setContentRect(newContentRect);
|
|
46
|
-
|
|
47
|
+
setTimeout(() => { //prevent Maximum update depth exceeded error
|
|
48
|
+
layout.redrawInternal("tabset content rect " + newContentRect);
|
|
49
|
+
}, 0);
|
|
47
50
|
}
|
|
48
51
|
});
|
|
49
52
|
|
|
50
53
|
// this must be after the useEffect, so the node rect is already set (else window popin will not position tabs correctly)
|
|
51
|
-
const { selfRef,
|
|
54
|
+
const { selfRef, userControlledPositionRef, onScroll, onScrollPointerDown, hiddenTabs, onMouseWheel, isTabOverflow } =
|
|
55
|
+
useTabOverflow(layout, node, Orientation.HORZ, tabStripInnerRef, miniScrollRef,
|
|
56
|
+
layout.getClassName(CLASSES.FLEXLAYOUT__TAB_BUTTON));
|
|
52
57
|
|
|
53
58
|
const onOverflowClick = (event: React.MouseEvent<HTMLElement, MouseEvent>) => {
|
|
54
59
|
const callback = layout.getShowOverflowMenu();
|
|
60
|
+
const items = hiddenTabs.map(h => { return { index: h, node: (node.getChildren()[h] as TabNode) }; });
|
|
55
61
|
if (callback !== undefined) {
|
|
56
|
-
callback(node, event,
|
|
62
|
+
callback(node, event, items, onOverflowItemSelect);
|
|
57
63
|
} else {
|
|
58
64
|
const element = overflowbuttonRef.current!;
|
|
59
65
|
showPopup(
|
|
60
66
|
element,
|
|
61
|
-
|
|
67
|
+
node,
|
|
68
|
+
items,
|
|
62
69
|
onOverflowItemSelect,
|
|
63
70
|
layout
|
|
64
71
|
);
|
|
@@ -68,7 +75,7 @@ export const TabSet = (props: ITabSetProps) => {
|
|
|
68
75
|
|
|
69
76
|
const onOverflowItemSelect = (item: { node: TabNode; index: number }) => {
|
|
70
77
|
layout.doAction(Actions.selectTab(item.node.getId()));
|
|
71
|
-
|
|
78
|
+
userControlledPositionRef.current = false;
|
|
72
79
|
};
|
|
73
80
|
|
|
74
81
|
const onDragStart = (event: React.DragEvent<HTMLElement>) => {
|
|
@@ -86,12 +93,6 @@ export const TabSet = (props: ITabSetProps) => {
|
|
|
86
93
|
|
|
87
94
|
const onPointerDown = (event: React.PointerEvent<HTMLElement>) => {
|
|
88
95
|
if (!isAuxMouseEvent(event)) {
|
|
89
|
-
let name = node.getName();
|
|
90
|
-
if (name === undefined) {
|
|
91
|
-
name = "";
|
|
92
|
-
} else {
|
|
93
|
-
name = ": " + name;
|
|
94
|
-
}
|
|
95
96
|
layout.doAction(Actions.setActiveTabset(node.getId(), layout.getWindowId()));
|
|
96
97
|
}
|
|
97
98
|
};
|
|
@@ -144,12 +145,6 @@ export const TabSet = (props: ITabSetProps) => {
|
|
|
144
145
|
// Start Render
|
|
145
146
|
|
|
146
147
|
const cm = layout.getClassName;
|
|
147
|
-
|
|
148
|
-
// tabbar inner can get shifted left via tab rename, this resets scrollleft to 0
|
|
149
|
-
if (tabStripInnerRef.current !== null && tabStripInnerRef.current!.scrollLeft !== 0) {
|
|
150
|
-
tabStripInnerRef.current.scrollLeft = 0;
|
|
151
|
-
}
|
|
152
|
-
|
|
153
148
|
const selectedTabNode: TabNode = node.getSelectedNode() as TabNode;
|
|
154
149
|
const path = node.getPath();
|
|
155
150
|
|
|
@@ -191,7 +186,7 @@ export const TabSet = (props: ITabSetProps) => {
|
|
|
191
186
|
}
|
|
192
187
|
|
|
193
188
|
if (stickyButtons.length > 0) {
|
|
194
|
-
if (!node.isEnableTabWrap() && (
|
|
189
|
+
if (!node.isEnableTabWrap() && (isTabOverflow || isTabStretch)) {
|
|
195
190
|
buttons = [...stickyButtons, ...buttons];
|
|
196
191
|
} else {
|
|
197
192
|
tabs.push(<div
|
|
@@ -207,11 +202,12 @@ export const TabSet = (props: ITabSetProps) => {
|
|
|
207
202
|
}
|
|
208
203
|
|
|
209
204
|
if (!node.isEnableTabWrap()) {
|
|
210
|
-
|
|
205
|
+
if (hiddenTabs.length > 0) {
|
|
211
206
|
const overflowTitle = layout.i18nName(I18nLabel.Overflow_Menu_Tooltip);
|
|
212
207
|
let overflowContent;
|
|
213
208
|
if (typeof icons.more === "function") {
|
|
214
|
-
|
|
209
|
+
const items = hiddenTabs.map(h => { return { index: h, node: (node.getChildren()[h] as TabNode) }; });
|
|
210
|
+
overflowContent = icons.more(node, items);
|
|
215
211
|
} else {
|
|
216
212
|
overflowContent = (<>
|
|
217
213
|
{icons.more}
|
|
@@ -235,10 +231,10 @@ export const TabSet = (props: ITabSetProps) => {
|
|
|
235
231
|
}
|
|
236
232
|
}
|
|
237
233
|
|
|
238
|
-
if (selectedTabNode !== undefined &&
|
|
239
|
-
layout.isSupportsPopout() &&
|
|
240
|
-
selectedTabNode.isEnablePopout() &&
|
|
241
|
-
selectedTabNode.isEnablePopoutIcon()
|
|
234
|
+
if (selectedTabNode !== undefined &&
|
|
235
|
+
layout.isSupportsPopout() &&
|
|
236
|
+
selectedTabNode.isEnablePopout() &&
|
|
237
|
+
selectedTabNode.isEnablePopoutIcon()) {
|
|
242
238
|
|
|
243
239
|
const popoutTitle = layout.i18nName(I18nLabel.Popout_Tab);
|
|
244
240
|
buttons.push(
|
|
@@ -297,7 +293,7 @@ export const TabSet = (props: ITabSetProps) => {
|
|
|
297
293
|
key="active"
|
|
298
294
|
data-layout-path={path + "/button/active"}
|
|
299
295
|
title={title}
|
|
300
|
-
className={cm(CLASSES.FLEXLAYOUT__TAB_TOOLBAR_ICON)
|
|
296
|
+
className={cm(CLASSES.FLEXLAYOUT__TAB_TOOLBAR_ICON)}
|
|
301
297
|
>
|
|
302
298
|
{(typeof icons.activeTabset === "function") ? icons.activeTabset(node) : icons.activeTabset}
|
|
303
299
|
</div>
|
|
@@ -341,7 +337,7 @@ export const TabSet = (props: ITabSetProps) => {
|
|
|
341
337
|
if (node.isEnableTabStrip()) {
|
|
342
338
|
tabStrip = (
|
|
343
339
|
<div className={tabStripClasses}
|
|
344
|
-
style={{ flexWrap: "wrap", gap:"1px", marginTop:"2px" }}
|
|
340
|
+
style={{ flexWrap: "wrap", gap: "1px", marginTop: "2px" }}
|
|
345
341
|
ref={tabStripRef}
|
|
346
342
|
data-layout-path={path + "/tabstrip"}
|
|
347
343
|
onPointerDown={onPointerDown}
|
|
@@ -360,6 +356,15 @@ export const TabSet = (props: ITabSetProps) => {
|
|
|
360
356
|
}
|
|
361
357
|
} else {
|
|
362
358
|
if (node.isEnableTabStrip()) {
|
|
359
|
+
let miniScrollbar = undefined;
|
|
360
|
+
if (node.isEnableTabScrollbar()) {
|
|
361
|
+
miniScrollbar = (
|
|
362
|
+
<div ref={miniScrollRef}
|
|
363
|
+
className={cm(CLASSES.FLEXLAYOUT__MINI_SCROLLBAR)}
|
|
364
|
+
onPointerDown={onScrollPointerDown}
|
|
365
|
+
/>
|
|
366
|
+
);
|
|
367
|
+
}
|
|
363
368
|
tabStrip = (
|
|
364
369
|
<div className={tabStripClasses}
|
|
365
370
|
ref={tabStripRef}
|
|
@@ -373,13 +378,20 @@ export const TabSet = (props: ITabSetProps) => {
|
|
|
373
378
|
onWheel={onMouseWheel}
|
|
374
379
|
onDragStart={onDragStart}
|
|
375
380
|
>
|
|
376
|
-
<div
|
|
377
|
-
<div
|
|
378
|
-
|
|
379
|
-
|
|
381
|
+
<div className={cm(CLASSES.FLEXLAYOUT__MINI_SCROLLBAR_CONTAINER)}>
|
|
382
|
+
<div ref={tabStripInnerRef}
|
|
383
|
+
className={cm(CLASSES.FLEXLAYOUT__TABSET_TABBAR_INNER) + " " + cm(CLASSES.FLEXLAYOUT__TABSET_TABBAR_INNER_ + node.getTabLocation())}
|
|
384
|
+
style={{ overflowX: 'auto', overflowY: "hidden" }}
|
|
385
|
+
onScroll={onScroll}
|
|
380
386
|
>
|
|
381
|
-
|
|
387
|
+
<div
|
|
388
|
+
style={{ width: (isTabStretch ? "100%" : "none") }}
|
|
389
|
+
className={cm(CLASSES.FLEXLAYOUT__TABSET_TABBAR_INNER_TAB_CONTAINER) + " " + cm(CLASSES.FLEXLAYOUT__TABSET_TABBAR_INNER_TAB_CONTAINER_ + node.getTabLocation())}
|
|
390
|
+
>
|
|
391
|
+
{tabs}
|
|
392
|
+
</div>
|
|
382
393
|
</div>
|
|
394
|
+
{miniScrollbar}
|
|
383
395
|
</div>
|
|
384
396
|
{buttonbar}
|
|
385
397
|
</div>
|