regular-layout 0.6.0 → 0.6.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/core/types.d.ts +14 -0
- package/dist/index.js +8 -8
- package/dist/index.js.map +3 -3
- package/dist/model/presize_queue.d.ts +12 -3
- package/dist/regular-layout.d.ts +14 -2
- package/package.json +1 -1
- package/src/core/types.ts +12 -0
- package/src/model/overlay_controller.ts +42 -29
- package/src/model/presize_queue.ts +33 -8
- package/src/regular-layout-frame.ts +1 -1
- package/src/regular-layout.ts +45 -11
|
@@ -1,4 +1,12 @@
|
|
|
1
|
-
import type { Layout } from "../core/types.ts";
|
|
1
|
+
import type { Layout, LayoutPath } from "../core/types.ts";
|
|
2
|
+
/**
|
|
3
|
+
* The dragged panel's preview geometry during a drag transition - see
|
|
4
|
+
* {@link PresizeDetail.overlay}.
|
|
5
|
+
*/
|
|
6
|
+
export type PresizeOverlay = {
|
|
7
|
+
slot: string;
|
|
8
|
+
path: LayoutPath;
|
|
9
|
+
} | null;
|
|
2
10
|
/**
|
|
3
11
|
* Manages cancelable pre-resize gating for layout updates.
|
|
4
12
|
*
|
|
@@ -11,7 +19,8 @@ export declare class PresizeQueue {
|
|
|
11
19
|
#private;
|
|
12
20
|
private _target;
|
|
13
21
|
private _eventName;
|
|
14
|
-
|
|
15
|
-
|
|
22
|
+
private _onDispatch?;
|
|
23
|
+
constructor(_target: EventTarget, _eventName: string, _onDispatch?: (() => void) | undefined);
|
|
24
|
+
run(layout: Layout, fn: () => void, overlay?: PresizeOverlay): Promise<void>;
|
|
16
25
|
resume(): void;
|
|
17
26
|
}
|
package/dist/regular-layout.d.ts
CHANGED
|
@@ -78,6 +78,7 @@ export declare class RegularLayout extends HTMLElement {
|
|
|
78
78
|
private _presizeQueue;
|
|
79
79
|
private _overlayController;
|
|
80
80
|
private _maximized?;
|
|
81
|
+
private _resize_observer?;
|
|
81
82
|
constructor();
|
|
82
83
|
connectedCallback(): void;
|
|
83
84
|
disconnectedCallback(): void;
|
|
@@ -174,16 +175,27 @@ export declare class RegularLayout extends HTMLElement {
|
|
|
174
175
|
* {@link save}, and any subsequent {@link restore} resets the layout to the
|
|
175
176
|
* minimized (normal, multi-panel) view.
|
|
176
177
|
*
|
|
178
|
+
* Before applying, dispatches a cancelable `regular-layout-before-resize`
|
|
179
|
+
* event (as {@link restore} does) whose paths describe the post-maximize
|
|
180
|
+
* geometry - a single full-window entry for `name`. If the event is
|
|
181
|
+
* cancelled via `preventDefault()`, the update is suspended until
|
|
182
|
+
* {@link resumeResize} is called.
|
|
183
|
+
*
|
|
177
184
|
* Has no effect if `name` is not present in the current layout.
|
|
178
185
|
*
|
|
179
186
|
* @param name - The name of the panel to maximize.
|
|
180
187
|
*/
|
|
181
|
-
maximize: (name: string) => void
|
|
188
|
+
maximize: (name: string) => Promise<void>;
|
|
182
189
|
/**
|
|
183
190
|
* Restores the normal multi-panel view after a {@link maximize}, without
|
|
184
191
|
* altering the layout tree. No-op if no panel is currently maximized.
|
|
192
|
+
*
|
|
193
|
+
* Before applying, dispatches a cancelable `regular-layout-before-resize`
|
|
194
|
+
* event (as {@link restore} does) with paths for the restored multi-panel
|
|
195
|
+
* geometry. If the event is cancelled via `preventDefault()`, the update
|
|
196
|
+
* is suspended until {@link resumeResize} is called.
|
|
185
197
|
*/
|
|
186
|
-
minimize: () => void
|
|
198
|
+
minimize: () => Promise<void>;
|
|
187
199
|
/**
|
|
188
200
|
* Restores the layout from a saved state synchronously, without
|
|
189
201
|
* dispatching the `regular-layout-before-resize` event.
|
package/package.json
CHANGED
package/src/core/types.ts
CHANGED
|
@@ -100,6 +100,18 @@ export interface LayoutPath {
|
|
|
100
100
|
*/
|
|
101
101
|
export interface PresizeDetail {
|
|
102
102
|
calculatePresizePaths(): Record<string, LayoutPath>;
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* When the transition is a drag preview, the dragged panel's name and the
|
|
106
|
+
* {@link LayoutPath} of its preview box. The dragged panel is removed from
|
|
107
|
+
* the layout tree during a drag (so it is absent from
|
|
108
|
+
* {@link PresizeDetail.calculatePresizePaths}), but `path.view_window` here
|
|
109
|
+
* is exactly where the overlay renders it, letting consumers presize the
|
|
110
|
+
* dragged panel like any other. `null` when the pointer is outside every
|
|
111
|
+
* drop target (the dragged panel is hidden), `undefined` for non-drag
|
|
112
|
+
* transitions.
|
|
113
|
+
*/
|
|
114
|
+
overlay?: { slot: string; path: LayoutPath } | null;
|
|
103
115
|
}
|
|
104
116
|
|
|
105
117
|
/**
|
|
@@ -70,36 +70,49 @@ export class OverlayController {
|
|
|
70
70
|
);
|
|
71
71
|
}
|
|
72
72
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
73
|
+
// The dragged panel is removed from `panel` above, so it is absent from
|
|
74
|
+
// the presize paths - `drop_target` carries its preview box instead
|
|
75
|
+
// (null when the pointer is outside every drop target and the dragged
|
|
76
|
+
// panel is hidden).
|
|
77
|
+
const overlay = drop_target ? { slot, path: drop_target } : null;
|
|
78
|
+
await host.presizeQueue.run(
|
|
79
|
+
panel,
|
|
80
|
+
() => {
|
|
81
|
+
if (mode === "grid" && drop_target) {
|
|
82
|
+
const path: [string, string] = [slot, drop_target?.slot];
|
|
83
|
+
const css = create_css_grid_layout(panel, path, host.physics);
|
|
84
|
+
host.stylesheet.replaceSync(css);
|
|
85
|
+
} else if (mode === "absolute") {
|
|
86
|
+
const grid_css = create_css_grid_layout(
|
|
87
|
+
panel,
|
|
88
|
+
undefined,
|
|
89
|
+
host.physics,
|
|
90
|
+
);
|
|
91
|
+
|
|
92
|
+
while (drag_element?.tagName === "SLOT" && drag_element) {
|
|
93
|
+
drag_element = (
|
|
94
|
+
drag_element as HTMLSlotElement
|
|
95
|
+
).assignedElements()[0];
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
const margin = drag_element
|
|
99
|
+
? getComputedStyle(drag_element)
|
|
100
|
+
: undefined;
|
|
101
|
+
|
|
102
|
+
const overlay_css = updateOverlaySheet(
|
|
103
|
+
slot,
|
|
104
|
+
box,
|
|
105
|
+
style,
|
|
106
|
+
drop_target,
|
|
107
|
+
host.physics,
|
|
108
|
+
margin,
|
|
109
|
+
);
|
|
110
|
+
|
|
111
|
+
host.stylesheet.replaceSync([grid_css, overlay_css].join("\n"));
|
|
85
112
|
}
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
: undefined;
|
|
90
|
-
|
|
91
|
-
const overlay_css = updateOverlaySheet(
|
|
92
|
-
slot,
|
|
93
|
-
box,
|
|
94
|
-
style,
|
|
95
|
-
drop_target,
|
|
96
|
-
host.physics,
|
|
97
|
-
margin,
|
|
98
|
-
);
|
|
99
|
-
|
|
100
|
-
host.stylesheet.replaceSync([grid_css, overlay_css].join("\n"));
|
|
101
|
-
}
|
|
102
|
-
});
|
|
113
|
+
},
|
|
114
|
+
overlay,
|
|
115
|
+
);
|
|
103
116
|
|
|
104
117
|
const event_name = `${host.physics.CUSTOM_EVENT_NAME_PREFIX}-before-update`;
|
|
105
118
|
const custom_event = new CustomEvent<Layout>(event_name, {
|
|
@@ -9,9 +9,15 @@
|
|
|
9
9
|
// ┃ * [Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0). * ┃
|
|
10
10
|
// ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
|
|
11
11
|
|
|
12
|
-
import type { Layout, PresizeDetail } from "../core/types.ts";
|
|
12
|
+
import type { Layout, LayoutPath, PresizeDetail } from "../core/types.ts";
|
|
13
13
|
import { calculate_presize_paths } from "../layout/calculate_presize_paths.ts";
|
|
14
14
|
|
|
15
|
+
/**
|
|
16
|
+
* The dragged panel's preview geometry during a drag transition - see
|
|
17
|
+
* {@link PresizeDetail.overlay}.
|
|
18
|
+
*/
|
|
19
|
+
export type PresizeOverlay = { slot: string; path: LayoutPath } | null;
|
|
20
|
+
|
|
15
21
|
/**
|
|
16
22
|
* Manages cancelable pre-resize gating for layout updates.
|
|
17
23
|
*
|
|
@@ -22,27 +28,40 @@ import { calculate_presize_paths } from "../layout/calculate_presize_paths.ts";
|
|
|
22
28
|
*/
|
|
23
29
|
export class PresizeQueue {
|
|
24
30
|
#resizing = false;
|
|
25
|
-
#queued: {
|
|
31
|
+
#queued: {
|
|
32
|
+
layout: Layout;
|
|
33
|
+
fn: () => void;
|
|
34
|
+
overlay?: PresizeOverlay;
|
|
35
|
+
} | null = null;
|
|
26
36
|
#pending: (() => void) | null = null;
|
|
27
37
|
|
|
28
38
|
constructor(
|
|
29
39
|
private _target: EventTarget,
|
|
30
40
|
private _eventName: string,
|
|
41
|
+
private _onDispatch?: () => void,
|
|
31
42
|
) {}
|
|
32
43
|
|
|
33
|
-
async run(
|
|
44
|
+
async run(
|
|
45
|
+
layout: Layout,
|
|
46
|
+
fn: () => void,
|
|
47
|
+
overlay?: PresizeOverlay,
|
|
48
|
+
): Promise<void> {
|
|
34
49
|
if (this.#resizing) {
|
|
35
|
-
this.#queued = { layout, fn };
|
|
50
|
+
this.#queued = { layout, fn, overlay };
|
|
36
51
|
return;
|
|
37
52
|
}
|
|
38
53
|
|
|
39
54
|
this.#resizing = true;
|
|
40
55
|
try {
|
|
41
|
-
await this.#dispatchAndMaybeWait(layout, fn);
|
|
56
|
+
await this.#dispatchAndMaybeWait(layout, fn, overlay);
|
|
42
57
|
while (this.#queued) {
|
|
43
|
-
const {
|
|
58
|
+
const {
|
|
59
|
+
layout: nextLayout,
|
|
60
|
+
fn: nextFn,
|
|
61
|
+
overlay: nextOverlay,
|
|
62
|
+
} = this.#queued;
|
|
44
63
|
this.#queued = null;
|
|
45
|
-
await this.#dispatchAndMaybeWait(nextLayout, nextFn);
|
|
64
|
+
await this.#dispatchAndMaybeWait(nextLayout, nextFn, nextOverlay);
|
|
46
65
|
}
|
|
47
66
|
} finally {
|
|
48
67
|
this.#resizing = false;
|
|
@@ -57,9 +76,15 @@ export class PresizeQueue {
|
|
|
57
76
|
}
|
|
58
77
|
}
|
|
59
78
|
|
|
60
|
-
async #dispatchAndMaybeWait(
|
|
79
|
+
async #dispatchAndMaybeWait(
|
|
80
|
+
layout: Layout,
|
|
81
|
+
fn: () => void,
|
|
82
|
+
overlay?: PresizeOverlay,
|
|
83
|
+
): Promise<void> {
|
|
84
|
+
this._onDispatch?.();
|
|
61
85
|
const detail: PresizeDetail = {
|
|
62
86
|
calculatePresizePaths: () => calculate_presize_paths(layout),
|
|
87
|
+
overlay,
|
|
63
88
|
};
|
|
64
89
|
|
|
65
90
|
const event = new CustomEvent(this._eventName, {
|
|
@@ -27,7 +27,7 @@ const CSS = `
|
|
|
27
27
|
`;
|
|
28
28
|
|
|
29
29
|
const HTML_TEMPLATE = `
|
|
30
|
-
<div part="titlebar"><div class="tabs"><slot name="tab"><regular-layout-tab></regular-layout-tab></slot></div></div>
|
|
30
|
+
<div part="titlebar"><div class="tabs" part="titlebar-track"><slot name="tab"><regular-layout-tab></regular-layout-tab></slot></div></div>
|
|
31
31
|
<div part="container"><slot></slot></div>
|
|
32
32
|
`;
|
|
33
33
|
|
package/src/regular-layout.ts
CHANGED
|
@@ -130,6 +130,7 @@ export class RegularLayout extends HTMLElement {
|
|
|
130
130
|
private _presizeQueue: PresizeQueue;
|
|
131
131
|
private _overlayController: OverlayController;
|
|
132
132
|
private _maximized?: string;
|
|
133
|
+
private _resize_observer?: ResizeObserver;
|
|
133
134
|
|
|
134
135
|
constructor() {
|
|
135
136
|
super();
|
|
@@ -141,7 +142,13 @@ export class RegularLayout extends HTMLElement {
|
|
|
141
142
|
this._cursor_stylesheet = new CSSStyleSheet();
|
|
142
143
|
this._cursor_override = false;
|
|
143
144
|
const event_name = `${this._physics.CUSTOM_EVENT_NAME_PREFIX}-before-resize`;
|
|
144
|
-
|
|
145
|
+
// Refresh the bounds cache once per layout transition, so consumers
|
|
146
|
+
// reading coordinates from a `before-resize` handler always see the
|
|
147
|
+
// element's current position - a `ResizeObserver` alone can't catch
|
|
148
|
+
// position-only moves of the host.
|
|
149
|
+
this._presizeQueue = new PresizeQueue(this, event_name, () => {
|
|
150
|
+
this._dimensions = undefined;
|
|
151
|
+
});
|
|
145
152
|
this._overlayController = new OverlayController(this.create_overlay_host());
|
|
146
153
|
this._shadowRoot.adoptedStyleSheets = [
|
|
147
154
|
this._stylesheet,
|
|
@@ -150,6 +157,11 @@ export class RegularLayout extends HTMLElement {
|
|
|
150
157
|
}
|
|
151
158
|
|
|
152
159
|
connectedCallback() {
|
|
160
|
+
this._resize_observer ??= new ResizeObserver(() => {
|
|
161
|
+
this._dimensions = undefined;
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
this._resize_observer.observe(this);
|
|
153
165
|
this.addEventListener("dblclick", this.onDblClick);
|
|
154
166
|
this.addEventListener("pointerdown", this.onPointerDown);
|
|
155
167
|
this.addEventListener("pointerup", this.onPointerUp);
|
|
@@ -157,6 +169,7 @@ export class RegularLayout extends HTMLElement {
|
|
|
157
169
|
}
|
|
158
170
|
|
|
159
171
|
disconnectedCallback() {
|
|
172
|
+
this._resize_observer?.disconnect();
|
|
160
173
|
this.removeEventListener("dblclick", this.onDblClick);
|
|
161
174
|
this.removeEventListener("pointerdown", this.onPointerDown);
|
|
162
175
|
this.removeEventListener("pointerup", this.onPointerUp);
|
|
@@ -334,34 +347,54 @@ export class RegularLayout extends HTMLElement {
|
|
|
334
347
|
* {@link save}, and any subsequent {@link restore} resets the layout to the
|
|
335
348
|
* minimized (normal, multi-panel) view.
|
|
336
349
|
*
|
|
350
|
+
* Before applying, dispatches a cancelable `regular-layout-before-resize`
|
|
351
|
+
* event (as {@link restore} does) whose paths describe the post-maximize
|
|
352
|
+
* geometry - a single full-window entry for `name`. If the event is
|
|
353
|
+
* cancelled via `preventDefault()`, the update is suspended until
|
|
354
|
+
* {@link resumeResize} is called.
|
|
355
|
+
*
|
|
337
356
|
* Has no effect if `name` is not present in the current layout.
|
|
338
357
|
*
|
|
339
358
|
* @param name - The name of the panel to maximize.
|
|
340
359
|
*/
|
|
341
|
-
maximize = (name: string) => {
|
|
360
|
+
maximize = async (name: string) => {
|
|
342
361
|
if (!this.getPanel(name)) {
|
|
343
362
|
return;
|
|
344
363
|
}
|
|
345
364
|
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
365
|
+
// The post-maximize geometry as a `Layout`: `name` alone, full-window.
|
|
366
|
+
// Panels hidden by the maximize are absent from the presize paths,
|
|
367
|
+
// consistent with `calculate_presize_paths`' visible-panels-only
|
|
368
|
+
// semantics.
|
|
369
|
+
const layout: Layout = { type: "tab-layout", tabs: [name], selected: 0 };
|
|
370
|
+
await this._presizeQueue.run(layout, () => {
|
|
371
|
+
this._maximized = name;
|
|
372
|
+
this._stylesheet.replaceSync(
|
|
373
|
+
create_css_maximize_layout(name, this._physics),
|
|
374
|
+
);
|
|
375
|
+
});
|
|
350
376
|
};
|
|
351
377
|
|
|
352
378
|
/**
|
|
353
379
|
* Restores the normal multi-panel view after a {@link maximize}, without
|
|
354
380
|
* altering the layout tree. No-op if no panel is currently maximized.
|
|
381
|
+
*
|
|
382
|
+
* Before applying, dispatches a cancelable `regular-layout-before-resize`
|
|
383
|
+
* event (as {@link restore} does) with paths for the restored multi-panel
|
|
384
|
+
* geometry. If the event is cancelled via `preventDefault()`, the update
|
|
385
|
+
* is suspended until {@link resumeResize} is called.
|
|
355
386
|
*/
|
|
356
|
-
minimize = () => {
|
|
387
|
+
minimize = async () => {
|
|
357
388
|
if (this._maximized === undefined) {
|
|
358
389
|
return;
|
|
359
390
|
}
|
|
360
391
|
|
|
361
|
-
this.
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
392
|
+
await this._presizeQueue.run(this._panel, () => {
|
|
393
|
+
this._maximized = undefined;
|
|
394
|
+
this._stylesheet.replaceSync(
|
|
395
|
+
create_css_grid_layout(this._panel, undefined, this._physics),
|
|
396
|
+
);
|
|
397
|
+
});
|
|
365
398
|
};
|
|
366
399
|
|
|
367
400
|
/**
|
|
@@ -371,6 +404,7 @@ export class RegularLayout extends HTMLElement {
|
|
|
371
404
|
* @param layout - The layout tree to restore
|
|
372
405
|
*/
|
|
373
406
|
restoreSync = (layout: Layout, _is_flattened: boolean = false) => {
|
|
407
|
+
this._dimensions = undefined;
|
|
374
408
|
this._maximized = undefined;
|
|
375
409
|
this._panel = !_is_flattened ? flatten(layout) : layout;
|
|
376
410
|
const css = create_css_grid_layout(this._panel, undefined, this._physics);
|