regular-layout 0.4.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/dist/extensions.d.ts +7 -0
- package/dist/index.js +9 -6
- package/dist/index.js.map +3 -3
- package/dist/layout/generate_grid.d.ts +9 -0
- package/dist/regular-layout-frame.d.ts +21 -0
- package/dist/regular-layout.d.ts +39 -0
- package/package.json +1 -1
- package/src/extensions.ts +12 -0
- package/src/layout/generate_grid.ts +18 -0
- package/src/model/overlay_controller.ts +0 -1
- package/src/regular-layout-frame.ts +56 -1
- package/src/regular-layout-tab.ts +19 -21
- package/src/regular-layout.ts +86 -1
|
@@ -31,3 +31,12 @@ import type { Layout } from "../core/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;
|
|
@@ -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
|
@@ -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;
|
|
@@ -69,6 +77,7 @@ export declare class RegularLayout extends HTMLElement {
|
|
|
69
77
|
private _physics;
|
|
70
78
|
private _presizeQueue;
|
|
71
79
|
private _overlayController;
|
|
80
|
+
private _maximized?;
|
|
72
81
|
constructor();
|
|
73
82
|
connectedCallback(): void;
|
|
74
83
|
disconnectedCallback(): void;
|
|
@@ -133,6 +142,20 @@ export declare class RegularLayout extends HTMLElement {
|
|
|
133
142
|
* @param name - Name of the panel to remove
|
|
134
143
|
*/
|
|
135
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>;
|
|
136
159
|
/**
|
|
137
160
|
* Retrieves a panel by name from the layout tree.
|
|
138
161
|
*
|
|
@@ -145,6 +168,22 @@ export declare class RegularLayout extends HTMLElement {
|
|
|
145
168
|
* Clears the entire layout, unslotting all panels.
|
|
146
169
|
*/
|
|
147
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;
|
|
148
187
|
/**
|
|
149
188
|
* Restores the layout from a saved state synchronously, without
|
|
150
189
|
* dispatching the `regular-layout-before-resize` event.
|
package/package.json
CHANGED
package/src/extensions.ts
CHANGED
|
@@ -65,6 +65,12 @@ declare global {
|
|
|
65
65
|
options?: { signal: AbortSignal },
|
|
66
66
|
): void;
|
|
67
67
|
|
|
68
|
+
addEventListener(
|
|
69
|
+
name: "regular-layout-select",
|
|
70
|
+
cb: (e: RegularLayoutSelectEvent) => void,
|
|
71
|
+
options?: { signal: AbortSignal },
|
|
72
|
+
): void;
|
|
73
|
+
|
|
68
74
|
removeEventListener(
|
|
69
75
|
name: "regular-layout-update",
|
|
70
76
|
cb: (e: RegularLayoutEvent) => void,
|
|
@@ -79,8 +85,14 @@ declare global {
|
|
|
79
85
|
name: "regular-layout-before-resize",
|
|
80
86
|
cb: (e: RegularLayoutPresizeEvent) => void,
|
|
81
87
|
): void;
|
|
88
|
+
|
|
89
|
+
removeEventListener(
|
|
90
|
+
name: "regular-layout-select",
|
|
91
|
+
cb: (e: RegularLayoutSelectEvent) => void,
|
|
92
|
+
): void;
|
|
82
93
|
}
|
|
83
94
|
}
|
|
84
95
|
|
|
85
96
|
export type RegularLayoutEvent = CustomEvent<Layout>;
|
|
86
97
|
export type RegularLayoutPresizeEvent = CustomEvent<PresizeDetail>;
|
|
98
|
+
export type RegularLayoutSelectEvent = CustomEvent<{ name: string }>;
|
|
@@ -264,3 +264,21 @@ export function create_css_grid_layout(
|
|
|
264
264
|
|
|
265
265
|
return css.join("\n");
|
|
266
266
|
}
|
|
267
|
+
|
|
268
|
+
/**
|
|
269
|
+
* Generates CSS Grid styles that render a single panel maximized to fill the
|
|
270
|
+
* entire layout, hiding all other slotted children.
|
|
271
|
+
*
|
|
272
|
+
* @param name - The name of the panel to maximize.
|
|
273
|
+
* @param physics - Instance constants (defaults to {@link DEFAULT_PHYSICS}).
|
|
274
|
+
* @returns CSS string showing only `name`, full-size.
|
|
275
|
+
*/
|
|
276
|
+
export function create_css_maximize_layout(
|
|
277
|
+
name: string,
|
|
278
|
+
physics: Physics = DEFAULT_PHYSICS,
|
|
279
|
+
): string {
|
|
280
|
+
return [
|
|
281
|
+
host_template("100%", "100%"),
|
|
282
|
+
child_template(physics, name, "1", "1"),
|
|
283
|
+
].join("\n");
|
|
284
|
+
}
|
|
@@ -58,7 +58,6 @@ export class OverlayController {
|
|
|
58
58
|
|
|
59
59
|
const [col, row, box, style] = host.relativeCoordinates(event, true);
|
|
60
60
|
let drop_target = calculate_intersection(col, row, panel);
|
|
61
|
-
console.log(row, col, drop_target);
|
|
62
61
|
if (drop_target) {
|
|
63
62
|
drop_target = calculate_edge(
|
|
64
63
|
col,
|
|
@@ -31,6 +31,20 @@ const HTML_TEMPLATE = `
|
|
|
31
31
|
|
|
32
32
|
type DragState = { moved?: boolean; path: LayoutPath };
|
|
33
33
|
|
|
34
|
+
/**
|
|
35
|
+
* Escapes a string for safe use as a CSS `<string>` token (the `content`
|
|
36
|
+
* fallback), handling backslashes, double-quotes, and newlines.
|
|
37
|
+
*/
|
|
38
|
+
const css_string = (value: string): string =>
|
|
39
|
+
`"${value.replace(/[\\"\n]/g, (c) => (c === "\n" ? "\\A " : `\\${c}`))}"`;
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* The per-slot CSS custom property a consumer sets to override a tab's label,
|
|
43
|
+
* e.g. `regular-layout { --regular-layout-my-panel--title: "My Panel"; }`.
|
|
44
|
+
*/
|
|
45
|
+
const title_variable = (slot: string): string =>
|
|
46
|
+
`--regular-layout-${globalThis.CSS.escape(slot)}--title`;
|
|
47
|
+
|
|
34
48
|
/**
|
|
35
49
|
* A custom element that represents a draggable panel within a
|
|
36
50
|
* `<regular-layout>`.
|
|
@@ -47,8 +61,17 @@ type DragState = { moved?: boolean; path: LayoutPath };
|
|
|
47
61
|
* [named `slot`s](https://developer.mozilla.org/en-US/docs/Web/API/Web_components/Using_templates_and_slots)
|
|
48
62
|
* for wholesale replacement of the underlying Shadow DOM.
|
|
49
63
|
*
|
|
64
|
+
* The `name` attribute identifies the panel within the layout. The tab label
|
|
65
|
+
* defaults to `name`, but can be overridden with pure CSS by setting the
|
|
66
|
+
* `--regular-layout-<name>--title` custom property to a CSS string - the label
|
|
67
|
+
* is rendered via the tab title's `::before` `content`, so the override
|
|
68
|
+
* applies reactively with no JavaScript.
|
|
69
|
+
*
|
|
50
70
|
* @example
|
|
51
71
|
* ```html
|
|
72
|
+
* <style>
|
|
73
|
+
* regular-layout { --regular-layout-panel-1--title: "Panel 1"; }
|
|
74
|
+
* </style>
|
|
52
75
|
* <regular-layout>
|
|
53
76
|
* <regular-layout-frame name="panel-1">
|
|
54
77
|
* <!-- Panel content here -->
|
|
@@ -59,6 +82,7 @@ type DragState = { moved?: boolean; path: LayoutPath };
|
|
|
59
82
|
export class RegularLayoutFrame extends HTMLElement {
|
|
60
83
|
private _shadowRoot!: ShadowRoot;
|
|
61
84
|
private _container_sheet!: CSSStyleSheet;
|
|
85
|
+
private _title_sheet!: CSSStyleSheet;
|
|
62
86
|
private _layout!: RegularLayout;
|
|
63
87
|
private _header!: HTMLElement;
|
|
64
88
|
private _drag: DragState | null = null;
|
|
@@ -72,8 +96,12 @@ export class RegularLayoutFrame extends HTMLElement {
|
|
|
72
96
|
connectedCallback() {
|
|
73
97
|
this._container_sheet ??= new CSSStyleSheet();
|
|
74
98
|
this._container_sheet.replaceSync(CSS);
|
|
99
|
+
this._title_sheet ??= new CSSStyleSheet();
|
|
75
100
|
this._shadowRoot ??= this.attachShadow({ mode: "open" });
|
|
76
|
-
this._shadowRoot.adoptedStyleSheets = [
|
|
101
|
+
this._shadowRoot.adoptedStyleSheets = [
|
|
102
|
+
this._container_sheet,
|
|
103
|
+
this._title_sheet,
|
|
104
|
+
];
|
|
77
105
|
this._shadowRoot.innerHTML = HTML_TEMPLATE;
|
|
78
106
|
this._layout = this.parentElement as RegularLayout;
|
|
79
107
|
this._header = this._shadowRoot.children[0] as HTMLElement;
|
|
@@ -106,6 +134,10 @@ export class RegularLayoutFrame extends HTMLElement {
|
|
|
106
134
|
}
|
|
107
135
|
|
|
108
136
|
private onPointerDown = (event: PointerEvent): void => {
|
|
137
|
+
if (event.button !== 0) {
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
|
|
109
141
|
const elem = event.target as RegularLayoutTab;
|
|
110
142
|
if (elem.part.contains("tab")) {
|
|
111
143
|
const path = this._layout.calculateIntersect(event);
|
|
@@ -184,5 +216,28 @@ export class RegularLayoutFrame extends HTMLElement {
|
|
|
184
216
|
for (let j = this._header.children.length - 1; j >= last_index; j--) {
|
|
185
217
|
this._header.removeChild(this._header.children[j]);
|
|
186
218
|
}
|
|
219
|
+
|
|
220
|
+
this.drawTitles(attr, new_tab_panel.tabs);
|
|
221
|
+
};
|
|
222
|
+
|
|
223
|
+
/**
|
|
224
|
+
* Regenerates this frame's title stylesheet, mapping each tab (by its slot
|
|
225
|
+
* `name`) to its label. The label is rendered via the title's `::before`
|
|
226
|
+
* `content`, reading the slot's `--regular-layout-<slot>--title` override
|
|
227
|
+
* variable with the slot name as the fallback - so a consumer can relabel a
|
|
228
|
+
* tab purely in CSS and the change applies reactively.
|
|
229
|
+
*
|
|
230
|
+
* @param attr - The child name attribute (`CHILD_ATTRIBUTE_NAME`).
|
|
231
|
+
* @param tabs - The slot names rendered in this frame's titlebar.
|
|
232
|
+
*/
|
|
233
|
+
private drawTitles = (attr: string, tabs: string[]) => {
|
|
234
|
+
const css = tabs
|
|
235
|
+
.map(
|
|
236
|
+
(slot) =>
|
|
237
|
+
`regular-layout-tab[${attr}="${slot}"] [part~="title"]::before{content:var(${title_variable(slot)}, ${css_string(slot)})}`,
|
|
238
|
+
)
|
|
239
|
+
.join("\n");
|
|
240
|
+
|
|
241
|
+
this._title_sheet.replaceSync(css);
|
|
187
242
|
};
|
|
188
243
|
}
|
|
@@ -34,6 +34,14 @@ export class RegularLayoutTab extends HTMLElement {
|
|
|
34
34
|
* @param index - The index of this tab within the tab panel.
|
|
35
35
|
*/
|
|
36
36
|
populate = (layout: RegularLayout, tab_panel: TabLayout, index: number) => {
|
|
37
|
+
// Stamp the slot onto the tab so its title can be styled by name (see
|
|
38
|
+
// `RegularLayoutFrame#drawTitles`). Tag-qualified selectors keep this
|
|
39
|
+
// distinct from the panel `name` on `<regular-layout-frame>`.
|
|
40
|
+
this.setAttribute(
|
|
41
|
+
layout.savePhysics().CHILD_ATTRIBUTE_NAME,
|
|
42
|
+
tab_panel.tabs[index],
|
|
43
|
+
);
|
|
44
|
+
|
|
37
45
|
if (this._tab_panel) {
|
|
38
46
|
const tab_changed =
|
|
39
47
|
(index === tab_panel.selected) !==
|
|
@@ -44,9 +52,6 @@ export class RegularLayoutTab extends HTMLElement {
|
|
|
44
52
|
|
|
45
53
|
if (index_changed) {
|
|
46
54
|
const selected = tab_panel.selected === index;
|
|
47
|
-
const slot = tab_panel.tabs[index];
|
|
48
|
-
this.children[0].textContent = slot;
|
|
49
|
-
|
|
50
55
|
if (selected) {
|
|
51
56
|
this.children[1].part.add("active-close");
|
|
52
57
|
this.part.add("active-tab");
|
|
@@ -56,7 +61,6 @@ export class RegularLayoutTab extends HTMLElement {
|
|
|
56
61
|
}
|
|
57
62
|
}
|
|
58
63
|
} else {
|
|
59
|
-
const slot = tab_panel.tabs[index];
|
|
60
64
|
const selected = tab_panel.selected === index;
|
|
61
65
|
const parts = selected ? "active-close close" : "close";
|
|
62
66
|
this.innerHTML = `<div part="title"></div><button part="${parts}"></button>`;
|
|
@@ -67,7 +71,6 @@ export class RegularLayoutTab extends HTMLElement {
|
|
|
67
71
|
}
|
|
68
72
|
|
|
69
73
|
this.addEventListener("pointerdown", this.onTabClick);
|
|
70
|
-
this.children[0].textContent = slot;
|
|
71
74
|
this.children[1].addEventListener("pointerdown", this.onTabClose);
|
|
72
75
|
}
|
|
73
76
|
|
|
@@ -76,28 +79,23 @@ export class RegularLayoutTab extends HTMLElement {
|
|
|
76
79
|
this._index = index;
|
|
77
80
|
};
|
|
78
81
|
|
|
79
|
-
private onTabClose = (
|
|
82
|
+
private onTabClose = (event: Event) => {
|
|
83
|
+
if (event instanceof PointerEvent && event?.button !== 0) {
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
|
|
80
87
|
if (this._tab_panel !== undefined && this._index !== undefined) {
|
|
81
88
|
this._layout?.removePanel(this._tab_panel.tabs[this._index]);
|
|
82
89
|
}
|
|
83
90
|
};
|
|
84
91
|
|
|
85
|
-
private onTabClick = (
|
|
86
|
-
if (
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
this._index !== this._tab_panel.selected
|
|
90
|
-
) {
|
|
91
|
-
const new_layout = this._layout?.save();
|
|
92
|
-
const new_tab_panel = this._layout?.getPanel(
|
|
93
|
-
this._tab_panel.tabs[this._index],
|
|
94
|
-
new_layout,
|
|
95
|
-
);
|
|
92
|
+
private onTabClick = (event: PointerEvent) => {
|
|
93
|
+
if (event.button !== 0) {
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
96
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
this._layout?.restore(new_layout);
|
|
100
|
-
}
|
|
97
|
+
if (this._tab_panel !== undefined && this._index !== undefined) {
|
|
98
|
+
this._layout?.select(this._tab_panel.tabs[this._index]);
|
|
101
99
|
}
|
|
102
100
|
};
|
|
103
101
|
}
|
package/src/regular-layout.ts
CHANGED
|
@@ -17,7 +17,10 @@
|
|
|
17
17
|
*/
|
|
18
18
|
|
|
19
19
|
import { EMPTY_PANEL } from "./core/types.ts";
|
|
20
|
-
import {
|
|
20
|
+
import {
|
|
21
|
+
create_css_grid_layout,
|
|
22
|
+
create_css_maximize_layout,
|
|
23
|
+
} from "./layout/generate_grid.ts";
|
|
21
24
|
import type {
|
|
22
25
|
LayoutPath,
|
|
23
26
|
Layout,
|
|
@@ -106,6 +109,14 @@ export interface PointerEventCoordinates {
|
|
|
106
109
|
* latter implementation would require synchronizing the light DOM
|
|
107
110
|
* and shadow DOM slots/slotted children continuously.
|
|
108
111
|
*
|
|
112
|
+
* Note that `::slotted()` only matches *directly* slotted nodes - it
|
|
113
|
+
* does not pierce a slotted `<slot>`. A consumer that wants to forward
|
|
114
|
+
* its own light-DOM content through `<regular-layout>` therefore cannot
|
|
115
|
+
* place a bare `<slot name="X">` as a layout child and expect it to be
|
|
116
|
+
* grid-positioned; instead, wrap the forwarding slot in a concrete
|
|
117
|
+
* `<regular-layout-frame name="X">` (which *is* directly slotted and so
|
|
118
|
+
* matched by the generated `::slotted([name="X"])` rules).
|
|
119
|
+
*
|
|
109
120
|
*/
|
|
110
121
|
export class RegularLayout extends HTMLElement {
|
|
111
122
|
private _shadowRoot: ShadowRoot;
|
|
@@ -118,6 +129,7 @@ export class RegularLayout extends HTMLElement {
|
|
|
118
129
|
private _physics: Physics;
|
|
119
130
|
private _presizeQueue: PresizeQueue;
|
|
120
131
|
private _overlayController: OverlayController;
|
|
132
|
+
private _maximized?: string;
|
|
121
133
|
|
|
122
134
|
constructor() {
|
|
123
135
|
super();
|
|
@@ -253,6 +265,36 @@ export class RegularLayout extends HTMLElement {
|
|
|
253
265
|
await this.restore(remove_child(this._panel, name));
|
|
254
266
|
};
|
|
255
267
|
|
|
268
|
+
/**
|
|
269
|
+
* Selects a panel by `name`, making it the front-most tab within its
|
|
270
|
+
* containing stack, then dispatches a `<prefix>-select` event with
|
|
271
|
+
* `detail: { name }`.
|
|
272
|
+
*
|
|
273
|
+
* The event fires whenever a tab is selected - including re-selecting the
|
|
274
|
+
* already-active tab, or selecting the sole tab of a single-element stack -
|
|
275
|
+
* so consumers can always map a tab interaction to an "active panel". When
|
|
276
|
+
* the selection actually changes, a `<prefix>-update` event is dispatched
|
|
277
|
+
* first (via {@link restore}).
|
|
278
|
+
*
|
|
279
|
+
* @param name - The name of the panel to select.
|
|
280
|
+
*/
|
|
281
|
+
select = async (name: string) => {
|
|
282
|
+
const layout = this.save();
|
|
283
|
+
const tab_panel = this.getPanel(name, layout);
|
|
284
|
+
if (!tab_panel) {
|
|
285
|
+
return;
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
const index = tab_panel.tabs.indexOf(name);
|
|
289
|
+
if (index !== -1 && tab_panel.selected !== index) {
|
|
290
|
+
tab_panel.selected = index;
|
|
291
|
+
await this.restore(layout);
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
const event_name = `${this._physics.CUSTOM_EVENT_NAME_PREFIX}-select`;
|
|
295
|
+
this.dispatchEvent(new CustomEvent(event_name, { detail: { name } }));
|
|
296
|
+
};
|
|
297
|
+
|
|
256
298
|
/**
|
|
257
299
|
* Retrieves a panel by name from the layout tree.
|
|
258
300
|
*
|
|
@@ -286,6 +328,42 @@ export class RegularLayout extends HTMLElement {
|
|
|
286
328
|
await this.restore(EMPTY_PANEL);
|
|
287
329
|
};
|
|
288
330
|
|
|
331
|
+
/**
|
|
332
|
+
* Maximizes a panel by `name`, rendering it full-size and hiding every
|
|
333
|
+
* other panel. This is transient display state - it is **not** persisted by
|
|
334
|
+
* {@link save}, and any subsequent {@link restore} resets the layout to the
|
|
335
|
+
* minimized (normal, multi-panel) view.
|
|
336
|
+
*
|
|
337
|
+
* Has no effect if `name` is not present in the current layout.
|
|
338
|
+
*
|
|
339
|
+
* @param name - The name of the panel to maximize.
|
|
340
|
+
*/
|
|
341
|
+
maximize = (name: string) => {
|
|
342
|
+
if (!this.getPanel(name)) {
|
|
343
|
+
return;
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
this._maximized = name;
|
|
347
|
+
this._stylesheet.replaceSync(
|
|
348
|
+
create_css_maximize_layout(name, this._physics),
|
|
349
|
+
);
|
|
350
|
+
};
|
|
351
|
+
|
|
352
|
+
/**
|
|
353
|
+
* Restores the normal multi-panel view after a {@link maximize}, without
|
|
354
|
+
* altering the layout tree. No-op if no panel is currently maximized.
|
|
355
|
+
*/
|
|
356
|
+
minimize = () => {
|
|
357
|
+
if (this._maximized === undefined) {
|
|
358
|
+
return;
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
this._maximized = undefined;
|
|
362
|
+
this._stylesheet.replaceSync(
|
|
363
|
+
create_css_grid_layout(this._panel, undefined, this._physics),
|
|
364
|
+
);
|
|
365
|
+
};
|
|
366
|
+
|
|
289
367
|
/**
|
|
290
368
|
* Restores the layout from a saved state synchronously, without
|
|
291
369
|
* dispatching the `regular-layout-before-resize` event.
|
|
@@ -293,6 +371,7 @@ export class RegularLayout extends HTMLElement {
|
|
|
293
371
|
* @param layout - The layout tree to restore
|
|
294
372
|
*/
|
|
295
373
|
restoreSync = (layout: Layout, _is_flattened: boolean = false) => {
|
|
374
|
+
this._maximized = undefined;
|
|
296
375
|
this._panel = !_is_flattened ? flatten(layout) : layout;
|
|
297
376
|
const css = create_css_grid_layout(this._panel, undefined, this._physics);
|
|
298
377
|
this._stylesheet.replaceSync(css);
|
|
@@ -320,6 +399,7 @@ export class RegularLayout extends HTMLElement {
|
|
|
320
399
|
restore = async (layout: Layout, _is_flattened: boolean = false) => {
|
|
321
400
|
const panel = !_is_flattened ? flatten(layout) : layout;
|
|
322
401
|
await this._presizeQueue.run(panel, () => {
|
|
402
|
+
this._maximized = undefined;
|
|
323
403
|
this._panel = panel;
|
|
324
404
|
const css = create_css_grid_layout(this._panel, undefined, this._physics);
|
|
325
405
|
this._stylesheet.replaceSync(css);
|
|
@@ -327,6 +407,7 @@ export class RegularLayout extends HTMLElement {
|
|
|
327
407
|
const event = new CustomEvent<Layout>(event_name, {
|
|
328
408
|
detail: this._panel,
|
|
329
409
|
});
|
|
410
|
+
|
|
330
411
|
this.dispatchEvent(event);
|
|
331
412
|
});
|
|
332
413
|
};
|
|
@@ -528,6 +609,10 @@ export class RegularLayout extends HTMLElement {
|
|
|
528
609
|
};
|
|
529
610
|
|
|
530
611
|
private onPointerDown = (event: PointerEvent) => {
|
|
612
|
+
if (event.button !== 0) {
|
|
613
|
+
return;
|
|
614
|
+
}
|
|
615
|
+
|
|
531
616
|
if (!this._physics.GRID_DIVIDER_CHECK_TARGET || event.target === this) {
|
|
532
617
|
const [col, row, rect] = this.relativeCoordinates(event);
|
|
533
618
|
const size = this._physics.GRID_DIVIDER_SIZE;
|