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
|
@@ -9,8 +9,8 @@
|
|
|
9
9
|
// ┃ * [Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0). * ┃
|
|
10
10
|
// ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
|
|
11
11
|
|
|
12
|
-
import type { Layout, TabLayout } from "
|
|
13
|
-
import { EMPTY_PANEL } from "
|
|
12
|
+
import type { Layout, TabLayout } from "../core/types.ts";
|
|
13
|
+
import { EMPTY_PANEL } from "../core/types.ts";
|
|
14
14
|
|
|
15
15
|
/**
|
|
16
16
|
* Removes a child panel from the layout tree by its name.
|
|
@@ -0,0 +1,162 @@
|
|
|
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, LayoutPath, OverlayMode } from "../core/types.ts";
|
|
13
|
+
import type { Physics } from "../core/constants.ts";
|
|
14
|
+
import type { PresizeQueue } from "./presize_queue.ts";
|
|
15
|
+
import { calculate_intersection } from "../layout/calculate_intersect.ts";
|
|
16
|
+
import { calculate_edge } from "../layout/calculate_edge.ts";
|
|
17
|
+
import { create_css_grid_layout } from "../layout/generate_grid.ts";
|
|
18
|
+
import { updateOverlaySheet } from "../layout/generate_overlay.ts";
|
|
19
|
+
import { remove_child } from "../layout/remove_child.ts";
|
|
20
|
+
import { insert_child } from "../layout/insert_child.ts";
|
|
21
|
+
|
|
22
|
+
export interface OverlayHost {
|
|
23
|
+
readonly panel: Layout;
|
|
24
|
+
readonly physics: Physics;
|
|
25
|
+
readonly stylesheet: CSSStyleSheet;
|
|
26
|
+
readonly presizeQueue: PresizeQueue;
|
|
27
|
+
relativeCoordinates(
|
|
28
|
+
event: { clientX: number; clientY: number },
|
|
29
|
+
recalculate: boolean,
|
|
30
|
+
): [number, number, DOMRect, CSSStyleDeclaration];
|
|
31
|
+
restore(layout: Layout, isFlattened?: boolean): Promise<void>;
|
|
32
|
+
querySelector(selectors: string): Element | null;
|
|
33
|
+
dispatchEvent(event: Event): boolean;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Manages overlay state during drag-and-drop panel rearrangement.
|
|
38
|
+
*
|
|
39
|
+
* Handles rendering a preview of where a dragged panel would land,
|
|
40
|
+
* and committing or cancelling the placement when the drag ends.
|
|
41
|
+
*/
|
|
42
|
+
export class OverlayController {
|
|
43
|
+
constructor(private _host: OverlayHost) {}
|
|
44
|
+
|
|
45
|
+
async set(
|
|
46
|
+
event: { clientX: number; clientY: number },
|
|
47
|
+
{ slot }: LayoutPath,
|
|
48
|
+
className: string = this._host.physics.OVERLAY_CLASSNAME,
|
|
49
|
+
mode: OverlayMode = this._host.physics.OVERLAY_DEFAULT,
|
|
50
|
+
): Promise<void> {
|
|
51
|
+
const host = this._host;
|
|
52
|
+
const panel = remove_child(host.panel, slot);
|
|
53
|
+
const query = `:scope > [${host.physics.CHILD_ATTRIBUTE_NAME}="${slot}"]`;
|
|
54
|
+
let drag_element = host.querySelector(query);
|
|
55
|
+
if (drag_element) {
|
|
56
|
+
drag_element.classList.add(className);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const [col, row, box, style] = host.relativeCoordinates(event, true);
|
|
60
|
+
let drop_target = calculate_intersection(col, row, panel);
|
|
61
|
+
console.log(row, col, drop_target);
|
|
62
|
+
if (drop_target) {
|
|
63
|
+
drop_target = calculate_edge(
|
|
64
|
+
col,
|
|
65
|
+
row,
|
|
66
|
+
panel,
|
|
67
|
+
slot,
|
|
68
|
+
drop_target,
|
|
69
|
+
box,
|
|
70
|
+
host.physics,
|
|
71
|
+
);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
await host.presizeQueue.run(panel, () => {
|
|
75
|
+
if (mode === "grid" && drop_target) {
|
|
76
|
+
const path: [string, string] = [slot, drop_target?.slot];
|
|
77
|
+
const css = create_css_grid_layout(panel, path, host.physics);
|
|
78
|
+
host.stylesheet.replaceSync(css);
|
|
79
|
+
} else if (mode === "absolute") {
|
|
80
|
+
const grid_css = create_css_grid_layout(panel, undefined, host.physics);
|
|
81
|
+
|
|
82
|
+
while (drag_element?.tagName === "SLOT" && drag_element) {
|
|
83
|
+
drag_element = (
|
|
84
|
+
drag_element as HTMLSlotElement
|
|
85
|
+
).assignedElements()[0];
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
const margin = drag_element
|
|
89
|
+
? getComputedStyle(drag_element)
|
|
90
|
+
: undefined;
|
|
91
|
+
|
|
92
|
+
const overlay_css = updateOverlaySheet(
|
|
93
|
+
slot,
|
|
94
|
+
box,
|
|
95
|
+
style,
|
|
96
|
+
drop_target,
|
|
97
|
+
host.physics,
|
|
98
|
+
margin,
|
|
99
|
+
);
|
|
100
|
+
|
|
101
|
+
host.stylesheet.replaceSync([grid_css, overlay_css].join("\n"));
|
|
102
|
+
}
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
const event_name = `${host.physics.CUSTOM_EVENT_NAME_PREFIX}-before-update`;
|
|
106
|
+
const custom_event = new CustomEvent<Layout>(event_name, {
|
|
107
|
+
detail: panel,
|
|
108
|
+
});
|
|
109
|
+
host.dispatchEvent(custom_event);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
async clear(
|
|
113
|
+
event: { clientX: number; clientY: number } | null,
|
|
114
|
+
{ slot, layout }: LayoutPath,
|
|
115
|
+
className: string = this._host.physics.OVERLAY_CLASSNAME,
|
|
116
|
+
): Promise<void> {
|
|
117
|
+
const host = this._host;
|
|
118
|
+
const panel = remove_child(host.panel, slot);
|
|
119
|
+
const query = `:scope > [${host.physics.CHILD_ATTRIBUTE_NAME}="${slot}"]`;
|
|
120
|
+
const drag_element = host.querySelector(query);
|
|
121
|
+
|
|
122
|
+
if (event === null) {
|
|
123
|
+
await host.restore(layout);
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
const [col, row, box] = host.relativeCoordinates(event, false);
|
|
128
|
+
let drop_target = calculate_intersection(col, row, panel);
|
|
129
|
+
if (drop_target) {
|
|
130
|
+
drop_target = calculate_edge(
|
|
131
|
+
col,
|
|
132
|
+
row,
|
|
133
|
+
panel,
|
|
134
|
+
slot,
|
|
135
|
+
drop_target,
|
|
136
|
+
box,
|
|
137
|
+
host.physics,
|
|
138
|
+
);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
if (drop_target) {
|
|
142
|
+
const orientation = drop_target?.is_edge
|
|
143
|
+
? drop_target.orientation
|
|
144
|
+
: undefined;
|
|
145
|
+
|
|
146
|
+
const new_layout = insert_child(
|
|
147
|
+
panel,
|
|
148
|
+
slot,
|
|
149
|
+
drop_target.path,
|
|
150
|
+
orientation,
|
|
151
|
+
);
|
|
152
|
+
|
|
153
|
+
await host.restore(new_layout);
|
|
154
|
+
} else {
|
|
155
|
+
await host.restore(layout);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
if (drag_element) {
|
|
159
|
+
drag_element.classList.remove(className);
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
}
|
|
@@ -0,0 +1,79 @@
|
|
|
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, PresizeDetail } from "../core/types.ts";
|
|
13
|
+
import { calculate_presize_paths } from "../layout/calculate_presize_paths.ts";
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Manages cancelable pre-resize gating for layout updates.
|
|
17
|
+
*
|
|
18
|
+
* Before each layout change, a cancelable `resize-before` event is dispatched
|
|
19
|
+
* on the target. If the event is cancelled via `preventDefault()`, the update
|
|
20
|
+
* is suspended until {@link resume} is called. Concurrent updates are queued
|
|
21
|
+
* and processed sequentially.
|
|
22
|
+
*/
|
|
23
|
+
export class PresizeQueue {
|
|
24
|
+
#resizing = false;
|
|
25
|
+
#queued: { layout: Layout; fn: () => void } | null = null;
|
|
26
|
+
#pending: (() => void) | null = null;
|
|
27
|
+
|
|
28
|
+
constructor(
|
|
29
|
+
private _target: EventTarget,
|
|
30
|
+
private _eventName: string,
|
|
31
|
+
) {}
|
|
32
|
+
|
|
33
|
+
async run(layout: Layout, fn: () => void): Promise<void> {
|
|
34
|
+
if (this.#resizing) {
|
|
35
|
+
this.#queued = { layout, fn };
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
this.#resizing = true;
|
|
40
|
+
try {
|
|
41
|
+
await this.#dispatchAndMaybeWait(layout, fn);
|
|
42
|
+
while (this.#queued) {
|
|
43
|
+
const { layout: nextLayout, fn: nextFn } = this.#queued;
|
|
44
|
+
this.#queued = null;
|
|
45
|
+
await this.#dispatchAndMaybeWait(nextLayout, nextFn);
|
|
46
|
+
}
|
|
47
|
+
} finally {
|
|
48
|
+
this.#resizing = false;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
resume(): void {
|
|
53
|
+
if (this.#pending) {
|
|
54
|
+
const resolve = this.#pending;
|
|
55
|
+
this.#pending = null;
|
|
56
|
+
resolve();
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
async #dispatchAndMaybeWait(layout: Layout, fn: () => void): Promise<void> {
|
|
61
|
+
const detail: PresizeDetail = {
|
|
62
|
+
calculatePresizePaths: () => calculate_presize_paths(layout),
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
const event = new CustomEvent(this._eventName, {
|
|
66
|
+
cancelable: true,
|
|
67
|
+
detail,
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
const proceed = this._target.dispatchEvent(event);
|
|
71
|
+
if (!proceed) {
|
|
72
|
+
await new Promise<void>((resolve) => {
|
|
73
|
+
this.#pending = resolve;
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
fn();
|
|
78
|
+
}
|
|
79
|
+
}
|
|
@@ -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 { LayoutPath } from "./
|
|
12
|
+
import type { LayoutPath } from "./core/types.ts";
|
|
13
13
|
import type { RegularLayoutEvent } from "./extensions.ts";
|
|
14
14
|
import type { RegularLayout } from "./regular-layout.ts";
|
|
15
15
|
import type { RegularLayoutTab } from "./regular-layout-tab.ts";
|
|
@@ -26,7 +26,7 @@ const CSS = `
|
|
|
26
26
|
|
|
27
27
|
const HTML_TEMPLATE = `
|
|
28
28
|
<div part="titlebar"></div>
|
|
29
|
-
<
|
|
29
|
+
<div part="container"><slot></slot></div>
|
|
30
30
|
`;
|
|
31
31
|
|
|
32
32
|
type DragState = { moved?: boolean; path: LayoutPath };
|
|
@@ -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 { TabLayout } from "./
|
|
12
|
+
import type { TabLayout } from "./core/types.ts";
|
|
13
13
|
import type { RegularLayout } from "./regular-layout.ts";
|
|
14
14
|
|
|
15
15
|
/**
|