regular-layout 0.2.1 → 0.3.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/index.js +6 -6
- package/dist/index.js.map +4 -4
- package/dist/layout/calculate_edge.d.ts +2 -2
- package/dist/layout/calculate_path.d.ts +12 -0
- package/dist/layout/generate_grid.d.ts +3 -3
- package/dist/layout/generate_overlay.d.ts +1 -1
- package/dist/layout/insert_child.d.ts +2 -2
- package/dist/layout/redistribute_panel_sizes.d.ts +2 -2
- package/dist/layout/types.d.ts +11 -11
- package/dist/regular-layout-frame.d.ts +10 -3
- package/dist/regular-layout.d.ts +58 -14
- package/package.json +4 -2
- package/src/layout/calculate_edge.ts +19 -8
- package/src/layout/calculate_intersect.ts +11 -5
- package/src/layout/calculate_path.ts +53 -0
- package/src/layout/flatten.ts +3 -3
- package/src/layout/generate_grid.ts +8 -8
- package/src/layout/generate_overlay.ts +1 -1
- package/src/layout/insert_child.ts +21 -21
- package/src/layout/redistribute_panel_sizes.ts +20 -16
- package/src/layout/remove_child.ts +11 -11
- package/src/layout/types.ts +13 -13
- package/src/regular-layout-frame.ts +53 -68
- package/src/regular-layout-tab.ts +5 -5
- package/src/regular-layout.ts +146 -105
|
@@ -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 "./types.ts";
|
|
12
|
+
import type { Layout, LayoutPathTraversal } from "./types.ts";
|
|
13
13
|
|
|
14
14
|
/**
|
|
15
15
|
* Inserts a new child panel into the layout tree at a specified location.
|
|
@@ -28,32 +28,32 @@ import type { Layout } from "./types.ts";
|
|
|
28
28
|
export function insert_child(
|
|
29
29
|
panel: Layout,
|
|
30
30
|
child: string,
|
|
31
|
-
path:
|
|
31
|
+
path: LayoutPathTraversal,
|
|
32
32
|
orientation?: "horizontal" | "vertical",
|
|
33
33
|
): Layout {
|
|
34
34
|
const createChildPanel = (childId: string): Layout => ({
|
|
35
|
-
type: "
|
|
36
|
-
|
|
35
|
+
type: "tab-layout",
|
|
36
|
+
tabs: [childId],
|
|
37
37
|
});
|
|
38
38
|
|
|
39
39
|
if (path.length === 0) {
|
|
40
40
|
// Insert at root level
|
|
41
|
-
if (panel.type === "
|
|
42
|
-
// Add to existing
|
|
41
|
+
if (panel.type === "tab-layout") {
|
|
42
|
+
// Add to existing tab-layout as a tab
|
|
43
43
|
return {
|
|
44
|
-
type: "
|
|
45
|
-
|
|
44
|
+
type: "tab-layout",
|
|
45
|
+
tabs: [child, ...panel.tabs],
|
|
46
46
|
};
|
|
47
47
|
} else if (orientation) {
|
|
48
48
|
// When inserting at edge of root, wrap the entire panel in a new split
|
|
49
49
|
return {
|
|
50
|
-
type: "split-
|
|
50
|
+
type: "split-layout",
|
|
51
51
|
orientation: orientation,
|
|
52
52
|
children: [createChildPanel(child), panel],
|
|
53
53
|
sizes: [0.5, 0.5],
|
|
54
54
|
};
|
|
55
55
|
} else {
|
|
56
|
-
// Append to existing split-
|
|
56
|
+
// Append to existing split-layout
|
|
57
57
|
const newChildren = [...panel.children, createChildPanel(child)];
|
|
58
58
|
const newSizes = [...panel.sizes, 1 / (newChildren.length - 1)];
|
|
59
59
|
return {
|
|
@@ -69,8 +69,8 @@ export function insert_child(
|
|
|
69
69
|
|
|
70
70
|
// Special case: when orientation is provided and restPath is empty, handle edge insertion
|
|
71
71
|
if (orientation && restPath.length === 0) {
|
|
72
|
-
// If panel is a split-
|
|
73
|
-
if (panel.type === "split-
|
|
72
|
+
// If panel is a split-layout with the same orientation, insert into its children
|
|
73
|
+
if (panel.type === "split-layout" && panel.orientation === orientation) {
|
|
74
74
|
const newChildren = [...panel.children];
|
|
75
75
|
newChildren.splice(index, 0, createChildPanel(child));
|
|
76
76
|
const newSizes = [...panel.sizes];
|
|
@@ -89,14 +89,14 @@ export function insert_child(
|
|
|
89
89
|
: [panel, createChildPanel(child)];
|
|
90
90
|
|
|
91
91
|
return {
|
|
92
|
-
type: "split-
|
|
92
|
+
type: "split-layout",
|
|
93
93
|
orientation: orientation,
|
|
94
94
|
children,
|
|
95
95
|
sizes: [0.5, 0.5],
|
|
96
96
|
};
|
|
97
97
|
}
|
|
98
98
|
|
|
99
|
-
if (panel.type === "
|
|
99
|
+
if (panel.type === "tab-layout") {
|
|
100
100
|
// Stack into child array only when ALL of these conditions are met:
|
|
101
101
|
// 1. Path has exactly one element (restPath is empty)
|
|
102
102
|
// 2. Orientation was NOT explicitly provided (orientation is undefined)
|
|
@@ -105,19 +105,19 @@ export function insert_child(
|
|
|
105
105
|
restPath.length === 0 &&
|
|
106
106
|
orientation === undefined &&
|
|
107
107
|
index >= 0 &&
|
|
108
|
-
index <= panel.
|
|
108
|
+
index <= panel.tabs.length
|
|
109
109
|
) {
|
|
110
|
-
const newChild = [...panel.
|
|
110
|
+
const newChild = [...panel.tabs];
|
|
111
111
|
newChild.splice(index, 0, child);
|
|
112
112
|
return {
|
|
113
113
|
...panel,
|
|
114
|
-
|
|
114
|
+
tabs: newChild,
|
|
115
115
|
};
|
|
116
116
|
}
|
|
117
117
|
|
|
118
118
|
// Otherwise, wrap in a split panel and recurse
|
|
119
119
|
const newPanel: Layout = {
|
|
120
|
-
type: "split-
|
|
120
|
+
type: "split-layout",
|
|
121
121
|
orientation: orientation || "horizontal",
|
|
122
122
|
children: [panel],
|
|
123
123
|
sizes: [1],
|
|
@@ -130,7 +130,7 @@ export function insert_child(
|
|
|
130
130
|
if (orientation && panel.children[index]) {
|
|
131
131
|
// When inserting at an edge, create a split panel with the new child and existing child
|
|
132
132
|
const newSplitPanel: Layout = {
|
|
133
|
-
type: "split-
|
|
133
|
+
type: "split-layout",
|
|
134
134
|
orientation: orientation,
|
|
135
135
|
children: [createChildPanel(child), panel.children[index]],
|
|
136
136
|
sizes: [0.5, 0.5],
|
|
@@ -159,9 +159,9 @@ export function insert_child(
|
|
|
159
159
|
|
|
160
160
|
const targetChild = panel.children[index];
|
|
161
161
|
|
|
162
|
-
// Determine the orientation to pass down when navigating into a
|
|
162
|
+
// Determine the orientation to pass down when navigating into a tab-layout
|
|
163
163
|
const childOrientation =
|
|
164
|
-
targetChild.type === "
|
|
164
|
+
targetChild.type === "tab-layout" &&
|
|
165
165
|
restPath.length > 0 &&
|
|
166
166
|
orientation !== undefined
|
|
167
167
|
? panel.orientation === "horizontal"
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
// ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
|
|
11
11
|
|
|
12
12
|
import { DEFAULT_PHYSICS } from "./constants.ts";
|
|
13
|
-
import type { Layout } from "./types.ts";
|
|
13
|
+
import type { Layout, LayoutPathTraversal } from "./types.ts";
|
|
14
14
|
|
|
15
15
|
/**
|
|
16
16
|
* Adjusts panel sizes during a drag operation on a divider.
|
|
@@ -31,8 +31,8 @@ import type { Layout } from "./types.ts";
|
|
|
31
31
|
*/
|
|
32
32
|
export function redistribute_panel_sizes(
|
|
33
33
|
panel: Layout,
|
|
34
|
-
path:
|
|
35
|
-
delta: number,
|
|
34
|
+
path: LayoutPathTraversal,
|
|
35
|
+
delta: number | undefined,
|
|
36
36
|
physics = DEFAULT_PHYSICS,
|
|
37
37
|
): Layout {
|
|
38
38
|
// Clone the entire panel structure
|
|
@@ -41,27 +41,31 @@ export function redistribute_panel_sizes(
|
|
|
41
41
|
// Find the orientation of the insertion panel,
|
|
42
42
|
// and scale the delta on the respective axis if aligned.
|
|
43
43
|
let current: Layout = result;
|
|
44
|
-
const deltas = { horizontal: delta, vertical: delta };
|
|
44
|
+
const deltas = { horizontal: delta || 0, vertical: delta || 0 };
|
|
45
45
|
for (let i = 0; i < path.length - 1; i++) {
|
|
46
|
-
if (current.type === "split-
|
|
46
|
+
if (current.type === "split-layout") {
|
|
47
47
|
deltas[current.orientation] /= current.sizes[path[i]];
|
|
48
48
|
current = current.children[path[i]];
|
|
49
49
|
}
|
|
50
50
|
}
|
|
51
51
|
|
|
52
52
|
// Apply the redistribution at the final path index
|
|
53
|
-
if (current.type === "split-
|
|
54
|
-
|
|
55
|
-
|
|
53
|
+
if (current.type === "split-layout") {
|
|
54
|
+
if (delta === undefined) {
|
|
55
|
+
current.sizes = current.sizes.map((_) => 1 / current.sizes.length);
|
|
56
|
+
} else {
|
|
57
|
+
const delta = deltas[current.orientation];
|
|
58
|
+
const index = path[path.length - 1];
|
|
56
59
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
60
|
+
// It would be fun to remove this condition.
|
|
61
|
+
if (index < current.sizes.length - 1) {
|
|
62
|
+
current.sizes = add_and_redistribute(
|
|
63
|
+
physics,
|
|
64
|
+
current.sizes,
|
|
65
|
+
index,
|
|
66
|
+
delta,
|
|
67
|
+
);
|
|
68
|
+
}
|
|
65
69
|
}
|
|
66
70
|
}
|
|
67
71
|
|
|
@@ -25,15 +25,15 @@ import { EMPTY_PANEL } from "./types.ts";
|
|
|
25
25
|
*/
|
|
26
26
|
export function remove_child(panel: Layout, child: string): Layout {
|
|
27
27
|
// If this is a child panel, handle tab removal
|
|
28
|
-
if (panel.type === "
|
|
29
|
-
if (panel.
|
|
30
|
-
const newChild = panel.
|
|
28
|
+
if (panel.type === "tab-layout") {
|
|
29
|
+
if (panel.tabs.includes(child)) {
|
|
30
|
+
const newChild = panel.tabs.filter((c) => c !== child);
|
|
31
31
|
if (newChild.length === 0) {
|
|
32
32
|
return structuredClone(EMPTY_PANEL);
|
|
33
33
|
}
|
|
34
34
|
return {
|
|
35
|
-
type: "
|
|
36
|
-
|
|
35
|
+
type: "tab-layout",
|
|
36
|
+
tabs: newChild,
|
|
37
37
|
};
|
|
38
38
|
}
|
|
39
39
|
|
|
@@ -45,8 +45,8 @@ export function remove_child(panel: Layout, child: string): Layout {
|
|
|
45
45
|
|
|
46
46
|
// Try to remove the child from this split panel's children
|
|
47
47
|
const index = result.children.findIndex((p) => {
|
|
48
|
-
if (p.type === "
|
|
49
|
-
return p.
|
|
48
|
+
if (p.type === "tab-layout") {
|
|
49
|
+
return p.tabs.includes(child);
|
|
50
50
|
}
|
|
51
51
|
|
|
52
52
|
return false;
|
|
@@ -54,7 +54,7 @@ export function remove_child(panel: Layout, child: string): Layout {
|
|
|
54
54
|
|
|
55
55
|
if (index !== -1) {
|
|
56
56
|
const tab_layout = result.children[index] as TabLayout;
|
|
57
|
-
if (tab_layout.
|
|
57
|
+
if (tab_layout.tabs.length === 1) {
|
|
58
58
|
// Found the child at this level - remove it
|
|
59
59
|
const newChildren = result.children.filter((_, i) => i !== index);
|
|
60
60
|
const newSizes = remove_and_redistribute(result.sizes, index);
|
|
@@ -67,10 +67,10 @@ export function remove_child(panel: Layout, child: string): Layout {
|
|
|
67
67
|
result.children = newChildren;
|
|
68
68
|
result.sizes = newSizes;
|
|
69
69
|
} else {
|
|
70
|
-
tab_layout.
|
|
70
|
+
tab_layout.tabs.splice(tab_layout.tabs.indexOf(child), 1);
|
|
71
71
|
if (
|
|
72
72
|
tab_layout.selected &&
|
|
73
|
-
tab_layout.selected >= tab_layout.
|
|
73
|
+
tab_layout.selected >= tab_layout.tabs.length
|
|
74
74
|
) {
|
|
75
75
|
tab_layout.selected--;
|
|
76
76
|
}
|
|
@@ -82,7 +82,7 @@ export function remove_child(panel: Layout, child: string): Layout {
|
|
|
82
82
|
// Child not found at this level - recursively search children
|
|
83
83
|
let modified = false;
|
|
84
84
|
const newChildren = result.children.map((p) => {
|
|
85
|
-
if (p.type === "split-
|
|
85
|
+
if (p.type === "split-layout") {
|
|
86
86
|
const updated = remove_child(p, child);
|
|
87
87
|
if (updated !== p) {
|
|
88
88
|
modified = true;
|
package/src/layout/types.ts
CHANGED
|
@@ -34,7 +34,6 @@ export interface ViewWindow {
|
|
|
34
34
|
col_end: number;
|
|
35
35
|
}
|
|
36
36
|
|
|
37
|
-
|
|
38
37
|
/**
|
|
39
38
|
* A split panel that divides space among multiple child layouts
|
|
40
39
|
* .
|
|
@@ -48,7 +47,7 @@ export interface ViewWindow {
|
|
|
48
47
|
* opposite `orientation` as its parent.
|
|
49
48
|
*/
|
|
50
49
|
export interface SplitLayout {
|
|
51
|
-
type: "split-
|
|
50
|
+
type: "split-layout";
|
|
52
51
|
children: Layout[];
|
|
53
52
|
sizes: number[];
|
|
54
53
|
orientation: Orientation;
|
|
@@ -58,33 +57,34 @@ export interface SplitLayout {
|
|
|
58
57
|
* A leaf panel node that contains a single named child element.
|
|
59
58
|
*/
|
|
60
59
|
export interface TabLayout {
|
|
61
|
-
type: "
|
|
62
|
-
|
|
60
|
+
type: "tab-layout";
|
|
61
|
+
tabs: string[];
|
|
63
62
|
selected?: number;
|
|
64
63
|
}
|
|
65
64
|
|
|
65
|
+
/**
|
|
66
|
+
* Represents a panel location relative to a `Layout` struct.
|
|
67
|
+
*/
|
|
68
|
+
export type LayoutPathTraversal = number[];
|
|
69
|
+
|
|
66
70
|
/**
|
|
67
71
|
* Represents a draggable divider between two panels in the layout.
|
|
68
72
|
*
|
|
69
73
|
* Used for hit detection.
|
|
70
74
|
*/
|
|
71
75
|
export interface LayoutDivider {
|
|
72
|
-
path:
|
|
76
|
+
path: LayoutPathTraversal;
|
|
73
77
|
view_window: ViewWindow;
|
|
74
78
|
type: Orientation;
|
|
75
79
|
}
|
|
76
80
|
|
|
77
81
|
/**
|
|
78
82
|
* Represents a panel location result from hit detection.
|
|
79
|
-
*
|
|
80
|
-
* Contains both the panel identifier and its grid position in relative units.
|
|
81
|
-
* The generic parameter `T` allows DOM-only properties (e.g. `DOMRect`) to be
|
|
82
|
-
* shared in this cross-platform module.
|
|
83
83
|
*/
|
|
84
|
-
export interface LayoutPath
|
|
84
|
+
export interface LayoutPath {
|
|
85
85
|
type: "layout-path";
|
|
86
86
|
slot: string;
|
|
87
|
-
path:
|
|
87
|
+
path: LayoutPathTraversal;
|
|
88
88
|
view_window: ViewWindow;
|
|
89
89
|
column: number;
|
|
90
90
|
row: number;
|
|
@@ -92,14 +92,14 @@ export interface LayoutPath<T = undefined> {
|
|
|
92
92
|
row_offset: number;
|
|
93
93
|
orientation: Orientation;
|
|
94
94
|
is_edge: boolean;
|
|
95
|
-
layout:
|
|
95
|
+
layout: Layout;
|
|
96
96
|
}
|
|
97
97
|
|
|
98
98
|
/**
|
|
99
99
|
* An empty `Layout` with no panels.
|
|
100
100
|
*/
|
|
101
101
|
export const EMPTY_PANEL: Layout = {
|
|
102
|
-
type: "split-
|
|
102
|
+
type: "split-layout",
|
|
103
103
|
orientation: "horizontal",
|
|
104
104
|
sizes: [],
|
|
105
105
|
children: [],
|
|
@@ -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 {
|
|
12
|
+
import type { LayoutPath } from "./layout/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";
|
|
@@ -29,6 +29,8 @@ const HTML_TEMPLATE = `
|
|
|
29
29
|
<slot part="container"></slot>
|
|
30
30
|
`;
|
|
31
31
|
|
|
32
|
+
type DragState = { moved?: boolean; path: LayoutPath };
|
|
33
|
+
|
|
32
34
|
/**
|
|
33
35
|
* A custom element that represents a draggable panel within a
|
|
34
36
|
* `<regular-layout>`.
|
|
@@ -55,29 +57,31 @@ const HTML_TEMPLATE = `
|
|
|
55
57
|
* ```
|
|
56
58
|
*/
|
|
57
59
|
export class RegularLayoutFrame extends HTMLElement {
|
|
58
|
-
private _shadowRoot
|
|
59
|
-
private _container_sheet
|
|
60
|
+
private _shadowRoot!: ShadowRoot;
|
|
61
|
+
private _container_sheet!: CSSStyleSheet;
|
|
60
62
|
private _layout!: RegularLayout;
|
|
61
63
|
private _header!: HTMLElement;
|
|
62
|
-
private
|
|
63
|
-
private _drag_moved: boolean = false;
|
|
64
|
+
private _drag: DragState | null = null;
|
|
64
65
|
private _tab_to_index_map: WeakMap<RegularLayoutTab, number> = new WeakMap();
|
|
65
|
-
constructor() {
|
|
66
|
-
super();
|
|
67
|
-
this._container_sheet = new CSSStyleSheet();
|
|
68
|
-
this._container_sheet.replaceSync(CSS);
|
|
69
|
-
this._shadowRoot = this.attachShadow({ mode: "open" });
|
|
70
|
-
this._shadowRoot.adoptedStyleSheets = [this._container_sheet];
|
|
71
|
-
}
|
|
72
66
|
|
|
67
|
+
/**
|
|
68
|
+
* Initializes this elements. Override this method and
|
|
69
|
+
* `disconnectedCallback` to modify how this subclass renders the Shadow
|
|
70
|
+
* DOM and registers events.
|
|
71
|
+
*/
|
|
73
72
|
connectedCallback() {
|
|
73
|
+
this._container_sheet ??= new CSSStyleSheet();
|
|
74
|
+
this._container_sheet.replaceSync(CSS);
|
|
75
|
+
this._shadowRoot ??= this.attachShadow({ mode: "open" });
|
|
76
|
+
this._shadowRoot.adoptedStyleSheets = [this._container_sheet];
|
|
74
77
|
this._shadowRoot.innerHTML = HTML_TEMPLATE;
|
|
75
78
|
this._layout = this.parentElement as RegularLayout;
|
|
76
79
|
this._header = this._shadowRoot.children[0] as HTMLElement;
|
|
77
80
|
this._header.addEventListener("pointerdown", this.onPointerDown);
|
|
78
|
-
this.
|
|
79
|
-
this.
|
|
80
|
-
this.
|
|
81
|
+
this.addEventListener("pointermove", this.onPointerMove);
|
|
82
|
+
this.addEventListener("pointerup", this.onPointerUp);
|
|
83
|
+
this.addEventListener("pointercancel", this.onPointerCancel);
|
|
84
|
+
this.addEventListener("lostpointercapture", this.onPointerLost);
|
|
81
85
|
this._layout.addEventListener("regular-layout-update", this.drawTabs);
|
|
82
86
|
this._layout.addEventListener(
|
|
83
87
|
"regular-layout-before-update",
|
|
@@ -85,11 +89,15 @@ export class RegularLayoutFrame extends HTMLElement {
|
|
|
85
89
|
);
|
|
86
90
|
}
|
|
87
91
|
|
|
92
|
+
/**
|
|
93
|
+
* Destroys this element.
|
|
94
|
+
*/
|
|
88
95
|
disconnectedCallback() {
|
|
89
96
|
this._header.removeEventListener("pointerdown", this.onPointerDown);
|
|
90
|
-
this.
|
|
91
|
-
this.
|
|
92
|
-
this.
|
|
97
|
+
this.removeEventListener("pointermove", this.onPointerMove);
|
|
98
|
+
this.removeEventListener("pointerup", this.onPointerUp);
|
|
99
|
+
this.removeEventListener("pointercancel", this.onPointerUp);
|
|
100
|
+
this.removeEventListener("lostpointercapture", this.onPointerLost);
|
|
93
101
|
this._layout.removeEventListener("regular-layout-update", this.drawTabs);
|
|
94
102
|
this._layout.removeEventListener(
|
|
95
103
|
"regular-layout-before-update",
|
|
@@ -100,75 +108,52 @@ export class RegularLayoutFrame extends HTMLElement {
|
|
|
100
108
|
private onPointerDown = (event: PointerEvent): void => {
|
|
101
109
|
const elem = event.target as RegularLayoutTab;
|
|
102
110
|
if (elem.part.contains("tab")) {
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
if (this._drag_state) {
|
|
109
|
-
this._header.setPointerCapture(event.pointerId);
|
|
111
|
+
const path = this._layout.calculateIntersect(event);
|
|
112
|
+
if (path) {
|
|
113
|
+
this._drag = { path };
|
|
114
|
+
this.setPointerCapture(event.pointerId);
|
|
110
115
|
event.preventDefault();
|
|
116
|
+
} else {
|
|
117
|
+
this._drag = null;
|
|
111
118
|
}
|
|
112
119
|
}
|
|
113
120
|
};
|
|
114
121
|
|
|
115
122
|
private onPointerMove = (event: PointerEvent): void => {
|
|
116
|
-
if (this.
|
|
123
|
+
if (this._drag) {
|
|
117
124
|
const physics = this._layout.savePhysics();
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
const [current_col, current_row, box] =
|
|
122
|
-
this._layout.relativeCoordinates(event.clientX, event.clientY);
|
|
123
|
-
|
|
124
|
-
const dx = (current_col - this._drag_state.column) * box.width;
|
|
125
|
-
const dy = (current_row - this._drag_state.row) * box.height;
|
|
126
|
-
if (Math.sqrt(dx * dx + dy * dy) <= physics.MIN_DRAG_DISTANCE) {
|
|
125
|
+
if (!this._drag.moved) {
|
|
126
|
+
const diff = this._layout.diffCoordinates(event, this._drag.path);
|
|
127
|
+
if (diff <= physics.MIN_DRAG_DISTANCE) {
|
|
127
128
|
return;
|
|
128
129
|
}
|
|
129
130
|
}
|
|
130
131
|
|
|
131
|
-
this.
|
|
132
|
-
this._layout.setOverlayState(
|
|
133
|
-
event.clientX,
|
|
134
|
-
event.clientY,
|
|
135
|
-
this._drag_state,
|
|
136
|
-
physics.OVERLAY_CLASSNAME,
|
|
137
|
-
);
|
|
132
|
+
this._drag.moved = true;
|
|
133
|
+
this._layout.setOverlayState(event, this._drag.path);
|
|
138
134
|
}
|
|
139
135
|
};
|
|
140
136
|
|
|
141
137
|
private onPointerUp = (event: PointerEvent): void => {
|
|
142
|
-
if (this.
|
|
143
|
-
this._layout.clearOverlayState(
|
|
144
|
-
event.clientX,
|
|
145
|
-
event.clientY,
|
|
146
|
-
this._drag_state,
|
|
147
|
-
);
|
|
138
|
+
if (this._drag?.moved) {
|
|
139
|
+
this._layout.clearOverlayState(event, this._drag.path);
|
|
148
140
|
}
|
|
149
|
-
|
|
150
|
-
// TODO This may be handled by `onPointerLost`, not sure if this is
|
|
151
|
-
// browser-specific behavior ...
|
|
152
|
-
this._header.releasePointerCapture(event.pointerId);
|
|
153
|
-
this._drag_state = null;
|
|
154
|
-
this._drag_moved = false;
|
|
155
141
|
};
|
|
156
142
|
|
|
157
|
-
private
|
|
158
|
-
if (this.
|
|
159
|
-
this._layout.clearOverlayState(
|
|
143
|
+
private onPointerCancel = (_: PointerEvent): void => {
|
|
144
|
+
if (this._drag?.moved) {
|
|
145
|
+
this._layout.clearOverlayState(null, this._drag.path);
|
|
160
146
|
}
|
|
147
|
+
};
|
|
161
148
|
|
|
162
|
-
|
|
163
|
-
this.
|
|
164
|
-
this.
|
|
149
|
+
private onPointerLost = (event: PointerEvent): void => {
|
|
150
|
+
this.releasePointerCapture(event.pointerId);
|
|
151
|
+
this._drag = null;
|
|
165
152
|
};
|
|
166
153
|
|
|
167
154
|
private drawTabs = (event: RegularLayoutEvent) => {
|
|
168
|
-
const
|
|
169
|
-
|
|
170
|
-
);
|
|
171
|
-
|
|
155
|
+
const attr = this._layout.savePhysics().CHILD_ATTRIBUTE_NAME;
|
|
156
|
+
const slot = this.getAttribute(attr);
|
|
172
157
|
if (!slot) {
|
|
173
158
|
return;
|
|
174
159
|
}
|
|
@@ -177,13 +162,13 @@ export class RegularLayoutFrame extends HTMLElement {
|
|
|
177
162
|
let new_tab_panel = this._layout.getPanel(slot, new_panel);
|
|
178
163
|
if (!new_tab_panel) {
|
|
179
164
|
new_tab_panel = {
|
|
180
|
-
type: "
|
|
181
|
-
|
|
165
|
+
type: "tab-layout",
|
|
166
|
+
tabs: [slot],
|
|
182
167
|
selected: 0,
|
|
183
168
|
};
|
|
184
169
|
}
|
|
185
170
|
|
|
186
|
-
for (let i = 0; i < new_tab_panel.
|
|
171
|
+
for (let i = 0; i < new_tab_panel.tabs.length; i++) {
|
|
187
172
|
if (i >= this._header.children.length) {
|
|
188
173
|
const new_tab = document.createElement("regular-layout-tab");
|
|
189
174
|
new_tab.populate(this._layout, new_tab_panel, i);
|
|
@@ -195,7 +180,7 @@ export class RegularLayoutFrame extends HTMLElement {
|
|
|
195
180
|
}
|
|
196
181
|
}
|
|
197
182
|
|
|
198
|
-
const last_index = new_tab_panel.
|
|
183
|
+
const last_index = new_tab_panel.tabs.length;
|
|
199
184
|
for (let j = this._header.children.length - 1; j >= last_index; j--) {
|
|
200
185
|
this._header.removeChild(this._header.children[j]);
|
|
201
186
|
}
|
|
@@ -40,11 +40,11 @@ export class RegularLayoutTab extends HTMLElement {
|
|
|
40
40
|
(index === this._tab_panel?.selected);
|
|
41
41
|
|
|
42
42
|
const index_changed =
|
|
43
|
-
tab_changed || this._tab_panel?.
|
|
43
|
+
tab_changed || this._tab_panel?.tabs[index] !== tab_panel.tabs[index];
|
|
44
44
|
|
|
45
45
|
if (index_changed) {
|
|
46
46
|
const selected = tab_panel.selected === index;
|
|
47
|
-
const slot = tab_panel.
|
|
47
|
+
const slot = tab_panel.tabs[index];
|
|
48
48
|
this.children[0].textContent = slot;
|
|
49
49
|
|
|
50
50
|
if (selected) {
|
|
@@ -56,7 +56,7 @@ export class RegularLayoutTab extends HTMLElement {
|
|
|
56
56
|
}
|
|
57
57
|
}
|
|
58
58
|
} else {
|
|
59
|
-
const slot = tab_panel.
|
|
59
|
+
const slot = tab_panel.tabs[index];
|
|
60
60
|
const selected = tab_panel.selected === index;
|
|
61
61
|
const parts = selected ? "active-close close" : "close";
|
|
62
62
|
this.innerHTML = `<div part="title"></div><button part="${parts}"></button>`;
|
|
@@ -78,7 +78,7 @@ export class RegularLayoutTab extends HTMLElement {
|
|
|
78
78
|
|
|
79
79
|
private onTabClose = (_: Event) => {
|
|
80
80
|
if (this._tab_panel !== undefined && this._index !== undefined) {
|
|
81
|
-
this._layout?.removePanel(this._tab_panel.
|
|
81
|
+
this._layout?.removePanel(this._tab_panel.tabs[this._index]);
|
|
82
82
|
}
|
|
83
83
|
};
|
|
84
84
|
|
|
@@ -90,7 +90,7 @@ export class RegularLayoutTab extends HTMLElement {
|
|
|
90
90
|
) {
|
|
91
91
|
const new_layout = this._layout?.save();
|
|
92
92
|
const new_tab_panel = this._layout?.getPanel(
|
|
93
|
-
this._tab_panel.
|
|
93
|
+
this._tab_panel.tabs[this._index],
|
|
94
94
|
new_layout,
|
|
95
95
|
);
|
|
96
96
|
|