react-simple-dock 0.1.0 → 0.1.2
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 +77 -21
- package/{dist/index.js → index.js} +7 -4
- package/package.json +6 -61
- package/dist/index.css +0 -192
- package/dist/index.d.ts +0 -9
- package/dist/types.d.ts +0 -67
- package/dist/types.js +0 -6
- package/dist/utils.d.ts +0 -26
- package/dist/utils.js +0 -248
package/README.md
CHANGED
|
@@ -2,16 +2,27 @@
|
|
|
2
2
|
|
|
3
3
|
A set of React components to create a dockable interface, allowing to arrange and resize tabs.
|
|
4
4
|
|
|
5
|
-
## Installation
|
|
5
|
+
## Installation of the javascript package
|
|
6
6
|
|
|
7
7
|
```bash
|
|
8
8
|
npm install react-simple-dock
|
|
9
9
|
```
|
|
10
10
|
|
|
11
|
+
## Installation of the PRET python package
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
pip install pret-simple-dock
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## Demo
|
|
18
|
+
|
|
19
|
+
[](https://codesandbox.io/p/sandbox/zwgwp3)
|
|
20
|
+
|
|
11
21
|
## Usage
|
|
12
22
|
|
|
13
23
|
```tsx
|
|
14
24
|
import React from "react";
|
|
25
|
+
import ReactDOM from "react-dom";
|
|
15
26
|
import { Layout, Panel } from "react-simple-dock";
|
|
16
27
|
import { DndProvider } from "react-dnd";
|
|
17
28
|
import { HTML5Backend } from "react-dnd-html5-backend";
|
|
@@ -33,29 +44,74 @@ const DEFAULT_CONFIG = {
|
|
|
33
44
|
};
|
|
34
45
|
|
|
35
46
|
const App = () => (
|
|
36
|
-
<
|
|
37
|
-
<
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
>
|
|
42
|
-
<
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
<
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
<
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
</div>
|
|
53
|
-
</DndProvider>
|
|
47
|
+
<div style={{ background: "#bdbdbd", width: "100vw", height: "100vh" }}>
|
|
48
|
+
<Layout
|
|
49
|
+
/* optional initial layout config */
|
|
50
|
+
defaultConfig={DEFAULT_CONFIG}
|
|
51
|
+
>
|
|
52
|
+
<Panel key="Panel 1">
|
|
53
|
+
<p>Content 1</p>
|
|
54
|
+
</Panel>
|
|
55
|
+
<Panel key="Panel 2" header={<i>Italic title</i>}>
|
|
56
|
+
<p>Content 2</p>
|
|
57
|
+
</Panel>
|
|
58
|
+
<Panel key="Panel 3">
|
|
59
|
+
<p>Content 3</p>
|
|
60
|
+
</Panel>
|
|
61
|
+
</Layout>
|
|
62
|
+
</div>
|
|
54
63
|
);
|
|
55
64
|
|
|
56
65
|
ReactDOM.createRoot(document.getElementById("root") as HTMLElement).render(<App />);
|
|
57
66
|
```
|
|
58
67
|
|
|
59
|
-
##
|
|
68
|
+
## Development
|
|
69
|
+
|
|
70
|
+
### Installation
|
|
71
|
+
|
|
72
|
+
Clone the repository:
|
|
73
|
+
|
|
74
|
+
```bash
|
|
75
|
+
git clone https://github.com/percevalw/react-simple-dock.git
|
|
76
|
+
cd react-simple-dock
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
Install the dependencies:
|
|
80
|
+
|
|
81
|
+
```bash
|
|
82
|
+
yarn install
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
Make your changes and run the demo:
|
|
86
|
+
|
|
87
|
+
```bash
|
|
88
|
+
yarn start
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
### Build the javascript library
|
|
92
|
+
|
|
93
|
+
To build the javascript library:
|
|
94
|
+
|
|
95
|
+
```bash
|
|
96
|
+
yarn build:lib
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
### Build the PRET python package
|
|
60
100
|
|
|
61
|
-
|
|
101
|
+
Ensure `pret` is installed.
|
|
102
|
+
|
|
103
|
+
```bash
|
|
104
|
+
pip install pret
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
If you have changed the signature of the components, you will need to update the python stubs.
|
|
108
|
+
|
|
109
|
+
```bash
|
|
110
|
+
pret stub . SimpleDock pret/ui/simple_dock/__init__.py
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
To build the python library and make it available in your environment:
|
|
114
|
+
|
|
115
|
+
```bash
|
|
116
|
+
pip install .
|
|
117
|
+
```
|
|
@@ -223,19 +223,19 @@ const Overlay = ({ panelElements, onDrop, rootConfig, }) => {
|
|
|
223
223
|
if (config.kind === "row" || config === rootConfig) {
|
|
224
224
|
const zoneWidth = width / (config.kind === "row" ? config.children.length + 1 : 2);
|
|
225
225
|
// check that the dragged item is not the last item in the row and a single tab
|
|
226
|
-
if (lastTabs.length > 1 || lastTabs[0] !== name) {
|
|
226
|
+
if (config === rootConfig || lastTabs.length > 1 || lastTabs[0] !== name) {
|
|
227
227
|
pushZone("RIGHT", left + width - zoneWidth, top, zoneWidth, height);
|
|
228
228
|
}
|
|
229
|
-
if (firstTabs.length > 1 || firstTabs[0] !== name) {
|
|
229
|
+
if (config === rootConfig || firstTabs.length > 1 || firstTabs[0] !== name) {
|
|
230
230
|
pushZone("LEFT", left, top, zoneWidth, height);
|
|
231
231
|
}
|
|
232
232
|
}
|
|
233
233
|
if (config.kind === "column" || config === rootConfig) {
|
|
234
234
|
const zoneHeight = height / (config.kind === "column" ? config.children.length + 1 : 2);
|
|
235
|
-
if (lastTabs.length > 1 || lastTabs[0] !== name) {
|
|
235
|
+
if (config === rootConfig || lastTabs.length > 1 || lastTabs[0] !== name) {
|
|
236
236
|
pushZone("BOTTOM", left, top + height - zoneHeight, width, zoneHeight);
|
|
237
237
|
}
|
|
238
|
-
if (firstTabs.length > 1 || firstTabs[0] !== name) {
|
|
238
|
+
if (config === rootConfig || firstTabs.length > 1 || firstTabs[0] !== name) {
|
|
239
239
|
pushZone("TOP", left, top, width, zoneHeight);
|
|
240
240
|
}
|
|
241
241
|
}
|
|
@@ -378,3 +378,6 @@ export function Layout(props) {
|
|
|
378
378
|
}
|
|
379
379
|
return container;
|
|
380
380
|
}
|
|
381
|
+
Layout.defaultProps = {
|
|
382
|
+
wrapDnd: true,
|
|
383
|
+
};
|
package/package.json
CHANGED
|
@@ -1,66 +1,24 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "react-simple-dock",
|
|
3
|
-
"version": "0.1.
|
|
4
|
-
"main": "
|
|
5
|
-
"types": "
|
|
6
|
-
"type": "module",
|
|
3
|
+
"version": "0.1.2",
|
|
4
|
+
"main": "index.js",
|
|
5
|
+
"types": "index.d.ts",
|
|
7
6
|
"description": "Simple dock component for React",
|
|
8
7
|
"repository": "https://github.com/percevalw/react-simple-dock",
|
|
9
8
|
"author": "Perceval Wajsbürt <perceval.wajsburt@gmail.com>",
|
|
10
|
-
"scripts": {
|
|
11
|
-
"start": "react-scripts start",
|
|
12
|
-
"build": "react-scripts build",
|
|
13
|
-
"test": "react-scripts test",
|
|
14
|
-
"eject": "react-scripts eject",
|
|
15
|
-
"build:lib:babel": "rm -rf dist && NODE_ENV=production babel src/lib --out-dir dist --copy-files --ignore __tests__,spec.js,test.js,__snapshots__,*.md",
|
|
16
|
-
"build:lib": "rm -rf dist && tsc --build tsconfig.lib.json && cp src/lib/*.css dist && rm -rf lib/",
|
|
17
|
-
"prepublishOnly": "npm run build:lib",
|
|
18
|
-
"build:pret": "jlpm clean && jupyter labextension build .",
|
|
19
|
-
"build:pret:dev": "jlpm clean && jupyter labextension build --development True .",
|
|
20
|
-
"clean": "rm -rf pret/ui/simple_dock/js-extension lib",
|
|
21
|
-
"prepare": "husky install"
|
|
22
|
-
},
|
|
23
9
|
"dependencies": {
|
|
24
10
|
"react-dnd": ">=16.0.1",
|
|
25
11
|
"react-dnd-html5-backend": ">=16.0.1"
|
|
26
12
|
},
|
|
27
13
|
"peerDependencies": {
|
|
28
|
-
"
|
|
29
|
-
"react": ">=
|
|
30
|
-
"react-dom": ">=16.0.0"
|
|
31
|
-
},
|
|
32
|
-
"devDependencies": {
|
|
33
|
-
"@testing-library/dom": "^10.4.0",
|
|
34
|
-
"@testing-library/jest-dom": "^6.6.3",
|
|
35
|
-
"@testing-library/react": "^16.2.0",
|
|
36
|
-
"@testing-library/user-event": "^13.5.0",
|
|
37
|
-
"@types/jest": "^27.5.2",
|
|
38
|
-
"@types/node": "^16.18.126",
|
|
39
|
-
"@types/react": "^19.0.10",
|
|
40
|
-
"@types/react-dom": "^19.0.4",
|
|
41
|
-
"babel-cli": "^6.26.0",
|
|
42
|
-
"husky": ">=6",
|
|
43
|
-
"lint-staged": ">=10",
|
|
44
|
-
"prettier": "^3.5.3",
|
|
45
|
-
"react": "^19.0.0",
|
|
46
|
-
"react-app-rewired": "^2.2.1",
|
|
47
|
-
"react-dom": "^19.0.0",
|
|
48
|
-
"react-scripts": "5.0.1",
|
|
49
|
-
"ts-loader": "^9.5.2",
|
|
50
|
-
"typescript": "^5.8.2",
|
|
51
|
-
"web-vitals": "^2.1.4"
|
|
14
|
+
"react": ">=17.0.0",
|
|
15
|
+
"react-dom": ">=17.0.0"
|
|
52
16
|
},
|
|
53
17
|
"files": [
|
|
54
18
|
"dist",
|
|
55
19
|
"LICENSE",
|
|
56
20
|
"README.md"
|
|
57
21
|
],
|
|
58
|
-
"jupyterlab": {
|
|
59
|
-
"extension": "src/plugin.ts",
|
|
60
|
-
"schemaDir": "src/schema",
|
|
61
|
-
"outputDir": "pret/ui/simple_dock/js-extension",
|
|
62
|
-
"webpackConfig": "./webpack.jupyter.js"
|
|
63
|
-
},
|
|
64
22
|
"browserslist": {
|
|
65
23
|
"production": [
|
|
66
24
|
">0.2%",
|
|
@@ -72,18 +30,5 @@
|
|
|
72
30
|
"last 1 firefox version",
|
|
73
31
|
"last 1 safari version"
|
|
74
32
|
]
|
|
75
|
-
},
|
|
76
|
-
"prettier": {
|
|
77
|
-
"printWidth": 120,
|
|
78
|
-
"tabWidth": 4
|
|
79
|
-
},
|
|
80
|
-
"eslintConfig": {
|
|
81
|
-
"extends": [
|
|
82
|
-
"react-app",
|
|
83
|
-
"react-app/jest"
|
|
84
|
-
]
|
|
85
|
-
},
|
|
86
|
-
"lint-staged": {
|
|
87
|
-
"*.{js,css,md}": "prettier --write"
|
|
88
33
|
}
|
|
89
|
-
}
|
|
34
|
+
}
|
package/dist/index.css
DELETED
|
@@ -1,192 +0,0 @@
|
|
|
1
|
-
/* Light Theme, feel free to override */
|
|
2
|
-
:root {
|
|
3
|
-
--sd-grid-gap: 3px;
|
|
4
|
-
}
|
|
5
|
-
|
|
6
|
-
:root[data-theme="light"] {
|
|
7
|
-
--sd-background-color: #fff;
|
|
8
|
-
--sd-text-color: #000;
|
|
9
|
-
--sd-tab-border-color: #bdbdbd;
|
|
10
|
-
--sd-tab-background-color: white;
|
|
11
|
-
--sd-header-background-color: white;
|
|
12
|
-
--sd-header-border-color: #cecece;
|
|
13
|
-
--sd-overlay-color: rgba(69, 159, 232, 0.51);
|
|
14
|
-
--sd-highlight-color: #4591e8;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
/* Dark Theme, feel free to override */
|
|
18
|
-
:root[data-theme="dark"] {
|
|
19
|
-
--sd-background-color: #22272e;
|
|
20
|
-
--sd-text-color: #fff;
|
|
21
|
-
--sd-border-color: #666;
|
|
22
|
-
--sd-tab-border-color: #111;
|
|
23
|
-
--sd-highlight-color: #4591e8;
|
|
24
|
-
--sd-tab-background-color: #1c2128;
|
|
25
|
-
--sd-header-background-color: #333;
|
|
26
|
-
--sd-header-border-color: #444;
|
|
27
|
-
--sd-overlay-color: rgba(105, 159, 232, 0.51);
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
body[data-jp-theme-light] {
|
|
31
|
-
/* don't care about the theme, just that we're in JupyterLab */
|
|
32
|
-
--sd-grid-gap: 7px;
|
|
33
|
-
--sd-background-color: var(--jp-layout-color3);
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
.resize-border {
|
|
37
|
-
position: absolute;
|
|
38
|
-
background-color: var(--sd-background-color, #fff);
|
|
39
|
-
opacity: 0;
|
|
40
|
-
z-index: 10;
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
.resize-border:hover {
|
|
44
|
-
opacity: 0.8;
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
.resize-border.bottom {
|
|
48
|
-
bottom: calc(0px - var(--sd-grid-gap));
|
|
49
|
-
left: 0;
|
|
50
|
-
right: 0;
|
|
51
|
-
height: calc((var(--sd-grid-gap)));
|
|
52
|
-
cursor: row-resize;
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
.resize-border.right {
|
|
56
|
-
top: 0;
|
|
57
|
-
bottom: 0;
|
|
58
|
-
right: calc(0px - var(--sd-grid-gap));
|
|
59
|
-
width: calc((var(--sd-grid-gap)));
|
|
60
|
-
cursor: col-resize;
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
.container {
|
|
64
|
-
width: 100%;
|
|
65
|
-
height: 100%;
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
.tab-handle-overlay {
|
|
69
|
-
position: absolute;
|
|
70
|
-
box-sizing: border-box;
|
|
71
|
-
display: none;
|
|
72
|
-
top: 0;
|
|
73
|
-
left: 0;
|
|
74
|
-
right: 0;
|
|
75
|
-
bottom: 0;
|
|
76
|
-
z-index: 100;
|
|
77
|
-
background: var(--sd-overlay-color, rgba(69, 159, 232, 0.51));
|
|
78
|
-
border: 2px dashed var(--sd-highlight-color, #4591e8);
|
|
79
|
-
|
|
80
|
-
pointer-events: none;
|
|
81
|
-
transition: 0s linear;
|
|
82
|
-
transition-property: left, right, top, bottom, width, height;
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
.panel {
|
|
86
|
-
position: relative;
|
|
87
|
-
display: flex;
|
|
88
|
-
flex: 1 100000000 0;
|
|
89
|
-
align-self: stretch;
|
|
90
|
-
height: 100%;
|
|
91
|
-
width: 100%;
|
|
92
|
-
background: var(--sd-background-color);
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
.panel.leaf {
|
|
96
|
-
flex-direction: column;
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
.row > .panel-content,
|
|
100
|
-
.column > .panel-content {
|
|
101
|
-
display: grid;
|
|
102
|
-
grid-gap: var(--sd-grid-gap);
|
|
103
|
-
position: absolute;
|
|
104
|
-
left: 0;
|
|
105
|
-
top: 0;
|
|
106
|
-
width: 100%;
|
|
107
|
-
height: 100%;
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
.leaf > .panel-content {
|
|
111
|
-
position: relative;
|
|
112
|
-
flex: 1;
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
.leaf > .panel-content > div {
|
|
116
|
-
background: var(--sd-tab-background-color);
|
|
117
|
-
position: absolute;
|
|
118
|
-
top: 0;
|
|
119
|
-
left: 0;
|
|
120
|
-
width: 100%;
|
|
121
|
-
height: 100%;
|
|
122
|
-
overflow: scroll;
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
/* TAB HEADER */
|
|
126
|
-
|
|
127
|
-
.tab-header {
|
|
128
|
-
display: flex;
|
|
129
|
-
flex-direction: row;
|
|
130
|
-
height: var(--sd-header-height);
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
.tab-header-border {
|
|
134
|
-
position: relative;
|
|
135
|
-
height: 1px;
|
|
136
|
-
background: var(--sd-tab-border-color, #cecece);
|
|
137
|
-
margin-top: -2px;
|
|
138
|
-
z-index: 0;
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
.tab-header-bottom {
|
|
142
|
-
position: relative;
|
|
143
|
-
background: var(--sd-tab-background-color, white);
|
|
144
|
-
height: 3px;
|
|
145
|
-
z-index: 2;
|
|
146
|
-
border: solid var(--sd-tab-border-color, #bdbdbd);
|
|
147
|
-
border-width: 0 1px 1px 1px;
|
|
148
|
-
/*
|
|
149
|
-
Why did I put this ? It messes up the overall width of the layout
|
|
150
|
-
margin-left: -1px;
|
|
151
|
-
margin-right: -1px;
|
|
152
|
-
*/
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
.tab-placeholder {
|
|
156
|
-
width: 0;
|
|
157
|
-
transition: width 0.1s linear;
|
|
158
|
-
height: 0;
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
.tab-handle {
|
|
162
|
-
position: relative;
|
|
163
|
-
box-sizing: border-box;
|
|
164
|
-
padding: 5px;
|
|
165
|
-
color: var(--sd-text-color, #000);
|
|
166
|
-
background: var(--sd-tab-background-color, white);
|
|
167
|
-
font-size: calc((var(--sd-header-height) - 12px) * 0.8);
|
|
168
|
-
border: solid var(--sd-tab-border-color, #bdbdbd);
|
|
169
|
-
border-width: 1px 1px 1px 1px;
|
|
170
|
-
/* Too overlap the border of the next handle and avoid double borders */
|
|
171
|
-
margin-right: -1px;
|
|
172
|
-
z-index: 1;
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
.tab-handle:nth-child(2) {
|
|
176
|
-
border-left-width: 1px;
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
.tab-handle.tab-handle__hidden {
|
|
180
|
-
z-index: 0;
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
.tab-handle.tab-handle__visible:before {
|
|
184
|
-
content: "";
|
|
185
|
-
display: block;
|
|
186
|
-
background: var(--sd-highlight-color, #4591e8);
|
|
187
|
-
position: absolute;
|
|
188
|
-
top: 0;
|
|
189
|
-
left: 0;
|
|
190
|
-
right: 0;
|
|
191
|
-
height: 2px;
|
|
192
|
-
}
|
package/dist/index.d.ts
DELETED
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
import React from "react";
|
|
2
|
-
import "./index.css";
|
|
3
|
-
import { LayoutConfig, PanelProps } from "./types";
|
|
4
|
-
export declare const Panel: (props: PanelProps) => any;
|
|
5
|
-
export declare function Layout(props: {
|
|
6
|
-
children: React.ReactElement<PanelProps>[] | React.ReactElement<PanelProps>;
|
|
7
|
-
defaultConfig?: LayoutConfig;
|
|
8
|
-
wrapDnd?: boolean;
|
|
9
|
-
}): import("react/jsx-runtime").JSX.Element;
|
package/dist/types.d.ts
DELETED
|
@@ -1,67 +0,0 @@
|
|
|
1
|
-
import React from "react";
|
|
2
|
-
/**
|
|
3
|
-
* Leaf layout configuration.
|
|
4
|
-
*
|
|
5
|
-
* Represents a panel that contains one or more tabs along with the current tab index and its size.
|
|
6
|
-
*/
|
|
7
|
-
export type LeafLayoutConfig = {
|
|
8
|
-
kind: "leaf";
|
|
9
|
-
tabs: string[];
|
|
10
|
-
tabIndex: number;
|
|
11
|
-
size: number;
|
|
12
|
-
nesting?: number;
|
|
13
|
-
};
|
|
14
|
-
/**
|
|
15
|
-
* Container layout configuration.
|
|
16
|
-
*
|
|
17
|
-
* Represents a container panel that arranges its child panels in either a row or a column.
|
|
18
|
-
*/
|
|
19
|
-
export type ContainerLayoutConfig = {
|
|
20
|
-
kind: "row" | "column";
|
|
21
|
-
children: LayoutConfig[];
|
|
22
|
-
size: number;
|
|
23
|
-
nesting?: number;
|
|
24
|
-
};
|
|
25
|
-
/**
|
|
26
|
-
* Layout configuration.
|
|
27
|
-
*
|
|
28
|
-
* A union type that represents either a leaf panel (with tabs) or a container panel (row/column with children).
|
|
29
|
-
*/
|
|
30
|
-
export type LayoutConfig = LeafLayoutConfig | ContainerLayoutConfig;
|
|
31
|
-
/**
|
|
32
|
-
* Properties for a panel component.
|
|
33
|
-
*
|
|
34
|
-
* Defines the children to render within the panel, an optional name, and an optional header.
|
|
35
|
-
*/
|
|
36
|
-
export type PanelProps = {
|
|
37
|
-
children: React.ReactNode;
|
|
38
|
-
name?: string;
|
|
39
|
-
header?: React.ReactNode;
|
|
40
|
-
};
|
|
41
|
-
/**
|
|
42
|
-
* Represents a drop zone within the layout.
|
|
43
|
-
*
|
|
44
|
-
* Includes the rectangular coordinates, the associated layout configuration,
|
|
45
|
-
* an a to indicate the drop position, and a reference to the related DOM element.
|
|
46
|
-
*/
|
|
47
|
-
export type Zone = {
|
|
48
|
-
rect: {
|
|
49
|
-
left: number;
|
|
50
|
-
top: number;
|
|
51
|
-
width: number;
|
|
52
|
-
height: number;
|
|
53
|
-
};
|
|
54
|
-
config: LayoutConfig;
|
|
55
|
-
index: "LEFT" | "RIGHT" | "TOP" | "BOTTOM" | "CENTER" | "TAB";
|
|
56
|
-
element: HTMLElement;
|
|
57
|
-
before?: string;
|
|
58
|
-
};
|
|
59
|
-
/**
|
|
60
|
-
* Drag and drop item for tab header drag operations.
|
|
61
|
-
*
|
|
62
|
-
* Contains the panel name and a reference to the draggable element.
|
|
63
|
-
*/
|
|
64
|
-
export type DnDItem = {
|
|
65
|
-
name: string;
|
|
66
|
-
handleElement: HTMLDivElement;
|
|
67
|
-
};
|
package/dist/types.js
DELETED
package/dist/utils.d.ts
DELETED
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
import { Zone, LayoutConfig } from "./types";
|
|
2
|
-
/**
|
|
3
|
-
* Moves a panel within the layout based on a drop zone and panel name.
|
|
4
|
-
*
|
|
5
|
-
* This function traverses the layout config tree, removing the panel from its original position,
|
|
6
|
-
* and re-inserting it based on the provided drop zone. It handles both leaf and container nodes,
|
|
7
|
-
* ensuring proper size calculations and layout updates.
|
|
8
|
-
*
|
|
9
|
-
* @param zone - The drop zone where the panel is to be moved.
|
|
10
|
-
* @param name - The name of the panel being moved.
|
|
11
|
-
* @param inside - The layout config in which the move operation takes place.
|
|
12
|
-
* @returns The updated layout config after moving the panel.
|
|
13
|
-
*/
|
|
14
|
-
export declare const movePanel: (zone: Zone | null, name: string, inside: LayoutConfig) => LayoutConfig;
|
|
15
|
-
/**
|
|
16
|
-
* Filters panels from a layout configuration based on a provided list of panel names.
|
|
17
|
-
*
|
|
18
|
-
* Traverses the layout configuration tree and removes any panels (or tabs within panels)
|
|
19
|
-
* whose names are not included in the specified list. It simplifies nodes by filtering
|
|
20
|
-
* out unwanted tabs and recursively updating container nodes.
|
|
21
|
-
*
|
|
22
|
-
* @param names - An array of panel names that should remain in the layout.
|
|
23
|
-
* @param inside - The layout configuration to filter.
|
|
24
|
-
* @returns The updated layout configuration with only the specified panels, or null if empty.
|
|
25
|
-
*/
|
|
26
|
-
export declare const filterPanels: (names: string[], inside: LayoutConfig) => LayoutConfig;
|
package/dist/utils.js
DELETED
|
@@ -1,248 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Simplifies a layout configuration by flattening nested layouts with the same kind.
|
|
3
|
-
*
|
|
4
|
-
* If the layout configuration is a row or column, this function will flatten any nested child configurations
|
|
5
|
-
* that have the same kind as the parent. It recalculates the size of nested items based on their parent's size.
|
|
6
|
-
*
|
|
7
|
-
* @param config - The layout configuration to simplify.
|
|
8
|
-
* @returns The simplified layout configuration.
|
|
9
|
-
*/
|
|
10
|
-
const simplifyLayout = (config) => {
|
|
11
|
-
if (config.kind === "row" || config.kind === "column") {
|
|
12
|
-
let expandedChildren = [];
|
|
13
|
-
let changed = false;
|
|
14
|
-
config.children.forEach((child) => {
|
|
15
|
-
if (child.kind === config.kind) {
|
|
16
|
-
changed = true;
|
|
17
|
-
const totalChildContainerSize = child.children.reduce((a, b) => a + b.size, 0);
|
|
18
|
-
child.children.forEach((grandChild) => {
|
|
19
|
-
expandedChildren.push({
|
|
20
|
-
...grandChild,
|
|
21
|
-
size: (child.size * grandChild.size) / totalChildContainerSize,
|
|
22
|
-
});
|
|
23
|
-
});
|
|
24
|
-
}
|
|
25
|
-
else {
|
|
26
|
-
expandedChildren.push(child);
|
|
27
|
-
}
|
|
28
|
-
});
|
|
29
|
-
if (changed) {
|
|
30
|
-
config = { ...config, children: expandedChildren };
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
return config;
|
|
34
|
-
};
|
|
35
|
-
/**
|
|
36
|
-
* Moves a panel within the layout based on a drop zone and panel name.
|
|
37
|
-
*
|
|
38
|
-
* This function traverses the layout config tree, removing the panel from its original position,
|
|
39
|
-
* and re-inserting it based on the provided drop zone. It handles both leaf and container nodes,
|
|
40
|
-
* ensuring proper size calculations and layout updates.
|
|
41
|
-
*
|
|
42
|
-
* @param zone - The drop zone where the panel is to be moved.
|
|
43
|
-
* @param name - The name of the panel being moved.
|
|
44
|
-
* @param inside - The layout config in which the move operation takes place.
|
|
45
|
-
* @returns The updated layout config after moving the panel.
|
|
46
|
-
*/
|
|
47
|
-
export const movePanel = (zone, name, inside) => {
|
|
48
|
-
const editLayout = (visitedConfig) => {
|
|
49
|
-
let config = visitedConfig;
|
|
50
|
-
if (config.kind === "leaf" && config.tabs.includes(name)) {
|
|
51
|
-
/* If it's a simple leaf, try to remove the matching tab if it was
|
|
52
|
-
* the tab that was picked by the user (since we're moving it) */
|
|
53
|
-
const newTabs = config.tabs.filter((tabName) => tabName !== name);
|
|
54
|
-
config = {
|
|
55
|
-
...config,
|
|
56
|
-
tabs: newTabs,
|
|
57
|
-
tabIndex: Math.min(newTabs.length - 1, config.tabIndex),
|
|
58
|
-
};
|
|
59
|
-
if (config.tabs.length === 0) {
|
|
60
|
-
config = null;
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
/* If this zone is the zone targeted as drop-zone by the user */
|
|
64
|
-
if (zone && visitedConfig === zone.config) {
|
|
65
|
-
if (config === null) {
|
|
66
|
-
return {
|
|
67
|
-
kind: "leaf",
|
|
68
|
-
tabs: [name],
|
|
69
|
-
tabIndex: 0,
|
|
70
|
-
size: visitedConfig.size,
|
|
71
|
-
};
|
|
72
|
-
}
|
|
73
|
-
// or it's a container zone
|
|
74
|
-
else if (config.kind === "row" || config.kind === "column") {
|
|
75
|
-
const totalSize = config.children.reduce((a, b) => a + b.size, 0);
|
|
76
|
-
const fraction = config.kind === "column"
|
|
77
|
-
? zone.rect.height / zone.element.parentElement.offsetHeight
|
|
78
|
-
: zone.rect.width / zone.element.parentElement.offsetWidth;
|
|
79
|
-
// x / (y + x) = a
|
|
80
|
-
// x = a y + a x
|
|
81
|
-
// x = a y / ( 1 - a )
|
|
82
|
-
const newConfigKind = zone.index === "TOP" || zone.index === "BOTTOM" ? "column" : "row";
|
|
83
|
-
if (config.kind === newConfigKind) {
|
|
84
|
-
const newZone = {
|
|
85
|
-
kind: "leaf",
|
|
86
|
-
tabs: [name],
|
|
87
|
-
tabIndex: 0,
|
|
88
|
-
size: (totalSize * fraction) / (1 - fraction),
|
|
89
|
-
};
|
|
90
|
-
config = {
|
|
91
|
-
...config,
|
|
92
|
-
children: zone.index === "LEFT" || zone.index === "TOP"
|
|
93
|
-
? [newZone, ...config.children.map(editLayout).filter((c) => c !== null)]
|
|
94
|
-
: [...config.children.map(editLayout).filter((c) => c !== null), newZone],
|
|
95
|
-
};
|
|
96
|
-
}
|
|
97
|
-
else {
|
|
98
|
-
const newZone = {
|
|
99
|
-
kind: "leaf",
|
|
100
|
-
tabs: [name],
|
|
101
|
-
tabIndex: 0,
|
|
102
|
-
size: 50,
|
|
103
|
-
};
|
|
104
|
-
const oldConfig = {
|
|
105
|
-
...config,
|
|
106
|
-
// Remove the panel that is being moved
|
|
107
|
-
children: config.children.map(editLayout).filter((c) => c !== null),
|
|
108
|
-
size: 50,
|
|
109
|
-
};
|
|
110
|
-
config = {
|
|
111
|
-
kind: newConfigKind,
|
|
112
|
-
children: zone.index === "TOP" || zone.index === "LEFT" ? [newZone, oldConfig] : [oldConfig, newZone],
|
|
113
|
-
size: config.size,
|
|
114
|
-
};
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
// Or it's a leaf zone
|
|
118
|
-
else if (config.kind === "leaf") {
|
|
119
|
-
if (zone.index === "CENTER") {
|
|
120
|
-
const newTabs = [...config.tabs.filter((tabName) => tabName !== name), name];
|
|
121
|
-
config = {
|
|
122
|
-
...config,
|
|
123
|
-
tabs: newTabs,
|
|
124
|
-
tabIndex: newTabs.length - 1,
|
|
125
|
-
};
|
|
126
|
-
}
|
|
127
|
-
else if (zone.index === "TAB") {
|
|
128
|
-
const newTabs = [...config.tabs];
|
|
129
|
-
let insertIndex = newTabs.findIndex((tabName) => tabName === zone.before);
|
|
130
|
-
if (insertIndex === -1) {
|
|
131
|
-
insertIndex = newTabs.length;
|
|
132
|
-
}
|
|
133
|
-
newTabs.splice(insertIndex, 0, name);
|
|
134
|
-
config = {
|
|
135
|
-
...config,
|
|
136
|
-
tabs: newTabs,
|
|
137
|
-
tabIndex: insertIndex,
|
|
138
|
-
};
|
|
139
|
-
}
|
|
140
|
-
else {
|
|
141
|
-
const newZone = {
|
|
142
|
-
kind: "leaf",
|
|
143
|
-
tabs: [name],
|
|
144
|
-
tabIndex: 0,
|
|
145
|
-
size: 50,
|
|
146
|
-
};
|
|
147
|
-
config = {
|
|
148
|
-
kind: zone.index === "TOP" || zone.index === "BOTTOM" ? "column" : "row",
|
|
149
|
-
children: zone.index === "TOP" || zone.index === "LEFT"
|
|
150
|
-
? [newZone, { ...config, size: 50 }]
|
|
151
|
-
: [{ ...config, size: 50, }, newZone],
|
|
152
|
-
size: config.size,
|
|
153
|
-
};
|
|
154
|
-
}
|
|
155
|
-
}
|
|
156
|
-
}
|
|
157
|
-
// If there is nothing left after removing the dropped zone
|
|
158
|
-
else if (config === null) {
|
|
159
|
-
return null;
|
|
160
|
-
}
|
|
161
|
-
// Otherwise, recurse into the node
|
|
162
|
-
else {
|
|
163
|
-
let hasChanged = false;
|
|
164
|
-
if (config.kind !== "leaf") {
|
|
165
|
-
const children = config.children
|
|
166
|
-
.map((child) => {
|
|
167
|
-
const updated = editLayout(child);
|
|
168
|
-
if (updated !== child) {
|
|
169
|
-
hasChanged = true;
|
|
170
|
-
}
|
|
171
|
-
return updated;
|
|
172
|
-
})
|
|
173
|
-
.filter((child) => child !== null);
|
|
174
|
-
if (hasChanged) {
|
|
175
|
-
config = { ...config, children };
|
|
176
|
-
}
|
|
177
|
-
}
|
|
178
|
-
}
|
|
179
|
-
if ((config.kind === "leaf" && config.tabs.length === 0) ||
|
|
180
|
-
(config.kind !== "leaf" && config.children.length === 0)) {
|
|
181
|
-
return null;
|
|
182
|
-
}
|
|
183
|
-
/* Simplify a node of the layout by flattening row children of rows
|
|
184
|
-
* or column children of columns */
|
|
185
|
-
config = simplifyLayout(config);
|
|
186
|
-
return config;
|
|
187
|
-
};
|
|
188
|
-
return editLayout(inside);
|
|
189
|
-
};
|
|
190
|
-
/**
|
|
191
|
-
* Filters panels from a layout configuration based on a provided list of panel names.
|
|
192
|
-
*
|
|
193
|
-
* Traverses the layout configuration tree and removes any panels (or tabs within panels)
|
|
194
|
-
* whose names are not included in the specified list. It simplifies nodes by filtering
|
|
195
|
-
* out unwanted tabs and recursively updating container nodes.
|
|
196
|
-
*
|
|
197
|
-
* @param names - An array of panel names that should remain in the layout.
|
|
198
|
-
* @param inside - The layout configuration to filter.
|
|
199
|
-
* @returns The updated layout configuration with only the specified panels, or null if empty.
|
|
200
|
-
*/
|
|
201
|
-
export const filterPanels = (names, inside) => {
|
|
202
|
-
const editLayout = (visitedConfig) => {
|
|
203
|
-
let config = visitedConfig;
|
|
204
|
-
if (config.kind === "leaf" && !config.tabs.every((name) => names.includes(name))) {
|
|
205
|
-
/* If it's a simple leaf, try to remove the matching tab if it was
|
|
206
|
-
* the tab that was picked by the user (since we're moving it) */
|
|
207
|
-
config = {
|
|
208
|
-
...config,
|
|
209
|
-
tabs: config.tabs.filter((name) => names.includes(name)),
|
|
210
|
-
tabIndex: Math.min(config.tabs.length - 1, config.tabIndex),
|
|
211
|
-
};
|
|
212
|
-
if (config.tabs.length === 0) {
|
|
213
|
-
config = null;
|
|
214
|
-
}
|
|
215
|
-
}
|
|
216
|
-
// If there is nothing left after removing the dropped zone
|
|
217
|
-
if (config === null) {
|
|
218
|
-
return null;
|
|
219
|
-
}
|
|
220
|
-
// Otherwise, recurse into the node
|
|
221
|
-
else {
|
|
222
|
-
let hasChanged = false;
|
|
223
|
-
if (config.kind !== "leaf") {
|
|
224
|
-
const children = config.children
|
|
225
|
-
.map((child) => {
|
|
226
|
-
const updated = editLayout(child);
|
|
227
|
-
if (updated !== child) {
|
|
228
|
-
hasChanged = true;
|
|
229
|
-
}
|
|
230
|
-
return updated;
|
|
231
|
-
})
|
|
232
|
-
.filter((child) => child !== null);
|
|
233
|
-
if (hasChanged) {
|
|
234
|
-
config = { ...config, children };
|
|
235
|
-
}
|
|
236
|
-
}
|
|
237
|
-
}
|
|
238
|
-
if ((config.kind === "leaf" && config.tabs.length === 0) ||
|
|
239
|
-
(config.kind !== "leaf" && config.children.length === 0)) {
|
|
240
|
-
return null;
|
|
241
|
-
}
|
|
242
|
-
/* Simplify a node of the layout by flattening row children of rows
|
|
243
|
-
* or column children of columns */
|
|
244
|
-
config = simplifyLayout(config);
|
|
245
|
-
return config;
|
|
246
|
-
};
|
|
247
|
-
return editLayout(inside);
|
|
248
|
-
};
|