@aptre/flex-layout 0.5.1 → 0.5.3
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/README.md +194 -151
- package/dist/index.mjs +26 -6
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -2,41 +2,37 @@
|
|
|
2
2
|
|
|
3
3
|
[](https://github.com/caplin/FlexLayout/blob/master/LICENSE)
|
|
4
4
|
|
|
5
|
-
FlexLayout is a layout manager
|
|
5
|
+
FlexLayout is a layout manager supporting multiple TabSets with draggable+resizable tabs.
|
|
6
6
|
|
|
7
7
|

|
|
8
8
|
|
|
9
|
-
**This is a
|
|
9
|
+
**This is a feature-fork of [FlexLayout](https://github.com/caplin/FlexLayout) by Caplin.** The original project hasn't been updated in some time, so this fork includes bug fixes, performance improvements, and new features like `OptimizedLayout`.
|
|
10
10
|
|
|
11
|
-
To install
|
|
12
|
-
|
|
13
|
-
[Run the Demo](https://rawgit.com/caplin/FlexLayout/demos/demos/v0.8/demo/index.html)
|
|
14
|
-
|
|
15
|
-
Try it now using [JSFiddle](https://jsfiddle.net/fvd9btea/)
|
|
16
|
-
|
|
17
|
-
[API Doc](https://rawgit.com/caplin/FlexLayout/demos/demos/v0.8/typedoc/index.html)
|
|
11
|
+
To install: `yarn add @aptre/flex-layout`
|
|
18
12
|
|
|
19
13
|
FlexLayout's only dependency is React.
|
|
20
14
|
|
|
21
15
|
Features:
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
16
|
+
|
|
17
|
+
- splitters
|
|
18
|
+
- tabs
|
|
19
|
+
- tab dragging and ordering
|
|
20
|
+
- tab set dragging (move all the tabs in a tab set in one operation)
|
|
21
|
+
- dock to tab set or edge of frame
|
|
22
|
+
- maximize tab set (double click tab set header or use icon)
|
|
23
|
+
- tab overflow (show menu when tabs overflow, scroll tabs using mouse wheel)
|
|
24
|
+
- border tab sets
|
|
25
|
+
- popout tabs into new browser windows
|
|
26
|
+
- submodels, allow layouts inside layouts
|
|
27
|
+
- tab renaming (double click tab text to rename)
|
|
28
|
+
- theming - light, underline, gray, round and dark
|
|
29
|
+
- works on mobile devices (iPad, Android)
|
|
30
|
+
- add tabs using drag, add to active tab set, add to tab set by id
|
|
31
|
+
- tab and tab set attributes: enableTabStrip, enableDock, enableDrop...
|
|
32
|
+
- customizable tabs and tab set rendering
|
|
33
|
+
- component state is preserved when tabs are moved
|
|
34
|
+
- typescript type declarations
|
|
35
|
+
- **OptimizedLayout** - renders tab content outside FlexLayout's DOM for better performance
|
|
40
36
|
|
|
41
37
|
## Demo
|
|
42
38
|
|
|
@@ -50,19 +46,18 @@ yarn test:browser
|
|
|
50
46
|
|
|
51
47
|
Your browser will open to show + all the tests with vitest Browser Mode.
|
|
52
48
|
|
|
53
|
-
|
|
54
49
|
## Installation
|
|
55
50
|
|
|
56
51
|
FlexLayout is in the npm repository. install using:
|
|
57
52
|
|
|
58
53
|
```
|
|
59
|
-
npm install
|
|
54
|
+
npm install @aptre/flex-layout
|
|
60
55
|
```
|
|
61
56
|
|
|
62
57
|
Import FlexLayout in your modules:
|
|
63
58
|
|
|
64
59
|
```
|
|
65
|
-
import {Layout, Model} from '
|
|
60
|
+
import {Layout, Model} from '@aptre/flex-layout';
|
|
66
61
|
```
|
|
67
62
|
|
|
68
63
|
Include the light, underline, gray or dark theme by either:
|
|
@@ -70,27 +65,25 @@ Include the light, underline, gray or dark theme by either:
|
|
|
70
65
|
Adding an additional import:
|
|
71
66
|
|
|
72
67
|
```
|
|
73
|
-
import '
|
|
68
|
+
import '@aptre/flex-layout/style/light.css';
|
|
74
69
|
```
|
|
75
70
|
|
|
76
71
|
or by adding the css to your html:
|
|
77
72
|
|
|
78
73
|
```
|
|
79
|
-
<link rel="stylesheet" href="node_modules/
|
|
74
|
+
<link rel="stylesheet" href="node_modules/@aptre/flex-layout/style/light.css" />
|
|
80
75
|
```
|
|
81
76
|
|
|
82
77
|
## Usage
|
|
83
78
|
|
|
84
79
|
The `<Layout>` component renders the tab sets and splitters, it takes the following props:
|
|
85
80
|
|
|
86
|
-
|
|
87
81
|
#### Required props:
|
|
88
82
|
|
|
89
|
-
|
|
90
|
-
|
|
|
91
|
-
|
|
|
92
|
-
|
|
|
93
|
-
| factory | a factory function for creating React components |
|
|
83
|
+
| Prop | Description |
|
|
84
|
+
| ------- | ------------------------------------------------ |
|
|
85
|
+
| model | the layout model |
|
|
86
|
+
| factory | a factory function for creating React components |
|
|
94
87
|
|
|
95
88
|
Additional [optional props](#optional-layout-props)
|
|
96
89
|
|
|
@@ -118,8 +111,8 @@ var json = {
|
|
|
118
111
|
type: "tab",
|
|
119
112
|
name: "One",
|
|
120
113
|
component: "button",
|
|
121
|
-
}
|
|
122
|
-
]
|
|
114
|
+
},
|
|
115
|
+
],
|
|
123
116
|
},
|
|
124
117
|
{
|
|
125
118
|
type: "tabset",
|
|
@@ -129,11 +122,11 @@ var json = {
|
|
|
129
122
|
type: "tab",
|
|
130
123
|
name: "Two",
|
|
131
124
|
component: "button",
|
|
132
|
-
}
|
|
133
|
-
]
|
|
134
|
-
}
|
|
135
|
-
]
|
|
136
|
-
}
|
|
125
|
+
},
|
|
126
|
+
],
|
|
127
|
+
},
|
|
128
|
+
],
|
|
129
|
+
},
|
|
137
130
|
};
|
|
138
131
|
```
|
|
139
132
|
|
|
@@ -143,63 +136,141 @@ var json = {
|
|
|
143
136
|
const model = Model.fromJson(json);
|
|
144
137
|
|
|
145
138
|
function App() {
|
|
139
|
+
const factory = (node) => {
|
|
140
|
+
var component = node.getComponent();
|
|
146
141
|
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
return <button>{node.getName()}</button>;
|
|
152
|
-
}
|
|
153
|
-
}
|
|
142
|
+
if (component === "button") {
|
|
143
|
+
return <button>{node.getName()}</button>;
|
|
144
|
+
}
|
|
145
|
+
};
|
|
154
146
|
|
|
155
|
-
|
|
156
|
-
<Layout
|
|
157
|
-
model={model}
|
|
158
|
-
factory={factory} />
|
|
159
|
-
);
|
|
147
|
+
return <Layout model={model} factory={factory} />;
|
|
160
148
|
}
|
|
161
|
-
```
|
|
149
|
+
```
|
|
162
150
|
|
|
163
151
|
The above code would render two tab sets horizontally each containing a single tab that hosts a button component. The tabs could be moved and resized by dragging and dropping. Additional grids could be added to the layout by sending actions to the model.
|
|
164
152
|
|
|
165
|
-
|
|
153
|
+
## OptimizedLayout
|
|
154
|
+
|
|
155
|
+
`OptimizedLayout` is a wrapper around `Layout` that renders tab content outside of FlexLayout's DOM structure for better performance. This is particularly useful for complex tab content that shouldn't re-render when the layout model changes.
|
|
156
|
+
|
|
157
|
+
### Key Benefits
|
|
158
|
+
|
|
159
|
+
1. **No re-renders on layout changes** - Tab components are NOT re-rendered when the Model changes
|
|
160
|
+
2. **State preservation** - Tab state (scroll position, form inputs, etc.) is preserved across layout mutations
|
|
161
|
+
3. **CSS-only updates** - Only CSS properties change when layout changes, avoiding React re-renders
|
|
166
162
|
|
|
167
|
-
|
|
163
|
+
### How It Works
|
|
168
164
|
|
|
169
|
-
|
|
165
|
+
1. `OptimizedLayout` renders `Layout` with lightweight `TabRef` placeholders instead of actual tab content
|
|
166
|
+
2. `TabRef` components listen to resize/visibility events from `TabNode`s
|
|
167
|
+
3. A sibling `TabContainer` renders the actual tab content with absolute positioning
|
|
168
|
+
4. During drag operations, `TabContainer` uses `pointer-events: none` to prevent interfering with FlexLayout's drag overlay
|
|
169
|
+
|
|
170
|
+
### Example Usage
|
|
171
|
+
|
|
172
|
+
```tsx
|
|
173
|
+
import { Model, OptimizedLayout } from "@aptre/flex-layout";
|
|
174
|
+
import "@aptre/flex-layout/style/light.css";
|
|
175
|
+
|
|
176
|
+
const json = {
|
|
177
|
+
global: {},
|
|
178
|
+
borders: [],
|
|
179
|
+
layout: {
|
|
180
|
+
type: "row",
|
|
181
|
+
weight: 100,
|
|
182
|
+
children: [
|
|
183
|
+
{
|
|
184
|
+
type: "tabset",
|
|
185
|
+
weight: 50,
|
|
186
|
+
children: [{ type: "tab", name: "Editor", component: "editor" }],
|
|
187
|
+
},
|
|
188
|
+
{
|
|
189
|
+
type: "tabset",
|
|
190
|
+
weight: 50,
|
|
191
|
+
children: [{ type: "tab", name: "Preview", component: "preview" }],
|
|
192
|
+
},
|
|
193
|
+
],
|
|
194
|
+
},
|
|
195
|
+
};
|
|
196
|
+
|
|
197
|
+
const model = Model.fromJson(json);
|
|
198
|
+
|
|
199
|
+
function App() {
|
|
200
|
+
// renderTab receives a TabNode and returns the content for that tab
|
|
201
|
+
const renderTab = (node) => {
|
|
202
|
+
const component = node.getComponent();
|
|
203
|
+
|
|
204
|
+
if (component === "editor") {
|
|
205
|
+
return <Editor />;
|
|
206
|
+
}
|
|
207
|
+
if (component === "preview") {
|
|
208
|
+
return <Preview />;
|
|
209
|
+
}
|
|
210
|
+
return <div>Unknown component: {component}</div>;
|
|
211
|
+
};
|
|
212
|
+
|
|
213
|
+
return (
|
|
214
|
+
<div style={{ position: "relative", width: "100%", height: "100vh" }}>
|
|
215
|
+
<OptimizedLayout model={model} renderTab={renderTab} />
|
|
216
|
+
</div>
|
|
217
|
+
);
|
|
218
|
+
}
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
### Props
|
|
222
|
+
|
|
223
|
+
`OptimizedLayout` accepts all the same props as `Layout`, except:
|
|
224
|
+
|
|
225
|
+
- **`renderTab`** (required) - Replaces `factory`. A function that receives a `TabNode` and returns a React element to render as the tab content.
|
|
226
|
+
|
|
227
|
+
All other `Layout` props (`model`, `onModelChange`, `classNameMapper`, etc.) work the same way.
|
|
228
|
+
|
|
229
|
+
### When to Use OptimizedLayout
|
|
230
|
+
|
|
231
|
+
Use `OptimizedLayout` when:
|
|
232
|
+
|
|
233
|
+
- Your tab content is expensive to render
|
|
234
|
+
- You need to preserve internal component state across layout changes
|
|
235
|
+
- You have many tabs and want to minimize re-renders
|
|
236
|
+
|
|
237
|
+
Use the standard `Layout` when:
|
|
238
|
+
|
|
239
|
+
- You need tab content to re-render when the model changes
|
|
240
|
+
- Your tab content is simple and lightweight
|
|
241
|
+
- You need the factory pattern for lazy loading components
|
|
242
|
+
|
|
243
|
+
## Model JSON Structure
|
|
170
244
|
|
|
171
245
|
The model json contains 4 top level elements:
|
|
172
246
|
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
247
|
+
- global - (optional) where global options are defined
|
|
248
|
+
- layout - where the main row/tabset/tabs layout hierarchy is defined
|
|
249
|
+
- borders - (optional) where up to 4 borders are defined ("top", "bottom", "left", "right").
|
|
250
|
+
- popouts - (optional) where the popout windows are defined
|
|
177
251
|
|
|
178
252
|
The layout element is built up using 3 types of 'node':
|
|
179
253
|
|
|
180
|
-
|
|
181
|
-
, child 'rows' will render in the opposite orientation to their parent row.
|
|
254
|
+
- row - rows contains a list of tabsets and child rows, the top level 'row' will render horizontally (unless the global attribute rootOrientationVertical is set)
|
|
255
|
+
, child 'rows' will render in the opposite orientation to their parent row.
|
|
182
256
|
|
|
183
|
-
|
|
257
|
+
- tabset - tabsets contain a list of tabs and the index of the selected tab
|
|
184
258
|
|
|
185
|
-
|
|
259
|
+
- tab - tabs specify the name of the component that they should host (that will be loaded via the factory) and the text of the actual tab.
|
|
186
260
|
|
|
187
261
|
The layout structure is defined with rows within rows that contain tabsets that themselves contain tabs.
|
|
188
262
|
|
|
189
263
|
The optional borders element is made up of border nodes
|
|
190
264
|
|
|
191
|
-
|
|
192
|
-
top level element.
|
|
265
|
+
- border - borders contain a list of tabs and the index of the selected tab, they can only be used in the borders
|
|
266
|
+
top level element.
|
|
193
267
|
|
|
194
|
-
The tree structure for the JSON model is well defined as Typescript interfaces, see
|
|
268
|
+
The tree structure for the JSON model is well defined as Typescript interfaces, see [JSON Model](#json-model-definition)
|
|
195
269
|
|
|
196
270
|
Each type of node has a defined set of requires/optional attributes.
|
|
197
271
|
|
|
198
272
|
Weights on rows and tabsets specify the relative weight of these nodes within the parent row, the actual values do not matter just their relative values (ie two tabsets of weights 30,70 would render the same if they had weights of 3,7).
|
|
199
273
|
|
|
200
|
-
NOTE: the easiest way to create your initial layout JSON is to use the [demo](https://rawgit.com/caplin/FlexLayout/demos/demos/v0.8/demo/index.html) app, modify one of the
|
|
201
|
-
existing layouts by dragging/dropping and adding nodes then press the 'Show Layout JSON in console' button to print the JSON to the browser developer console.
|
|
202
|
-
|
|
203
274
|
By changing global or node attributes you can change the layout appearance and functionality, for example:
|
|
204
275
|
|
|
205
276
|
Setting tabSetEnableTabStrip:false in the global options would change the layout into a multi-splitter (without
|
|
@@ -213,29 +284,28 @@ tabs or drag and drop).
|
|
|
213
284
|
|
|
214
285
|
Once the model json has been loaded all changes to the model are applied through actions.
|
|
215
286
|
|
|
216
|
-
|
|
217
287
|
You apply actions using the `Model.doAction()` method.
|
|
218
288
|
|
|
219
289
|
This method takes a single argument, created by one of the action
|
|
220
|
-
generators (typically accessed as `FlexLayout.Actions.<actionName>`)
|
|
290
|
+
generators (typically accessed as `FlexLayout.Actions.<actionName>`).
|
|
221
291
|
|
|
222
|
-
[Actions
|
|
292
|
+
See [Actions.ts](src/model/Actions.ts) for available actions.
|
|
223
293
|
|
|
224
294
|
### Examples
|
|
225
295
|
|
|
226
296
|
```js
|
|
227
|
-
model.doAction(
|
|
228
|
-
|
|
229
|
-
|
|
297
|
+
model.doAction(
|
|
298
|
+
FlexLayout.Actions.updateModelAttributes({
|
|
299
|
+
splitterSize: 40,
|
|
300
|
+
}),
|
|
301
|
+
);
|
|
230
302
|
```
|
|
231
303
|
|
|
232
304
|
The above example would increase the size of the splitters, this could be used to make
|
|
233
305
|
adjusting the layout easier on a small device.
|
|
234
306
|
|
|
235
307
|
```js
|
|
236
|
-
model.doAction(FlexLayout.Actions.addNode(
|
|
237
|
-
{type:"tab", component:"grid", name:"a grid", id:"5"},
|
|
238
|
-
"1", FlexLayout.DockLocation.CENTER, 0));
|
|
308
|
+
model.doAction(FlexLayout.Actions.addNode({ type: "tab", component: "grid", name: "a grid", id: "5" }, "1", FlexLayout.DockLocation.CENTER, 0));
|
|
239
309
|
```
|
|
240
310
|
|
|
241
311
|
This example adds a new grid component to the center of tabset with id "1" and at the 0'th tab position (use value -1 to add to the end of the tabs).
|
|
@@ -249,59 +319,30 @@ implementing the `onAction` callback property of the `Layout`.
|
|
|
249
319
|
|
|
250
320
|
## Optional Layout Props
|
|
251
321
|
|
|
252
|
-
There are many optional properties that can be applied to the layout
|
|
253
|
-
|
|
254
|
-
[Layout Properties doc](https://rawgit.com/caplin/FlexLayout/demos/demos/v0.8/typedoc/interfaces/ILayoutProps.html)
|
|
255
|
-
|
|
322
|
+
There are many optional properties that can be applied to the layout. See [ILayoutProps in Layout.tsx](src/view/Layout.tsx) for the full interface.
|
|
256
323
|
|
|
257
324
|
## JSON Model Definition
|
|
258
325
|
|
|
259
|
-
The JSON model is well defined as a set of TypeScript interfaces
|
|
260
|
-
|
|
261
|
-
## Model Config Attributes
|
|
262
|
-
|
|
263
|
-
[Model Attributes doc](https://rawgit.com/caplin/FlexLayout/demos/demos/v0.8/typedoc/interfaces/IJsonModel.html)
|
|
264
|
-
|
|
265
|
-
## Global Config Attributes
|
|
266
|
-
|
|
267
|
-
[Global Attributes doc](https://rawgit.com/caplin/FlexLayout/demos/demos/v0.8/typedoc/interfaces/IGlobalAttributes.html)
|
|
268
|
-
|
|
269
|
-
## Row Config Attributes
|
|
270
|
-
|
|
271
|
-
[Row Attributes doc](https://rawgit.com/caplin/FlexLayout/demos/demos/v0.8/typedoc/interfaces/IJsonRowNode.html)
|
|
272
|
-
|
|
273
|
-
## Tab Set Config Attributes
|
|
274
|
-
|
|
275
|
-
[Tab set Attributes doc](https://rawgit.com/caplin/FlexLayout/demos/demos/v0.8/typedoc/interfaces/IJsonTabSetNode.html)
|
|
276
|
-
|
|
277
|
-
Note: tab sets will be dynamically created as tabs are moved, and deleted when all their tabs are removed (unless enableDeleteWhenEmpty is false).
|
|
278
|
-
|
|
279
|
-
## Tab Config attributes
|
|
280
|
-
|
|
281
|
-
[Tab Attributes doc](https://rawgit.com/caplin/FlexLayout/demos/demos/v0.8/typedoc/interfaces/IJsonTabNode.html)
|
|
282
|
-
|
|
283
|
-
## Border Config attributes
|
|
284
|
-
|
|
285
|
-
[Border Attributes doc](https://rawgit.com/caplin/FlexLayout/demos/demos/v0.8/typedoc/interfaces/IJsonBorderNode.html)
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
326
|
+
The JSON model is well defined as a set of TypeScript interfaces. See [IJsonModel.ts](src/model/IJsonModel.ts) for all available attributes:
|
|
289
327
|
|
|
328
|
+
- `IJsonModel` - Top-level model structure
|
|
329
|
+
- `IGlobalAttributes` - Global config attributes
|
|
330
|
+
- `IJsonRowNode` - Row config attributes
|
|
331
|
+
- `IJsonTabSetNode` - Tab set config attributes (note: tab sets are dynamically created as tabs are moved, and deleted when empty unless `enableDeleteWhenEmpty` is false)
|
|
332
|
+
- `IJsonTabNode` - Tab config attributes
|
|
333
|
+
- `IJsonBorderNode` - Border config attributes
|
|
290
334
|
|
|
291
335
|
## Layout Component Methods to Create New Tabs
|
|
292
336
|
|
|
293
|
-
There are methods on the Layout Component for adding tabs
|
|
294
|
-
|
|
295
|
-
[Layout Methods doc](https://rawgit.com/caplin/FlexLayout/demos/demos/v0.8/typedoc/classes/Layout.html)
|
|
337
|
+
There are methods on the Layout Component for adding tabs. See [Layout.tsx](src/view/Layout.tsx) for available methods.
|
|
296
338
|
|
|
297
339
|
Example:
|
|
298
340
|
|
|
299
341
|
```
|
|
300
342
|
layoutRef.current.addTabToTabSet("NAVIGATION", {type:"tab", component:"grid", name:"a grid"});
|
|
301
343
|
```
|
|
302
|
-
This would add a new grid component to the tab set with id "NAVIGATION" (where layoutRef is a ref to the Layout element, see https://reactjs.org/docs/refs-and-the-dom.html ).
|
|
303
|
-
|
|
304
344
|
|
|
345
|
+
This would add a new grid component to the tab set with id "NAVIGATION" (where layoutRef is a ref to the Layout element, see https://reactjs.org/docs/refs-and-the-dom.html ).
|
|
305
346
|
|
|
306
347
|
## Tab Node Events
|
|
307
348
|
|
|
@@ -309,6 +350,7 @@ You can handle events on nodes by adding a listener, this would typically be don
|
|
|
309
350
|
when the component is mounted in a useEffect method:
|
|
310
351
|
|
|
311
352
|
Example:
|
|
353
|
+
|
|
312
354
|
```
|
|
313
355
|
function MyComponent({node}) {
|
|
314
356
|
|
|
@@ -322,12 +364,12 @@ Example:
|
|
|
322
364
|
|
|
323
365
|
```
|
|
324
366
|
|
|
325
|
-
| Event
|
|
326
|
-
|
|
|
327
|
-
| resize
|
|
328
|
-
| close
|
|
329
|
-
| visibility |
|
|
330
|
-
| save
|
|
367
|
+
| Event | parameters | Description |
|
|
368
|
+
| ---------- | :--------: | -------------------------------------------------------------------------------------------------------------------------------- |
|
|
369
|
+
| resize | {rect} | called when tab is resized during layout, called before it is rendered with the new size |
|
|
370
|
+
| close | none | called when a tab is closed |
|
|
371
|
+
| visibility | {visible} | called when the visibility of a tab changes |
|
|
372
|
+
| save | none | called before a tabnode is serialized to json, use to save node config by adding data to the object returned by node.getConfig() |
|
|
331
373
|
|
|
332
374
|
## Popout Windows
|
|
333
375
|
|
|
@@ -356,31 +398,32 @@ Note: libraries may support popout windows by allowing you to specify the docume
|
|
|
356
398
|
for example see the getDocument() callback in agGrid at https://www.ag-grid.com/javascript-grid-callbacks/
|
|
357
399
|
|
|
358
400
|
### Limitations of Popouts
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
401
|
+
|
|
402
|
+
- FlexLayout uses React Portals to draw the popout window content,
|
|
403
|
+
this means all the code runs in the main Window's JS context, so effectively the popout windows are just extensions of the area on which the main window can render panels.
|
|
404
|
+
|
|
405
|
+
- Your code must use the popout window/document in popout windows when adding event listeners (e.g popoutDocument.addEventListener(...)).
|
|
406
|
+
|
|
407
|
+
- Timers throttle when main window is in the background
|
|
408
|
+
you could implement a webworker timer replacement if needed (which will not throttle)
|
|
409
|
+
- Many third party controls will use the global document for some event listeners,
|
|
410
|
+
these will not work correctly without modification
|
|
411
|
+
- Some third party controls will suspend when the global document is hidden
|
|
412
|
+
you can use the tab overlay attribute to 'gray out' these tabs when the main window is hidden
|
|
413
|
+
- Resize observers may be throttled (or stay attached to the main window), so you may need to use some other way to resize the component when in a popout (see aggrid component in demo).
|
|
414
|
+
- Popouts will not size and position correctly when the browser is zoomed (ie set to 50% zoom)
|
|
415
|
+
- Popouts cannot reload in maximized or minimized states
|
|
416
|
+
- by default flexlayout will maintain react state when moving tabs between windows, but you can use the
|
|
417
|
+
enableWindowReMount tab attribute to force the component to re-mount.
|
|
375
418
|
|
|
376
419
|
See this article about using React portals in this way: https://dev.to/noriste/the-challenges-of-rendering-an-openlayers-map-in-a-popup-through-react-2elh
|
|
377
420
|
|
|
378
421
|
## Alternative Layout Managers
|
|
379
422
|
|
|
380
|
-
| Name
|
|
381
|
-
| -------------
|
|
382
|
-
| rc-dock
|
|
383
|
-
| Dockview
|
|
384
|
-
| lumino
|
|
423
|
+
| Name | Repository |
|
|
424
|
+
| ------------- | :--------------------------------------------- |
|
|
425
|
+
| rc-dock | https://github.com/ticlo/rc-dock |
|
|
426
|
+
| Dockview | https://dockview.dev/ |
|
|
427
|
+
| lumino | https://github.com/jupyterlab/lumino |
|
|
385
428
|
| golden-layout | https://github.com/golden-layout/golden-layout |
|
|
386
|
-
| react-mosaic
|
|
429
|
+
| react-mosaic | https://github.com/nomcopter/react-mosaic |
|
package/dist/index.mjs
CHANGED
|
@@ -5786,9 +5786,16 @@ var LayoutInternal = class _LayoutInternal extends React19.Component {
|
|
|
5786
5786
|
this.dragEnterCount--;
|
|
5787
5787
|
if (this.dragEnterCount === 0) {
|
|
5788
5788
|
const relatedTarget = event.relatedTarget;
|
|
5789
|
-
if (relatedTarget
|
|
5790
|
-
this.
|
|
5791
|
-
|
|
5789
|
+
if (relatedTarget) {
|
|
5790
|
+
if (this.selfRef.current?.contains(relatedTarget)) {
|
|
5791
|
+
this.dragEnterCount = 1;
|
|
5792
|
+
return;
|
|
5793
|
+
}
|
|
5794
|
+
const tabContainer = relatedTarget.closest('[data-layout-path="/tab-container"]');
|
|
5795
|
+
if (tabContainer && this.selfRef.current?.parentElement?.contains(tabContainer)) {
|
|
5796
|
+
this.dragEnterCount = 1;
|
|
5797
|
+
return;
|
|
5798
|
+
}
|
|
5792
5799
|
}
|
|
5793
5800
|
this.onDragLeave(event);
|
|
5794
5801
|
}
|
|
@@ -5972,7 +5979,8 @@ function TabContainer({
|
|
|
5972
5979
|
tabs,
|
|
5973
5980
|
renderTab,
|
|
5974
5981
|
isDragging,
|
|
5975
|
-
classNameMapper
|
|
5982
|
+
classNameMapper,
|
|
5983
|
+
model
|
|
5976
5984
|
}) {
|
|
5977
5985
|
const getClassName = useCallback(
|
|
5978
5986
|
(defaultClassName) => {
|
|
@@ -5980,6 +5988,17 @@ function TabContainer({
|
|
|
5980
5988
|
},
|
|
5981
5989
|
[classNameMapper]
|
|
5982
5990
|
);
|
|
5991
|
+
const handlePointerDown = useCallback(
|
|
5992
|
+
(node) => {
|
|
5993
|
+
const parent = node.getParent();
|
|
5994
|
+
if (parent instanceof TabSetNode) {
|
|
5995
|
+
if (!parent.isActive()) {
|
|
5996
|
+
model.doAction(Actions.setActiveTabset(parent.getId(), Model.MAIN_WINDOW_ID));
|
|
5997
|
+
}
|
|
5998
|
+
}
|
|
5999
|
+
},
|
|
6000
|
+
[model]
|
|
6001
|
+
);
|
|
5983
6002
|
return /* @__PURE__ */ React20.createElement(
|
|
5984
6003
|
"div",
|
|
5985
6004
|
{
|
|
@@ -6018,7 +6037,8 @@ function TabContainer({
|
|
|
6018
6037
|
overflow: "auto",
|
|
6019
6038
|
// Tab panels receive pointer events when visible and not dragging
|
|
6020
6039
|
pointerEvents: visible && !isDragging ? "auto" : "none"
|
|
6021
|
-
}
|
|
6040
|
+
},
|
|
6041
|
+
onPointerDown: () => handlePointerDown(node)
|
|
6022
6042
|
},
|
|
6023
6043
|
renderTab(node)
|
|
6024
6044
|
);
|
|
@@ -6114,7 +6134,7 @@ function OptimizedLayout({ model, renderTab, classNameMapper, onDragStateChange,
|
|
|
6114
6134
|
},
|
|
6115
6135
|
[handleTabMount, handleRectChange, handleVisibilityChange]
|
|
6116
6136
|
);
|
|
6117
|
-
return /* @__PURE__ */ React20.createElement(React20.Fragment, null, /* @__PURE__ */ React20.createElement(Layout, { model, factory, classNameMapper, onDragStateChange: handleDragStateChange, onModelChange: handleModelChange, ...layoutProps }), /* @__PURE__ */ React20.createElement(TabContainer, { tabs, renderTab, isDragging, classNameMapper }));
|
|
6137
|
+
return /* @__PURE__ */ React20.createElement(React20.Fragment, null, /* @__PURE__ */ React20.createElement(Layout, { model, factory, classNameMapper, onDragStateChange: handleDragStateChange, onModelChange: handleModelChange, ...layoutProps }), /* @__PURE__ */ React20.createElement(TabContainer, { tabs, renderTab, isDragging, classNameMapper, model }));
|
|
6118
6138
|
}
|
|
6119
6139
|
|
|
6120
6140
|
// src/model/walk.ts
|