regular-layout 0.2.0 → 0.2.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/dist/index.js +6 -6
- package/dist/index.js.map +3 -3
- package/dist/layout/calculate_edge.d.ts +2 -1
- package/dist/layout/calculate_intersect.d.ts +8 -2
- package/dist/layout/constants.d.ts +76 -38
- package/dist/layout/generate_grid.d.ts +4 -3
- package/dist/layout/generate_overlay.d.ts +1 -1
- package/dist/layout/redistribute_panel_sizes.d.ts +1 -1
- package/dist/layout/types.d.ts +3 -7
- package/dist/regular-layout-frame.d.ts +10 -3
- package/dist/regular-layout.d.ts +63 -12
- package/package.json +4 -2
- package/src/layout/calculate_edge.ts +26 -16
- package/src/layout/calculate_intersect.ts +11 -9
- package/src/layout/constants.ts +94 -38
- package/src/layout/generate_grid.ts +71 -27
- package/src/layout/generate_overlay.ts +5 -3
- package/src/layout/insert_child.ts +5 -5
- package/src/layout/redistribute_panel_sizes.ts +20 -9
- package/src/layout/remove_child.ts +7 -7
- package/src/layout/types.ts +3 -7
- package/src/regular-layout-frame.ts +53 -65
- package/src/regular-layout-tab.ts +5 -5
- package/src/regular-layout.ts +206 -114
- package/themes/lorax.css +2 -1
package/src/layout/constants.ts
CHANGED
|
@@ -12,52 +12,108 @@
|
|
|
12
12
|
import type { OverlayMode } from "./types";
|
|
13
13
|
|
|
14
14
|
/**
|
|
15
|
-
*
|
|
16
|
-
*
|
|
15
|
+
* Instance-specific constants which define the behavior and rendering details
|
|
16
|
+
* of a `<regular-layout>`.
|
|
17
17
|
*/
|
|
18
|
-
export
|
|
18
|
+
export interface Physics {
|
|
19
|
+
/**
|
|
20
|
+
* The prefix to use for `CustomEvent`s generated by `regular-layout`, e.g.
|
|
21
|
+
* `"regular-layout-before-update"`.
|
|
22
|
+
*/
|
|
23
|
+
CUSTOM_EVENT_NAME_PREFIX: string;
|
|
19
24
|
|
|
20
|
-
/**
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
25
|
+
/**
|
|
26
|
+
* The attribute name to use for matching child `Element`s to grid
|
|
27
|
+
* positions.
|
|
28
|
+
*/
|
|
29
|
+
CHILD_ATTRIBUTE_NAME: string;
|
|
24
30
|
|
|
25
|
-
/**
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
31
|
+
/**
|
|
32
|
+
* The minimum number of pixels the mouse must move to be considered a drag.
|
|
33
|
+
*/
|
|
34
|
+
MIN_DRAG_DISTANCE: number;
|
|
29
35
|
|
|
30
|
-
/**
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
export const MINIMUM_REDISTRIBUTION_SIZE_THRESHOLD = 0.15;
|
|
36
|
+
/**
|
|
37
|
+
* Should floating point pixel calculations be rounded. Useful for testing.
|
|
38
|
+
*/
|
|
39
|
+
SHOULD_ROUND: boolean;
|
|
35
40
|
|
|
36
|
-
/**
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
41
|
+
/**
|
|
42
|
+
* Class name to use for child elements in overlay position (dragging).
|
|
43
|
+
*/
|
|
44
|
+
OVERLAY_CLASSNAME: string;
|
|
40
45
|
|
|
41
|
-
/**
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
export const SPLIT_ROOT_EDGE_TOLERANCE = 0.01;
|
|
46
|
+
/**
|
|
47
|
+
* The percentage of the maximum resize distance that will be clamped.
|
|
48
|
+
*/
|
|
49
|
+
MINIMUM_REDISTRIBUTION_SIZE_THRESHOLD: number;
|
|
46
50
|
|
|
47
|
-
/**
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
* this value are treated as the same position to avoid redundant grid tracks.
|
|
52
|
-
*/
|
|
53
|
-
export const GRID_TRACK_COLLAPSE_TOLERANCE = 0.001;
|
|
51
|
+
/**
|
|
52
|
+
* Threshold from panel edge that is considered a split vs drop action.
|
|
53
|
+
*/
|
|
54
|
+
SPLIT_EDGE_TOLERANCE: number;
|
|
54
55
|
|
|
55
|
-
/**
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
56
|
+
/**
|
|
57
|
+
* Threshold from _container_ edge that is considered a split action on the root
|
|
58
|
+
* node.
|
|
59
|
+
*/
|
|
60
|
+
SPLIT_ROOT_EDGE_TOLERANCE: number;
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Tolerance threshold for considering two grid track positions as identical.
|
|
64
|
+
*
|
|
65
|
+
* When collecting and deduplicating track positions, any positions closer than
|
|
66
|
+
* this value are treated as the same position to avoid redundant grid tracks.
|
|
67
|
+
*/
|
|
68
|
+
GRID_TRACK_COLLAPSE_TOLERANCE: number;
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* The overlay default behavior.
|
|
72
|
+
*/
|
|
73
|
+
OVERLAY_DEFAULT: OverlayMode;
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Width of split panel dividers in pixels (for hit-test purposes).
|
|
77
|
+
*/
|
|
78
|
+
GRID_DIVIDER_SIZE: number;
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Whether the grid should trigger column resize if the grid itself is not
|
|
82
|
+
* the `event.target`.
|
|
83
|
+
*/
|
|
84
|
+
GRID_DIVIDER_CHECK_TARGET: boolean;
|
|
85
|
+
}
|
|
59
86
|
|
|
60
87
|
/**
|
|
61
|
-
*
|
|
88
|
+
* Like `GlobalPhysics`, but suitable for partial definition for incremental
|
|
89
|
+
* updates.
|
|
62
90
|
*/
|
|
63
|
-
export
|
|
91
|
+
export interface PhysicsUpdate {
|
|
92
|
+
CUSTOM_EVENT_NAME_PREFIX?: string;
|
|
93
|
+
CHILD_ATTRIBUTE_NAME?: string;
|
|
94
|
+
MIN_DRAG_DISTANCE?: number;
|
|
95
|
+
SHOULD_ROUND?: boolean;
|
|
96
|
+
OVERLAY_CLASSNAME?: string;
|
|
97
|
+
MINIMUM_REDISTRIBUTION_SIZE_THRESHOLD?: number;
|
|
98
|
+
SPLIT_EDGE_TOLERANCE?: number;
|
|
99
|
+
SPLIT_ROOT_EDGE_TOLERANCE?: number;
|
|
100
|
+
GRID_TRACK_COLLAPSE_TOLERANCE?: number;
|
|
101
|
+
OVERLAY_DEFAULT?: OverlayMode;
|
|
102
|
+
GRID_DIVIDER_SIZE?: number;
|
|
103
|
+
GRID_DIVIDER_CHECK_TARGET?: boolean;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
export const DEFAULT_PHYSICS: Physics = Object.freeze({
|
|
107
|
+
CUSTOM_EVENT_NAME_PREFIX: "regular-layout",
|
|
108
|
+
CHILD_ATTRIBUTE_NAME: "name",
|
|
109
|
+
MIN_DRAG_DISTANCE: 10,
|
|
110
|
+
SHOULD_ROUND: false,
|
|
111
|
+
OVERLAY_CLASSNAME: "overlay",
|
|
112
|
+
MINIMUM_REDISTRIBUTION_SIZE_THRESHOLD: 0.15,
|
|
113
|
+
SPLIT_EDGE_TOLERANCE: 0.25,
|
|
114
|
+
SPLIT_ROOT_EDGE_TOLERANCE: 0.01,
|
|
115
|
+
GRID_TRACK_COLLAPSE_TOLERANCE: 0.001,
|
|
116
|
+
OVERLAY_DEFAULT: "absolute",
|
|
117
|
+
GRID_DIVIDER_SIZE: 6,
|
|
118
|
+
GRID_DIVIDER_CHECK_TARGET: true,
|
|
119
|
+
});
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
// ┃ * [Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0). * ┃
|
|
10
10
|
// ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
|
|
11
11
|
|
|
12
|
-
import {
|
|
12
|
+
import { DEFAULT_PHYSICS, type Physics } from "./constants.ts";
|
|
13
13
|
import type { Layout } from "./types.ts";
|
|
14
14
|
|
|
15
15
|
interface GridCell {
|
|
@@ -20,10 +20,11 @@ interface GridCell {
|
|
|
20
20
|
rowEnd: number;
|
|
21
21
|
}
|
|
22
22
|
|
|
23
|
-
function dedupe_sort(result: number[], pos: number) {
|
|
23
|
+
function dedupe_sort(physics: Physics, result: number[], pos: number) {
|
|
24
24
|
if (
|
|
25
25
|
result.length === 0 ||
|
|
26
|
-
Math.abs(pos - result[result.length - 1]) >
|
|
26
|
+
Math.abs(pos - result[result.length - 1]) >
|
|
27
|
+
physics.GRID_TRACK_COLLAPSE_TOLERANCE
|
|
27
28
|
) {
|
|
28
29
|
result.push(pos);
|
|
29
30
|
}
|
|
@@ -31,9 +32,9 @@ function dedupe_sort(result: number[], pos: number) {
|
|
|
31
32
|
return result;
|
|
32
33
|
}
|
|
33
34
|
|
|
34
|
-
function dedupe_positions(positions: number[]): number[] {
|
|
35
|
+
function dedupe_positions(physics: Physics, positions: number[]): number[] {
|
|
35
36
|
const sorted = positions.sort((a, b) => a - b);
|
|
36
|
-
return sorted.reduce(dedupe_sort, []);
|
|
37
|
+
return sorted.reduce(dedupe_sort.bind(undefined, physics), []);
|
|
37
38
|
}
|
|
38
39
|
|
|
39
40
|
function collect_track_positions(
|
|
@@ -41,6 +42,7 @@ function collect_track_positions(
|
|
|
41
42
|
orientation: "horizontal" | "vertical",
|
|
42
43
|
start: number,
|
|
43
44
|
end: number,
|
|
45
|
+
physics: Physics,
|
|
44
46
|
): number[] {
|
|
45
47
|
if (panel.type === "child-panel") {
|
|
46
48
|
return [start, end];
|
|
@@ -59,6 +61,7 @@ function collect_track_positions(
|
|
|
59
61
|
orientation,
|
|
60
62
|
current,
|
|
61
63
|
next,
|
|
64
|
+
physics,
|
|
62
65
|
),
|
|
63
66
|
);
|
|
64
67
|
|
|
@@ -67,17 +70,21 @@ function collect_track_positions(
|
|
|
67
70
|
} else {
|
|
68
71
|
for (const child of panel.children) {
|
|
69
72
|
positions.push(
|
|
70
|
-
...collect_track_positions(child, orientation, start, end),
|
|
73
|
+
...collect_track_positions(child, orientation, start, end, physics),
|
|
71
74
|
);
|
|
72
75
|
}
|
|
73
76
|
}
|
|
74
77
|
|
|
75
|
-
return dedupe_positions(positions);
|
|
78
|
+
return dedupe_positions(physics, positions);
|
|
76
79
|
}
|
|
77
80
|
|
|
78
|
-
function find_track_index(
|
|
81
|
+
function find_track_index(
|
|
82
|
+
physics: Physics,
|
|
83
|
+
positions: number[],
|
|
84
|
+
value: number,
|
|
85
|
+
): number {
|
|
79
86
|
const index = positions.findIndex(
|
|
80
|
-
(pos) => Math.abs(pos - value) < GRID_TRACK_COLLAPSE_TOLERANCE,
|
|
87
|
+
(pos) => Math.abs(pos - value) < physics.GRID_TRACK_COLLAPSE_TOLERANCE,
|
|
81
88
|
);
|
|
82
89
|
|
|
83
90
|
return index === -1 ? 0 : index;
|
|
@@ -91,16 +98,17 @@ function build_cells(
|
|
|
91
98
|
colEnd: number,
|
|
92
99
|
rowStart: number,
|
|
93
100
|
rowEnd: number,
|
|
101
|
+
physics: Physics,
|
|
94
102
|
): GridCell[] {
|
|
95
103
|
if (panel.type === "child-panel") {
|
|
96
104
|
const selected = panel.selected ?? 0;
|
|
97
105
|
return [
|
|
98
106
|
{
|
|
99
|
-
child: panel.
|
|
100
|
-
colStart: find_track_index(colPositions, colStart),
|
|
101
|
-
colEnd: find_track_index(colPositions, colEnd),
|
|
102
|
-
rowStart: find_track_index(rowPositions, rowStart),
|
|
103
|
-
rowEnd: find_track_index(rowPositions, rowEnd),
|
|
107
|
+
child: panel.tabs[selected],
|
|
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),
|
|
104
112
|
},
|
|
105
113
|
];
|
|
106
114
|
}
|
|
@@ -122,6 +130,7 @@ function build_cells(
|
|
|
122
130
|
next,
|
|
123
131
|
rowStart,
|
|
124
132
|
rowEnd,
|
|
133
|
+
physics,
|
|
125
134
|
),
|
|
126
135
|
);
|
|
127
136
|
} else {
|
|
@@ -134,6 +143,7 @@ function build_cells(
|
|
|
134
143
|
colEnd,
|
|
135
144
|
current,
|
|
136
145
|
next,
|
|
146
|
+
physics,
|
|
137
147
|
),
|
|
138
148
|
);
|
|
139
149
|
}
|
|
@@ -147,8 +157,13 @@ function build_cells(
|
|
|
147
157
|
const host_template = (rowTemplate: string, colTemplate: string) =>
|
|
148
158
|
`:host ::slotted(*){display:none}:host{display:grid;grid-template-rows:${rowTemplate};grid-template-columns:${colTemplate}}`;
|
|
149
159
|
|
|
150
|
-
const child_template = (
|
|
151
|
-
|
|
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}}`;
|
|
152
167
|
|
|
153
168
|
/**
|
|
154
169
|
* Generates CSS Grid styles to render a layout tree.
|
|
@@ -167,8 +182,8 @@ const child_template = (slot: string, rowPart: string, colPart: string) =>
|
|
|
167
182
|
* type: "split-panel",
|
|
168
183
|
* orientation: "horizontal",
|
|
169
184
|
* children: [
|
|
170
|
-
* { type: "child-panel",
|
|
171
|
-
* { type: "child-panel",
|
|
185
|
+
* { type: "child-panel", tabs: "sidebar" },
|
|
186
|
+
* { type: "child-panel", tabs: "main" }
|
|
172
187
|
* ],
|
|
173
188
|
* sizes: [0.25, 0.75]
|
|
174
189
|
* };
|
|
@@ -182,12 +197,15 @@ const child_template = (slot: string, rowPart: string, colPart: string) =>
|
|
|
182
197
|
*/
|
|
183
198
|
export function create_css_grid_layout(
|
|
184
199
|
layout: Layout,
|
|
185
|
-
round: boolean = false,
|
|
186
200
|
overlay?: [string, string],
|
|
201
|
+
physics: Physics = DEFAULT_PHYSICS,
|
|
187
202
|
): string {
|
|
188
203
|
if (layout.type === "child-panel") {
|
|
189
204
|
const selected = layout.selected ?? 0;
|
|
190
|
-
return
|
|
205
|
+
return [
|
|
206
|
+
host_template("100%", "100%"),
|
|
207
|
+
child_template(physics, layout.tabs[selected], "1", "1"),
|
|
208
|
+
].join("\n");
|
|
191
209
|
}
|
|
192
210
|
|
|
193
211
|
const createTemplate = (positions: number[]) => {
|
|
@@ -195,26 +213,52 @@ export function create_css_grid_layout(
|
|
|
195
213
|
.slice(0, -1)
|
|
196
214
|
.map((pos, i) => positions[i + 1] - pos);
|
|
197
215
|
return sizes
|
|
198
|
-
.map((s) => `${
|
|
216
|
+
.map((s) => `${physics.SHOULD_ROUND ? Math.round(s * 100) : s * 100}fr`)
|
|
199
217
|
.join(" ");
|
|
200
218
|
};
|
|
201
219
|
|
|
202
|
-
const colPositions = collect_track_positions(
|
|
220
|
+
const colPositions = collect_track_positions(
|
|
221
|
+
layout,
|
|
222
|
+
"horizontal",
|
|
223
|
+
0,
|
|
224
|
+
1,
|
|
225
|
+
physics,
|
|
226
|
+
);
|
|
227
|
+
|
|
203
228
|
const colTemplate = createTemplate(colPositions);
|
|
204
|
-
const rowPositions = collect_track_positions(
|
|
229
|
+
const rowPositions = collect_track_positions(
|
|
230
|
+
layout,
|
|
231
|
+
"vertical",
|
|
232
|
+
0,
|
|
233
|
+
1,
|
|
234
|
+
physics,
|
|
235
|
+
);
|
|
236
|
+
|
|
205
237
|
const rowTemplate = createTemplate(rowPositions);
|
|
206
238
|
const formatGridLine = (start: number, end: number) =>
|
|
207
239
|
end - start === 1 ? `${start + 1}` : `${start + 1} / ${end + 1}`;
|
|
208
240
|
|
|
209
|
-
const cells = build_cells(
|
|
241
|
+
const cells = build_cells(
|
|
242
|
+
layout,
|
|
243
|
+
colPositions,
|
|
244
|
+
rowPositions,
|
|
245
|
+
0,
|
|
246
|
+
1,
|
|
247
|
+
0,
|
|
248
|
+
1,
|
|
249
|
+
physics,
|
|
250
|
+
);
|
|
251
|
+
|
|
210
252
|
const css = [host_template(rowTemplate, colTemplate)];
|
|
211
253
|
for (const cell of cells) {
|
|
212
254
|
const colPart = formatGridLine(cell.colStart, cell.colEnd);
|
|
213
255
|
const rowPart = formatGridLine(cell.rowStart, cell.rowEnd);
|
|
214
|
-
css.push(child_template(cell.child, rowPart, colPart));
|
|
256
|
+
css.push(child_template(physics, cell.child, rowPart, colPart));
|
|
215
257
|
if (cell.child === overlay?.[1]) {
|
|
216
|
-
css.push(child_template(overlay[0], rowPart, colPart));
|
|
217
|
-
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
|
+
);
|
|
218
262
|
}
|
|
219
263
|
}
|
|
220
264
|
|
|
@@ -9,16 +9,18 @@
|
|
|
9
9
|
// ┃ * [Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0). * ┃
|
|
10
10
|
// ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
|
|
11
11
|
|
|
12
|
+
import { DEFAULT_PHYSICS } from "./constants";
|
|
12
13
|
import type { LayoutPath } from "./types";
|
|
13
14
|
|
|
14
15
|
export function updateOverlaySheet(
|
|
15
16
|
slot: string,
|
|
16
17
|
box: DOMRect,
|
|
17
18
|
style: CSSStyleDeclaration,
|
|
18
|
-
drag_target: LayoutPath
|
|
19
|
+
drag_target: LayoutPath | null,
|
|
20
|
+
physics = DEFAULT_PHYSICS,
|
|
19
21
|
) {
|
|
20
22
|
if (!drag_target) {
|
|
21
|
-
return `:host ::slotted([
|
|
23
|
+
return `:host ::slotted([${physics.CHILD_ATTRIBUTE_NAME}="${slot}"]){display:none;}`;
|
|
22
24
|
}
|
|
23
25
|
|
|
24
26
|
const {
|
|
@@ -36,5 +38,5 @@ export function updateOverlaySheet(
|
|
|
36
38
|
const height = (row_end - row_start) * box_height;
|
|
37
39
|
const width = (col_end - col_start) * box_width;
|
|
38
40
|
const css = `display:flex;position:absolute!important;z-index:1;top:${top}px;left:${left}px;height:${height}px;width:${width}px;`;
|
|
39
|
-
return `::slotted([
|
|
41
|
+
return `::slotted([${physics.CHILD_ATTRIBUTE_NAME}="${slot}"]){${css}}`;
|
|
40
42
|
}
|
|
@@ -33,7 +33,7 @@ export function insert_child(
|
|
|
33
33
|
): Layout {
|
|
34
34
|
const createChildPanel = (childId: string): Layout => ({
|
|
35
35
|
type: "child-panel",
|
|
36
|
-
|
|
36
|
+
tabs: [childId],
|
|
37
37
|
});
|
|
38
38
|
|
|
39
39
|
if (path.length === 0) {
|
|
@@ -42,7 +42,7 @@ export function insert_child(
|
|
|
42
42
|
// Add to existing child-panel as a tab
|
|
43
43
|
return {
|
|
44
44
|
type: "child-panel",
|
|
45
|
-
|
|
45
|
+
tabs: [child, ...panel.tabs],
|
|
46
46
|
};
|
|
47
47
|
} else if (orientation) {
|
|
48
48
|
// When inserting at edge of root, wrap the entire panel in a new split
|
|
@@ -105,13 +105,13 @@ export function insert_child(
|
|
|
105
105
|
restPath.length === 0 &&
|
|
106
106
|
orientation === undefined &&
|
|
107
107
|
index >= 0 &&
|
|
108
|
-
index <= panel.
|
|
108
|
+
index <= panel.tabs.length
|
|
109
109
|
) {
|
|
110
|
-
const newChild = [...panel.
|
|
110
|
+
const newChild = [...panel.tabs];
|
|
111
111
|
newChild.splice(index, 0, child);
|
|
112
112
|
return {
|
|
113
113
|
...panel,
|
|
114
|
-
|
|
114
|
+
tabs: newChild,
|
|
115
115
|
};
|
|
116
116
|
}
|
|
117
117
|
|
|
@@ -9,7 +9,7 @@
|
|
|
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.ts";
|
|
13
13
|
import type { Layout } from "./types.ts";
|
|
14
14
|
|
|
15
15
|
/**
|
|
@@ -32,7 +32,8 @@ import type { Layout } from "./types.ts";
|
|
|
32
32
|
export function redistribute_panel_sizes(
|
|
33
33
|
panel: Layout,
|
|
34
34
|
path: number[],
|
|
35
|
-
delta: number,
|
|
35
|
+
delta: number | undefined,
|
|
36
|
+
physics = DEFAULT_PHYSICS,
|
|
36
37
|
): Layout {
|
|
37
38
|
// Clone the entire panel structure
|
|
38
39
|
const result = structuredClone(panel);
|
|
@@ -40,7 +41,7 @@ export function redistribute_panel_sizes(
|
|
|
40
41
|
// Find the orientation of the insertion panel,
|
|
41
42
|
// and scale the delta on the respective axis if aligned.
|
|
42
43
|
let current: Layout = result;
|
|
43
|
-
const deltas = { horizontal: delta, vertical: delta };
|
|
44
|
+
const deltas = { horizontal: delta || 0, vertical: delta || 0 };
|
|
44
45
|
for (let i = 0; i < path.length - 1; i++) {
|
|
45
46
|
if (current.type === "split-panel") {
|
|
46
47
|
deltas[current.orientation] /= current.sizes[path[i]];
|
|
@@ -50,12 +51,21 @@ export function redistribute_panel_sizes(
|
|
|
50
51
|
|
|
51
52
|
// Apply the redistribution at the final path index
|
|
52
53
|
if (current.type === "split-panel") {
|
|
53
|
-
|
|
54
|
-
|
|
54
|
+
if (delta === undefined) {
|
|
55
|
+
current.sizes = current.sizes.map((_) => 1 / current.sizes.length);
|
|
56
|
+
} else {
|
|
57
|
+
const delta = deltas[current.orientation];
|
|
58
|
+
const index = path[path.length - 1];
|
|
55
59
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
60
|
+
// It would be fun to remove this condition.
|
|
61
|
+
if (index < current.sizes.length - 1) {
|
|
62
|
+
current.sizes = add_and_redistribute(
|
|
63
|
+
physics,
|
|
64
|
+
current.sizes,
|
|
65
|
+
index,
|
|
66
|
+
delta,
|
|
67
|
+
);
|
|
68
|
+
}
|
|
59
69
|
}
|
|
60
70
|
}
|
|
61
71
|
|
|
@@ -63,6 +73,7 @@ export function redistribute_panel_sizes(
|
|
|
63
73
|
}
|
|
64
74
|
|
|
65
75
|
function add_and_redistribute(
|
|
76
|
+
physics: typeof DEFAULT_PHYSICS,
|
|
66
77
|
arr: number[],
|
|
67
78
|
index: number,
|
|
68
79
|
delta: number,
|
|
@@ -83,7 +94,7 @@ function add_and_redistribute(
|
|
|
83
94
|
Math.sign(delta) *
|
|
84
95
|
Math.min(
|
|
85
96
|
Math.abs(delta),
|
|
86
|
-
(1 - MINIMUM_REDISTRIBUTION_SIZE_THRESHOLD) *
|
|
97
|
+
(1 - physics.MINIMUM_REDISTRIBUTION_SIZE_THRESHOLD) *
|
|
87
98
|
(delta > 0 ? before_total : after_total),
|
|
88
99
|
);
|
|
89
100
|
|
|
@@ -26,14 +26,14 @@ import { EMPTY_PANEL } from "./types.ts";
|
|
|
26
26
|
export function remove_child(panel: Layout, child: string): Layout {
|
|
27
27
|
// If this is a child panel, handle tab removal
|
|
28
28
|
if (panel.type === "child-panel") {
|
|
29
|
-
if (panel.
|
|
30
|
-
const newChild = panel.
|
|
29
|
+
if (panel.tabs.includes(child)) {
|
|
30
|
+
const newChild = panel.tabs.filter((c) => c !== child);
|
|
31
31
|
if (newChild.length === 0) {
|
|
32
32
|
return structuredClone(EMPTY_PANEL);
|
|
33
33
|
}
|
|
34
34
|
return {
|
|
35
35
|
type: "child-panel",
|
|
36
|
-
|
|
36
|
+
tabs: newChild,
|
|
37
37
|
};
|
|
38
38
|
}
|
|
39
39
|
|
|
@@ -46,7 +46,7 @@ export function remove_child(panel: Layout, child: string): Layout {
|
|
|
46
46
|
// Try to remove the child from this split panel's children
|
|
47
47
|
const index = result.children.findIndex((p) => {
|
|
48
48
|
if (p.type === "child-panel") {
|
|
49
|
-
return p.
|
|
49
|
+
return p.tabs.includes(child);
|
|
50
50
|
}
|
|
51
51
|
|
|
52
52
|
return false;
|
|
@@ -54,7 +54,7 @@ export function remove_child(panel: Layout, child: string): Layout {
|
|
|
54
54
|
|
|
55
55
|
if (index !== -1) {
|
|
56
56
|
const tab_layout = result.children[index] as TabLayout;
|
|
57
|
-
if (tab_layout.
|
|
57
|
+
if (tab_layout.tabs.length === 1) {
|
|
58
58
|
// Found the child at this level - remove it
|
|
59
59
|
const newChildren = result.children.filter((_, i) => i !== index);
|
|
60
60
|
const newSizes = remove_and_redistribute(result.sizes, index);
|
|
@@ -67,10 +67,10 @@ export function remove_child(panel: Layout, child: string): Layout {
|
|
|
67
67
|
result.children = newChildren;
|
|
68
68
|
result.sizes = newSizes;
|
|
69
69
|
} else {
|
|
70
|
-
tab_layout.
|
|
70
|
+
tab_layout.tabs.splice(tab_layout.tabs.indexOf(child), 1);
|
|
71
71
|
if (
|
|
72
72
|
tab_layout.selected &&
|
|
73
|
-
tab_layout.selected >= tab_layout.
|
|
73
|
+
tab_layout.selected >= tab_layout.tabs.length
|
|
74
74
|
) {
|
|
75
75
|
tab_layout.selected--;
|
|
76
76
|
}
|
package/src/layout/types.ts
CHANGED
|
@@ -58,7 +58,7 @@ export interface SplitLayout {
|
|
|
58
58
|
*/
|
|
59
59
|
export interface TabLayout {
|
|
60
60
|
type: "child-panel";
|
|
61
|
-
|
|
61
|
+
tabs: string[];
|
|
62
62
|
selected?: number;
|
|
63
63
|
}
|
|
64
64
|
|
|
@@ -75,12 +75,8 @@ export interface LayoutDivider {
|
|
|
75
75
|
|
|
76
76
|
/**
|
|
77
77
|
* Represents a panel location result from hit detection.
|
|
78
|
-
*
|
|
79
|
-
* Contains both the panel identifier and its grid position in relative units.
|
|
80
|
-
* The generic parameter `T` allows DOM-only properties (e.g. `DOMRect`) to be
|
|
81
|
-
* shared in this cross-platform module.
|
|
82
78
|
*/
|
|
83
|
-
export interface LayoutPath
|
|
79
|
+
export interface LayoutPath {
|
|
84
80
|
type: "layout-path";
|
|
85
81
|
slot: string;
|
|
86
82
|
path: number[];
|
|
@@ -91,7 +87,7 @@ export interface LayoutPath<T = undefined> {
|
|
|
91
87
|
row_offset: number;
|
|
92
88
|
orientation: Orientation;
|
|
93
89
|
is_edge: boolean;
|
|
94
|
-
layout:
|
|
90
|
+
layout: Layout;
|
|
95
91
|
}
|
|
96
92
|
|
|
97
93
|
/**
|