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
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
// ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
|
|
2
|
+
// ░░░░░░░░▄▀░█▀▄░█▀▀░█▀▀░█░█░█░░░█▀█░█▀▄░░░░░█░░░█▀█░█░█░█▀█░█░█░▀█▀░▀▄░░░░░░░░
|
|
3
|
+
// ░░░░░░░▀▄░░█▀▄░█▀▀░█░█░█░█░█░░░█▀█░█▀▄░▀▀▀░█░░░█▀█░░█░░█░█░█░█░░█░░░▄▀░░░░░░░
|
|
4
|
+
// ░░░░░░░░░▀░▀░▀░▀▀▀░▀▀▀░▀▀▀░▀▀▀░▀░▀░▀░▀░░░░░▀▀▀░▀░▀░░▀░░▀▀▀░▀▀▀░░▀░░▀░░░░░░░░░
|
|
5
|
+
// ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
|
|
6
|
+
// ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
|
|
7
|
+
// ┃ * Copyright (c) 2026, the Regular Layout Authors. This file is part * ┃
|
|
8
|
+
// ┃ * of the Regular Layout library, distributed under the terms of the * ┃
|
|
9
|
+
// ┃ * [Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0). * ┃
|
|
10
|
+
// ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
|
|
11
|
+
|
|
12
|
+
import { DEFAULT_PHYSICS, type Physics } from "./constants";
|
|
13
|
+
import { insert_child } from "./insert_child";
|
|
14
|
+
import type { Layout, LayoutPath, Orientation, ViewWindow } from "./types";
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Calculates an insertion point (which may involve splitting a single
|
|
18
|
+
* `"child-panel"` into a new `"split-panel"`), based on the cursor position.
|
|
19
|
+
* *
|
|
20
|
+
* @param col - The cursor column.
|
|
21
|
+
* @param row - The cursor row.
|
|
22
|
+
* @param panel - The `Layout` to insert into.
|
|
23
|
+
* @param slot - The slot identifier where the insert should occur
|
|
24
|
+
* @param drop_target - The `LayoutPath` (from `calculateIntersect`) of the
|
|
25
|
+
* panel to either insert next to, or split by.
|
|
26
|
+
* @returns A new `LayoutPath` reflecting the updated (maybe) `"split-panel"`,
|
|
27
|
+
* which is enough to draw the overlay.
|
|
28
|
+
*/
|
|
29
|
+
export function calculate_edge(
|
|
30
|
+
col: number,
|
|
31
|
+
row: number,
|
|
32
|
+
panel: Layout,
|
|
33
|
+
slot: string,
|
|
34
|
+
drop_target: LayoutPath,
|
|
35
|
+
box?: DOMRect,
|
|
36
|
+
physics: Physics = DEFAULT_PHYSICS,
|
|
37
|
+
): LayoutPath {
|
|
38
|
+
// Check root edges first
|
|
39
|
+
if (col < physics.SPLIT_ROOT_EDGE_TOLERANCE) {
|
|
40
|
+
return insert_root_edge(panel, slot, drop_target, [0], true, "horizontal");
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
if (col > 1 - physics.SPLIT_ROOT_EDGE_TOLERANCE) {
|
|
44
|
+
return insert_root_edge(
|
|
45
|
+
panel,
|
|
46
|
+
slot,
|
|
47
|
+
drop_target,
|
|
48
|
+
drop_target.path.length > 0 ? drop_target.path : [1],
|
|
49
|
+
false,
|
|
50
|
+
"horizontal",
|
|
51
|
+
);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
if (row < physics.SPLIT_ROOT_EDGE_TOLERANCE) {
|
|
55
|
+
return insert_root_edge(panel, slot, drop_target, [0], true, "vertical");
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
if (row > 1 - physics.SPLIT_ROOT_EDGE_TOLERANCE) {
|
|
59
|
+
return insert_root_edge(
|
|
60
|
+
panel,
|
|
61
|
+
slot,
|
|
62
|
+
drop_target,
|
|
63
|
+
drop_target.path.length > 0 ? drop_target.path : [1],
|
|
64
|
+
false,
|
|
65
|
+
"vertical",
|
|
66
|
+
);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// Check panel edges
|
|
70
|
+
const is_column_edge =
|
|
71
|
+
drop_target.column_offset < physics.SPLIT_EDGE_TOLERANCE ||
|
|
72
|
+
drop_target.column_offset > 1 - physics.SPLIT_EDGE_TOLERANCE;
|
|
73
|
+
|
|
74
|
+
const is_row_edge =
|
|
75
|
+
drop_target.row_offset < physics.SPLIT_EDGE_TOLERANCE ||
|
|
76
|
+
drop_target.row_offset > 1 - physics.SPLIT_EDGE_TOLERANCE;
|
|
77
|
+
|
|
78
|
+
// If both edges triggered, choose closer axis
|
|
79
|
+
if (is_column_edge && is_row_edge) {
|
|
80
|
+
const col_distance = Math.abs(drop_target.column_offset - 0.5);
|
|
81
|
+
const row_distance = Math.abs(drop_target.row_offset - 0.5);
|
|
82
|
+
const col_scale =
|
|
83
|
+
(box?.width || 1) *
|
|
84
|
+
(drop_target.view_window.col_end - drop_target.view_window.col_start);
|
|
85
|
+
|
|
86
|
+
const row_scale =
|
|
87
|
+
(box?.height || 1) *
|
|
88
|
+
(drop_target.view_window.row_end - drop_target.view_window.row_start);
|
|
89
|
+
|
|
90
|
+
const use_column = col_distance * col_scale > row_distance * row_scale;
|
|
91
|
+
|
|
92
|
+
return insert_axis(
|
|
93
|
+
panel,
|
|
94
|
+
slot,
|
|
95
|
+
drop_target,
|
|
96
|
+
use_column
|
|
97
|
+
? drop_target.column_offset < physics.SPLIT_EDGE_TOLERANCE
|
|
98
|
+
: drop_target.row_offset < physics.SPLIT_EDGE_TOLERANCE,
|
|
99
|
+
use_column ? "horizontal" : "vertical",
|
|
100
|
+
);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
if (is_column_edge) {
|
|
104
|
+
return insert_axis(
|
|
105
|
+
panel,
|
|
106
|
+
slot,
|
|
107
|
+
drop_target,
|
|
108
|
+
drop_target.column_offset < physics.SPLIT_EDGE_TOLERANCE,
|
|
109
|
+
"horizontal",
|
|
110
|
+
);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
if (is_row_edge) {
|
|
114
|
+
return insert_axis(
|
|
115
|
+
panel,
|
|
116
|
+
slot,
|
|
117
|
+
drop_target,
|
|
118
|
+
drop_target.row_offset < physics.SPLIT_EDGE_TOLERANCE,
|
|
119
|
+
"vertical",
|
|
120
|
+
);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// Not at an edge - insert as a tab
|
|
124
|
+
return {
|
|
125
|
+
...drop_target,
|
|
126
|
+
path: [...drop_target.path, 0],
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
function insert_root_edge(
|
|
131
|
+
panel: Layout,
|
|
132
|
+
slot: string,
|
|
133
|
+
drop_target: LayoutPath,
|
|
134
|
+
path: number[],
|
|
135
|
+
is_before: boolean,
|
|
136
|
+
orientation: Orientation,
|
|
137
|
+
): LayoutPath {
|
|
138
|
+
return insert_axis(
|
|
139
|
+
panel,
|
|
140
|
+
slot,
|
|
141
|
+
{ ...drop_target, path, orientation },
|
|
142
|
+
is_before,
|
|
143
|
+
orientation,
|
|
144
|
+
);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
function insert_axis(
|
|
148
|
+
panel: Layout,
|
|
149
|
+
slot: string,
|
|
150
|
+
drop_target: LayoutPath,
|
|
151
|
+
is_before: boolean,
|
|
152
|
+
axis_orientation: Orientation,
|
|
153
|
+
): LayoutPath {
|
|
154
|
+
let result_path: number[];
|
|
155
|
+
|
|
156
|
+
if (drop_target.orientation === axis_orientation) {
|
|
157
|
+
// Same orientation - insert into existing split
|
|
158
|
+
if (drop_target.path.length === 0) {
|
|
159
|
+
result_path = [is_before ? 0 : 1];
|
|
160
|
+
} else {
|
|
161
|
+
const last_index = drop_target.path[drop_target.path.length - 1];
|
|
162
|
+
result_path = [
|
|
163
|
+
...drop_target.path.slice(0, -1),
|
|
164
|
+
is_before ? last_index : last_index + 1,
|
|
165
|
+
];
|
|
166
|
+
}
|
|
167
|
+
} else {
|
|
168
|
+
// Different orientation - split the child panel
|
|
169
|
+
result_path = [...drop_target.path, is_before ? 0 : 1];
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
const new_panel = insert_child(panel, slot, result_path, axis_orientation);
|
|
173
|
+
const view_window = calculate_view_window(new_panel, result_path);
|
|
174
|
+
return {
|
|
175
|
+
...drop_target,
|
|
176
|
+
path: result_path,
|
|
177
|
+
slot: drop_target.slot,
|
|
178
|
+
is_edge: true,
|
|
179
|
+
orientation: axis_orientation,
|
|
180
|
+
view_window,
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
function calculate_view_window(panel: Layout, path: number[]): ViewWindow {
|
|
185
|
+
let view_window: ViewWindow = {
|
|
186
|
+
row_start: 0,
|
|
187
|
+
row_end: 1,
|
|
188
|
+
col_start: 0,
|
|
189
|
+
col_end: 1,
|
|
190
|
+
};
|
|
191
|
+
|
|
192
|
+
let current_panel = panel;
|
|
193
|
+
for (const step of path) {
|
|
194
|
+
if (current_panel.type === "child-panel") {
|
|
195
|
+
break;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
const index = Math.min(step, current_panel.children.length - 1);
|
|
199
|
+
const is_vertical = current_panel.orientation === "vertical";
|
|
200
|
+
const start_key = is_vertical ? "row_start" : "col_start";
|
|
201
|
+
const end_key = is_vertical ? "row_end" : "col_end";
|
|
202
|
+
const total_size = view_window[end_key] - view_window[start_key];
|
|
203
|
+
const offset = current_panel.sizes
|
|
204
|
+
.slice(0, index)
|
|
205
|
+
.reduce((sum, size) => sum + size * total_size, view_window[start_key]);
|
|
206
|
+
|
|
207
|
+
view_window = {
|
|
208
|
+
...view_window,
|
|
209
|
+
[start_key]: offset,
|
|
210
|
+
[end_key]: offset + total_size * current_panel.sizes[index],
|
|
211
|
+
};
|
|
212
|
+
|
|
213
|
+
current_panel = current_panel.children[index];
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
return view_window;
|
|
217
|
+
}
|
|
@@ -9,24 +9,24 @@
|
|
|
9
9
|
// ┃ * [Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0). * ┃
|
|
10
10
|
// ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
|
|
11
11
|
|
|
12
|
-
import type {
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
12
|
+
import type { LayoutPath, LayoutDivider, Layout, ViewWindow } from "./types.ts";
|
|
13
|
+
|
|
14
|
+
const VIEW_WINDOW = {
|
|
15
|
+
row_start: 0,
|
|
16
|
+
row_end: 1,
|
|
17
|
+
col_start: 0,
|
|
18
|
+
col_end: 1,
|
|
19
|
+
};
|
|
18
20
|
|
|
19
21
|
/**
|
|
20
22
|
* Determines which panel or divider is located at a given position in the
|
|
21
23
|
* layout.
|
|
22
24
|
*
|
|
23
|
-
* @param
|
|
24
|
-
* height
|
|
25
|
-
* @param column - Horizontal position as a fraction (0-1) of the
|
|
26
|
-
* container width
|
|
25
|
+
* @param column - Horizontal position as a fraction (0-1) of the container width
|
|
26
|
+
* @param row - Vertical position as a fraction (0-1) of the container height
|
|
27
27
|
* @param layout - The layout tree to search
|
|
28
28
|
* @param check_dividers - Whether `LayoutDivider` intersection should be
|
|
29
|
-
* checked, which
|
|
29
|
+
* checked, which you may not want for e.g. `drop` actions.
|
|
30
30
|
* @returns The panel path if over a panel, a divider if over a resizable
|
|
31
31
|
* boundary, or null if outside all panels
|
|
32
32
|
*/
|
|
@@ -34,44 +34,37 @@ export function calculate_intersection(
|
|
|
34
34
|
column: number,
|
|
35
35
|
row: number,
|
|
36
36
|
layout: Layout,
|
|
37
|
-
check_dividers
|
|
38
|
-
): LayoutPath;
|
|
37
|
+
check_dividers?: null,
|
|
38
|
+
): LayoutPath | null;
|
|
39
39
|
|
|
40
40
|
export function calculate_intersection(
|
|
41
41
|
column: number,
|
|
42
42
|
row: number,
|
|
43
43
|
layout: Layout,
|
|
44
|
-
check_dividers:
|
|
44
|
+
check_dividers?: { rect: DOMRect; size: number },
|
|
45
45
|
): LayoutPath | null | LayoutDivider;
|
|
46
46
|
|
|
47
47
|
export function calculate_intersection(
|
|
48
48
|
column: number,
|
|
49
49
|
row: number,
|
|
50
50
|
layout: Layout,
|
|
51
|
-
check_dividers?:
|
|
51
|
+
check_dividers?: { rect: DOMRect; size: number } | null,
|
|
52
52
|
): LayoutPath | null | LayoutDivider;
|
|
53
53
|
|
|
54
54
|
export function calculate_intersection(
|
|
55
55
|
column: number,
|
|
56
56
|
row: number,
|
|
57
57
|
layout: Layout,
|
|
58
|
-
check_dividers:
|
|
58
|
+
check_dividers: { rect: DOMRect; size: number } | null = null,
|
|
59
59
|
): LayoutPath | null | LayoutDivider {
|
|
60
60
|
return calculate_intersection_recursive(column, row, layout, check_dividers);
|
|
61
61
|
}
|
|
62
62
|
|
|
63
|
-
const VIEW_WINDOW = {
|
|
64
|
-
row_start: 0,
|
|
65
|
-
row_end: 1,
|
|
66
|
-
col_start: 0,
|
|
67
|
-
col_end: 1,
|
|
68
|
-
};
|
|
69
|
-
|
|
70
63
|
function calculate_intersection_recursive(
|
|
71
64
|
column: number,
|
|
72
65
|
row: number,
|
|
73
66
|
panel: Layout,
|
|
74
|
-
check_dividers:
|
|
67
|
+
check_dividers: { rect: DOMRect; size: number } | null,
|
|
75
68
|
parent_orientation: "horizontal" | "vertical" | null = null,
|
|
76
69
|
view_window: ViewWindow = structuredClone(VIEW_WINDOW),
|
|
77
70
|
path: number[] = [],
|
|
@@ -83,104 +76,71 @@ function calculate_intersection_recursive(
|
|
|
83
76
|
// Base case: if this is a child panel, return its name
|
|
84
77
|
if (panel.type === "child-panel") {
|
|
85
78
|
const selected = panel.selected ?? 0;
|
|
86
|
-
const
|
|
87
|
-
|
|
88
|
-
(view_window.col_end - view_window.col_start);
|
|
89
|
-
|
|
90
|
-
const row_offset =
|
|
91
|
-
(row - view_window.row_start) /
|
|
92
|
-
(view_window.row_end - view_window.row_start);
|
|
93
|
-
|
|
79
|
+
const col_width = view_window.col_end - view_window.col_start;
|
|
80
|
+
const row_height = view_window.row_end - view_window.row_start;
|
|
94
81
|
return {
|
|
95
82
|
type: "layout-path",
|
|
96
|
-
|
|
83
|
+
layout: undefined,
|
|
97
84
|
slot: panel.child[selected],
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
view_window: view_window,
|
|
85
|
+
path,
|
|
86
|
+
view_window,
|
|
101
87
|
is_edge: false,
|
|
102
88
|
column,
|
|
103
89
|
row,
|
|
104
|
-
column_offset,
|
|
105
|
-
row_offset,
|
|
90
|
+
column_offset: (column - view_window.col_start) / col_width,
|
|
91
|
+
row_offset: (row - view_window.row_start) / row_height,
|
|
106
92
|
orientation: parent_orientation || "horizontal",
|
|
107
93
|
};
|
|
108
94
|
}
|
|
109
95
|
|
|
110
96
|
// For split panels, determine which child was hit
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
97
|
+
const is_vertical = panel.orientation === "vertical";
|
|
98
|
+
const position = is_vertical ? row : column;
|
|
99
|
+
const start_key = is_vertical ? "row_start" : "col_start";
|
|
100
|
+
const end_key = is_vertical ? "row_end" : "col_end";
|
|
101
|
+
const rect_dim = is_vertical
|
|
102
|
+
? check_dividers?.rect?.height
|
|
103
|
+
: check_dividers?.rect?.width;
|
|
104
|
+
|
|
105
|
+
let current_pos = view_window[start_key];
|
|
106
|
+
const total_size = view_window[end_key] - view_window[start_key];
|
|
107
|
+
for (let i = 0; i < panel.children.length; i++) {
|
|
108
|
+
const next_pos = current_pos + total_size * panel.sizes[i];
|
|
109
|
+
|
|
110
|
+
// Check if position is on a divider
|
|
111
|
+
if (check_dividers && rect_dim) {
|
|
112
|
+
const divider_threshold = check_dividers.size / rect_dim;
|
|
113
|
+
if (Math.abs(position - next_pos) < divider_threshold) {
|
|
118
114
|
return {
|
|
119
115
|
path: [...path, i],
|
|
120
|
-
type:
|
|
116
|
+
type: panel.orientation,
|
|
121
117
|
view_window: {
|
|
122
118
|
...view_window,
|
|
123
|
-
|
|
124
|
-
|
|
119
|
+
[start_key]: current_pos,
|
|
120
|
+
[end_key]: next_pos,
|
|
125
121
|
},
|
|
126
122
|
};
|
|
127
123
|
}
|
|
128
|
-
|
|
129
|
-
if (row >= current_row && row < next_row) {
|
|
130
|
-
return calculate_intersection_recursive(
|
|
131
|
-
column,
|
|
132
|
-
row,
|
|
133
|
-
panel.children[i],
|
|
134
|
-
check_dividers,
|
|
135
|
-
"vertical",
|
|
136
|
-
{
|
|
137
|
-
...view_window,
|
|
138
|
-
row_start: current_row,
|
|
139
|
-
row_end: next_row,
|
|
140
|
-
},
|
|
141
|
-
[...path, i],
|
|
142
|
-
);
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
current_row = next_row;
|
|
146
124
|
}
|
|
147
|
-
} else {
|
|
148
|
-
// Horizontal orientation = columns
|
|
149
|
-
let current_col = view_window.col_start;
|
|
150
|
-
for (let i = 0; i < panel.children.length; i++) {
|
|
151
|
-
const total_size = view_window.col_end - view_window.col_start;
|
|
152
|
-
const next_col = current_col + total_size * panel.sizes[i];
|
|
153
|
-
if (check_dividers && Math.abs(column - next_col) < 0.01) {
|
|
154
|
-
return {
|
|
155
|
-
path: [...path, i],
|
|
156
|
-
type: "horizontal",
|
|
157
|
-
view_window: {
|
|
158
|
-
...view_window,
|
|
159
|
-
col_start: current_col,
|
|
160
|
-
col_end: next_col,
|
|
161
|
-
},
|
|
162
|
-
};
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
// Check if the column falls within this child's bounds.
|
|
166
|
-
if (column >= current_col && column < next_col) {
|
|
167
|
-
return calculate_intersection_recursive(
|
|
168
|
-
column,
|
|
169
|
-
row,
|
|
170
|
-
panel.children[i],
|
|
171
|
-
check_dividers,
|
|
172
|
-
"horizontal",
|
|
173
|
-
{
|
|
174
|
-
...view_window,
|
|
175
|
-
col_start: current_col,
|
|
176
|
-
col_end: next_col,
|
|
177
|
-
},
|
|
178
|
-
[...path, i],
|
|
179
|
-
);
|
|
180
|
-
}
|
|
181
125
|
|
|
182
|
-
|
|
126
|
+
// Check if position falls within this child's bounds
|
|
127
|
+
if (position >= current_pos && position < next_pos) {
|
|
128
|
+
return calculate_intersection_recursive(
|
|
129
|
+
column,
|
|
130
|
+
row,
|
|
131
|
+
panel.children[i],
|
|
132
|
+
check_dividers,
|
|
133
|
+
panel.orientation,
|
|
134
|
+
{
|
|
135
|
+
...view_window,
|
|
136
|
+
[start_key]: current_pos,
|
|
137
|
+
[end_key]: next_pos,
|
|
138
|
+
},
|
|
139
|
+
[...path, i],
|
|
140
|
+
);
|
|
183
141
|
}
|
|
142
|
+
|
|
143
|
+
current_pos = next_pos;
|
|
184
144
|
}
|
|
185
145
|
|
|
186
146
|
// If we get here, the hit was outside all children (possibly in a gap or
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
// ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
|
|
2
|
+
// ░░░░░░░░▄▀░█▀▄░█▀▀░█▀▀░█░█░█░░░█▀█░█▀▄░░░░░█░░░█▀█░█░█░█▀█░█░█░▀█▀░▀▄░░░░░░░░
|
|
3
|
+
// ░░░░░░░▀▄░░█▀▄░█▀▀░█░█░█░█░█░░░█▀█░█▀▄░▀▀▀░█░░░█▀█░░█░░█░█░█░█░░█░░░▄▀░░░░░░░
|
|
4
|
+
// ░░░░░░░░░▀░▀░▀░▀▀▀░▀▀▀░▀▀▀░▀▀▀░▀░▀░▀░▀░░░░░▀▀▀░▀░▀░░▀░░▀▀▀░▀▀▀░░▀░░▀░░░░░░░░░
|
|
5
|
+
// ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
|
|
6
|
+
// ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
|
|
7
|
+
// ┃ * Copyright (c) 2026, the Regular Layout Authors. This file is part * ┃
|
|
8
|
+
// ┃ * of the Regular Layout library, distributed under the terms of the * ┃
|
|
9
|
+
// ┃ * [Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0). * ┃
|
|
10
|
+
// ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
|
|
11
|
+
|
|
12
|
+
import type { OverlayMode } from "./types";
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Instance-specific constants which define the behavior and rendering details
|
|
16
|
+
* of a `<regular-layout>`.
|
|
17
|
+
*/
|
|
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;
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* The attribute name to use for matching child `Element`s to grid
|
|
27
|
+
* positions.
|
|
28
|
+
*/
|
|
29
|
+
CHILD_ATTRIBUTE_NAME: string;
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* The minimum number of pixels the mouse must move to be considered a drag.
|
|
33
|
+
*/
|
|
34
|
+
MIN_DRAG_DISTANCE: number;
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Should floating point pixel calculations be rounded. Useful for testing.
|
|
38
|
+
*/
|
|
39
|
+
SHOULD_ROUND: boolean;
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Class name to use for child elements in overlay position (dragging).
|
|
43
|
+
*/
|
|
44
|
+
OVERLAY_CLASSNAME: string;
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* The percentage of the maximum resize distance that will be clamped.
|
|
48
|
+
*/
|
|
49
|
+
MINIMUM_REDISTRIBUTION_SIZE_THRESHOLD: number;
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Threshold from panel edge that is considered a split vs drop action.
|
|
53
|
+
*/
|
|
54
|
+
SPLIT_EDGE_TOLERANCE: number;
|
|
55
|
+
|
|
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
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Like `GlobalPhysics`, but suitable for partial definition for incremental
|
|
89
|
+
* updates.
|
|
90
|
+
*/
|
|
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 type { Layout } from "./
|
|
12
|
+
import type { Layout } from "./types.ts";
|
|
13
13
|
|
|
14
14
|
/**
|
|
15
15
|
* Flattens the layout tree by merging parent and child split panels that have
|