dockview 1.8.5 → 1.9.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 (46) hide show
  1. package/README.md +15 -10
  2. package/dist/cjs/dockview/defaultTab.js.map +1 -1
  3. package/dist/cjs/dockview/dockview.d.ts +4 -1
  4. package/dist/cjs/dockview/dockview.d.ts.map +1 -1
  5. package/dist/cjs/dockview/dockview.js +11 -0
  6. package/dist/cjs/dockview/dockview.js.map +1 -1
  7. package/dist/cjs/dockview/reactWatermarkPart.js.map +1 -1
  8. package/dist/cjs/gridview/gridview.js.map +1 -1
  9. package/dist/cjs/gridview/view.js.map +1 -1
  10. package/dist/cjs/paneview/paneview.js.map +1 -1
  11. package/dist/cjs/react.js.map +1 -1
  12. package/dist/cjs/splitview/splitview.js.map +1 -1
  13. package/dist/cjs/splitview/view.js.map +1 -1
  14. package/dist/dockview.amd.js +976 -228
  15. package/dist/dockview.amd.js.map +1 -1
  16. package/dist/dockview.amd.min.js +2 -2
  17. package/dist/dockview.amd.min.js.map +1 -1
  18. package/dist/dockview.amd.min.noStyle.js +2 -2
  19. package/dist/dockview.amd.min.noStyle.js.map +1 -1
  20. package/dist/dockview.amd.noStyle.js +976 -228
  21. package/dist/dockview.amd.noStyle.js.map +1 -1
  22. package/dist/dockview.cjs.js +976 -228
  23. package/dist/dockview.cjs.js.map +1 -1
  24. package/dist/dockview.esm.js +976 -228
  25. package/dist/dockview.esm.js.map +1 -1
  26. package/dist/dockview.esm.min.js +2 -2
  27. package/dist/dockview.esm.min.js.map +1 -1
  28. package/dist/dockview.js +976 -228
  29. package/dist/dockview.js.map +1 -1
  30. package/dist/dockview.min.js +2 -2
  31. package/dist/dockview.min.js.map +1 -1
  32. package/dist/dockview.min.noStyle.js +2 -2
  33. package/dist/dockview.min.noStyle.js.map +1 -1
  34. package/dist/dockview.noStyle.js +976 -228
  35. package/dist/dockview.noStyle.js.map +1 -1
  36. package/dist/esm/dockview/defaultTab.js.map +1 -1
  37. package/dist/esm/dockview/dockview.d.ts +4 -1
  38. package/dist/esm/dockview/dockview.d.ts.map +1 -1
  39. package/dist/esm/dockview/dockview.js +11 -0
  40. package/dist/esm/dockview/dockview.js.map +1 -1
  41. package/dist/esm/dockview/reactWatermarkPart.js.map +1 -1
  42. package/dist/esm/gridview/gridview.js.map +1 -1
  43. package/dist/esm/paneview/paneview.js.map +1 -1
  44. package/dist/esm/react.js.map +1 -1
  45. package/dist/esm/splitview/splitview.js.map +1 -1
  46. package/package.json +2 -2
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * dockview
3
- * @version 1.8.5
3
+ * @version 1.9.1
4
4
  * @link https://github.com/mathuo/dockview
5
5
  * @license MIT
6
6
  */
@@ -256,6 +256,14 @@
256
256
  // noop
257
257
  },
258
258
  };
259
+ function from(func) {
260
+ return {
261
+ dispose: () => {
262
+ func();
263
+ },
264
+ };
265
+ }
266
+ Disposable.from = from;
259
267
  })(Disposable || (Disposable = {}));
260
268
  class CompositeDisposable {
261
269
  get isDisposed() {
@@ -440,6 +448,61 @@
440
448
  function quasiDefaultPrevented(event) {
441
449
  return event[QUASI_PREVENT_DEFAULT_KEY];
442
450
  }
451
+ function addStyles(document, styleSheetList) {
452
+ const styleSheets = Array.from(styleSheetList);
453
+ for (const styleSheet of styleSheets) {
454
+ if (styleSheet.href) {
455
+ const link = document.createElement('link');
456
+ link.href = styleSheet.href;
457
+ link.type = styleSheet.type;
458
+ link.rel = 'stylesheet';
459
+ document.head.appendChild(link);
460
+ }
461
+ let cssTexts = [];
462
+ try {
463
+ if (styleSheet.cssRules) {
464
+ cssTexts = Array.from(styleSheet.cssRules).map((rule) => rule.cssText);
465
+ }
466
+ }
467
+ catch (err) {
468
+ // security errors (lack of permissions), ignore
469
+ }
470
+ for (const rule of cssTexts) {
471
+ const style = document.createElement('style');
472
+ style.appendChild(document.createTextNode(rule));
473
+ document.head.appendChild(style);
474
+ }
475
+ }
476
+ }
477
+ function getDomNodePagePosition(domNode) {
478
+ const { left, top, width, height } = domNode.getBoundingClientRect();
479
+ return {
480
+ left: left + window.scrollX,
481
+ top: top + window.scrollY,
482
+ width: width,
483
+ height: height,
484
+ };
485
+ }
486
+ /**
487
+ * Check whether an element is in the DOM (including the Shadow DOM)
488
+ * @see https://terodox.tech/how-to-tell-if-an-element-is-in-the-dom-including-the-shadow-dom/
489
+ */
490
+ function isInDocument(element) {
491
+ let currentElement = element;
492
+ while (currentElement === null || currentElement === void 0 ? void 0 : currentElement.parentNode) {
493
+ if (currentElement.parentNode === document) {
494
+ return true;
495
+ }
496
+ else if (currentElement.parentNode instanceof DocumentFragment) {
497
+ // handle shadow DOMs
498
+ currentElement = currentElement.parentNode.host;
499
+ }
500
+ else {
501
+ currentElement = currentElement.parentNode;
502
+ }
503
+ }
504
+ return false;
505
+ }
443
506
 
444
507
  function tail(arr) {
445
508
  if (arr.length === 0) {
@@ -637,6 +700,9 @@
637
700
  Sizing.Invisible = Invisible;
638
701
  })(exports.Sizing || (exports.Sizing = {}));
639
702
  class Splitview {
703
+ get contentSize() {
704
+ return this._contentSize;
705
+ }
640
706
  get size() {
641
707
  return this._size;
642
708
  }
@@ -702,7 +768,7 @@
702
768
  this.sashes = [];
703
769
  this._size = 0;
704
770
  this._orthogonalSize = 0;
705
- this.contentSize = 0;
771
+ this._contentSize = 0;
706
772
  this._proportions = undefined;
707
773
  this._startSnappingEnabled = true;
708
774
  this._endSnappingEnabled = true;
@@ -821,7 +887,7 @@
821
887
  );
822
888
  });
823
889
  // Initialize content size and proportions for first layout
824
- this.contentSize = this.viewItems.reduce((r, i) => r + i.size, 0);
890
+ this._contentSize = this.viewItems.reduce((r, i) => r + i.size, 0);
825
891
  this.saveProportions();
826
892
  }
827
893
  }
@@ -1095,7 +1161,7 @@
1095
1161
  this.addView(view, sizing, to);
1096
1162
  }
1097
1163
  layout(size, orthogonalSize) {
1098
- const previousSize = Math.max(this.size, this.contentSize);
1164
+ const previousSize = Math.max(this.size, this._contentSize);
1099
1165
  this.size = size;
1100
1166
  this.orthogonalSize = orthogonalSize;
1101
1167
  if (!this.proportions) {
@@ -1105,9 +1171,23 @@
1105
1171
  this.resize(this.viewItems.length - 1, size - previousSize, undefined, lowPriorityIndexes, highPriorityIndexes);
1106
1172
  }
1107
1173
  else {
1174
+ let total = 0;
1108
1175
  for (let i = 0; i < this.viewItems.length; i++) {
1109
1176
  const item = this.viewItems[i];
1110
- item.size = clamp(Math.round(this.proportions[i] * size), item.minimumSize, item.maximumSize);
1177
+ const proportion = this.proportions[i];
1178
+ if (typeof proportion === 'number') {
1179
+ total += proportion;
1180
+ }
1181
+ else {
1182
+ size -= item.size;
1183
+ }
1184
+ }
1185
+ for (let i = 0; i < this.viewItems.length; i++) {
1186
+ const item = this.viewItems[i];
1187
+ const proportion = this.proportions[i];
1188
+ if (typeof proportion === 'number' && total > 0) {
1189
+ item.size = clamp(Math.round((proportion * size) / total), item.minimumSize, item.maximumSize);
1190
+ }
1111
1191
  }
1112
1192
  }
1113
1193
  this.distributeEmptySpace();
@@ -1144,12 +1224,12 @@
1144
1224
  }
1145
1225
  }
1146
1226
  saveProportions() {
1147
- if (this.proportionalLayout && this.contentSize > 0) {
1148
- this._proportions = this.viewItems.map((i) => i.size / this.contentSize);
1227
+ if (this.proportionalLayout && this._contentSize > 0) {
1228
+ this._proportions = this.viewItems.map((i) => i.visible ? i.size / this._contentSize : undefined);
1149
1229
  }
1150
1230
  }
1151
1231
  layoutViews() {
1152
- this.contentSize = this.viewItems.reduce((r, i) => r + i.size, 0);
1232
+ this._contentSize = this.viewItems.reduce((r, i) => r + i.size, 0);
1153
1233
  let sum = 0;
1154
1234
  const x = [];
1155
1235
  this.updateSashEnablement();
@@ -1243,7 +1323,7 @@
1243
1323
  }
1244
1324
  else if (snappedAfter &&
1245
1325
  collapsesDown[index] &&
1246
- (position < this.contentSize || this.endSnappingEnabled)) {
1326
+ (position < this._contentSize || this.endSnappingEnabled)) {
1247
1327
  this.updateSash(sash, exports.SashState.MAXIMUM);
1248
1328
  }
1249
1329
  else {
@@ -1532,7 +1612,6 @@
1532
1612
  setVisible(visible) {
1533
1613
  if (this.view.setVisible) {
1534
1614
  this.view.setVisible(visible);
1535
- this._onDidChange.fire({});
1536
1615
  }
1537
1616
  }
1538
1617
  layout(size, orthogonalSize) {
@@ -1564,10 +1643,14 @@
1564
1643
  get minimumSize() {
1565
1644
  return this.children.length === 0
1566
1645
  ? 0
1567
- : Math.max(...this.children.map((c) => c.minimumOrthogonalSize));
1646
+ : Math.max(...this.children.map((c, index) => this.splitview.isViewVisible(index)
1647
+ ? c.minimumOrthogonalSize
1648
+ : 0));
1568
1649
  }
1569
1650
  get maximumSize() {
1570
- return Math.min(...this.children.map((c) => c.maximumOrthogonalSize));
1651
+ return Math.min(...this.children.map((c, index) => this.splitview.isViewVisible(index)
1652
+ ? c.maximumOrthogonalSize
1653
+ : Number.POSITIVE_INFINITY));
1571
1654
  }
1572
1655
  get minimumOrthogonalSize() {
1573
1656
  return this.splitview.minimumSize;
@@ -1625,6 +1708,8 @@
1625
1708
  this.children = [];
1626
1709
  this._onDidChange = new Emitter();
1627
1710
  this.onDidChange = this._onDidChange.event;
1711
+ this._onDidVisibilityChange = new Emitter();
1712
+ this.onDidVisibilityChange = this._onDidVisibilityChange.event;
1628
1713
  this._orthogonalSize = orthogonalSize;
1629
1714
  this._size = size;
1630
1715
  this.element = document.createElement('div');
@@ -1659,7 +1744,7 @@
1659
1744
  styles,
1660
1745
  });
1661
1746
  }
1662
- this.addDisposables(this._onDidChange, this.splitview.onDidSashEnd(() => {
1747
+ this.addDisposables(this._onDidChange, this._onDidVisibilityChange, this.splitview.onDidSashEnd(() => {
1663
1748
  this._onDidChange.fire({});
1664
1749
  }));
1665
1750
  this.setupChildrenEvents();
@@ -1682,7 +1767,15 @@
1682
1767
  if (this.splitview.isViewVisible(index) === visible) {
1683
1768
  return;
1684
1769
  }
1770
+ const wereAllChildrenHidden = this.splitview.contentSize === 0;
1685
1771
  this.splitview.setViewVisible(index, visible);
1772
+ const areAllChildrenHidden = this.splitview.contentSize === 0;
1773
+ // If all children are hidden then the parent should hide the entire splitview
1774
+ // If the entire splitview is hidden then the parent should show the splitview when a child is shown
1775
+ if ((visible && wereAllChildrenHidden) ||
1776
+ (!visible && areAllChildrenHidden)) {
1777
+ this._onDidVisibilityChange.fire(visible);
1778
+ }
1686
1779
  }
1687
1780
  moveChild(from, to) {
1688
1781
  if (from === to) {
@@ -1746,13 +1839,20 @@
1746
1839
  }
1747
1840
  setupChildrenEvents() {
1748
1841
  this._childrenDisposable.dispose();
1749
- this._childrenDisposable = exports.DockviewEvent.any(...this.children.map((c) => c.onDidChange))((e) => {
1842
+ this._childrenDisposable = new CompositeDisposable(exports.DockviewEvent.any(...this.children.map((c) => c.onDidChange))((e) => {
1750
1843
  /**
1751
1844
  * indicate a change has occured to allows any re-rendering but don't bubble
1752
1845
  * event because that was specific to this branch
1753
1846
  */
1754
1847
  this._onDidChange.fire({ size: e.orthogonalSize });
1755
- });
1848
+ }), ...this.children.map((c, i) => {
1849
+ if (c instanceof BranchNode) {
1850
+ return c.onDidVisibilityChange((visible) => {
1851
+ this.setChildVisible(i, visible);
1852
+ });
1853
+ }
1854
+ return Disposable.NONE;
1855
+ }));
1756
1856
  }
1757
1857
  dispose() {
1758
1858
  this._childrenDisposable.dispose();
@@ -1913,7 +2013,69 @@
1913
2013
  get maximumHeight() {
1914
2014
  return this.root.maximumHeight;
1915
2015
  }
2016
+ maximizedView() {
2017
+ var _a;
2018
+ return (_a = this._maximizedNode) === null || _a === void 0 ? void 0 : _a.view;
2019
+ }
2020
+ hasMaximizedView() {
2021
+ return this._maximizedNode !== undefined;
2022
+ }
2023
+ maximizeView(view) {
2024
+ const location = getGridLocation(view.element);
2025
+ const [_, node] = this.getNode(location);
2026
+ if (!(node instanceof LeafNode)) {
2027
+ return;
2028
+ }
2029
+ if (this._maximizedNode === node) {
2030
+ return;
2031
+ }
2032
+ if (this.hasMaximizedView()) {
2033
+ this.exitMaximizedView();
2034
+ }
2035
+ function hideAllViewsBut(parent, exclude) {
2036
+ for (let i = 0; i < parent.children.length; i++) {
2037
+ const child = parent.children[i];
2038
+ if (child instanceof LeafNode) {
2039
+ if (child !== exclude) {
2040
+ parent.setChildVisible(i, false);
2041
+ }
2042
+ }
2043
+ else {
2044
+ hideAllViewsBut(child, exclude);
2045
+ }
2046
+ }
2047
+ }
2048
+ hideAllViewsBut(this.root, node);
2049
+ this._maximizedNode = node;
2050
+ this._onDidMaxmizedNodeChange.fire();
2051
+ }
2052
+ exitMaximizedView() {
2053
+ if (!this._maximizedNode) {
2054
+ return;
2055
+ }
2056
+ function showViewsInReverseOrder(parent) {
2057
+ for (let index = parent.children.length - 1; index >= 0; index--) {
2058
+ const child = parent.children[index];
2059
+ if (child instanceof LeafNode) {
2060
+ parent.setChildVisible(index, true);
2061
+ }
2062
+ else {
2063
+ showViewsInReverseOrder(child);
2064
+ }
2065
+ }
2066
+ }
2067
+ showViewsInReverseOrder(this.root);
2068
+ this._maximizedNode = undefined;
2069
+ this._onDidMaxmizedNodeChange.fire();
2070
+ }
1916
2071
  serialize() {
2072
+ if (this.hasMaximizedView()) {
2073
+ /**
2074
+ * do not persist maximized view state but we must first exit any maximized views
2075
+ * before serialization to ensure the correct dimensions are persisted
2076
+ */
2077
+ this.exitMaximizedView();
2078
+ }
1917
2079
  const root = serializeBranchNode(this.getView(), this.orientation);
1918
2080
  return {
1919
2081
  root,
@@ -1925,7 +2087,9 @@
1925
2087
  dispose() {
1926
2088
  this.disposable.dispose();
1927
2089
  this._onDidChange.dispose();
2090
+ this._onDidMaxmizedNodeChange.dispose();
1928
2091
  this.root.dispose();
2092
+ this._maximizedNode = undefined;
1929
2093
  this.element.remove();
1930
2094
  }
1931
2095
  clear() {
@@ -1966,6 +2130,7 @@
1966
2130
  const oldRoot = this._root;
1967
2131
  if (oldRoot) {
1968
2132
  oldRoot.dispose();
2133
+ this._maximizedNode = undefined;
1969
2134
  this.element.removeChild(oldRoot.element);
1970
2135
  }
1971
2136
  this._root = root;
@@ -2052,9 +2217,12 @@
2052
2217
  constructor(proportionalLayout, styles, orientation) {
2053
2218
  this.proportionalLayout = proportionalLayout;
2054
2219
  this.styles = styles;
2220
+ this._maximizedNode = undefined;
2055
2221
  this.disposable = new MutableDisposable();
2056
2222
  this._onDidChange = new Emitter();
2057
2223
  this.onDidChange = this._onDidChange.event;
2224
+ this._onDidMaxmizedNodeChange = new Emitter();
2225
+ this.onDidMaxmizedNodeChange = this._onDidMaxmizedNodeChange.event;
2058
2226
  this.element = document.createElement('div');
2059
2227
  this.element.className = 'grid-view';
2060
2228
  this.root = new BranchNode(orientation, proportionalLayout, styles, 0, 0);
@@ -2068,6 +2236,9 @@
2068
2236
  return parent.isChildVisible(index);
2069
2237
  }
2070
2238
  setViewVisible(location, visible) {
2239
+ if (this.hasMaximizedView()) {
2240
+ this.exitMaximizedView();
2241
+ }
2071
2242
  const [rest, index] = tail(location);
2072
2243
  const [, parent] = this.getNode(rest);
2073
2244
  if (!(parent instanceof BranchNode)) {
@@ -2076,6 +2247,9 @@
2076
2247
  parent.setChildVisible(index, visible);
2077
2248
  }
2078
2249
  moveView(parentLocation, from, to) {
2250
+ if (this.hasMaximizedView()) {
2251
+ this.exitMaximizedView();
2252
+ }
2079
2253
  const [, parent] = this.getNode(parentLocation);
2080
2254
  if (!(parent instanceof BranchNode)) {
2081
2255
  throw new Error('Invalid location');
@@ -2083,6 +2257,9 @@
2083
2257
  parent.moveChild(from, to);
2084
2258
  }
2085
2259
  addView(view, size, location) {
2260
+ if (this.hasMaximizedView()) {
2261
+ this.exitMaximizedView();
2262
+ }
2086
2263
  const [rest, index] = tail(location);
2087
2264
  const [pathToParent, parent] = this.getNode(rest);
2088
2265
  if (parent instanceof BranchNode) {
@@ -2115,6 +2292,9 @@
2115
2292
  return this.removeView(location, sizing);
2116
2293
  }
2117
2294
  removeView(location, sizing) {
2295
+ if (this.hasMaximizedView()) {
2296
+ this.exitMaximizedView();
2297
+ }
2118
2298
  const [rest, index] = tail(location);
2119
2299
  const [pathToParent, parent] = this.getNode(rest);
2120
2300
  if (!(parent instanceof BranchNode)) {
@@ -2852,6 +3032,24 @@
2852
3032
  moveToPrevious(options) {
2853
3033
  this.component.moveToPrevious(options);
2854
3034
  }
3035
+ maximizeGroup(panel) {
3036
+ this.component.maximizeGroup(panel.group);
3037
+ }
3038
+ hasMaximizedGroup() {
3039
+ return this.component.hasMaximizedGroup();
3040
+ }
3041
+ exitMaxmizedGroup() {
3042
+ this.component.exitMaximizedGroup();
3043
+ }
3044
+ get onDidMaxmizedGroupChange() {
3045
+ return this.component.onDidMaxmizedGroupChange;
3046
+ }
3047
+ /**
3048
+ * Add a popout group in a new Window
3049
+ */
3050
+ addPopoutGroup(item, options) {
3051
+ this.component.addPopoutGroup(item, options);
3052
+ }
2855
3053
  }
2856
3054
 
2857
3055
  class DragAndDropObserver extends CompositeDisposable {
@@ -2862,36 +3060,48 @@
2862
3060
  this.target = null;
2863
3061
  this.registerListeners();
2864
3062
  }
3063
+ onDragEnter(e) {
3064
+ this.target = e.target;
3065
+ this.callbacks.onDragEnter(e);
3066
+ }
3067
+ onDragOver(e) {
3068
+ e.preventDefault(); // needed so that the drop event fires (https://stackoverflow.com/questions/21339924/drop-event-not-firing-in-chrome)
3069
+ if (this.callbacks.onDragOver) {
3070
+ this.callbacks.onDragOver(e);
3071
+ }
3072
+ }
3073
+ onDragLeave(e) {
3074
+ if (this.target === e.target) {
3075
+ this.target = null;
3076
+ this.callbacks.onDragLeave(e);
3077
+ }
3078
+ }
3079
+ onDragEnd(e) {
3080
+ this.target = null;
3081
+ this.callbacks.onDragEnd(e);
3082
+ }
3083
+ onDrop(e) {
3084
+ this.callbacks.onDrop(e);
3085
+ }
2865
3086
  registerListeners() {
2866
3087
  this.addDisposables(addDisposableListener(this.element, 'dragenter', (e) => {
2867
- this.target = e.target;
2868
- this.callbacks.onDragEnter(e);
3088
+ this.onDragEnter(e);
2869
3089
  }, true));
2870
3090
  this.addDisposables(addDisposableListener(this.element, 'dragover', (e) => {
2871
- e.preventDefault(); // needed so that the drop event fires (https://stackoverflow.com/questions/21339924/drop-event-not-firing-in-chrome)
2872
- if (this.callbacks.onDragOver) {
2873
- this.callbacks.onDragOver(e);
2874
- }
3091
+ this.onDragOver(e);
2875
3092
  }, true));
2876
3093
  this.addDisposables(addDisposableListener(this.element, 'dragleave', (e) => {
2877
- if (this.target === e.target) {
2878
- this.target = null;
2879
- this.callbacks.onDragLeave(e);
2880
- }
3094
+ this.onDragLeave(e);
2881
3095
  }));
2882
3096
  this.addDisposables(addDisposableListener(this.element, 'dragend', (e) => {
2883
- this.target = null;
2884
- this.callbacks.onDragEnd(e);
3097
+ this.onDragEnd(e);
2885
3098
  }));
2886
3099
  this.addDisposables(addDisposableListener(this.element, 'drop', (e) => {
2887
- this.callbacks.onDrop(e);
3100
+ this.onDrop(e);
2888
3101
  }));
2889
3102
  }
2890
3103
  }
2891
3104
 
2892
- function numberOrFallback(maybeNumber, fallback) {
2893
- return typeof maybeNumber === 'number' ? maybeNumber : fallback;
2894
- }
2895
3105
  function directionToPosition(direction) {
2896
3106
  switch (direction) {
2897
3107
  case 'above':
@@ -2924,6 +3134,16 @@
2924
3134
  throw new Error(`invalid position '${position}'`);
2925
3135
  }
2926
3136
  }
3137
+ const DEFAULT_ACTIVATION_SIZE = {
3138
+ value: 20,
3139
+ type: 'percentage',
3140
+ };
3141
+ const DEFAULT_SIZE = {
3142
+ value: 50,
3143
+ type: 'percentage',
3144
+ };
3145
+ const SMALL_WIDTH_BOUNDARY = 100;
3146
+ const SMALL_HEIGHT_BOUNDARY = 100;
2927
3147
  class Droptarget extends CompositeDisposable {
2928
3148
  get state() {
2929
3149
  return this._state;
@@ -2936,7 +3156,7 @@
2936
3156
  this.onDrop = this._onDrop.event;
2937
3157
  // use a set to take advantage of #<set>.has
2938
3158
  this._acceptedTargetZonesSet = new Set(this.options.acceptedTargetZones);
2939
- this.addDisposables(this._onDrop, new DragAndDropObserver(this.element, {
3159
+ this.dnd = new DragAndDropObserver(this.element, {
2940
3160
  onDragEnter: () => undefined,
2941
3161
  onDragOver: (e) => {
2942
3162
  if (this._acceptedTargetZonesSet.size === 0) {
@@ -2984,7 +3204,7 @@
2984
3204
  this.element.append(this.targetElement);
2985
3205
  }
2986
3206
  this.toggleClasses(quadrant, width, height);
2987
- this.setState(quadrant);
3207
+ this._state = quadrant;
2988
3208
  },
2989
3209
  onDragLeave: () => {
2990
3210
  this.removeDropTarget();
@@ -3003,11 +3223,15 @@
3003
3223
  this._onDrop.fire({ position: state, nativeEvent: e });
3004
3224
  }
3005
3225
  },
3006
- }));
3226
+ });
3227
+ this.addDisposables(this._onDrop, this.dnd);
3007
3228
  }
3008
3229
  setTargetZones(acceptedTargetZones) {
3009
3230
  this._acceptedTargetZonesSet = new Set(acceptedTargetZones);
3010
3231
  }
3232
+ setOverlayModel(model) {
3233
+ this.options.overlayModel = model;
3234
+ }
3011
3235
  dispose() {
3012
3236
  this.removeDropTarget();
3013
3237
  super.dispose();
@@ -3019,19 +3243,19 @@
3019
3243
  event[Droptarget.USED_EVENT_ID] = true;
3020
3244
  }
3021
3245
  /**
3022
- * Check is the event has already been used by another instance od DropTarget
3246
+ * Check is the event has already been used by another instance of DropTarget
3023
3247
  */
3024
3248
  isAlreadyUsed(event) {
3025
3249
  const value = event[Droptarget.USED_EVENT_ID];
3026
3250
  return typeof value === 'boolean' && value;
3027
3251
  }
3028
3252
  toggleClasses(quadrant, width, height) {
3029
- var _a, _b, _c, _d;
3253
+ var _a, _b;
3030
3254
  if (!this.overlayElement) {
3031
3255
  return;
3032
3256
  }
3033
- const isSmallX = width < 100;
3034
- const isSmallY = height < 100;
3257
+ const isSmallX = width < SMALL_WIDTH_BOUNDARY;
3258
+ const isSmallY = height < SMALL_HEIGHT_BOUNDARY;
3035
3259
  const isLeft = quadrant === 'left';
3036
3260
  const isRight = quadrant === 'right';
3037
3261
  const isTop = quadrant === 'top';
@@ -3040,20 +3264,17 @@
3040
3264
  const leftClass = !isSmallX && isLeft;
3041
3265
  const topClass = !isSmallY && isTop;
3042
3266
  const bottomClass = !isSmallY && isBottom;
3043
- let size = 0.5;
3044
- if (((_b = (_a = this.options.overlayModel) === null || _a === void 0 ? void 0 : _a.size) === null || _b === void 0 ? void 0 : _b.type) === 'percentage') {
3045
- size = clamp(this.options.overlayModel.size.value, 0, 100) / 100;
3267
+ let size = 1;
3268
+ const sizeOptions = (_b = (_a = this.options.overlayModel) === null || _a === void 0 ? void 0 : _a.size) !== null && _b !== void 0 ? _b : DEFAULT_SIZE;
3269
+ if (sizeOptions.type === 'percentage') {
3270
+ size = clamp(sizeOptions.value, 0, 100) / 100;
3046
3271
  }
3047
- if (((_d = (_c = this.options.overlayModel) === null || _c === void 0 ? void 0 : _c.size) === null || _d === void 0 ? void 0 : _d.type) === 'pixels') {
3272
+ else {
3048
3273
  if (rightClass || leftClass) {
3049
- size =
3050
- clamp(0, this.options.overlayModel.size.value, width) /
3051
- width;
3274
+ size = clamp(0, sizeOptions.value, width) / width;
3052
3275
  }
3053
3276
  if (topClass || bottomClass) {
3054
- size =
3055
- clamp(0, this.options.overlayModel.size.value, height) /
3056
- height;
3277
+ size = clamp(0, sizeOptions.value, height) / height;
3057
3278
  }
3058
3279
  }
3059
3280
  const translate = (1 - size) / 2;
@@ -3075,39 +3296,22 @@
3075
3296
  transform = '';
3076
3297
  }
3077
3298
  this.overlayElement.style.transform = transform;
3078
- toggleClass(this.overlayElement, 'small-right', isSmallX && isRight);
3079
- toggleClass(this.overlayElement, 'small-left', isSmallX && isLeft);
3080
- toggleClass(this.overlayElement, 'small-top', isSmallY && isTop);
3081
- toggleClass(this.overlayElement, 'small-bottom', isSmallY && isBottom);
3082
- }
3083
- setState(quadrant) {
3084
- switch (quadrant) {
3085
- case 'top':
3086
- this._state = 'top';
3087
- break;
3088
- case 'left':
3089
- this._state = 'left';
3090
- break;
3091
- case 'bottom':
3092
- this._state = 'bottom';
3093
- break;
3094
- case 'right':
3095
- this._state = 'right';
3096
- break;
3097
- case 'center':
3098
- this._state = 'center';
3099
- break;
3100
- }
3299
+ toggleClass(this.overlayElement, 'dv-drop-target-small-vertical', isSmallY);
3300
+ toggleClass(this.overlayElement, 'dv-drop-target-small-horizontal', isSmallX);
3301
+ toggleClass(this.overlayElement, 'dv-drop-target-left', isLeft);
3302
+ toggleClass(this.overlayElement, 'dv-drop-target-right', isRight);
3303
+ toggleClass(this.overlayElement, 'dv-drop-target-top', isTop);
3304
+ toggleClass(this.overlayElement, 'dv-drop-target-bottom', isBottom);
3305
+ toggleClass(this.overlayElement, 'dv-drop-target-center', quadrant === 'center');
3101
3306
  }
3102
3307
  calculateQuadrant(overlayType, x, y, width, height) {
3103
- var _a, _b, _c, _d, _e, _f;
3104
- const isPercentage = ((_a = this.options.overlayModel) === null || _a === void 0 ? void 0 : _a.activationSize) === undefined ||
3105
- ((_c = (_b = this.options.overlayModel) === null || _b === void 0 ? void 0 : _b.activationSize) === null || _c === void 0 ? void 0 : _c.type) === 'percentage';
3106
- const value = numberOrFallback((_f = (_e = (_d = this.options) === null || _d === void 0 ? void 0 : _d.overlayModel) === null || _e === void 0 ? void 0 : _e.activationSize) === null || _f === void 0 ? void 0 : _f.value, 20);
3308
+ var _a, _b;
3309
+ const activationSizeOptions = (_b = (_a = this.options.overlayModel) === null || _a === void 0 ? void 0 : _a.activationSize) !== null && _b !== void 0 ? _b : DEFAULT_ACTIVATION_SIZE;
3310
+ const isPercentage = activationSizeOptions.type === 'percentage';
3107
3311
  if (isPercentage) {
3108
- return calculateQuadrantAsPercentage(overlayType, x, y, width, height, value);
3312
+ return calculateQuadrantAsPercentage(overlayType, x, y, width, height, activationSizeOptions.value);
3109
3313
  }
3110
- return calculateQuadrantAsPixels(overlayType, x, y, width, height, value);
3314
+ return calculateQuadrantAsPixels(overlayType, x, y, width, height, activationSizeOptions.value);
3111
3315
  }
3112
3316
  removeDropTarget() {
3113
3317
  if (this.targetElement) {
@@ -3159,12 +3363,22 @@
3159
3363
  return 'center';
3160
3364
  }
3161
3365
 
3366
+ exports.DockviewDropTargets = void 0;
3367
+ (function (DockviewDropTargets) {
3368
+ DockviewDropTargets[DockviewDropTargets["Tab"] = 0] = "Tab";
3369
+ DockviewDropTargets[DockviewDropTargets["Panel"] = 1] = "Panel";
3370
+ DockviewDropTargets[DockviewDropTargets["TabContainer"] = 2] = "TabContainer";
3371
+ DockviewDropTargets[DockviewDropTargets["Edge"] = 3] = "Edge";
3372
+ })(exports.DockviewDropTargets || (exports.DockviewDropTargets = {}));
3373
+
3162
3374
  class ContentContainer extends CompositeDisposable {
3163
3375
  get element() {
3164
3376
  return this._element;
3165
3377
  }
3166
- constructor() {
3378
+ constructor(accessor, group) {
3167
3379
  super();
3380
+ this.accessor = accessor;
3381
+ this.group = group;
3168
3382
  this.disposable = new MutableDisposable();
3169
3383
  this._onDidFocus = new Emitter();
3170
3384
  this.onDidFocus = this._onDidFocus.event;
@@ -3174,11 +3388,38 @@
3174
3388
  this._element.className = 'content-container';
3175
3389
  this._element.tabIndex = -1;
3176
3390
  this.addDisposables(this._onDidFocus, this._onDidBlur);
3177
- // for hosted containers
3178
- // 1) register a drop target on the host
3179
- // 2) register window dragStart events to disable pointer events
3180
- // 3) register dragEnd events
3181
- // 4) register mouseMove events (if no buttons are present we take this as a dragEnd event)
3391
+ this.dropTarget = new Droptarget(this.element, {
3392
+ acceptedTargetZones: ['top', 'bottom', 'left', 'right', 'center'],
3393
+ canDisplayOverlay: (event, position) => {
3394
+ if (this.group.locked === 'no-drop-target' ||
3395
+ (this.group.locked && position === 'center')) {
3396
+ return false;
3397
+ }
3398
+ const data = getPanelData();
3399
+ if (!data &&
3400
+ event.shiftKey &&
3401
+ this.group.location !== 'floating') {
3402
+ return false;
3403
+ }
3404
+ if (data && data.viewId === this.accessor.id) {
3405
+ if (data.groupId === this.group.id) {
3406
+ if (position === 'center') {
3407
+ // don't allow to drop on self for center position
3408
+ return false;
3409
+ }
3410
+ if (data.panelId === null) {
3411
+ // don't allow group move to drop anywhere on self
3412
+ return false;
3413
+ }
3414
+ }
3415
+ const groupHasOnePanelAndIsActiveDragElement = this.group.panels.length === 1 &&
3416
+ data.groupId === this.group.id;
3417
+ return !groupHasOnePanelAndIsActiveDragElement;
3418
+ }
3419
+ return this.group.canDisplayOverlay(event, position, exports.DockviewDropTargets.Panel);
3420
+ },
3421
+ });
3422
+ this.addDisposables(this.dropTarget);
3182
3423
  }
3183
3424
  show() {
3184
3425
  this.element.style.display = '';
@@ -3186,23 +3427,43 @@
3186
3427
  hide() {
3187
3428
  this.element.style.display = 'none';
3188
3429
  }
3189
- openPanel(panel) {
3190
- var _a;
3191
- if (this.panel === panel) {
3192
- return;
3193
- }
3194
- if (this.panel) {
3195
- if ((_a = this.panel.view) === null || _a === void 0 ? void 0 : _a.content) {
3196
- this._element.removeChild(this.panel.view.content.element);
3197
- }
3198
- this.panel = undefined;
3430
+ renderPanel(panel, options = { asActive: true }) {
3431
+ const doRender = options.asActive ||
3432
+ (this.panel && this.group.isPanelActive(this.panel));
3433
+ if (this.panel &&
3434
+ this.panel.view.content.element.parentElement === this._element) {
3435
+ /**
3436
+ * If the currently attached panel is mounted directly to the content then remove it
3437
+ */
3438
+ this._element.removeChild(this.panel.view.content.element);
3199
3439
  }
3200
3440
  this.panel = panel;
3201
- const disposable = new CompositeDisposable();
3202
- if (this.panel.view) {
3203
- const _onDidFocus = this.panel.view.content.onDidFocus;
3204
- const _onDidBlur = this.panel.view.content.onDidBlur;
3205
- const focusTracker = trackFocus(this._element);
3441
+ let container;
3442
+ switch (panel.api.renderer) {
3443
+ case 'onlyWhenVisibile':
3444
+ this.accessor.overlayRenderContainer.detatch(panel);
3445
+ if (this.panel) {
3446
+ if (doRender) {
3447
+ this._element.appendChild(this.panel.view.content.element);
3448
+ }
3449
+ }
3450
+ container = this._element;
3451
+ break;
3452
+ case 'always':
3453
+ if (panel.view.content.element.parentElement === this._element) {
3454
+ this._element.removeChild(panel.view.content.element);
3455
+ }
3456
+ container = this.accessor.overlayRenderContainer.attach({
3457
+ panel,
3458
+ referenceContainer: this,
3459
+ });
3460
+ break;
3461
+ }
3462
+ if (doRender) {
3463
+ const _onDidFocus = panel.view.content.onDidFocus;
3464
+ const _onDidBlur = panel.view.content.onDidBlur;
3465
+ const focusTracker = trackFocus(container);
3466
+ const disposable = new CompositeDisposable();
3206
3467
  disposable.addDisposables(focusTracker, focusTracker.onDidFocus(() => this._onDidFocus.fire()), focusTracker.onDidBlur(() => this._onDidBlur.fire()));
3207
3468
  if (_onDidFocus) {
3208
3469
  disposable.addDisposables(_onDidFocus(() => this._onDidFocus.fire()));
@@ -3210,17 +3471,23 @@
3210
3471
  if (_onDidBlur) {
3211
3472
  disposable.addDisposables(_onDidBlur(() => this._onDidBlur.fire()));
3212
3473
  }
3213
- this._element.appendChild(this.panel.view.content.element);
3474
+ this.disposable.value = disposable;
3214
3475
  }
3215
- this.disposable.value = disposable;
3476
+ }
3477
+ openPanel(panel) {
3478
+ if (this.panel === panel) {
3479
+ return;
3480
+ }
3481
+ this.renderPanel(panel);
3216
3482
  }
3217
3483
  layout(_width, _height) {
3218
3484
  // noop
3219
3485
  }
3220
3486
  closePanel() {
3221
- var _a, _b, _c;
3222
- if ((_c = (_b = (_a = this.panel) === null || _a === void 0 ? void 0 : _a.view) === null || _b === void 0 ? void 0 : _b.content) === null || _c === void 0 ? void 0 : _c.element) {
3223
- this._element.removeChild(this.panel.view.content.element);
3487
+ if (this.panel) {
3488
+ if (this.accessor.options.defaultRenderer === 'onlyWhenVisibile') {
3489
+ this._element.removeChild(this.panel.view.content.element);
3490
+ }
3224
3491
  this.panel = undefined;
3225
3492
  }
3226
3493
  }
@@ -3230,14 +3497,6 @@
3230
3497
  }
3231
3498
  }
3232
3499
 
3233
- exports.DockviewDropTargets = void 0;
3234
- (function (DockviewDropTargets) {
3235
- DockviewDropTargets[DockviewDropTargets["Tab"] = 0] = "Tab";
3236
- DockviewDropTargets[DockviewDropTargets["Panel"] = 1] = "Panel";
3237
- DockviewDropTargets[DockviewDropTargets["TabContainer"] = 2] = "TabContainer";
3238
- DockviewDropTargets[DockviewDropTargets["Edge"] = 3] = "Edge";
3239
- })(exports.DockviewDropTargets || (exports.DockviewDropTargets = {}));
3240
-
3241
3500
  class DragHandler extends CompositeDisposable {
3242
3501
  constructor(el) {
3243
3502
  super();
@@ -3412,7 +3671,7 @@
3412
3671
  }, true));
3413
3672
  }
3414
3673
  isCancelled(_event) {
3415
- if (this.group.api.isFloating && !_event.shiftKey) {
3674
+ if (this.group.api.location === 'floating' && !_event.shiftKey) {
3416
3675
  return true;
3417
3676
  }
3418
3677
  return false;
@@ -3614,7 +3873,7 @@
3614
3873
  const isFloatingGroupsEnabled = !this.accessor.options.disableFloatingGroups;
3615
3874
  if (isFloatingGroupsEnabled &&
3616
3875
  event.shiftKey &&
3617
- !this.group.api.isFloating) {
3876
+ this.group.api.location !== 'floating') {
3618
3877
  event.preventDefault();
3619
3878
  const { top, left } = this.element.getBoundingClientRect();
3620
3879
  const { top: rootTop, left: rootLeft } = this.accessor.element.getBoundingClientRect();
@@ -3679,7 +3938,7 @@
3679
3938
  }), tab.onChanged((event) => {
3680
3939
  var _a;
3681
3940
  const isFloatingGroupsEnabled = !this.accessor.options.disableFloatingGroups;
3682
- const isFloatingWithOnePanel = this.group.api.isFloating && this.size === 1;
3941
+ const isFloatingWithOnePanel = this.group.api.location === 'floating' && this.size === 1;
3683
3942
  if (isFloatingGroupsEnabled &&
3684
3943
  !isFloatingWithOnePanel &&
3685
3944
  event.shiftKey) {
@@ -3762,15 +4021,37 @@
3762
4021
  }
3763
4022
  return isAncestor(document.activeElement, this.contentContainer.element);
3764
4023
  }
3765
- get isFloating() {
3766
- return this._isFloating;
3767
- }
3768
- set isFloating(value) {
3769
- this._isFloating = value;
3770
- this.dropTarget.setTargetZones(value ? ['center'] : ['top', 'bottom', 'left', 'right', 'center']);
3771
- toggleClass(this.container, 'dv-groupview-floating', value);
3772
- this.groupPanel.api._onDidFloatingStateChange.fire({
3773
- isFloating: this.isFloating,
4024
+ get location() {
4025
+ return this._location;
4026
+ }
4027
+ set location(value) {
4028
+ this._location = value;
4029
+ toggleClass(this.container, 'dv-groupview-floating', false);
4030
+ toggleClass(this.container, 'dv-groupview-popout', false);
4031
+ switch (value) {
4032
+ case 'grid':
4033
+ this.contentContainer.dropTarget.setTargetZones([
4034
+ 'top',
4035
+ 'bottom',
4036
+ 'left',
4037
+ 'right',
4038
+ 'center',
4039
+ ]);
4040
+ break;
4041
+ case 'floating':
4042
+ this.contentContainer.dropTarget.setTargetZones(['center']);
4043
+ this.contentContainer.dropTarget.setTargetZones(value
4044
+ ? ['center']
4045
+ : ['top', 'bottom', 'left', 'right', 'center']);
4046
+ toggleClass(this.container, 'dv-groupview-floating', true);
4047
+ break;
4048
+ case 'popout':
4049
+ this.contentContainer.dropTarget.setTargetZones(['center']);
4050
+ toggleClass(this.container, 'dv-groupview-popout', true);
4051
+ break;
4052
+ }
4053
+ this.groupPanel.api._onDidLocationChange.fire({
4054
+ location: this.location,
3774
4055
  });
3775
4056
  }
3776
4057
  constructor(container, accessor, id, options, groupPanel) {
@@ -3783,7 +4064,7 @@
3783
4064
  this.groupPanel = groupPanel;
3784
4065
  this._isGroupActive = false;
3785
4066
  this._locked = false;
3786
- this._isFloating = false;
4067
+ this._location = 'grid';
3787
4068
  this.mostRecentlyUsed = [];
3788
4069
  this._onDidChange = new Emitter();
3789
4070
  this.onDidChange = this._onDidChange.event;
@@ -3806,35 +4087,7 @@
3806
4087
  this.onDidActivePanelChange = this._onDidActivePanelChange.event;
3807
4088
  toggleClass(this.container, 'groupview', true);
3808
4089
  this.tabsContainer = new TabsContainer(this.accessor, this.groupPanel);
3809
- this.contentContainer = new ContentContainer();
3810
- this.dropTarget = new Droptarget(this.contentContainer.element, {
3811
- acceptedTargetZones: ['top', 'bottom', 'left', 'right', 'center'],
3812
- canDisplayOverlay: (event, position) => {
3813
- if (this.locked === 'no-drop-target' ||
3814
- (this.locked && position === 'center')) {
3815
- return false;
3816
- }
3817
- const data = getPanelData();
3818
- if (!data && event.shiftKey && !this.isFloating) {
3819
- return false;
3820
- }
3821
- if (data && data.viewId === this.accessor.id) {
3822
- if (data.groupId === this.id) {
3823
- if (position === 'center') {
3824
- // don't allow to drop on self for center position
3825
- return false;
3826
- }
3827
- if (data.panelId === null) {
3828
- // don't allow group move to drop anywhere on self
3829
- return false;
3830
- }
3831
- }
3832
- const groupHasOnePanelAndIsActiveDragElement = this._panels.length === 1 && data.groupId === this.id;
3833
- return !groupHasOnePanelAndIsActiveDragElement;
3834
- }
3835
- return this.canDisplayOverlay(event, position, exports.DockviewDropTargets.Panel);
3836
- },
3837
- });
4090
+ this.contentContainer = new ContentContainer(this.accessor, this);
3838
4091
  container.append(this.tabsContainer.element, this.contentContainer.element);
3839
4092
  this.header.hidden = !!options.hideHeader;
3840
4093
  this.locked = (_a = options.locked) !== null && _a !== void 0 ? _a : false;
@@ -3848,7 +4101,7 @@
3848
4101
  this.accessor.doSetGroupActive(this.groupPanel, true);
3849
4102
  }), this.contentContainer.onDidBlur(() => {
3850
4103
  // noop
3851
- }), this.dropTarget.onDrop((event) => {
4104
+ }), this.contentContainer.dropTarget.onDrop((event) => {
3852
4105
  this.handleDropEvent(event.nativeEvent, event.position);
3853
4106
  }), this._onMove, this._onDidChange, this._onDidDrop, this._onDidAddPanel, this._onDidRemovePanel, this._onDidActivePanelChange);
3854
4107
  }
@@ -3897,6 +4150,9 @@
3897
4150
  this.tabsContainer.setPrefixActionsElement(this._prefixHeaderActions.element);
3898
4151
  }
3899
4152
  }
4153
+ rerender(panel) {
4154
+ this.contentContainer.renderPanel(panel, { asActive: false });
4155
+ }
3900
4156
  indexOf(panel) {
3901
4157
  return this.tabsContainer.indexOf(panel.id);
3902
4158
  }
@@ -4088,12 +4344,12 @@
4088
4344
  doAddPanel(panel, index = this.panels.length, skipSetActive = false) {
4089
4345
  const existingPanel = this._panels.indexOf(panel);
4090
4346
  const hasExistingPanel = existingPanel > -1;
4347
+ this.tabsContainer.show();
4348
+ this.contentContainer.show();
4091
4349
  this.tabsContainer.openPanel(panel, index);
4092
4350
  if (!skipSetActive) {
4093
4351
  this.contentContainer.openPanel(panel);
4094
4352
  }
4095
- this.tabsContainer.show();
4096
- this.contentContainer.show();
4097
4353
  if (hasExistingPanel) {
4098
4354
  // TODO - need to ensure ordering hasn't changed and if it has need to re-order this.panels
4099
4355
  return;
@@ -4209,7 +4465,6 @@
4209
4465
  for (const panel of this.panels) {
4210
4466
  panel.dispose();
4211
4467
  }
4212
- this.dropTarget.dispose();
4213
4468
  this.tabsContainer.dispose();
4214
4469
  this.contentContainer.dispose();
4215
4470
  }
@@ -4248,7 +4503,22 @@
4248
4503
  if (this.disableResizing) {
4249
4504
  return;
4250
4505
  }
4251
- if (!document.body.contains(this._element)) {
4506
+ if (!this._element.offsetParent) {
4507
+ /**
4508
+ * offsetParent === null is equivalent to display: none being set on the element or one
4509
+ * of it's parents. In the display: none case the size will become (0, 0) which we do
4510
+ * not want to propagate.
4511
+ *
4512
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/offsetParent
4513
+ *
4514
+ * You could use checkVisibility() but at the time of writing it's not supported across
4515
+ * all Browsers
4516
+ *
4517
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/Element/checkVisibility
4518
+ */
4519
+ return;
4520
+ }
4521
+ if (!isInDocument(this._element)) {
4252
4522
  /**
4253
4523
  * since the event is dispatched through requestAnimationFrame there is a small chance
4254
4524
  * the component is no longer attached to the DOM, if that is the case the dimensions
@@ -4340,6 +4610,21 @@
4340
4610
  isVisible(panel) {
4341
4611
  return this.gridview.isViewVisible(getGridLocation(panel.element));
4342
4612
  }
4613
+ maximizeGroup(panel) {
4614
+ this.gridview.maximizeView(panel);
4615
+ }
4616
+ isMaximizedGroup(panel) {
4617
+ return this.gridview.maximizedView() === panel;
4618
+ }
4619
+ exitMaximizedGroup() {
4620
+ this.gridview.exitMaximizedView();
4621
+ }
4622
+ hasMaximizedGroup() {
4623
+ return this.gridview.hasMaximizedView();
4624
+ }
4625
+ get onDidMaxmizedGroupChange() {
4626
+ return this.gridview.onDidMaxmizedNodeChange;
4627
+ }
4343
4628
  doAddGroup(group, location = [0], size) {
4344
4629
  this.gridview.addView(group, size !== null && size !== void 0 ? size : exports.Sizing.Distribute, location);
4345
4630
  this._onDidAddGroup.fire(group);
@@ -5116,32 +5401,63 @@
5116
5401
  }
5117
5402
  }
5118
5403
 
5404
+ // TODO find a better way to initialize and avoid needing null checks
5405
+ const NOT_INITIALIZED_MESSAGE = 'DockviewGroupPanelApiImpl not initialized';
5119
5406
  class DockviewGroupPanelApiImpl extends GridviewPanelApiImpl {
5120
- get isFloating() {
5407
+ get location() {
5121
5408
  if (!this._group) {
5122
- throw new Error(`DockviewGroupPanelApiImpl not initialized`);
5409
+ throw new Error(NOT_INITIALIZED_MESSAGE);
5123
5410
  }
5124
- return this._group.model.isFloating;
5411
+ return this._group.model.location;
5125
5412
  }
5126
5413
  constructor(id, accessor) {
5127
5414
  super(id);
5128
5415
  this.accessor = accessor;
5129
- this._onDidFloatingStateChange = new Emitter();
5130
- this.onDidFloatingStateChange = this._onDidFloatingStateChange.event;
5131
- this.addDisposables(this._onDidFloatingStateChange);
5416
+ this._onDidLocationChange = new Emitter();
5417
+ this.onDidLocationChange = this._onDidLocationChange.event;
5418
+ this.addDisposables(this._onDidLocationChange);
5132
5419
  }
5133
5420
  moveTo(options) {
5134
- var _a;
5421
+ var _a, _b, _c;
5422
+ if (!this._group) {
5423
+ throw new Error(NOT_INITIALIZED_MESSAGE);
5424
+ }
5425
+ const group = (_a = options.group) !== null && _a !== void 0 ? _a : this.accessor.addGroup({
5426
+ direction: positionToDirection((_b = options.position) !== null && _b !== void 0 ? _b : 'right'),
5427
+ });
5428
+ this.accessor.moveGroupOrPanel(group, this._group.id, undefined, options.group ? (_c = options.position) !== null && _c !== void 0 ? _c : 'center' : 'center');
5429
+ }
5430
+ maximize() {
5431
+ if (!this._group) {
5432
+ throw new Error(NOT_INITIALIZED_MESSAGE);
5433
+ }
5434
+ if (this.location !== 'grid') {
5435
+ // only grid groups can be maximized
5436
+ return;
5437
+ }
5438
+ this.accessor.maximizeGroup(this._group);
5439
+ }
5440
+ isMaximized() {
5135
5441
  if (!this._group) {
5136
- throw new Error(`DockviewGroupPanelApiImpl not initialized`);
5442
+ throw new Error(NOT_INITIALIZED_MESSAGE);
5443
+ }
5444
+ return this.accessor.isMaximizedGroup(this._group);
5445
+ }
5446
+ exitMaximized() {
5447
+ if (!this._group) {
5448
+ throw new Error(NOT_INITIALIZED_MESSAGE);
5449
+ }
5450
+ if (this.isMaximized()) {
5451
+ this.accessor.exitMaximizedGroup();
5137
5452
  }
5138
- this.accessor.moveGroupOrPanel(options.group, this._group.id, undefined, (_a = options.position) !== null && _a !== void 0 ? _a : 'center');
5139
5453
  }
5140
5454
  initialize(group) {
5141
5455
  this._group = group;
5142
5456
  }
5143
5457
  }
5144
5458
 
5459
+ const MINIMUM_DOCKVIEW_GROUP_PANEL_WIDTH = 100;
5460
+ const MINIMUM_DOCKVIEW_GROUP_PANEL_HEIGHT = 100;
5145
5461
  class DockviewGroupPanel extends GridviewPanel {
5146
5462
  get panels() {
5147
5463
  return this._model.panels;
@@ -5166,8 +5482,8 @@
5166
5482
  }
5167
5483
  constructor(accessor, id, options) {
5168
5484
  super(id, 'groupview_default', {
5169
- minimumHeight: 100,
5170
- minimumWidth: 100,
5485
+ minimumHeight: MINIMUM_DOCKVIEW_GROUP_PANEL_HEIGHT,
5486
+ minimumWidth: MINIMUM_DOCKVIEW_GROUP_PANEL_WIDTH,
5171
5487
  }, new DockviewGroupPanelApiImpl(id, accessor));
5172
5488
  this.api.initialize(this); // cannot use 'this' after after 'super' call
5173
5489
  this._model = new DockviewGroupPanelModel(this.element, accessor, id, options, this);
@@ -5221,8 +5537,10 @@
5221
5537
  return this.panel.title;
5222
5538
  }
5223
5539
  get isGroupActive() {
5224
- var _a;
5225
- return !!((_a = this.group) === null || _a === void 0 ? void 0 : _a.isActive);
5540
+ return this.group.isActive;
5541
+ }
5542
+ get renderer() {
5543
+ return this.panel.renderer;
5226
5544
  }
5227
5545
  set group(value) {
5228
5546
  const isOldGroupActive = this.isGroupActive;
@@ -5250,10 +5568,12 @@
5250
5568
  this.onDidActiveGroupChange = this._onDidActiveGroupChange.event;
5251
5569
  this._onDidGroupChange = new Emitter();
5252
5570
  this.onDidGroupChange = this._onDidGroupChange.event;
5571
+ this._onDidRendererChange = new Emitter();
5572
+ this.onDidRendererChange = this._onDidRendererChange.event;
5253
5573
  this.disposable = new MutableDisposable();
5254
5574
  this.initialize(panel);
5255
5575
  this._group = group;
5256
- this.addDisposables(this.disposable, this._onDidTitleChange, this._onDidGroupChange, this._onDidActiveGroupChange);
5576
+ this.addDisposables(this.disposable, this._onDidRendererChange, this._onDidTitleChange, this._onDidGroupChange, this._onDidActiveGroupChange);
5257
5577
  }
5258
5578
  moveTo(options) {
5259
5579
  var _a;
@@ -5262,9 +5582,21 @@
5262
5582
  setTitle(title) {
5263
5583
  this.panel.setTitle(title);
5264
5584
  }
5585
+ setRenderer(renderer) {
5586
+ this.panel.setRenderer(renderer);
5587
+ }
5265
5588
  close() {
5266
5589
  this.group.model.closePanel(this.panel);
5267
5590
  }
5591
+ maximize() {
5592
+ this.group.api.maximize();
5593
+ }
5594
+ isMaximized() {
5595
+ return this.group.api.isMaximized();
5596
+ }
5597
+ exitMaximized() {
5598
+ this.group.api.exitMaximized();
5599
+ }
5268
5600
  }
5269
5601
 
5270
5602
  class DockviewPanel extends CompositeDisposable {
@@ -5277,11 +5609,17 @@
5277
5609
  get group() {
5278
5610
  return this._group;
5279
5611
  }
5280
- constructor(id, accessor, containerApi, group, view) {
5612
+ get renderer() {
5613
+ var _a;
5614
+ return (_a = this._renderer) !== null && _a !== void 0 ? _a : this.accessor.renderer;
5615
+ }
5616
+ constructor(id, accessor, containerApi, group, view, options) {
5281
5617
  super();
5282
5618
  this.id = id;
5619
+ this.accessor = accessor;
5283
5620
  this.containerApi = containerApi;
5284
5621
  this.view = view;
5622
+ this._renderer = options.renderer;
5285
5623
  this._group = group;
5286
5624
  this.api = new DockviewPanelApiImpl(this, this._group, accessor);
5287
5625
  this.addDisposables(this.api.onActiveChange(() => {
@@ -5290,6 +5628,8 @@
5290
5628
  // forward the resize event to the group since if you want to resize a panel
5291
5629
  // you are actually just resizing the panels parent which is the group
5292
5630
  this.group.api.setSize(event);
5631
+ }), this.api.onDidRendererChange((event) => {
5632
+ this.group.model.rerender(this);
5293
5633
  }));
5294
5634
  }
5295
5635
  init(params) {
@@ -5309,6 +5649,7 @@
5309
5649
  ? this._params
5310
5650
  : undefined,
5311
5651
  title: this.title,
5652
+ renderer: this._renderer,
5312
5653
  };
5313
5654
  }
5314
5655
  setTitle(title) {
@@ -5324,6 +5665,15 @@
5324
5665
  this.api._onDidTitleChange.fire({ title });
5325
5666
  }
5326
5667
  }
5668
+ setRenderer(renderer) {
5669
+ const didChange = renderer !== this.renderer;
5670
+ if (didChange) {
5671
+ this._renderer = renderer;
5672
+ this.api._onDidRendererChange.fire({
5673
+ renderer: renderer,
5674
+ });
5675
+ }
5676
+ }
5327
5677
  update(event) {
5328
5678
  var _a;
5329
5679
  // merge the new parameters with the existing parameters
@@ -5542,8 +5892,8 @@
5542
5892
  }
5543
5893
 
5544
5894
  class DefaultDockviewDeserialzier {
5545
- constructor(layout) {
5546
- this.layout = layout;
5895
+ constructor(accessor) {
5896
+ this.accessor = accessor;
5547
5897
  }
5548
5898
  fromJSON(panelData, group) {
5549
5899
  var _a, _b;
@@ -5557,8 +5907,10 @@
5557
5907
  const tabComponent = viewData
5558
5908
  ? (_b = viewData.tab) === null || _b === void 0 ? void 0 : _b.id
5559
5909
  : panelData.tabComponent;
5560
- const view = new DockviewPanelModel(this.layout, panelId, contentComponent, tabComponent);
5561
- const panel = new DockviewPanel(panelId, this.layout, new DockviewApi(this.layout), group, view);
5910
+ const view = new DockviewPanelModel(this.accessor, panelId, contentComponent, tabComponent);
5911
+ const panel = new DockviewPanel(panelId, this.accessor, new DockviewApi(this.accessor), group, view, {
5912
+ renderer: panelData.renderer,
5913
+ });
5562
5914
  panel.init({
5563
5915
  title: title !== null && title !== void 0 ? title : panelId,
5564
5916
  params: params !== null && params !== void 0 ? params : {},
@@ -5938,7 +6290,265 @@
5938
6290
  }
5939
6291
  }
5940
6292
 
6293
+ class PopoutWindow extends CompositeDisposable {
6294
+ constructor(id, className, options) {
6295
+ super();
6296
+ this.id = id;
6297
+ this.className = className;
6298
+ this.options = options;
6299
+ this._onDidClose = new Emitter();
6300
+ this.onDidClose = this._onDidClose.event;
6301
+ this._window = null;
6302
+ this.addDisposables(this._onDidClose, {
6303
+ dispose: () => {
6304
+ this.close();
6305
+ },
6306
+ });
6307
+ }
6308
+ dimensions() {
6309
+ if (!this._window) {
6310
+ return null;
6311
+ }
6312
+ const left = this._window.value.screenX;
6313
+ const top = this._window.value.screenY;
6314
+ const width = this._window.value.innerWidth;
6315
+ const height = this._window.value.innerHeight;
6316
+ return { top, left, width, height };
6317
+ }
6318
+ close() {
6319
+ if (this._window) {
6320
+ this._window.disposable.dispose();
6321
+ this._window.value.close();
6322
+ this._window = null;
6323
+ }
6324
+ }
6325
+ open(content) {
6326
+ if (this._window) {
6327
+ throw new Error('instance of popout window is already open');
6328
+ }
6329
+ const url = `${this.options.url}`;
6330
+ const features = Object.entries({
6331
+ top: this.options.top,
6332
+ left: this.options.left,
6333
+ width: this.options.width,
6334
+ height: this.options.height,
6335
+ })
6336
+ .map(([key, value]) => `${key}=${value}`)
6337
+ .join(',');
6338
+ // https://developer.mozilla.org/en-US/docs/Web/API/Window/open
6339
+ const externalWindow = window.open(url, this.id, features);
6340
+ if (!externalWindow) {
6341
+ return;
6342
+ }
6343
+ const disposable = new CompositeDisposable();
6344
+ this._window = { value: externalWindow, disposable };
6345
+ const cleanUp = () => {
6346
+ this._onDidClose.fire();
6347
+ this._window = null;
6348
+ };
6349
+ // prevent any default content from loading
6350
+ // externalWindow.document.body.replaceWith(document.createElement('div'));
6351
+ disposable.addDisposables(addDisposableWindowListener(window, 'beforeunload', () => {
6352
+ cleanUp();
6353
+ this.close();
6354
+ }));
6355
+ externalWindow.addEventListener('load', () => {
6356
+ const externalDocument = externalWindow.document;
6357
+ externalDocument.title = document.title;
6358
+ const div = document.createElement('div');
6359
+ div.classList.add('dv-popout-window');
6360
+ div.style.position = 'absolute';
6361
+ div.style.width = '100%';
6362
+ div.style.height = '100%';
6363
+ div.style.top = '0px';
6364
+ div.style.left = '0px';
6365
+ div.classList.add(this.className);
6366
+ div.appendChild(content);
6367
+ externalDocument.body.replaceChildren(div);
6368
+ externalDocument.body.classList.add(this.className);
6369
+ addStyles(externalDocument, window.document.styleSheets);
6370
+ externalWindow.addEventListener('beforeunload', () => {
6371
+ // TODO: indicate external window is closing
6372
+ cleanUp();
6373
+ });
6374
+ });
6375
+ }
6376
+ }
6377
+
6378
+ class DockviewPopoutGroupPanel extends CompositeDisposable {
6379
+ constructor(id, group, options) {
6380
+ var _a;
6381
+ super();
6382
+ this.id = id;
6383
+ this.group = group;
6384
+ this.options = options;
6385
+ this.window = new PopoutWindow(id, (_a = options.className) !== null && _a !== void 0 ? _a : '', {
6386
+ url: this.options.popoutUrl,
6387
+ left: this.options.box.left,
6388
+ top: this.options.box.top,
6389
+ width: this.options.box.width,
6390
+ height: this.options.box.height,
6391
+ });
6392
+ group.model.location = 'popout';
6393
+ this.addDisposables(this.window, {
6394
+ dispose: () => {
6395
+ group.model.location = 'grid';
6396
+ },
6397
+ }, this.window.onDidClose(() => {
6398
+ this.dispose();
6399
+ }));
6400
+ this.window.open(group.element);
6401
+ }
6402
+ }
6403
+
5941
6404
  const DEFAULT_FLOATING_GROUP_OVERFLOW_SIZE = 100;
6405
+ const DEFAULT_FLOATING_GROUP_POSITION = { left: 100, top: 100 };
6406
+
6407
+ function createFocusableElement() {
6408
+ const element = document.createElement('div');
6409
+ element.tabIndex = -1;
6410
+ return element;
6411
+ }
6412
+ class OverlayRenderContainer extends CompositeDisposable {
6413
+ constructor(element) {
6414
+ super();
6415
+ this.element = element;
6416
+ this.map = {};
6417
+ this.addDisposables(Disposable.from(() => {
6418
+ for (const value of Object.values(this.map)) {
6419
+ value.disposable.dispose();
6420
+ value.destroy.dispose();
6421
+ }
6422
+ }));
6423
+ }
6424
+ detatch(panel) {
6425
+ if (this.map[panel.api.id]) {
6426
+ const { disposable, destroy } = this.map[panel.api.id];
6427
+ disposable.dispose();
6428
+ destroy.dispose();
6429
+ delete this.map[panel.api.id];
6430
+ return true;
6431
+ }
6432
+ return false;
6433
+ }
6434
+ attach(options) {
6435
+ const { panel, referenceContainer } = options;
6436
+ if (!this.map[panel.api.id]) {
6437
+ const element = createFocusableElement();
6438
+ element.className = 'dv-render-overlay';
6439
+ this.map[panel.api.id] = {
6440
+ panel,
6441
+ disposable: Disposable.NONE,
6442
+ destroy: Disposable.NONE,
6443
+ element,
6444
+ };
6445
+ }
6446
+ const focusContainer = this.map[panel.api.id].element;
6447
+ if (panel.view.content.element.parentElement !== focusContainer) {
6448
+ focusContainer.appendChild(panel.view.content.element);
6449
+ }
6450
+ if (focusContainer.parentElement !== this.element) {
6451
+ this.element.appendChild(focusContainer);
6452
+ }
6453
+ const resize = () => {
6454
+ // TODO propagate position to avoid getDomNodePagePosition calls, possible performance bottleneck?
6455
+ const box = getDomNodePagePosition(referenceContainer.element);
6456
+ const box2 = getDomNodePagePosition(this.element);
6457
+ focusContainer.style.left = `${box.left - box2.left}px`;
6458
+ focusContainer.style.top = `${box.top - box2.top}px`;
6459
+ focusContainer.style.width = `${box.width}px`;
6460
+ focusContainer.style.height = `${box.height}px`;
6461
+ toggleClass(focusContainer, 'dv-render-overlay-float', panel.group.api.location === 'floating');
6462
+ };
6463
+ const visibilityChanged = () => {
6464
+ if (panel.api.isVisible) {
6465
+ resize();
6466
+ }
6467
+ focusContainer.style.display = panel.api.isVisible ? '' : 'none';
6468
+ };
6469
+ const disposable = new CompositeDisposable(
6470
+ /**
6471
+ * since container is positioned absoutely we must explicitly forward
6472
+ * the dnd events for the expect behaviours to continue to occur in terms of dnd
6473
+ *
6474
+ * the dnd observer does not need to be conditional on whether the panel is visible since
6475
+ * non-visible panels are 'display: none' and in such case the dnd observer will not fire.
6476
+ */
6477
+ new DragAndDropObserver(focusContainer, {
6478
+ onDragEnd: (e) => {
6479
+ referenceContainer.dropTarget.dnd.onDragEnd(e);
6480
+ },
6481
+ onDragEnter: (e) => {
6482
+ referenceContainer.dropTarget.dnd.onDragEnter(e);
6483
+ },
6484
+ onDragLeave: (e) => {
6485
+ referenceContainer.dropTarget.dnd.onDragLeave(e);
6486
+ },
6487
+ onDrop: (e) => {
6488
+ referenceContainer.dropTarget.dnd.onDrop(e);
6489
+ },
6490
+ onDragOver: (e) => {
6491
+ referenceContainer.dropTarget.dnd.onDragOver(e);
6492
+ },
6493
+ }), panel.api.onDidVisibilityChange((event) => {
6494
+ /**
6495
+ * Control the visibility of the content, however even when not visible (display: none)
6496
+ * the content is still maintained within the DOM hence DOM specific attributes
6497
+ * such as scroll position are maintained when next made visible.
6498
+ */
6499
+ visibilityChanged();
6500
+ }), panel.api.onDidDimensionsChange(() => {
6501
+ if (!panel.api.isVisible) {
6502
+ return;
6503
+ }
6504
+ resize();
6505
+ }));
6506
+ this.map[panel.api.id].destroy = Disposable.from(() => {
6507
+ focusContainer.removeChild(panel.view.content.element);
6508
+ this.element.removeChild(focusContainer);
6509
+ });
6510
+ queueMicrotask(() => {
6511
+ if (this.isDisposed) {
6512
+ return;
6513
+ }
6514
+ /**
6515
+ * wait until everything has finished in the current stack-frame call before
6516
+ * calling the first resize as other size-altering events may still occur before
6517
+ * the end of the stack-frame.
6518
+ */
6519
+ visibilityChanged();
6520
+ });
6521
+ // dispose of logic asoccciated with previous reference-container
6522
+ this.map[panel.api.id].disposable.dispose();
6523
+ // and reset the disposable to the active reference-container
6524
+ this.map[panel.api.id].disposable = disposable;
6525
+ return focusContainer;
6526
+ }
6527
+ }
6528
+
6529
+ const DEFAULT_ROOT_OVERLAY_MODEL = {
6530
+ activationSize: { type: 'pixels', value: 10 },
6531
+ size: { type: 'pixels', value: 20 },
6532
+ };
6533
+ function getTheme(element) {
6534
+ function toClassList(element) {
6535
+ const list = [];
6536
+ for (let i = 0; i < element.classList.length; i++) {
6537
+ list.push(element.classList.item(i));
6538
+ }
6539
+ return list;
6540
+ }
6541
+ let theme = undefined;
6542
+ let parent = element;
6543
+ while (parent !== null) {
6544
+ theme = toClassList(parent).find((cls) => cls.startsWith('dockview-theme-'));
6545
+ if (typeof theme === 'string') {
6546
+ break;
6547
+ }
6548
+ parent = parent.parentElement;
6549
+ }
6550
+ return theme;
6551
+ }
5942
6552
  class DockviewComponent extends BaseGrid {
5943
6553
  get orientation() {
5944
6554
  return this.gridview.orientation;
@@ -5959,8 +6569,12 @@
5959
6569
  }
5960
6570
  return activeGroup.activePanel;
5961
6571
  }
5962
- constructor(options) {
6572
+ get renderer() {
5963
6573
  var _a;
6574
+ return (_a = this.options.defaultRenderer) !== null && _a !== void 0 ? _a : 'onlyWhenVisibile';
6575
+ }
6576
+ constructor(options) {
6577
+ var _a, _b;
5964
6578
  super({
5965
6579
  proportionalLayout: true,
5966
6580
  orientation: (_a = options.orientation) !== null && _a !== void 0 ? _a : exports.Orientation.HORIZONTAL,
@@ -5985,12 +6599,27 @@
5985
6599
  this.onDidLayoutFromJSON = this._onDidLayoutFromJSON.event;
5986
6600
  this._onDidActivePanelChange = new Emitter();
5987
6601
  this.onDidActivePanelChange = this._onDidActivePanelChange.event;
5988
- this.floatingGroups = [];
6602
+ this._floatingGroups = [];
6603
+ this._popoutGroups = [];
6604
+ const gready = document.createElement('div');
6605
+ gready.className = 'dv-overlay-render-container';
6606
+ this.gridview.element.appendChild(gready);
6607
+ this.overlayRenderContainer = new OverlayRenderContainer(gready);
5989
6608
  toggleClass(this.gridview.element, 'dv-dockview', true);
5990
- this.addDisposables(this._onWillDragPanel, this._onWillDragGroup, this._onDidActivePanelChange, this._onDidAddPanel, this._onDidRemovePanel, this._onDidLayoutFromJSON, this._onDidDrop, exports.DockviewEvent.any(this.onDidAddGroup, this.onDidRemoveGroup)(() => {
6609
+ toggleClass(this.element, 'dv-debug', !!options.debug);
6610
+ this.addDisposables(this.overlayRenderContainer, this._onWillDragPanel, this._onWillDragGroup, this._onDidActivePanelChange, this._onDidAddPanel, this._onDidRemovePanel, this._onDidLayoutFromJSON, this._onDidDrop, exports.DockviewEvent.any(this.onDidAddGroup, this.onDidRemoveGroup)(() => {
5991
6611
  this.updateWatermark();
5992
6612
  }), exports.DockviewEvent.any(this.onDidAddPanel, this.onDidRemovePanel, this.onDidActivePanelChange)(() => {
5993
6613
  this._bufferOnDidLayoutChange.fire();
6614
+ }), Disposable.from(() => {
6615
+ // iterate over a copy of the array since .dispose() mutates the original array
6616
+ for (const group of [...this._floatingGroups]) {
6617
+ group.dispose();
6618
+ }
6619
+ // iterate over a copy of the array since .dispose() mutates the original array
6620
+ for (const group of [...this._popoutGroups]) {
6621
+ group.dispose();
6622
+ }
5994
6623
  }));
5995
6624
  this._options = options;
5996
6625
  if (!this.options.components) {
@@ -6009,7 +6638,7 @@
6009
6638
  !this.options.watermarkFrameworkComponent) {
6010
6639
  this.options.watermarkComponent = Watermark;
6011
6640
  }
6012
- const dropTarget = new Droptarget(this.element, {
6641
+ this._rootDropTarget = new Droptarget(this.element, {
6013
6642
  canDisplayOverlay: (event, position) => {
6014
6643
  const data = getPanelData();
6015
6644
  if (data) {
@@ -6042,12 +6671,9 @@
6042
6671
  return false;
6043
6672
  },
6044
6673
  acceptedTargetZones: ['top', 'bottom', 'left', 'right', 'center'],
6045
- overlayModel: {
6046
- activationSize: { type: 'pixels', value: 10 },
6047
- size: { type: 'pixels', value: 20 },
6048
- },
6674
+ overlayModel: (_b = this.options.rootOverlayModel) !== null && _b !== void 0 ? _b : DEFAULT_ROOT_OVERLAY_MODEL,
6049
6675
  });
6050
- this.addDisposables(dropTarget.onDrop((event) => {
6676
+ this.addDisposables(this._rootDropTarget.onDrop((event) => {
6051
6677
  var _a;
6052
6678
  const data = getPanelData();
6053
6679
  if (data) {
@@ -6056,10 +6682,59 @@
6056
6682
  else {
6057
6683
  this._onDidDrop.fire(Object.assign(Object.assign({}, event), { api: this._api, group: null, getData: getPanelData }));
6058
6684
  }
6059
- }), dropTarget);
6685
+ }), this._rootDropTarget);
6060
6686
  this._api = new DockviewApi(this);
6061
6687
  this.updateWatermark();
6062
6688
  }
6689
+ addPopoutGroup(item, options) {
6690
+ var _a;
6691
+ let group;
6692
+ let box = options === null || options === void 0 ? void 0 : options.position;
6693
+ if (item instanceof DockviewPanel) {
6694
+ group = this.createGroup();
6695
+ this.removePanel(item, {
6696
+ removeEmptyGroup: true,
6697
+ skipDispose: true,
6698
+ });
6699
+ group.model.openPanel(item);
6700
+ if (!box) {
6701
+ box = this.element.getBoundingClientRect();
6702
+ }
6703
+ }
6704
+ else {
6705
+ group = item;
6706
+ if (!box) {
6707
+ box = group.element.getBoundingClientRect();
6708
+ }
6709
+ const skip = typeof (options === null || options === void 0 ? void 0 : options.skipRemoveGroup) === 'boolean' &&
6710
+ options.skipRemoveGroup;
6711
+ if (!skip) {
6712
+ this.doRemoveGroup(item, { skipDispose: true });
6713
+ }
6714
+ }
6715
+ const theme = getTheme(this.gridview.element);
6716
+ const popoutWindow = new DockviewPopoutGroupPanel(`${this.id}-${group.id}`, // globally unique within dockview
6717
+ group, {
6718
+ className: theme !== null && theme !== void 0 ? theme : '',
6719
+ popoutUrl: (_a = options === null || options === void 0 ? void 0 : options.popoutUrl) !== null && _a !== void 0 ? _a : '/popout.html',
6720
+ box: {
6721
+ left: window.screenX + box.left,
6722
+ top: window.screenY + box.top,
6723
+ width: box.width,
6724
+ height: box.height,
6725
+ },
6726
+ });
6727
+ popoutWindow.addDisposables({
6728
+ dispose: () => {
6729
+ remove(this._popoutGroups, popoutWindow);
6730
+ this.updateWatermark();
6731
+ },
6732
+ }, popoutWindow.window.onDidClose(() => {
6733
+ this.doAddGroup(group, [0]);
6734
+ }));
6735
+ this._popoutGroups.push(popoutWindow);
6736
+ this.updateWatermark();
6737
+ }
6063
6738
  addFloatingGroup(item, coord, options) {
6064
6739
  var _a, _b, _c, _d, _e, _f;
6065
6740
  let group;
@@ -6079,9 +6754,13 @@
6079
6754
  this.doRemoveGroup(item, { skipDispose: true });
6080
6755
  }
6081
6756
  }
6082
- group.model.isFloating = true;
6083
- const overlayLeft = typeof (coord === null || coord === void 0 ? void 0 : coord.x) === 'number' ? Math.max(coord.x, 0) : 100;
6084
- const overlayTop = typeof (coord === null || coord === void 0 ? void 0 : coord.y) === 'number' ? Math.max(coord.y, 0) : 100;
6757
+ group.model.location = 'floating';
6758
+ const overlayLeft = typeof (coord === null || coord === void 0 ? void 0 : coord.x) === 'number'
6759
+ ? Math.max(coord.x, 0)
6760
+ : DEFAULT_FLOATING_GROUP_POSITION.left;
6761
+ const overlayTop = typeof (coord === null || coord === void 0 ? void 0 : coord.y) === 'number'
6762
+ ? Math.max(coord.y, 0)
6763
+ : DEFAULT_FLOATING_GROUP_POSITION.top;
6085
6764
  const overlay = new Overlay({
6086
6765
  container: this.gridview.element,
6087
6766
  content: group.element,
@@ -6125,12 +6804,12 @@
6125
6804
  }), {
6126
6805
  dispose: () => {
6127
6806
  disposable.dispose();
6128
- group.model.isFloating = false;
6129
- remove(this.floatingGroups, floatingGroupPanel);
6807
+ group.model.location = 'grid';
6808
+ remove(this._floatingGroups, floatingGroupPanel);
6130
6809
  this.updateWatermark();
6131
6810
  },
6132
6811
  });
6133
- this.floatingGroups.push(floatingGroupPanel);
6812
+ this._floatingGroups.push(floatingGroupPanel);
6134
6813
  this.updateWatermark();
6135
6814
  }
6136
6815
  orthogonalize(position) {
@@ -6166,16 +6845,18 @@
6166
6845
  }
6167
6846
  updateOptions(options) {
6168
6847
  var _a, _b;
6169
- const hasOrientationChanged = typeof options.orientation === 'string' &&
6848
+ const changed_orientation = typeof options.orientation === 'string' &&
6170
6849
  this.gridview.orientation !== options.orientation;
6171
- const hasFloatingGroupOptionsChanged = options.floatingGroupBounds !== undefined &&
6850
+ const changed_floatingGroupBounds = options.floatingGroupBounds !== undefined &&
6172
6851
  options.floatingGroupBounds !== this.options.floatingGroupBounds;
6852
+ const changed_rootOverlayOptions = options.rootOverlayModel !== undefined &&
6853
+ options.rootOverlayModel !== this.options.rootOverlayModel;
6173
6854
  this._options = Object.assign(Object.assign({}, this.options), options);
6174
- if (hasOrientationChanged) {
6855
+ if (changed_orientation) {
6175
6856
  this.gridview.orientation = options.orientation;
6176
6857
  }
6177
- if (hasFloatingGroupOptionsChanged) {
6178
- for (const group of this.floatingGroups) {
6858
+ if (changed_floatingGroupBounds) {
6859
+ for (const group of this._floatingGroups) {
6179
6860
  switch (this.options.floatingGroupBounds) {
6180
6861
  case 'boundedWithinViewport':
6181
6862
  group.overlay.minimumInViewportHeight = undefined;
@@ -6196,12 +6877,15 @@
6196
6877
  group.overlay.setBounds({});
6197
6878
  }
6198
6879
  }
6880
+ if (changed_rootOverlayOptions) {
6881
+ this._rootDropTarget.setOverlayModel(options.rootOverlayModel);
6882
+ }
6199
6883
  this.layout(this.gridview.width, this.gridview.height, true);
6200
6884
  }
6201
6885
  layout(width, height, forceResize) {
6202
6886
  super.layout(width, height, forceResize);
6203
- if (this.floatingGroups) {
6204
- for (const floating of this.floatingGroups) {
6887
+ if (this._floatingGroups) {
6888
+ for (const floating of this._floatingGroups) {
6205
6889
  // ensure floting groups stay within visible boundaries
6206
6890
  floating.overlay.setBounds();
6207
6891
  }
@@ -6269,10 +6953,16 @@
6269
6953
  collection[panel.id] = panel.toJSON();
6270
6954
  return collection;
6271
6955
  }, {});
6272
- const floats = this.floatingGroups.map((floatingGroup) => {
6956
+ const floats = this._floatingGroups.map((group) => {
6273
6957
  return {
6274
- data: floatingGroup.group.toJSON(),
6275
- position: floatingGroup.overlay.toJSON(),
6958
+ data: group.group.toJSON(),
6959
+ position: group.overlay.toJSON(),
6960
+ };
6961
+ });
6962
+ const popoutGroups = this._popoutGroups.map((group) => {
6963
+ return {
6964
+ data: group.group.toJSON(),
6965
+ position: group.window.dimensions(),
6276
6966
  };
6277
6967
  });
6278
6968
  const result = {
@@ -6283,10 +6973,13 @@
6283
6973
  if (floats.length > 0) {
6284
6974
  result.floatingGroups = floats;
6285
6975
  }
6976
+ if (popoutGroups.length > 0) {
6977
+ result.popoutGroups = popoutGroups;
6978
+ }
6286
6979
  return result;
6287
6980
  }
6288
6981
  fromJSON(data) {
6289
- var _a;
6982
+ var _a, _b;
6290
6983
  this.clear();
6291
6984
  if (typeof data !== 'object' || data === null) {
6292
6985
  throw new Error('serialized layout must be a non-null object');
@@ -6353,7 +7046,16 @@
6353
7046
  width: position.width,
6354
7047
  }, { skipRemoveGroup: true, inDragMode: false });
6355
7048
  }
6356
- for (const floatingGroup of this.floatingGroups) {
7049
+ const serializedPopoutGroups = (_b = data.popoutGroups) !== null && _b !== void 0 ? _b : [];
7050
+ for (const serializedPopoutGroup of serializedPopoutGroups) {
7051
+ const { data, position } = serializedPopoutGroup;
7052
+ const group = createGroupFromSerializedState(data);
7053
+ this.addPopoutGroup(group, {
7054
+ skipRemoveGroup: true,
7055
+ position: position !== null && position !== void 0 ? position : undefined,
7056
+ });
7057
+ }
7058
+ for (const floatingGroup of this._floatingGroups) {
6357
7059
  floatingGroup.overlay.setBounds();
6358
7060
  }
6359
7061
  if (typeof activeGroup === 'string') {
@@ -6385,7 +7087,7 @@
6385
7087
  this._onDidRemoveGroup.fire(group);
6386
7088
  }
6387
7089
  // iterate over a reassigned array since original array will be modified
6388
- for (const floatingGroup of [...this.floatingGroups]) {
7090
+ for (const floatingGroup of [...this._floatingGroups]) {
6389
7091
  floatingGroup.dispose();
6390
7092
  }
6391
7093
  // fires clean-up events and clears the underlying HTML gridview.
@@ -6477,7 +7179,8 @@
6477
7179
  group.model.openPanel(panel);
6478
7180
  this.doSetGroupAndPanelActive(group);
6479
7181
  }
6480
- else if (referenceGroup.api.isFloating || target === 'center') {
7182
+ else if (referenceGroup.api.location === 'floating' ||
7183
+ target === 'center') {
6481
7184
  panel = this.createPanel(options, referenceGroup);
6482
7185
  referenceGroup.model.openPanel(panel);
6483
7186
  }
@@ -6521,6 +7224,7 @@
6521
7224
  }
6522
7225
  group.model.removePanel(panel);
6523
7226
  if (!options.skipDispose) {
7227
+ this.overlayRenderContainer.detatch(panel);
6524
7228
  panel.dispose();
6525
7229
  }
6526
7230
  if (group.size === 0 && options.removeEmptyGroup) {
@@ -6537,7 +7241,7 @@
6537
7241
  }
6538
7242
  updateWatermark() {
6539
7243
  var _a, _b;
6540
- if (this.groups.filter((x) => !x.api.isFloating).length === 0) {
7244
+ if (this.groups.filter((x) => x.api.location === 'grid').length === 0) {
6541
7245
  if (!this.watermark) {
6542
7246
  this.watermark = this.createWatermarkComponent();
6543
7247
  this.watermark.init({
@@ -6612,19 +7316,40 @@
6612
7316
  }
6613
7317
  }
6614
7318
  doRemoveGroup(group, options) {
6615
- const floatingGroup = this.floatingGroups.find((_) => _.group === group);
6616
- if (floatingGroup) {
6617
- if (!(options === null || options === void 0 ? void 0 : options.skipDispose)) {
6618
- floatingGroup.group.dispose();
6619
- this._groups.delete(group.id);
6620
- this._onDidRemoveGroup.fire(group);
7319
+ if (group.api.location === 'floating') {
7320
+ const floatingGroup = this._floatingGroups.find((_) => _.group === group);
7321
+ if (floatingGroup) {
7322
+ if (!(options === null || options === void 0 ? void 0 : options.skipDispose)) {
7323
+ floatingGroup.group.dispose();
7324
+ this._groups.delete(group.id);
7325
+ this._onDidRemoveGroup.fire(group);
7326
+ }
7327
+ remove(this._floatingGroups, floatingGroup);
7328
+ floatingGroup.dispose();
7329
+ if (!(options === null || options === void 0 ? void 0 : options.skipActive) && this._activeGroup === group) {
7330
+ const groups = Array.from(this._groups.values());
7331
+ this.doSetGroupActive(groups.length > 0 ? groups[0].value : undefined);
7332
+ }
7333
+ return floatingGroup.group;
6621
7334
  }
6622
- floatingGroup.dispose();
6623
- if (!(options === null || options === void 0 ? void 0 : options.skipActive) && this._activeGroup === group) {
6624
- const groups = Array.from(this._groups.values());
6625
- this.doSetGroupActive(groups.length > 0 ? groups[0].value : undefined);
7335
+ throw new Error('failed to find floating group');
7336
+ }
7337
+ if (group.api.location === 'popout') {
7338
+ const selectedGroup = this._popoutGroups.find((_) => _.group === group);
7339
+ if (selectedGroup) {
7340
+ if (!(options === null || options === void 0 ? void 0 : options.skipDispose)) {
7341
+ selectedGroup.group.dispose();
7342
+ this._groups.delete(group.id);
7343
+ this._onDidRemoveGroup.fire(group);
7344
+ }
7345
+ selectedGroup.dispose();
7346
+ if (!(options === null || options === void 0 ? void 0 : options.skipActive) && this._activeGroup === group) {
7347
+ const groups = Array.from(this._groups.values());
7348
+ this.doSetGroupActive(groups.length > 0 ? groups[0].value : undefined);
7349
+ }
7350
+ return selectedGroup.group;
6626
7351
  }
6627
- return floatingGroup.group;
7352
+ throw new Error('failed to find popout group');
6628
7353
  }
6629
7354
  return super.doRemoveGroup(group, options);
6630
7355
  }
@@ -6656,8 +7381,7 @@
6656
7381
  const targetLocation = getRelativeLocation(this.gridview.orientation, referenceLocation, destinationTarget);
6657
7382
  if (sourceGroup && sourceGroup.size < 2) {
6658
7383
  const [targetParentLocation, to] = tail(targetLocation);
6659
- const isFloating = this.floatingGroups.find((x) => x.group === sourceGroup);
6660
- if (!isFloating) {
7384
+ if (sourceGroup.api.location === 'grid') {
6661
7385
  const sourceLocation = getGridLocation(sourceGroup.element);
6662
7386
  const [sourceParentLocation, from] = tail(sourceLocation);
6663
7387
  if (sequenceEquals(sourceParentLocation, targetParentLocation)) {
@@ -6703,12 +7427,25 @@
6703
7427
  }
6704
7428
  }
6705
7429
  else {
6706
- const floatingGroup = this.floatingGroups.find((x) => x.group === sourceGroup);
6707
- if (floatingGroup) {
6708
- floatingGroup.dispose();
6709
- }
6710
- else {
6711
- this.gridview.removeView(getGridLocation(sourceGroup.element));
7430
+ switch (sourceGroup.api.location) {
7431
+ case 'grid':
7432
+ this.gridview.removeView(getGridLocation(sourceGroup.element));
7433
+ break;
7434
+ case 'floating': {
7435
+ const selectedFloatingGroup = this._floatingGroups.find((x) => x.group === sourceGroup);
7436
+ if (!selectedFloatingGroup) {
7437
+ throw new Error('failed to find floating group');
7438
+ }
7439
+ selectedFloatingGroup.dispose();
7440
+ break;
7441
+ }
7442
+ case 'popout': {
7443
+ const selectedPopoutGroup = this._popoutGroups.find((x) => x.group === sourceGroup);
7444
+ if (!selectedPopoutGroup) {
7445
+ throw new Error('failed to find popout group');
7446
+ }
7447
+ selectedPopoutGroup.dispose();
7448
+ }
6712
7449
  }
6713
7450
  const referenceLocation = getGridLocation(referenceGroup.element);
6714
7451
  const dropLocation = getRelativeLocation(this.gridview.orientation, referenceLocation, target);
@@ -6770,7 +7507,7 @@
6770
7507
  const contentComponent = options.component;
6771
7508
  const tabComponent = (_a = options.tabComponent) !== null && _a !== void 0 ? _a : this.options.defaultTabComponent;
6772
7509
  const view = new DockviewPanelModel(this, options.id, contentComponent, tabComponent);
6773
- const panel = new DockviewPanel(options.id, this, this._api, group, view);
7510
+ const panel = new DockviewPanel(options.id, this, this._api, group, view, { renderer: options.renderer });
6774
7511
  panel.init({
6775
7512
  title: (_b = options.title) !== null && _b !== void 0 ? _b : options.id,
6776
7513
  params: (_c = options === null || options === void 0 ? void 0 : options.params) !== null && _c !== void 0 ? _c : {},
@@ -8116,6 +8853,9 @@
8116
8853
  singleTabMode: props.singleTabMode,
8117
8854
  disableFloatingGroups: props.disableFloatingGroups,
8118
8855
  floatingGroupBounds: props.floatingGroupBounds,
8856
+ defaultRenderer: props.defaultRenderer,
8857
+ debug: props.debug,
8858
+ rootOverlayModel: props.rootOverlayModel,
8119
8859
  });
8120
8860
  const { clientWidth, clientHeight } = domRef.current;
8121
8861
  dockview.layout(clientWidth, clientHeight);
@@ -8223,6 +8963,14 @@
8223
8963
  createLeftHeaderActionsElement: createGroupControlElement(props.leftHeaderActionsComponent, { addPortal }),
8224
8964
  });
8225
8965
  }, [props.leftHeaderActionsComponent]);
8966
+ React__namespace.useEffect(() => {
8967
+ if (!dockviewRef.current) {
8968
+ return;
8969
+ }
8970
+ dockviewRef.current.updateOptions({
8971
+ rootOverlayModel: props.rootOverlayModel,
8972
+ });
8973
+ }, [props.rootOverlayModel]);
8226
8974
  React__namespace.useEffect(() => {
8227
8975
  if (!dockviewRef.current) {
8228
8976
  return;