regular-layout 0.3.0 → 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 +6 -0
- 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 +2 -2
- package/dist/layout/calculate_intersect.d.ts +1 -1
- package/dist/layout/calculate_path.d.ts +1 -1
- 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 +2 -2
- package/dist/layout/generate_overlay.d.ts +18 -2
- package/dist/layout/insert_child.d.ts +1 -1
- 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 +37 -9
- package/package.json +5 -4
- package/src/{layout → core}/constants.ts +2 -2
- package/src/{layout → core}/types.ts +7 -0
- package/src/extensions.ts +13 -1
- package/src/index.ts +3 -1
- package/src/layout/calculate_edge.ts +2 -2
- package/src/layout/calculate_intersect.ts +5 -2
- package/src/layout/calculate_path.ts +1 -1
- package/src/layout/calculate_presize_paths.ts +93 -0
- package/src/layout/flatten.ts +1 -1
- package/src/layout/generate_grid.ts +2 -2
- package/src/layout/generate_overlay.ts +48 -16
- package/src/layout/insert_child.ts +1 -1
- package/src/layout/redistribute_panel_sizes.ts +2 -2
- package/src/layout/remove_child.ts +2 -2
- package/src/model/overlay_controller.ts +162 -0
- package/src/model/presize_queue.ts +79 -0
- package/src/regular-layout-frame.ts +2 -2
- package/src/regular-layout-tab.ts +1 -1
- package/src/regular-layout.ts +166 -131
- 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,5 +1,5 @@
|
|
|
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.
|
|
@@ -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
|
|
@@ -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, LayoutPathTraversal } 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
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { Layout, LayoutPathTraversal } 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, LayoutPathTraversal } 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: LayoutPathTraversal, delta: number | undefined, physics?: import("
|
|
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, LayoutPathTraversal } 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;
|
|
@@ -99,7 +101,7 @@ export declare class RegularLayout extends HTMLElement {
|
|
|
99
101
|
* the target, "absolute" positions the panel absolutely. Defaults to
|
|
100
102
|
* "absolute".
|
|
101
103
|
*/
|
|
102
|
-
setOverlayState: (event: PointerEventCoordinates,
|
|
104
|
+
setOverlayState: (event: PointerEventCoordinates, target: LayoutPath, className?: string, mode?: OverlayMode) => Promise<void>;
|
|
103
105
|
/**
|
|
104
106
|
* Clears the overlay state and commits the panel placement.
|
|
105
107
|
*
|
|
@@ -112,7 +114,7 @@ export declare class RegularLayout extends HTMLElement {
|
|
|
112
114
|
* @param mode - Overlay rendering mode that was used, must match the mode
|
|
113
115
|
* passed to `setOverlayState`. Defaults to "absolute".
|
|
114
116
|
*/
|
|
115
|
-
clearOverlayState: (event: PointerEventCoordinates | null,
|
|
117
|
+
clearOverlayState: (event: PointerEventCoordinates | null, target: LayoutPath, className?: string) => Promise<void>;
|
|
116
118
|
/**
|
|
117
119
|
* Inserts a new panel into the layout at a specified path.
|
|
118
120
|
*
|
|
@@ -124,13 +126,13 @@ export declare class RegularLayout extends HTMLElement {
|
|
|
124
126
|
* the new `SplitPanel` _if_ there is an option of orientation (e.g. if
|
|
125
127
|
* the layout had no pre-existing `SplitPanel`)
|
|
126
128
|
*/
|
|
127
|
-
insertPanel: (name: string, path?: LayoutPathTraversal, split?: boolean | Orientation) => void
|
|
129
|
+
insertPanel: (name: string, path?: LayoutPathTraversal, split?: boolean | Orientation) => Promise<void>;
|
|
128
130
|
/**
|
|
129
131
|
* Removes a panel from the layout by name.
|
|
130
132
|
*
|
|
131
133
|
* @param name - Name of the panel to remove
|
|
132
134
|
*/
|
|
133
|
-
removePanel: (name: string) => void
|
|
135
|
+
removePanel: (name: string) => Promise<void>;
|
|
134
136
|
/**
|
|
135
137
|
* Retrieves a panel by name from the layout tree.
|
|
136
138
|
*
|
|
@@ -142,20 +144,36 @@ export declare class RegularLayout extends HTMLElement {
|
|
|
142
144
|
/**
|
|
143
145
|
* Clears the entire layout, unslotting all panels.
|
|
144
146
|
*/
|
|
145
|
-
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;
|
|
146
155
|
/**
|
|
147
156
|
* Restores the layout from a saved state.
|
|
148
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
|
+
*
|
|
149
162
|
* @param layout - The layout tree to restore
|
|
150
163
|
*
|
|
151
164
|
* @example
|
|
152
165
|
* ```typescript
|
|
153
166
|
* const layout = document.querySelector('regular-layout');
|
|
154
167
|
* const savedState = JSON.parse(localStorage.getItem('layout'));
|
|
155
|
-
* layout.restore(savedState);
|
|
168
|
+
* await layout.restore(savedState);
|
|
156
169
|
* ```
|
|
157
170
|
*/
|
|
158
|
-
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;
|
|
159
177
|
/**
|
|
160
178
|
* Serializes the current layout state, which can be restored via `restore`.
|
|
161
179
|
*
|
|
@@ -195,6 +213,15 @@ export declare class RegularLayout extends HTMLElement {
|
|
|
195
213
|
* - box: The layout element's bounding rectangle
|
|
196
214
|
*/
|
|
197
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;
|
|
198
225
|
/**
|
|
199
226
|
* Calculates the Euclidean distance in pixels between the current pointer
|
|
200
227
|
* coordinates and a drag target's position within the layout.
|
|
@@ -206,6 +233,7 @@ export declare class RegularLayout extends HTMLElement {
|
|
|
206
233
|
* target.
|
|
207
234
|
*/
|
|
208
235
|
diffCoordinates: (event: PointerEventCoordinates, drag_target: LayoutPath) => number;
|
|
236
|
+
private create_overlay_host;
|
|
209
237
|
private onDblClick;
|
|
210
238
|
private onPointerDown;
|
|
211
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,
|
|
@@ -95,6 +95,13 @@ export interface LayoutPath {
|
|
|
95
95
|
layout: Layout;
|
|
96
96
|
}
|
|
97
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
|
+
|
|
98
105
|
/**
|
|
99
106
|
* An empty `Layout` with no panels.
|
|
100
107
|
*/
|
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,7 +9,7 @@
|
|
|
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
14
|
import type {
|
|
15
15
|
Layout,
|
|
@@ -17,7 +17,7 @@ import type {
|
|
|
17
17
|
LayoutPathTraversal,
|
|
18
18
|
Orientation,
|
|
19
19
|
ViewWindow,
|
|
20
|
-
} from "
|
|
20
|
+
} from "../core/types";
|
|
21
21
|
|
|
22
22
|
/**
|
|
23
23
|
* Calculates an insertion point (which may involve splitting a single
|
|
@@ -15,7 +15,7 @@ import type {
|
|
|
15
15
|
Layout,
|
|
16
16
|
ViewWindow,
|
|
17
17
|
LayoutPathTraversal,
|
|
18
|
-
} from "
|
|
18
|
+
} from "../core/types.ts";
|
|
19
19
|
|
|
20
20
|
const VIEW_WINDOW = {
|
|
21
21
|
row_start: 0,
|
|
@@ -130,7 +130,10 @@ function calculate_intersection_recursive(
|
|
|
130
130
|
}
|
|
131
131
|
|
|
132
132
|
// Check if position falls within this child's bounds
|
|
133
|
-
if (
|
|
133
|
+
if (
|
|
134
|
+
position >= current_pos &&
|
|
135
|
+
(position < next_pos || i === panel.children.length - 1)
|
|
136
|
+
) {
|
|
134
137
|
return calculate_intersection_recursive(
|
|
135
138
|
column,
|
|
136
139
|
row,
|
|
@@ -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, LayoutPathTraversal } from "
|
|
12
|
+
import type { Layout, LayoutPathTraversal } from "../core/types.ts";
|
|
13
13
|
|
|
14
14
|
/**
|
|
15
15
|
* Calculates the index path for a panel with the given name.
|
|
@@ -0,0 +1,93 @@
|
|
|
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 {
|
|
13
|
+
Layout,
|
|
14
|
+
LayoutPath,
|
|
15
|
+
Orientation,
|
|
16
|
+
ViewWindow,
|
|
17
|
+
} from "../core/types.ts";
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Walks a layout tree and returns a {@link LayoutPath} for every visible
|
|
21
|
+
* panel, keyed by panel name. Each path's `column`/`row` are set to the
|
|
22
|
+
* center of the panel's view window, with offsets of 0.5.
|
|
23
|
+
*
|
|
24
|
+
* @param layout - The layout tree to walk.
|
|
25
|
+
* @returns A record mapping panel names to their layout paths.
|
|
26
|
+
*/
|
|
27
|
+
export function calculate_presize_paths(
|
|
28
|
+
layout: Layout,
|
|
29
|
+
): Record<string, LayoutPath> {
|
|
30
|
+
const result: Record<string, LayoutPath> = {};
|
|
31
|
+
walk_layout(layout, result, [], null, {
|
|
32
|
+
row_start: 0,
|
|
33
|
+
row_end: 1,
|
|
34
|
+
col_start: 0,
|
|
35
|
+
col_end: 1,
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
return result;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function walk_layout(
|
|
42
|
+
layout: Layout,
|
|
43
|
+
result: Record<string, LayoutPath>,
|
|
44
|
+
path: number[],
|
|
45
|
+
parentOrientation: Orientation | null,
|
|
46
|
+
viewWindow: ViewWindow,
|
|
47
|
+
): void {
|
|
48
|
+
if (layout.type === "tab-layout") {
|
|
49
|
+
const selected = layout.selected ?? 0;
|
|
50
|
+
const slot = layout.tabs[selected];
|
|
51
|
+
const col = (viewWindow.col_start + viewWindow.col_end) / 2;
|
|
52
|
+
const row = (viewWindow.row_start + viewWindow.row_end) / 2;
|
|
53
|
+
result[slot] = {
|
|
54
|
+
type: "layout-path",
|
|
55
|
+
layout,
|
|
56
|
+
slot,
|
|
57
|
+
path,
|
|
58
|
+
view_window: viewWindow,
|
|
59
|
+
is_edge: false,
|
|
60
|
+
column: col,
|
|
61
|
+
row: row,
|
|
62
|
+
column_offset: 0.5,
|
|
63
|
+
row_offset: 0.5,
|
|
64
|
+
orientation: parentOrientation || "horizontal",
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const isVertical = layout.orientation === "vertical";
|
|
71
|
+
const startKey = isVertical ? "row_start" : "col_start";
|
|
72
|
+
const endKey = isVertical ? "row_end" : "col_end";
|
|
73
|
+
let currentPos = viewWindow[startKey];
|
|
74
|
+
const totalSize = viewWindow[endKey] - viewWindow[startKey];
|
|
75
|
+
for (let i = 0; i < layout.children.length; i++) {
|
|
76
|
+
const nextPos = currentPos + totalSize * layout.sizes[i];
|
|
77
|
+
const childWindow: ViewWindow = {
|
|
78
|
+
...viewWindow,
|
|
79
|
+
[startKey]: currentPos,
|
|
80
|
+
[endKey]: nextPos,
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
walk_layout(
|
|
84
|
+
layout.children[i],
|
|
85
|
+
result,
|
|
86
|
+
[...path, i],
|
|
87
|
+
layout.orientation,
|
|
88
|
+
childWindow,
|
|
89
|
+
);
|
|
90
|
+
|
|
91
|
+
currentPos = nextPos;
|
|
92
|
+
}
|
|
93
|
+
}
|
package/src/layout/flatten.ts
CHANGED
|
@@ -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 "../core/types.ts";
|
|
13
13
|
|
|
14
14
|
/**
|
|
15
15
|
* Flattens the layout tree by merging parent and child split panels that have
|
|
@@ -9,8 +9,8 @@
|
|
|
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 "
|
|
13
|
-
import type { Layout } from "
|
|
12
|
+
import { DEFAULT_PHYSICS, type Physics } from "../core/constants.ts";
|
|
13
|
+
import type { Layout } from "../core/types.ts";
|
|
14
14
|
|
|
15
15
|
interface GridCell {
|
|
16
16
|
child: string;
|
|
@@ -9,8 +9,46 @@
|
|
|
9
9
|
// ┃ * [Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0). * ┃
|
|
10
10
|
// ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
|
|
11
11
|
|
|
12
|
-
import { DEFAULT_PHYSICS } from "
|
|
13
|
-
import type { LayoutPath } from "
|
|
12
|
+
import { DEFAULT_PHYSICS } from "../core/constants";
|
|
13
|
+
import type { LayoutPath, ViewWindow } from "../core/types";
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Converts a {@link ViewWindow} to element-relative pixel coordinates,
|
|
17
|
+
* accounting for padding and optionally CSS `gap` and child `margin`.
|
|
18
|
+
*
|
|
19
|
+
* @param window - The view window in normalized 0–1 coordinates.
|
|
20
|
+
* @param box - The element's bounding client rect.
|
|
21
|
+
* @param style - The element's computed style (for padding).
|
|
22
|
+
* @param margin - Optional child element's computed style (for margin inset).
|
|
23
|
+
* @returns Pixel coordinates relative to the element's border-box origin.
|
|
24
|
+
*/
|
|
25
|
+
export function viewWindowToLocalRect(
|
|
26
|
+
window: ViewWindow,
|
|
27
|
+
box: DOMRect,
|
|
28
|
+
style: CSSStyleDeclaration,
|
|
29
|
+
margin?: CSSStyleDeclaration,
|
|
30
|
+
): { x: number; y: number; width: number; height: number } {
|
|
31
|
+
const paddingLeft = parseFloat(style.paddingLeft);
|
|
32
|
+
const paddingTop = parseFloat(style.paddingTop);
|
|
33
|
+
const contentWidth = box.width - paddingLeft - parseFloat(style.paddingRight);
|
|
34
|
+
const contentHeight =
|
|
35
|
+
box.height - paddingTop - parseFloat(style.paddingBottom);
|
|
36
|
+
|
|
37
|
+
const x = paddingLeft + window.col_start * contentWidth;
|
|
38
|
+
const y = paddingTop + window.row_start * contentHeight;
|
|
39
|
+
let width = (window.col_end - window.col_start) * contentWidth;
|
|
40
|
+
let height = (window.row_end - window.row_start) * contentHeight;
|
|
41
|
+
if (margin) {
|
|
42
|
+
const marginTop = parseFloat(margin.marginTop);
|
|
43
|
+
const marginRight = parseFloat(margin.marginRight);
|
|
44
|
+
const marginBottom = parseFloat(margin.marginBottom);
|
|
45
|
+
const marginLeft = parseFloat(margin.marginLeft);
|
|
46
|
+
width -= marginLeft + marginRight;
|
|
47
|
+
height -= marginTop + marginBottom;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
return { x, y, width, height };
|
|
51
|
+
}
|
|
14
52
|
|
|
15
53
|
export function updateOverlaySheet(
|
|
16
54
|
slot: string,
|
|
@@ -18,25 +56,19 @@ export function updateOverlaySheet(
|
|
|
18
56
|
style: CSSStyleDeclaration,
|
|
19
57
|
drag_target: LayoutPath | null,
|
|
20
58
|
physics = DEFAULT_PHYSICS,
|
|
59
|
+
margin?: CSSStyleDeclaration,
|
|
21
60
|
) {
|
|
22
61
|
if (!drag_target) {
|
|
23
62
|
return `:host ::slotted([${physics.CHILD_ATTRIBUTE_NAME}="${slot}"]){display:none;}`;
|
|
24
63
|
}
|
|
25
64
|
|
|
26
|
-
const
|
|
27
|
-
view_window
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
const box_width =
|
|
34
|
-
box.width - parseFloat(style.paddingLeft) - parseFloat(style.paddingRight);
|
|
65
|
+
const local = viewWindowToLocalRect(
|
|
66
|
+
drag_target.view_window,
|
|
67
|
+
box,
|
|
68
|
+
style,
|
|
69
|
+
margin,
|
|
70
|
+
);
|
|
35
71
|
|
|
36
|
-
const
|
|
37
|
-
const left = col_start * box_width + parseFloat(style.paddingLeft);
|
|
38
|
-
const height = (row_end - row_start) * box_height;
|
|
39
|
-
const width = (col_end - col_start) * box_width;
|
|
40
|
-
const css = `display:flex;position:absolute!important;z-index:1;top:${top}px;left:${left}px;height:${height}px;width:${width}px;`;
|
|
72
|
+
const css = `display:flex;position:absolute!important;z-index:1;top:${local.y}px;left:${local.x}px;height:${local.height}px;width:${local.width}px;`;
|
|
41
73
|
return `::slotted([${physics.CHILD_ATTRIBUTE_NAME}="${slot}"]){${css}}`;
|
|
42
74
|
}
|
|
@@ -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, LayoutPathTraversal } from "
|
|
12
|
+
import type { Layout, LayoutPathTraversal } from "../core/types.ts";
|
|
13
13
|
|
|
14
14
|
/**
|
|
15
15
|
* Inserts a new child panel into the layout tree at a specified location.
|
|
@@ -9,8 +9,8 @@
|
|
|
9
9
|
// ┃ * [Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0). * ┃
|
|
10
10
|
// ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
|
|
11
11
|
|
|
12
|
-
import { DEFAULT_PHYSICS } from "
|
|
13
|
-
import type { Layout, LayoutPathTraversal } from "
|
|
12
|
+
import { DEFAULT_PHYSICS } from "../core/constants.ts";
|
|
13
|
+
import type { Layout, LayoutPathTraversal } from "../core/types.ts";
|
|
14
14
|
|
|
15
15
|
/**
|
|
16
16
|
* Adjusts panel sizes during a drag operation on a divider.
|