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
  */
@@ -263,6 +263,14 @@ var Disposable;
263
263
  // noop
264
264
  },
265
265
  };
266
+ function from(func) {
267
+ return {
268
+ dispose: () => {
269
+ func();
270
+ },
271
+ };
272
+ }
273
+ Disposable.from = from;
266
274
  })(Disposable || (Disposable = {}));
267
275
  class CompositeDisposable {
268
276
  get isDisposed() {
@@ -447,6 +455,61 @@ function quasiPreventDefault(event) {
447
455
  function quasiDefaultPrevented(event) {
448
456
  return event[QUASI_PREVENT_DEFAULT_KEY];
449
457
  }
458
+ function addStyles(document, styleSheetList) {
459
+ const styleSheets = Array.from(styleSheetList);
460
+ for (const styleSheet of styleSheets) {
461
+ if (styleSheet.href) {
462
+ const link = document.createElement('link');
463
+ link.href = styleSheet.href;
464
+ link.type = styleSheet.type;
465
+ link.rel = 'stylesheet';
466
+ document.head.appendChild(link);
467
+ }
468
+ let cssTexts = [];
469
+ try {
470
+ if (styleSheet.cssRules) {
471
+ cssTexts = Array.from(styleSheet.cssRules).map((rule) => rule.cssText);
472
+ }
473
+ }
474
+ catch (err) {
475
+ // security errors (lack of permissions), ignore
476
+ }
477
+ for (const rule of cssTexts) {
478
+ const style = document.createElement('style');
479
+ style.appendChild(document.createTextNode(rule));
480
+ document.head.appendChild(style);
481
+ }
482
+ }
483
+ }
484
+ function getDomNodePagePosition(domNode) {
485
+ const { left, top, width, height } = domNode.getBoundingClientRect();
486
+ return {
487
+ left: left + window.scrollX,
488
+ top: top + window.scrollY,
489
+ width: width,
490
+ height: height,
491
+ };
492
+ }
493
+ /**
494
+ * Check whether an element is in the DOM (including the Shadow DOM)
495
+ * @see https://terodox.tech/how-to-tell-if-an-element-is-in-the-dom-including-the-shadow-dom/
496
+ */
497
+ function isInDocument(element) {
498
+ let currentElement = element;
499
+ while (currentElement === null || currentElement === void 0 ? void 0 : currentElement.parentNode) {
500
+ if (currentElement.parentNode === document) {
501
+ return true;
502
+ }
503
+ else if (currentElement.parentNode instanceof DocumentFragment) {
504
+ // handle shadow DOMs
505
+ currentElement = currentElement.parentNode.host;
506
+ }
507
+ else {
508
+ currentElement = currentElement.parentNode;
509
+ }
510
+ }
511
+ return false;
512
+ }
450
513
 
451
514
  function tail(arr) {
452
515
  if (arr.length === 0) {
@@ -644,6 +707,9 @@ var Sizing;
644
707
  Sizing.Invisible = Invisible;
645
708
  })(Sizing || (Sizing = {}));
646
709
  class Splitview {
710
+ get contentSize() {
711
+ return this._contentSize;
712
+ }
647
713
  get size() {
648
714
  return this._size;
649
715
  }
@@ -709,7 +775,7 @@ class Splitview {
709
775
  this.sashes = [];
710
776
  this._size = 0;
711
777
  this._orthogonalSize = 0;
712
- this.contentSize = 0;
778
+ this._contentSize = 0;
713
779
  this._proportions = undefined;
714
780
  this._startSnappingEnabled = true;
715
781
  this._endSnappingEnabled = true;
@@ -828,7 +894,7 @@ class Splitview {
828
894
  );
829
895
  });
830
896
  // Initialize content size and proportions for first layout
831
- this.contentSize = this.viewItems.reduce((r, i) => r + i.size, 0);
897
+ this._contentSize = this.viewItems.reduce((r, i) => r + i.size, 0);
832
898
  this.saveProportions();
833
899
  }
834
900
  }
@@ -1102,7 +1168,7 @@ class Splitview {
1102
1168
  this.addView(view, sizing, to);
1103
1169
  }
1104
1170
  layout(size, orthogonalSize) {
1105
- const previousSize = Math.max(this.size, this.contentSize);
1171
+ const previousSize = Math.max(this.size, this._contentSize);
1106
1172
  this.size = size;
1107
1173
  this.orthogonalSize = orthogonalSize;
1108
1174
  if (!this.proportions) {
@@ -1112,9 +1178,23 @@ class Splitview {
1112
1178
  this.resize(this.viewItems.length - 1, size - previousSize, undefined, lowPriorityIndexes, highPriorityIndexes);
1113
1179
  }
1114
1180
  else {
1181
+ let total = 0;
1115
1182
  for (let i = 0; i < this.viewItems.length; i++) {
1116
1183
  const item = this.viewItems[i];
1117
- item.size = clamp(Math.round(this.proportions[i] * size), item.minimumSize, item.maximumSize);
1184
+ const proportion = this.proportions[i];
1185
+ if (typeof proportion === 'number') {
1186
+ total += proportion;
1187
+ }
1188
+ else {
1189
+ size -= item.size;
1190
+ }
1191
+ }
1192
+ for (let i = 0; i < this.viewItems.length; i++) {
1193
+ const item = this.viewItems[i];
1194
+ const proportion = this.proportions[i];
1195
+ if (typeof proportion === 'number' && total > 0) {
1196
+ item.size = clamp(Math.round((proportion * size) / total), item.minimumSize, item.maximumSize);
1197
+ }
1118
1198
  }
1119
1199
  }
1120
1200
  this.distributeEmptySpace();
@@ -1151,12 +1231,12 @@ class Splitview {
1151
1231
  }
1152
1232
  }
1153
1233
  saveProportions() {
1154
- if (this.proportionalLayout && this.contentSize > 0) {
1155
- this._proportions = this.viewItems.map((i) => i.size / this.contentSize);
1234
+ if (this.proportionalLayout && this._contentSize > 0) {
1235
+ this._proportions = this.viewItems.map((i) => i.visible ? i.size / this._contentSize : undefined);
1156
1236
  }
1157
1237
  }
1158
1238
  layoutViews() {
1159
- this.contentSize = this.viewItems.reduce((r, i) => r + i.size, 0);
1239
+ this._contentSize = this.viewItems.reduce((r, i) => r + i.size, 0);
1160
1240
  let sum = 0;
1161
1241
  const x = [];
1162
1242
  this.updateSashEnablement();
@@ -1250,7 +1330,7 @@ class Splitview {
1250
1330
  }
1251
1331
  else if (snappedAfter &&
1252
1332
  collapsesDown[index] &&
1253
- (position < this.contentSize || this.endSnappingEnabled)) {
1333
+ (position < this._contentSize || this.endSnappingEnabled)) {
1254
1334
  this.updateSash(sash, SashState.MAXIMUM);
1255
1335
  }
1256
1336
  else {
@@ -1539,7 +1619,6 @@ class LeafNode {
1539
1619
  setVisible(visible) {
1540
1620
  if (this.view.setVisible) {
1541
1621
  this.view.setVisible(visible);
1542
- this._onDidChange.fire({});
1543
1622
  }
1544
1623
  }
1545
1624
  layout(size, orthogonalSize) {
@@ -1571,10 +1650,14 @@ class BranchNode extends CompositeDisposable {
1571
1650
  get minimumSize() {
1572
1651
  return this.children.length === 0
1573
1652
  ? 0
1574
- : Math.max(...this.children.map((c) => c.minimumOrthogonalSize));
1653
+ : Math.max(...this.children.map((c, index) => this.splitview.isViewVisible(index)
1654
+ ? c.minimumOrthogonalSize
1655
+ : 0));
1575
1656
  }
1576
1657
  get maximumSize() {
1577
- return Math.min(...this.children.map((c) => c.maximumOrthogonalSize));
1658
+ return Math.min(...this.children.map((c, index) => this.splitview.isViewVisible(index)
1659
+ ? c.maximumOrthogonalSize
1660
+ : Number.POSITIVE_INFINITY));
1578
1661
  }
1579
1662
  get minimumOrthogonalSize() {
1580
1663
  return this.splitview.minimumSize;
@@ -1632,6 +1715,8 @@ class BranchNode extends CompositeDisposable {
1632
1715
  this.children = [];
1633
1716
  this._onDidChange = new Emitter();
1634
1717
  this.onDidChange = this._onDidChange.event;
1718
+ this._onDidVisibilityChange = new Emitter();
1719
+ this.onDidVisibilityChange = this._onDidVisibilityChange.event;
1635
1720
  this._orthogonalSize = orthogonalSize;
1636
1721
  this._size = size;
1637
1722
  this.element = document.createElement('div');
@@ -1666,7 +1751,7 @@ class BranchNode extends CompositeDisposable {
1666
1751
  styles,
1667
1752
  });
1668
1753
  }
1669
- this.addDisposables(this._onDidChange, this.splitview.onDidSashEnd(() => {
1754
+ this.addDisposables(this._onDidChange, this._onDidVisibilityChange, this.splitview.onDidSashEnd(() => {
1670
1755
  this._onDidChange.fire({});
1671
1756
  }));
1672
1757
  this.setupChildrenEvents();
@@ -1689,7 +1774,15 @@ class BranchNode extends CompositeDisposable {
1689
1774
  if (this.splitview.isViewVisible(index) === visible) {
1690
1775
  return;
1691
1776
  }
1777
+ const wereAllChildrenHidden = this.splitview.contentSize === 0;
1692
1778
  this.splitview.setViewVisible(index, visible);
1779
+ const areAllChildrenHidden = this.splitview.contentSize === 0;
1780
+ // If all children are hidden then the parent should hide the entire splitview
1781
+ // If the entire splitview is hidden then the parent should show the splitview when a child is shown
1782
+ if ((visible && wereAllChildrenHidden) ||
1783
+ (!visible && areAllChildrenHidden)) {
1784
+ this._onDidVisibilityChange.fire(visible);
1785
+ }
1693
1786
  }
1694
1787
  moveChild(from, to) {
1695
1788
  if (from === to) {
@@ -1753,13 +1846,20 @@ class BranchNode extends CompositeDisposable {
1753
1846
  }
1754
1847
  setupChildrenEvents() {
1755
1848
  this._childrenDisposable.dispose();
1756
- this._childrenDisposable = Event.any(...this.children.map((c) => c.onDidChange))((e) => {
1849
+ this._childrenDisposable = new CompositeDisposable(Event.any(...this.children.map((c) => c.onDidChange))((e) => {
1757
1850
  /**
1758
1851
  * indicate a change has occured to allows any re-rendering but don't bubble
1759
1852
  * event because that was specific to this branch
1760
1853
  */
1761
1854
  this._onDidChange.fire({ size: e.orthogonalSize });
1762
- });
1855
+ }), ...this.children.map((c, i) => {
1856
+ if (c instanceof BranchNode) {
1857
+ return c.onDidVisibilityChange((visible) => {
1858
+ this.setChildVisible(i, visible);
1859
+ });
1860
+ }
1861
+ return Disposable.NONE;
1862
+ }));
1763
1863
  }
1764
1864
  dispose() {
1765
1865
  this._childrenDisposable.dispose();
@@ -1920,7 +2020,69 @@ class Gridview {
1920
2020
  get maximumHeight() {
1921
2021
  return this.root.maximumHeight;
1922
2022
  }
2023
+ maximizedView() {
2024
+ var _a;
2025
+ return (_a = this._maximizedNode) === null || _a === void 0 ? void 0 : _a.view;
2026
+ }
2027
+ hasMaximizedView() {
2028
+ return this._maximizedNode !== undefined;
2029
+ }
2030
+ maximizeView(view) {
2031
+ const location = getGridLocation(view.element);
2032
+ const [_, node] = this.getNode(location);
2033
+ if (!(node instanceof LeafNode)) {
2034
+ return;
2035
+ }
2036
+ if (this._maximizedNode === node) {
2037
+ return;
2038
+ }
2039
+ if (this.hasMaximizedView()) {
2040
+ this.exitMaximizedView();
2041
+ }
2042
+ function hideAllViewsBut(parent, exclude) {
2043
+ for (let i = 0; i < parent.children.length; i++) {
2044
+ const child = parent.children[i];
2045
+ if (child instanceof LeafNode) {
2046
+ if (child !== exclude) {
2047
+ parent.setChildVisible(i, false);
2048
+ }
2049
+ }
2050
+ else {
2051
+ hideAllViewsBut(child, exclude);
2052
+ }
2053
+ }
2054
+ }
2055
+ hideAllViewsBut(this.root, node);
2056
+ this._maximizedNode = node;
2057
+ this._onDidMaxmizedNodeChange.fire();
2058
+ }
2059
+ exitMaximizedView() {
2060
+ if (!this._maximizedNode) {
2061
+ return;
2062
+ }
2063
+ function showViewsInReverseOrder(parent) {
2064
+ for (let index = parent.children.length - 1; index >= 0; index--) {
2065
+ const child = parent.children[index];
2066
+ if (child instanceof LeafNode) {
2067
+ parent.setChildVisible(index, true);
2068
+ }
2069
+ else {
2070
+ showViewsInReverseOrder(child);
2071
+ }
2072
+ }
2073
+ }
2074
+ showViewsInReverseOrder(this.root);
2075
+ this._maximizedNode = undefined;
2076
+ this._onDidMaxmizedNodeChange.fire();
2077
+ }
1923
2078
  serialize() {
2079
+ if (this.hasMaximizedView()) {
2080
+ /**
2081
+ * do not persist maximized view state but we must first exit any maximized views
2082
+ * before serialization to ensure the correct dimensions are persisted
2083
+ */
2084
+ this.exitMaximizedView();
2085
+ }
1924
2086
  const root = serializeBranchNode(this.getView(), this.orientation);
1925
2087
  return {
1926
2088
  root,
@@ -1932,7 +2094,9 @@ class Gridview {
1932
2094
  dispose() {
1933
2095
  this.disposable.dispose();
1934
2096
  this._onDidChange.dispose();
2097
+ this._onDidMaxmizedNodeChange.dispose();
1935
2098
  this.root.dispose();
2099
+ this._maximizedNode = undefined;
1936
2100
  this.element.remove();
1937
2101
  }
1938
2102
  clear() {
@@ -1973,6 +2137,7 @@ class Gridview {
1973
2137
  const oldRoot = this._root;
1974
2138
  if (oldRoot) {
1975
2139
  oldRoot.dispose();
2140
+ this._maximizedNode = undefined;
1976
2141
  this.element.removeChild(oldRoot.element);
1977
2142
  }
1978
2143
  this._root = root;
@@ -2059,9 +2224,12 @@ class Gridview {
2059
2224
  constructor(proportionalLayout, styles, orientation) {
2060
2225
  this.proportionalLayout = proportionalLayout;
2061
2226
  this.styles = styles;
2227
+ this._maximizedNode = undefined;
2062
2228
  this.disposable = new MutableDisposable();
2063
2229
  this._onDidChange = new Emitter();
2064
2230
  this.onDidChange = this._onDidChange.event;
2231
+ this._onDidMaxmizedNodeChange = new Emitter();
2232
+ this.onDidMaxmizedNodeChange = this._onDidMaxmizedNodeChange.event;
2065
2233
  this.element = document.createElement('div');
2066
2234
  this.element.className = 'grid-view';
2067
2235
  this.root = new BranchNode(orientation, proportionalLayout, styles, 0, 0);
@@ -2075,6 +2243,9 @@ class Gridview {
2075
2243
  return parent.isChildVisible(index);
2076
2244
  }
2077
2245
  setViewVisible(location, visible) {
2246
+ if (this.hasMaximizedView()) {
2247
+ this.exitMaximizedView();
2248
+ }
2078
2249
  const [rest, index] = tail(location);
2079
2250
  const [, parent] = this.getNode(rest);
2080
2251
  if (!(parent instanceof BranchNode)) {
@@ -2083,6 +2254,9 @@ class Gridview {
2083
2254
  parent.setChildVisible(index, visible);
2084
2255
  }
2085
2256
  moveView(parentLocation, from, to) {
2257
+ if (this.hasMaximizedView()) {
2258
+ this.exitMaximizedView();
2259
+ }
2086
2260
  const [, parent] = this.getNode(parentLocation);
2087
2261
  if (!(parent instanceof BranchNode)) {
2088
2262
  throw new Error('Invalid location');
@@ -2090,6 +2264,9 @@ class Gridview {
2090
2264
  parent.moveChild(from, to);
2091
2265
  }
2092
2266
  addView(view, size, location) {
2267
+ if (this.hasMaximizedView()) {
2268
+ this.exitMaximizedView();
2269
+ }
2093
2270
  const [rest, index] = tail(location);
2094
2271
  const [pathToParent, parent] = this.getNode(rest);
2095
2272
  if (parent instanceof BranchNode) {
@@ -2122,6 +2299,9 @@ class Gridview {
2122
2299
  return this.removeView(location, sizing);
2123
2300
  }
2124
2301
  removeView(location, sizing) {
2302
+ if (this.hasMaximizedView()) {
2303
+ this.exitMaximizedView();
2304
+ }
2125
2305
  const [rest, index] = tail(location);
2126
2306
  const [pathToParent, parent] = this.getNode(rest);
2127
2307
  if (!(parent instanceof BranchNode)) {
@@ -2859,6 +3039,24 @@ class DockviewApi {
2859
3039
  moveToPrevious(options) {
2860
3040
  this.component.moveToPrevious(options);
2861
3041
  }
3042
+ maximizeGroup(panel) {
3043
+ this.component.maximizeGroup(panel.group);
3044
+ }
3045
+ hasMaximizedGroup() {
3046
+ return this.component.hasMaximizedGroup();
3047
+ }
3048
+ exitMaxmizedGroup() {
3049
+ this.component.exitMaximizedGroup();
3050
+ }
3051
+ get onDidMaxmizedGroupChange() {
3052
+ return this.component.onDidMaxmizedGroupChange;
3053
+ }
3054
+ /**
3055
+ * Add a popout group in a new Window
3056
+ */
3057
+ addPopoutGroup(item, options) {
3058
+ this.component.addPopoutGroup(item, options);
3059
+ }
2862
3060
  }
2863
3061
 
2864
3062
  class DragAndDropObserver extends CompositeDisposable {
@@ -2869,36 +3067,48 @@ class DragAndDropObserver extends CompositeDisposable {
2869
3067
  this.target = null;
2870
3068
  this.registerListeners();
2871
3069
  }
3070
+ onDragEnter(e) {
3071
+ this.target = e.target;
3072
+ this.callbacks.onDragEnter(e);
3073
+ }
3074
+ onDragOver(e) {
3075
+ e.preventDefault(); // needed so that the drop event fires (https://stackoverflow.com/questions/21339924/drop-event-not-firing-in-chrome)
3076
+ if (this.callbacks.onDragOver) {
3077
+ this.callbacks.onDragOver(e);
3078
+ }
3079
+ }
3080
+ onDragLeave(e) {
3081
+ if (this.target === e.target) {
3082
+ this.target = null;
3083
+ this.callbacks.onDragLeave(e);
3084
+ }
3085
+ }
3086
+ onDragEnd(e) {
3087
+ this.target = null;
3088
+ this.callbacks.onDragEnd(e);
3089
+ }
3090
+ onDrop(e) {
3091
+ this.callbacks.onDrop(e);
3092
+ }
2872
3093
  registerListeners() {
2873
3094
  this.addDisposables(addDisposableListener(this.element, 'dragenter', (e) => {
2874
- this.target = e.target;
2875
- this.callbacks.onDragEnter(e);
3095
+ this.onDragEnter(e);
2876
3096
  }, true));
2877
3097
  this.addDisposables(addDisposableListener(this.element, 'dragover', (e) => {
2878
- e.preventDefault(); // needed so that the drop event fires (https://stackoverflow.com/questions/21339924/drop-event-not-firing-in-chrome)
2879
- if (this.callbacks.onDragOver) {
2880
- this.callbacks.onDragOver(e);
2881
- }
3098
+ this.onDragOver(e);
2882
3099
  }, true));
2883
3100
  this.addDisposables(addDisposableListener(this.element, 'dragleave', (e) => {
2884
- if (this.target === e.target) {
2885
- this.target = null;
2886
- this.callbacks.onDragLeave(e);
2887
- }
3101
+ this.onDragLeave(e);
2888
3102
  }));
2889
3103
  this.addDisposables(addDisposableListener(this.element, 'dragend', (e) => {
2890
- this.target = null;
2891
- this.callbacks.onDragEnd(e);
3104
+ this.onDragEnd(e);
2892
3105
  }));
2893
3106
  this.addDisposables(addDisposableListener(this.element, 'drop', (e) => {
2894
- this.callbacks.onDrop(e);
3107
+ this.onDrop(e);
2895
3108
  }));
2896
3109
  }
2897
3110
  }
2898
3111
 
2899
- function numberOrFallback(maybeNumber, fallback) {
2900
- return typeof maybeNumber === 'number' ? maybeNumber : fallback;
2901
- }
2902
3112
  function directionToPosition(direction) {
2903
3113
  switch (direction) {
2904
3114
  case 'above':
@@ -2931,6 +3141,16 @@ function positionToDirection(position) {
2931
3141
  throw new Error(`invalid position '${position}'`);
2932
3142
  }
2933
3143
  }
3144
+ const DEFAULT_ACTIVATION_SIZE = {
3145
+ value: 20,
3146
+ type: 'percentage',
3147
+ };
3148
+ const DEFAULT_SIZE = {
3149
+ value: 50,
3150
+ type: 'percentage',
3151
+ };
3152
+ const SMALL_WIDTH_BOUNDARY = 100;
3153
+ const SMALL_HEIGHT_BOUNDARY = 100;
2934
3154
  class Droptarget extends CompositeDisposable {
2935
3155
  get state() {
2936
3156
  return this._state;
@@ -2943,7 +3163,7 @@ class Droptarget extends CompositeDisposable {
2943
3163
  this.onDrop = this._onDrop.event;
2944
3164
  // use a set to take advantage of #<set>.has
2945
3165
  this._acceptedTargetZonesSet = new Set(this.options.acceptedTargetZones);
2946
- this.addDisposables(this._onDrop, new DragAndDropObserver(this.element, {
3166
+ this.dnd = new DragAndDropObserver(this.element, {
2947
3167
  onDragEnter: () => undefined,
2948
3168
  onDragOver: (e) => {
2949
3169
  if (this._acceptedTargetZonesSet.size === 0) {
@@ -2991,7 +3211,7 @@ class Droptarget extends CompositeDisposable {
2991
3211
  this.element.append(this.targetElement);
2992
3212
  }
2993
3213
  this.toggleClasses(quadrant, width, height);
2994
- this.setState(quadrant);
3214
+ this._state = quadrant;
2995
3215
  },
2996
3216
  onDragLeave: () => {
2997
3217
  this.removeDropTarget();
@@ -3010,11 +3230,15 @@ class Droptarget extends CompositeDisposable {
3010
3230
  this._onDrop.fire({ position: state, nativeEvent: e });
3011
3231
  }
3012
3232
  },
3013
- }));
3233
+ });
3234
+ this.addDisposables(this._onDrop, this.dnd);
3014
3235
  }
3015
3236
  setTargetZones(acceptedTargetZones) {
3016
3237
  this._acceptedTargetZonesSet = new Set(acceptedTargetZones);
3017
3238
  }
3239
+ setOverlayModel(model) {
3240
+ this.options.overlayModel = model;
3241
+ }
3018
3242
  dispose() {
3019
3243
  this.removeDropTarget();
3020
3244
  super.dispose();
@@ -3026,19 +3250,19 @@ class Droptarget extends CompositeDisposable {
3026
3250
  event[Droptarget.USED_EVENT_ID] = true;
3027
3251
  }
3028
3252
  /**
3029
- * Check is the event has already been used by another instance od DropTarget
3253
+ * Check is the event has already been used by another instance of DropTarget
3030
3254
  */
3031
3255
  isAlreadyUsed(event) {
3032
3256
  const value = event[Droptarget.USED_EVENT_ID];
3033
3257
  return typeof value === 'boolean' && value;
3034
3258
  }
3035
3259
  toggleClasses(quadrant, width, height) {
3036
- var _a, _b, _c, _d;
3260
+ var _a, _b;
3037
3261
  if (!this.overlayElement) {
3038
3262
  return;
3039
3263
  }
3040
- const isSmallX = width < 100;
3041
- const isSmallY = height < 100;
3264
+ const isSmallX = width < SMALL_WIDTH_BOUNDARY;
3265
+ const isSmallY = height < SMALL_HEIGHT_BOUNDARY;
3042
3266
  const isLeft = quadrant === 'left';
3043
3267
  const isRight = quadrant === 'right';
3044
3268
  const isTop = quadrant === 'top';
@@ -3047,20 +3271,17 @@ class Droptarget extends CompositeDisposable {
3047
3271
  const leftClass = !isSmallX && isLeft;
3048
3272
  const topClass = !isSmallY && isTop;
3049
3273
  const bottomClass = !isSmallY && isBottom;
3050
- let size = 0.5;
3051
- if (((_b = (_a = this.options.overlayModel) === null || _a === void 0 ? void 0 : _a.size) === null || _b === void 0 ? void 0 : _b.type) === 'percentage') {
3052
- size = clamp(this.options.overlayModel.size.value, 0, 100) / 100;
3274
+ let size = 1;
3275
+ const sizeOptions = (_b = (_a = this.options.overlayModel) === null || _a === void 0 ? void 0 : _a.size) !== null && _b !== void 0 ? _b : DEFAULT_SIZE;
3276
+ if (sizeOptions.type === 'percentage') {
3277
+ size = clamp(sizeOptions.value, 0, 100) / 100;
3053
3278
  }
3054
- if (((_d = (_c = this.options.overlayModel) === null || _c === void 0 ? void 0 : _c.size) === null || _d === void 0 ? void 0 : _d.type) === 'pixels') {
3279
+ else {
3055
3280
  if (rightClass || leftClass) {
3056
- size =
3057
- clamp(0, this.options.overlayModel.size.value, width) /
3058
- width;
3281
+ size = clamp(0, sizeOptions.value, width) / width;
3059
3282
  }
3060
3283
  if (topClass || bottomClass) {
3061
- size =
3062
- clamp(0, this.options.overlayModel.size.value, height) /
3063
- height;
3284
+ size = clamp(0, sizeOptions.value, height) / height;
3064
3285
  }
3065
3286
  }
3066
3287
  const translate = (1 - size) / 2;
@@ -3082,39 +3303,22 @@ class Droptarget extends CompositeDisposable {
3082
3303
  transform = '';
3083
3304
  }
3084
3305
  this.overlayElement.style.transform = transform;
3085
- toggleClass(this.overlayElement, 'small-right', isSmallX && isRight);
3086
- toggleClass(this.overlayElement, 'small-left', isSmallX && isLeft);
3087
- toggleClass(this.overlayElement, 'small-top', isSmallY && isTop);
3088
- toggleClass(this.overlayElement, 'small-bottom', isSmallY && isBottom);
3089
- }
3090
- setState(quadrant) {
3091
- switch (quadrant) {
3092
- case 'top':
3093
- this._state = 'top';
3094
- break;
3095
- case 'left':
3096
- this._state = 'left';
3097
- break;
3098
- case 'bottom':
3099
- this._state = 'bottom';
3100
- break;
3101
- case 'right':
3102
- this._state = 'right';
3103
- break;
3104
- case 'center':
3105
- this._state = 'center';
3106
- break;
3107
- }
3306
+ toggleClass(this.overlayElement, 'dv-drop-target-small-vertical', isSmallY);
3307
+ toggleClass(this.overlayElement, 'dv-drop-target-small-horizontal', isSmallX);
3308
+ toggleClass(this.overlayElement, 'dv-drop-target-left', isLeft);
3309
+ toggleClass(this.overlayElement, 'dv-drop-target-right', isRight);
3310
+ toggleClass(this.overlayElement, 'dv-drop-target-top', isTop);
3311
+ toggleClass(this.overlayElement, 'dv-drop-target-bottom', isBottom);
3312
+ toggleClass(this.overlayElement, 'dv-drop-target-center', quadrant === 'center');
3108
3313
  }
3109
3314
  calculateQuadrant(overlayType, x, y, width, height) {
3110
- var _a, _b, _c, _d, _e, _f;
3111
- const isPercentage = ((_a = this.options.overlayModel) === null || _a === void 0 ? void 0 : _a.activationSize) === undefined ||
3112
- ((_c = (_b = this.options.overlayModel) === null || _b === void 0 ? void 0 : _b.activationSize) === null || _c === void 0 ? void 0 : _c.type) === 'percentage';
3113
- 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);
3315
+ var _a, _b;
3316
+ const activationSizeOptions = (_b = (_a = this.options.overlayModel) === null || _a === void 0 ? void 0 : _a.activationSize) !== null && _b !== void 0 ? _b : DEFAULT_ACTIVATION_SIZE;
3317
+ const isPercentage = activationSizeOptions.type === 'percentage';
3114
3318
  if (isPercentage) {
3115
- return calculateQuadrantAsPercentage(overlayType, x, y, width, height, value);
3319
+ return calculateQuadrantAsPercentage(overlayType, x, y, width, height, activationSizeOptions.value);
3116
3320
  }
3117
- return calculateQuadrantAsPixels(overlayType, x, y, width, height, value);
3321
+ return calculateQuadrantAsPixels(overlayType, x, y, width, height, activationSizeOptions.value);
3118
3322
  }
3119
3323
  removeDropTarget() {
3120
3324
  if (this.targetElement) {
@@ -3166,12 +3370,22 @@ function calculateQuadrantAsPixels(overlayType, x, y, width, height, threshold)
3166
3370
  return 'center';
3167
3371
  }
3168
3372
 
3373
+ var DockviewDropTargets;
3374
+ (function (DockviewDropTargets) {
3375
+ DockviewDropTargets[DockviewDropTargets["Tab"] = 0] = "Tab";
3376
+ DockviewDropTargets[DockviewDropTargets["Panel"] = 1] = "Panel";
3377
+ DockviewDropTargets[DockviewDropTargets["TabContainer"] = 2] = "TabContainer";
3378
+ DockviewDropTargets[DockviewDropTargets["Edge"] = 3] = "Edge";
3379
+ })(DockviewDropTargets || (DockviewDropTargets = {}));
3380
+
3169
3381
  class ContentContainer extends CompositeDisposable {
3170
3382
  get element() {
3171
3383
  return this._element;
3172
3384
  }
3173
- constructor() {
3385
+ constructor(accessor, group) {
3174
3386
  super();
3387
+ this.accessor = accessor;
3388
+ this.group = group;
3175
3389
  this.disposable = new MutableDisposable();
3176
3390
  this._onDidFocus = new Emitter();
3177
3391
  this.onDidFocus = this._onDidFocus.event;
@@ -3181,11 +3395,38 @@ class ContentContainer extends CompositeDisposable {
3181
3395
  this._element.className = 'content-container';
3182
3396
  this._element.tabIndex = -1;
3183
3397
  this.addDisposables(this._onDidFocus, this._onDidBlur);
3184
- // for hosted containers
3185
- // 1) register a drop target on the host
3186
- // 2) register window dragStart events to disable pointer events
3187
- // 3) register dragEnd events
3188
- // 4) register mouseMove events (if no buttons are present we take this as a dragEnd event)
3398
+ this.dropTarget = new Droptarget(this.element, {
3399
+ acceptedTargetZones: ['top', 'bottom', 'left', 'right', 'center'],
3400
+ canDisplayOverlay: (event, position) => {
3401
+ if (this.group.locked === 'no-drop-target' ||
3402
+ (this.group.locked && position === 'center')) {
3403
+ return false;
3404
+ }
3405
+ const data = getPanelData();
3406
+ if (!data &&
3407
+ event.shiftKey &&
3408
+ this.group.location !== 'floating') {
3409
+ return false;
3410
+ }
3411
+ if (data && data.viewId === this.accessor.id) {
3412
+ if (data.groupId === this.group.id) {
3413
+ if (position === 'center') {
3414
+ // don't allow to drop on self for center position
3415
+ return false;
3416
+ }
3417
+ if (data.panelId === null) {
3418
+ // don't allow group move to drop anywhere on self
3419
+ return false;
3420
+ }
3421
+ }
3422
+ const groupHasOnePanelAndIsActiveDragElement = this.group.panels.length === 1 &&
3423
+ data.groupId === this.group.id;
3424
+ return !groupHasOnePanelAndIsActiveDragElement;
3425
+ }
3426
+ return this.group.canDisplayOverlay(event, position, DockviewDropTargets.Panel);
3427
+ },
3428
+ });
3429
+ this.addDisposables(this.dropTarget);
3189
3430
  }
3190
3431
  show() {
3191
3432
  this.element.style.display = '';
@@ -3193,23 +3434,43 @@ class ContentContainer extends CompositeDisposable {
3193
3434
  hide() {
3194
3435
  this.element.style.display = 'none';
3195
3436
  }
3196
- openPanel(panel) {
3197
- var _a;
3198
- if (this.panel === panel) {
3199
- return;
3200
- }
3201
- if (this.panel) {
3202
- if ((_a = this.panel.view) === null || _a === void 0 ? void 0 : _a.content) {
3203
- this._element.removeChild(this.panel.view.content.element);
3204
- }
3205
- this.panel = undefined;
3437
+ renderPanel(panel, options = { asActive: true }) {
3438
+ const doRender = options.asActive ||
3439
+ (this.panel && this.group.isPanelActive(this.panel));
3440
+ if (this.panel &&
3441
+ this.panel.view.content.element.parentElement === this._element) {
3442
+ /**
3443
+ * If the currently attached panel is mounted directly to the content then remove it
3444
+ */
3445
+ this._element.removeChild(this.panel.view.content.element);
3206
3446
  }
3207
3447
  this.panel = panel;
3208
- const disposable = new CompositeDisposable();
3209
- if (this.panel.view) {
3210
- const _onDidFocus = this.panel.view.content.onDidFocus;
3211
- const _onDidBlur = this.panel.view.content.onDidBlur;
3212
- const focusTracker = trackFocus(this._element);
3448
+ let container;
3449
+ switch (panel.api.renderer) {
3450
+ case 'onlyWhenVisibile':
3451
+ this.accessor.overlayRenderContainer.detatch(panel);
3452
+ if (this.panel) {
3453
+ if (doRender) {
3454
+ this._element.appendChild(this.panel.view.content.element);
3455
+ }
3456
+ }
3457
+ container = this._element;
3458
+ break;
3459
+ case 'always':
3460
+ if (panel.view.content.element.parentElement === this._element) {
3461
+ this._element.removeChild(panel.view.content.element);
3462
+ }
3463
+ container = this.accessor.overlayRenderContainer.attach({
3464
+ panel,
3465
+ referenceContainer: this,
3466
+ });
3467
+ break;
3468
+ }
3469
+ if (doRender) {
3470
+ const _onDidFocus = panel.view.content.onDidFocus;
3471
+ const _onDidBlur = panel.view.content.onDidBlur;
3472
+ const focusTracker = trackFocus(container);
3473
+ const disposable = new CompositeDisposable();
3213
3474
  disposable.addDisposables(focusTracker, focusTracker.onDidFocus(() => this._onDidFocus.fire()), focusTracker.onDidBlur(() => this._onDidBlur.fire()));
3214
3475
  if (_onDidFocus) {
3215
3476
  disposable.addDisposables(_onDidFocus(() => this._onDidFocus.fire()));
@@ -3217,17 +3478,23 @@ class ContentContainer extends CompositeDisposable {
3217
3478
  if (_onDidBlur) {
3218
3479
  disposable.addDisposables(_onDidBlur(() => this._onDidBlur.fire()));
3219
3480
  }
3220
- this._element.appendChild(this.panel.view.content.element);
3481
+ this.disposable.value = disposable;
3221
3482
  }
3222
- this.disposable.value = disposable;
3483
+ }
3484
+ openPanel(panel) {
3485
+ if (this.panel === panel) {
3486
+ return;
3487
+ }
3488
+ this.renderPanel(panel);
3223
3489
  }
3224
3490
  layout(_width, _height) {
3225
3491
  // noop
3226
3492
  }
3227
3493
  closePanel() {
3228
- var _a, _b, _c;
3229
- 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) {
3230
- this._element.removeChild(this.panel.view.content.element);
3494
+ if (this.panel) {
3495
+ if (this.accessor.options.defaultRenderer === 'onlyWhenVisibile') {
3496
+ this._element.removeChild(this.panel.view.content.element);
3497
+ }
3231
3498
  this.panel = undefined;
3232
3499
  }
3233
3500
  }
@@ -3237,14 +3504,6 @@ class ContentContainer extends CompositeDisposable {
3237
3504
  }
3238
3505
  }
3239
3506
 
3240
- var DockviewDropTargets;
3241
- (function (DockviewDropTargets) {
3242
- DockviewDropTargets[DockviewDropTargets["Tab"] = 0] = "Tab";
3243
- DockviewDropTargets[DockviewDropTargets["Panel"] = 1] = "Panel";
3244
- DockviewDropTargets[DockviewDropTargets["TabContainer"] = 2] = "TabContainer";
3245
- DockviewDropTargets[DockviewDropTargets["Edge"] = 3] = "Edge";
3246
- })(DockviewDropTargets || (DockviewDropTargets = {}));
3247
-
3248
3507
  class DragHandler extends CompositeDisposable {
3249
3508
  constructor(el) {
3250
3509
  super();
@@ -3419,7 +3678,7 @@ class GroupDragHandler extends DragHandler {
3419
3678
  }, true));
3420
3679
  }
3421
3680
  isCancelled(_event) {
3422
- if (this.group.api.isFloating && !_event.shiftKey) {
3681
+ if (this.group.api.location === 'floating' && !_event.shiftKey) {
3423
3682
  return true;
3424
3683
  }
3425
3684
  return false;
@@ -3621,7 +3880,7 @@ class TabsContainer extends CompositeDisposable {
3621
3880
  const isFloatingGroupsEnabled = !this.accessor.options.disableFloatingGroups;
3622
3881
  if (isFloatingGroupsEnabled &&
3623
3882
  event.shiftKey &&
3624
- !this.group.api.isFloating) {
3883
+ this.group.api.location !== 'floating') {
3625
3884
  event.preventDefault();
3626
3885
  const { top, left } = this.element.getBoundingClientRect();
3627
3886
  const { top: rootTop, left: rootLeft } = this.accessor.element.getBoundingClientRect();
@@ -3686,7 +3945,7 @@ class TabsContainer extends CompositeDisposable {
3686
3945
  }), tab.onChanged((event) => {
3687
3946
  var _a;
3688
3947
  const isFloatingGroupsEnabled = !this.accessor.options.disableFloatingGroups;
3689
- const isFloatingWithOnePanel = this.group.api.isFloating && this.size === 1;
3948
+ const isFloatingWithOnePanel = this.group.api.location === 'floating' && this.size === 1;
3690
3949
  if (isFloatingGroupsEnabled &&
3691
3950
  !isFloatingWithOnePanel &&
3692
3951
  event.shiftKey) {
@@ -3769,15 +4028,37 @@ class DockviewGroupPanelModel extends CompositeDisposable {
3769
4028
  }
3770
4029
  return isAncestor(document.activeElement, this.contentContainer.element);
3771
4030
  }
3772
- get isFloating() {
3773
- return this._isFloating;
3774
- }
3775
- set isFloating(value) {
3776
- this._isFloating = value;
3777
- this.dropTarget.setTargetZones(value ? ['center'] : ['top', 'bottom', 'left', 'right', 'center']);
3778
- toggleClass(this.container, 'dv-groupview-floating', value);
3779
- this.groupPanel.api._onDidFloatingStateChange.fire({
3780
- isFloating: this.isFloating,
4031
+ get location() {
4032
+ return this._location;
4033
+ }
4034
+ set location(value) {
4035
+ this._location = value;
4036
+ toggleClass(this.container, 'dv-groupview-floating', false);
4037
+ toggleClass(this.container, 'dv-groupview-popout', false);
4038
+ switch (value) {
4039
+ case 'grid':
4040
+ this.contentContainer.dropTarget.setTargetZones([
4041
+ 'top',
4042
+ 'bottom',
4043
+ 'left',
4044
+ 'right',
4045
+ 'center',
4046
+ ]);
4047
+ break;
4048
+ case 'floating':
4049
+ this.contentContainer.dropTarget.setTargetZones(['center']);
4050
+ this.contentContainer.dropTarget.setTargetZones(value
4051
+ ? ['center']
4052
+ : ['top', 'bottom', 'left', 'right', 'center']);
4053
+ toggleClass(this.container, 'dv-groupview-floating', true);
4054
+ break;
4055
+ case 'popout':
4056
+ this.contentContainer.dropTarget.setTargetZones(['center']);
4057
+ toggleClass(this.container, 'dv-groupview-popout', true);
4058
+ break;
4059
+ }
4060
+ this.groupPanel.api._onDidLocationChange.fire({
4061
+ location: this.location,
3781
4062
  });
3782
4063
  }
3783
4064
  constructor(container, accessor, id, options, groupPanel) {
@@ -3790,7 +4071,7 @@ class DockviewGroupPanelModel extends CompositeDisposable {
3790
4071
  this.groupPanel = groupPanel;
3791
4072
  this._isGroupActive = false;
3792
4073
  this._locked = false;
3793
- this._isFloating = false;
4074
+ this._location = 'grid';
3794
4075
  this.mostRecentlyUsed = [];
3795
4076
  this._onDidChange = new Emitter();
3796
4077
  this.onDidChange = this._onDidChange.event;
@@ -3813,35 +4094,7 @@ class DockviewGroupPanelModel extends CompositeDisposable {
3813
4094
  this.onDidActivePanelChange = this._onDidActivePanelChange.event;
3814
4095
  toggleClass(this.container, 'groupview', true);
3815
4096
  this.tabsContainer = new TabsContainer(this.accessor, this.groupPanel);
3816
- this.contentContainer = new ContentContainer();
3817
- this.dropTarget = new Droptarget(this.contentContainer.element, {
3818
- acceptedTargetZones: ['top', 'bottom', 'left', 'right', 'center'],
3819
- canDisplayOverlay: (event, position) => {
3820
- if (this.locked === 'no-drop-target' ||
3821
- (this.locked && position === 'center')) {
3822
- return false;
3823
- }
3824
- const data = getPanelData();
3825
- if (!data && event.shiftKey && !this.isFloating) {
3826
- return false;
3827
- }
3828
- if (data && data.viewId === this.accessor.id) {
3829
- if (data.groupId === this.id) {
3830
- if (position === 'center') {
3831
- // don't allow to drop on self for center position
3832
- return false;
3833
- }
3834
- if (data.panelId === null) {
3835
- // don't allow group move to drop anywhere on self
3836
- return false;
3837
- }
3838
- }
3839
- const groupHasOnePanelAndIsActiveDragElement = this._panels.length === 1 && data.groupId === this.id;
3840
- return !groupHasOnePanelAndIsActiveDragElement;
3841
- }
3842
- return this.canDisplayOverlay(event, position, DockviewDropTargets.Panel);
3843
- },
3844
- });
4097
+ this.contentContainer = new ContentContainer(this.accessor, this);
3845
4098
  container.append(this.tabsContainer.element, this.contentContainer.element);
3846
4099
  this.header.hidden = !!options.hideHeader;
3847
4100
  this.locked = (_a = options.locked) !== null && _a !== void 0 ? _a : false;
@@ -3855,7 +4108,7 @@ class DockviewGroupPanelModel extends CompositeDisposable {
3855
4108
  this.accessor.doSetGroupActive(this.groupPanel, true);
3856
4109
  }), this.contentContainer.onDidBlur(() => {
3857
4110
  // noop
3858
- }), this.dropTarget.onDrop((event) => {
4111
+ }), this.contentContainer.dropTarget.onDrop((event) => {
3859
4112
  this.handleDropEvent(event.nativeEvent, event.position);
3860
4113
  }), this._onMove, this._onDidChange, this._onDidDrop, this._onDidAddPanel, this._onDidRemovePanel, this._onDidActivePanelChange);
3861
4114
  }
@@ -3904,6 +4157,9 @@ class DockviewGroupPanelModel extends CompositeDisposable {
3904
4157
  this.tabsContainer.setPrefixActionsElement(this._prefixHeaderActions.element);
3905
4158
  }
3906
4159
  }
4160
+ rerender(panel) {
4161
+ this.contentContainer.renderPanel(panel, { asActive: false });
4162
+ }
3907
4163
  indexOf(panel) {
3908
4164
  return this.tabsContainer.indexOf(panel.id);
3909
4165
  }
@@ -4095,12 +4351,12 @@ class DockviewGroupPanelModel extends CompositeDisposable {
4095
4351
  doAddPanel(panel, index = this.panels.length, skipSetActive = false) {
4096
4352
  const existingPanel = this._panels.indexOf(panel);
4097
4353
  const hasExistingPanel = existingPanel > -1;
4354
+ this.tabsContainer.show();
4355
+ this.contentContainer.show();
4098
4356
  this.tabsContainer.openPanel(panel, index);
4099
4357
  if (!skipSetActive) {
4100
4358
  this.contentContainer.openPanel(panel);
4101
4359
  }
4102
- this.tabsContainer.show();
4103
- this.contentContainer.show();
4104
4360
  if (hasExistingPanel) {
4105
4361
  // TODO - need to ensure ordering hasn't changed and if it has need to re-order this.panels
4106
4362
  return;
@@ -4216,7 +4472,6 @@ class DockviewGroupPanelModel extends CompositeDisposable {
4216
4472
  for (const panel of this.panels) {
4217
4473
  panel.dispose();
4218
4474
  }
4219
- this.dropTarget.dispose();
4220
4475
  this.tabsContainer.dispose();
4221
4476
  this.contentContainer.dispose();
4222
4477
  }
@@ -4255,7 +4510,22 @@ class Resizable extends CompositeDisposable {
4255
4510
  if (this.disableResizing) {
4256
4511
  return;
4257
4512
  }
4258
- if (!document.body.contains(this._element)) {
4513
+ if (!this._element.offsetParent) {
4514
+ /**
4515
+ * offsetParent === null is equivalent to display: none being set on the element or one
4516
+ * of it's parents. In the display: none case the size will become (0, 0) which we do
4517
+ * not want to propagate.
4518
+ *
4519
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/offsetParent
4520
+ *
4521
+ * You could use checkVisibility() but at the time of writing it's not supported across
4522
+ * all Browsers
4523
+ *
4524
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/Element/checkVisibility
4525
+ */
4526
+ return;
4527
+ }
4528
+ if (!isInDocument(this._element)) {
4259
4529
  /**
4260
4530
  * since the event is dispatched through requestAnimationFrame there is a small chance
4261
4531
  * the component is no longer attached to the DOM, if that is the case the dimensions
@@ -4347,6 +4617,21 @@ class BaseGrid extends Resizable {
4347
4617
  isVisible(panel) {
4348
4618
  return this.gridview.isViewVisible(getGridLocation(panel.element));
4349
4619
  }
4620
+ maximizeGroup(panel) {
4621
+ this.gridview.maximizeView(panel);
4622
+ }
4623
+ isMaximizedGroup(panel) {
4624
+ return this.gridview.maximizedView() === panel;
4625
+ }
4626
+ exitMaximizedGroup() {
4627
+ this.gridview.exitMaximizedView();
4628
+ }
4629
+ hasMaximizedGroup() {
4630
+ return this.gridview.hasMaximizedView();
4631
+ }
4632
+ get onDidMaxmizedGroupChange() {
4633
+ return this.gridview.onDidMaxmizedNodeChange;
4634
+ }
4350
4635
  doAddGroup(group, location = [0], size) {
4351
4636
  this.gridview.addView(group, size !== null && size !== void 0 ? size : Sizing.Distribute, location);
4352
4637
  this._onDidAddGroup.fire(group);
@@ -5123,32 +5408,63 @@ class GridviewPanel extends BasePanelView {
5123
5408
  }
5124
5409
  }
5125
5410
 
5411
+ // TODO find a better way to initialize and avoid needing null checks
5412
+ const NOT_INITIALIZED_MESSAGE = 'DockviewGroupPanelApiImpl not initialized';
5126
5413
  class DockviewGroupPanelApiImpl extends GridviewPanelApiImpl {
5127
- get isFloating() {
5414
+ get location() {
5128
5415
  if (!this._group) {
5129
- throw new Error(`DockviewGroupPanelApiImpl not initialized`);
5416
+ throw new Error(NOT_INITIALIZED_MESSAGE);
5130
5417
  }
5131
- return this._group.model.isFloating;
5418
+ return this._group.model.location;
5132
5419
  }
5133
5420
  constructor(id, accessor) {
5134
5421
  super(id);
5135
5422
  this.accessor = accessor;
5136
- this._onDidFloatingStateChange = new Emitter();
5137
- this.onDidFloatingStateChange = this._onDidFloatingStateChange.event;
5138
- this.addDisposables(this._onDidFloatingStateChange);
5423
+ this._onDidLocationChange = new Emitter();
5424
+ this.onDidLocationChange = this._onDidLocationChange.event;
5425
+ this.addDisposables(this._onDidLocationChange);
5139
5426
  }
5140
5427
  moveTo(options) {
5141
- var _a;
5428
+ var _a, _b, _c;
5429
+ if (!this._group) {
5430
+ throw new Error(NOT_INITIALIZED_MESSAGE);
5431
+ }
5432
+ const group = (_a = options.group) !== null && _a !== void 0 ? _a : this.accessor.addGroup({
5433
+ direction: positionToDirection((_b = options.position) !== null && _b !== void 0 ? _b : 'right'),
5434
+ });
5435
+ this.accessor.moveGroupOrPanel(group, this._group.id, undefined, options.group ? (_c = options.position) !== null && _c !== void 0 ? _c : 'center' : 'center');
5436
+ }
5437
+ maximize() {
5438
+ if (!this._group) {
5439
+ throw new Error(NOT_INITIALIZED_MESSAGE);
5440
+ }
5441
+ if (this.location !== 'grid') {
5442
+ // only grid groups can be maximized
5443
+ return;
5444
+ }
5445
+ this.accessor.maximizeGroup(this._group);
5446
+ }
5447
+ isMaximized() {
5142
5448
  if (!this._group) {
5143
- throw new Error(`DockviewGroupPanelApiImpl not initialized`);
5449
+ throw new Error(NOT_INITIALIZED_MESSAGE);
5450
+ }
5451
+ return this.accessor.isMaximizedGroup(this._group);
5452
+ }
5453
+ exitMaximized() {
5454
+ if (!this._group) {
5455
+ throw new Error(NOT_INITIALIZED_MESSAGE);
5456
+ }
5457
+ if (this.isMaximized()) {
5458
+ this.accessor.exitMaximizedGroup();
5144
5459
  }
5145
- this.accessor.moveGroupOrPanel(options.group, this._group.id, undefined, (_a = options.position) !== null && _a !== void 0 ? _a : 'center');
5146
5460
  }
5147
5461
  initialize(group) {
5148
5462
  this._group = group;
5149
5463
  }
5150
5464
  }
5151
5465
 
5466
+ const MINIMUM_DOCKVIEW_GROUP_PANEL_WIDTH = 100;
5467
+ const MINIMUM_DOCKVIEW_GROUP_PANEL_HEIGHT = 100;
5152
5468
  class DockviewGroupPanel extends GridviewPanel {
5153
5469
  get panels() {
5154
5470
  return this._model.panels;
@@ -5173,8 +5489,8 @@ class DockviewGroupPanel extends GridviewPanel {
5173
5489
  }
5174
5490
  constructor(accessor, id, options) {
5175
5491
  super(id, 'groupview_default', {
5176
- minimumHeight: 100,
5177
- minimumWidth: 100,
5492
+ minimumHeight: MINIMUM_DOCKVIEW_GROUP_PANEL_HEIGHT,
5493
+ minimumWidth: MINIMUM_DOCKVIEW_GROUP_PANEL_WIDTH,
5178
5494
  }, new DockviewGroupPanelApiImpl(id, accessor));
5179
5495
  this.api.initialize(this); // cannot use 'this' after after 'super' call
5180
5496
  this._model = new DockviewGroupPanelModel(this.element, accessor, id, options, this);
@@ -5228,8 +5544,10 @@ class DockviewPanelApiImpl extends GridviewPanelApiImpl {
5228
5544
  return this.panel.title;
5229
5545
  }
5230
5546
  get isGroupActive() {
5231
- var _a;
5232
- return !!((_a = this.group) === null || _a === void 0 ? void 0 : _a.isActive);
5547
+ return this.group.isActive;
5548
+ }
5549
+ get renderer() {
5550
+ return this.panel.renderer;
5233
5551
  }
5234
5552
  set group(value) {
5235
5553
  const isOldGroupActive = this.isGroupActive;
@@ -5257,10 +5575,12 @@ class DockviewPanelApiImpl extends GridviewPanelApiImpl {
5257
5575
  this.onDidActiveGroupChange = this._onDidActiveGroupChange.event;
5258
5576
  this._onDidGroupChange = new Emitter();
5259
5577
  this.onDidGroupChange = this._onDidGroupChange.event;
5578
+ this._onDidRendererChange = new Emitter();
5579
+ this.onDidRendererChange = this._onDidRendererChange.event;
5260
5580
  this.disposable = new MutableDisposable();
5261
5581
  this.initialize(panel);
5262
5582
  this._group = group;
5263
- this.addDisposables(this.disposable, this._onDidTitleChange, this._onDidGroupChange, this._onDidActiveGroupChange);
5583
+ this.addDisposables(this.disposable, this._onDidRendererChange, this._onDidTitleChange, this._onDidGroupChange, this._onDidActiveGroupChange);
5264
5584
  }
5265
5585
  moveTo(options) {
5266
5586
  var _a;
@@ -5269,9 +5589,21 @@ class DockviewPanelApiImpl extends GridviewPanelApiImpl {
5269
5589
  setTitle(title) {
5270
5590
  this.panel.setTitle(title);
5271
5591
  }
5592
+ setRenderer(renderer) {
5593
+ this.panel.setRenderer(renderer);
5594
+ }
5272
5595
  close() {
5273
5596
  this.group.model.closePanel(this.panel);
5274
5597
  }
5598
+ maximize() {
5599
+ this.group.api.maximize();
5600
+ }
5601
+ isMaximized() {
5602
+ return this.group.api.isMaximized();
5603
+ }
5604
+ exitMaximized() {
5605
+ this.group.api.exitMaximized();
5606
+ }
5275
5607
  }
5276
5608
 
5277
5609
  class DockviewPanel extends CompositeDisposable {
@@ -5284,11 +5616,17 @@ class DockviewPanel extends CompositeDisposable {
5284
5616
  get group() {
5285
5617
  return this._group;
5286
5618
  }
5287
- constructor(id, accessor, containerApi, group, view) {
5619
+ get renderer() {
5620
+ var _a;
5621
+ return (_a = this._renderer) !== null && _a !== void 0 ? _a : this.accessor.renderer;
5622
+ }
5623
+ constructor(id, accessor, containerApi, group, view, options) {
5288
5624
  super();
5289
5625
  this.id = id;
5626
+ this.accessor = accessor;
5290
5627
  this.containerApi = containerApi;
5291
5628
  this.view = view;
5629
+ this._renderer = options.renderer;
5292
5630
  this._group = group;
5293
5631
  this.api = new DockviewPanelApiImpl(this, this._group, accessor);
5294
5632
  this.addDisposables(this.api.onActiveChange(() => {
@@ -5297,6 +5635,8 @@ class DockviewPanel extends CompositeDisposable {
5297
5635
  // forward the resize event to the group since if you want to resize a panel
5298
5636
  // you are actually just resizing the panels parent which is the group
5299
5637
  this.group.api.setSize(event);
5638
+ }), this.api.onDidRendererChange((event) => {
5639
+ this.group.model.rerender(this);
5300
5640
  }));
5301
5641
  }
5302
5642
  init(params) {
@@ -5316,6 +5656,7 @@ class DockviewPanel extends CompositeDisposable {
5316
5656
  ? this._params
5317
5657
  : undefined,
5318
5658
  title: this.title,
5659
+ renderer: this._renderer,
5319
5660
  };
5320
5661
  }
5321
5662
  setTitle(title) {
@@ -5331,6 +5672,15 @@ class DockviewPanel extends CompositeDisposable {
5331
5672
  this.api._onDidTitleChange.fire({ title });
5332
5673
  }
5333
5674
  }
5675
+ setRenderer(renderer) {
5676
+ const didChange = renderer !== this.renderer;
5677
+ if (didChange) {
5678
+ this._renderer = renderer;
5679
+ this.api._onDidRendererChange.fire({
5680
+ renderer: renderer,
5681
+ });
5682
+ }
5683
+ }
5334
5684
  update(event) {
5335
5685
  var _a;
5336
5686
  // merge the new parameters with the existing parameters
@@ -5549,8 +5899,8 @@ class DockviewPanelModel {
5549
5899
  }
5550
5900
 
5551
5901
  class DefaultDockviewDeserialzier {
5552
- constructor(layout) {
5553
- this.layout = layout;
5902
+ constructor(accessor) {
5903
+ this.accessor = accessor;
5554
5904
  }
5555
5905
  fromJSON(panelData, group) {
5556
5906
  var _a, _b;
@@ -5564,8 +5914,10 @@ class DefaultDockviewDeserialzier {
5564
5914
  const tabComponent = viewData
5565
5915
  ? (_b = viewData.tab) === null || _b === void 0 ? void 0 : _b.id
5566
5916
  : panelData.tabComponent;
5567
- const view = new DockviewPanelModel(this.layout, panelId, contentComponent, tabComponent);
5568
- const panel = new DockviewPanel(panelId, this.layout, new DockviewApi(this.layout), group, view);
5917
+ const view = new DockviewPanelModel(this.accessor, panelId, contentComponent, tabComponent);
5918
+ const panel = new DockviewPanel(panelId, this.accessor, new DockviewApi(this.accessor), group, view, {
5919
+ renderer: panelData.renderer,
5920
+ });
5569
5921
  panel.init({
5570
5922
  title: title !== null && title !== void 0 ? title : panelId,
5571
5923
  params: params !== null && params !== void 0 ? params : {},
@@ -5945,7 +6297,265 @@ class DockviewFloatingGroupPanel extends CompositeDisposable {
5945
6297
  }
5946
6298
  }
5947
6299
 
6300
+ class PopoutWindow extends CompositeDisposable {
6301
+ constructor(id, className, options) {
6302
+ super();
6303
+ this.id = id;
6304
+ this.className = className;
6305
+ this.options = options;
6306
+ this._onDidClose = new Emitter();
6307
+ this.onDidClose = this._onDidClose.event;
6308
+ this._window = null;
6309
+ this.addDisposables(this._onDidClose, {
6310
+ dispose: () => {
6311
+ this.close();
6312
+ },
6313
+ });
6314
+ }
6315
+ dimensions() {
6316
+ if (!this._window) {
6317
+ return null;
6318
+ }
6319
+ const left = this._window.value.screenX;
6320
+ const top = this._window.value.screenY;
6321
+ const width = this._window.value.innerWidth;
6322
+ const height = this._window.value.innerHeight;
6323
+ return { top, left, width, height };
6324
+ }
6325
+ close() {
6326
+ if (this._window) {
6327
+ this._window.disposable.dispose();
6328
+ this._window.value.close();
6329
+ this._window = null;
6330
+ }
6331
+ }
6332
+ open(content) {
6333
+ if (this._window) {
6334
+ throw new Error('instance of popout window is already open');
6335
+ }
6336
+ const url = `${this.options.url}`;
6337
+ const features = Object.entries({
6338
+ top: this.options.top,
6339
+ left: this.options.left,
6340
+ width: this.options.width,
6341
+ height: this.options.height,
6342
+ })
6343
+ .map(([key, value]) => `${key}=${value}`)
6344
+ .join(',');
6345
+ // https://developer.mozilla.org/en-US/docs/Web/API/Window/open
6346
+ const externalWindow = window.open(url, this.id, features);
6347
+ if (!externalWindow) {
6348
+ return;
6349
+ }
6350
+ const disposable = new CompositeDisposable();
6351
+ this._window = { value: externalWindow, disposable };
6352
+ const cleanUp = () => {
6353
+ this._onDidClose.fire();
6354
+ this._window = null;
6355
+ };
6356
+ // prevent any default content from loading
6357
+ // externalWindow.document.body.replaceWith(document.createElement('div'));
6358
+ disposable.addDisposables(addDisposableWindowListener(window, 'beforeunload', () => {
6359
+ cleanUp();
6360
+ this.close();
6361
+ }));
6362
+ externalWindow.addEventListener('load', () => {
6363
+ const externalDocument = externalWindow.document;
6364
+ externalDocument.title = document.title;
6365
+ const div = document.createElement('div');
6366
+ div.classList.add('dv-popout-window');
6367
+ div.style.position = 'absolute';
6368
+ div.style.width = '100%';
6369
+ div.style.height = '100%';
6370
+ div.style.top = '0px';
6371
+ div.style.left = '0px';
6372
+ div.classList.add(this.className);
6373
+ div.appendChild(content);
6374
+ externalDocument.body.replaceChildren(div);
6375
+ externalDocument.body.classList.add(this.className);
6376
+ addStyles(externalDocument, window.document.styleSheets);
6377
+ externalWindow.addEventListener('beforeunload', () => {
6378
+ // TODO: indicate external window is closing
6379
+ cleanUp();
6380
+ });
6381
+ });
6382
+ }
6383
+ }
6384
+
6385
+ class DockviewPopoutGroupPanel extends CompositeDisposable {
6386
+ constructor(id, group, options) {
6387
+ var _a;
6388
+ super();
6389
+ this.id = id;
6390
+ this.group = group;
6391
+ this.options = options;
6392
+ this.window = new PopoutWindow(id, (_a = options.className) !== null && _a !== void 0 ? _a : '', {
6393
+ url: this.options.popoutUrl,
6394
+ left: this.options.box.left,
6395
+ top: this.options.box.top,
6396
+ width: this.options.box.width,
6397
+ height: this.options.box.height,
6398
+ });
6399
+ group.model.location = 'popout';
6400
+ this.addDisposables(this.window, {
6401
+ dispose: () => {
6402
+ group.model.location = 'grid';
6403
+ },
6404
+ }, this.window.onDidClose(() => {
6405
+ this.dispose();
6406
+ }));
6407
+ this.window.open(group.element);
6408
+ }
6409
+ }
6410
+
5948
6411
  const DEFAULT_FLOATING_GROUP_OVERFLOW_SIZE = 100;
6412
+ const DEFAULT_FLOATING_GROUP_POSITION = { left: 100, top: 100 };
6413
+
6414
+ function createFocusableElement() {
6415
+ const element = document.createElement('div');
6416
+ element.tabIndex = -1;
6417
+ return element;
6418
+ }
6419
+ class OverlayRenderContainer extends CompositeDisposable {
6420
+ constructor(element) {
6421
+ super();
6422
+ this.element = element;
6423
+ this.map = {};
6424
+ this.addDisposables(Disposable.from(() => {
6425
+ for (const value of Object.values(this.map)) {
6426
+ value.disposable.dispose();
6427
+ value.destroy.dispose();
6428
+ }
6429
+ }));
6430
+ }
6431
+ detatch(panel) {
6432
+ if (this.map[panel.api.id]) {
6433
+ const { disposable, destroy } = this.map[panel.api.id];
6434
+ disposable.dispose();
6435
+ destroy.dispose();
6436
+ delete this.map[panel.api.id];
6437
+ return true;
6438
+ }
6439
+ return false;
6440
+ }
6441
+ attach(options) {
6442
+ const { panel, referenceContainer } = options;
6443
+ if (!this.map[panel.api.id]) {
6444
+ const element = createFocusableElement();
6445
+ element.className = 'dv-render-overlay';
6446
+ this.map[panel.api.id] = {
6447
+ panel,
6448
+ disposable: Disposable.NONE,
6449
+ destroy: Disposable.NONE,
6450
+ element,
6451
+ };
6452
+ }
6453
+ const focusContainer = this.map[panel.api.id].element;
6454
+ if (panel.view.content.element.parentElement !== focusContainer) {
6455
+ focusContainer.appendChild(panel.view.content.element);
6456
+ }
6457
+ if (focusContainer.parentElement !== this.element) {
6458
+ this.element.appendChild(focusContainer);
6459
+ }
6460
+ const resize = () => {
6461
+ // TODO propagate position to avoid getDomNodePagePosition calls, possible performance bottleneck?
6462
+ const box = getDomNodePagePosition(referenceContainer.element);
6463
+ const box2 = getDomNodePagePosition(this.element);
6464
+ focusContainer.style.left = `${box.left - box2.left}px`;
6465
+ focusContainer.style.top = `${box.top - box2.top}px`;
6466
+ focusContainer.style.width = `${box.width}px`;
6467
+ focusContainer.style.height = `${box.height}px`;
6468
+ toggleClass(focusContainer, 'dv-render-overlay-float', panel.group.api.location === 'floating');
6469
+ };
6470
+ const visibilityChanged = () => {
6471
+ if (panel.api.isVisible) {
6472
+ resize();
6473
+ }
6474
+ focusContainer.style.display = panel.api.isVisible ? '' : 'none';
6475
+ };
6476
+ const disposable = new CompositeDisposable(
6477
+ /**
6478
+ * since container is positioned absoutely we must explicitly forward
6479
+ * the dnd events for the expect behaviours to continue to occur in terms of dnd
6480
+ *
6481
+ * the dnd observer does not need to be conditional on whether the panel is visible since
6482
+ * non-visible panels are 'display: none' and in such case the dnd observer will not fire.
6483
+ */
6484
+ new DragAndDropObserver(focusContainer, {
6485
+ onDragEnd: (e) => {
6486
+ referenceContainer.dropTarget.dnd.onDragEnd(e);
6487
+ },
6488
+ onDragEnter: (e) => {
6489
+ referenceContainer.dropTarget.dnd.onDragEnter(e);
6490
+ },
6491
+ onDragLeave: (e) => {
6492
+ referenceContainer.dropTarget.dnd.onDragLeave(e);
6493
+ },
6494
+ onDrop: (e) => {
6495
+ referenceContainer.dropTarget.dnd.onDrop(e);
6496
+ },
6497
+ onDragOver: (e) => {
6498
+ referenceContainer.dropTarget.dnd.onDragOver(e);
6499
+ },
6500
+ }), panel.api.onDidVisibilityChange((event) => {
6501
+ /**
6502
+ * Control the visibility of the content, however even when not visible (display: none)
6503
+ * the content is still maintained within the DOM hence DOM specific attributes
6504
+ * such as scroll position are maintained when next made visible.
6505
+ */
6506
+ visibilityChanged();
6507
+ }), panel.api.onDidDimensionsChange(() => {
6508
+ if (!panel.api.isVisible) {
6509
+ return;
6510
+ }
6511
+ resize();
6512
+ }));
6513
+ this.map[panel.api.id].destroy = Disposable.from(() => {
6514
+ focusContainer.removeChild(panel.view.content.element);
6515
+ this.element.removeChild(focusContainer);
6516
+ });
6517
+ queueMicrotask(() => {
6518
+ if (this.isDisposed) {
6519
+ return;
6520
+ }
6521
+ /**
6522
+ * wait until everything has finished in the current stack-frame call before
6523
+ * calling the first resize as other size-altering events may still occur before
6524
+ * the end of the stack-frame.
6525
+ */
6526
+ visibilityChanged();
6527
+ });
6528
+ // dispose of logic asoccciated with previous reference-container
6529
+ this.map[panel.api.id].disposable.dispose();
6530
+ // and reset the disposable to the active reference-container
6531
+ this.map[panel.api.id].disposable = disposable;
6532
+ return focusContainer;
6533
+ }
6534
+ }
6535
+
6536
+ const DEFAULT_ROOT_OVERLAY_MODEL = {
6537
+ activationSize: { type: 'pixels', value: 10 },
6538
+ size: { type: 'pixels', value: 20 },
6539
+ };
6540
+ function getTheme(element) {
6541
+ function toClassList(element) {
6542
+ const list = [];
6543
+ for (let i = 0; i < element.classList.length; i++) {
6544
+ list.push(element.classList.item(i));
6545
+ }
6546
+ return list;
6547
+ }
6548
+ let theme = undefined;
6549
+ let parent = element;
6550
+ while (parent !== null) {
6551
+ theme = toClassList(parent).find((cls) => cls.startsWith('dockview-theme-'));
6552
+ if (typeof theme === 'string') {
6553
+ break;
6554
+ }
6555
+ parent = parent.parentElement;
6556
+ }
6557
+ return theme;
6558
+ }
5949
6559
  class DockviewComponent extends BaseGrid {
5950
6560
  get orientation() {
5951
6561
  return this.gridview.orientation;
@@ -5966,8 +6576,12 @@ class DockviewComponent extends BaseGrid {
5966
6576
  }
5967
6577
  return activeGroup.activePanel;
5968
6578
  }
5969
- constructor(options) {
6579
+ get renderer() {
5970
6580
  var _a;
6581
+ return (_a = this.options.defaultRenderer) !== null && _a !== void 0 ? _a : 'onlyWhenVisibile';
6582
+ }
6583
+ constructor(options) {
6584
+ var _a, _b;
5971
6585
  super({
5972
6586
  proportionalLayout: true,
5973
6587
  orientation: (_a = options.orientation) !== null && _a !== void 0 ? _a : Orientation.HORIZONTAL,
@@ -5992,12 +6606,27 @@ class DockviewComponent extends BaseGrid {
5992
6606
  this.onDidLayoutFromJSON = this._onDidLayoutFromJSON.event;
5993
6607
  this._onDidActivePanelChange = new Emitter();
5994
6608
  this.onDidActivePanelChange = this._onDidActivePanelChange.event;
5995
- this.floatingGroups = [];
6609
+ this._floatingGroups = [];
6610
+ this._popoutGroups = [];
6611
+ const gready = document.createElement('div');
6612
+ gready.className = 'dv-overlay-render-container';
6613
+ this.gridview.element.appendChild(gready);
6614
+ this.overlayRenderContainer = new OverlayRenderContainer(gready);
5996
6615
  toggleClass(this.gridview.element, 'dv-dockview', true);
5997
- this.addDisposables(this._onWillDragPanel, this._onWillDragGroup, this._onDidActivePanelChange, this._onDidAddPanel, this._onDidRemovePanel, this._onDidLayoutFromJSON, this._onDidDrop, Event.any(this.onDidAddGroup, this.onDidRemoveGroup)(() => {
6616
+ toggleClass(this.element, 'dv-debug', !!options.debug);
6617
+ this.addDisposables(this.overlayRenderContainer, this._onWillDragPanel, this._onWillDragGroup, this._onDidActivePanelChange, this._onDidAddPanel, this._onDidRemovePanel, this._onDidLayoutFromJSON, this._onDidDrop, Event.any(this.onDidAddGroup, this.onDidRemoveGroup)(() => {
5998
6618
  this.updateWatermark();
5999
6619
  }), Event.any(this.onDidAddPanel, this.onDidRemovePanel, this.onDidActivePanelChange)(() => {
6000
6620
  this._bufferOnDidLayoutChange.fire();
6621
+ }), Disposable.from(() => {
6622
+ // iterate over a copy of the array since .dispose() mutates the original array
6623
+ for (const group of [...this._floatingGroups]) {
6624
+ group.dispose();
6625
+ }
6626
+ // iterate over a copy of the array since .dispose() mutates the original array
6627
+ for (const group of [...this._popoutGroups]) {
6628
+ group.dispose();
6629
+ }
6001
6630
  }));
6002
6631
  this._options = options;
6003
6632
  if (!this.options.components) {
@@ -6016,7 +6645,7 @@ class DockviewComponent extends BaseGrid {
6016
6645
  !this.options.watermarkFrameworkComponent) {
6017
6646
  this.options.watermarkComponent = Watermark;
6018
6647
  }
6019
- const dropTarget = new Droptarget(this.element, {
6648
+ this._rootDropTarget = new Droptarget(this.element, {
6020
6649
  canDisplayOverlay: (event, position) => {
6021
6650
  const data = getPanelData();
6022
6651
  if (data) {
@@ -6049,12 +6678,9 @@ class DockviewComponent extends BaseGrid {
6049
6678
  return false;
6050
6679
  },
6051
6680
  acceptedTargetZones: ['top', 'bottom', 'left', 'right', 'center'],
6052
- overlayModel: {
6053
- activationSize: { type: 'pixels', value: 10 },
6054
- size: { type: 'pixels', value: 20 },
6055
- },
6681
+ overlayModel: (_b = this.options.rootOverlayModel) !== null && _b !== void 0 ? _b : DEFAULT_ROOT_OVERLAY_MODEL,
6056
6682
  });
6057
- this.addDisposables(dropTarget.onDrop((event) => {
6683
+ this.addDisposables(this._rootDropTarget.onDrop((event) => {
6058
6684
  var _a;
6059
6685
  const data = getPanelData();
6060
6686
  if (data) {
@@ -6063,10 +6689,59 @@ class DockviewComponent extends BaseGrid {
6063
6689
  else {
6064
6690
  this._onDidDrop.fire(Object.assign(Object.assign({}, event), { api: this._api, group: null, getData: getPanelData }));
6065
6691
  }
6066
- }), dropTarget);
6692
+ }), this._rootDropTarget);
6067
6693
  this._api = new DockviewApi(this);
6068
6694
  this.updateWatermark();
6069
6695
  }
6696
+ addPopoutGroup(item, options) {
6697
+ var _a;
6698
+ let group;
6699
+ let box = options === null || options === void 0 ? void 0 : options.position;
6700
+ if (item instanceof DockviewPanel) {
6701
+ group = this.createGroup();
6702
+ this.removePanel(item, {
6703
+ removeEmptyGroup: true,
6704
+ skipDispose: true,
6705
+ });
6706
+ group.model.openPanel(item);
6707
+ if (!box) {
6708
+ box = this.element.getBoundingClientRect();
6709
+ }
6710
+ }
6711
+ else {
6712
+ group = item;
6713
+ if (!box) {
6714
+ box = group.element.getBoundingClientRect();
6715
+ }
6716
+ const skip = typeof (options === null || options === void 0 ? void 0 : options.skipRemoveGroup) === 'boolean' &&
6717
+ options.skipRemoveGroup;
6718
+ if (!skip) {
6719
+ this.doRemoveGroup(item, { skipDispose: true });
6720
+ }
6721
+ }
6722
+ const theme = getTheme(this.gridview.element);
6723
+ const popoutWindow = new DockviewPopoutGroupPanel(`${this.id}-${group.id}`, // globally unique within dockview
6724
+ group, {
6725
+ className: theme !== null && theme !== void 0 ? theme : '',
6726
+ popoutUrl: (_a = options === null || options === void 0 ? void 0 : options.popoutUrl) !== null && _a !== void 0 ? _a : '/popout.html',
6727
+ box: {
6728
+ left: window.screenX + box.left,
6729
+ top: window.screenY + box.top,
6730
+ width: box.width,
6731
+ height: box.height,
6732
+ },
6733
+ });
6734
+ popoutWindow.addDisposables({
6735
+ dispose: () => {
6736
+ remove(this._popoutGroups, popoutWindow);
6737
+ this.updateWatermark();
6738
+ },
6739
+ }, popoutWindow.window.onDidClose(() => {
6740
+ this.doAddGroup(group, [0]);
6741
+ }));
6742
+ this._popoutGroups.push(popoutWindow);
6743
+ this.updateWatermark();
6744
+ }
6070
6745
  addFloatingGroup(item, coord, options) {
6071
6746
  var _a, _b, _c, _d, _e, _f;
6072
6747
  let group;
@@ -6086,9 +6761,13 @@ class DockviewComponent extends BaseGrid {
6086
6761
  this.doRemoveGroup(item, { skipDispose: true });
6087
6762
  }
6088
6763
  }
6089
- group.model.isFloating = true;
6090
- const overlayLeft = typeof (coord === null || coord === void 0 ? void 0 : coord.x) === 'number' ? Math.max(coord.x, 0) : 100;
6091
- const overlayTop = typeof (coord === null || coord === void 0 ? void 0 : coord.y) === 'number' ? Math.max(coord.y, 0) : 100;
6764
+ group.model.location = 'floating';
6765
+ const overlayLeft = typeof (coord === null || coord === void 0 ? void 0 : coord.x) === 'number'
6766
+ ? Math.max(coord.x, 0)
6767
+ : DEFAULT_FLOATING_GROUP_POSITION.left;
6768
+ const overlayTop = typeof (coord === null || coord === void 0 ? void 0 : coord.y) === 'number'
6769
+ ? Math.max(coord.y, 0)
6770
+ : DEFAULT_FLOATING_GROUP_POSITION.top;
6092
6771
  const overlay = new Overlay({
6093
6772
  container: this.gridview.element,
6094
6773
  content: group.element,
@@ -6132,12 +6811,12 @@ class DockviewComponent extends BaseGrid {
6132
6811
  }), {
6133
6812
  dispose: () => {
6134
6813
  disposable.dispose();
6135
- group.model.isFloating = false;
6136
- remove(this.floatingGroups, floatingGroupPanel);
6814
+ group.model.location = 'grid';
6815
+ remove(this._floatingGroups, floatingGroupPanel);
6137
6816
  this.updateWatermark();
6138
6817
  },
6139
6818
  });
6140
- this.floatingGroups.push(floatingGroupPanel);
6819
+ this._floatingGroups.push(floatingGroupPanel);
6141
6820
  this.updateWatermark();
6142
6821
  }
6143
6822
  orthogonalize(position) {
@@ -6173,16 +6852,18 @@ class DockviewComponent extends BaseGrid {
6173
6852
  }
6174
6853
  updateOptions(options) {
6175
6854
  var _a, _b;
6176
- const hasOrientationChanged = typeof options.orientation === 'string' &&
6855
+ const changed_orientation = typeof options.orientation === 'string' &&
6177
6856
  this.gridview.orientation !== options.orientation;
6178
- const hasFloatingGroupOptionsChanged = options.floatingGroupBounds !== undefined &&
6857
+ const changed_floatingGroupBounds = options.floatingGroupBounds !== undefined &&
6179
6858
  options.floatingGroupBounds !== this.options.floatingGroupBounds;
6859
+ const changed_rootOverlayOptions = options.rootOverlayModel !== undefined &&
6860
+ options.rootOverlayModel !== this.options.rootOverlayModel;
6180
6861
  this._options = Object.assign(Object.assign({}, this.options), options);
6181
- if (hasOrientationChanged) {
6862
+ if (changed_orientation) {
6182
6863
  this.gridview.orientation = options.orientation;
6183
6864
  }
6184
- if (hasFloatingGroupOptionsChanged) {
6185
- for (const group of this.floatingGroups) {
6865
+ if (changed_floatingGroupBounds) {
6866
+ for (const group of this._floatingGroups) {
6186
6867
  switch (this.options.floatingGroupBounds) {
6187
6868
  case 'boundedWithinViewport':
6188
6869
  group.overlay.minimumInViewportHeight = undefined;
@@ -6203,12 +6884,15 @@ class DockviewComponent extends BaseGrid {
6203
6884
  group.overlay.setBounds({});
6204
6885
  }
6205
6886
  }
6887
+ if (changed_rootOverlayOptions) {
6888
+ this._rootDropTarget.setOverlayModel(options.rootOverlayModel);
6889
+ }
6206
6890
  this.layout(this.gridview.width, this.gridview.height, true);
6207
6891
  }
6208
6892
  layout(width, height, forceResize) {
6209
6893
  super.layout(width, height, forceResize);
6210
- if (this.floatingGroups) {
6211
- for (const floating of this.floatingGroups) {
6894
+ if (this._floatingGroups) {
6895
+ for (const floating of this._floatingGroups) {
6212
6896
  // ensure floting groups stay within visible boundaries
6213
6897
  floating.overlay.setBounds();
6214
6898
  }
@@ -6276,10 +6960,16 @@ class DockviewComponent extends BaseGrid {
6276
6960
  collection[panel.id] = panel.toJSON();
6277
6961
  return collection;
6278
6962
  }, {});
6279
- const floats = this.floatingGroups.map((floatingGroup) => {
6963
+ const floats = this._floatingGroups.map((group) => {
6280
6964
  return {
6281
- data: floatingGroup.group.toJSON(),
6282
- position: floatingGroup.overlay.toJSON(),
6965
+ data: group.group.toJSON(),
6966
+ position: group.overlay.toJSON(),
6967
+ };
6968
+ });
6969
+ const popoutGroups = this._popoutGroups.map((group) => {
6970
+ return {
6971
+ data: group.group.toJSON(),
6972
+ position: group.window.dimensions(),
6283
6973
  };
6284
6974
  });
6285
6975
  const result = {
@@ -6290,10 +6980,13 @@ class DockviewComponent extends BaseGrid {
6290
6980
  if (floats.length > 0) {
6291
6981
  result.floatingGroups = floats;
6292
6982
  }
6983
+ if (popoutGroups.length > 0) {
6984
+ result.popoutGroups = popoutGroups;
6985
+ }
6293
6986
  return result;
6294
6987
  }
6295
6988
  fromJSON(data) {
6296
- var _a;
6989
+ var _a, _b;
6297
6990
  this.clear();
6298
6991
  if (typeof data !== 'object' || data === null) {
6299
6992
  throw new Error('serialized layout must be a non-null object');
@@ -6360,7 +7053,16 @@ class DockviewComponent extends BaseGrid {
6360
7053
  width: position.width,
6361
7054
  }, { skipRemoveGroup: true, inDragMode: false });
6362
7055
  }
6363
- for (const floatingGroup of this.floatingGroups) {
7056
+ const serializedPopoutGroups = (_b = data.popoutGroups) !== null && _b !== void 0 ? _b : [];
7057
+ for (const serializedPopoutGroup of serializedPopoutGroups) {
7058
+ const { data, position } = serializedPopoutGroup;
7059
+ const group = createGroupFromSerializedState(data);
7060
+ this.addPopoutGroup(group, {
7061
+ skipRemoveGroup: true,
7062
+ position: position !== null && position !== void 0 ? position : undefined,
7063
+ });
7064
+ }
7065
+ for (const floatingGroup of this._floatingGroups) {
6364
7066
  floatingGroup.overlay.setBounds();
6365
7067
  }
6366
7068
  if (typeof activeGroup === 'string') {
@@ -6392,7 +7094,7 @@ class DockviewComponent extends BaseGrid {
6392
7094
  this._onDidRemoveGroup.fire(group);
6393
7095
  }
6394
7096
  // iterate over a reassigned array since original array will be modified
6395
- for (const floatingGroup of [...this.floatingGroups]) {
7097
+ for (const floatingGroup of [...this._floatingGroups]) {
6396
7098
  floatingGroup.dispose();
6397
7099
  }
6398
7100
  // fires clean-up events and clears the underlying HTML gridview.
@@ -6484,7 +7186,8 @@ class DockviewComponent extends BaseGrid {
6484
7186
  group.model.openPanel(panel);
6485
7187
  this.doSetGroupAndPanelActive(group);
6486
7188
  }
6487
- else if (referenceGroup.api.isFloating || target === 'center') {
7189
+ else if (referenceGroup.api.location === 'floating' ||
7190
+ target === 'center') {
6488
7191
  panel = this.createPanel(options, referenceGroup);
6489
7192
  referenceGroup.model.openPanel(panel);
6490
7193
  }
@@ -6528,6 +7231,7 @@ class DockviewComponent extends BaseGrid {
6528
7231
  }
6529
7232
  group.model.removePanel(panel);
6530
7233
  if (!options.skipDispose) {
7234
+ this.overlayRenderContainer.detatch(panel);
6531
7235
  panel.dispose();
6532
7236
  }
6533
7237
  if (group.size === 0 && options.removeEmptyGroup) {
@@ -6544,7 +7248,7 @@ class DockviewComponent extends BaseGrid {
6544
7248
  }
6545
7249
  updateWatermark() {
6546
7250
  var _a, _b;
6547
- if (this.groups.filter((x) => !x.api.isFloating).length === 0) {
7251
+ if (this.groups.filter((x) => x.api.location === 'grid').length === 0) {
6548
7252
  if (!this.watermark) {
6549
7253
  this.watermark = this.createWatermarkComponent();
6550
7254
  this.watermark.init({
@@ -6619,19 +7323,40 @@ class DockviewComponent extends BaseGrid {
6619
7323
  }
6620
7324
  }
6621
7325
  doRemoveGroup(group, options) {
6622
- const floatingGroup = this.floatingGroups.find((_) => _.group === group);
6623
- if (floatingGroup) {
6624
- if (!(options === null || options === void 0 ? void 0 : options.skipDispose)) {
6625
- floatingGroup.group.dispose();
6626
- this._groups.delete(group.id);
6627
- this._onDidRemoveGroup.fire(group);
7326
+ if (group.api.location === 'floating') {
7327
+ const floatingGroup = this._floatingGroups.find((_) => _.group === group);
7328
+ if (floatingGroup) {
7329
+ if (!(options === null || options === void 0 ? void 0 : options.skipDispose)) {
7330
+ floatingGroup.group.dispose();
7331
+ this._groups.delete(group.id);
7332
+ this._onDidRemoveGroup.fire(group);
7333
+ }
7334
+ remove(this._floatingGroups, floatingGroup);
7335
+ floatingGroup.dispose();
7336
+ if (!(options === null || options === void 0 ? void 0 : options.skipActive) && this._activeGroup === group) {
7337
+ const groups = Array.from(this._groups.values());
7338
+ this.doSetGroupActive(groups.length > 0 ? groups[0].value : undefined);
7339
+ }
7340
+ return floatingGroup.group;
6628
7341
  }
6629
- floatingGroup.dispose();
6630
- if (!(options === null || options === void 0 ? void 0 : options.skipActive) && this._activeGroup === group) {
6631
- const groups = Array.from(this._groups.values());
6632
- this.doSetGroupActive(groups.length > 0 ? groups[0].value : undefined);
7342
+ throw new Error('failed to find floating group');
7343
+ }
7344
+ if (group.api.location === 'popout') {
7345
+ const selectedGroup = this._popoutGroups.find((_) => _.group === group);
7346
+ if (selectedGroup) {
7347
+ if (!(options === null || options === void 0 ? void 0 : options.skipDispose)) {
7348
+ selectedGroup.group.dispose();
7349
+ this._groups.delete(group.id);
7350
+ this._onDidRemoveGroup.fire(group);
7351
+ }
7352
+ selectedGroup.dispose();
7353
+ if (!(options === null || options === void 0 ? void 0 : options.skipActive) && this._activeGroup === group) {
7354
+ const groups = Array.from(this._groups.values());
7355
+ this.doSetGroupActive(groups.length > 0 ? groups[0].value : undefined);
7356
+ }
7357
+ return selectedGroup.group;
6633
7358
  }
6634
- return floatingGroup.group;
7359
+ throw new Error('failed to find popout group');
6635
7360
  }
6636
7361
  return super.doRemoveGroup(group, options);
6637
7362
  }
@@ -6663,8 +7388,7 @@ class DockviewComponent extends BaseGrid {
6663
7388
  const targetLocation = getRelativeLocation(this.gridview.orientation, referenceLocation, destinationTarget);
6664
7389
  if (sourceGroup && sourceGroup.size < 2) {
6665
7390
  const [targetParentLocation, to] = tail(targetLocation);
6666
- const isFloating = this.floatingGroups.find((x) => x.group === sourceGroup);
6667
- if (!isFloating) {
7391
+ if (sourceGroup.api.location === 'grid') {
6668
7392
  const sourceLocation = getGridLocation(sourceGroup.element);
6669
7393
  const [sourceParentLocation, from] = tail(sourceLocation);
6670
7394
  if (sequenceEquals(sourceParentLocation, targetParentLocation)) {
@@ -6710,12 +7434,25 @@ class DockviewComponent extends BaseGrid {
6710
7434
  }
6711
7435
  }
6712
7436
  else {
6713
- const floatingGroup = this.floatingGroups.find((x) => x.group === sourceGroup);
6714
- if (floatingGroup) {
6715
- floatingGroup.dispose();
6716
- }
6717
- else {
6718
- this.gridview.removeView(getGridLocation(sourceGroup.element));
7437
+ switch (sourceGroup.api.location) {
7438
+ case 'grid':
7439
+ this.gridview.removeView(getGridLocation(sourceGroup.element));
7440
+ break;
7441
+ case 'floating': {
7442
+ const selectedFloatingGroup = this._floatingGroups.find((x) => x.group === sourceGroup);
7443
+ if (!selectedFloatingGroup) {
7444
+ throw new Error('failed to find floating group');
7445
+ }
7446
+ selectedFloatingGroup.dispose();
7447
+ break;
7448
+ }
7449
+ case 'popout': {
7450
+ const selectedPopoutGroup = this._popoutGroups.find((x) => x.group === sourceGroup);
7451
+ if (!selectedPopoutGroup) {
7452
+ throw new Error('failed to find popout group');
7453
+ }
7454
+ selectedPopoutGroup.dispose();
7455
+ }
6719
7456
  }
6720
7457
  const referenceLocation = getGridLocation(referenceGroup.element);
6721
7458
  const dropLocation = getRelativeLocation(this.gridview.orientation, referenceLocation, target);
@@ -6777,7 +7514,7 @@ class DockviewComponent extends BaseGrid {
6777
7514
  const contentComponent = options.component;
6778
7515
  const tabComponent = (_a = options.tabComponent) !== null && _a !== void 0 ? _a : this.options.defaultTabComponent;
6779
7516
  const view = new DockviewPanelModel(this, options.id, contentComponent, tabComponent);
6780
- const panel = new DockviewPanel(options.id, this, this._api, group, view);
7517
+ const panel = new DockviewPanel(options.id, this, this._api, group, view, { renderer: options.renderer });
6781
7518
  panel.init({
6782
7519
  title: (_b = options.title) !== null && _b !== void 0 ? _b : options.id,
6783
7520
  params: (_c = options === null || options === void 0 ? void 0 : options.params) !== null && _c !== void 0 ? _c : {},
@@ -8123,6 +8860,9 @@ const DockviewReact = React.forwardRef((props, ref) => {
8123
8860
  singleTabMode: props.singleTabMode,
8124
8861
  disableFloatingGroups: props.disableFloatingGroups,
8125
8862
  floatingGroupBounds: props.floatingGroupBounds,
8863
+ defaultRenderer: props.defaultRenderer,
8864
+ debug: props.debug,
8865
+ rootOverlayModel: props.rootOverlayModel,
8126
8866
  });
8127
8867
  const { clientWidth, clientHeight } = domRef.current;
8128
8868
  dockview.layout(clientWidth, clientHeight);
@@ -8230,6 +8970,14 @@ const DockviewReact = React.forwardRef((props, ref) => {
8230
8970
  createLeftHeaderActionsElement: createGroupControlElement(props.leftHeaderActionsComponent, { addPortal }),
8231
8971
  });
8232
8972
  }, [props.leftHeaderActionsComponent]);
8973
+ React.useEffect(() => {
8974
+ if (!dockviewRef.current) {
8975
+ return;
8976
+ }
8977
+ dockviewRef.current.updateOptions({
8978
+ rootOverlayModel: props.rootOverlayModel,
8979
+ });
8980
+ }, [props.rootOverlayModel]);
8233
8981
  React.useEffect(() => {
8234
8982
  if (!dockviewRef.current) {
8235
8983
  return;