dockview 1.7.6 → 1.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (50) hide show
  1. package/README.md +2 -1
  2. package/dist/cjs/dockview/dockview.d.ts +4 -2
  3. package/dist/cjs/dockview/dockview.d.ts.map +1 -1
  4. package/dist/cjs/dockview/dockview.js +23 -5
  5. package/dist/cjs/dockview/dockview.js.map +1 -1
  6. package/dist/cjs/dockview/{groupControlsRenderer.d.ts → headerActionsRenderer.d.ts} +6 -5
  7. package/dist/cjs/dockview/headerActionsRenderer.d.ts.map +1 -0
  8. package/dist/cjs/dockview/{groupControlsRenderer.js → headerActionsRenderer.js} +17 -16
  9. package/dist/cjs/dockview/{groupControlsRenderer.js.map → headerActionsRenderer.js.map} +1 -1
  10. package/dist/cjs/index.d.ts +1 -1
  11. package/dist/cjs/index.d.ts.map +1 -1
  12. package/dist/cjs/svg.d.ts +3 -3
  13. package/dist/cjs/svg.d.ts.map +1 -1
  14. package/dist/dockview.amd.js +795 -138
  15. package/dist/dockview.amd.js.map +1 -0
  16. package/dist/dockview.amd.min.js +3 -2
  17. package/dist/dockview.amd.min.js.map +1 -0
  18. package/dist/dockview.amd.min.noStyle.js +3 -2
  19. package/dist/dockview.amd.min.noStyle.js.map +1 -0
  20. package/dist/dockview.amd.noStyle.js +795 -138
  21. package/dist/dockview.amd.noStyle.js.map +1 -0
  22. package/dist/dockview.cjs.js +795 -138
  23. package/dist/dockview.cjs.js.map +1 -0
  24. package/dist/dockview.esm.js +796 -138
  25. package/dist/dockview.esm.js.map +1 -0
  26. package/dist/dockview.esm.min.js +3 -2
  27. package/dist/dockview.esm.min.js.map +1 -0
  28. package/dist/dockview.js +795 -138
  29. package/dist/dockview.js.map +1 -0
  30. package/dist/dockview.min.js +3 -2
  31. package/dist/dockview.min.js.map +1 -0
  32. package/dist/dockview.min.noStyle.js +3 -2
  33. package/dist/dockview.min.noStyle.js.map +1 -0
  34. package/dist/dockview.noStyle.js +795 -138
  35. package/dist/dockview.noStyle.js.map +1 -0
  36. package/dist/esm/dockview/dockview.d.ts +4 -2
  37. package/dist/esm/dockview/dockview.d.ts.map +1 -1
  38. package/dist/esm/dockview/dockview.js +23 -5
  39. package/dist/esm/dockview/dockview.js.map +1 -1
  40. package/dist/esm/dockview/{groupControlsRenderer.d.ts → headerActionsRenderer.d.ts} +6 -5
  41. package/dist/esm/dockview/headerActionsRenderer.d.ts.map +1 -0
  42. package/dist/esm/dockview/{groupControlsRenderer.js → headerActionsRenderer.js} +3 -2
  43. package/dist/esm/dockview/{groupControlsRenderer.js.map → headerActionsRenderer.js.map} +1 -1
  44. package/dist/esm/index.d.ts +1 -1
  45. package/dist/esm/index.d.ts.map +1 -1
  46. package/dist/esm/svg.d.ts +3 -3
  47. package/dist/esm/svg.d.ts.map +1 -1
  48. package/package.json +6 -6
  49. package/dist/cjs/dockview/groupControlsRenderer.d.ts.map +0 -1
  50. package/dist/esm/dockview/groupControlsRenderer.d.ts.map +0 -1
package/dist/dockview.js CHANGED
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * dockview
3
- * @version 1.7.6
3
+ * @version 1.8.0
4
4
  * @link https://github.com/mathuo/dockview
5
5
  * @license MIT
6
6
  */
@@ -327,6 +327,31 @@
327
327
  }
328
328
  }
329
329
 
330
+ function createComponent(id, componentName, components = {}, frameworkComponents = {}, createFrameworkComponent, fallback) {
331
+ const Component = typeof componentName === 'string'
332
+ ? components[componentName]
333
+ : undefined;
334
+ const FrameworkComponent = typeof componentName === 'string'
335
+ ? frameworkComponents[componentName]
336
+ : undefined;
337
+ if (Component && FrameworkComponent) {
338
+ throw new Error(`Cannot create '${id}'. component '${componentName}' registered as both a component and frameworkComponent`);
339
+ }
340
+ if (FrameworkComponent) {
341
+ if (!createFrameworkComponent) {
342
+ throw new Error(`Cannot create '${id}' for framework component '${componentName}'. you must register a frameworkPanelWrapper to use framework components`);
343
+ }
344
+ return createFrameworkComponent.createComponent(id, componentName, FrameworkComponent);
345
+ }
346
+ if (!Component) {
347
+ if (fallback) {
348
+ return fallback();
349
+ }
350
+ throw new Error(`Cannot create '${id}', no component '${componentName}' provided`);
351
+ }
352
+ return new Component(id, componentName);
353
+ }
354
+
330
355
  function watchElementResize(element, cb) {
331
356
  const observer = new ResizeObserver((entires) => {
332
357
  /**
@@ -440,31 +465,16 @@
440
465
  refreshState() {
441
466
  this._refreshStateHandler();
442
467
  }
443
- }
444
-
445
- function createComponent(id, componentName, components = {}, frameworkComponents = {}, createFrameworkComponent, fallback) {
446
- const Component = typeof componentName === 'string'
447
- ? components[componentName]
448
- : undefined;
449
- const FrameworkComponent = typeof componentName === 'string'
450
- ? frameworkComponents[componentName]
451
- : undefined;
452
- if (Component && FrameworkComponent) {
453
- throw new Error(`Cannot create '${id}'. component '${componentName}' registered as both a component and frameworkComponent`);
454
- }
455
- if (FrameworkComponent) {
456
- if (!createFrameworkComponent) {
457
- throw new Error(`Cannot create '${id}' for framework component '${componentName}'. you must register a frameworkPanelWrapper to use framework components`);
458
- }
459
- return createFrameworkComponent.createComponent(id, componentName, FrameworkComponent);
460
- }
461
- if (!Component) {
462
- if (fallback) {
463
- return fallback();
464
- }
465
- throw new Error(`Cannot create '${id}', no component '${componentName}' provided`);
466
- }
467
- return new Component(id, componentName);
468
+ }
469
+ // quasi: apparently, but not really; seemingly
470
+ const QUASI_PREVENT_DEFAULT_KEY = 'dv-quasiPreventDefault';
471
+ // mark an event directly for other listeners to check
472
+ function quasiPreventDefault(event) {
473
+ event[QUASI_PREVENT_DEFAULT_KEY] = true;
474
+ }
475
+ // check if this event has been marked
476
+ function quasiDefaultPrevented(event) {
477
+ return event[QUASI_PREVENT_DEFAULT_KEY];
468
478
  }
469
479
 
470
480
  function tail(arr) {
@@ -515,6 +525,14 @@
515
525
  }
516
526
  }
517
527
  return -1;
528
+ }
529
+ function remove(array, value) {
530
+ const index = array.findIndex((t) => t === value);
531
+ if (index > -1) {
532
+ array.splice(index, 1);
533
+ return true;
534
+ }
535
+ return false;
518
536
  }
519
537
 
520
538
  const clamp = (value, min, max) => {
@@ -1657,7 +1675,7 @@
1657
1675
  : true,
1658
1676
  };
1659
1677
  }),
1660
- size: this.size,
1678
+ size: this.orthogonalSize,
1661
1679
  };
1662
1680
  this.children = childDescriptors.map((c) => c.node);
1663
1681
  this.splitview = new Splitview(this.element, {
@@ -1720,7 +1738,7 @@
1720
1738
  layout(size, orthogonalSize) {
1721
1739
  this._size = orthogonalSize;
1722
1740
  this._orthogonalSize = size;
1723
- this.splitview.layout(this.size, this.orthogonalSize);
1741
+ this.splitview.layout(orthogonalSize, size);
1724
1742
  }
1725
1743
  addChild(node, size, index, skipLayout) {
1726
1744
  if (index < 0 || index > this.children.length) {
@@ -1945,9 +1963,9 @@
1945
1963
  this._deserialize(json.root, orientation, deserializer, height);
1946
1964
  }
1947
1965
  _deserialize(root, orientation, deserializer, orthogonalSize) {
1948
- this.root = this._deserializeNode(root, orientation, deserializer, orthogonalSize, true);
1966
+ this.root = this._deserializeNode(root, orientation, deserializer, orthogonalSize);
1949
1967
  }
1950
- _deserializeNode(node, orientation, deserializer, orthogonalSize, isRoot = false) {
1968
+ _deserializeNode(node, orientation, deserializer, orthogonalSize) {
1951
1969
  let result;
1952
1970
  if (node.type === 'branch') {
1953
1971
  const serializedChildren = node.data;
@@ -1957,9 +1975,9 @@
1957
1975
  visible: serializedChild.visible,
1958
1976
  };
1959
1977
  });
1960
- // HORIZONTAL => height=orthogonalsize width=size
1961
- // VERTICAL => height=size width=orthogonalsize
1962
- result = new BranchNode(orientation, this.proportionalLayout, this.styles, isRoot ? orthogonalSize : node.size, isRoot ? node.size : orthogonalSize, children);
1978
+ result = new BranchNode(orientation, this.proportionalLayout, this.styles, node.size, // <- orthogonal size - flips at each depth
1979
+ orthogonalSize, // <- size - flips at each depth
1980
+ children);
1963
1981
  }
1964
1982
  else {
1965
1983
  result = new LeafNode(deserializer.fromJSON(node), orientation, orthogonalSize, node.size);
@@ -1992,7 +2010,8 @@
1992
2010
  const oldRoot = this.root;
1993
2011
  oldRoot.element.remove();
1994
2012
  this._root = new BranchNode(orthogonal(oldRoot.orientation), this.proportionalLayout, this.styles, this.root.orthogonalSize, this.root.size);
1995
- if (oldRoot.children.length === 1) {
2013
+ if (oldRoot.children.length === 0) ;
2014
+ else if (oldRoot.children.length === 1) {
1996
2015
  // can remove one level of redundant branching if there is only a single child
1997
2016
  const childReference = oldRoot.children[0];
1998
2017
  const child = oldRoot.removeChild(0); // remove to prevent disposal when disposing of unwanted root
@@ -2508,6 +2527,9 @@
2508
2527
  addPanel(options) {
2509
2528
  return this.component.addPanel(options);
2510
2529
  }
2530
+ removePanel(panel) {
2531
+ this.component.removePanel(panel);
2532
+ }
2511
2533
  addGroup(options) {
2512
2534
  return this.component.addGroup(options);
2513
2535
  }
@@ -2526,6 +2548,9 @@
2526
2548
  getGroup(id) {
2527
2549
  return this.component.getPanel(id);
2528
2550
  }
2551
+ addFloatingGroup(item, coord) {
2552
+ return this.component.addFloatingGroup(item, coord);
2553
+ }
2529
2554
  fromJSON(data) {
2530
2555
  this.component.fromJSON(data);
2531
2556
  }
@@ -2618,10 +2643,14 @@
2618
2643
  this._onDrop = new Emitter();
2619
2644
  this.onDrop = this._onDrop.event;
2620
2645
  // use a set to take advantage of #<set>.has
2621
- const acceptedTargetZonesSet = new Set(this.options.acceptedTargetZones);
2646
+ this._acceptedTargetZonesSet = new Set(this.options.acceptedTargetZones);
2622
2647
  this.addDisposables(this._onDrop, new DragAndDropObserver(this.element, {
2623
2648
  onDragEnter: () => undefined,
2624
2649
  onDragOver: (e) => {
2650
+ if (this._acceptedTargetZonesSet.size === 0) {
2651
+ this.removeDropTarget();
2652
+ return;
2653
+ }
2625
2654
  const width = this.element.clientWidth;
2626
2655
  const height = this.element.clientHeight;
2627
2656
  if (width === 0 || height === 0) {
@@ -2630,20 +2659,28 @@
2630
2659
  const rect = e.currentTarget.getBoundingClientRect();
2631
2660
  const x = e.clientX - rect.left;
2632
2661
  const y = e.clientY - rect.top;
2633
- const quadrant = this.calculateQuadrant(acceptedTargetZonesSet, x, y, width, height);
2634
- if (quadrant === null) {
2662
+ const quadrant = this.calculateQuadrant(this._acceptedTargetZonesSet, x, y, width, height);
2663
+ /**
2664
+ * If the event has already been used by another DropTarget instance
2665
+ * then don't show a second drop target, only one target should be
2666
+ * active at any one time
2667
+ */
2668
+ if (this.isAlreadyUsed(e) || quadrant === null) {
2635
2669
  // no drop target should be displayed
2636
2670
  this.removeDropTarget();
2637
2671
  return;
2638
2672
  }
2639
2673
  if (typeof this.options.canDisplayOverlay === 'boolean') {
2640
2674
  if (!this.options.canDisplayOverlay) {
2675
+ this.removeDropTarget();
2641
2676
  return;
2642
2677
  }
2643
2678
  }
2644
2679
  else if (!this.options.canDisplayOverlay(e, quadrant)) {
2680
+ this.removeDropTarget();
2645
2681
  return;
2646
2682
  }
2683
+ this.markAsUsed(e);
2647
2684
  if (!this.targetElement) {
2648
2685
  this.targetElement = document.createElement('div');
2649
2686
  this.targetElement.className = 'drop-target-dropzone';
@@ -2654,12 +2691,6 @@
2654
2691
  this.element.classList.add('drop-target');
2655
2692
  this.element.append(this.targetElement);
2656
2693
  }
2657
- if (this.options.acceptedTargetZones.length === 0) {
2658
- return;
2659
- }
2660
- if (!this.targetElement || !this.overlayElement) {
2661
- return;
2662
- }
2663
2694
  this.toggleClasses(quadrant, width, height);
2664
2695
  this.setState(quadrant);
2665
2696
  },
@@ -2682,10 +2713,26 @@
2682
2713
  },
2683
2714
  }));
2684
2715
  }
2716
+ setTargetZones(acceptedTargetZones) {
2717
+ this._acceptedTargetZonesSet = new Set(acceptedTargetZones);
2718
+ }
2685
2719
  dispose() {
2686
2720
  this.removeDropTarget();
2687
2721
  super.dispose();
2688
2722
  }
2723
+ /**
2724
+ * Add a property to the event object for other potential listeners to check
2725
+ */
2726
+ markAsUsed(event) {
2727
+ event[Droptarget.USED_EVENT_ID] = true;
2728
+ }
2729
+ /**
2730
+ * Check is the event has already been used by another instance od DropTarget
2731
+ */
2732
+ isAlreadyUsed(event) {
2733
+ const value = event[Droptarget.USED_EVENT_ID];
2734
+ return typeof value === 'boolean' && value;
2735
+ }
2689
2736
  toggleClasses(quadrant, width, height) {
2690
2737
  var _a, _b, _c, _d;
2691
2738
  if (!this.overlayElement) {
@@ -2780,6 +2827,7 @@
2780
2827
  }
2781
2828
  }
2782
2829
  }
2830
+ Droptarget.USED_EVENT_ID = '__dockview_droptarget_event_is_used__';
2783
2831
  function calculateQuadrantAsPercentage(overlayType, x, y, width, height, threshold) {
2784
2832
  const xp = (100 * x) / width;
2785
2833
  const yp = (100 * y) / height;
@@ -2909,8 +2957,15 @@
2909
2957
  this.addDisposables(this._onDragStart, this.dataDisposable, this.pointerEventsDisposable);
2910
2958
  this.configure();
2911
2959
  }
2960
+ isCancelled(_event) {
2961
+ return false;
2962
+ }
2912
2963
  configure() {
2913
2964
  this.addDisposables(this._onDragStart, addDisposableListener(this.el, 'dragstart', (event) => {
2965
+ if (this.isCancelled(event)) {
2966
+ event.preventDefault();
2967
+ return;
2968
+ }
2914
2969
  const iframes = [
2915
2970
  ...getElementsByTagName('iframe'),
2916
2971
  ...getElementsByTagName('webview'),
@@ -2984,13 +3039,6 @@
2984
3039
  if (event.defaultPrevented) {
2985
3040
  return;
2986
3041
  }
2987
- /**
2988
- * TODO: alternative to stopPropagation
2989
- *
2990
- * I need to stop the event propagation here since otherwise it'll be intercepted by event handlers
2991
- * on the tabs-container. I cannot use event.preventDefault() since I need the on DragStart event to occur
2992
- */
2993
- event.stopPropagation();
2994
3042
  this._onChanged.fire(event);
2995
3043
  }));
2996
3044
  this.droptarget = new Droptarget(this._element, {
@@ -3048,6 +3096,22 @@
3048
3096
  this.accessorId = accessorId;
3049
3097
  this.group = group;
3050
3098
  this.panelTransfer = LocalSelectionTransfer.getInstance();
3099
+ this.addDisposables(addDisposableListener(element, 'mousedown', (e) => {
3100
+ if (e.shiftKey) {
3101
+ /**
3102
+ * You cannot call e.preventDefault() because that will prevent drag events from firing
3103
+ * but we also need to stop any group overlay drag events from occuring
3104
+ * Use a custom event marker that can be checked by the overlay drag events
3105
+ */
3106
+ quasiPreventDefault(e);
3107
+ }
3108
+ }, true));
3109
+ }
3110
+ isCancelled(_event) {
3111
+ if (this.group.api.isFloating && !_event.shiftKey) {
3112
+ return true;
3113
+ }
3114
+ return false;
3051
3115
  }
3052
3116
  getData(dataTransfer) {
3053
3117
  this.panelTransfer.setData([new PanelTransfer(this.accessorId, this.group.id, null)], PanelTransfer.prototype);
@@ -3138,17 +3202,30 @@
3138
3202
  hide() {
3139
3203
  this._element.style.display = 'none';
3140
3204
  }
3141
- setActionElement(element) {
3142
- if (this.actions === element) {
3205
+ setRightActionsElement(element) {
3206
+ if (this.rightActions === element) {
3143
3207
  return;
3144
3208
  }
3145
- if (this.actions) {
3146
- this.actions.remove();
3147
- this.actions = undefined;
3209
+ if (this.rightActions) {
3210
+ this.rightActions.remove();
3211
+ this.rightActions = undefined;
3148
3212
  }
3149
3213
  if (element) {
3150
- this.actionContainer.appendChild(element);
3151
- this.actions = element;
3214
+ this.rightActionsContainer.appendChild(element);
3215
+ this.rightActions = element;
3216
+ }
3217
+ }
3218
+ setLeftActionsElement(element) {
3219
+ if (this.leftActions === element) {
3220
+ return;
3221
+ }
3222
+ if (this.leftActions) {
3223
+ this.leftActions.remove();
3224
+ this.leftActions = undefined;
3225
+ }
3226
+ if (element) {
3227
+ this.leftActionsContainer.appendChild(element);
3228
+ this.leftActions = element;
3152
3229
  }
3153
3230
  }
3154
3231
  get element() {
@@ -3183,19 +3260,35 @@
3183
3260
  toggleClass(this._element, 'dv-single-tab', this.size === 1);
3184
3261
  }
3185
3262
  }));
3186
- this.actionContainer = document.createElement('div');
3187
- this.actionContainer.className = 'action-container';
3263
+ this.rightActionsContainer = document.createElement('div');
3264
+ this.rightActionsContainer.className = 'right-actions-container';
3265
+ this.leftActionsContainer = document.createElement('div');
3266
+ this.leftActionsContainer.className = 'left-actions-container';
3188
3267
  this.tabContainer = document.createElement('div');
3189
3268
  this.tabContainer.className = 'tabs-container';
3190
3269
  this.voidContainer = new VoidContainer(this.accessor, this.group);
3191
3270
  this._element.appendChild(this.tabContainer);
3271
+ this._element.appendChild(this.leftActionsContainer);
3192
3272
  this._element.appendChild(this.voidContainer.element);
3193
- this._element.appendChild(this.actionContainer);
3273
+ this._element.appendChild(this.rightActionsContainer);
3194
3274
  this.addDisposables(this.voidContainer, this.voidContainer.onDrop((event) => {
3195
3275
  this._onDrop.fire({
3196
3276
  event: event.nativeEvent,
3197
3277
  index: this.tabs.length,
3198
3278
  });
3279
+ }), addDisposableListener(this.voidContainer.element, 'mousedown', (event) => {
3280
+ const isFloatingGroupsEnabled = !this.accessor.options.disableFloatingGroups;
3281
+ if (isFloatingGroupsEnabled &&
3282
+ event.shiftKey &&
3283
+ !this.group.api.isFloating) {
3284
+ event.preventDefault();
3285
+ const { top, left } = this.element.getBoundingClientRect();
3286
+ const { top: rootTop, left: rootLeft } = this.accessor.element.getBoundingClientRect();
3287
+ this.accessor.addFloatingGroup(this.group, {
3288
+ x: left - rootLeft + 20,
3289
+ y: top - rootTop + 20,
3290
+ }, { inDragMode: true });
3291
+ }
3199
3292
  }), addDisposableListener(this.tabContainer, 'mousedown', (event) => {
3200
3293
  if (event.defaultPrevented) {
3201
3294
  return;
@@ -3249,6 +3342,21 @@
3249
3342
  tabToAdd.setContent(panel.view.tab);
3250
3343
  const disposable = CompositeDisposable.from(tabToAdd.onChanged((event) => {
3251
3344
  var _a;
3345
+ const isFloatingGroupsEnabled = !this.accessor.options.disableFloatingGroups;
3346
+ const isFloatingWithOnePanel = this.group.api.isFloating && this.size === 1;
3347
+ if (isFloatingGroupsEnabled &&
3348
+ !isFloatingWithOnePanel &&
3349
+ event.shiftKey) {
3350
+ event.preventDefault();
3351
+ const panel = this.accessor.getGroupPanel(tabToAdd.panelId);
3352
+ const { top, left } = tabToAdd.element.getBoundingClientRect();
3353
+ const { top: rootTop, left: rootLeft } = this.accessor.element.getBoundingClientRect();
3354
+ this.accessor.addFloatingGroup(panel, {
3355
+ x: left - rootLeft,
3356
+ y: top - rootTop,
3357
+ }, { inDragMode: true });
3358
+ return;
3359
+ }
3252
3360
  const alreadyFocused = panel.id === ((_a = this.group.model.activePanel) === null || _a === void 0 ? void 0 : _a.id) &&
3253
3361
  this.group.model.isContentFocused;
3254
3362
  const isLeftClick = event.button === 0;
@@ -3318,6 +3426,17 @@
3318
3426
  }
3319
3427
  return isAncestor(document.activeElement, this.contentContainer.element);
3320
3428
  }
3429
+ get isFloating() {
3430
+ return this._isFloating;
3431
+ }
3432
+ set isFloating(value) {
3433
+ this._isFloating = value;
3434
+ this.dropTarget.setTargetZones(value ? ['center'] : ['top', 'bottom', 'left', 'right', 'center']);
3435
+ toggleClass(this.container, 'dv-groupview-floating', value);
3436
+ this.groupPanel.api._onDidFloatingStateChange.fire({
3437
+ isFloating: this.isFloating,
3438
+ });
3439
+ }
3321
3440
  constructor(container, accessor, id, options, groupPanel) {
3322
3441
  super();
3323
3442
  this.container = container;
@@ -3327,6 +3446,7 @@
3327
3446
  this.groupPanel = groupPanel;
3328
3447
  this._isGroupActive = false;
3329
3448
  this._locked = false;
3449
+ this._isFloating = false;
3330
3450
  this.mostRecentlyUsed = [];
3331
3451
  this._onDidChange = new Emitter();
3332
3452
  this.onDidChange = this._onDidChange.event;
@@ -3343,7 +3463,7 @@
3343
3463
  this.onDidRemovePanel = this._onDidRemovePanel.event;
3344
3464
  this._onDidActivePanelChange = new Emitter();
3345
3465
  this.onDidActivePanelChange = this._onDidActivePanelChange.event;
3346
- this.container.classList.add('groupview');
3466
+ toggleClass(this.container, 'groupview', true);
3347
3467
  this.tabsContainer = new TabsContainer(this.accessor, this.groupPanel);
3348
3468
  this.contentContainer = new ContentContainer();
3349
3469
  this.dropTarget = new Droptarget(this.contentContainer.element, {
@@ -3353,6 +3473,9 @@
3353
3473
  return false;
3354
3474
  }
3355
3475
  const data = getPanelData();
3476
+ if (!data && event.shiftKey && !this.isFloating) {
3477
+ return false;
3478
+ }
3356
3479
  if (data && data.viewId === this.accessor.id) {
3357
3480
  if (data.groupId === this.id) {
3358
3481
  if (position === 'center') {
@@ -3397,14 +3520,25 @@
3397
3520
  // correctly initialized
3398
3521
  this.setActive(this.isActive, true, true);
3399
3522
  this.updateContainer();
3400
- if (this.accessor.options.createGroupControlElement) {
3401
- this._control = this.accessor.options.createGroupControlElement(this.groupPanel);
3402
- this.addDisposables(this._control);
3403
- this._control.init({
3523
+ if (this.accessor.options.createRightHeaderActionsElement) {
3524
+ this._rightHeaderActions =
3525
+ this.accessor.options.createRightHeaderActionsElement(this.groupPanel);
3526
+ this.addDisposables(this._rightHeaderActions);
3527
+ this._rightHeaderActions.init({
3404
3528
  containerApi: new DockviewApi(this.accessor),
3405
3529
  api: this.groupPanel.api,
3406
3530
  });
3407
- this.tabsContainer.setActionElement(this._control.element);
3531
+ this.tabsContainer.setRightActionsElement(this._rightHeaderActions.element);
3532
+ }
3533
+ if (this.accessor.options.createLeftHeaderActionsElement) {
3534
+ this._leftHeaderActions =
3535
+ this.accessor.options.createLeftHeaderActionsElement(this.groupPanel);
3536
+ this.addDisposables(this._leftHeaderActions);
3537
+ this._leftHeaderActions.init({
3538
+ containerApi: new DockviewApi(this.accessor),
3539
+ api: this.groupPanel.api,
3540
+ });
3541
+ this.tabsContainer.setLeftActionsElement(this._leftHeaderActions.element);
3408
3542
  }
3409
3543
  }
3410
3544
  indexOf(panel) {
@@ -3537,7 +3671,7 @@
3537
3671
  return this._activePanel === panel;
3538
3672
  }
3539
3673
  updateActions(element) {
3540
- this.tabsContainer.setActionElement(element);
3674
+ this.tabsContainer.setRightActionsElement(element);
3541
3675
  }
3542
3676
  setActive(isGroupActive, skipFocus = false, force = false) {
3543
3677
  var _a, _b, _c, _d;
@@ -3709,9 +3843,10 @@
3709
3843
  }
3710
3844
  }
3711
3845
  dispose() {
3712
- var _a, _b;
3846
+ var _a, _b, _c;
3713
3847
  super.dispose();
3714
- (_b = (_a = this.watermark) === null || _a === void 0 ? void 0 : _a.dispose) === null || _b === void 0 ? void 0 : _b.call(_a);
3848
+ (_a = this.watermark) === null || _a === void 0 ? void 0 : _a.element.remove();
3849
+ (_c = (_b = this.watermark) === null || _b === void 0 ? void 0 : _b.dispose) === null || _c === void 0 ? void 0 : _c.call(_b);
3715
3850
  for (const panel of this.panels) {
3716
3851
  panel.dispose();
3717
3852
  }
@@ -4505,8 +4640,8 @@
4505
4640
  get isActive() {
4506
4641
  return this.api.isActive;
4507
4642
  }
4508
- constructor(id, component, options) {
4509
- super(id, component, new GridviewPanelApiImpl(id));
4643
+ constructor(id, component, options, api) {
4644
+ super(id, component, api !== null && api !== void 0 ? api : new GridviewPanelApiImpl(id));
4510
4645
  this._evaluatedMinimumWidth = 0;
4511
4646
  this._evaluatedMaximumWidth = Number.MAX_SAFE_INTEGER;
4512
4647
  this._evaluatedMinimumHeight = 0;
@@ -4604,6 +4739,32 @@
4604
4739
  }
4605
4740
  }
4606
4741
 
4742
+ class DockviewGroupPanelApiImpl extends GridviewPanelApiImpl {
4743
+ get isFloating() {
4744
+ if (!this._group) {
4745
+ throw new Error(`DockviewGroupPanelApiImpl not initialized`);
4746
+ }
4747
+ return this._group.model.isFloating;
4748
+ }
4749
+ constructor(id, accessor) {
4750
+ super(id);
4751
+ this.accessor = accessor;
4752
+ this._onDidFloatingStateChange = new Emitter();
4753
+ this.onDidFloatingStateChange = this._onDidFloatingStateChange.event;
4754
+ this.addDisposables(this._onDidFloatingStateChange);
4755
+ }
4756
+ moveTo(options) {
4757
+ var _a;
4758
+ if (!this._group) {
4759
+ throw new Error(`DockviewGroupPanelApiImpl not initialized`);
4760
+ }
4761
+ this.accessor.moveGroupOrPanel(options.group, this._group.id, undefined, (_a = options.position) !== null && _a !== void 0 ? _a : 'center');
4762
+ }
4763
+ initialize(group) {
4764
+ this._group = group;
4765
+ }
4766
+ }
4767
+
4607
4768
  class DockviewGroupPanel extends GridviewPanel {
4608
4769
  get panels() {
4609
4770
  return this._model.panels;
@@ -4630,7 +4791,8 @@
4630
4791
  super(id, 'groupview_default', {
4631
4792
  minimumHeight: 100,
4632
4793
  minimumWidth: 100,
4633
- });
4794
+ }, new DockviewGroupPanelApiImpl(id, accessor));
4795
+ this.api.initialize(this); // cannot use 'this' after after 'super' call
4634
4796
  this._model = new DockviewGroupPanelModel(this.element, accessor, id, options, this);
4635
4797
  }
4636
4798
  initialize() {
@@ -4648,7 +4810,6 @@
4648
4810
  return this._model;
4649
4811
  }
4650
4812
  toJSON() {
4651
- // TODO fix typing
4652
4813
  return this.model.toJSON();
4653
4814
  }
4654
4815
  }
@@ -4702,9 +4863,10 @@
4702
4863
  get group() {
4703
4864
  return this._group;
4704
4865
  }
4705
- constructor(panel, group) {
4866
+ constructor(panel, group, accessor) {
4706
4867
  super(panel.id);
4707
4868
  this.panel = panel;
4869
+ this.accessor = accessor;
4708
4870
  this._onDidTitleChange = new Emitter();
4709
4871
  this.onDidTitleChange = this._onDidTitleChange.event;
4710
4872
  this._onDidActiveGroupChange = new Emitter();
@@ -4716,6 +4878,10 @@
4716
4878
  this._group = group;
4717
4879
  this.addDisposables(this.disposable, this._onDidTitleChange, this._onDidGroupChange, this._onDidActiveGroupChange);
4718
4880
  }
4881
+ moveTo(options) {
4882
+ var _a;
4883
+ this.accessor.moveGroupOrPanel(options.group, this._group.id, this.panel.id, (_a = options.position) !== null && _a !== void 0 ? _a : 'center', options.index);
4884
+ }
4719
4885
  setTitle(title) {
4720
4886
  this.panel.setTitle(title);
4721
4887
  }
@@ -4740,7 +4906,7 @@
4740
4906
  this.containerApi = containerApi;
4741
4907
  this.view = view;
4742
4908
  this._group = group;
4743
- this.api = new DockviewPanelApiImpl(this, this._group);
4909
+ this.api = new DockviewPanelApiImpl(this, this._group, accessor);
4744
4910
  this.addDisposables(this.api.onActiveChange(() => {
4745
4911
  accessor.setActivePanel(this);
4746
4912
  }), this.api.onDidSizeChange((event) => {
@@ -5081,6 +5247,296 @@
5081
5247
  }
5082
5248
  }
5083
5249
 
5250
+ const bringElementToFront = (() => {
5251
+ let previous = null;
5252
+ function pushToTop(element) {
5253
+ if (previous !== element && previous !== null) {
5254
+ toggleClass(previous, 'dv-bring-to-front', false);
5255
+ }
5256
+ toggleClass(element, 'dv-bring-to-front', true);
5257
+ previous = element;
5258
+ }
5259
+ return pushToTop;
5260
+ })();
5261
+ class Overlay extends CompositeDisposable {
5262
+ constructor(options) {
5263
+ super();
5264
+ this.options = options;
5265
+ this._element = document.createElement('div');
5266
+ this._onDidChange = new Emitter();
5267
+ this.onDidChange = this._onDidChange.event;
5268
+ this._onDidChangeEnd = new Emitter();
5269
+ this.onDidChangeEnd = this._onDidChangeEnd.event;
5270
+ this.addDisposables(this._onDidChange, this._onDidChangeEnd);
5271
+ this._element.className = 'dv-resize-container';
5272
+ this.setupResize('top');
5273
+ this.setupResize('bottom');
5274
+ this.setupResize('left');
5275
+ this.setupResize('right');
5276
+ this.setupResize('topleft');
5277
+ this.setupResize('topright');
5278
+ this.setupResize('bottomleft');
5279
+ this.setupResize('bottomright');
5280
+ this._element.appendChild(this.options.content);
5281
+ this.options.container.appendChild(this._element);
5282
+ // if input bad resize within acceptable boundaries
5283
+ this.setBounds({
5284
+ height: this.options.height,
5285
+ width: this.options.width,
5286
+ top: this.options.top,
5287
+ left: this.options.left,
5288
+ });
5289
+ }
5290
+ setBounds(bounds = {}) {
5291
+ if (typeof bounds.height === 'number') {
5292
+ this._element.style.height = `${bounds.height}px`;
5293
+ }
5294
+ if (typeof bounds.width === 'number') {
5295
+ this._element.style.width = `${bounds.width}px`;
5296
+ }
5297
+ if (typeof bounds.top === 'number') {
5298
+ this._element.style.top = `${bounds.top}px`;
5299
+ }
5300
+ if (typeof bounds.left === 'number') {
5301
+ this._element.style.left = `${bounds.left}px`;
5302
+ }
5303
+ const containerRect = this.options.container.getBoundingClientRect();
5304
+ const overlayRect = this._element.getBoundingClientRect();
5305
+ // region: ensure bounds within allowable limits
5306
+ // a minimum width of minimumViewportWidth must be inside the viewport
5307
+ const xOffset = Math.max(0, overlayRect.width - this.options.minimumInViewportWidth);
5308
+ // a minimum height of minimumViewportHeight must be inside the viewport
5309
+ const yOffset = Math.max(0, overlayRect.height - this.options.minimumInViewportHeight);
5310
+ const left = clamp(overlayRect.left - containerRect.left, -xOffset, Math.max(0, containerRect.width - overlayRect.width + xOffset));
5311
+ const top = clamp(overlayRect.top - containerRect.top, -yOffset, Math.max(0, containerRect.height - overlayRect.height + yOffset));
5312
+ this._element.style.left = `${left}px`;
5313
+ this._element.style.top = `${top}px`;
5314
+ this._onDidChange.fire();
5315
+ }
5316
+ toJSON() {
5317
+ const container = this.options.container.getBoundingClientRect();
5318
+ const element = this._element.getBoundingClientRect();
5319
+ return {
5320
+ top: element.top - container.top,
5321
+ left: element.left - container.left,
5322
+ width: element.width,
5323
+ height: element.height,
5324
+ };
5325
+ }
5326
+ setupDrag(dragTarget, options = { inDragMode: false }) {
5327
+ const move = new MutableDisposable();
5328
+ const track = () => {
5329
+ let offset = null;
5330
+ const iframes = [
5331
+ ...getElementsByTagName('iframe'),
5332
+ ...getElementsByTagName('webview'),
5333
+ ];
5334
+ for (const iframe of iframes) {
5335
+ iframe.style.pointerEvents = 'none';
5336
+ }
5337
+ move.value = new CompositeDisposable({
5338
+ dispose: () => {
5339
+ for (const iframe of iframes) {
5340
+ iframe.style.pointerEvents = 'auto';
5341
+ }
5342
+ },
5343
+ }, addDisposableWindowListener(window, 'mousemove', (e) => {
5344
+ const containerRect = this.options.container.getBoundingClientRect();
5345
+ const x = e.clientX - containerRect.left;
5346
+ const y = e.clientY - containerRect.top;
5347
+ toggleClass(this._element, 'dv-resize-container-dragging', true);
5348
+ const overlayRect = this._element.getBoundingClientRect();
5349
+ if (offset === null) {
5350
+ offset = {
5351
+ x: e.clientX - overlayRect.left,
5352
+ y: e.clientY - overlayRect.top,
5353
+ };
5354
+ }
5355
+ const xOffset = Math.max(0, overlayRect.width - this.options.minimumInViewportWidth);
5356
+ const yOffset = Math.max(0, overlayRect.height -
5357
+ this.options.minimumInViewportHeight);
5358
+ const left = clamp(x - offset.x, -xOffset, Math.max(0, containerRect.width - overlayRect.width + xOffset));
5359
+ const top = clamp(y - offset.y, -yOffset, Math.max(0, containerRect.height - overlayRect.height + yOffset));
5360
+ this.setBounds({ top, left });
5361
+ }), addDisposableWindowListener(window, 'mouseup', () => {
5362
+ toggleClass(this._element, 'dv-resize-container-dragging', false);
5363
+ move.dispose();
5364
+ this._onDidChangeEnd.fire();
5365
+ }));
5366
+ };
5367
+ this.addDisposables(move, addDisposableListener(dragTarget, 'mousedown', (event) => {
5368
+ if (event.defaultPrevented) {
5369
+ event.preventDefault();
5370
+ return;
5371
+ }
5372
+ // if somebody has marked this event then treat as a defaultPrevented
5373
+ // without actually calling event.preventDefault()
5374
+ if (quasiDefaultPrevented(event)) {
5375
+ return;
5376
+ }
5377
+ track();
5378
+ }), addDisposableListener(this.options.content, 'mousedown', (event) => {
5379
+ if (event.defaultPrevented) {
5380
+ return;
5381
+ }
5382
+ // if somebody has marked this event then treat as a defaultPrevented
5383
+ // without actually calling event.preventDefault()
5384
+ if (quasiDefaultPrevented(event)) {
5385
+ return;
5386
+ }
5387
+ if (event.shiftKey) {
5388
+ track();
5389
+ }
5390
+ }), addDisposableListener(this.options.content, 'mousedown', () => {
5391
+ bringElementToFront(this._element);
5392
+ }, true));
5393
+ bringElementToFront(this._element);
5394
+ if (options.inDragMode) {
5395
+ track();
5396
+ }
5397
+ }
5398
+ setupResize(direction) {
5399
+ const resizeHandleElement = document.createElement('div');
5400
+ resizeHandleElement.className = `dv-resize-handle-${direction}`;
5401
+ this._element.appendChild(resizeHandleElement);
5402
+ const move = new MutableDisposable();
5403
+ this.addDisposables(move, addDisposableListener(resizeHandleElement, 'mousedown', (e) => {
5404
+ e.preventDefault();
5405
+ let startPosition = null;
5406
+ const iframes = [
5407
+ ...getElementsByTagName('iframe'),
5408
+ ...getElementsByTagName('webview'),
5409
+ ];
5410
+ for (const iframe of iframes) {
5411
+ iframe.style.pointerEvents = 'none';
5412
+ }
5413
+ move.value = new CompositeDisposable(addDisposableWindowListener(window, 'mousemove', (e) => {
5414
+ const containerRect = this.options.container.getBoundingClientRect();
5415
+ const overlayRect = this._element.getBoundingClientRect();
5416
+ const y = e.clientY - containerRect.top;
5417
+ const x = e.clientX - containerRect.left;
5418
+ if (startPosition === null) {
5419
+ // record the initial dimensions since as all subsequence moves are relative to this
5420
+ startPosition = {
5421
+ originalY: y,
5422
+ originalHeight: overlayRect.height,
5423
+ originalX: x,
5424
+ originalWidth: overlayRect.width,
5425
+ };
5426
+ }
5427
+ let top = undefined;
5428
+ let height = undefined;
5429
+ let left = undefined;
5430
+ let width = undefined;
5431
+ const minimumInViewportHeight = this.options.minimumInViewportHeight;
5432
+ const minimumInViewportWidth = this.options.minimumInViewportWidth;
5433
+ function moveTop() {
5434
+ top = clamp(y, -Number.MAX_VALUE, startPosition.originalY +
5435
+ startPosition.originalHeight >
5436
+ containerRect.height
5437
+ ? containerRect.height -
5438
+ minimumInViewportHeight
5439
+ : Math.max(0, startPosition.originalY +
5440
+ startPosition.originalHeight -
5441
+ Overlay.MINIMUM_HEIGHT));
5442
+ height =
5443
+ startPosition.originalY +
5444
+ startPosition.originalHeight -
5445
+ top;
5446
+ }
5447
+ function moveBottom() {
5448
+ top =
5449
+ startPosition.originalY -
5450
+ startPosition.originalHeight;
5451
+ height = clamp(y - top, top < 0
5452
+ ? -top + minimumInViewportHeight
5453
+ : Overlay.MINIMUM_HEIGHT, Number.MAX_VALUE);
5454
+ }
5455
+ function moveLeft() {
5456
+ left = clamp(x, -Number.MAX_VALUE, startPosition.originalX +
5457
+ startPosition.originalWidth >
5458
+ containerRect.width
5459
+ ? containerRect.width -
5460
+ minimumInViewportWidth
5461
+ : Math.max(0, startPosition.originalX +
5462
+ startPosition.originalWidth -
5463
+ Overlay.MINIMUM_WIDTH));
5464
+ width =
5465
+ startPosition.originalX +
5466
+ startPosition.originalWidth -
5467
+ left;
5468
+ }
5469
+ function moveRight() {
5470
+ left =
5471
+ startPosition.originalX -
5472
+ startPosition.originalWidth;
5473
+ width = clamp(x - left, left < 0
5474
+ ? -left + minimumInViewportWidth
5475
+ : Overlay.MINIMUM_WIDTH, Number.MAX_VALUE);
5476
+ }
5477
+ switch (direction) {
5478
+ case 'top':
5479
+ moveTop();
5480
+ break;
5481
+ case 'bottom':
5482
+ moveBottom();
5483
+ break;
5484
+ case 'left':
5485
+ moveLeft();
5486
+ break;
5487
+ case 'right':
5488
+ moveRight();
5489
+ break;
5490
+ case 'topleft':
5491
+ moveTop();
5492
+ moveLeft();
5493
+ break;
5494
+ case 'topright':
5495
+ moveTop();
5496
+ moveRight();
5497
+ break;
5498
+ case 'bottomleft':
5499
+ moveBottom();
5500
+ moveLeft();
5501
+ break;
5502
+ case 'bottomright':
5503
+ moveBottom();
5504
+ moveRight();
5505
+ break;
5506
+ }
5507
+ this.setBounds({ height, width, top, left });
5508
+ }), {
5509
+ dispose: () => {
5510
+ for (const iframe of iframes) {
5511
+ iframe.style.pointerEvents = 'auto';
5512
+ }
5513
+ },
5514
+ }, addDisposableWindowListener(window, 'mouseup', () => {
5515
+ move.dispose();
5516
+ this._onDidChangeEnd.fire();
5517
+ }));
5518
+ }));
5519
+ }
5520
+ dispose() {
5521
+ this._element.remove();
5522
+ super.dispose();
5523
+ }
5524
+ }
5525
+ Overlay.MINIMUM_HEIGHT = 20;
5526
+ Overlay.MINIMUM_WIDTH = 20;
5527
+
5528
+ class DockviewFloatingGroupPanel extends CompositeDisposable {
5529
+ constructor(group, overlay) {
5530
+ super();
5531
+ this.group = group;
5532
+ this.overlay = overlay;
5533
+ this.addDisposables(overlay);
5534
+ }
5535
+ position(bounds) {
5536
+ this.overlay.setBounds(bounds);
5537
+ }
5538
+ }
5539
+
5084
5540
  class DockviewComponent extends BaseGrid {
5085
5541
  get orientation() {
5086
5542
  return this.gridview.orientation;
@@ -5121,7 +5577,8 @@
5121
5577
  this.onDidLayoutFromJSON = this._onDidLayoutFromJSON.event;
5122
5578
  this._onDidActivePanelChange = new Emitter();
5123
5579
  this.onDidActivePanelChange = this._onDidActivePanelChange.event;
5124
- this.element.classList.add('dv-dockview');
5580
+ this.floatingGroups = [];
5581
+ toggleClass(this.gridview.element, 'dv-dockview', true);
5125
5582
  this.addDisposables(this._onDidDrop, exports.DockviewEvent.any(this.onDidAddGroup, this.onDidRemoveGroup)(() => {
5126
5583
  this.updateWatermark();
5127
5584
  }), exports.DockviewEvent.any(this.onDidAddPanel, this.onDidRemovePanel, this.onDidActivePanelChange)(() => {
@@ -5151,6 +5608,11 @@
5151
5608
  if (data.viewId !== this.id) {
5152
5609
  return false;
5153
5610
  }
5611
+ if (position === 'center') {
5612
+ // center drop target is only allowed if there are no panels in the grid
5613
+ // floating panels are allowed
5614
+ return this.gridview.length === 0;
5615
+ }
5154
5616
  return true;
5155
5617
  }
5156
5618
  if (this.options.showDndOverlay) {
@@ -5163,7 +5625,7 @@
5163
5625
  }
5164
5626
  return false;
5165
5627
  },
5166
- acceptedTargetZones: ['top', 'bottom', 'left', 'right'],
5628
+ acceptedTargetZones: ['top', 'bottom', 'left', 'right', 'center'],
5167
5629
  overlayModel: {
5168
5630
  activationSize: { type: 'pixels', value: 10 },
5169
5631
  size: { type: 'pixels', value: 20 },
@@ -5181,6 +5643,75 @@
5181
5643
  this._api = new DockviewApi(this);
5182
5644
  this.updateWatermark();
5183
5645
  }
5646
+ addFloatingGroup(item, coord, options) {
5647
+ var _a, _b;
5648
+ let group;
5649
+ if (item instanceof DockviewPanel) {
5650
+ group = this.createGroup();
5651
+ this.removePanel(item, {
5652
+ removeEmptyGroup: true,
5653
+ skipDispose: true,
5654
+ });
5655
+ group.model.openPanel(item);
5656
+ }
5657
+ else {
5658
+ group = item;
5659
+ const skip = typeof (options === null || options === void 0 ? void 0 : options.skipRemoveGroup) === 'boolean' &&
5660
+ options.skipRemoveGroup;
5661
+ if (!skip) {
5662
+ this.doRemoveGroup(item, { skipDispose: true });
5663
+ }
5664
+ }
5665
+ group.model.isFloating = true;
5666
+ const overlayLeft = typeof (coord === null || coord === void 0 ? void 0 : coord.x) === 'number' ? Math.max(coord.x, 0) : 100;
5667
+ const overlayTop = typeof (coord === null || coord === void 0 ? void 0 : coord.y) === 'number' ? Math.max(coord.y, 0) : 100;
5668
+ const overlay = new Overlay({
5669
+ container: this.gridview.element,
5670
+ content: group.element,
5671
+ height: (_a = coord === null || coord === void 0 ? void 0 : coord.height) !== null && _a !== void 0 ? _a : 300,
5672
+ width: (_b = coord === null || coord === void 0 ? void 0 : coord.width) !== null && _b !== void 0 ? _b : 300,
5673
+ left: overlayLeft,
5674
+ top: overlayTop,
5675
+ minimumInViewportWidth: 100,
5676
+ minimumInViewportHeight: 100,
5677
+ });
5678
+ const el = group.element.querySelector('.void-container');
5679
+ if (!el) {
5680
+ throw new Error('failed to find drag handle');
5681
+ }
5682
+ overlay.setupDrag(el, {
5683
+ inDragMode: typeof (options === null || options === void 0 ? void 0 : options.inDragMode) === 'boolean'
5684
+ ? options.inDragMode
5685
+ : false,
5686
+ });
5687
+ const floatingGroupPanel = new DockviewFloatingGroupPanel(group, overlay);
5688
+ const disposable = watchElementResize(group.element, (entry) => {
5689
+ const { width, height } = entry.contentRect;
5690
+ group.layout(width, height); // let the group know it's size is changing so it can fire events to the panel
5691
+ });
5692
+ floatingGroupPanel.addDisposables(overlay.onDidChange(() => {
5693
+ // this is either a resize or a move
5694
+ // to inform the panels .layout(...) the group with it's current size
5695
+ // don't care about resize since the above watcher handles that
5696
+ group.layout(group.height, group.width);
5697
+ }), overlay.onDidChangeEnd(() => {
5698
+ this._bufferOnDidLayoutChange.fire();
5699
+ }), group.onDidChange((event) => {
5700
+ overlay.setBounds({
5701
+ height: event === null || event === void 0 ? void 0 : event.height,
5702
+ width: event === null || event === void 0 ? void 0 : event.width,
5703
+ });
5704
+ }), {
5705
+ dispose: () => {
5706
+ disposable.dispose();
5707
+ group.model.isFloating = false;
5708
+ remove(this.floatingGroups, floatingGroupPanel);
5709
+ this.updateWatermark();
5710
+ },
5711
+ });
5712
+ this.floatingGroups.push(floatingGroupPanel);
5713
+ this.updateWatermark();
5714
+ }
5184
5715
  orthogonalize(position) {
5185
5716
  switch (position) {
5186
5717
  case 'top':
@@ -5203,6 +5734,7 @@
5203
5734
  switch (position) {
5204
5735
  case 'top':
5205
5736
  case 'left':
5737
+ case 'center':
5206
5738
  return this.createGroupAtLocation([0]); // insert into first position
5207
5739
  case 'bottom':
5208
5740
  case 'right':
@@ -5220,6 +5752,15 @@
5220
5752
  }
5221
5753
  this.layout(this.gridview.width, this.gridview.height, true);
5222
5754
  }
5755
+ layout(width, height, forceResize) {
5756
+ super.layout(width, height, forceResize);
5757
+ if (this.floatingGroups) {
5758
+ for (const floating of this.floatingGroups) {
5759
+ // ensure floting groups stay within visible boundaries
5760
+ floating.overlay.setBounds();
5761
+ }
5762
+ }
5763
+ }
5223
5764
  focus() {
5224
5765
  var _a;
5225
5766
  (_a = this.activeGroup) === null || _a === void 0 ? void 0 : _a.focus();
@@ -5282,51 +5823,81 @@
5282
5823
  collection[panel.id] = panel.toJSON();
5283
5824
  return collection;
5284
5825
  }, {});
5285
- return {
5826
+ const floats = this.floatingGroups.map((floatingGroup) => {
5827
+ return {
5828
+ data: floatingGroup.group.toJSON(),
5829
+ position: floatingGroup.overlay.toJSON(),
5830
+ };
5831
+ });
5832
+ const result = {
5286
5833
  grid: data,
5287
5834
  panels,
5288
5835
  activeGroup: (_a = this.activeGroup) === null || _a === void 0 ? void 0 : _a.id,
5289
5836
  };
5837
+ if (floats.length > 0) {
5838
+ result.floatingGroups = floats;
5839
+ }
5840
+ return result;
5290
5841
  }
5291
5842
  fromJSON(data) {
5843
+ var _a;
5292
5844
  this.clear();
5293
5845
  const { grid, panels, activeGroup } = data;
5294
5846
  if (grid.root.type !== 'branch' || !Array.isArray(grid.root.data)) {
5295
5847
  throw new Error('root must be of type branch');
5296
5848
  }
5849
+ // take note of the existing dimensions
5850
+ const width = this.width;
5851
+ const height = this.height;
5852
+ const createGroupFromSerializedState = (data) => {
5853
+ const { id, locked, hideHeader, views, activeView } = data;
5854
+ const group = this.createGroup({
5855
+ id,
5856
+ locked: !!locked,
5857
+ hideHeader: !!hideHeader,
5858
+ });
5859
+ this._onDidAddGroup.fire(group);
5860
+ for (const child of views) {
5861
+ const panel = this._deserializer.fromJSON(panels[child], group);
5862
+ const isActive = typeof activeView === 'string' && activeView === panel.id;
5863
+ group.model.openPanel(panel, {
5864
+ skipSetPanelActive: !isActive,
5865
+ skipSetGroupActive: true,
5866
+ });
5867
+ }
5868
+ if (!group.activePanel && group.panels.length > 0) {
5869
+ group.model.openPanel(group.panels[group.panels.length - 1], {
5870
+ skipSetGroupActive: true,
5871
+ });
5872
+ }
5873
+ return group;
5874
+ };
5297
5875
  this.gridview.deserialize(grid, {
5298
5876
  fromJSON: (node) => {
5299
- const { id, locked, hideHeader, views, activeView } = node.data;
5300
- const group = this.createGroup({
5301
- id,
5302
- locked: !!locked,
5303
- hideHeader: !!hideHeader,
5304
- });
5305
- this._onDidAddGroup.fire(group);
5306
- for (const child of views) {
5307
- const panel = this._deserializer.fromJSON(panels[child], group);
5308
- const isActive = typeof activeView === 'string' &&
5309
- activeView === panel.id;
5310
- group.model.openPanel(panel, {
5311
- skipSetPanelActive: !isActive,
5312
- skipSetGroupActive: true,
5313
- });
5314
- }
5315
- if (!group.activePanel && group.panels.length > 0) {
5316
- group.model.openPanel(group.panels[group.panels.length - 1], {
5317
- skipSetGroupActive: true,
5318
- });
5319
- }
5320
- return group;
5877
+ return createGroupFromSerializedState(node.data);
5321
5878
  },
5322
5879
  });
5880
+ this.layout(width, height, true);
5881
+ const serializedFloatingGroups = (_a = data.floatingGroups) !== null && _a !== void 0 ? _a : [];
5882
+ for (const serializedFloatingGroup of serializedFloatingGroups) {
5883
+ const { data, position } = serializedFloatingGroup;
5884
+ const group = createGroupFromSerializedState(data);
5885
+ this.addFloatingGroup(group, {
5886
+ x: position.left,
5887
+ y: position.top,
5888
+ height: position.height,
5889
+ width: position.width,
5890
+ }, { skipRemoveGroup: true, inDragMode: false });
5891
+ }
5892
+ for (const floatingGroup of this.floatingGroups) {
5893
+ floatingGroup.overlay.setBounds();
5894
+ }
5323
5895
  if (typeof activeGroup === 'string') {
5324
5896
  const panel = this.getPanel(activeGroup);
5325
5897
  if (panel) {
5326
5898
  this.doSetGroupActive(panel);
5327
5899
  }
5328
5900
  }
5329
- this.gridview.layout(this.width, this.height);
5330
5901
  this._onDidLayoutFromJSON.fire();
5331
5902
  }
5332
5903
  clear() {
@@ -5335,7 +5906,7 @@
5335
5906
  const hasActivePanel = !!this.activePanel;
5336
5907
  for (const group of groups) {
5337
5908
  // remove the group will automatically remove the panels
5338
- this.removeGroup(group, true);
5909
+ this.removeGroup(group, { skipActive: true });
5339
5910
  }
5340
5911
  if (hasActiveGroup) {
5341
5912
  this.doSetGroupActive(undefined);
@@ -5357,6 +5928,9 @@
5357
5928
  throw new Error(`panel with id ${options.id} already exists`);
5358
5929
  }
5359
5930
  let referenceGroup;
5931
+ if (options.position && options.floating) {
5932
+ throw new Error('you can only provide one of: position, floating as arguments to .addPanel(...)');
5933
+ }
5360
5934
  if (options.position) {
5361
5935
  if (isPanelOptionsWithPanel(options.position)) {
5362
5936
  const referencePanel = typeof options.position.referencePanel === 'string'
@@ -5389,7 +5963,20 @@
5389
5963
  let panel;
5390
5964
  if (referenceGroup) {
5391
5965
  const target = toTarget(((_b = options.position) === null || _b === void 0 ? void 0 : _b.direction) || 'within');
5392
- if (target === 'center') {
5966
+ if (options.floating) {
5967
+ const group = this.createGroup();
5968
+ panel = this.createPanel(options, group);
5969
+ group.model.openPanel(panel);
5970
+ const o = typeof options.floating === 'object' &&
5971
+ options.floating !== null
5972
+ ? options.floating
5973
+ : {};
5974
+ this.addFloatingGroup(group, o, {
5975
+ inDragMode: false,
5976
+ skipRemoveGroup: true,
5977
+ });
5978
+ }
5979
+ else if (referenceGroup.api.isFloating || target === 'center') {
5393
5980
  panel = this.createPanel(options, referenceGroup);
5394
5981
  referenceGroup.model.openPanel(panel);
5395
5982
  }
@@ -5401,6 +5988,19 @@
5401
5988
  group.model.openPanel(panel);
5402
5989
  }
5403
5990
  }
5991
+ else if (options.floating) {
5992
+ const group = this.createGroup();
5993
+ panel = this.createPanel(options, group);
5994
+ group.model.openPanel(panel);
5995
+ const o = typeof options.floating === 'object' &&
5996
+ options.floating !== null
5997
+ ? options.floating
5998
+ : {};
5999
+ this.addFloatingGroup(group, o, {
6000
+ inDragMode: false,
6001
+ skipRemoveGroup: true,
6002
+ });
6003
+ }
5404
6004
  else {
5405
6005
  const group = this.createGroupAtLocation();
5406
6006
  panel = this.createPanel(options, group);
@@ -5417,7 +6017,9 @@
5417
6017
  throw new Error(`cannot remove panel ${panel.id}. it's missing a group.`);
5418
6018
  }
5419
6019
  group.model.removePanel(panel);
5420
- panel.dispose();
6020
+ if (!options.skipDispose) {
6021
+ panel.dispose();
6022
+ }
5421
6023
  if (group.size === 0 && options.removeEmptyGroup) {
5422
6024
  this.removeGroup(group);
5423
6025
  }
@@ -5432,7 +6034,7 @@
5432
6034
  }
5433
6035
  updateWatermark() {
5434
6036
  var _a, _b;
5435
- if (this.groups.length === 0) {
6037
+ if (this.groups.filter((x) => !x.api.isFloating).length === 0) {
5436
6038
  if (!this.watermark) {
5437
6039
  this.watermark = this.createWatermarkComponent();
5438
6040
  this.watermark.init({
@@ -5441,7 +6043,7 @@
5441
6043
  const watermarkContainer = document.createElement('div');
5442
6044
  watermarkContainer.className = 'dv-watermark-container';
5443
6045
  watermarkContainer.appendChild(this.watermark.element);
5444
- this.element.appendChild(watermarkContainer);
6046
+ this.gridview.element.appendChild(watermarkContainer);
5445
6047
  }
5446
6048
  }
5447
6049
  else if (this.watermark) {
@@ -5491,15 +6093,28 @@
5491
6093
  return group;
5492
6094
  }
5493
6095
  }
5494
- removeGroup(group, skipActive = false) {
6096
+ removeGroup(group, options) {
6097
+ var _a;
5495
6098
  const panels = [...group.panels]; // reassign since group panels will mutate
5496
6099
  for (const panel of panels) {
5497
6100
  this.removePanel(panel, {
5498
6101
  removeEmptyGroup: false,
5499
- skipDispose: false,
6102
+ skipDispose: (_a = options === null || options === void 0 ? void 0 : options.skipDispose) !== null && _a !== void 0 ? _a : false,
5500
6103
  });
5501
6104
  }
5502
- super.doRemoveGroup(group, { skipActive });
6105
+ this.doRemoveGroup(group, options);
6106
+ }
6107
+ doRemoveGroup(group, options) {
6108
+ const floatingGroup = this.floatingGroups.find((_) => _.group === group);
6109
+ if (floatingGroup) {
6110
+ if (!(options === null || options === void 0 ? void 0 : options.skipDispose)) {
6111
+ floatingGroup.group.dispose();
6112
+ this._groups.delete(group.id);
6113
+ }
6114
+ floatingGroup.dispose();
6115
+ return floatingGroup.group;
6116
+ }
6117
+ return super.doRemoveGroup(group, options);
5503
6118
  }
5504
6119
  moveGroupOrPanel(destinationGroup, sourceGroupId, sourceItemId, destinationTarget, destinationIndex) {
5505
6120
  var _a;
@@ -5530,25 +6145,26 @@
5530
6145
  const targetLocation = getRelativeLocation(this.gridview.orientation, referenceLocation, destinationTarget);
5531
6146
  if (sourceGroup && sourceGroup.size < 2) {
5532
6147
  const [targetParentLocation, to] = tail(targetLocation);
5533
- const sourceLocation = getGridLocation(sourceGroup.element);
5534
- const [sourceParentLocation, from] = tail(sourceLocation);
5535
- if (sequenceEquals(sourceParentLocation, targetParentLocation)) {
5536
- // special case when 'swapping' two views within same grid location
5537
- // if a group has one tab - we are essentially moving the 'group'
5538
- // which is equivalent to swapping two views in this case
5539
- this.gridview.moveView(sourceParentLocation, from, to);
5540
- }
5541
- else {
5542
- // source group will become empty so delete the group
5543
- const targetGroup = this.doRemoveGroup(sourceGroup, {
5544
- skipActive: true,
5545
- skipDispose: true,
5546
- });
5547
- // after deleting the group we need to re-evaulate the ref location
5548
- const updatedReferenceLocation = getGridLocation(destinationGroup.element);
5549
- const location = getRelativeLocation(this.gridview.orientation, updatedReferenceLocation, destinationTarget);
5550
- this.doAddGroup(targetGroup, location);
6148
+ const isFloating = this.floatingGroups.find((x) => x.group === sourceGroup);
6149
+ if (!isFloating) {
6150
+ const sourceLocation = getGridLocation(sourceGroup.element);
6151
+ const [sourceParentLocation, from] = tail(sourceLocation);
6152
+ if (sequenceEquals(sourceParentLocation, targetParentLocation)) {
6153
+ // special case when 'swapping' two views within same grid location
6154
+ // if a group has one tab - we are essentially moving the 'group'
6155
+ // which is equivalent to swapping two views in this case
6156
+ this.gridview.moveView(sourceParentLocation, from, to);
6157
+ }
5551
6158
  }
6159
+ // source group will become empty so delete the group
6160
+ const targetGroup = this.doRemoveGroup(sourceGroup, {
6161
+ skipActive: true,
6162
+ skipDispose: true,
6163
+ });
6164
+ // after deleting the group we need to re-evaulate the ref location
6165
+ const updatedReferenceLocation = getGridLocation(destinationGroup.element);
6166
+ const location = getRelativeLocation(this.gridview.orientation, updatedReferenceLocation, destinationTarget);
6167
+ this.doAddGroup(targetGroup, location);
5552
6168
  }
5553
6169
  else {
5554
6170
  const groupItem = (sourceGroup === null || sourceGroup === void 0 ? void 0 : sourceGroup.model.removePanel(sourceItemId)) ||
@@ -5577,7 +6193,13 @@
5577
6193
  }
5578
6194
  }
5579
6195
  else {
5580
- this.gridview.removeView(getGridLocation(sourceGroup.element));
6196
+ const floatingGroup = this.floatingGroups.find((x) => x.group === sourceGroup);
6197
+ if (floatingGroup) {
6198
+ floatingGroup.dispose();
6199
+ }
6200
+ else {
6201
+ this.gridview.removeView(getGridLocation(sourceGroup.element));
6202
+ }
5581
6203
  const referenceLocation = getGridLocation(referenceGroup.element);
5582
6204
  const dropLocation = getRelativeLocation(this.gridview.orientation, referenceLocation, target);
5583
6205
  this.gridview.addView(sourceGroup, exports.Sizing.Distribute, dropLocation);
@@ -5732,6 +6354,9 @@
5732
6354
  this.clear();
5733
6355
  const { grid, activePanel } = serializedGridview;
5734
6356
  const queue = [];
6357
+ // take note of the existing dimensions
6358
+ const width = this.width;
6359
+ const height = this.height;
5735
6360
  this.gridview.deserialize(grid, {
5736
6361
  fromJSON: (node) => {
5737
6362
  const { data } = node;
@@ -5757,7 +6382,7 @@
5757
6382
  return view;
5758
6383
  },
5759
6384
  });
5760
- this.layout(this.width, this.height, true);
6385
+ this.layout(width, height, true);
5761
6386
  queue.forEach((f) => f());
5762
6387
  if (typeof activePanel === 'string') {
5763
6388
  const panel = this.getPanel(activePanel);
@@ -6071,6 +6696,9 @@
6071
6696
  this.clear();
6072
6697
  const { views, orientation, size, activeView } = serializedSplitview;
6073
6698
  const queue = [];
6699
+ // take note of the existing dimensions
6700
+ const width = this.width;
6701
+ const height = this.height;
6074
6702
  this.splitview = new Splitview(this.element, {
6075
6703
  orientation,
6076
6704
  proportionalLayout: this.options.proportionalLayout,
@@ -6107,7 +6735,7 @@
6107
6735
  }),
6108
6736
  },
6109
6737
  });
6110
- this.layout(this.width, this.height);
6738
+ this.layout(width, height);
6111
6739
  queue.forEach((f) => f());
6112
6740
  if (typeof activeView === 'string') {
6113
6741
  const panel = this.getPanel(activeView);
@@ -6374,6 +7002,9 @@
6374
7002
  this.clear();
6375
7003
  const { views, size } = serializedPaneview;
6376
7004
  const queue = [];
7005
+ // take note of the existing dimensions
7006
+ const width = this.width;
7007
+ const height = this.height;
6377
7008
  this.paneview = new Paneview(this.element, {
6378
7009
  orientation: exports.Orientation.VERTICAL,
6379
7010
  descriptor: {
@@ -6429,7 +7060,7 @@
6429
7060
  }),
6430
7061
  },
6431
7062
  });
6432
- this.layout(this.width, this.height);
7063
+ this.layout(width, height);
6433
7064
  queue.forEach((f) => f());
6434
7065
  this._onDidLayoutfromJSON.fire();
6435
7066
  }
@@ -6812,7 +7443,7 @@
6812
7443
  }
6813
7444
  }
6814
7445
 
6815
- class ReactGroupControlsRendererPart {
7446
+ class ReactHeaderActionsRendererPart {
6816
7447
  get element() {
6817
7448
  return this._element;
6818
7449
  }
@@ -6849,6 +7480,7 @@
6849
7480
  panels: this._group.model.panels,
6850
7481
  activePanel: this._group.model.activePanel,
6851
7482
  isGroupActive: this._group.api.isActive,
7483
+ group: this._group,
6852
7484
  });
6853
7485
  }
6854
7486
  update(event) {
@@ -6882,7 +7514,7 @@
6882
7514
  function createGroupControlElement(component, store) {
6883
7515
  return component
6884
7516
  ? (groupPanel) => {
6885
- return new ReactGroupControlsRendererPart(component, store, groupPanel);
7517
+ return new ReactHeaderActionsRendererPart(component, store, groupPanel);
6886
7518
  }
6887
7519
  : undefined;
6888
7520
  }
@@ -6939,8 +7571,10 @@
6939
7571
  ? { separatorBorder: 'transparent' }
6940
7572
  : undefined,
6941
7573
  showDndOverlay: props.showDndOverlay,
6942
- createGroupControlElement: createGroupControlElement(props.groupControlComponent, { addPortal }),
7574
+ createLeftHeaderActionsElement: createGroupControlElement(props.leftHeaderActionsComponent, { addPortal }),
7575
+ createRightHeaderActionsElement: createGroupControlElement(props.rightHeaderActionsComponent, { addPortal }),
6943
7576
  singleTabMode: props.singleTabMode,
7577
+ disableFloatingGroups: props.disableFloatingGroups,
6944
7578
  });
6945
7579
  const { clientWidth, clientHeight } = domRef.current;
6946
7580
  dockview.layout(clientWidth, clientHeight);
@@ -6999,6 +7633,14 @@
6999
7633
  frameworkTabComponents: props.tabComponents,
7000
7634
  });
7001
7635
  }, [props.tabComponents]);
7636
+ React__namespace.useEffect(() => {
7637
+ if (!dockviewRef.current) {
7638
+ return;
7639
+ }
7640
+ dockviewRef.current.updateOptions({
7641
+ disableFloatingGroups: props.disableFloatingGroups,
7642
+ });
7643
+ }, [props.disableFloatingGroups]);
7002
7644
  React__namespace.useEffect(() => {
7003
7645
  if (!dockviewRef.current) {
7004
7646
  return;
@@ -7020,9 +7662,17 @@
7020
7662
  return;
7021
7663
  }
7022
7664
  dockviewRef.current.updateOptions({
7023
- createGroupControlElement: createGroupControlElement(props.groupControlComponent, { addPortal }),
7665
+ createRightHeaderActionsElement: createGroupControlElement(props.rightHeaderActionsComponent, { addPortal }),
7666
+ });
7667
+ }, [props.rightHeaderActionsComponent]);
7668
+ React__namespace.useEffect(() => {
7669
+ if (!dockviewRef.current) {
7670
+ return;
7671
+ }
7672
+ dockviewRef.current.updateOptions({
7673
+ createLeftHeaderActionsElement: createGroupControlElement(props.leftHeaderActionsComponent, { addPortal }),
7024
7674
  });
7025
- }, [props.groupControlComponent]);
7675
+ }, [props.leftHeaderActionsComponent]);
7026
7676
  return (React__namespace.createElement("div", { className: props.className, style: { height: '100%', width: '100%' }, ref: domRef }, portals));
7027
7677
  });
7028
7678
  DockviewReact.displayName = 'DockviewComponent';
@@ -7041,6 +7691,8 @@
7041
7691
  OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
7042
7692
  PERFORMANCE OF THIS SOFTWARE.
7043
7693
  ***************************************************************************** */
7694
+ /* global Reflect, Promise, SuppressedError, Symbol */
7695
+
7044
7696
 
7045
7697
  function __rest(s, e) {
7046
7698
  var t = {};
@@ -7052,7 +7704,12 @@
7052
7704
  t[p[i]] = s[p[i]];
7053
7705
  }
7054
7706
  return t;
7055
- }
7707
+ }
7708
+
7709
+ typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) {
7710
+ var e = new Error(message);
7711
+ return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
7712
+ };
7056
7713
 
7057
7714
  const CloseButton = () => (React__namespace.createElement("svg", { height: "11", width: "11", viewBox: "0 0 28 28", "aria-hidden": 'false', focusable: false, className: "dockview-svg" },
7058
7715
  React__namespace.createElement("path", { d: "M2.1 27.3L0 25.2L11.55 13.65L0 2.1L2.1 0L13.65 11.55L25.2 0L27.3 2.1L15.75 13.65L27.3 25.2L25.2 27.3L13.65 15.75L2.1 27.3Z" })));
@@ -7380,6 +8037,6 @@
7380
8037
  exports.positionToDirection = positionToDirection;
7381
8038
  exports.toTarget = toTarget;
7382
8039
  exports.usePortalsLifecycle = usePortalsLifecycle;
7383
- exports.watchElementResize = watchElementResize;
7384
8040
 
7385
8041
  }));
8042
+ //# sourceMappingURL=dockview.js.map