regular-layout 0.1.0 → 0.2.1
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/LICENSE.md +1 -1
- package/README.md +6 -6
- package/dist/extensions.d.ts +5 -4
- package/dist/index.d.ts +3 -3
- package/dist/index.js +15 -10
- package/dist/index.js.map +4 -4
- package/dist/{common → layout}/calculate_edge.d.ts +3 -2
- package/dist/{common → layout}/calculate_intersect.d.ts +13 -9
- package/dist/layout/constants.d.ts +81 -0
- package/dist/{common → layout}/flatten.d.ts +1 -1
- package/dist/{common → layout}/generate_grid.d.ts +5 -4
- package/dist/layout/generate_overlay.d.ts +2 -0
- package/dist/{common → layout}/insert_child.d.ts +3 -2
- package/dist/{common → layout}/redistribute_panel_sizes.d.ts +2 -2
- package/dist/{common → layout}/remove_child.d.ts +1 -1
- package/dist/{common/layout_config.d.ts → layout/types.d.ts} +6 -10
- package/dist/regular-layout-frame.d.ts +1 -4
- package/dist/regular-layout-tab.d.ts +26 -0
- package/dist/regular-layout.d.ts +37 -18
- package/package.json +9 -7
- package/src/extensions.ts +10 -4
- package/src/index.ts +3 -7
- package/src/layout/calculate_edge.ts +217 -0
- package/src/{common → layout}/calculate_intersect.ts +61 -101
- package/src/layout/constants.ts +119 -0
- package/src/{common → layout}/flatten.ts +1 -1
- package/src/{common → layout}/generate_grid.ts +120 -106
- package/src/{common → layout}/generate_overlay.ts +26 -12
- package/src/{common → layout}/insert_child.ts +105 -51
- package/src/{common → layout}/redistribute_panel_sizes.ts +11 -4
- package/src/{common → layout}/remove_child.ts +2 -2
- package/src/{common/layout_config.ts → layout/types.ts} +7 -19
- package/src/regular-layout-frame.ts +40 -74
- package/src/regular-layout-tab.ts +103 -0
- package/src/regular-layout.ts +260 -148
- package/themes/chicago.css +89 -0
- package/themes/fluxbox.css +110 -0
- package/themes/gibson.css +264 -0
- package/themes/hotdog.css +88 -0
- package/themes/lorax.css +130 -0
- package/dist/common/constants.d.ts +0 -29
- package/dist/common/generate_overlay.d.ts +0 -2
- package/src/common/calculate_edge.ts +0 -104
- package/src/common/constants.ts +0 -46
|
@@ -9,9 +9,8 @@
|
|
|
9
9
|
// ┃ * [Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0). * ┃
|
|
10
10
|
// ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
|
|
11
11
|
|
|
12
|
-
import {
|
|
13
|
-
import type { Layout } from "./
|
|
14
|
-
import { remove_child } from "./remove_child.ts";
|
|
12
|
+
import { DEFAULT_PHYSICS, type Physics } from "./constants.ts";
|
|
13
|
+
import type { Layout } from "./types.ts";
|
|
15
14
|
|
|
16
15
|
interface GridCell {
|
|
17
16
|
child: string;
|
|
@@ -21,80 +20,77 @@ interface GridCell {
|
|
|
21
20
|
rowEnd: number;
|
|
22
21
|
}
|
|
23
22
|
|
|
24
|
-
function
|
|
25
|
-
if (
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
for (let i = 1; i < sorted.length; i++) {
|
|
32
|
-
if (
|
|
33
|
-
Math.abs(sorted[i] - result[result.length - 1]) >
|
|
34
|
-
GRID_TRACK_COLLAPSE_TOLERANCE
|
|
35
|
-
) {
|
|
36
|
-
result.push(sorted[i]);
|
|
37
|
-
}
|
|
23
|
+
function dedupe_sort(physics: Physics, result: number[], pos: number) {
|
|
24
|
+
if (
|
|
25
|
+
result.length === 0 ||
|
|
26
|
+
Math.abs(pos - result[result.length - 1]) >
|
|
27
|
+
physics.GRID_TRACK_COLLAPSE_TOLERANCE
|
|
28
|
+
) {
|
|
29
|
+
result.push(pos);
|
|
38
30
|
}
|
|
39
31
|
|
|
40
32
|
return result;
|
|
41
33
|
}
|
|
42
34
|
|
|
43
|
-
function
|
|
35
|
+
function dedupe_positions(physics: Physics, positions: number[]): number[] {
|
|
36
|
+
const sorted = positions.sort((a, b) => a - b);
|
|
37
|
+
return sorted.reduce(dedupe_sort.bind(undefined, physics), []);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function collect_track_positions(
|
|
44
41
|
panel: Layout,
|
|
45
42
|
orientation: "horizontal" | "vertical",
|
|
46
43
|
start: number,
|
|
47
44
|
end: number,
|
|
45
|
+
physics: Physics,
|
|
48
46
|
): number[] {
|
|
49
47
|
if (panel.type === "child-panel") {
|
|
50
48
|
return [start, end];
|
|
51
49
|
}
|
|
52
50
|
|
|
51
|
+
const positions: number[] = [start, end];
|
|
53
52
|
if (panel.orientation === orientation) {
|
|
54
|
-
const positions: number[] = [start, end];
|
|
55
53
|
let current = start;
|
|
54
|
+
const range = end - start;
|
|
56
55
|
for (let i = 0; i < panel.children.length; i++) {
|
|
57
56
|
const size = panel.sizes[i];
|
|
58
|
-
const
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
57
|
+
const next = current + size * range;
|
|
58
|
+
positions.push(
|
|
59
|
+
...collect_track_positions(
|
|
60
|
+
panel.children[i],
|
|
61
|
+
orientation,
|
|
62
|
+
current,
|
|
63
|
+
next,
|
|
64
|
+
physics,
|
|
65
|
+
),
|
|
63
66
|
);
|
|
64
67
|
|
|
65
|
-
|
|
66
|
-
current = current + size * (end - start);
|
|
68
|
+
current = next;
|
|
67
69
|
}
|
|
68
|
-
|
|
69
|
-
return dedupePositions(positions);
|
|
70
70
|
} else {
|
|
71
|
-
const allPositions: number[] = [start, end];
|
|
72
71
|
for (const child of panel.children) {
|
|
73
|
-
|
|
74
|
-
child,
|
|
75
|
-
orientation,
|
|
76
|
-
start,
|
|
77
|
-
end,
|
|
72
|
+
positions.push(
|
|
73
|
+
...collect_track_positions(child, orientation, start, end, physics),
|
|
78
74
|
);
|
|
79
|
-
|
|
80
|
-
allPositions.push(...childPositions);
|
|
81
75
|
}
|
|
82
|
-
|
|
83
|
-
return dedupePositions(allPositions);
|
|
84
76
|
}
|
|
77
|
+
|
|
78
|
+
return dedupe_positions(physics, positions);
|
|
85
79
|
}
|
|
86
80
|
|
|
87
|
-
function
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
81
|
+
function find_track_index(
|
|
82
|
+
physics: Physics,
|
|
83
|
+
positions: number[],
|
|
84
|
+
value: number,
|
|
85
|
+
): number {
|
|
86
|
+
const index = positions.findIndex(
|
|
87
|
+
(pos) => Math.abs(pos - value) < physics.GRID_TRACK_COLLAPSE_TOLERANCE,
|
|
88
|
+
);
|
|
93
89
|
|
|
94
|
-
|
|
90
|
+
return index === -1 ? 0 : index;
|
|
95
91
|
}
|
|
96
92
|
|
|
97
|
-
function
|
|
93
|
+
function build_cells(
|
|
98
94
|
panel: Layout,
|
|
99
95
|
colPositions: number[],
|
|
100
96
|
rowPositions: number[],
|
|
@@ -102,28 +98,31 @@ function buildCells(
|
|
|
102
98
|
colEnd: number,
|
|
103
99
|
rowStart: number,
|
|
104
100
|
rowEnd: number,
|
|
101
|
+
physics: Physics,
|
|
105
102
|
): GridCell[] {
|
|
106
103
|
if (panel.type === "child-panel") {
|
|
107
104
|
const selected = panel.selected ?? 0;
|
|
108
105
|
return [
|
|
109
106
|
{
|
|
110
107
|
child: panel.child[selected],
|
|
111
|
-
colStart:
|
|
112
|
-
colEnd:
|
|
113
|
-
rowStart:
|
|
114
|
-
rowEnd:
|
|
108
|
+
colStart: find_track_index(physics, colPositions, colStart),
|
|
109
|
+
colEnd: find_track_index(physics, colPositions, colEnd),
|
|
110
|
+
rowStart: find_track_index(physics, rowPositions, rowStart),
|
|
111
|
+
rowEnd: find_track_index(physics, rowPositions, rowEnd),
|
|
115
112
|
},
|
|
116
113
|
];
|
|
117
114
|
}
|
|
118
115
|
|
|
119
|
-
const cells: GridCell[] = [];
|
|
120
116
|
const { children, sizes, orientation } = panel;
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
117
|
+
const isHorizontal = orientation === "horizontal";
|
|
118
|
+
let current = isHorizontal ? colStart : rowStart;
|
|
119
|
+
const range = isHorizontal ? colEnd - colStart : rowEnd - rowStart;
|
|
120
|
+
const cells: GridCell[] = [];
|
|
121
|
+
for (let i = 0; i < children.length; i++) {
|
|
122
|
+
const next = current + sizes[i] * range;
|
|
123
|
+
if (isHorizontal) {
|
|
125
124
|
cells.push(
|
|
126
|
-
...
|
|
125
|
+
...build_cells(
|
|
127
126
|
children[i],
|
|
128
127
|
colPositions,
|
|
129
128
|
rowPositions,
|
|
@@ -131,17 +130,12 @@ function buildCells(
|
|
|
131
130
|
next,
|
|
132
131
|
rowStart,
|
|
133
132
|
rowEnd,
|
|
133
|
+
physics,
|
|
134
134
|
),
|
|
135
135
|
);
|
|
136
|
-
|
|
137
|
-
current = next;
|
|
138
|
-
}
|
|
139
|
-
} else {
|
|
140
|
-
let current = rowStart;
|
|
141
|
-
for (let i = 0; i < children.length; i++) {
|
|
142
|
-
const next = current + sizes[i] * (rowEnd - rowStart);
|
|
136
|
+
} else {
|
|
143
137
|
cells.push(
|
|
144
|
-
...
|
|
138
|
+
...build_cells(
|
|
145
139
|
children[i],
|
|
146
140
|
colPositions,
|
|
147
141
|
rowPositions,
|
|
@@ -149,21 +143,27 @@ function buildCells(
|
|
|
149
143
|
colEnd,
|
|
150
144
|
current,
|
|
151
145
|
next,
|
|
146
|
+
physics,
|
|
152
147
|
),
|
|
153
148
|
);
|
|
154
|
-
|
|
155
|
-
current = next;
|
|
156
149
|
}
|
|
150
|
+
|
|
151
|
+
current = next;
|
|
157
152
|
}
|
|
158
153
|
|
|
159
154
|
return cells;
|
|
160
155
|
}
|
|
161
156
|
|
|
162
157
|
const host_template = (rowTemplate: string, colTemplate: string) =>
|
|
163
|
-
`:host {
|
|
158
|
+
`:host ::slotted(*){display:none}:host{display:grid;grid-template-rows:${rowTemplate};grid-template-columns:${colTemplate}}`;
|
|
164
159
|
|
|
165
|
-
const child_template = (
|
|
166
|
-
|
|
160
|
+
const child_template = (
|
|
161
|
+
physics: Physics,
|
|
162
|
+
slot: string,
|
|
163
|
+
rowPart: string,
|
|
164
|
+
colPart: string,
|
|
165
|
+
) =>
|
|
166
|
+
`:host ::slotted([${physics.CHILD_ATTRIBUTE_NAME}="${slot}"]){display:flex;grid-column:${colPart};grid-row:${rowPart}}`;
|
|
167
167
|
|
|
168
168
|
/**
|
|
169
169
|
* Generates CSS Grid styles to render a layout tree.
|
|
@@ -191,60 +191,74 @@ const child_template = (slot: string, rowPart: string, colPart: string) =>
|
|
|
191
191
|
* const css = create_css_grid_layout(layout);
|
|
192
192
|
* // Returns CSS like:
|
|
193
193
|
* // :host { display: grid; grid-template-columns: 25% 75%; ... }
|
|
194
|
-
* // :host ::slotted([
|
|
195
|
-
* // :host ::slotted([
|
|
194
|
+
* // :host ::slotted([name=sidebar]) { grid-column: 1; grid-row: 1; }
|
|
195
|
+
* // :host ::slotted([name=main]) { grid-column: 2; grid-row: 1; }
|
|
196
196
|
* ```
|
|
197
197
|
*/
|
|
198
198
|
export function create_css_grid_layout(
|
|
199
199
|
layout: Layout,
|
|
200
|
-
round: boolean = false,
|
|
201
200
|
overlay?: [string, string],
|
|
201
|
+
physics: Physics = DEFAULT_PHYSICS,
|
|
202
202
|
): string {
|
|
203
|
-
if (overlay) {
|
|
204
|
-
layout = remove_child(layout, overlay[0]);
|
|
205
|
-
}
|
|
206
|
-
|
|
207
203
|
if (layout.type === "child-panel") {
|
|
208
204
|
const selected = layout.selected ?? 0;
|
|
209
|
-
return
|
|
205
|
+
return [
|
|
206
|
+
host_template("100%", "100%"),
|
|
207
|
+
child_template(physics, layout.child[selected], "1", "1"),
|
|
208
|
+
].join("\n");
|
|
210
209
|
}
|
|
211
210
|
|
|
212
|
-
const
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
211
|
+
const createTemplate = (positions: number[]) => {
|
|
212
|
+
const sizes = positions
|
|
213
|
+
.slice(0, -1)
|
|
214
|
+
.map((pos, i) => positions[i + 1] - pos);
|
|
215
|
+
return sizes
|
|
216
|
+
.map((s) => `${physics.SHOULD_ROUND ? Math.round(s * 100) : s * 100}fr`)
|
|
217
|
+
.join(" ");
|
|
218
|
+
};
|
|
217
219
|
|
|
218
|
-
const
|
|
219
|
-
|
|
220
|
-
|
|
220
|
+
const colPositions = collect_track_positions(
|
|
221
|
+
layout,
|
|
222
|
+
"horizontal",
|
|
223
|
+
0,
|
|
224
|
+
1,
|
|
225
|
+
physics,
|
|
226
|
+
);
|
|
221
227
|
|
|
222
|
-
const
|
|
223
|
-
const
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
228
|
+
const colTemplate = createTemplate(colPositions);
|
|
229
|
+
const rowPositions = collect_track_positions(
|
|
230
|
+
layout,
|
|
231
|
+
"vertical",
|
|
232
|
+
0,
|
|
233
|
+
1,
|
|
234
|
+
physics,
|
|
235
|
+
);
|
|
227
236
|
|
|
228
|
-
const rowTemplate =
|
|
229
|
-
|
|
230
|
-
|
|
237
|
+
const rowTemplate = createTemplate(rowPositions);
|
|
238
|
+
const formatGridLine = (start: number, end: number) =>
|
|
239
|
+
end - start === 1 ? `${start + 1}` : `${start + 1} / ${end + 1}`;
|
|
240
|
+
|
|
241
|
+
const cells = build_cells(
|
|
242
|
+
layout,
|
|
243
|
+
colPositions,
|
|
244
|
+
rowPositions,
|
|
245
|
+
0,
|
|
246
|
+
1,
|
|
247
|
+
0,
|
|
248
|
+
1,
|
|
249
|
+
physics,
|
|
250
|
+
);
|
|
231
251
|
|
|
232
|
-
const cells = buildCells(layout, colPositions, rowPositions, 0, 1, 0, 1);
|
|
233
252
|
const css = [host_template(rowTemplate, colTemplate)];
|
|
234
253
|
for (const cell of cells) {
|
|
235
|
-
const colPart =
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
: `${cell.colStart + 1} / ${cell.colEnd + 1}`;
|
|
239
|
-
const rowPart =
|
|
240
|
-
cell.rowEnd - cell.rowStart === 1
|
|
241
|
-
? `${cell.rowStart + 1}`
|
|
242
|
-
: `${cell.rowStart + 1} / ${cell.rowEnd + 1}`;
|
|
243
|
-
|
|
244
|
-
css.push(`${child_template(cell.child, rowPart, colPart)}`);
|
|
254
|
+
const colPart = formatGridLine(cell.colStart, cell.colEnd);
|
|
255
|
+
const rowPart = formatGridLine(cell.rowStart, cell.rowEnd);
|
|
256
|
+
css.push(child_template(physics, cell.child, rowPart, colPart));
|
|
245
257
|
if (cell.child === overlay?.[1]) {
|
|
246
|
-
css.push(
|
|
247
|
-
css.push(
|
|
258
|
+
css.push(child_template(physics, overlay[0], rowPart, colPart));
|
|
259
|
+
css.push(
|
|
260
|
+
`:host ::slotted([${physics.CHILD_ATTRIBUTE_NAME}=${overlay[0]}]){z-index:1}`,
|
|
261
|
+
);
|
|
248
262
|
}
|
|
249
263
|
}
|
|
250
264
|
|
|
@@ -9,20 +9,34 @@
|
|
|
9
9
|
// ┃ * [Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0). * ┃
|
|
10
10
|
// ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
|
|
11
11
|
|
|
12
|
-
import
|
|
12
|
+
import { DEFAULT_PHYSICS } from "./constants";
|
|
13
|
+
import type { LayoutPath } from "./types";
|
|
13
14
|
|
|
14
15
|
export function updateOverlaySheet(
|
|
15
16
|
slot: string,
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
17
|
+
box: DOMRect,
|
|
18
|
+
style: CSSStyleDeclaration,
|
|
19
|
+
drag_target: LayoutPath<undefined> | null,
|
|
20
|
+
physics = DEFAULT_PHYSICS,
|
|
20
21
|
) {
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
const
|
|
26
|
-
|
|
27
|
-
|
|
22
|
+
if (!drag_target) {
|
|
23
|
+
return `:host ::slotted([${physics.CHILD_ATTRIBUTE_NAME}="${slot}"]){display:none;}`;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const {
|
|
27
|
+
view_window: { row_start, row_end, col_start, col_end },
|
|
28
|
+
} = drag_target;
|
|
29
|
+
|
|
30
|
+
const box_height =
|
|
31
|
+
box.height - parseFloat(style.paddingTop) - parseFloat(style.paddingBottom);
|
|
32
|
+
|
|
33
|
+
const box_width =
|
|
34
|
+
box.width - parseFloat(style.paddingLeft) - parseFloat(style.paddingRight);
|
|
35
|
+
|
|
36
|
+
const top = row_start * box_height + parseFloat(style.paddingTop);
|
|
37
|
+
const left = col_start * box_width + parseFloat(style.paddingLeft);
|
|
38
|
+
const height = (row_end - row_start) * box_height;
|
|
39
|
+
const width = (col_end - col_start) * box_width;
|
|
40
|
+
const css = `display:flex;position:absolute!important;z-index:1;top:${top}px;left:${left}px;height:${height}px;width:${width}px;`;
|
|
41
|
+
return `::slotted([${physics.CHILD_ATTRIBUTE_NAME}="${slot}"]){${css}}`;
|
|
28
42
|
}
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
// ┃ * [Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0). * ┃
|
|
10
10
|
// ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
|
|
11
11
|
|
|
12
|
-
import type { Layout } from "./
|
|
12
|
+
import type { Layout } from "./types.ts";
|
|
13
13
|
|
|
14
14
|
/**
|
|
15
15
|
* Inserts a new child panel into the layout tree at a specified location.
|
|
@@ -22,15 +22,20 @@ import type { Layout } from "./layout_config.ts";
|
|
|
22
22
|
* at root level.
|
|
23
23
|
* @param orientation - Orientation for newly created split panels. Defaults to
|
|
24
24
|
* "horizontal".
|
|
25
|
+
* @param is_edge - If true, create the split at the parent level.
|
|
25
26
|
* @returns A new layout tree with the child inserted (original is not mutated).
|
|
26
27
|
*/
|
|
27
28
|
export function insert_child(
|
|
28
29
|
panel: Layout,
|
|
29
30
|
child: string,
|
|
30
31
|
path: number[],
|
|
31
|
-
orientation
|
|
32
|
-
is_edge?: boolean,
|
|
32
|
+
orientation?: "horizontal" | "vertical",
|
|
33
33
|
): Layout {
|
|
34
|
+
const createChildPanel = (childId: string): Layout => ({
|
|
35
|
+
type: "child-panel",
|
|
36
|
+
child: [childId],
|
|
37
|
+
});
|
|
38
|
+
|
|
34
39
|
if (path.length === 0) {
|
|
35
40
|
// Insert at root level
|
|
36
41
|
if (panel.type === "child-panel") {
|
|
@@ -39,92 +44,136 @@ export function insert_child(
|
|
|
39
44
|
type: "child-panel",
|
|
40
45
|
child: [child, ...panel.child],
|
|
41
46
|
};
|
|
47
|
+
} else if (orientation) {
|
|
48
|
+
// When inserting at edge of root, wrap the entire panel in a new split
|
|
49
|
+
return {
|
|
50
|
+
type: "split-panel",
|
|
51
|
+
orientation: orientation,
|
|
52
|
+
children: [createChildPanel(child), panel],
|
|
53
|
+
sizes: [0.5, 0.5],
|
|
54
|
+
};
|
|
42
55
|
} else {
|
|
43
56
|
// Append to existing split-panel
|
|
44
|
-
const newChildren = [
|
|
45
|
-
|
|
46
|
-
{
|
|
47
|
-
type: "child-panel",
|
|
48
|
-
child: [child],
|
|
49
|
-
} as Layout,
|
|
50
|
-
];
|
|
51
|
-
|
|
52
|
-
const numChildren = newChildren.length;
|
|
53
|
-
const newSizes = Array(numChildren).fill(1 / numChildren);
|
|
57
|
+
const newChildren = [...panel.children, createChildPanel(child)];
|
|
58
|
+
const newSizes = [...panel.sizes, 1 / (newChildren.length - 1)];
|
|
54
59
|
return {
|
|
55
60
|
...panel,
|
|
56
61
|
children: newChildren,
|
|
57
|
-
sizes: newSizes,
|
|
62
|
+
sizes: redistribute(newSizes),
|
|
58
63
|
};
|
|
59
64
|
}
|
|
60
65
|
}
|
|
61
66
|
|
|
62
67
|
// Navigate down the path
|
|
63
68
|
const [index, ...restPath] = path;
|
|
69
|
+
|
|
70
|
+
// Special case: when orientation is provided and restPath is empty, handle edge insertion
|
|
71
|
+
if (orientation && restPath.length === 0) {
|
|
72
|
+
// If panel is a split-panel with the same orientation, insert into its children
|
|
73
|
+
if (panel.type === "split-panel" && panel.orientation === orientation) {
|
|
74
|
+
const newChildren = [...panel.children];
|
|
75
|
+
newChildren.splice(index, 0, createChildPanel(child));
|
|
76
|
+
const newSizes = [...panel.sizes];
|
|
77
|
+
newSizes.splice(index, 0, 1 / (newChildren.length - 1));
|
|
78
|
+
return {
|
|
79
|
+
...panel,
|
|
80
|
+
children: newChildren,
|
|
81
|
+
sizes: redistribute(newSizes),
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// Otherwise, wrap the entire panel in a new split at the edge
|
|
86
|
+
const children =
|
|
87
|
+
index === 0
|
|
88
|
+
? [createChildPanel(child), panel]
|
|
89
|
+
: [panel, createChildPanel(child)];
|
|
90
|
+
|
|
91
|
+
return {
|
|
92
|
+
type: "split-panel",
|
|
93
|
+
orientation: orientation,
|
|
94
|
+
children,
|
|
95
|
+
sizes: [0.5, 0.5],
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
|
|
64
99
|
if (panel.type === "child-panel") {
|
|
65
|
-
//
|
|
66
|
-
//
|
|
100
|
+
// Stack into child array only when ALL of these conditions are met:
|
|
101
|
+
// 1. Path has exactly one element (restPath is empty)
|
|
102
|
+
// 2. Orientation was NOT explicitly provided (orientation is undefined)
|
|
103
|
+
// 3. Index is within the valid stacking range [0, child.length]
|
|
104
|
+
if (
|
|
105
|
+
restPath.length === 0 &&
|
|
106
|
+
orientation === undefined &&
|
|
107
|
+
index >= 0 &&
|
|
108
|
+
index <= panel.child.length
|
|
109
|
+
) {
|
|
110
|
+
const newChild = [...panel.child];
|
|
111
|
+
newChild.splice(index, 0, child);
|
|
112
|
+
return {
|
|
113
|
+
...panel,
|
|
114
|
+
child: newChild,
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// Otherwise, wrap in a split panel and recurse
|
|
67
119
|
const newPanel: Layout = {
|
|
68
120
|
type: "split-panel",
|
|
69
|
-
orientation,
|
|
121
|
+
orientation: orientation || "horizontal",
|
|
70
122
|
children: [panel],
|
|
71
123
|
sizes: [1],
|
|
72
124
|
};
|
|
73
125
|
|
|
74
|
-
return insert_child(newPanel, child, path, orientation
|
|
126
|
+
return insert_child(newPanel, child, path, orientation);
|
|
75
127
|
}
|
|
76
128
|
|
|
77
129
|
if (restPath.length === 0 || index === panel.children.length) {
|
|
78
|
-
if (
|
|
79
|
-
panel
|
|
80
|
-
|
|
81
|
-
|
|
130
|
+
if (orientation && panel.children[index]) {
|
|
131
|
+
// When inserting at an edge, create a split panel with the new child and existing child
|
|
132
|
+
const newSplitPanel: Layout = {
|
|
133
|
+
type: "split-panel",
|
|
134
|
+
orientation: orientation,
|
|
135
|
+
children: [createChildPanel(child), panel.children[index]],
|
|
136
|
+
sizes: [0.5, 0.5],
|
|
137
|
+
};
|
|
138
|
+
|
|
139
|
+
const newChildren = [...panel.children];
|
|
140
|
+
newChildren[index] = newSplitPanel;
|
|
141
|
+
return {
|
|
142
|
+
...panel,
|
|
143
|
+
children: newChildren,
|
|
144
|
+
sizes: redistribute(panel.sizes),
|
|
145
|
+
};
|
|
82
146
|
}
|
|
83
147
|
|
|
84
148
|
// Insert at this level at the specified index
|
|
85
149
|
const newChildren = [...panel.children];
|
|
86
|
-
newChildren.splice(index, 0,
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
});
|
|
90
|
-
|
|
91
|
-
const numChildren = newChildren.length;
|
|
92
|
-
const newSizes = Array(numChildren).fill(1 / numChildren);
|
|
150
|
+
newChildren.splice(index, 0, createChildPanel(child));
|
|
151
|
+
const newSizes = [...panel.sizes];
|
|
152
|
+
newSizes.splice(index, 0, 1 / (newChildren.length - 1));
|
|
93
153
|
return {
|
|
94
154
|
...panel,
|
|
95
155
|
children: newChildren,
|
|
96
|
-
sizes: newSizes,
|
|
156
|
+
sizes: redistribute(newSizes),
|
|
97
157
|
};
|
|
98
158
|
}
|
|
99
159
|
|
|
100
160
|
const targetChild = panel.children[index];
|
|
101
|
-
if (targetChild.type === "child-panel" && restPath.length > 0) {
|
|
102
|
-
// Need to split this child-panel
|
|
103
|
-
const oppositeOrientation =
|
|
104
|
-
panel.orientation === "horizontal" ? "vertical" : "horizontal";
|
|
105
|
-
|
|
106
|
-
const newSplitPanel = insert_child(
|
|
107
|
-
targetChild,
|
|
108
|
-
child,
|
|
109
|
-
restPath,
|
|
110
|
-
oppositeOrientation,
|
|
111
|
-
is_edge,
|
|
112
|
-
);
|
|
113
161
|
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
162
|
+
// Determine the orientation to pass down when navigating into a child-panel
|
|
163
|
+
const childOrientation =
|
|
164
|
+
targetChild.type === "child-panel" &&
|
|
165
|
+
restPath.length > 0 &&
|
|
166
|
+
orientation !== undefined
|
|
167
|
+
? panel.orientation === "horizontal"
|
|
168
|
+
? "vertical"
|
|
169
|
+
: "horizontal"
|
|
170
|
+
: orientation;
|
|
121
171
|
|
|
122
172
|
const updatedChild = insert_child(
|
|
123
173
|
targetChild,
|
|
124
174
|
child,
|
|
125
175
|
restPath,
|
|
126
|
-
|
|
127
|
-
is_edge,
|
|
176
|
+
childOrientation,
|
|
128
177
|
);
|
|
129
178
|
|
|
130
179
|
const newChildren = [...panel.children];
|
|
@@ -134,3 +183,8 @@ export function insert_child(
|
|
|
134
183
|
children: newChildren,
|
|
135
184
|
};
|
|
136
185
|
}
|
|
186
|
+
|
|
187
|
+
function redistribute(arr: number[]): number[] {
|
|
188
|
+
const total = arr.reduce((sum, val) => sum + val, 0);
|
|
189
|
+
return arr.map((val) => val / total);
|
|
190
|
+
}
|
|
@@ -9,8 +9,8 @@
|
|
|
9
9
|
// ┃ * [Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0). * ┃
|
|
10
10
|
// ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
|
|
11
11
|
|
|
12
|
-
import {
|
|
13
|
-
import type { Layout } from "./
|
|
12
|
+
import { DEFAULT_PHYSICS } from "./constants.ts";
|
|
13
|
+
import type { Layout } from "./types.ts";
|
|
14
14
|
|
|
15
15
|
/**
|
|
16
16
|
* Adjusts panel sizes during a drag operation on a divider.
|
|
@@ -33,6 +33,7 @@ export function redistribute_panel_sizes(
|
|
|
33
33
|
panel: Layout,
|
|
34
34
|
path: number[],
|
|
35
35
|
delta: number,
|
|
36
|
+
physics = DEFAULT_PHYSICS,
|
|
36
37
|
): Layout {
|
|
37
38
|
// Clone the entire panel structure
|
|
38
39
|
const result = structuredClone(panel);
|
|
@@ -55,7 +56,12 @@ export function redistribute_panel_sizes(
|
|
|
55
56
|
|
|
56
57
|
// It would be fun to remove this condition.
|
|
57
58
|
if (index < current.sizes.length - 1) {
|
|
58
|
-
current.sizes = add_and_redistribute(
|
|
59
|
+
current.sizes = add_and_redistribute(
|
|
60
|
+
physics,
|
|
61
|
+
current.sizes,
|
|
62
|
+
index,
|
|
63
|
+
delta,
|
|
64
|
+
);
|
|
59
65
|
}
|
|
60
66
|
}
|
|
61
67
|
|
|
@@ -63,6 +69,7 @@ export function redistribute_panel_sizes(
|
|
|
63
69
|
}
|
|
64
70
|
|
|
65
71
|
function add_and_redistribute(
|
|
72
|
+
physics: typeof DEFAULT_PHYSICS,
|
|
66
73
|
arr: number[],
|
|
67
74
|
index: number,
|
|
68
75
|
delta: number,
|
|
@@ -83,7 +90,7 @@ function add_and_redistribute(
|
|
|
83
90
|
Math.sign(delta) *
|
|
84
91
|
Math.min(
|
|
85
92
|
Math.abs(delta),
|
|
86
|
-
(1 - MINIMUM_REDISTRIBUTION_SIZE_THRESHOLD) *
|
|
93
|
+
(1 - physics.MINIMUM_REDISTRIBUTION_SIZE_THRESHOLD) *
|
|
87
94
|
(delta > 0 ? before_total : after_total),
|
|
88
95
|
);
|
|
89
96
|
|
|
@@ -9,8 +9,8 @@
|
|
|
9
9
|
// ┃ * [Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0). * ┃
|
|
10
10
|
// ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
|
|
11
11
|
|
|
12
|
-
import type { Layout, TabLayout } from "./
|
|
13
|
-
import { EMPTY_PANEL } from "./
|
|
12
|
+
import type { Layout, TabLayout } from "./types.ts";
|
|
13
|
+
import { EMPTY_PANEL } from "./types.ts";
|
|
14
14
|
|
|
15
15
|
/**
|
|
16
16
|
* Removes a child panel from the layout tree by its name.
|