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