dock-spawn-ts 2.513.1 → 2.515.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.
Files changed (106) hide show
  1. package/lib/es5/dock-spawn-ts.js +1 -1
  2. package/lib/js/ContainerType.d.ts +6 -6
  3. package/lib/js/ContainerType.js +7 -7
  4. package/lib/js/Dialog.d.ts +43 -43
  5. package/lib/js/Dialog.js +136 -136
  6. package/lib/js/DockConfig.d.ts +7 -7
  7. package/lib/js/DockConfig.js +8 -8
  8. package/lib/js/DockGraphDeserializer.d.ts +18 -18
  9. package/lib/js/DockGraphDeserializer.js +116 -116
  10. package/lib/js/DockGraphSerializer.d.ts +13 -13
  11. package/lib/js/DockGraphSerializer.js +40 -40
  12. package/lib/js/DockLayoutEngine.d.ts +35 -35
  13. package/lib/js/DockLayoutEngine.js +319 -319
  14. package/lib/js/DockManager.d.ts +153 -153
  15. package/lib/js/DockManager.js +730 -728
  16. package/lib/js/DockManager.js.map +1 -1
  17. package/lib/js/DockManagerContext.d.ts +9 -9
  18. package/lib/js/DockManagerContext.js +9 -9
  19. package/lib/js/DockModel.d.ts +8 -8
  20. package/lib/js/DockModel.js +5 -5
  21. package/lib/js/DockNode.d.ts +15 -15
  22. package/lib/js/DockNode.js +61 -61
  23. package/lib/js/DockWheel.d.ts +40 -40
  24. package/lib/js/DockWheel.js +200 -200
  25. package/lib/js/DockWheelItem.d.ts +15 -15
  26. package/lib/js/DockWheelItem.js +27 -27
  27. package/lib/js/DocumentManagerContainer.d.ts +16 -16
  28. package/lib/js/DocumentManagerContainer.js +28 -28
  29. package/lib/js/DocumentTabPage.d.ts +14 -14
  30. package/lib/js/DocumentTabPage.js +26 -26
  31. package/lib/js/DraggableContainer.d.ts +52 -52
  32. package/lib/js/DraggableContainer.js +183 -183
  33. package/lib/js/EventHandler.d.ts +7 -7
  34. package/lib/js/EventHandler.js +11 -11
  35. package/lib/js/Exports.d.ts +30 -30
  36. package/lib/js/Exports.js +30 -30
  37. package/lib/js/FillDockContainer.d.ts +33 -33
  38. package/lib/js/FillDockContainer.js +69 -69
  39. package/lib/js/HorizontalDockContainer.d.ts +6 -6
  40. package/lib/js/HorizontalDockContainer.js +9 -9
  41. package/lib/js/PanelContainer.d.ts +101 -101
  42. package/lib/js/PanelContainer.js +384 -384
  43. package/lib/js/PanelContainer.js.map +1 -1
  44. package/lib/js/Point.d.ts +5 -5
  45. package/lib/js/Point.js +6 -6
  46. package/lib/js/ResizableContainer.d.ts +55 -55
  47. package/lib/js/ResizableContainer.js +241 -241
  48. package/lib/js/ResizeHandle.d.ts +15 -15
  49. package/lib/js/ResizeHandle.js +48 -48
  50. package/lib/js/SplitterBar.d.ts +35 -35
  51. package/lib/js/SplitterBar.js +149 -149
  52. package/lib/js/SplitterDockContainer.d.ts +35 -35
  53. package/lib/js/SplitterDockContainer.js +65 -65
  54. package/lib/js/SplitterPanel.d.ts +26 -26
  55. package/lib/js/SplitterPanel.js +191 -191
  56. package/lib/js/TabHandle.d.ts +55 -55
  57. package/lib/js/TabHandle.js +261 -260
  58. package/lib/js/TabHandle.js.map +1 -1
  59. package/lib/js/TabHost.d.ts +46 -46
  60. package/lib/js/TabHost.js +223 -223
  61. package/lib/js/TabHost.js.map +1 -1
  62. package/lib/js/TabPage.d.ts +19 -19
  63. package/lib/js/TabPage.js +74 -73
  64. package/lib/js/TabPage.js.map +1 -1
  65. package/lib/js/UndockInitiator.d.ts +31 -31
  66. package/lib/js/UndockInitiator.js +140 -140
  67. package/lib/js/Utils.d.ts +14 -14
  68. package/lib/js/Utils.js +70 -69
  69. package/lib/js/Utils.js.map +1 -1
  70. package/lib/js/VerticalDockContainer.d.ts +6 -6
  71. package/lib/js/VerticalDockContainer.js +9 -9
  72. package/lib/js/enums/PanelType.d.ts +4 -4
  73. package/lib/js/enums/PanelType.js +5 -5
  74. package/lib/js/enums/TabHostDirection.d.ts +6 -6
  75. package/lib/js/enums/TabHostDirection.js +7 -7
  76. package/lib/js/enums/WheelTypes.d.ts +11 -11
  77. package/lib/js/enums/WheelTypes.js +14 -14
  78. package/lib/js/interfaces/IDockContainer.d.ts +25 -25
  79. package/lib/js/interfaces/IDockContainer.js +1 -1
  80. package/lib/js/interfaces/IDockContainerWithSize.d.ts +5 -5
  81. package/lib/js/interfaces/IDockContainerWithSize.js +1 -1
  82. package/lib/js/interfaces/ILayoutEventListener.d.ts +26 -26
  83. package/lib/js/interfaces/ILayoutEventListener.js +1 -1
  84. package/lib/js/interfaces/IMouseOrTouchEvent.d.ts +6 -6
  85. package/lib/js/interfaces/IMouseOrTouchEvent.js +1 -1
  86. package/lib/js/interfaces/INodeInfo.d.ts +7 -7
  87. package/lib/js/interfaces/INodeInfo.js +1 -1
  88. package/lib/js/interfaces/IPanelInfo.d.ts +9 -9
  89. package/lib/js/interfaces/IPanelInfo.js +1 -1
  90. package/lib/js/interfaces/IRectangle.d.ts +6 -6
  91. package/lib/js/interfaces/IRectangle.js +1 -1
  92. package/lib/js/interfaces/ISize.d.ts +4 -4
  93. package/lib/js/interfaces/ISize.js +1 -1
  94. package/lib/js/interfaces/IState.d.ts +10 -10
  95. package/lib/js/interfaces/IState.js +1 -1
  96. package/lib/js/interfaces/IThickness.d.ts +6 -6
  97. package/lib/js/interfaces/IThickness.js +1 -1
  98. package/lib/js/webcomponent/DockSpawnTsWebcomponent.d.ts +31 -30
  99. package/lib/js/webcomponent/DockSpawnTsWebcomponent.js +158 -152
  100. package/lib/js/webcomponent/DockSpawnTsWebcomponent.js.map +1 -1
  101. package/package.json +6 -6
  102. package/src/DockManager.ts +5 -3
  103. package/src/PanelContainer.ts +1 -1
  104. package/src/TabHost.ts +5 -5
  105. package/src/TabPage.ts +4 -3
  106. package/src/webcomponent/DockSpawnTsWebcomponent.ts +6 -0
@@ -1,729 +1,731 @@
1
- import { DockWheel } from "./DockWheel.js";
2
- import { Utils } from "./Utils.js";
3
- import { Point } from "./Point.js";
4
- import { DockManagerContext } from "./DockManagerContext.js";
5
- import { DockNode } from "./DockNode.js";
6
- import { DockLayoutEngine } from "./DockLayoutEngine.js";
7
- import { EventHandler } from "./EventHandler.js";
8
- import { Dialog } from "./Dialog.js";
9
- import { DockGraphSerializer } from "./DockGraphSerializer.js";
10
- import { DockGraphDeserializer } from "./DockGraphDeserializer.js";
11
- import { PanelContainer } from "./PanelContainer.js";
12
- import { DockConfig } from "./DockConfig.js";
13
- import { PanelType } from "./enums/PanelType.js";
14
- /**
15
- * Dock manager manages all the dock panels in a hierarchy, similar to visual studio.
16
- * It owns a Html Div element inside which all panels are docked
17
- * Initially the document manager takes up the central space and acts as the root node
18
- */
19
- export class DockManager {
20
- constructor(element, config) {
21
- if (element === undefined)
22
- throw new Error('Invalid Dock Manager element provided');
23
- this._config = Object.assign(new DockConfig(), config);
24
- this.element = element;
25
- this.context = this.dockWheel = this.layoutEngine = this.mouseMoveHandler = this.touchMoveHandler = undefined;
26
- this.layoutEventListeners = [];
27
- this.defaultDialogPosition = new Point(0, 0);
28
- }
29
- get config() {
30
- return this._config;
31
- }
32
- initialize() {
33
- this.backgroundContext = this.element.children[0];
34
- this.context = new DockManagerContext(this);
35
- let documentNode = new DockNode(this.context.documentManagerView);
36
- this.context.model.rootNode = documentNode;
37
- this.context.model.documentManagerNode = documentNode;
38
- this.context.model.dialogs = [];
39
- this.setRootNode(this.context.model.rootNode);
40
- // Resize the layout
41
- this.resize(this.element.clientWidth, this.element.clientHeight);
42
- this.dockWheel = new DockWheel(this);
43
- this.layoutEngine = new DockLayoutEngine(this);
44
- this._undockEnabled = true;
45
- this.rebuildLayout(this.context.model.rootNode);
46
- this.zIndexCounter = 1001;
47
- this.zIndexTabHost = 1000;
48
- this.zIndexTabHandle = 100;
49
- this.zIndexDialogCounter = 10001;
50
- if (this.backgroundContext != null) {
51
- this.context.model.rootNode.container.tabHost.hostElement
52
- .insertBefore(this.backgroundContext, this.context.model.rootNode.container.tabHost.hostElement.firstChild);
53
- }
54
- this.onKeyPressBound = this.onKeyPress.bind(this);
55
- this.element.addEventListener('keydown', this.onKeyPressBound);
56
- }
57
- onKeyPress(e) {
58
- if (e.key == "Escape" && this.activePanel && !this.activePanel._hideCloseButton) {
59
- if ((this.activePanel.isDialog && this._config.escClosesDialog) || (!this.activePanel.isDialog && this._config.escClosesWindow)) {
60
- let panel = this.activePanel;
61
- this.activePanel = null;
62
- panel.close();
63
- }
64
- }
65
- }
66
- checkXBounds(container, currentMousePosition, previousMousePosition, resizeWest, resizeEast) {
67
- if (this._config.moveOnlyWithinDockConatiner)
68
- return this.checkXBoundsWithinDockContainer(container, currentMousePosition, previousMousePosition, resizeWest, resizeEast);
69
- let dx = Math.floor(currentMousePosition.x - previousMousePosition.x);
70
- let leftBounds = container.offsetLeft + container.offsetWidth + dx < 40; // || (container.offsetLeft + container.offsetWidth + dx - 40 ) < 0;
71
- let rightBounds = container.offsetLeft + dx > (window.innerWidth - 40);
72
- if (leftBounds) {
73
- previousMousePosition.x = currentMousePosition.x;
74
- dx = 0;
75
- let d = 40 - (container.offsetWidth + container.offsetLeft);
76
- if (d > 0)
77
- dx = d;
78
- }
79
- else if (rightBounds) {
80
- previousMousePosition.x = currentMousePosition.x;
81
- dx = 0;
82
- let d = (window.innerWidth - 40) - container.offsetLeft;
83
- if (d > 0)
84
- dx = d;
85
- }
86
- return dx;
87
- }
88
- checkXBoundsWithinDockContainer(container, currentMousePosition, previousMousePosition, resizeWest, resizeEast) {
89
- let dx = currentMousePosition.x - previousMousePosition.x;
90
- let bbOuter = this.element.getBoundingClientRect();
91
- let bbInner = container.getBoundingClientRect();
92
- let leftBounds = dx < 0 && bbInner.left + dx < bbOuter.left && !resizeEast;
93
- let rightBounds = dx > 0 && bbInner.right + dx > bbOuter.right && !resizeWest;
94
- if (leftBounds) {
95
- currentMousePosition.x -= dx;
96
- dx = bbOuter.left - bbInner.left;
97
- currentMousePosition.x -= dx;
98
- }
99
- else if (rightBounds) {
100
- currentMousePosition.x -= dx;
101
- dx = bbOuter.right - bbInner.right;
102
- currentMousePosition.x -= dx;
103
- }
104
- return dx;
105
- }
106
- checkYBounds(container, currentMousePosition, previousMousePosition, resizeNorth, resizeSouth) {
107
- if (this._config.moveOnlyWithinDockConatiner)
108
- return this.checkYBoundsWithinDockContainer(container, currentMousePosition, previousMousePosition, resizeNorth, resizeSouth);
109
- let dy = Math.floor(currentMousePosition.y - previousMousePosition.y);
110
- let topBounds = container.offsetTop + dy < 0;
111
- let bottomBounds = container.offsetTop + dy > (window.innerHeight - 16);
112
- if (topBounds) {
113
- previousMousePosition.y = currentMousePosition.y;
114
- dy = 0;
115
- }
116
- else if (bottomBounds) {
117
- previousMousePosition.y = currentMousePosition.y;
118
- dy = 0;
119
- let d = (window.innerHeight - 25) - container.offsetTop;
120
- if (d > 0)
121
- dy = d;
122
- }
123
- return dy;
124
- }
125
- checkYBoundsWithinDockContainer(container, currentMousePosition, previousMousePosition, resizeNorth, resizeSouth) {
126
- let dy = currentMousePosition.y - previousMousePosition.y;
127
- let bbOuter = this.element.getBoundingClientRect();
128
- let bbInner = container.getBoundingClientRect();
129
- let topBounds = dy < 0 && bbInner.top + dy < bbOuter.top && !resizeSouth;
130
- let bottomBounds = dy > 0 && bbInner.bottom + dy > bbOuter.bottom && !resizeNorth;
131
- if (topBounds) {
132
- currentMousePosition.y -= dy;
133
- dy = bbOuter.top - bbInner.top;
134
- currentMousePosition.y -= dy;
135
- }
136
- else if (bottomBounds) {
137
- currentMousePosition.y -= dy;
138
- dy = bbOuter.bottom - bbInner.bottom;
139
- currentMousePosition.y -= dy;
140
- }
141
- return dy;
142
- }
143
- rebuildLayout(node) {
144
- node.children.forEach((child) => {
145
- this.rebuildLayout(child);
146
- });
147
- node.performLayout(false);
148
- }
149
- invalidate() {
150
- this.resize(this.element.clientWidth, this.element.clientHeight);
151
- }
152
- resize(width, height) {
153
- this.element.style.width = width + 'px';
154
- this.element.style.height = height + 'px';
155
- this.context.model.rootNode.container.resize(width, height);
156
- let offsetX = 0, offsetY = 0;
157
- for (let dialog of this.context.model.dialogs) {
158
- if (dialog.position.x > this.element.clientWidth || dialog.position.y > this.element.clientHeight) {
159
- if (offsetX > this.element.clientWidth || offsetY > this.element.clientHeight)
160
- offsetX = 0, offsetY = 0;
161
- dialog.setPosition(100 + offsetX, 100 + offsetY);
162
- offsetX += 100;
163
- offsetY += 100;
164
- }
165
- }
166
- }
167
- /**
168
- * Reset the dock model . This happens when the state is loaded from json
169
- */
170
- setModel(model) {
171
- Utils.removeNode(this.context.documentManagerView.containerElement);
172
- this.context.model = model;
173
- this.setRootNode(model.rootNode);
174
- this.rebuildLayout(model.rootNode);
175
- this.loadResize(model.rootNode);
176
- // this.invalidate();
177
- }
178
- loadResize(node) {
179
- node.children.reverse().forEach((child) => {
180
- this.loadResize(child);
181
- node.container.setActiveChild(child.container);
182
- });
183
- node.children.reverse();
184
- let container = node.container;
185
- node.container.resize(container.state.width, container.state.height);
186
- // node.performLayout();
187
- }
188
- setRootNode(node) {
189
- // if (this.context.model.rootNode)
190
- // {
191
- // // detach it from the dock manager's base element
192
- // context.model.rootNode.detachFromParent();
193
- // }
194
- // Attach the new node to the dock manager's base element and set as root node
195
- node.detachFromParent();
196
- this.context.model.rootNode = node;
197
- this.element.appendChild(node.container.containerElement);
198
- }
199
- _onDialogDragStarted(sender, e) {
200
- this.dockWheel.activeNode = this._findNodeOnPoint(e.clientX, e.clientY);
201
- this.dockWheel.activeDialog = sender;
202
- if (sender.noDocking == null || sender.noDocking !== true)
203
- this.dockWheel.showWheel();
204
- if (this.mouseMoveHandler) {
205
- this.mouseMoveHandler.cancel();
206
- delete this.mouseMoveHandler;
207
- }
208
- if (this.touchMoveHandler) {
209
- this.touchMoveHandler.cancel();
210
- delete this.touchMoveHandler;
211
- }
212
- this.mouseMoveHandler = new EventHandler(window, 'mousemove', this._onMouseMoved.bind(this));
213
- this.touchMoveHandler = new EventHandler(window, 'touchmove', this._onMouseMoved.bind(this));
214
- }
215
- _onDialogDragEnded(sender, e) {
216
- if (this.mouseMoveHandler) {
217
- this.mouseMoveHandler.cancel();
218
- delete this.mouseMoveHandler;
219
- }
220
- if (this.touchMoveHandler) {
221
- this.touchMoveHandler.cancel();
222
- delete this.touchMoveHandler;
223
- }
224
- this.dockWheel.onDialogDropped(sender);
225
- this.dockWheel.hideWheel();
226
- delete this.dockWheel.activeDialog;
227
- //TODO: not so good
228
- sender.saveState(sender.elementDialog.offsetLeft, sender.elementDialog.offsetTop);
229
- }
230
- _onMouseMoved(e) {
231
- if (e.changedTouches != null) { // TouchMove Event
232
- e = e.changedTouches[0];
233
- }
234
- this.dockWheel.activeNode = this._findNodeOnPoint(e.clientX, e.clientY);
235
- }
236
- /**
237
- * Perform a DFS (DeepFirstSearch) on the dock model's tree to find the
238
- * deepest level panel (i.e. the top-most non-overlapping panel)
239
- * that is under the mouse cursor
240
- * Retuns null if no node is found under this point
241
- */
242
- _findNodeOnPoint(x, y) {
243
- let stack = [];
244
- stack.push(this.context.model.rootNode);
245
- let bestMatch;
246
- while (stack.length > 0) {
247
- let topNode = stack.pop();
248
- if (Utils.isPointInsideNode(x, y, topNode)) {
249
- // This node contains the point.
250
- bestMatch = topNode;
251
- // Keep looking future down
252
- [].push.apply(stack, topNode.children);
253
- }
254
- }
255
- return bestMatch;
256
- }
257
- /** Dock the [dialog] to the left of the [referenceNode] node */
258
- dockDialogLeft(referenceNode, dialog) {
259
- return this._requestDockDialog(referenceNode, dialog, this.layoutEngine.dockLeft.bind(this.layoutEngine));
260
- }
261
- /** Dock the [dialog] to the right of the [referenceNode] node */
262
- dockDialogRight(referenceNode, dialog) {
263
- return this._requestDockDialog(referenceNode, dialog, this.layoutEngine.dockRight.bind(this.layoutEngine));
264
- }
265
- /** Dock the [dialog] above the [referenceNode] node */
266
- dockDialogUp(referenceNode, dialog) {
267
- return this._requestDockDialog(referenceNode, dialog, this.layoutEngine.dockUp.bind(this.layoutEngine));
268
- }
269
- /** Dock the [dialog] below the [referenceNode] node */
270
- dockDialogDown(referenceNode, dialog) {
271
- return this._requestDockDialog(referenceNode, dialog, this.layoutEngine.dockDown.bind(this.layoutEngine));
272
- }
273
- /** Dock the [dialog] as a tab inside the [referenceNode] node */
274
- dockDialogFill(referenceNode, dialog) {
275
- return this._requestDockDialog(referenceNode, dialog, this.layoutEngine.dockFill.bind(this.layoutEngine));
276
- }
277
- /** Dock the [container] to the left of the [referenceNode] node */
278
- dockLeft(referenceNode, container, ratio) {
279
- return this._requestDockContainer(referenceNode, container, this.layoutEngine.dockLeft.bind(this.layoutEngine), false, ratio);
280
- }
281
- /** Dock the [container] to the right of the [referenceNode] node */
282
- dockRight(referenceNode, container, ratio) {
283
- return this._requestDockContainer(referenceNode, container, this.layoutEngine.dockRight.bind(this.layoutEngine), true, ratio);
284
- }
285
- /** Dock the [container] above the [referenceNode] node */
286
- dockUp(referenceNode, container, ratio) {
287
- return this._requestDockContainer(referenceNode, container, this.layoutEngine.dockUp.bind(this.layoutEngine), false, ratio);
288
- }
289
- /** Dock the [container] below the [referenceNode] node */
290
- dockDown(referenceNode, container, ratio) {
291
- return this._requestDockContainer(referenceNode, container, this.layoutEngine.dockDown.bind(this.layoutEngine), true, ratio);
292
- }
293
- /** Dock the [container] as a tab inside the [referenceNode] node */
294
- dockFill(referenceNode, container) {
295
- return this._requestDockContainer(referenceNode, container, this.layoutEngine.dockFill.bind(this.layoutEngine), false);
296
- }
297
- floatDialog(container, x, y, grayoutParent, disableResize) {
298
- let retdiag = undefined;
299
- //check the dialog do not already exist
300
- this.context.model.dialogs.forEach((dialog) => {
301
- if (container == dialog.panel) {
302
- dialog.show();
303
- dialog.setPosition(x, y);
304
- retdiag = dialog;
305
- }
306
- });
307
- if (retdiag)
308
- return retdiag;
309
- //try to undock just in case
310
- try {
311
- let node = this._findNodeFromContainer(container);
312
- this.layoutEngine.undock(node);
313
- }
314
- catch (err) { }
315
- let panel = container;
316
- Utils.removeNode(panel.elementPanel);
317
- panel.isDialog = true;
318
- let dialog = new Dialog(panel, this, grayoutParent, disableResize);
319
- dialog.setPosition(x, y);
320
- return dialog;
321
- }
322
- _requestDockDialog(referenceNode, dialog, layoutDockFunction) {
323
- // Get the active dialog that was dragged on to the dock wheel
324
- let panel = dialog.panel;
325
- let newNode = new DockNode(panel);
326
- panel.prepareForDocking();
327
- panel.elementContentContainer.style.zIndex = '';
328
- dialog.destroy();
329
- layoutDockFunction(referenceNode, newNode);
330
- // this.invalidate();
331
- return newNode;
332
- }
333
- _checkShowBackgroundContext() {
334
- if (this.backgroundContext != null) {
335
- if (this.context.model.documentManagerNode.children.length > 0) {
336
- this.backgroundContext.style.display = "none";
337
- }
338
- else {
339
- this.backgroundContext.style.display = "block";
340
- }
341
- }
342
- }
343
- _requestDockContainer(referenceNode, container, layoutDockFunction, dockedToPrevious, ratio) {
344
- // Get the active dialog that was dragged on to the dock wheel
345
- let newNode = new DockNode(container);
346
- if (container.containerType === 'panel') {
347
- let panel = container;
348
- panel.prepareForDocking();
349
- Utils.removeNode(panel.elementPanel);
350
- }
351
- let ratios = null;
352
- let oldSplitter;
353
- if (referenceNode.parent && referenceNode.parent.container) {
354
- oldSplitter = referenceNode.parent.container;
355
- if (oldSplitter.getRatios)
356
- ratios = oldSplitter.getRatios();
357
- }
358
- layoutDockFunction(referenceNode, newNode);
359
- if (ratio && newNode.parent && (newNode.parent.container.containerType === 'vertical' || newNode.parent.container.containerType === 'horizontal')) {
360
- let splitter = newNode.parent.container;
361
- if (ratios && oldSplitter == splitter) {
362
- if (dockedToPrevious) {
363
- for (let i = 0; i < ratios.length; i++) {
364
- ratios[i] = ratios[i] - ratios[i] * ratio;
365
- }
366
- ratios.push(ratio);
367
- }
368
- else {
369
- ratios[0] = ratios[0] - ratio;
370
- ratios.unshift(ratio);
371
- }
372
- splitter.setRatios(ratios);
373
- }
374
- else
375
- splitter.setContainerRatio(container, ratio);
376
- }
377
- this.rebuildLayout(this.context.model.rootNode);
378
- this.invalidate();
379
- this._checkShowBackgroundContext();
380
- return newNode;
381
- }
382
- _requestTabReorder(container, e) {
383
- let node = this._findNodeFromContainer(container);
384
- this.layoutEngine.reorderTabs(node, e.handle, e.state, e.index);
385
- }
386
- /**
387
- * Undocks a panel and converts it into a floating dialog window
388
- * It is assumed that only leaf nodes (panels) can be undocked
389
- */
390
- requestUndockToDialog(container, event, dragOffset) {
391
- let node = this._findNodeFromContainer(container);
392
- this.layoutEngine.undock(node);
393
- let panelContainer = node.container;
394
- panelContainer.elementPanel.style.display = 'block';
395
- // Create a new dialog window for the undocked panel
396
- let dialog = new Dialog(panelContainer, this, null);
397
- if (panelContainer.lastDialogSize)
398
- dialog.resize(panelContainer.lastDialogSize.width, panelContainer.lastDialogSize.height);
399
- if (event !== undefined) {
400
- // Adjust the relative position
401
- let dialogWidth = dialog.elementDialog.clientWidth;
402
- if (dragOffset.x > dialogWidth)
403
- dragOffset.x = 0.75 * dialogWidth;
404
- dialog.setPosition(event.clientX - dragOffset.x, event.clientY - dragOffset.y);
405
- dialog.draggable.onMouseDown(event);
406
- }
407
- return dialog;
408
- }
409
- /**
410
- * closes a Panel
411
- */
412
- requestClose(container) {
413
- let node = this._findNodeFromContainer(container);
414
- this.layoutEngine.close(node);
415
- if (this.activePanel == container)
416
- this.activePanel = null;
417
- if (this._activeDocument == container) {
418
- const last = this._activeDocument;
419
- this._activeDocument = null;
420
- this.notifyOnActiveDocumentChange(null, last);
421
- }
422
- }
423
- /**
424
- * Opens a Element in a Dialog
425
- * It is assumed that only leaf nodes (panels) can be undocked
426
- */
427
- openInDialog(container, event, dragOffset, disableResize) {
428
- // Create a new dialog window for the undocked panel
429
- let dialog = new Dialog(container, this, null, disableResize);
430
- if (event !== undefined) {
431
- // Adjust the relative position
432
- let dialogWidth = dialog.elementDialog.clientWidth;
433
- if (dragOffset.x > dialogWidth)
434
- dragOffset.x = 0.75 * dialogWidth;
435
- dialog.setPosition(event.clientX - dragOffset.x, event.clientY - dragOffset.y);
436
- dialog.draggable.onMouseDown(event);
437
- }
438
- return dialog;
439
- }
440
- /** Undocks a panel and converts it into a floating dialog window
441
- * It is assumed that only leaf nodes (panels) can be undocked
442
- */
443
- requestUndock(container) {
444
- let node = this._findNodeFromContainer(container);
445
- this.layoutEngine.undock(node);
446
- }
447
- /**
448
- * Removes a dock container from the dock layout hierarcy
449
- * Returns the node that was removed from the dock tree
450
- */
451
- requestRemove(container) {
452
- let node = this._findNodeFromContainer(container);
453
- let parent = node.parent;
454
- node.detachFromParent();
455
- if (parent)
456
- this.rebuildLayout(parent);
457
- return node;
458
- }
459
- getNodeByElementId(id) {
460
- let stack = [];
461
- stack.push(this.context.model.rootNode);
462
- while (stack.length > 0) {
463
- let topNode = stack.pop();
464
- if (topNode.container instanceof PanelContainer && topNode.container.elementContent.id === id)
465
- return topNode;
466
- [].push.apply(stack, topNode.children);
467
- }
468
- return null;
469
- }
470
- /** Finds the node that owns the specified [container] */
471
- _findNodeFromContainer(container) {
472
- let stack = [];
473
- stack.push(this.context.model.rootNode);
474
- while (stack.length > 0) {
475
- let topNode = stack.pop();
476
- if (topNode.container === container)
477
- return topNode;
478
- [].push.apply(stack, topNode.children);
479
- }
480
- return null;
481
- }
482
- findNodeFromContainerElement(containerElement) {
483
- let stack = [];
484
- stack.push(this.context.model.rootNode);
485
- while (stack.length > 0) {
486
- let topNode = stack.pop();
487
- if (topNode.container.containerElement === containerElement)
488
- return topNode;
489
- [].push.apply(stack, topNode.children);
490
- }
491
- return null;
492
- }
493
- addLayoutListener(listener) {
494
- this.layoutEventListeners.push(listener);
495
- }
496
- removeLayoutListener(listener) {
497
- this.layoutEventListeners.splice(this.layoutEventListeners.indexOf(listener), 1);
498
- }
499
- suspendLayout(panel) {
500
- this.layoutEventListeners.forEach((listener) => {
501
- if (listener.onSuspendLayout)
502
- listener.onSuspendLayout(this, panel);
503
- });
504
- }
505
- resumeLayout(panel) {
506
- this.layoutEventListeners.forEach((listener) => {
507
- if (listener.onResumeLayout)
508
- listener.onResumeLayout(this, panel);
509
- });
510
- }
511
- notifyOnDock(dockNode) {
512
- this._checkShowBackgroundContext();
513
- this.layoutEventListeners.forEach((listener) => {
514
- if (listener.onDock) {
515
- listener.onDock(this, dockNode);
516
- dockNode.container.resize(dockNode.container.width, dockNode.container.height);
517
- }
518
- });
519
- }
520
- notifyOnTabsReorder(dockNode) {
521
- this.layoutEventListeners.forEach((listener) => {
522
- if (listener.onTabsReorder) {
523
- listener.onTabsReorder(this, dockNode);
524
- }
525
- });
526
- }
527
- notifyOnUnDock(dockNode) {
528
- this._checkShowBackgroundContext();
529
- this.layoutEventListeners.forEach((listener) => {
530
- if (listener.onUndock) {
531
- listener.onUndock(this, dockNode);
532
- }
533
- });
534
- }
535
- notifyOnClosePanel(panel) {
536
- this._checkShowBackgroundContext();
537
- if (this.activePanel == panel)
538
- this.activePanel = null;
539
- if (this._activeDocument == panel) {
540
- const last = this._activeDocument;
541
- this._activeDocument = null;
542
- this.notifyOnActiveDocumentChange(null, last);
543
- }
544
- this.layoutEventListeners.forEach((listener) => {
545
- if (listener.onClosePanel) {
546
- listener.onClosePanel(this, panel);
547
- }
548
- });
549
- }
550
- notifyOnCreateDialog(dialog) {
551
- this.layoutEventListeners.forEach((listener) => {
552
- if (listener.onCreateDialog) {
553
- listener.onCreateDialog(this, dialog);
554
- }
555
- });
556
- }
557
- notifyOnHideDialog(dialog) {
558
- this.layoutEventListeners.forEach((listener) => {
559
- if (listener.onHideDialog) {
560
- listener.onHideDialog(this, dialog);
561
- }
562
- });
563
- }
564
- notifyOnShowDialog(dialog) {
565
- this.layoutEventListeners.forEach((listener) => {
566
- if (listener.onShowDialog) {
567
- listener.onShowDialog(this, dialog);
568
- }
569
- });
570
- }
571
- notifyOnChangeDialogPosition(dialog, x, y) {
572
- this.layoutEventListeners.forEach((listener) => {
573
- if (listener.onChangeDialogPosition) {
574
- listener.onChangeDialogPosition(this, dialog, x, y);
575
- }
576
- });
577
- }
578
- notifyOnContainerResized(dockContainer) {
579
- this.layoutEventListeners.forEach((listener) => {
580
- if (listener.onContainerResized) {
581
- listener.onContainerResized(this, dockContainer);
582
- }
583
- });
584
- }
585
- notifyOnTabChange(tabpage) {
586
- this.layoutEventListeners.forEach((listener) => {
587
- if (listener.onTabChanged) {
588
- listener.onTabChanged(this, tabpage);
589
- }
590
- });
591
- }
592
- notifyOnActivePanelChange(panel, oldActive) {
593
- this.layoutEventListeners.forEach((listener) => {
594
- if (listener.onActivePanelChange) {
595
- listener.onActivePanelChange(this, panel, oldActive);
596
- }
597
- });
598
- }
599
- notifyOnActiveDocumentChange(panel, oldActive) {
600
- this.layoutEventListeners.forEach((listener) => {
601
- if (listener.onActiveDocumentChange) {
602
- listener.onActiveDocumentChange(this, panel, oldActive);
603
- }
604
- });
605
- }
606
- saveState() {
607
- let serializer = new DockGraphSerializer();
608
- return serializer.serialize(this.context.model);
609
- }
610
- async loadState(json) {
611
- let deserializer = new DockGraphDeserializer(this);
612
- this.context.model = await deserializer.deserialize(json);
613
- this.setModel(this.context.model);
614
- }
615
- getPanels() {
616
- let panels = [];
617
- //all visible nodes
618
- this._allPanels(this.context.model.rootNode, panels);
619
- //all visible or not dialogs
620
- this.context.model.dialogs.forEach((dialog) => {
621
- //TODO: check visible
622
- panels.push(dialog.panel);
623
- });
624
- return panels;
625
- }
626
- undockEnabled(state) {
627
- this._undockEnabled = state;
628
- this.getPanels().forEach((panel) => {
629
- panel.canUndock(state);
630
- });
631
- }
632
- lockDockState(state) {
633
- this.undockEnabled(!state); // false - not enabled
634
- this.hideCloseButton(state); //true - hide
635
- }
636
- hideCloseButton(state) {
637
- this.getPanels().forEach((panel) => {
638
- panel.hideCloseButton(state);
639
- });
640
- }
641
- updatePanels(ids) {
642
- let panels = [];
643
- //all visible nodes
644
- this._allPanels(this.context.model.rootNode, panels);
645
- //only remove
646
- panels.forEach((panel) => {
647
- if (!Utils.arrayContains(ids, panel.elementContent.id)) {
648
- panel.close();
649
- }
650
- });
651
- this.context.model.dialogs.forEach((dialog) => {
652
- if (Utils.arrayContains(ids, dialog.panel.elementContent.id)) {
653
- dialog.show();
654
- }
655
- else {
656
- dialog.hide();
657
- }
658
- });
659
- return panels;
660
- }
661
- getVisiblePanels() {
662
- let panels = [];
663
- //all visible nodes
664
- this._allPanels(this.context.model.rootNode, panels);
665
- //all visible
666
- this.context.model.dialogs.forEach((dialog) => {
667
- if (!dialog.isHidden) {
668
- panels.push(dialog.panel);
669
- }
670
- });
671
- return panels;
672
- }
673
- _allPanels(node, panels) {
674
- node.children.forEach((child) => {
675
- this._allPanels(child, panels);
676
- });
677
- if (node.container.containerType === 'panel') {
678
- panels.push(node.container);
679
- }
680
- }
681
- get activeDocument() {
682
- return this._activeDocument;
683
- }
684
- get activePanel() {
685
- return this._activePanel;
686
- }
687
- set activePanel(value) {
688
- if (value !== this._activePanel) {
689
- if (value && !value.isDialog) //todo store compliete list of panels, remove the closed ones and switch back focus
690
- this._lastPanelNotADialog = value;
691
- if (this._lastPanelNotADialog && this.getPanels().indexOf(this._lastPanelNotADialog) < 0)
692
- this._lastPanelNotADialog = null;
693
- let oldActive = this.activePanel;
694
- if (this.activePanel) {
695
- this.activePanel.elementTitle.classList.remove("dockspan-panel-active");
696
- this.activePanel.elementTitleText.classList.remove("dockspan-panel-titlebar-text-active");
697
- if (this.activePanel.tabPage) {
698
- this.activePanel.tabPage.host.setActive(false);
699
- }
700
- }
701
- this._activePanel = value;
702
- let lastActiveDocument = this._activeDocument;
703
- if (value && value.panelType == PanelType.document) {
704
- this._activeDocument = value;
705
- }
706
- if (!value && oldActive && oldActive.isDialog && value == null && this._lastPanelNotADialog && this.activePanel != this._lastPanelNotADialog) {
707
- value = this._lastPanelNotADialog;
708
- this._lastPanelNotADialog = undefined;
709
- }
710
- this.notifyOnActivePanelChange(value, oldActive);
711
- if (lastActiveDocument != this._activeDocument) {
712
- this.notifyOnActiveDocumentChange(this._activeDocument, lastActiveDocument);
713
- }
714
- if (value) {
715
- value.elementTitle.classList.add("dockspan-panel-active");
716
- value.elementTitleText.classList.add("dockspan-panel-titlebar-text-active");
717
- if (value.tabPage) {
718
- value.tabPage.host.setActive(true);
719
- }
720
- }
721
- }
722
- else {
723
- if (value && value.tabPage) {
724
- value.tabPage.host.setActive(true);
725
- }
726
- }
727
- }
728
- }
1
+ import { DockWheel } from "./DockWheel.js";
2
+ import { Utils } from "./Utils.js";
3
+ import { Point } from "./Point.js";
4
+ import { DockManagerContext } from "./DockManagerContext.js";
5
+ import { DockNode } from "./DockNode.js";
6
+ import { DockLayoutEngine } from "./DockLayoutEngine.js";
7
+ import { EventHandler } from "./EventHandler.js";
8
+ import { Dialog } from "./Dialog.js";
9
+ import { DockGraphSerializer } from "./DockGraphSerializer.js";
10
+ import { DockGraphDeserializer } from "./DockGraphDeserializer.js";
11
+ import { PanelContainer } from "./PanelContainer.js";
12
+ import { DockConfig } from "./DockConfig.js";
13
+ import { PanelType } from "./enums/PanelType.js";
14
+ /**
15
+ * Dock manager manages all the dock panels in a hierarchy, similar to visual studio.
16
+ * It owns a Html Div element inside which all panels are docked
17
+ * Initially the document manager takes up the central space and acts as the root node
18
+ */
19
+ export class DockManager {
20
+ constructor(element, config) {
21
+ if (element === undefined)
22
+ throw new Error('Invalid Dock Manager element provided');
23
+ this._config = Object.assign(new DockConfig(), config);
24
+ this.element = element;
25
+ this.context = this.dockWheel = this.layoutEngine = this.mouseMoveHandler = this.touchMoveHandler = undefined;
26
+ this.layoutEventListeners = [];
27
+ this.defaultDialogPosition = new Point(0, 0);
28
+ }
29
+ get config() {
30
+ return this._config;
31
+ }
32
+ initialize() {
33
+ this.backgroundContext = this.element.children[0];
34
+ this.context = new DockManagerContext(this);
35
+ let documentNode = new DockNode(this.context.documentManagerView);
36
+ this.context.model.rootNode = documentNode;
37
+ this.context.model.documentManagerNode = documentNode;
38
+ this.context.model.dialogs = [];
39
+ this.setRootNode(this.context.model.rootNode);
40
+ // Resize the layout
41
+ this.resize(this.element.clientWidth, this.element.clientHeight);
42
+ this.dockWheel = new DockWheel(this);
43
+ this.layoutEngine = new DockLayoutEngine(this);
44
+ this._undockEnabled = true;
45
+ this.rebuildLayout(this.context.model.rootNode);
46
+ this.zIndexCounter = 1001;
47
+ this.zIndexTabHost = 1000;
48
+ this.zIndexTabHandle = 100;
49
+ this.zIndexDialogCounter = 10001;
50
+ if (this.backgroundContext != null) {
51
+ this.context.model.rootNode.container.tabHost.hostElement
52
+ .insertBefore(this.backgroundContext, this.context.model.rootNode.container.tabHost.hostElement.firstChild);
53
+ }
54
+ this.onKeyPressBound = this.onKeyPress.bind(this);
55
+ this.element.addEventListener('keydown', this.onKeyPressBound);
56
+ }
57
+ onKeyPress(e) {
58
+ if (e.key == "Escape" && this.activePanel && !this.activePanel._hideCloseButton) {
59
+ if ((this.activePanel.isDialog && this._config.escClosesDialog) || (!this.activePanel.isDialog && this._config.escClosesWindow)) {
60
+ let panel = this.activePanel;
61
+ this.activePanel = null;
62
+ panel.close();
63
+ }
64
+ }
65
+ }
66
+ checkXBounds(container, currentMousePosition, previousMousePosition, resizeWest, resizeEast) {
67
+ if (this._config.moveOnlyWithinDockConatiner)
68
+ return this.checkXBoundsWithinDockContainer(container, currentMousePosition, previousMousePosition, resizeWest, resizeEast);
69
+ let rect = this.element.getBoundingClientRect();
70
+ let dx = Math.floor(currentMousePosition.x - previousMousePosition.x);
71
+ let leftBounds = container.offsetLeft + container.offsetWidth + dx + rect.left < 40; // || (container.offsetLeft + container.offsetWidth + dx - 40 ) < 0;
72
+ let rightBounds = container.offsetLeft + dx > (window.innerWidth - 40);
73
+ if (leftBounds) {
74
+ previousMousePosition.x = currentMousePosition.x;
75
+ dx = 0;
76
+ let d = 40 - (container.offsetWidth + container.offsetLeft);
77
+ if (d > 0)
78
+ dx = d;
79
+ }
80
+ else if (rightBounds) {
81
+ previousMousePosition.x = currentMousePosition.x;
82
+ dx = 0;
83
+ let d = (window.innerWidth - 40) - container.offsetLeft;
84
+ if (d > 0)
85
+ dx = d;
86
+ }
87
+ return dx;
88
+ }
89
+ checkXBoundsWithinDockContainer(container, currentMousePosition, previousMousePosition, resizeWest, resizeEast) {
90
+ let dx = currentMousePosition.x - previousMousePosition.x;
91
+ let bbOuter = this.element.getBoundingClientRect();
92
+ let bbInner = container.getBoundingClientRect();
93
+ let leftBounds = dx < 0 && bbInner.left + dx < bbOuter.left && !resizeEast;
94
+ let rightBounds = dx > 0 && bbInner.right + dx > bbOuter.right && !resizeWest;
95
+ if (leftBounds) {
96
+ currentMousePosition.x -= dx;
97
+ dx = bbOuter.left - bbInner.left;
98
+ currentMousePosition.x -= dx;
99
+ }
100
+ else if (rightBounds) {
101
+ currentMousePosition.x -= dx;
102
+ dx = bbOuter.right - bbInner.right;
103
+ currentMousePosition.x -= dx;
104
+ }
105
+ return dx;
106
+ }
107
+ checkYBounds(container, currentMousePosition, previousMousePosition, resizeNorth, resizeSouth) {
108
+ if (this._config.moveOnlyWithinDockConatiner)
109
+ return this.checkYBoundsWithinDockContainer(container, currentMousePosition, previousMousePosition, resizeNorth, resizeSouth);
110
+ let rect = this.element.getBoundingClientRect();
111
+ let dy = Math.floor(currentMousePosition.y - previousMousePosition.y);
112
+ let topBounds = container.offsetTop + dy + rect.top < 0;
113
+ let bottomBounds = container.offsetTop + dy > (window.innerHeight - 16);
114
+ if (topBounds) {
115
+ previousMousePosition.y = currentMousePosition.y;
116
+ dy = 0;
117
+ }
118
+ else if (bottomBounds) {
119
+ previousMousePosition.y = currentMousePosition.y;
120
+ dy = 0;
121
+ let d = (window.innerHeight - 25) - container.offsetTop;
122
+ if (d > 0)
123
+ dy = d;
124
+ }
125
+ return dy;
126
+ }
127
+ checkYBoundsWithinDockContainer(container, currentMousePosition, previousMousePosition, resizeNorth, resizeSouth) {
128
+ let dy = currentMousePosition.y - previousMousePosition.y;
129
+ let bbOuter = this.element.getBoundingClientRect();
130
+ let bbInner = container.getBoundingClientRect();
131
+ let topBounds = dy < 0 && bbInner.top + dy < bbOuter.top && !resizeSouth;
132
+ let bottomBounds = dy > 0 && bbInner.bottom + dy > bbOuter.bottom && !resizeNorth;
133
+ if (topBounds) {
134
+ currentMousePosition.y -= dy;
135
+ dy = bbOuter.top - bbInner.top;
136
+ currentMousePosition.y -= dy;
137
+ }
138
+ else if (bottomBounds) {
139
+ currentMousePosition.y -= dy;
140
+ dy = bbOuter.bottom - bbInner.bottom;
141
+ currentMousePosition.y -= dy;
142
+ }
143
+ return dy;
144
+ }
145
+ rebuildLayout(node) {
146
+ node.children.forEach((child) => {
147
+ this.rebuildLayout(child);
148
+ });
149
+ node.performLayout(false);
150
+ }
151
+ invalidate() {
152
+ this.resize(this.element.clientWidth, this.element.clientHeight);
153
+ }
154
+ resize(width, height) {
155
+ this.element.style.width = width + 'px';
156
+ this.element.style.height = height + 'px';
157
+ this.context.model.rootNode.container.resize(width, height);
158
+ let offsetX = 0, offsetY = 0;
159
+ for (let dialog of this.context.model.dialogs) {
160
+ if (dialog.position.x > this.element.clientWidth || dialog.position.y > this.element.clientHeight) {
161
+ if (offsetX > this.element.clientWidth || offsetY > this.element.clientHeight)
162
+ offsetX = 0, offsetY = 0;
163
+ dialog.setPosition(100 + offsetX, 100 + offsetY);
164
+ offsetX += 100;
165
+ offsetY += 100;
166
+ }
167
+ }
168
+ }
169
+ /**
170
+ * Reset the dock model . This happens when the state is loaded from json
171
+ */
172
+ setModel(model) {
173
+ Utils.removeNode(this.context.documentManagerView.containerElement);
174
+ this.context.model = model;
175
+ this.setRootNode(model.rootNode);
176
+ this.rebuildLayout(model.rootNode);
177
+ this.loadResize(model.rootNode);
178
+ // this.invalidate();
179
+ }
180
+ loadResize(node) {
181
+ node.children.reverse().forEach((child) => {
182
+ this.loadResize(child);
183
+ node.container.setActiveChild(child.container);
184
+ });
185
+ node.children.reverse();
186
+ let container = node.container;
187
+ node.container.resize(container.state.width, container.state.height);
188
+ // node.performLayout();
189
+ }
190
+ setRootNode(node) {
191
+ // if (this.context.model.rootNode)
192
+ // {
193
+ // // detach it from the dock manager's base element
194
+ // context.model.rootNode.detachFromParent();
195
+ // }
196
+ // Attach the new node to the dock manager's base element and set as root node
197
+ node.detachFromParent();
198
+ this.context.model.rootNode = node;
199
+ this.element.appendChild(node.container.containerElement);
200
+ }
201
+ _onDialogDragStarted(sender, e) {
202
+ this.dockWheel.activeNode = this._findNodeOnPoint(e.clientX, e.clientY);
203
+ this.dockWheel.activeDialog = sender;
204
+ if (sender.noDocking == null || sender.noDocking !== true)
205
+ this.dockWheel.showWheel();
206
+ if (this.mouseMoveHandler) {
207
+ this.mouseMoveHandler.cancel();
208
+ delete this.mouseMoveHandler;
209
+ }
210
+ if (this.touchMoveHandler) {
211
+ this.touchMoveHandler.cancel();
212
+ delete this.touchMoveHandler;
213
+ }
214
+ this.mouseMoveHandler = new EventHandler(window, 'mousemove', this._onMouseMoved.bind(this));
215
+ this.touchMoveHandler = new EventHandler(window, 'touchmove', this._onMouseMoved.bind(this));
216
+ }
217
+ _onDialogDragEnded(sender, e) {
218
+ if (this.mouseMoveHandler) {
219
+ this.mouseMoveHandler.cancel();
220
+ delete this.mouseMoveHandler;
221
+ }
222
+ if (this.touchMoveHandler) {
223
+ this.touchMoveHandler.cancel();
224
+ delete this.touchMoveHandler;
225
+ }
226
+ this.dockWheel.onDialogDropped(sender);
227
+ this.dockWheel.hideWheel();
228
+ delete this.dockWheel.activeDialog;
229
+ //TODO: not so good
230
+ sender.saveState(sender.elementDialog.offsetLeft, sender.elementDialog.offsetTop);
231
+ }
232
+ _onMouseMoved(e) {
233
+ if (e.changedTouches != null) { // TouchMove Event
234
+ e = e.changedTouches[0];
235
+ }
236
+ this.dockWheel.activeNode = this._findNodeOnPoint(e.clientX, e.clientY);
237
+ }
238
+ /**
239
+ * Perform a DFS (DeepFirstSearch) on the dock model's tree to find the
240
+ * deepest level panel (i.e. the top-most non-overlapping panel)
241
+ * that is under the mouse cursor
242
+ * Retuns null if no node is found under this point
243
+ */
244
+ _findNodeOnPoint(x, y) {
245
+ let stack = [];
246
+ stack.push(this.context.model.rootNode);
247
+ let bestMatch;
248
+ while (stack.length > 0) {
249
+ let topNode = stack.pop();
250
+ if (Utils.isPointInsideNode(x, y, topNode)) {
251
+ // This node contains the point.
252
+ bestMatch = topNode;
253
+ // Keep looking future down
254
+ [].push.apply(stack, topNode.children);
255
+ }
256
+ }
257
+ return bestMatch;
258
+ }
259
+ /** Dock the [dialog] to the left of the [referenceNode] node */
260
+ dockDialogLeft(referenceNode, dialog) {
261
+ return this._requestDockDialog(referenceNode, dialog, this.layoutEngine.dockLeft.bind(this.layoutEngine));
262
+ }
263
+ /** Dock the [dialog] to the right of the [referenceNode] node */
264
+ dockDialogRight(referenceNode, dialog) {
265
+ return this._requestDockDialog(referenceNode, dialog, this.layoutEngine.dockRight.bind(this.layoutEngine));
266
+ }
267
+ /** Dock the [dialog] above the [referenceNode] node */
268
+ dockDialogUp(referenceNode, dialog) {
269
+ return this._requestDockDialog(referenceNode, dialog, this.layoutEngine.dockUp.bind(this.layoutEngine));
270
+ }
271
+ /** Dock the [dialog] below the [referenceNode] node */
272
+ dockDialogDown(referenceNode, dialog) {
273
+ return this._requestDockDialog(referenceNode, dialog, this.layoutEngine.dockDown.bind(this.layoutEngine));
274
+ }
275
+ /** Dock the [dialog] as a tab inside the [referenceNode] node */
276
+ dockDialogFill(referenceNode, dialog) {
277
+ return this._requestDockDialog(referenceNode, dialog, this.layoutEngine.dockFill.bind(this.layoutEngine));
278
+ }
279
+ /** Dock the [container] to the left of the [referenceNode] node */
280
+ dockLeft(referenceNode, container, ratio) {
281
+ return this._requestDockContainer(referenceNode, container, this.layoutEngine.dockLeft.bind(this.layoutEngine), false, ratio);
282
+ }
283
+ /** Dock the [container] to the right of the [referenceNode] node */
284
+ dockRight(referenceNode, container, ratio) {
285
+ return this._requestDockContainer(referenceNode, container, this.layoutEngine.dockRight.bind(this.layoutEngine), true, ratio);
286
+ }
287
+ /** Dock the [container] above the [referenceNode] node */
288
+ dockUp(referenceNode, container, ratio) {
289
+ return this._requestDockContainer(referenceNode, container, this.layoutEngine.dockUp.bind(this.layoutEngine), false, ratio);
290
+ }
291
+ /** Dock the [container] below the [referenceNode] node */
292
+ dockDown(referenceNode, container, ratio) {
293
+ return this._requestDockContainer(referenceNode, container, this.layoutEngine.dockDown.bind(this.layoutEngine), true, ratio);
294
+ }
295
+ /** Dock the [container] as a tab inside the [referenceNode] node */
296
+ dockFill(referenceNode, container) {
297
+ return this._requestDockContainer(referenceNode, container, this.layoutEngine.dockFill.bind(this.layoutEngine), false);
298
+ }
299
+ floatDialog(container, x, y, grayoutParent, disableResize) {
300
+ let retdiag = undefined;
301
+ //check the dialog do not already exist
302
+ this.context.model.dialogs.forEach((dialog) => {
303
+ if (container == dialog.panel) {
304
+ dialog.show();
305
+ dialog.setPosition(x, y);
306
+ retdiag = dialog;
307
+ }
308
+ });
309
+ if (retdiag)
310
+ return retdiag;
311
+ //try to undock just in case
312
+ try {
313
+ let node = this._findNodeFromContainer(container);
314
+ this.layoutEngine.undock(node);
315
+ }
316
+ catch (err) { }
317
+ let panel = container;
318
+ Utils.removeNode(panel.elementPanel);
319
+ panel.isDialog = true;
320
+ let dialog = new Dialog(panel, this, grayoutParent, disableResize);
321
+ dialog.setPosition(x, y);
322
+ return dialog;
323
+ }
324
+ _requestDockDialog(referenceNode, dialog, layoutDockFunction) {
325
+ // Get the active dialog that was dragged on to the dock wheel
326
+ let panel = dialog.panel;
327
+ let newNode = new DockNode(panel);
328
+ panel.prepareForDocking();
329
+ panel.elementContentContainer.style.zIndex = '';
330
+ dialog.destroy();
331
+ layoutDockFunction(referenceNode, newNode);
332
+ // this.invalidate();
333
+ return newNode;
334
+ }
335
+ _checkShowBackgroundContext() {
336
+ if (this.backgroundContext != null) {
337
+ if (this.context.model.documentManagerNode.children.length > 0) {
338
+ this.backgroundContext.style.display = "none";
339
+ }
340
+ else {
341
+ this.backgroundContext.style.display = "block";
342
+ }
343
+ }
344
+ }
345
+ _requestDockContainer(referenceNode, container, layoutDockFunction, dockedToPrevious, ratio) {
346
+ // Get the active dialog that was dragged on to the dock wheel
347
+ let newNode = new DockNode(container);
348
+ if (container.containerType === 'panel') {
349
+ let panel = container;
350
+ panel.prepareForDocking();
351
+ Utils.removeNode(panel.elementPanel);
352
+ }
353
+ let ratios = null;
354
+ let oldSplitter;
355
+ if (referenceNode.parent && referenceNode.parent.container) {
356
+ oldSplitter = referenceNode.parent.container;
357
+ if (oldSplitter.getRatios)
358
+ ratios = oldSplitter.getRatios();
359
+ }
360
+ layoutDockFunction(referenceNode, newNode);
361
+ if (ratio && newNode.parent && (newNode.parent.container.containerType === 'vertical' || newNode.parent.container.containerType === 'horizontal')) {
362
+ let splitter = newNode.parent.container;
363
+ if (ratios && oldSplitter == splitter) {
364
+ if (dockedToPrevious) {
365
+ for (let i = 0; i < ratios.length; i++) {
366
+ ratios[i] = ratios[i] - ratios[i] * ratio;
367
+ }
368
+ ratios.push(ratio);
369
+ }
370
+ else {
371
+ ratios[0] = ratios[0] - ratio;
372
+ ratios.unshift(ratio);
373
+ }
374
+ splitter.setRatios(ratios);
375
+ }
376
+ else
377
+ splitter.setContainerRatio(container, ratio);
378
+ }
379
+ this.rebuildLayout(this.context.model.rootNode);
380
+ this.invalidate();
381
+ this._checkShowBackgroundContext();
382
+ return newNode;
383
+ }
384
+ _requestTabReorder(container, e) {
385
+ let node = this._findNodeFromContainer(container);
386
+ this.layoutEngine.reorderTabs(node, e.handle, e.state, e.index);
387
+ }
388
+ /**
389
+ * Undocks a panel and converts it into a floating dialog window
390
+ * It is assumed that only leaf nodes (panels) can be undocked
391
+ */
392
+ requestUndockToDialog(container, event, dragOffset) {
393
+ let node = this._findNodeFromContainer(container);
394
+ this.layoutEngine.undock(node);
395
+ let panelContainer = node.container;
396
+ panelContainer.elementPanel.style.display = 'block';
397
+ // Create a new dialog window for the undocked panel
398
+ let dialog = new Dialog(panelContainer, this, null);
399
+ if (panelContainer.lastDialogSize)
400
+ dialog.resize(panelContainer.lastDialogSize.width, panelContainer.lastDialogSize.height);
401
+ if (event !== undefined) {
402
+ // Adjust the relative position
403
+ let dialogWidth = dialog.elementDialog.clientWidth;
404
+ if (dragOffset.x > dialogWidth)
405
+ dragOffset.x = 0.75 * dialogWidth;
406
+ dialog.setPosition(event.clientX - dragOffset.x, event.clientY - dragOffset.y);
407
+ dialog.draggable.onMouseDown(event);
408
+ }
409
+ return dialog;
410
+ }
411
+ /**
412
+ * closes a Panel
413
+ */
414
+ requestClose(container) {
415
+ let node = this._findNodeFromContainer(container);
416
+ this.layoutEngine.close(node);
417
+ if (this.activePanel == container)
418
+ this.activePanel = null;
419
+ if (this._activeDocument == container) {
420
+ const last = this._activeDocument;
421
+ this._activeDocument = null;
422
+ this.notifyOnActiveDocumentChange(null, last);
423
+ }
424
+ }
425
+ /**
426
+ * Opens a Element in a Dialog
427
+ * It is assumed that only leaf nodes (panels) can be undocked
428
+ */
429
+ openInDialog(container, event, dragOffset, disableResize) {
430
+ // Create a new dialog window for the undocked panel
431
+ let dialog = new Dialog(container, this, null, disableResize);
432
+ if (event != null) {
433
+ // Adjust the relative position
434
+ let dialogWidth = dialog.elementDialog.clientWidth;
435
+ if (dragOffset.x > dialogWidth)
436
+ dragOffset.x = 0.75 * dialogWidth;
437
+ dialog.setPosition(event.clientX - dragOffset.x, event.clientY - dragOffset.y);
438
+ dialog.draggable.onMouseDown(event);
439
+ }
440
+ return dialog;
441
+ }
442
+ /** Undocks a panel and converts it into a floating dialog window
443
+ * It is assumed that only leaf nodes (panels) can be undocked
444
+ */
445
+ requestUndock(container) {
446
+ let node = this._findNodeFromContainer(container);
447
+ this.layoutEngine.undock(node);
448
+ }
449
+ /**
450
+ * Removes a dock container from the dock layout hierarcy
451
+ * Returns the node that was removed from the dock tree
452
+ */
453
+ requestRemove(container) {
454
+ let node = this._findNodeFromContainer(container);
455
+ let parent = node.parent;
456
+ node.detachFromParent();
457
+ if (parent)
458
+ this.rebuildLayout(parent);
459
+ return node;
460
+ }
461
+ getNodeByElementId(id) {
462
+ let stack = [];
463
+ stack.push(this.context.model.rootNode);
464
+ while (stack.length > 0) {
465
+ let topNode = stack.pop();
466
+ if (topNode.container instanceof PanelContainer && topNode.container.elementContent.id === id)
467
+ return topNode;
468
+ [].push.apply(stack, topNode.children);
469
+ }
470
+ return null;
471
+ }
472
+ /** Finds the node that owns the specified [container] */
473
+ _findNodeFromContainer(container) {
474
+ let stack = [];
475
+ stack.push(this.context.model.rootNode);
476
+ while (stack.length > 0) {
477
+ let topNode = stack.pop();
478
+ if (topNode.container === container)
479
+ return topNode;
480
+ [].push.apply(stack, topNode.children);
481
+ }
482
+ return null;
483
+ }
484
+ findNodeFromContainerElement(containerElement) {
485
+ let stack = [];
486
+ stack.push(this.context.model.rootNode);
487
+ while (stack.length > 0) {
488
+ let topNode = stack.pop();
489
+ if (topNode.container.containerElement === containerElement)
490
+ return topNode;
491
+ [].push.apply(stack, topNode.children);
492
+ }
493
+ return null;
494
+ }
495
+ addLayoutListener(listener) {
496
+ this.layoutEventListeners.push(listener);
497
+ }
498
+ removeLayoutListener(listener) {
499
+ this.layoutEventListeners.splice(this.layoutEventListeners.indexOf(listener), 1);
500
+ }
501
+ suspendLayout(panel) {
502
+ this.layoutEventListeners.forEach((listener) => {
503
+ if (listener.onSuspendLayout)
504
+ listener.onSuspendLayout(this, panel);
505
+ });
506
+ }
507
+ resumeLayout(panel) {
508
+ this.layoutEventListeners.forEach((listener) => {
509
+ if (listener.onResumeLayout)
510
+ listener.onResumeLayout(this, panel);
511
+ });
512
+ }
513
+ notifyOnDock(dockNode) {
514
+ this._checkShowBackgroundContext();
515
+ this.layoutEventListeners.forEach((listener) => {
516
+ if (listener.onDock) {
517
+ listener.onDock(this, dockNode);
518
+ dockNode.container.resize(dockNode.container.width, dockNode.container.height);
519
+ }
520
+ });
521
+ }
522
+ notifyOnTabsReorder(dockNode) {
523
+ this.layoutEventListeners.forEach((listener) => {
524
+ if (listener.onTabsReorder) {
525
+ listener.onTabsReorder(this, dockNode);
526
+ }
527
+ });
528
+ }
529
+ notifyOnUnDock(dockNode) {
530
+ this._checkShowBackgroundContext();
531
+ this.layoutEventListeners.forEach((listener) => {
532
+ if (listener.onUndock) {
533
+ listener.onUndock(this, dockNode);
534
+ }
535
+ });
536
+ }
537
+ notifyOnClosePanel(panel) {
538
+ this._checkShowBackgroundContext();
539
+ if (this.activePanel == panel)
540
+ this.activePanel = null;
541
+ if (this._activeDocument == panel) {
542
+ const last = this._activeDocument;
543
+ this._activeDocument = null;
544
+ this.notifyOnActiveDocumentChange(null, last);
545
+ }
546
+ this.layoutEventListeners.forEach((listener) => {
547
+ if (listener.onClosePanel) {
548
+ listener.onClosePanel(this, panel);
549
+ }
550
+ });
551
+ }
552
+ notifyOnCreateDialog(dialog) {
553
+ this.layoutEventListeners.forEach((listener) => {
554
+ if (listener.onCreateDialog) {
555
+ listener.onCreateDialog(this, dialog);
556
+ }
557
+ });
558
+ }
559
+ notifyOnHideDialog(dialog) {
560
+ this.layoutEventListeners.forEach((listener) => {
561
+ if (listener.onHideDialog) {
562
+ listener.onHideDialog(this, dialog);
563
+ }
564
+ });
565
+ }
566
+ notifyOnShowDialog(dialog) {
567
+ this.layoutEventListeners.forEach((listener) => {
568
+ if (listener.onShowDialog) {
569
+ listener.onShowDialog(this, dialog);
570
+ }
571
+ });
572
+ }
573
+ notifyOnChangeDialogPosition(dialog, x, y) {
574
+ this.layoutEventListeners.forEach((listener) => {
575
+ if (listener.onChangeDialogPosition) {
576
+ listener.onChangeDialogPosition(this, dialog, x, y);
577
+ }
578
+ });
579
+ }
580
+ notifyOnContainerResized(dockContainer) {
581
+ this.layoutEventListeners.forEach((listener) => {
582
+ if (listener.onContainerResized) {
583
+ listener.onContainerResized(this, dockContainer);
584
+ }
585
+ });
586
+ }
587
+ notifyOnTabChange(tabpage) {
588
+ this.layoutEventListeners.forEach((listener) => {
589
+ if (listener.onTabChanged) {
590
+ listener.onTabChanged(this, tabpage);
591
+ }
592
+ });
593
+ }
594
+ notifyOnActivePanelChange(panel, oldActive) {
595
+ this.layoutEventListeners.forEach((listener) => {
596
+ if (listener.onActivePanelChange) {
597
+ listener.onActivePanelChange(this, panel, oldActive);
598
+ }
599
+ });
600
+ }
601
+ notifyOnActiveDocumentChange(panel, oldActive) {
602
+ this.layoutEventListeners.forEach((listener) => {
603
+ if (listener.onActiveDocumentChange) {
604
+ listener.onActiveDocumentChange(this, panel, oldActive);
605
+ }
606
+ });
607
+ }
608
+ saveState() {
609
+ let serializer = new DockGraphSerializer();
610
+ return serializer.serialize(this.context.model);
611
+ }
612
+ async loadState(json) {
613
+ let deserializer = new DockGraphDeserializer(this);
614
+ this.context.model = await deserializer.deserialize(json);
615
+ this.setModel(this.context.model);
616
+ }
617
+ getPanels() {
618
+ let panels = [];
619
+ //all visible nodes
620
+ this._allPanels(this.context.model.rootNode, panels);
621
+ //all visible or not dialogs
622
+ this.context.model.dialogs.forEach((dialog) => {
623
+ //TODO: check visible
624
+ panels.push(dialog.panel);
625
+ });
626
+ return panels;
627
+ }
628
+ undockEnabled(state) {
629
+ this._undockEnabled = state;
630
+ this.getPanels().forEach((panel) => {
631
+ panel.canUndock(state);
632
+ });
633
+ }
634
+ lockDockState(state) {
635
+ this.undockEnabled(!state); // false - not enabled
636
+ this.hideCloseButton(state); //true - hide
637
+ }
638
+ hideCloseButton(state) {
639
+ this.getPanels().forEach((panel) => {
640
+ panel.hideCloseButton(state);
641
+ });
642
+ }
643
+ updatePanels(ids) {
644
+ let panels = [];
645
+ //all visible nodes
646
+ this._allPanels(this.context.model.rootNode, panels);
647
+ //only remove
648
+ panels.forEach((panel) => {
649
+ if (!Utils.arrayContains(ids, panel.elementContent.id)) {
650
+ panel.close();
651
+ }
652
+ });
653
+ this.context.model.dialogs.forEach((dialog) => {
654
+ if (Utils.arrayContains(ids, dialog.panel.elementContent.id)) {
655
+ dialog.show();
656
+ }
657
+ else {
658
+ dialog.hide();
659
+ }
660
+ });
661
+ return panels;
662
+ }
663
+ getVisiblePanels() {
664
+ let panels = [];
665
+ //all visible nodes
666
+ this._allPanels(this.context.model.rootNode, panels);
667
+ //all visible
668
+ this.context.model.dialogs.forEach((dialog) => {
669
+ if (!dialog.isHidden) {
670
+ panels.push(dialog.panel);
671
+ }
672
+ });
673
+ return panels;
674
+ }
675
+ _allPanels(node, panels) {
676
+ node.children.forEach((child) => {
677
+ this._allPanels(child, panels);
678
+ });
679
+ if (node.container.containerType === 'panel') {
680
+ panels.push(node.container);
681
+ }
682
+ }
683
+ get activeDocument() {
684
+ return this._activeDocument;
685
+ }
686
+ get activePanel() {
687
+ return this._activePanel;
688
+ }
689
+ set activePanel(value) {
690
+ if (value !== this._activePanel) {
691
+ if (value && !value.isDialog) //todo store compliete list of panels, remove the closed ones and switch back focus
692
+ this._lastPanelNotADialog = value;
693
+ if (this._lastPanelNotADialog && this.getPanels().indexOf(this._lastPanelNotADialog) < 0)
694
+ this._lastPanelNotADialog = null;
695
+ let oldActive = this.activePanel;
696
+ if (this.activePanel) {
697
+ this.activePanel.elementTitle.classList.remove("dockspan-panel-active");
698
+ this.activePanel.elementTitleText.classList.remove("dockspan-panel-titlebar-text-active");
699
+ if (this.activePanel.tabPage) {
700
+ this.activePanel.tabPage.host.setActive(false);
701
+ }
702
+ }
703
+ this._activePanel = value;
704
+ let lastActiveDocument = this._activeDocument;
705
+ if (value && value.panelType == PanelType.document) {
706
+ this._activeDocument = value;
707
+ }
708
+ if (!value && oldActive && oldActive.isDialog && value == null && this._lastPanelNotADialog && this.activePanel != this._lastPanelNotADialog) {
709
+ value = this._lastPanelNotADialog;
710
+ this._lastPanelNotADialog = undefined;
711
+ }
712
+ this.notifyOnActivePanelChange(value, oldActive);
713
+ if (lastActiveDocument != this._activeDocument) {
714
+ this.notifyOnActiveDocumentChange(this._activeDocument, lastActiveDocument);
715
+ }
716
+ if (value) {
717
+ value.elementTitle.classList.add("dockspan-panel-active");
718
+ value.elementTitleText.classList.add("dockspan-panel-titlebar-text-active");
719
+ if (value.tabPage) {
720
+ value.tabPage.host.setActive(true);
721
+ }
722
+ }
723
+ }
724
+ else {
725
+ if (value && value.tabPage) {
726
+ value.tabPage.host.setActive(true);
727
+ }
728
+ }
729
+ }
730
+ }
729
731
  //# sourceMappingURL=DockManager.js.map