regular-layout 0.2.1 → 0.3.0
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 +4 -4
- package/dist/layout/calculate_edge.d.ts +2 -2
- package/dist/layout/calculate_path.d.ts +12 -0
- package/dist/layout/generate_grid.d.ts +3 -3
- package/dist/layout/generate_overlay.d.ts +1 -1
- package/dist/layout/insert_child.d.ts +2 -2
- package/dist/layout/redistribute_panel_sizes.d.ts +2 -2
- package/dist/layout/types.d.ts +11 -11
- package/dist/regular-layout-frame.d.ts +10 -3
- package/dist/regular-layout.d.ts +58 -14
- package/package.json +4 -2
- package/src/layout/calculate_edge.ts +19 -8
- package/src/layout/calculate_intersect.ts +11 -5
- package/src/layout/calculate_path.ts +53 -0
- package/src/layout/flatten.ts +3 -3
- package/src/layout/generate_grid.ts +8 -8
- package/src/layout/generate_overlay.ts +1 -1
- package/src/layout/insert_child.ts +21 -21
- package/src/layout/redistribute_panel_sizes.ts +20 -16
- package/src/layout/remove_child.ts +11 -11
- package/src/layout/types.ts +13 -13
- package/src/regular-layout-frame.ts +53 -68
- package/src/regular-layout-tab.ts +5 -5
- package/src/regular-layout.ts +146 -105
|
@@ -2,7 +2,7 @@ import { type Physics } from "./constants";
|
|
|
2
2
|
import type { Layout, LayoutPath } from "./types";
|
|
3
3
|
/**
|
|
4
4
|
* Calculates an insertion point (which may involve splitting a single
|
|
5
|
-
* `"
|
|
5
|
+
* `"tab-layout"` into a new `"split-layout"`), based on the cursor position.
|
|
6
6
|
* *
|
|
7
7
|
* @param col - The cursor column.
|
|
8
8
|
* @param row - The cursor row.
|
|
@@ -10,7 +10,7 @@ import type { Layout, LayoutPath } from "./types";
|
|
|
10
10
|
* @param slot - The slot identifier where the insert should occur
|
|
11
11
|
* @param drop_target - The `LayoutPath` (from `calculateIntersect`) of the
|
|
12
12
|
* panel to either insert next to, or split by.
|
|
13
|
-
* @returns A new `LayoutPath` reflecting the updated (maybe) `"split-
|
|
13
|
+
* @returns A new `LayoutPath` reflecting the updated (maybe) `"split-layout"`,
|
|
14
14
|
* which is enough to draw the overlay.
|
|
15
15
|
*/
|
|
16
16
|
export declare function calculate_edge(col: number, row: number, panel: Layout, slot: string, drop_target: LayoutPath, box?: DOMRect, physics?: Physics): LayoutPath;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { Layout } from "./types.ts";
|
|
2
|
+
/**
|
|
3
|
+
* Calculates the index path for a panel with the given name.
|
|
4
|
+
*
|
|
5
|
+
* Traverses the layout tree to find the named panel and returns the
|
|
6
|
+
* index path describing its position in the tree.
|
|
7
|
+
*
|
|
8
|
+
* @param name - The name of the panel to find.
|
|
9
|
+
* @param layout - The layout tree to search.
|
|
10
|
+
* @returns The panel's index path if found, `null` otherwise.
|
|
11
|
+
*/
|
|
12
|
+
export declare function calculate_path(name: string, layout: Layout): number[] | null;
|
|
@@ -14,11 +14,11 @@ import type { Layout } from "./types.ts";
|
|
|
14
14
|
* @example
|
|
15
15
|
* ```typescript
|
|
16
16
|
* const layout = {
|
|
17
|
-
* type: "split-
|
|
17
|
+
* type: "split-layout",
|
|
18
18
|
* orientation: "horizontal",
|
|
19
19
|
* children: [
|
|
20
|
-
* { type: "
|
|
21
|
-
* { type: "
|
|
20
|
+
* { type: "tab-layout", tabs: "sidebar" },
|
|
21
|
+
* { type: "tab-layout", tabs: "main" }
|
|
22
22
|
* ],
|
|
23
23
|
* sizes: [0.25, 0.75]
|
|
24
24
|
* };
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
import type { LayoutPath } from "./types";
|
|
2
|
-
export declare function updateOverlaySheet(slot: string, box: DOMRect, style: CSSStyleDeclaration, drag_target: LayoutPath
|
|
2
|
+
export declare function updateOverlaySheet(slot: string, box: DOMRect, style: CSSStyleDeclaration, drag_target: LayoutPath | null, physics?: import("./constants").Physics): string;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { Layout } from "./types.ts";
|
|
1
|
+
import type { Layout, LayoutPathTraversal } from "./types.ts";
|
|
2
2
|
/**
|
|
3
3
|
* Inserts a new child panel into the layout tree at a specified location.
|
|
4
4
|
* Creates a split panel if necessary and redistributes sizes equally among all
|
|
@@ -13,4 +13,4 @@ import type { Layout } from "./types.ts";
|
|
|
13
13
|
* @param is_edge - If true, create the split at the parent level.
|
|
14
14
|
* @returns A new layout tree with the child inserted (original is not mutated).
|
|
15
15
|
*/
|
|
16
|
-
export declare function insert_child(panel: Layout, child: string, path:
|
|
16
|
+
export declare function insert_child(panel: Layout, child: string, path: LayoutPathTraversal, orientation?: "horizontal" | "vertical"): Layout;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { Layout } from "./types.ts";
|
|
1
|
+
import type { Layout, LayoutPathTraversal } from "./types.ts";
|
|
2
2
|
/**
|
|
3
3
|
* Adjusts panel sizes during a drag operation on a divider.
|
|
4
4
|
*
|
|
@@ -16,4 +16,4 @@ import type { Layout } from "./types.ts";
|
|
|
16
16
|
* @returns A new layout tree with updated sizes (original is not mutated).
|
|
17
17
|
* ```
|
|
18
18
|
*/
|
|
19
|
-
export declare function redistribute_panel_sizes(panel: Layout, path:
|
|
19
|
+
export declare function redistribute_panel_sizes(panel: Layout, path: LayoutPathTraversal, delta: number | undefined, physics?: import("./constants.ts").Physics): Layout;
|
package/dist/layout/types.d.ts
CHANGED
|
@@ -32,7 +32,7 @@ export interface ViewWindow {
|
|
|
32
32
|
* opposite `orientation` as its parent.
|
|
33
33
|
*/
|
|
34
34
|
export interface SplitLayout {
|
|
35
|
-
type: "split-
|
|
35
|
+
type: "split-layout";
|
|
36
36
|
children: Layout[];
|
|
37
37
|
sizes: number[];
|
|
38
38
|
orientation: Orientation;
|
|
@@ -41,31 +41,31 @@ export interface SplitLayout {
|
|
|
41
41
|
* A leaf panel node that contains a single named child element.
|
|
42
42
|
*/
|
|
43
43
|
export interface TabLayout {
|
|
44
|
-
type: "
|
|
45
|
-
|
|
44
|
+
type: "tab-layout";
|
|
45
|
+
tabs: string[];
|
|
46
46
|
selected?: number;
|
|
47
47
|
}
|
|
48
|
+
/**
|
|
49
|
+
* Represents a panel location relative to a `Layout` struct.
|
|
50
|
+
*/
|
|
51
|
+
export type LayoutPathTraversal = number[];
|
|
48
52
|
/**
|
|
49
53
|
* Represents a draggable divider between two panels in the layout.
|
|
50
54
|
*
|
|
51
55
|
* Used for hit detection.
|
|
52
56
|
*/
|
|
53
57
|
export interface LayoutDivider {
|
|
54
|
-
path:
|
|
58
|
+
path: LayoutPathTraversal;
|
|
55
59
|
view_window: ViewWindow;
|
|
56
60
|
type: Orientation;
|
|
57
61
|
}
|
|
58
62
|
/**
|
|
59
63
|
* Represents a panel location result from hit detection.
|
|
60
|
-
*
|
|
61
|
-
* Contains both the panel identifier and its grid position in relative units.
|
|
62
|
-
* The generic parameter `T` allows DOM-only properties (e.g. `DOMRect`) to be
|
|
63
|
-
* shared in this cross-platform module.
|
|
64
64
|
*/
|
|
65
|
-
export interface LayoutPath
|
|
65
|
+
export interface LayoutPath {
|
|
66
66
|
type: "layout-path";
|
|
67
67
|
slot: string;
|
|
68
|
-
path:
|
|
68
|
+
path: LayoutPathTraversal;
|
|
69
69
|
view_window: ViewWindow;
|
|
70
70
|
column: number;
|
|
71
71
|
row: number;
|
|
@@ -73,7 +73,7 @@ export interface LayoutPath<T = undefined> {
|
|
|
73
73
|
row_offset: number;
|
|
74
74
|
orientation: Orientation;
|
|
75
75
|
is_edge: boolean;
|
|
76
|
-
layout:
|
|
76
|
+
layout: Layout;
|
|
77
77
|
}
|
|
78
78
|
/**
|
|
79
79
|
* An empty `Layout` with no panels.
|
|
@@ -28,15 +28,22 @@ export declare class RegularLayoutFrame extends HTMLElement {
|
|
|
28
28
|
private _container_sheet;
|
|
29
29
|
private _layout;
|
|
30
30
|
private _header;
|
|
31
|
-
private
|
|
32
|
-
private _drag_moved;
|
|
31
|
+
private _drag;
|
|
33
32
|
private _tab_to_index_map;
|
|
34
|
-
|
|
33
|
+
/**
|
|
34
|
+
* Initializes this elements. Override this method and
|
|
35
|
+
* `disconnectedCallback` to modify how this subclass renders the Shadow
|
|
36
|
+
* DOM and registers events.
|
|
37
|
+
*/
|
|
35
38
|
connectedCallback(): void;
|
|
39
|
+
/**
|
|
40
|
+
* Destroys this element.
|
|
41
|
+
*/
|
|
36
42
|
disconnectedCallback(): void;
|
|
37
43
|
private onPointerDown;
|
|
38
44
|
private onPointerMove;
|
|
39
45
|
private onPointerUp;
|
|
46
|
+
private onPointerCancel;
|
|
40
47
|
private onPointerLost;
|
|
41
48
|
private drawTabs;
|
|
42
49
|
}
|
package/dist/regular-layout.d.ts
CHANGED
|
@@ -1,5 +1,14 @@
|
|
|
1
|
-
import type { LayoutPath, Layout, TabLayout, OverlayMode, Orientation } from "./layout/types.ts";
|
|
1
|
+
import type { LayoutPath, Layout, TabLayout, OverlayMode, Orientation, LayoutPathTraversal } from "./layout/types.ts";
|
|
2
2
|
import { type PhysicsUpdate, type Physics } from "./layout/constants.ts";
|
|
3
|
+
/**
|
|
4
|
+
* An interface which models the fields of `PointerEvent` that
|
|
5
|
+
* `<regular-layour>` actually uses, making it easier to sub out with an
|
|
6
|
+
* JavaScript object lieral when you don't have a `PointerEvent` handy.
|
|
7
|
+
*/
|
|
8
|
+
export interface PointerEventCoordinates {
|
|
9
|
+
clientX: number;
|
|
10
|
+
clientY: number;
|
|
11
|
+
}
|
|
3
12
|
/**
|
|
4
13
|
* A Web Component that provides a resizable panel layout system.
|
|
5
14
|
* Panels are arranged using CSS Grid and can be resized by dragging dividers.
|
|
@@ -32,6 +41,22 @@ import { type PhysicsUpdate, type Physics } from "./layout/constants.ts";
|
|
|
32
41
|
* layout.restore(state);
|
|
33
42
|
* ```
|
|
34
43
|
*
|
|
44
|
+
* @remarks
|
|
45
|
+
*
|
|
46
|
+
* Why does this implementation use a `<slot>` at all? We must use
|
|
47
|
+
* `<slot>` and the Shadow DOM to scope the grid CSS rules to each
|
|
48
|
+
* instance of `<regular-layout>` (without e.g. giving them unique
|
|
49
|
+
* `"id"` and injecting into `document,head`), and we can only select
|
|
50
|
+
* `::slotted` light DOM children from `adoptedStyleSheets` on the
|
|
51
|
+
* `ShadowRoot`.
|
|
52
|
+
*
|
|
53
|
+
* Why does this implementation use a single `<slot>` and the child
|
|
54
|
+
* `"name"` attribute, as opposed to a named `<slot name="my_slot">`
|
|
55
|
+
* and the built-in `"slot"` child attribute? Children with a `"slot"`
|
|
56
|
+
* attribute don't fallback to the un-named `<slot>`, so using the
|
|
57
|
+
* latter implementation would require synchronizing the light DOM
|
|
58
|
+
* and shadow DOM slots/slotted children continuously.
|
|
59
|
+
*
|
|
35
60
|
*/
|
|
36
61
|
export declare class RegularLayout extends HTMLElement {
|
|
37
62
|
private _shadowRoot;
|
|
@@ -48,17 +73,24 @@ export declare class RegularLayout extends HTMLElement {
|
|
|
48
73
|
/**
|
|
49
74
|
* Determines which panel is at a given screen coordinate.
|
|
50
75
|
*
|
|
51
|
-
* @param
|
|
52
|
-
*
|
|
76
|
+
* @param coordinates - `PointerEvent`, `MouseEvent`, or just X and Y
|
|
77
|
+
* coordinates in screen pixels.
|
|
53
78
|
* @returns Panel information if a panel is at that position, null otherwise.
|
|
54
79
|
*/
|
|
55
|
-
calculateIntersect: (
|
|
80
|
+
calculateIntersect: (coordinates: PointerEventCoordinates) => LayoutPath | null;
|
|
81
|
+
/**
|
|
82
|
+
* Calculates the index path for a panel with the given name.
|
|
83
|
+
*
|
|
84
|
+
* @param name - The name of the panel to find.
|
|
85
|
+
* @returns The panel's index path if found, `null` otherwise.
|
|
86
|
+
*/
|
|
87
|
+
calculatePath: (name: string) => number[] | null;
|
|
56
88
|
/**
|
|
57
89
|
* Sets the visual overlay state during drag-and-drop operations.
|
|
58
90
|
* Displays a preview of where a panel would be placed at the given coordinates.
|
|
59
91
|
*
|
|
60
|
-
* @param
|
|
61
|
-
*
|
|
92
|
+
* @param event - `PointerEvent`, `MouseEvent`, or just X and Y
|
|
93
|
+
* coordinates in screen pixels.
|
|
62
94
|
* @param dragTarget - A `LayoutPath` (presumably from `calculateIntersect`)
|
|
63
95
|
* which points to the drag element in the current layout.
|
|
64
96
|
* @param className - The CSS class name to use for the overlay panel
|
|
@@ -67,12 +99,12 @@ export declare class RegularLayout extends HTMLElement {
|
|
|
67
99
|
* the target, "absolute" positions the panel absolutely. Defaults to
|
|
68
100
|
* "absolute".
|
|
69
101
|
*/
|
|
70
|
-
setOverlayState: (
|
|
102
|
+
setOverlayState: (event: PointerEventCoordinates, { slot }: LayoutPath, className?: string, mode?: OverlayMode) => void;
|
|
71
103
|
/**
|
|
72
104
|
* Clears the overlay state and commits the panel placement.
|
|
73
105
|
*
|
|
74
|
-
* @param
|
|
75
|
-
*
|
|
106
|
+
* @param event - `PointerEvent`, `MouseEvent`, or just X and Y
|
|
107
|
+
* coordinates in screen pixels.
|
|
76
108
|
* @param dragTarget - A `LayoutPath` (presumably from `calculateIntersect`)
|
|
77
109
|
* which points to the drag element in the current layout.
|
|
78
110
|
* @param className - The CSS class name to use for the overlay panel
|
|
@@ -80,7 +112,7 @@ export declare class RegularLayout extends HTMLElement {
|
|
|
80
112
|
* @param mode - Overlay rendering mode that was used, must match the mode
|
|
81
113
|
* passed to `setOverlayState`. Defaults to "absolute".
|
|
82
114
|
*/
|
|
83
|
-
clearOverlayState: (
|
|
115
|
+
clearOverlayState: (event: PointerEventCoordinates | null, { slot, layout }: LayoutPath, className?: string) => void;
|
|
84
116
|
/**
|
|
85
117
|
* Inserts a new panel into the layout at a specified path.
|
|
86
118
|
*
|
|
@@ -92,7 +124,7 @@ export declare class RegularLayout extends HTMLElement {
|
|
|
92
124
|
* the new `SplitPanel` _if_ there is an option of orientation (e.g. if
|
|
93
125
|
* the layout had no pre-existing `SplitPanel`)
|
|
94
126
|
*/
|
|
95
|
-
insertPanel: (name: string, path?:
|
|
127
|
+
insertPanel: (name: string, path?: LayoutPathTraversal, split?: boolean | Orientation) => void;
|
|
96
128
|
/**
|
|
97
129
|
* Removes a panel from the layout by name.
|
|
98
130
|
*
|
|
@@ -155,14 +187,26 @@ export declare class RegularLayout extends HTMLElement {
|
|
|
155
187
|
* Transforms absolute pixel positions into normalized coordinates (0-1 range)
|
|
156
188
|
* relative to the layout's bounding box.
|
|
157
189
|
*
|
|
158
|
-
* @param
|
|
159
|
-
*
|
|
190
|
+
* @param coordinates - `PointerEvent`, `MouseEvent`, or just X and Y
|
|
191
|
+
* coordinates in screen pixels.
|
|
160
192
|
* @returns A tuple containing:
|
|
161
193
|
* - col: Normalized X coordinate (0 = left edge, 1 = right edge)
|
|
162
194
|
* - row: Normalized Y coordinate (0 = top edge, 1 = bottom edge)
|
|
163
195
|
* - box: The layout element's bounding rectangle
|
|
164
196
|
*/
|
|
165
|
-
relativeCoordinates: (
|
|
197
|
+
relativeCoordinates: (event: PointerEventCoordinates, recalculate_bounds?: boolean) => [number, number, DOMRect, CSSStyleDeclaration];
|
|
198
|
+
/**
|
|
199
|
+
* Calculates the Euclidean distance in pixels between the current pointer
|
|
200
|
+
* coordinates and a drag target's position within the layout.
|
|
201
|
+
*
|
|
202
|
+
* @param coordinates - The current pointer event coordinates.
|
|
203
|
+
* @param drag_target - The layout path representing the drag target
|
|
204
|
+
* position.
|
|
205
|
+
* @returns The distance in pixels between the coordinates and the drag
|
|
206
|
+
* target.
|
|
207
|
+
*/
|
|
208
|
+
diffCoordinates: (event: PointerEventCoordinates, drag_target: LayoutPath) => number;
|
|
209
|
+
private onDblClick;
|
|
166
210
|
private onPointerDown;
|
|
167
211
|
private onPointerMove;
|
|
168
212
|
private onPointerUp;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "regular-layout",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "A regular CSS `grid` container",
|
|
5
5
|
"keywords": [],
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
},
|
|
11
11
|
"browser": "dist/index.js",
|
|
12
12
|
"main": "dist/index.js",
|
|
13
|
+
"types": "dist/index.d.ts",
|
|
13
14
|
"type": "module",
|
|
14
15
|
"files": [
|
|
15
16
|
"dist/**/*",
|
|
@@ -20,7 +21,8 @@
|
|
|
20
21
|
"build": "tsx build.ts",
|
|
21
22
|
"build:watch": "tsx build.ts --watch",
|
|
22
23
|
"clean": "rm -rf dist",
|
|
23
|
-
"test": "playwright test",
|
|
24
|
+
"test": "playwright test tests/integration tests/unit",
|
|
25
|
+
"test:perf": "playwright test --workers=1 ./benchmarks",
|
|
24
26
|
"example": "tsx serve.ts",
|
|
25
27
|
"deploy": "tsx deploy.ts",
|
|
26
28
|
"lint": "biome lint src tests",
|
|
@@ -11,11 +11,17 @@
|
|
|
11
11
|
|
|
12
12
|
import { DEFAULT_PHYSICS, type Physics } from "./constants";
|
|
13
13
|
import { insert_child } from "./insert_child";
|
|
14
|
-
import type {
|
|
14
|
+
import type {
|
|
15
|
+
Layout,
|
|
16
|
+
LayoutPath,
|
|
17
|
+
LayoutPathTraversal,
|
|
18
|
+
Orientation,
|
|
19
|
+
ViewWindow,
|
|
20
|
+
} from "./types";
|
|
15
21
|
|
|
16
22
|
/**
|
|
17
23
|
* Calculates an insertion point (which may involve splitting a single
|
|
18
|
-
* `"
|
|
24
|
+
* `"tab-layout"` into a new `"split-layout"`), based on the cursor position.
|
|
19
25
|
* *
|
|
20
26
|
* @param col - The cursor column.
|
|
21
27
|
* @param row - The cursor row.
|
|
@@ -23,7 +29,7 @@ import type { Layout, LayoutPath, Orientation, ViewWindow } from "./types";
|
|
|
23
29
|
* @param slot - The slot identifier where the insert should occur
|
|
24
30
|
* @param drop_target - The `LayoutPath` (from `calculateIntersect`) of the
|
|
25
31
|
* panel to either insert next to, or split by.
|
|
26
|
-
* @returns A new `LayoutPath` reflecting the updated (maybe) `"split-
|
|
32
|
+
* @returns A new `LayoutPath` reflecting the updated (maybe) `"split-layout"`,
|
|
27
33
|
* which is enough to draw the overlay.
|
|
28
34
|
*/
|
|
29
35
|
export function calculate_edge(
|
|
@@ -87,7 +93,9 @@ export function calculate_edge(
|
|
|
87
93
|
(box?.height || 1) *
|
|
88
94
|
(drop_target.view_window.row_end - drop_target.view_window.row_start);
|
|
89
95
|
|
|
90
|
-
const use_column =
|
|
96
|
+
const use_column =
|
|
97
|
+
col_scale / 2 - col_distance * col_scale <
|
|
98
|
+
row_scale / 2 - row_distance * row_scale;
|
|
91
99
|
|
|
92
100
|
return insert_axis(
|
|
93
101
|
panel,
|
|
@@ -131,7 +139,7 @@ function insert_root_edge(
|
|
|
131
139
|
panel: Layout,
|
|
132
140
|
slot: string,
|
|
133
141
|
drop_target: LayoutPath,
|
|
134
|
-
path:
|
|
142
|
+
path: LayoutPathTraversal,
|
|
135
143
|
is_before: boolean,
|
|
136
144
|
orientation: Orientation,
|
|
137
145
|
): LayoutPath {
|
|
@@ -151,7 +159,7 @@ function insert_axis(
|
|
|
151
159
|
is_before: boolean,
|
|
152
160
|
axis_orientation: Orientation,
|
|
153
161
|
): LayoutPath {
|
|
154
|
-
let result_path:
|
|
162
|
+
let result_path: LayoutPathTraversal;
|
|
155
163
|
|
|
156
164
|
if (drop_target.orientation === axis_orientation) {
|
|
157
165
|
// Same orientation - insert into existing split
|
|
@@ -181,7 +189,10 @@ function insert_axis(
|
|
|
181
189
|
};
|
|
182
190
|
}
|
|
183
191
|
|
|
184
|
-
function calculate_view_window(
|
|
192
|
+
function calculate_view_window(
|
|
193
|
+
panel: Layout,
|
|
194
|
+
path: LayoutPathTraversal,
|
|
195
|
+
): ViewWindow {
|
|
185
196
|
let view_window: ViewWindow = {
|
|
186
197
|
row_start: 0,
|
|
187
198
|
row_end: 1,
|
|
@@ -191,7 +202,7 @@ function calculate_view_window(panel: Layout, path: number[]): ViewWindow {
|
|
|
191
202
|
|
|
192
203
|
let current_panel = panel;
|
|
193
204
|
for (const step of path) {
|
|
194
|
-
if (current_panel.type === "
|
|
205
|
+
if (current_panel.type === "tab-layout") {
|
|
195
206
|
break;
|
|
196
207
|
}
|
|
197
208
|
|
|
@@ -9,7 +9,13 @@
|
|
|
9
9
|
// ┃ * [Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0). * ┃
|
|
10
10
|
// ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
|
|
11
11
|
|
|
12
|
-
import type {
|
|
12
|
+
import type {
|
|
13
|
+
LayoutPath,
|
|
14
|
+
LayoutDivider,
|
|
15
|
+
Layout,
|
|
16
|
+
ViewWindow,
|
|
17
|
+
LayoutPathTraversal,
|
|
18
|
+
} from "./types.ts";
|
|
13
19
|
|
|
14
20
|
const VIEW_WINDOW = {
|
|
15
21
|
row_start: 0,
|
|
@@ -67,21 +73,21 @@ function calculate_intersection_recursive(
|
|
|
67
73
|
check_dividers: { rect: DOMRect; size: number } | null,
|
|
68
74
|
parent_orientation: "horizontal" | "vertical" | null = null,
|
|
69
75
|
view_window: ViewWindow = structuredClone(VIEW_WINDOW),
|
|
70
|
-
path:
|
|
76
|
+
path: LayoutPathTraversal = [],
|
|
71
77
|
): LayoutPath | null | LayoutDivider {
|
|
72
78
|
if (column < 0 || row < 0 || column > 1 || row > 1) {
|
|
73
79
|
return null;
|
|
74
80
|
}
|
|
75
81
|
|
|
76
82
|
// Base case: if this is a child panel, return its name
|
|
77
|
-
if (panel.type === "
|
|
83
|
+
if (panel.type === "tab-layout") {
|
|
78
84
|
const selected = panel.selected ?? 0;
|
|
79
85
|
const col_width = view_window.col_end - view_window.col_start;
|
|
80
86
|
const row_height = view_window.row_end - view_window.row_start;
|
|
81
87
|
return {
|
|
82
88
|
type: "layout-path",
|
|
83
|
-
layout:
|
|
84
|
-
slot: panel.
|
|
89
|
+
layout: panel,
|
|
90
|
+
slot: panel.tabs[selected],
|
|
85
91
|
path,
|
|
86
92
|
view_window,
|
|
87
93
|
is_edge: false,
|
|
@@ -0,0 +1,53 @@
|
|
|
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 { Layout, LayoutPathTraversal } from "./types.ts";
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Calculates the index path for a panel with the given name.
|
|
16
|
+
*
|
|
17
|
+
* Traverses the layout tree to find the named panel and returns the
|
|
18
|
+
* index path describing its position in the tree.
|
|
19
|
+
*
|
|
20
|
+
* @param name - The name of the panel to find.
|
|
21
|
+
* @param layout - The layout tree to search.
|
|
22
|
+
* @returns The panel's index path if found, `null` otherwise.
|
|
23
|
+
*/
|
|
24
|
+
export function calculate_path(name: string, layout: Layout): number[] | null {
|
|
25
|
+
return calculate_path_recursive(name, layout, []);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function calculate_path_recursive(
|
|
29
|
+
name: string,
|
|
30
|
+
panel: Layout,
|
|
31
|
+
path: LayoutPathTraversal,
|
|
32
|
+
): number[] | null {
|
|
33
|
+
if (panel.type === "tab-layout") {
|
|
34
|
+
if (!panel.tabs.includes(name)) {
|
|
35
|
+
return null;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
return path;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
for (let i = 0; i < panel.children.length; i++) {
|
|
42
|
+
const result = calculate_path_recursive(name, panel.children[i], [
|
|
43
|
+
...path,
|
|
44
|
+
i,
|
|
45
|
+
]);
|
|
46
|
+
|
|
47
|
+
if (result) {
|
|
48
|
+
return result;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
return null;
|
|
53
|
+
}
|
package/src/layout/flatten.ts
CHANGED
|
@@ -23,7 +23,7 @@ import type { Layout } from "./types.ts";
|
|
|
23
23
|
* @returns A new flattened layout tree (original is not mutated).
|
|
24
24
|
*/
|
|
25
25
|
export function flatten(layout: Layout): Layout {
|
|
26
|
-
if (layout.type === "
|
|
26
|
+
if (layout.type === "tab-layout") {
|
|
27
27
|
layout.selected = layout.selected || 0;
|
|
28
28
|
return layout;
|
|
29
29
|
}
|
|
@@ -36,7 +36,7 @@ export function flatten(layout: Layout): Layout {
|
|
|
36
36
|
const flattenedChild = flatten(child);
|
|
37
37
|
|
|
38
38
|
if (
|
|
39
|
-
flattenedChild.type === "split-
|
|
39
|
+
flattenedChild.type === "split-layout" &&
|
|
40
40
|
flattenedChild.orientation === layout.orientation
|
|
41
41
|
) {
|
|
42
42
|
for (let j = 0; j < flattenedChild.children.length; j++) {
|
|
@@ -54,7 +54,7 @@ export function flatten(layout: Layout): Layout {
|
|
|
54
54
|
}
|
|
55
55
|
|
|
56
56
|
return {
|
|
57
|
-
type: "split-
|
|
57
|
+
type: "split-layout",
|
|
58
58
|
orientation: layout.orientation,
|
|
59
59
|
children: flattenedChildren,
|
|
60
60
|
sizes: flattenedSizes,
|
|
@@ -44,7 +44,7 @@ function collect_track_positions(
|
|
|
44
44
|
end: number,
|
|
45
45
|
physics: Physics,
|
|
46
46
|
): number[] {
|
|
47
|
-
if (panel.type === "
|
|
47
|
+
if (panel.type === "tab-layout") {
|
|
48
48
|
return [start, end];
|
|
49
49
|
}
|
|
50
50
|
|
|
@@ -100,11 +100,11 @@ function build_cells(
|
|
|
100
100
|
rowEnd: number,
|
|
101
101
|
physics: Physics,
|
|
102
102
|
): GridCell[] {
|
|
103
|
-
if (panel.type === "
|
|
103
|
+
if (panel.type === "tab-layout") {
|
|
104
104
|
const selected = panel.selected ?? 0;
|
|
105
105
|
return [
|
|
106
106
|
{
|
|
107
|
-
child: panel.
|
|
107
|
+
child: panel.tabs[selected],
|
|
108
108
|
colStart: find_track_index(physics, colPositions, colStart),
|
|
109
109
|
colEnd: find_track_index(physics, colPositions, colEnd),
|
|
110
110
|
rowStart: find_track_index(physics, rowPositions, rowStart),
|
|
@@ -179,11 +179,11 @@ const child_template = (
|
|
|
179
179
|
* @example
|
|
180
180
|
* ```typescript
|
|
181
181
|
* const layout = {
|
|
182
|
-
* type: "split-
|
|
182
|
+
* type: "split-layout",
|
|
183
183
|
* orientation: "horizontal",
|
|
184
184
|
* children: [
|
|
185
|
-
* { type: "
|
|
186
|
-
* { type: "
|
|
185
|
+
* { type: "tab-layout", tabs: "sidebar" },
|
|
186
|
+
* { type: "tab-layout", tabs: "main" }
|
|
187
187
|
* ],
|
|
188
188
|
* sizes: [0.25, 0.75]
|
|
189
189
|
* };
|
|
@@ -200,11 +200,11 @@ export function create_css_grid_layout(
|
|
|
200
200
|
overlay?: [string, string],
|
|
201
201
|
physics: Physics = DEFAULT_PHYSICS,
|
|
202
202
|
): string {
|
|
203
|
-
if (layout.type === "
|
|
203
|
+
if (layout.type === "tab-layout") {
|
|
204
204
|
const selected = layout.selected ?? 0;
|
|
205
205
|
return [
|
|
206
206
|
host_template("100%", "100%"),
|
|
207
|
-
child_template(physics, layout.
|
|
207
|
+
child_template(physics, layout.tabs[selected], "1", "1"),
|
|
208
208
|
].join("\n");
|
|
209
209
|
}
|
|
210
210
|
|