regular-layout 0.3.0 → 0.5.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 +13 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.js +10 -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 +11 -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-frame.d.ts +21 -0
- package/dist/regular-layout-tab.d.ts +1 -1
- package/dist/regular-layout.d.ts +76 -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 +25 -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 +20 -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 +161 -0
- package/src/model/presize_queue.ts +79 -0
- package/src/regular-layout-frame.ts +58 -3
- package/src/regular-layout-tab.ts +20 -22
- package/src/regular-layout.ts +252 -132
- 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
|
|
@@ -31,3 +31,12 @@ import type { Layout } from "./types.ts";
|
|
|
31
31
|
* ```
|
|
32
32
|
*/
|
|
33
33
|
export declare function create_css_grid_layout(layout: Layout, overlay?: [string, string], physics?: Physics): string;
|
|
34
|
+
/**
|
|
35
|
+
* Generates CSS Grid styles that render a single panel maximized to fill the
|
|
36
|
+
* entire layout, hiding all other slotted children.
|
|
37
|
+
*
|
|
38
|
+
* @param name - The name of the panel to maximize.
|
|
39
|
+
* @param physics - Instance constants (defaults to {@link DEFAULT_PHYSICS}).
|
|
40
|
+
* @returns CSS string showing only `name`, full-size.
|
|
41
|
+
*/
|
|
42
|
+
export declare function create_css_maximize_layout(name: string, physics?: Physics): string;
|
|
@@ -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
|
+
}
|
|
@@ -14,8 +14,17 @@
|
|
|
14
14
|
* [named `slot`s](https://developer.mozilla.org/en-US/docs/Web/API/Web_components/Using_templates_and_slots)
|
|
15
15
|
* for wholesale replacement of the underlying Shadow DOM.
|
|
16
16
|
*
|
|
17
|
+
* The `name` attribute identifies the panel within the layout. The tab label
|
|
18
|
+
* defaults to `name`, but can be overridden with pure CSS by setting the
|
|
19
|
+
* `--regular-layout-<name>--title` custom property to a CSS string - the label
|
|
20
|
+
* is rendered via the tab title's `::before` `content`, so the override
|
|
21
|
+
* applies reactively with no JavaScript.
|
|
22
|
+
*
|
|
17
23
|
* @example
|
|
18
24
|
* ```html
|
|
25
|
+
* <style>
|
|
26
|
+
* regular-layout { --regular-layout-panel-1--title: "Panel 1"; }
|
|
27
|
+
* </style>
|
|
19
28
|
* <regular-layout>
|
|
20
29
|
* <regular-layout-frame name="panel-1">
|
|
21
30
|
* <!-- Panel content here -->
|
|
@@ -26,6 +35,7 @@
|
|
|
26
35
|
export declare class RegularLayoutFrame extends HTMLElement {
|
|
27
36
|
private _shadowRoot;
|
|
28
37
|
private _container_sheet;
|
|
38
|
+
private _title_sheet;
|
|
29
39
|
private _layout;
|
|
30
40
|
private _header;
|
|
31
41
|
private _drag;
|
|
@@ -46,4 +56,15 @@ export declare class RegularLayoutFrame extends HTMLElement {
|
|
|
46
56
|
private onPointerCancel;
|
|
47
57
|
private onPointerLost;
|
|
48
58
|
private drawTabs;
|
|
59
|
+
/**
|
|
60
|
+
* Regenerates this frame's title stylesheet, mapping each tab (by its slot
|
|
61
|
+
* `name`) to its label. The label is rendered via the title's `::before`
|
|
62
|
+
* `content`, reading the slot's `--regular-layout-<slot>--title` override
|
|
63
|
+
* variable with the slot name as the fallback - so a consumer can relabel a
|
|
64
|
+
* tab purely in CSS and the change applies reactively.
|
|
65
|
+
*
|
|
66
|
+
* @param attr - The child name attribute (`CHILD_ATTRIBUTE_NAME`).
|
|
67
|
+
* @param tabs - The slot names rendered in this frame's titlebar.
|
|
68
|
+
*/
|
|
69
|
+
private drawTitles;
|
|
49
70
|
}
|
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
|
|
@@ -57,6 +57,14 @@ export interface PointerEventCoordinates {
|
|
|
57
57
|
* latter implementation would require synchronizing the light DOM
|
|
58
58
|
* and shadow DOM slots/slotted children continuously.
|
|
59
59
|
*
|
|
60
|
+
* Note that `::slotted()` only matches *directly* slotted nodes - it
|
|
61
|
+
* does not pierce a slotted `<slot>`. A consumer that wants to forward
|
|
62
|
+
* its own light-DOM content through `<regular-layout>` therefore cannot
|
|
63
|
+
* place a bare `<slot name="X">` as a layout child and expect it to be
|
|
64
|
+
* grid-positioned; instead, wrap the forwarding slot in a concrete
|
|
65
|
+
* `<regular-layout-frame name="X">` (which *is* directly slotted and so
|
|
66
|
+
* matched by the generated `::slotted([name="X"])` rules).
|
|
67
|
+
*
|
|
60
68
|
*/
|
|
61
69
|
export declare class RegularLayout extends HTMLElement {
|
|
62
70
|
private _shadowRoot;
|
|
@@ -67,6 +75,9 @@ export declare class RegularLayout extends HTMLElement {
|
|
|
67
75
|
private _cursor_override;
|
|
68
76
|
private _dimensions?;
|
|
69
77
|
private _physics;
|
|
78
|
+
private _presizeQueue;
|
|
79
|
+
private _overlayController;
|
|
80
|
+
private _maximized?;
|
|
70
81
|
constructor();
|
|
71
82
|
connectedCallback(): void;
|
|
72
83
|
disconnectedCallback(): void;
|
|
@@ -99,7 +110,7 @@ export declare class RegularLayout extends HTMLElement {
|
|
|
99
110
|
* the target, "absolute" positions the panel absolutely. Defaults to
|
|
100
111
|
* "absolute".
|
|
101
112
|
*/
|
|
102
|
-
setOverlayState: (event: PointerEventCoordinates,
|
|
113
|
+
setOverlayState: (event: PointerEventCoordinates, target: LayoutPath, className?: string, mode?: OverlayMode) => Promise<void>;
|
|
103
114
|
/**
|
|
104
115
|
* Clears the overlay state and commits the panel placement.
|
|
105
116
|
*
|
|
@@ -112,7 +123,7 @@ export declare class RegularLayout extends HTMLElement {
|
|
|
112
123
|
* @param mode - Overlay rendering mode that was used, must match the mode
|
|
113
124
|
* passed to `setOverlayState`. Defaults to "absolute".
|
|
114
125
|
*/
|
|
115
|
-
clearOverlayState: (event: PointerEventCoordinates | null,
|
|
126
|
+
clearOverlayState: (event: PointerEventCoordinates | null, target: LayoutPath, className?: string) => Promise<void>;
|
|
116
127
|
/**
|
|
117
128
|
* Inserts a new panel into the layout at a specified path.
|
|
118
129
|
*
|
|
@@ -124,13 +135,27 @@ export declare class RegularLayout extends HTMLElement {
|
|
|
124
135
|
* the new `SplitPanel` _if_ there is an option of orientation (e.g. if
|
|
125
136
|
* the layout had no pre-existing `SplitPanel`)
|
|
126
137
|
*/
|
|
127
|
-
insertPanel: (name: string, path?: LayoutPathTraversal, split?: boolean | Orientation) => void
|
|
138
|
+
insertPanel: (name: string, path?: LayoutPathTraversal, split?: boolean | Orientation) => Promise<void>;
|
|
128
139
|
/**
|
|
129
140
|
* Removes a panel from the layout by name.
|
|
130
141
|
*
|
|
131
142
|
* @param name - Name of the panel to remove
|
|
132
143
|
*/
|
|
133
|
-
removePanel: (name: string) => void
|
|
144
|
+
removePanel: (name: string) => Promise<void>;
|
|
145
|
+
/**
|
|
146
|
+
* Selects a panel by `name`, making it the front-most tab within its
|
|
147
|
+
* containing stack, then dispatches a `<prefix>-select` event with
|
|
148
|
+
* `detail: { name }`.
|
|
149
|
+
*
|
|
150
|
+
* The event fires whenever a tab is selected - including re-selecting the
|
|
151
|
+
* already-active tab, or selecting the sole tab of a single-element stack -
|
|
152
|
+
* so consumers can always map a tab interaction to an "active panel". When
|
|
153
|
+
* the selection actually changes, a `<prefix>-update` event is dispatched
|
|
154
|
+
* first (via {@link restore}).
|
|
155
|
+
*
|
|
156
|
+
* @param name - The name of the panel to select.
|
|
157
|
+
*/
|
|
158
|
+
select: (name: string) => Promise<void>;
|
|
134
159
|
/**
|
|
135
160
|
* Retrieves a panel by name from the layout tree.
|
|
136
161
|
*
|
|
@@ -142,20 +167,52 @@ export declare class RegularLayout extends HTMLElement {
|
|
|
142
167
|
/**
|
|
143
168
|
* Clears the entire layout, unslotting all panels.
|
|
144
169
|
*/
|
|
145
|
-
clear: () => void
|
|
170
|
+
clear: () => Promise<void>;
|
|
171
|
+
/**
|
|
172
|
+
* Maximizes a panel by `name`, rendering it full-size and hiding every
|
|
173
|
+
* other panel. This is transient display state - it is **not** persisted by
|
|
174
|
+
* {@link save}, and any subsequent {@link restore} resets the layout to the
|
|
175
|
+
* minimized (normal, multi-panel) view.
|
|
176
|
+
*
|
|
177
|
+
* Has no effect if `name` is not present in the current layout.
|
|
178
|
+
*
|
|
179
|
+
* @param name - The name of the panel to maximize.
|
|
180
|
+
*/
|
|
181
|
+
maximize: (name: string) => void;
|
|
182
|
+
/**
|
|
183
|
+
* Restores the normal multi-panel view after a {@link maximize}, without
|
|
184
|
+
* altering the layout tree. No-op if no panel is currently maximized.
|
|
185
|
+
*/
|
|
186
|
+
minimize: () => void;
|
|
187
|
+
/**
|
|
188
|
+
* Restores the layout from a saved state synchronously, without
|
|
189
|
+
* dispatching the `regular-layout-before-resize` event.
|
|
190
|
+
*
|
|
191
|
+
* @param layout - The layout tree to restore
|
|
192
|
+
*/
|
|
193
|
+
restoreSync: (layout: Layout, _is_flattened?: boolean) => void;
|
|
146
194
|
/**
|
|
147
195
|
* Restores the layout from a saved state.
|
|
148
196
|
*
|
|
197
|
+
* Before applying, dispatches a cancelable `regular-layout-before-resize`
|
|
198
|
+
* event. If the event is cancelled via `preventDefault()`, the layout
|
|
199
|
+
* update is suspended until {@link resumeResize} is called.
|
|
200
|
+
*
|
|
149
201
|
* @param layout - The layout tree to restore
|
|
150
202
|
*
|
|
151
203
|
* @example
|
|
152
204
|
* ```typescript
|
|
153
205
|
* const layout = document.querySelector('regular-layout');
|
|
154
206
|
* const savedState = JSON.parse(localStorage.getItem('layout'));
|
|
155
|
-
* layout.restore(savedState);
|
|
207
|
+
* await layout.restore(savedState);
|
|
156
208
|
* ```
|
|
157
209
|
*/
|
|
158
|
-
restore: (layout: Layout, _is_flattened?: boolean) => void
|
|
210
|
+
restore: (layout: Layout, _is_flattened?: boolean) => Promise<void>;
|
|
211
|
+
/**
|
|
212
|
+
* Resumes a layout update that was suspended by cancelling the
|
|
213
|
+
* `regular-layout-before-resize` event.
|
|
214
|
+
*/
|
|
215
|
+
resumeResize: () => void;
|
|
159
216
|
/**
|
|
160
217
|
* Serializes the current layout state, which can be restored via `restore`.
|
|
161
218
|
*
|
|
@@ -195,6 +252,15 @@ export declare class RegularLayout extends HTMLElement {
|
|
|
195
252
|
* - box: The layout element's bounding rectangle
|
|
196
253
|
*/
|
|
197
254
|
relativeCoordinates: (event: PointerEventCoordinates, recalculate_bounds?: boolean) => [number, number, DOMRect, CSSStyleDeclaration];
|
|
255
|
+
/**
|
|
256
|
+
* Converts a {@link ViewWindow} (normalized 0–1 coordinates) to a
|
|
257
|
+
* `DOMRect` in screen pixels, accounting for padding and optionally
|
|
258
|
+
* CSS `gap` and child `margin`.
|
|
259
|
+
*
|
|
260
|
+
* @param window - The view window to convert.
|
|
261
|
+
* @returns A `DOMRect` representing the window in screen coordinates.
|
|
262
|
+
*/
|
|
263
|
+
realCoordinates: (window: ViewWindow, child?: HTMLElement) => DOMRect;
|
|
198
264
|
/**
|
|
199
265
|
* Calculates the Euclidean distance in pixels between the current pointer
|
|
200
266
|
* coordinates and a drag target's position within the layout.
|
|
@@ -206,6 +272,7 @@ export declare class RegularLayout extends HTMLElement {
|
|
|
206
272
|
* target.
|
|
207
273
|
*/
|
|
208
274
|
diffCoordinates: (event: PointerEventCoordinates, drag_target: LayoutPath) => number;
|
|
275
|
+
private create_overlay_host;
|
|
209
276
|
private onDblClick;
|
|
210
277
|
private onPointerDown;
|
|
211
278
|
private onPointerMove;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "regular-layout",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.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,18 @@ 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
|
+
|
|
68
|
+
addEventListener(
|
|
69
|
+
name: "regular-layout-select",
|
|
70
|
+
cb: (e: RegularLayoutSelectEvent) => void,
|
|
71
|
+
options?: { signal: AbortSignal },
|
|
72
|
+
): void;
|
|
73
|
+
|
|
62
74
|
removeEventListener(
|
|
63
75
|
name: "regular-layout-update",
|
|
64
76
|
cb: (e: RegularLayoutEvent) => void,
|
|
@@ -68,7 +80,19 @@ declare global {
|
|
|
68
80
|
name: "regular-layout-before-update",
|
|
69
81
|
cb: (e: RegularLayoutEvent) => void,
|
|
70
82
|
): void;
|
|
83
|
+
|
|
84
|
+
removeEventListener(
|
|
85
|
+
name: "regular-layout-before-resize",
|
|
86
|
+
cb: (e: RegularLayoutPresizeEvent) => void,
|
|
87
|
+
): void;
|
|
88
|
+
|
|
89
|
+
removeEventListener(
|
|
90
|
+
name: "regular-layout-select",
|
|
91
|
+
cb: (e: RegularLayoutSelectEvent) => void,
|
|
92
|
+
): void;
|
|
71
93
|
}
|
|
72
94
|
}
|
|
73
95
|
|
|
74
96
|
export type RegularLayoutEvent = CustomEvent<Layout>;
|
|
97
|
+
export type RegularLayoutPresizeEvent = CustomEvent<PresizeDetail>;
|
|
98
|
+
export type RegularLayoutSelectEvent = CustomEvent<{ name: string }>;
|
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
|