regular-layout 0.4.0 → 0.6.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.
@@ -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,10 +35,11 @@
26
35
  export declare class RegularLayoutFrame extends HTMLElement {
27
36
  private _shadowRoot;
28
37
  private _container_sheet;
38
+ private _tab_sheet;
29
39
  private _layout;
30
40
  private _header;
41
+ private _default_tab;
31
42
  private _drag;
32
- private _tab_to_index_map;
33
43
  /**
34
44
  * Initializes this elements. Override this method and
35
45
  * `disconnectedCallback` to modify how this subclass renders the Shadow
@@ -46,4 +56,17 @@ export declare class RegularLayoutFrame extends HTMLElement {
46
56
  private onPointerCancel;
47
57
  private onPointerLost;
48
58
  private drawTabs;
59
+ /**
60
+ * Regenerates this frame's tab stylesheet: the shared titlebar grid template
61
+ * (so every frame in the stack aligns), the column this frame's tab occupies,
62
+ * and the tab label. The label renders via the title's `::before` `content`,
63
+ * reading the slot's `--regular-layout-<slot>--title` override variable with
64
+ * the slot name as the fallback - so a consumer can relabel a tab purely in
65
+ * CSS and the change applies reactively.
66
+ *
67
+ * @param slot - This frame's panel name.
68
+ * @param index - This frame's column (zero-based) within the stack.
69
+ * @param count - The number of tabs in the stack.
70
+ */
71
+ private drawTab;
49
72
  }
@@ -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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "regular-layout",
3
- "version": "0.4.0",
3
+ "version": "0.6.0",
4
4
  "description": "A regular CSS `grid` container",
5
5
  "keywords": [],
6
6
  "license": "Apache-2.0",
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 }>;
@@ -18,6 +18,7 @@ interface GridCell {
18
18
  colEnd: number;
19
19
  rowStart: number;
20
20
  rowEnd: number;
21
+ zIndex?: number;
21
22
  }
22
23
 
23
24
  function dedupe_sort(physics: Physics, result: number[], pos: number) {
@@ -102,15 +103,26 @@ function build_cells(
102
103
  ): GridCell[] {
103
104
  if (panel.type === "tab-layout") {
104
105
  const selected = panel.selected ?? 0;
105
- return [
106
- {
107
- child: panel.tabs[selected],
108
- colStart: find_track_index(physics, colPositions, colStart),
109
- colEnd: find_track_index(physics, colPositions, colEnd),
110
- rowStart: find_track_index(physics, rowPositions, rowStart),
111
- rowEnd: find_track_index(physics, rowPositions, rowEnd),
112
- },
113
- ];
106
+ const col_start = find_track_index(physics, colPositions, colStart);
107
+ const col_end = find_track_index(physics, colPositions, colEnd);
108
+ const row_start = find_track_index(physics, rowPositions, rowStart);
109
+ const row_end = find_track_index(physics, rowPositions, rowEnd);
110
+
111
+ // Stacked tabs all occupy the same cell, overlapping. Only the selected
112
+ // frame is lifted (`z-index:1`) so its content sits on top; the rest
113
+ // self-hide their content and just contribute their tab. Keeping the
114
+ // max frame `z-index` at 1 lets the drag overlay sit above any stack
115
+ // with a small constant (2). A single-tab stack
116
+ // emits one placement with no `z-index`, so non-stacked output is
117
+ // unchanged.
118
+ return panel.tabs.map((child, i) => ({
119
+ child,
120
+ colStart: col_start,
121
+ colEnd: col_end,
122
+ rowStart: row_start,
123
+ rowEnd: row_end,
124
+ zIndex: panel.tabs.length > 1 && i === selected ? 1 : undefined,
125
+ }));
114
126
  }
115
127
 
116
128
  const { children, sizes, orientation } = panel;
@@ -162,8 +174,11 @@ const child_template = (
162
174
  slot: string,
163
175
  rowPart: string,
164
176
  colPart: string,
177
+ zIndex?: number,
165
178
  ) =>
166
- `:host ::slotted([${physics.CHILD_ATTRIBUTE_NAME}="${slot}"]){display:flex;grid-column:${colPart};grid-row:${rowPart}}`;
179
+ `:host ::slotted([${physics.CHILD_ATTRIBUTE_NAME}="${slot}"]){display:flex;grid-column:${colPart};grid-row:${rowPart}${
180
+ zIndex === undefined ? "" : `;z-index:${zIndex}`
181
+ }}`;
167
182
 
168
183
  /**
169
184
  * Generates CSS Grid styles to render a layout tree.
@@ -202,9 +217,18 @@ export function create_css_grid_layout(
202
217
  ): string {
203
218
  if (layout.type === "tab-layout") {
204
219
  const selected = layout.selected ?? 0;
220
+ const stacked = layout.tabs.length > 1;
205
221
  return [
206
222
  host_template("100%", "100%"),
207
- child_template(physics, layout.tabs[selected], "1", "1"),
223
+ ...layout.tabs.map((tab, i) =>
224
+ child_template(
225
+ physics,
226
+ tab,
227
+ "1",
228
+ "1",
229
+ stacked && i === selected ? 1 : undefined,
230
+ ),
231
+ ),
208
232
  ].join("\n");
209
233
  }
210
234
 
@@ -253,14 +277,34 @@ export function create_css_grid_layout(
253
277
  for (const cell of cells) {
254
278
  const colPart = formatGridLine(cell.colStart, cell.colEnd);
255
279
  const rowPart = formatGridLine(cell.rowStart, cell.rowEnd);
256
- css.push(child_template(physics, cell.child, rowPart, colPart));
280
+ css.push(
281
+ child_template(physics, cell.child, rowPart, colPart, cell.zIndex),
282
+ );
257
283
  if (cell.child === overlay?.[1]) {
258
284
  css.push(child_template(physics, overlay[0], rowPart, colPart));
259
285
  css.push(
260
- `:host ::slotted([${physics.CHILD_ATTRIBUTE_NAME}=${overlay[0]}]){z-index:1}`,
286
+ `:host ::slotted([${physics.CHILD_ATTRIBUTE_NAME}=${overlay[0]}]){z-index:2}`,
261
287
  );
262
288
  }
263
289
  }
264
290
 
265
291
  return css.join("\n");
266
292
  }
293
+
294
+ /**
295
+ * Generates CSS Grid styles that render a single panel maximized to fill the
296
+ * entire layout, hiding all other slotted children.
297
+ *
298
+ * @param name - The name of the panel to maximize.
299
+ * @param physics - Instance constants (defaults to {@link DEFAULT_PHYSICS}).
300
+ * @returns CSS string showing only `name`, full-size.
301
+ */
302
+ export function create_css_maximize_layout(
303
+ name: string,
304
+ physics: Physics = DEFAULT_PHYSICS,
305
+ ): string {
306
+ return [
307
+ host_template("100%", "100%"),
308
+ child_template(physics, name, "1", "1"),
309
+ ].join("\n");
310
+ }
@@ -69,6 +69,6 @@ export function updateOverlaySheet(
69
69
  margin,
70
70
  );
71
71
 
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;`;
72
+ const css = `display:flex;position:absolute!important;z-index:2;top:${local.y}px;left:${local.x}px;height:${local.height}px;width:${local.width}px;`;
73
73
  return `::slotted([${physics.CHILD_ATTRIBUTE_NAME}="${slot}"]){${css}}`;
74
74
  }
@@ -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,
@@ -15,22 +15,38 @@ import type { RegularLayout } from "./regular-layout.ts";
15
15
  import type { RegularLayoutTab } from "./regular-layout-tab.ts";
16
16
 
17
17
  const CSS = `
18
- :host{box-sizing:border-box;flex-direction:column}
19
- :host::part(titlebar){display:flex;height:24px;user-select:none;overflow:hidden}
20
- :host::part(container){flex:1 1 auto}
21
- :host::part(title){flex:1 1 auto;pointer-events:none}
22
- :host::part(close){align-self:stretch}
23
- :host::slotted{flex:1 1 auto;}
24
- :host regular-layout-tab{width:0px;}
18
+ :host{box-sizing:border-box;flex-direction:column;pointer-events:none}
19
+ [part~="titlebar"]{height:24px;user-select:none;overflow:hidden}
20
+ [part~="container"]{flex:1 1 auto;pointer-events:auto}
21
+ [part~="title"]{flex:1 1 auto;pointer-events:none}
22
+ [part~="close"]{align-self:stretch}
23
+ :host([inactive]) [part~="container"]{display:none}
24
+ .tabs{display:grid;width:100%;height:100%}
25
+ .tabs slot[name="tab"]{display:grid;grid-row:1;pointer-events:auto}
26
+ .tabs regular-layout-tab{display:flex;overflow:hidden}
25
27
  `;
26
28
 
27
29
  const HTML_TEMPLATE = `
28
- <div part="titlebar"></div>
30
+ <div part="titlebar"><div class="tabs"><slot name="tab"><regular-layout-tab></regular-layout-tab></slot></div></div>
29
31
  <div part="container"><slot></slot></div>
30
32
  `;
31
33
 
32
34
  type DragState = { moved?: boolean; path: LayoutPath };
33
35
 
36
+ /**
37
+ * Escapes a string for safe use as a CSS `<string>` token (the `content`
38
+ * fallback), handling backslashes, double-quotes, and newlines.
39
+ */
40
+ const css_string = (value: string): string =>
41
+ `"${value.replace(/[\\"\n]/g, (c) => (c === "\n" ? "\\A " : `\\${c}`))}"`;
42
+
43
+ /**
44
+ * The per-slot CSS custom property a consumer sets to override a tab's label,
45
+ * e.g. `regular-layout { --regular-layout-my-panel--title: "My Panel"; }`.
46
+ */
47
+ const title_variable = (slot: string): string =>
48
+ `--regular-layout-${globalThis.CSS.escape(slot)}--title`;
49
+
34
50
  /**
35
51
  * A custom element that represents a draggable panel within a
36
52
  * `<regular-layout>`.
@@ -47,8 +63,17 @@ type DragState = { moved?: boolean; path: LayoutPath };
47
63
  * [named `slot`s](https://developer.mozilla.org/en-US/docs/Web/API/Web_components/Using_templates_and_slots)
48
64
  * for wholesale replacement of the underlying Shadow DOM.
49
65
  *
66
+ * The `name` attribute identifies the panel within the layout. The tab label
67
+ * defaults to `name`, but can be overridden with pure CSS by setting the
68
+ * `--regular-layout-<name>--title` custom property to a CSS string - the label
69
+ * is rendered via the tab title's `::before` `content`, so the override
70
+ * applies reactively with no JavaScript.
71
+ *
50
72
  * @example
51
73
  * ```html
74
+ * <style>
75
+ * regular-layout { --regular-layout-panel-1--title: "Panel 1"; }
76
+ * </style>
52
77
  * <regular-layout>
53
78
  * <regular-layout-frame name="panel-1">
54
79
  * <!-- Panel content here -->
@@ -59,10 +84,11 @@ type DragState = { moved?: boolean; path: LayoutPath };
59
84
  export class RegularLayoutFrame extends HTMLElement {
60
85
  private _shadowRoot!: ShadowRoot;
61
86
  private _container_sheet!: CSSStyleSheet;
87
+ private _tab_sheet!: CSSStyleSheet;
62
88
  private _layout!: RegularLayout;
63
89
  private _header!: HTMLElement;
90
+ private _default_tab!: RegularLayoutTab;
64
91
  private _drag: DragState | null = null;
65
- private _tab_to_index_map: WeakMap<RegularLayoutTab, number> = new WeakMap();
66
92
 
67
93
  /**
68
94
  * Initializes this elements. Override this method and
@@ -72,11 +98,20 @@ export class RegularLayoutFrame extends HTMLElement {
72
98
  connectedCallback() {
73
99
  this._container_sheet ??= new CSSStyleSheet();
74
100
  this._container_sheet.replaceSync(CSS);
101
+ this._tab_sheet ??= new CSSStyleSheet();
75
102
  this._shadowRoot ??= this.attachShadow({ mode: "open" });
76
- this._shadowRoot.adoptedStyleSheets = [this._container_sheet];
103
+ this._shadowRoot.adoptedStyleSheets = [
104
+ this._container_sheet,
105
+ this._tab_sheet,
106
+ ];
77
107
  this._shadowRoot.innerHTML = HTML_TEMPLATE;
78
108
  this._layout = this.parentElement as RegularLayout;
79
109
  this._header = this._shadowRoot.children[0] as HTMLElement;
110
+ // The built-in tab is the `slot="tab"` fallback; a consumer-supplied
111
+ // `slot="tab"` child replaces it.
112
+ this._default_tab = this._header.querySelector(
113
+ "regular-layout-tab",
114
+ ) as RegularLayoutTab;
80
115
  this._header.addEventListener("pointerdown", this.onPointerDown);
81
116
  this.addEventListener("pointermove", this.onPointerMove);
82
117
  this.addEventListener("pointerup", this.onPointerUp);
@@ -106,11 +141,23 @@ export class RegularLayoutFrame extends HTMLElement {
106
141
  }
107
142
 
108
143
  private onPointerDown = (event: PointerEvent): void => {
144
+ if (event.button !== 0) {
145
+ return;
146
+ }
147
+
109
148
  const elem = event.target as RegularLayoutTab;
110
149
  if (elem.part.contains("tab")) {
150
+ const slot = this.getAttribute(
151
+ this._layout.savePhysics().CHILD_ATTRIBUTE_NAME,
152
+ );
153
+
111
154
  const path = this._layout.calculateIntersect(event);
112
- if (path) {
113
- this._drag = { path };
155
+ if (path && slot) {
156
+ // Drag *this frame's* panel, not whichever panel is front-most at
157
+ // the pointer (which is what `calculateIntersect` resolves to in a
158
+ // stack). The overlapping stack shares geometry, so only the slot
159
+ // needs overriding.
160
+ this._drag = { path: { ...path, slot } };
114
161
  this.setPointerCapture(event.pointerId);
115
162
  event.preventDefault();
116
163
  } else {
@@ -158,31 +205,41 @@ export class RegularLayoutFrame extends HTMLElement {
158
205
  return;
159
206
  }
160
207
 
161
- const new_panel = event.detail;
162
- let new_tab_panel = this._layout.getPanel(slot, new_panel);
163
- if (!new_tab_panel) {
164
- new_tab_panel = {
165
- type: "tab-layout",
166
- tabs: [slot],
167
- selected: 0,
168
- };
208
+ let tab_panel = this._layout.getPanel(slot, event.detail);
209
+ if (!tab_panel) {
210
+ tab_panel = { type: "tab-layout", tabs: [slot], selected: 0 };
169
211
  }
170
212
 
171
- for (let i = 0; i < new_tab_panel.tabs.length; i++) {
172
- if (i >= this._header.children.length) {
173
- const new_tab = document.createElement("regular-layout-tab");
174
- new_tab.populate(this._layout, new_tab_panel, i);
175
- this._header.appendChild(new_tab);
176
- this._tab_to_index_map.set(new_tab, i);
177
- } else {
178
- const tab = this._header.children[i] as RegularLayoutTab;
179
- tab.populate(this._layout, new_tab_panel, i);
180
- }
181
- }
213
+ // Each frame renders only its *own* tab. Frames in a stack overlap (see
214
+ // `create_css_grid_layout`); the tabs tile because every frame derives
215
+ // the same column template and places its tab in its own column. Only
216
+ // the selected frame shows its content; the rest keep just their tab.
217
+ const index = tab_panel.tabs.indexOf(slot);
218
+ const selected = (tab_panel.selected ?? 0) === index;
219
+ this.toggleAttribute("inactive", !selected);
220
+ this._default_tab.populate(this._layout, tab_panel, index);
221
+ this.drawTab(slot, index, tab_panel.tabs.length);
222
+ };
182
223
 
183
- const last_index = new_tab_panel.tabs.length;
184
- for (let j = this._header.children.length - 1; j >= last_index; j--) {
185
- this._header.removeChild(this._header.children[j]);
186
- }
224
+ /**
225
+ * Regenerates this frame's tab stylesheet: the shared titlebar grid template
226
+ * (so every frame in the stack aligns), the column this frame's tab occupies,
227
+ * and the tab label. The label renders via the title's `::before` `content`,
228
+ * reading the slot's `--regular-layout-<slot>--title` override variable with
229
+ * the slot name as the fallback - so a consumer can relabel a tab purely in
230
+ * CSS and the change applies reactively.
231
+ *
232
+ * @param slot - This frame's panel name.
233
+ * @param index - This frame's column (zero-based) within the stack.
234
+ * @param count - The number of tabs in the stack.
235
+ */
236
+ private drawTab = (slot: string, index: number, count: number) => {
237
+ this._tab_sheet.replaceSync(
238
+ [
239
+ `.tabs{grid-template-columns:repeat(${count},var(--rl-tab-width,1fr))}`,
240
+ `.tabs slot[name="tab"]{grid-column:${index + 1}}`,
241
+ `[part~="title"]::before{content:var(${title_variable(slot)}, ${css_string(slot)})}`,
242
+ ].join("\n"),
243
+ );
187
244
  };
188
245
  }
@@ -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 = (_: Event) => {
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 = (_: PointerEvent) => {
86
- if (
87
- this._tab_panel !== undefined &&
88
- this._index !== undefined &&
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
- if (new_tab_panel && new_layout) {
98
- new_tab_panel.selected = this._index;
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
  }