regular-layout 0.2.2 → 0.4.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/README.md +110 -22
- package/dist/{layout → core}/types.d.ts +14 -4
- package/dist/extensions.d.ts +6 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.js +7 -7
- package/dist/index.js.map +4 -4
- package/dist/layout/calculate_edge.d.ts +4 -4
- package/dist/layout/calculate_intersect.d.ts +1 -1
- package/dist/layout/calculate_path.d.ts +12 -0
- package/dist/layout/calculate_presize_paths.d.ts +10 -0
- package/dist/layout/flatten.d.ts +1 -1
- package/dist/layout/generate_grid.d.ts +5 -5
- package/dist/layout/generate_overlay.d.ts +18 -2
- package/dist/layout/insert_child.d.ts +2 -2
- package/dist/layout/redistribute_panel_sizes.d.ts +2 -2
- package/dist/layout/remove_child.d.ts +1 -1
- package/dist/model/overlay_controller.d.ts +34 -0
- package/dist/model/presize_queue.d.ts +17 -0
- package/dist/regular-layout-tab.d.ts +1 -1
- package/dist/regular-layout.d.ts +44 -9
- package/package.json +5 -4
- package/src/{layout → core}/constants.ts +2 -2
- package/src/{layout → core}/types.ts +17 -5
- package/src/extensions.ts +13 -1
- package/src/index.ts +3 -1
- package/src/layout/calculate_edge.ts +17 -8
- package/src/layout/calculate_intersect.ts +13 -4
- package/src/layout/calculate_path.ts +53 -0
- package/src/layout/calculate_presize_paths.ts +93 -0
- package/src/layout/flatten.ts +4 -4
- package/src/layout/generate_grid.ts +8 -8
- package/src/layout/generate_overlay.ts +48 -16
- package/src/layout/insert_child.ts +16 -16
- package/src/layout/redistribute_panel_sizes.ts +5 -5
- package/src/layout/remove_child.ts +6 -6
- package/src/model/overlay_controller.ts +162 -0
- package/src/model/presize_queue.ts +79 -0
- package/src/regular-layout-frame.ts +3 -3
- package/src/regular-layout-tab.ts +1 -1
- package/src/regular-layout.ts +180 -133
- package/themes/borland.css +103 -0
- package/themes/chicago.css +55 -49
- package/themes/fluxbox.css +64 -60
- package/themes/gibson.css +174 -164
- package/themes/hotdog.css +53 -47
- package/themes/lorax.css +82 -75
- /package/dist/{layout → core}/constants.d.ts +0 -0
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { type Physics } from "
|
|
2
|
-
import type { Layout, LayoutPath } from "
|
|
1
|
+
import { type Physics } from "../core/constants";
|
|
2
|
+
import type { Layout, LayoutPath } from "../core/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 "../core/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;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { Layout, LayoutPath } from "../core/types.ts";
|
|
2
|
+
/**
|
|
3
|
+
* Walks a layout tree and returns a {@link LayoutPath} for every visible
|
|
4
|
+
* panel, keyed by panel name. Each path's `column`/`row` are set to the
|
|
5
|
+
* center of the panel's view window, with offsets of 0.5.
|
|
6
|
+
*
|
|
7
|
+
* @param layout - The layout tree to walk.
|
|
8
|
+
* @returns A record mapping panel names to their layout paths.
|
|
9
|
+
*/
|
|
10
|
+
export declare function calculate_presize_paths(layout: Layout): Record<string, LayoutPath>;
|
package/dist/layout/flatten.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { type Physics } from "
|
|
2
|
-
import type { Layout } from "
|
|
1
|
+
import { type Physics } from "../core/constants.ts";
|
|
2
|
+
import type { Layout } from "../core/types.ts";
|
|
3
3
|
/**
|
|
4
4
|
* Generates CSS Grid styles to render a layout tree.
|
|
5
5
|
* Creates grid-template-rows, grid-template-columns, and positioning rules for
|
|
@@ -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,18 @@
|
|
|
1
|
-
import type { LayoutPath } from "
|
|
2
|
-
|
|
1
|
+
import type { LayoutPath, ViewWindow } from "../core/types";
|
|
2
|
+
/**
|
|
3
|
+
* Converts a {@link ViewWindow} to element-relative pixel coordinates,
|
|
4
|
+
* accounting for padding and optionally CSS `gap` and child `margin`.
|
|
5
|
+
*
|
|
6
|
+
* @param window - The view window in normalized 0–1 coordinates.
|
|
7
|
+
* @param box - The element's bounding client rect.
|
|
8
|
+
* @param style - The element's computed style (for padding).
|
|
9
|
+
* @param margin - Optional child element's computed style (for margin inset).
|
|
10
|
+
* @returns Pixel coordinates relative to the element's border-box origin.
|
|
11
|
+
*/
|
|
12
|
+
export declare function viewWindowToLocalRect(window: ViewWindow, box: DOMRect, style: CSSStyleDeclaration, margin?: CSSStyleDeclaration): {
|
|
13
|
+
x: number;
|
|
14
|
+
y: number;
|
|
15
|
+
width: number;
|
|
16
|
+
height: number;
|
|
17
|
+
};
|
|
18
|
+
export declare function updateOverlaySheet(slot: string, box: DOMRect, style: CSSStyleDeclaration, drag_target: LayoutPath | null, physics?: import("../core/constants").Physics, margin?: CSSStyleDeclaration): string;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { Layout } from "
|
|
1
|
+
import type { Layout, LayoutPathTraversal } from "../core/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 "
|
|
1
|
+
import type { Layout, LayoutPathTraversal } from "../core/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("../core/constants.ts").Physics): Layout;
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import type { Layout, LayoutPath, OverlayMode } from "../core/types.ts";
|
|
2
|
+
import type { Physics } from "../core/constants.ts";
|
|
3
|
+
import type { PresizeQueue } from "./presize_queue.ts";
|
|
4
|
+
export interface OverlayHost {
|
|
5
|
+
readonly panel: Layout;
|
|
6
|
+
readonly physics: Physics;
|
|
7
|
+
readonly stylesheet: CSSStyleSheet;
|
|
8
|
+
readonly presizeQueue: PresizeQueue;
|
|
9
|
+
relativeCoordinates(event: {
|
|
10
|
+
clientX: number;
|
|
11
|
+
clientY: number;
|
|
12
|
+
}, recalculate: boolean): [number, number, DOMRect, CSSStyleDeclaration];
|
|
13
|
+
restore(layout: Layout, isFlattened?: boolean): Promise<void>;
|
|
14
|
+
querySelector(selectors: string): Element | null;
|
|
15
|
+
dispatchEvent(event: Event): boolean;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Manages overlay state during drag-and-drop panel rearrangement.
|
|
19
|
+
*
|
|
20
|
+
* Handles rendering a preview of where a dragged panel would land,
|
|
21
|
+
* and committing or cancelling the placement when the drag ends.
|
|
22
|
+
*/
|
|
23
|
+
export declare class OverlayController {
|
|
24
|
+
private _host;
|
|
25
|
+
constructor(_host: OverlayHost);
|
|
26
|
+
set(event: {
|
|
27
|
+
clientX: number;
|
|
28
|
+
clientY: number;
|
|
29
|
+
}, { slot }: LayoutPath, className?: string, mode?: OverlayMode): Promise<void>;
|
|
30
|
+
clear(event: {
|
|
31
|
+
clientX: number;
|
|
32
|
+
clientY: number;
|
|
33
|
+
} | null, { slot, layout }: LayoutPath, className?: string): Promise<void>;
|
|
34
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { Layout } from "../core/types.ts";
|
|
2
|
+
/**
|
|
3
|
+
* Manages cancelable pre-resize gating for layout updates.
|
|
4
|
+
*
|
|
5
|
+
* Before each layout change, a cancelable `resize-before` event is dispatched
|
|
6
|
+
* on the target. If the event is cancelled via `preventDefault()`, the update
|
|
7
|
+
* is suspended until {@link resume} is called. Concurrent updates are queued
|
|
8
|
+
* and processed sequentially.
|
|
9
|
+
*/
|
|
10
|
+
export declare class PresizeQueue {
|
|
11
|
+
#private;
|
|
12
|
+
private _target;
|
|
13
|
+
private _eventName;
|
|
14
|
+
constructor(_target: EventTarget, _eventName: string);
|
|
15
|
+
run(layout: Layout, fn: () => void): Promise<void>;
|
|
16
|
+
resume(): void;
|
|
17
|
+
}
|
package/dist/regular-layout.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import type { LayoutPath, Layout, TabLayout, OverlayMode, Orientation } from "./
|
|
2
|
-
import { type PhysicsUpdate, type Physics } from "./
|
|
1
|
+
import type { LayoutPath, Layout, TabLayout, OverlayMode, Orientation, LayoutPathTraversal, ViewWindow } from "./core/types.ts";
|
|
2
|
+
import { type PhysicsUpdate, type Physics } from "./core/constants.ts";
|
|
3
3
|
/**
|
|
4
4
|
* An interface which models the fields of `PointerEvent` that
|
|
5
5
|
* `<regular-layour>` actually uses, making it easier to sub out with an
|
|
@@ -67,6 +67,8 @@ export declare class RegularLayout extends HTMLElement {
|
|
|
67
67
|
private _cursor_override;
|
|
68
68
|
private _dimensions?;
|
|
69
69
|
private _physics;
|
|
70
|
+
private _presizeQueue;
|
|
71
|
+
private _overlayController;
|
|
70
72
|
constructor();
|
|
71
73
|
connectedCallback(): void;
|
|
72
74
|
disconnectedCallback(): void;
|
|
@@ -78,6 +80,13 @@ export declare class RegularLayout extends HTMLElement {
|
|
|
78
80
|
* @returns Panel information if a panel is at that position, null otherwise.
|
|
79
81
|
*/
|
|
80
82
|
calculateIntersect: (coordinates: PointerEventCoordinates) => LayoutPath | null;
|
|
83
|
+
/**
|
|
84
|
+
* Calculates the index path for a panel with the given name.
|
|
85
|
+
*
|
|
86
|
+
* @param name - The name of the panel to find.
|
|
87
|
+
* @returns The panel's index path if found, `null` otherwise.
|
|
88
|
+
*/
|
|
89
|
+
calculatePath: (name: string) => number[] | null;
|
|
81
90
|
/**
|
|
82
91
|
* Sets the visual overlay state during drag-and-drop operations.
|
|
83
92
|
* Displays a preview of where a panel would be placed at the given coordinates.
|
|
@@ -92,7 +101,7 @@ export declare class RegularLayout extends HTMLElement {
|
|
|
92
101
|
* the target, "absolute" positions the panel absolutely. Defaults to
|
|
93
102
|
* "absolute".
|
|
94
103
|
*/
|
|
95
|
-
setOverlayState: (event: PointerEventCoordinates,
|
|
104
|
+
setOverlayState: (event: PointerEventCoordinates, target: LayoutPath, className?: string, mode?: OverlayMode) => Promise<void>;
|
|
96
105
|
/**
|
|
97
106
|
* Clears the overlay state and commits the panel placement.
|
|
98
107
|
*
|
|
@@ -105,7 +114,7 @@ export declare class RegularLayout extends HTMLElement {
|
|
|
105
114
|
* @param mode - Overlay rendering mode that was used, must match the mode
|
|
106
115
|
* passed to `setOverlayState`. Defaults to "absolute".
|
|
107
116
|
*/
|
|
108
|
-
clearOverlayState: (event: PointerEventCoordinates | null,
|
|
117
|
+
clearOverlayState: (event: PointerEventCoordinates | null, target: LayoutPath, className?: string) => Promise<void>;
|
|
109
118
|
/**
|
|
110
119
|
* Inserts a new panel into the layout at a specified path.
|
|
111
120
|
*
|
|
@@ -117,13 +126,13 @@ export declare class RegularLayout extends HTMLElement {
|
|
|
117
126
|
* the new `SplitPanel` _if_ there is an option of orientation (e.g. if
|
|
118
127
|
* the layout had no pre-existing `SplitPanel`)
|
|
119
128
|
*/
|
|
120
|
-
insertPanel: (name: string, path?:
|
|
129
|
+
insertPanel: (name: string, path?: LayoutPathTraversal, split?: boolean | Orientation) => Promise<void>;
|
|
121
130
|
/**
|
|
122
131
|
* Removes a panel from the layout by name.
|
|
123
132
|
*
|
|
124
133
|
* @param name - Name of the panel to remove
|
|
125
134
|
*/
|
|
126
|
-
removePanel: (name: string) => void
|
|
135
|
+
removePanel: (name: string) => Promise<void>;
|
|
127
136
|
/**
|
|
128
137
|
* Retrieves a panel by name from the layout tree.
|
|
129
138
|
*
|
|
@@ -135,20 +144,36 @@ export declare class RegularLayout extends HTMLElement {
|
|
|
135
144
|
/**
|
|
136
145
|
* Clears the entire layout, unslotting all panels.
|
|
137
146
|
*/
|
|
138
|
-
clear: () => void
|
|
147
|
+
clear: () => Promise<void>;
|
|
148
|
+
/**
|
|
149
|
+
* Restores the layout from a saved state synchronously, without
|
|
150
|
+
* dispatching the `regular-layout-before-resize` event.
|
|
151
|
+
*
|
|
152
|
+
* @param layout - The layout tree to restore
|
|
153
|
+
*/
|
|
154
|
+
restoreSync: (layout: Layout, _is_flattened?: boolean) => void;
|
|
139
155
|
/**
|
|
140
156
|
* Restores the layout from a saved state.
|
|
141
157
|
*
|
|
158
|
+
* Before applying, dispatches a cancelable `regular-layout-before-resize`
|
|
159
|
+
* event. If the event is cancelled via `preventDefault()`, the layout
|
|
160
|
+
* update is suspended until {@link resumeResize} is called.
|
|
161
|
+
*
|
|
142
162
|
* @param layout - The layout tree to restore
|
|
143
163
|
*
|
|
144
164
|
* @example
|
|
145
165
|
* ```typescript
|
|
146
166
|
* const layout = document.querySelector('regular-layout');
|
|
147
167
|
* const savedState = JSON.parse(localStorage.getItem('layout'));
|
|
148
|
-
* layout.restore(savedState);
|
|
168
|
+
* await layout.restore(savedState);
|
|
149
169
|
* ```
|
|
150
170
|
*/
|
|
151
|
-
restore: (layout: Layout, _is_flattened?: boolean) => void
|
|
171
|
+
restore: (layout: Layout, _is_flattened?: boolean) => Promise<void>;
|
|
172
|
+
/**
|
|
173
|
+
* Resumes a layout update that was suspended by cancelling the
|
|
174
|
+
* `regular-layout-before-resize` event.
|
|
175
|
+
*/
|
|
176
|
+
resumeResize: () => void;
|
|
152
177
|
/**
|
|
153
178
|
* Serializes the current layout state, which can be restored via `restore`.
|
|
154
179
|
*
|
|
@@ -188,6 +213,15 @@ export declare class RegularLayout extends HTMLElement {
|
|
|
188
213
|
* - box: The layout element's bounding rectangle
|
|
189
214
|
*/
|
|
190
215
|
relativeCoordinates: (event: PointerEventCoordinates, recalculate_bounds?: boolean) => [number, number, DOMRect, CSSStyleDeclaration];
|
|
216
|
+
/**
|
|
217
|
+
* Converts a {@link ViewWindow} (normalized 0–1 coordinates) to a
|
|
218
|
+
* `DOMRect` in screen pixels, accounting for padding and optionally
|
|
219
|
+
* CSS `gap` and child `margin`.
|
|
220
|
+
*
|
|
221
|
+
* @param window - The view window to convert.
|
|
222
|
+
* @returns A `DOMRect` representing the window in screen coordinates.
|
|
223
|
+
*/
|
|
224
|
+
realCoordinates: (window: ViewWindow, child?: HTMLElement) => DOMRect;
|
|
191
225
|
/**
|
|
192
226
|
* Calculates the Euclidean distance in pixels between the current pointer
|
|
193
227
|
* coordinates and a drag target's position within the layout.
|
|
@@ -199,6 +233,7 @@ export declare class RegularLayout extends HTMLElement {
|
|
|
199
233
|
* target.
|
|
200
234
|
*/
|
|
201
235
|
diffCoordinates: (event: PointerEventCoordinates, drag_target: LayoutPath) => number;
|
|
236
|
+
private create_overlay_host;
|
|
202
237
|
private onDblClick;
|
|
203
238
|
private onPointerDown;
|
|
204
239
|
private onPointerMove;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "regular-layout",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"description": "A regular CSS `grid` container",
|
|
5
5
|
"keywords": [],
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -19,10 +19,10 @@
|
|
|
19
19
|
],
|
|
20
20
|
"scripts": {
|
|
21
21
|
"build": "tsx build.ts",
|
|
22
|
-
"
|
|
22
|
+
"watch": "tsx build.ts --watch",
|
|
23
23
|
"clean": "rm -rf dist",
|
|
24
|
-
"test": "
|
|
25
|
-
"
|
|
24
|
+
"test": "tsx test.ts",
|
|
25
|
+
"bench": "playwright test --workers=1 ./benchmarks",
|
|
26
26
|
"example": "tsx serve.ts",
|
|
27
27
|
"deploy": "tsx deploy.ts",
|
|
28
28
|
"lint": "biome lint src tests",
|
|
@@ -34,6 +34,7 @@
|
|
|
34
34
|
"@playwright/test": "^1.57.0",
|
|
35
35
|
"@types/node": "^22.10.5",
|
|
36
36
|
"esbuild": "^0.27.2",
|
|
37
|
+
"monocart-coverage-reports": "^2.12.2",
|
|
37
38
|
"tsx": "^4.21.0",
|
|
38
39
|
"typescript": "^5.9.3"
|
|
39
40
|
}
|
|
@@ -110,8 +110,8 @@ export const DEFAULT_PHYSICS: Physics = Object.freeze({
|
|
|
110
110
|
SHOULD_ROUND: false,
|
|
111
111
|
OVERLAY_CLASSNAME: "overlay",
|
|
112
112
|
MINIMUM_REDISTRIBUTION_SIZE_THRESHOLD: 0.15,
|
|
113
|
-
SPLIT_EDGE_TOLERANCE: 0.
|
|
114
|
-
SPLIT_ROOT_EDGE_TOLERANCE: 0.
|
|
113
|
+
SPLIT_EDGE_TOLERANCE: 0.33,
|
|
114
|
+
SPLIT_ROOT_EDGE_TOLERANCE: 0.03,
|
|
115
115
|
GRID_TRACK_COLLAPSE_TOLERANCE: 0.001,
|
|
116
116
|
OVERLAY_DEFAULT: "absolute",
|
|
117
117
|
GRID_DIVIDER_SIZE: 6,
|
|
@@ -47,7 +47,7 @@ export interface ViewWindow {
|
|
|
47
47
|
* opposite `orientation` as its parent.
|
|
48
48
|
*/
|
|
49
49
|
export interface SplitLayout {
|
|
50
|
-
type: "split-
|
|
50
|
+
type: "split-layout";
|
|
51
51
|
children: Layout[];
|
|
52
52
|
sizes: number[];
|
|
53
53
|
orientation: Orientation;
|
|
@@ -57,18 +57,23 @@ export interface SplitLayout {
|
|
|
57
57
|
* A leaf panel node that contains a single named child element.
|
|
58
58
|
*/
|
|
59
59
|
export interface TabLayout {
|
|
60
|
-
type: "
|
|
60
|
+
type: "tab-layout";
|
|
61
61
|
tabs: string[];
|
|
62
62
|
selected?: number;
|
|
63
63
|
}
|
|
64
64
|
|
|
65
|
+
/**
|
|
66
|
+
* Represents a panel location relative to a `Layout` struct.
|
|
67
|
+
*/
|
|
68
|
+
export type LayoutPathTraversal = number[];
|
|
69
|
+
|
|
65
70
|
/**
|
|
66
71
|
* Represents a draggable divider between two panels in the layout.
|
|
67
72
|
*
|
|
68
73
|
* Used for hit detection.
|
|
69
74
|
*/
|
|
70
75
|
export interface LayoutDivider {
|
|
71
|
-
path:
|
|
76
|
+
path: LayoutPathTraversal;
|
|
72
77
|
view_window: ViewWindow;
|
|
73
78
|
type: Orientation;
|
|
74
79
|
}
|
|
@@ -79,7 +84,7 @@ export interface LayoutDivider {
|
|
|
79
84
|
export interface LayoutPath {
|
|
80
85
|
type: "layout-path";
|
|
81
86
|
slot: string;
|
|
82
|
-
path:
|
|
87
|
+
path: LayoutPathTraversal;
|
|
83
88
|
view_window: ViewWindow;
|
|
84
89
|
column: number;
|
|
85
90
|
row: number;
|
|
@@ -90,11 +95,18 @@ export interface LayoutPath {
|
|
|
90
95
|
layout: Layout;
|
|
91
96
|
}
|
|
92
97
|
|
|
98
|
+
/**
|
|
99
|
+
* The detail payload of the `regular-layout-before-resize` event.
|
|
100
|
+
*/
|
|
101
|
+
export interface PresizeDetail {
|
|
102
|
+
calculatePresizePaths(): Record<string, LayoutPath>;
|
|
103
|
+
}
|
|
104
|
+
|
|
93
105
|
/**
|
|
94
106
|
* An empty `Layout` with no panels.
|
|
95
107
|
*/
|
|
96
108
|
export const EMPTY_PANEL: Layout = {
|
|
97
|
-
type: "split-
|
|
109
|
+
type: "split-layout",
|
|
98
110
|
orientation: "horizontal",
|
|
99
111
|
sizes: [],
|
|
100
112
|
children: [],
|
package/src/extensions.ts
CHANGED
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
|
|
12
12
|
import { RegularLayout } from "./regular-layout.ts";
|
|
13
13
|
import { RegularLayoutFrame } from "./regular-layout-frame.ts";
|
|
14
|
-
import type { Layout } from "./
|
|
14
|
+
import type { Layout, PresizeDetail } from "./core/types.ts";
|
|
15
15
|
import { RegularLayoutTab } from "./regular-layout-tab.ts";
|
|
16
16
|
|
|
17
17
|
customElements.define("regular-layout", RegularLayout);
|
|
@@ -59,6 +59,12 @@ declare global {
|
|
|
59
59
|
options?: { signal: AbortSignal },
|
|
60
60
|
): void;
|
|
61
61
|
|
|
62
|
+
addEventListener(
|
|
63
|
+
name: "regular-layout-before-resize",
|
|
64
|
+
cb: (e: RegularLayoutPresizeEvent) => void,
|
|
65
|
+
options?: { signal: AbortSignal },
|
|
66
|
+
): void;
|
|
67
|
+
|
|
62
68
|
removeEventListener(
|
|
63
69
|
name: "regular-layout-update",
|
|
64
70
|
cb: (e: RegularLayoutEvent) => void,
|
|
@@ -68,7 +74,13 @@ declare global {
|
|
|
68
74
|
name: "regular-layout-before-update",
|
|
69
75
|
cb: (e: RegularLayoutEvent) => void,
|
|
70
76
|
): void;
|
|
77
|
+
|
|
78
|
+
removeEventListener(
|
|
79
|
+
name: "regular-layout-before-resize",
|
|
80
|
+
cb: (e: RegularLayoutPresizeEvent) => void,
|
|
81
|
+
): void;
|
|
71
82
|
}
|
|
72
83
|
}
|
|
73
84
|
|
|
74
85
|
export type RegularLayoutEvent = CustomEvent<Layout>;
|
|
86
|
+
export type RegularLayoutPresizeEvent = CustomEvent<PresizeDetail>;
|
package/src/index.ts
CHANGED
|
@@ -59,10 +59,12 @@
|
|
|
59
59
|
* @packageDocumentation
|
|
60
60
|
*/
|
|
61
61
|
|
|
62
|
-
export type * from "./
|
|
62
|
+
export type * from "./core/types.ts";
|
|
63
63
|
|
|
64
64
|
export { RegularLayout } from "./regular-layout.ts";
|
|
65
65
|
export { RegularLayoutFrame } from "./regular-layout-frame.ts";
|
|
66
66
|
|
|
67
|
+
export type * from "./extensions.ts";
|
|
68
|
+
|
|
67
69
|
// Side effects
|
|
68
70
|
import "./extensions.ts";
|
|
@@ -9,13 +9,19 @@
|
|
|
9
9
|
// ┃ * [Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0). * ┃
|
|
10
10
|
// ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
|
|
11
11
|
|
|
12
|
-
import { DEFAULT_PHYSICS, type Physics } from "
|
|
12
|
+
import { DEFAULT_PHYSICS, type Physics } from "../core/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 "../core/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(
|
|
@@ -133,7 +139,7 @@ function insert_root_edge(
|
|
|
133
139
|
panel: Layout,
|
|
134
140
|
slot: string,
|
|
135
141
|
drop_target: LayoutPath,
|
|
136
|
-
path:
|
|
142
|
+
path: LayoutPathTraversal,
|
|
137
143
|
is_before: boolean,
|
|
138
144
|
orientation: Orientation,
|
|
139
145
|
): LayoutPath {
|
|
@@ -153,7 +159,7 @@ function insert_axis(
|
|
|
153
159
|
is_before: boolean,
|
|
154
160
|
axis_orientation: Orientation,
|
|
155
161
|
): LayoutPath {
|
|
156
|
-
let result_path:
|
|
162
|
+
let result_path: LayoutPathTraversal;
|
|
157
163
|
|
|
158
164
|
if (drop_target.orientation === axis_orientation) {
|
|
159
165
|
// Same orientation - insert into existing split
|
|
@@ -183,7 +189,10 @@ function insert_axis(
|
|
|
183
189
|
};
|
|
184
190
|
}
|
|
185
191
|
|
|
186
|
-
function calculate_view_window(
|
|
192
|
+
function calculate_view_window(
|
|
193
|
+
panel: Layout,
|
|
194
|
+
path: LayoutPathTraversal,
|
|
195
|
+
): ViewWindow {
|
|
187
196
|
let view_window: ViewWindow = {
|
|
188
197
|
row_start: 0,
|
|
189
198
|
row_end: 1,
|
|
@@ -193,7 +202,7 @@ function calculate_view_window(panel: Layout, path: number[]): ViewWindow {
|
|
|
193
202
|
|
|
194
203
|
let current_panel = panel;
|
|
195
204
|
for (const step of path) {
|
|
196
|
-
if (current_panel.type === "
|
|
205
|
+
if (current_panel.type === "tab-layout") {
|
|
197
206
|
break;
|
|
198
207
|
}
|
|
199
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 "../core/types.ts";
|
|
13
19
|
|
|
14
20
|
const VIEW_WINDOW = {
|
|
15
21
|
row_start: 0,
|
|
@@ -67,14 +73,14 @@ 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;
|
|
@@ -124,7 +130,10 @@ function calculate_intersection_recursive(
|
|
|
124
130
|
}
|
|
125
131
|
|
|
126
132
|
// Check if position falls within this child's bounds
|
|
127
|
-
if (
|
|
133
|
+
if (
|
|
134
|
+
position >= current_pos &&
|
|
135
|
+
(position < next_pos || i === panel.children.length - 1)
|
|
136
|
+
) {
|
|
128
137
|
return calculate_intersection_recursive(
|
|
129
138
|
column,
|
|
130
139
|
row,
|
|
@@ -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 "../core/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
|
+
}
|