split-dock 1.0.0 → 1.0.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/README.md +0 -0
- package/package.json +1 -1
- package/src/dock.js +125 -76
- package/src/frame.js +118 -130
- package/src/handles/drag-drop-handler.js +145 -0
- package/src/handles/frame-adjust-handler.js +5 -17
- package/src/index.js +19 -65
- package/src/panel.js +10 -17
- package/src/handles/dock-drop-handler.js +0 -187
- package/src/handles/drop-handler.js +0 -348
- package/src/handles/tab-bar-drop-handler.js +0 -173
package/README.md
CHANGED
|
Binary file
|
package/package.json
CHANGED
package/src/dock.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Panel } from './panel.js';
|
|
2
|
-
import {
|
|
3
|
-
import { generateId } from './index.js';
|
|
2
|
+
import { DragDropHandler } from './handles/drag-drop-handler.js';
|
|
3
|
+
import { generateId, CONFIG } from './index.js';
|
|
4
4
|
|
|
5
5
|
// Dock class - contains panels
|
|
6
6
|
export class Dock {
|
|
@@ -10,76 +10,54 @@ export class Dock {
|
|
|
10
10
|
this.splitDock = parentFrame?.splitDock || null;
|
|
11
11
|
this.panels = [];
|
|
12
12
|
this.activePanel = null;
|
|
13
|
-
this.
|
|
13
|
+
this.abortController = new AbortController();
|
|
14
14
|
|
|
15
15
|
this.element = element || document.createElement('div');
|
|
16
16
|
this.initializeElements();
|
|
17
17
|
|
|
18
|
-
this.
|
|
18
|
+
this.dragDropHandler = new DragDropHandler(this);
|
|
19
19
|
|
|
20
20
|
this.setupEventListeners();
|
|
21
21
|
}
|
|
22
22
|
|
|
23
23
|
initializeElements() {
|
|
24
24
|
const hasExistingDock = this.element.classList.contains('sd-dock');
|
|
25
|
+
if (!hasExistingDock) this.element.className = 'sd-dock';
|
|
25
26
|
|
|
26
|
-
|
|
27
|
-
|
|
27
|
+
this.navbar = this.element.querySelector('.sd-dock-navbar');
|
|
28
|
+
if (!this.navbar) {
|
|
29
|
+
this.navbar = document.createElement('div');
|
|
30
|
+
this.navbar.className = 'sd-dock-navbar';
|
|
31
|
+
this.element.insertBefore(this.navbar, this.element.firstChild);
|
|
28
32
|
}
|
|
29
33
|
|
|
30
|
-
this.
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
this.
|
|
34
|
+
this.content = this.element.querySelector('.sd-dock-content');
|
|
35
|
+
if (!this.content) {
|
|
36
|
+
this.content = document.createElement('div');
|
|
37
|
+
this.content.className = 'sd-dock-content';
|
|
38
|
+
this.element.appendChild(this.content);
|
|
34
39
|
}
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
findOrCreateSubElements() {
|
|
38
|
-
this.navbar = this.element.querySelector('.sd-dock-navbar') || this.createNavbar();
|
|
39
|
-
this.content = this.element.querySelector('.sd-dock-content') || this.createContent();
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
createNavbar() {
|
|
43
|
-
const navbar = document.createElement('div');
|
|
44
|
-
navbar.className = 'sd-dock-navbar';
|
|
45
|
-
this.element.insertBefore(navbar, this.element.firstChild);
|
|
46
|
-
return navbar;
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
createContent() {
|
|
50
|
-
const content = document.createElement('div');
|
|
51
|
-
content.className = 'sd-dock-content';
|
|
52
|
-
this.element.appendChild(content);
|
|
53
|
-
return content;
|
|
40
|
+
|
|
41
|
+
if (hasExistingDock) this.loadPanelsFromHTML();
|
|
54
42
|
}
|
|
55
43
|
|
|
56
44
|
loadPanelsFromHTML() {
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
panelElement.remove();
|
|
66
|
-
const panel = new Panel(titleEl, contentEl);
|
|
67
|
-
this.addPanel(panel);
|
|
45
|
+
this.element.querySelectorAll('.sd-panel').forEach(panelEl => {
|
|
46
|
+
const titleEl = panelEl.querySelector('.sd-panel-title');
|
|
47
|
+
const contentEl = panelEl.querySelector('.sd-panel-content');
|
|
48
|
+
if (titleEl && contentEl) {
|
|
49
|
+
panelEl.remove();
|
|
50
|
+
this.addPanel(new Panel(titleEl, contentEl));
|
|
51
|
+
}
|
|
68
52
|
});
|
|
69
53
|
}
|
|
70
54
|
|
|
71
55
|
setupEventListeners() {
|
|
72
|
-
const
|
|
73
|
-
|
|
74
|
-
this.
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
this.element.addEventListener('drop', dropHandler);
|
|
78
|
-
this.eventListeners.push({ event: 'drop', handler: dropHandler });
|
|
79
|
-
|
|
80
|
-
const dragLeaveHandler = (e) => this.dropHandler.onDragLeave(e);
|
|
81
|
-
this.element.addEventListener('dragleave', dragLeaveHandler);
|
|
82
|
-
this.eventListeners.push({ event: 'dragleave', handler: dragLeaveHandler });
|
|
56
|
+
const signal = this.abortController.signal;
|
|
57
|
+
|
|
58
|
+
this.element.addEventListener('dragover', (e) => this.dragDropHandler.onDragOver(e, this), { signal });
|
|
59
|
+
this.element.addEventListener('drop', (e) => this.dragDropHandler.onDrop(e, this), { signal });
|
|
60
|
+
this.element.addEventListener('dragleave', (e) => this.dragDropHandler.onDragLeave(e, this), { signal });
|
|
83
61
|
}
|
|
84
62
|
|
|
85
63
|
addPanel(panel) {
|
|
@@ -87,11 +65,7 @@ export class Dock {
|
|
|
87
65
|
this.panels.push(panel);
|
|
88
66
|
this.navbar.appendChild(panel.titleElement);
|
|
89
67
|
this.content.appendChild(panel.contentElement);
|
|
90
|
-
|
|
91
|
-
if (this.panels.length === 1) {
|
|
92
|
-
this.setActivePanel(panel);
|
|
93
|
-
}
|
|
94
|
-
|
|
68
|
+
if (this.panels.length === 1) this.setActivePanel(panel);
|
|
95
69
|
return panel;
|
|
96
70
|
}
|
|
97
71
|
|
|
@@ -106,20 +80,15 @@ export class Dock {
|
|
|
106
80
|
if (index === -1) return;
|
|
107
81
|
|
|
108
82
|
this.panels.splice(index, 1);
|
|
109
|
-
|
|
110
|
-
if (!skipCheck) {
|
|
111
|
-
panel.remove();
|
|
112
|
-
}
|
|
83
|
+
if (!skipCheck) panel.remove();
|
|
113
84
|
|
|
114
85
|
if (this.activePanel === panel) {
|
|
115
86
|
this.activePanel = null;
|
|
116
87
|
if (this.panels.length > 0) {
|
|
117
|
-
|
|
118
|
-
this.setActivePanel(this.panels[newIndex]);
|
|
88
|
+
this.setActivePanel(this.panels[Math.min(index, this.panels.length - 1)]);
|
|
119
89
|
}
|
|
120
90
|
}
|
|
121
91
|
|
|
122
|
-
// If dock is empty and has parent, remove it
|
|
123
92
|
if (this.panels.length === 0 && this.parentFrame) {
|
|
124
93
|
this.parentFrame.removeChild(this);
|
|
125
94
|
}
|
|
@@ -131,26 +100,106 @@ export class Dock {
|
|
|
131
100
|
this.activePanel = panel;
|
|
132
101
|
}
|
|
133
102
|
|
|
134
|
-
|
|
135
|
-
this.
|
|
136
|
-
this.
|
|
103
|
+
clearDropIndicators() {
|
|
104
|
+
this.element.classList.remove('drop-center', 'drop-top', 'drop-bottom', 'drop-left', 'drop-right', 'drop-navbar');
|
|
105
|
+
this.navbar.querySelectorAll('.sd-panel-title').forEach(el =>
|
|
106
|
+
el.classList.remove('drop-before', 'drop-after')
|
|
107
|
+
);
|
|
137
108
|
}
|
|
138
109
|
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
110
|
+
showNavbarDropIndicator(e) {
|
|
111
|
+
const titles = Array.from(this.navbar.querySelectorAll('.sd-panel-title:not(.dragging)'));
|
|
112
|
+
if (titles.length === 0) return;
|
|
113
|
+
|
|
114
|
+
const firstRect = titles[0].getBoundingClientRect();
|
|
115
|
+
const lastRect = titles[titles.length - 1].getBoundingClientRect();
|
|
116
|
+
|
|
117
|
+
if (e.clientX < firstRect.left) {
|
|
118
|
+
titles[0].classList.add('drop-before');
|
|
119
|
+
} else if (e.clientX > lastRect.right) {
|
|
120
|
+
titles[titles.length - 1].classList.add('drop-after');
|
|
121
|
+
} else {
|
|
122
|
+
const hovered = titles.find(el => {
|
|
123
|
+
const rect = el.getBoundingClientRect();
|
|
124
|
+
return e.clientX >= rect.left && e.clientX <= rect.right;
|
|
125
|
+
});
|
|
126
|
+
if (hovered) {
|
|
127
|
+
const rect = hovered.getBoundingClientRect();
|
|
128
|
+
hovered.classList.add(e.clientX < rect.left + rect.width / 2 ? 'drop-before' : 'drop-after');
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
getDropTargetPanelInfo(e) {
|
|
134
|
+
const titles = Array.from(this.navbar.querySelectorAll('.sd-panel-title:not(.dragging)'));
|
|
135
|
+
if (titles.length === 0) return null;
|
|
136
|
+
|
|
137
|
+
const firstRect = titles[0].getBoundingClientRect();
|
|
138
|
+
const lastRect = titles[titles.length - 1].getBoundingClientRect();
|
|
139
|
+
|
|
140
|
+
let targetTitle, position;
|
|
141
|
+
if (e.clientX < firstRect.left) {
|
|
142
|
+
targetTitle = titles[0];
|
|
143
|
+
position = 'before';
|
|
144
|
+
} else if (e.clientX > lastRect.right) {
|
|
145
|
+
targetTitle = titles[titles.length - 1];
|
|
146
|
+
position = 'after';
|
|
147
|
+
} else {
|
|
148
|
+
targetTitle = titles.find(el => {
|
|
149
|
+
const rect = el.getBoundingClientRect();
|
|
150
|
+
return e.clientX >= rect.left && e.clientX <= rect.right;
|
|
151
|
+
});
|
|
152
|
+
if (targetTitle) {
|
|
153
|
+
const rect = targetTitle.getBoundingClientRect();
|
|
154
|
+
position = e.clientX < rect.left + rect.width / 2 ? 'before' : 'after';
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
const panel = this.panels.find(p => p.titleElement === targetTitle);
|
|
159
|
+
return panel ? { panel, position } : null;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
reorderPanel(panel, targetPanel, position) {
|
|
163
|
+
const panelIdx = this.panels.indexOf(panel);
|
|
164
|
+
const targetIdx = this.panels.indexOf(targetPanel);
|
|
165
|
+
if (panelIdx === -1 || targetIdx === -1) return;
|
|
166
|
+
|
|
167
|
+
this.panels.splice(panelIdx, 1);
|
|
168
|
+
let newIdx = targetIdx + (position === 'after' ? 1 : 0);
|
|
169
|
+
if (panelIdx < targetIdx) newIdx--;
|
|
170
|
+
this.panels.splice(newIdx, 0, panel);
|
|
171
|
+
|
|
172
|
+
const refNode = position === 'before' ? targetPanel.titleElement : targetPanel.titleElement.nextSibling;
|
|
173
|
+
this.navbar.insertBefore(panel.titleElement, refNode);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
acceptPanelAt(panel, fromDock, targetPanel, position) {
|
|
177
|
+
fromDock.removePanel(panel, true);
|
|
145
178
|
|
|
146
|
-
|
|
179
|
+
const targetIdx = this.panels.indexOf(targetPanel);
|
|
180
|
+
if (targetIdx === -1) {
|
|
181
|
+
this.addPanel(panel);
|
|
182
|
+
return;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
panel.dock = this;
|
|
186
|
+
this.panels.splice(position === 'before' ? targetIdx : targetIdx + 1, 0, panel);
|
|
187
|
+
|
|
188
|
+
const refNode = position === 'before' ? targetPanel.titleElement : targetPanel.titleElement.nextSibling;
|
|
189
|
+
this.navbar.insertBefore(panel.titleElement, refNode);
|
|
190
|
+
this.content.appendChild(panel.contentElement);
|
|
191
|
+
this.setActivePanel(panel);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
destroy() {
|
|
195
|
+
this.abortController.abort();
|
|
147
196
|
this.panels.forEach(panel => panel.destroy());
|
|
148
197
|
this.panels = [];
|
|
149
|
-
|
|
150
|
-
// Clear references
|
|
151
198
|
this.activePanel = null;
|
|
152
199
|
this.parentFrame = null;
|
|
153
200
|
this.splitDock = null;
|
|
154
|
-
this.
|
|
201
|
+
this.dragDropHandler?.destroy();
|
|
202
|
+
this.dragDropHandler = null;
|
|
203
|
+
this.element.remove();
|
|
155
204
|
}
|
|
156
205
|
}
|
package/src/frame.js
CHANGED
|
@@ -1,94 +1,54 @@
|
|
|
1
1
|
import { Dock } from './dock.js';
|
|
2
2
|
import { FrameAdjustHandler } from './handles/frame-adjust-handler.js';
|
|
3
|
-
import { generateId } from './index.js';
|
|
3
|
+
import { generateId, CONFIG } from './index.js';
|
|
4
4
|
|
|
5
5
|
// Frame class - can contain docks or other frames
|
|
6
6
|
export class Frame {
|
|
7
7
|
constructor(element, splitDock) {
|
|
8
8
|
this.id = generateId();
|
|
9
|
-
this.element = element;
|
|
10
9
|
this.splitDock = splitDock;
|
|
11
10
|
this.adjustHandler = new FrameAdjustHandler(this);
|
|
12
11
|
this.parentFrame = null;
|
|
13
|
-
this.children = [];
|
|
14
|
-
this.splitDirection = null;
|
|
12
|
+
this.children = [];
|
|
13
|
+
this.splitDirection = null;
|
|
15
14
|
|
|
16
15
|
if (element) {
|
|
16
|
+
this.element = element;
|
|
17
17
|
this.loadChildrenFromHTML();
|
|
18
18
|
} else {
|
|
19
|
-
this.
|
|
19
|
+
this.element = document.createElement('div');
|
|
20
|
+
this.element.className = 'sd-frame';
|
|
20
21
|
}
|
|
21
22
|
|
|
22
23
|
this.adjustHandler.setupResizeHandles();
|
|
23
24
|
}
|
|
24
25
|
|
|
25
|
-
createElements() {
|
|
26
|
-
this.element = document.createElement('div');
|
|
27
|
-
this.element.className = 'sd-frame';
|
|
28
|
-
}
|
|
29
|
-
|
|
30
26
|
loadChildrenFromHTML() {
|
|
31
|
-
|
|
32
|
-
if (this.element.classList.contains('horizontal')) {
|
|
33
|
-
this.splitDirection = 'horizontal';
|
|
34
|
-
} else if (this.element.classList.contains('vertical')) {
|
|
35
|
-
this.splitDirection = 'vertical';
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
// First check for dock elements (if this is a leaf window)
|
|
39
|
-
const dockElements = Array.from(this.element.querySelectorAll(':scope > .sd-dock'));
|
|
27
|
+
this.detectSplitDirection();
|
|
40
28
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
const childFrame = new Frame(frameWrapper, this.splitDock);
|
|
56
|
-
childFrame.parentFrame = this;
|
|
57
|
-
this.children.push(childFrame);
|
|
58
|
-
});
|
|
59
|
-
} else {
|
|
60
|
-
// Single dock, add directly
|
|
61
|
-
dockElements.forEach(dockElement => {
|
|
62
|
-
const dock = new Dock(dockElement, this);
|
|
63
|
-
this.children.push(dock);
|
|
64
|
-
});
|
|
65
|
-
}
|
|
66
|
-
} else {
|
|
67
|
-
// Check for nested frames
|
|
68
|
-
const windowElements = Array.from(this.element.querySelectorAll(':scope > .sd-frame'));
|
|
69
|
-
|
|
70
|
-
windowElements.forEach(winElement => {
|
|
71
|
-
const childWindow = new Frame(winElement, this.splitDock);
|
|
72
|
-
childWindow.parentFrame = this;
|
|
73
|
-
this.children.push(childWindow);
|
|
74
|
-
});
|
|
75
|
-
}
|
|
29
|
+
this.element.querySelectorAll(':scope > .sd-dock').forEach(el => {
|
|
30
|
+
this.children.push(new Dock(el, this));
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
this.element.querySelectorAll(':scope > .sd-frame').forEach(el => {
|
|
34
|
+
const frame = new Frame(el, this.splitDock);
|
|
35
|
+
frame.parentFrame = this;
|
|
36
|
+
this.children.push(frame);
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
detectSplitDirection() {
|
|
41
|
+
if (this.element.classList.contains('horizontal')) this.splitDirection = 'horizontal';
|
|
42
|
+
else if (this.element.classList.contains('vertical')) this.splitDirection = 'vertical';
|
|
76
43
|
}
|
|
77
44
|
|
|
78
45
|
addChild(child) {
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
child.splitDock = this.splitDock;
|
|
82
|
-
} else if (child instanceof Frame) {
|
|
83
|
-
child.parentFrame = this;
|
|
84
|
-
}
|
|
46
|
+
child.parentFrame = this;
|
|
47
|
+
if (child instanceof Dock) child.splitDock = this.splitDock;
|
|
85
48
|
|
|
86
49
|
this.children.push(child);
|
|
87
50
|
this.element.appendChild(child.element);
|
|
88
|
-
|
|
89
|
-
// Update styles after adding a child
|
|
90
51
|
this.updateStyles();
|
|
91
|
-
|
|
92
52
|
return child;
|
|
93
53
|
}
|
|
94
54
|
|
|
@@ -96,94 +56,122 @@ export class Frame {
|
|
|
96
56
|
const index = this.children.indexOf(child);
|
|
97
57
|
if (index === -1) return;
|
|
98
58
|
|
|
99
|
-
child.remove();
|
|
100
59
|
this.children.splice(index, 1);
|
|
60
|
+
child.element?.remove();
|
|
101
61
|
|
|
102
|
-
|
|
103
|
-
if (this.
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
const remainingChild = this.children[0];
|
|
108
|
-
|
|
109
|
-
// Reset flex style to fill available space
|
|
110
|
-
remainingChild.element.style.flex = '';
|
|
111
|
-
|
|
112
|
-
// If this window has a parent, we should promote the remaining child
|
|
113
|
-
if (this.parentFrame) {
|
|
114
|
-
const parentIndex = this.parentFrame.children.indexOf(this);
|
|
115
|
-
|
|
116
|
-
if (parentIndex !== -1) {
|
|
117
|
-
// Replace this window with the remaining child in parent
|
|
118
|
-
this.parentFrame.children[parentIndex] = remainingChild;
|
|
119
|
-
|
|
120
|
-
// Update parent reference
|
|
121
|
-
if (remainingChild instanceof Dock) {
|
|
122
|
-
remainingChild.parentFrame = this.parentFrame;
|
|
123
|
-
remainingChild.splitDock = this.parentFrame.splitDock;
|
|
124
|
-
} else if (remainingChild instanceof Frame) {
|
|
125
|
-
remainingChild.parentFrame = this.parentFrame;
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
// Move the child element to parent and remove this window
|
|
129
|
-
this.parentFrame.element.insertBefore(remainingChild.element, this.element);
|
|
130
|
-
this.element.remove();
|
|
131
|
-
this.children = [];
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
}
|
|
62
|
+
if (this.children.length === 1) this.promoteChild();
|
|
63
|
+
else if (this.isEmpty()) this.handleEmptyFrame();
|
|
64
|
+
else this.updateStyles();
|
|
65
|
+
}
|
|
135
66
|
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
67
|
+
promoteChild() {
|
|
68
|
+
this.splitDirection = null;
|
|
69
|
+
this.element.classList.remove('horizontal', 'vertical');
|
|
70
|
+
|
|
71
|
+
const child = this.children[0];
|
|
72
|
+
child.element.style.flex = '';
|
|
73
|
+
|
|
74
|
+
if (this.parentFrame) {
|
|
75
|
+
const parentIdx = this.parentFrame.children.indexOf(this);
|
|
76
|
+
if (parentIdx !== -1) {
|
|
77
|
+
this.parentFrame.children[parentIdx] = child;
|
|
78
|
+
child.parentFrame = this.parentFrame;
|
|
79
|
+
if (child instanceof Dock) child.splitDock = this.parentFrame.splitDock;
|
|
80
|
+
|
|
81
|
+
this.parentFrame.element.insertBefore(child.element, this.element);
|
|
142
82
|
this.element.remove();
|
|
83
|
+
this.children = [];
|
|
84
|
+
this.parentFrame.updateStyles();
|
|
143
85
|
}
|
|
144
86
|
}
|
|
145
|
-
|
|
146
|
-
// Update styles after removing a child
|
|
147
|
-
this.updateStyles();
|
|
148
87
|
}
|
|
149
88
|
|
|
150
|
-
|
|
151
|
-
this.
|
|
152
|
-
this.element.remove();
|
|
89
|
+
handleEmptyFrame() {
|
|
90
|
+
if (this.parentFrame) this.parentFrame.removeChild(this);
|
|
91
|
+
else this.element.remove();
|
|
153
92
|
}
|
|
154
93
|
|
|
155
|
-
|
|
156
|
-
// Destroy all children
|
|
157
|
-
this.children.forEach(child => {
|
|
158
|
-
if (child instanceof Dock || child instanceof Frame) {
|
|
159
|
-
child.destroy();
|
|
160
|
-
}
|
|
161
|
-
});
|
|
162
|
-
this.children = [];
|
|
94
|
+
isEmpty() { return this.children.length === 0; }
|
|
163
95
|
|
|
164
|
-
|
|
165
|
-
if (this.adjustHandler) {
|
|
166
|
-
this.adjustHandler.destroy();
|
|
167
|
-
this.adjustHandler = null;
|
|
168
|
-
}
|
|
96
|
+
hasMultipleChildren() { return this.children.length > 1; }
|
|
169
97
|
|
|
170
|
-
|
|
98
|
+
destroy() {
|
|
99
|
+
this.children.forEach(child => child.destroy());
|
|
100
|
+
this.children = [];
|
|
101
|
+
this.adjustHandler?.destroy();
|
|
102
|
+
this.adjustHandler = null;
|
|
171
103
|
this.parentFrame = null;
|
|
172
104
|
this.splitDock = null;
|
|
173
105
|
}
|
|
174
106
|
|
|
175
107
|
updateStyles() {
|
|
176
|
-
|
|
177
|
-
if (this.splitDirection)
|
|
178
|
-
|
|
179
|
-
|
|
108
|
+
this.element.classList.remove('horizontal', 'vertical');
|
|
109
|
+
if (this.splitDirection) this.element.classList.add(this.splitDirection);
|
|
110
|
+
this.adjustHandler.setupResizeHandles();
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
splitWithPanel(dock, direction, panel, fromDock) {
|
|
114
|
+
const currentIndex = this.children.indexOf(dock);
|
|
115
|
+
if (currentIndex === -1) return;
|
|
116
|
+
|
|
117
|
+
const newDock = new Dock(null, this);
|
|
118
|
+
this.applySplit(dock, direction, newDock, currentIndex);
|
|
119
|
+
fromDock.removePanel(panel, true);
|
|
120
|
+
newDock.addPanel(panel);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
applySplit(dock, direction, newDock, currentIndex) {
|
|
124
|
+
const needsVerticalSplit = direction === 'top' || direction === 'bottom';
|
|
125
|
+
const needsHorizontalSplit = direction === 'left' || direction === 'right';
|
|
126
|
+
|
|
127
|
+
if ((needsVerticalSplit && this.splitDirection === 'vertical') ||
|
|
128
|
+
(needsHorizontalSplit && this.splitDirection === 'horizontal')) {
|
|
129
|
+
this.splitIntoExisting(dock, direction, newDock, currentIndex);
|
|
130
|
+
} else if (this.splitDirection === null && this.children.length === 1) {
|
|
131
|
+
this.initializeSplit(direction, newDock, currentIndex);
|
|
180
132
|
} else {
|
|
181
|
-
this.
|
|
133
|
+
this.createNestedSplit(dock, direction, newDock);
|
|
182
134
|
}
|
|
135
|
+
}
|
|
183
136
|
|
|
184
|
-
|
|
185
|
-
|
|
137
|
+
splitIntoExisting(dock, direction, newDock, currentIndex) {
|
|
138
|
+
const insertBefore = direction === 'top' || direction === 'left';
|
|
139
|
+
const insertIdx = insertBefore ? currentIndex : currentIndex + 1;
|
|
140
|
+
|
|
141
|
+
this.children.splice(insertIdx, 0, newDock);
|
|
142
|
+
const refNode = insertBefore ? dock.element : dock.element.nextSibling;
|
|
143
|
+
this.element.insertBefore(newDock.element, refNode);
|
|
144
|
+
|
|
145
|
+
dock.element.style.flex = CONFIG.layout.defaultFlexBasis;
|
|
146
|
+
newDock.element.style.flex = CONFIG.layout.defaultFlexBasis;
|
|
147
|
+
this.updateStyles();
|
|
186
148
|
}
|
|
187
149
|
|
|
150
|
+
initializeSplit(direction, newDock, currentIndex) {
|
|
151
|
+
this.splitDirection = (direction === 'top' || direction === 'bottom') ? 'vertical' : 'horizontal';
|
|
152
|
+
this.splitIntoExisting(this.children[0], direction, newDock, currentIndex);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
createNestedSplit(dock, direction, newDock) {
|
|
156
|
+
const currentIndex = this.children.indexOf(dock);
|
|
157
|
+
const splitDirection = (direction === 'top' || direction === 'bottom') ? 'vertical' : 'horizontal';
|
|
158
|
+
|
|
159
|
+
const wrapper = new Frame(null, this.splitDock);
|
|
160
|
+
wrapper.splitDirection = splitDirection;
|
|
161
|
+
wrapper.parentFrame = this;
|
|
188
162
|
|
|
163
|
+
this.element.insertBefore(wrapper.element, dock.element);
|
|
164
|
+
dock.element.remove();
|
|
165
|
+
this.children[currentIndex] = wrapper;
|
|
166
|
+
dock.parentFrame = wrapper;
|
|
167
|
+
newDock.parentFrame = wrapper;
|
|
168
|
+
|
|
169
|
+
const insertBefore = direction === 'top' || direction === 'left';
|
|
170
|
+
wrapper.children = insertBefore ? [newDock, dock] : [dock, newDock];
|
|
171
|
+
wrapper.children.forEach(child => wrapper.element.appendChild(child.element));
|
|
172
|
+
|
|
173
|
+
dock.element.style.flex = CONFIG.layout.defaultFlexBasis;
|
|
174
|
+
newDock.element.style.flex = CONFIG.layout.defaultFlexBasis;
|
|
175
|
+
wrapper.updateStyles();
|
|
176
|
+
}
|
|
189
177
|
}
|