dockview-core 6.2.2 → 6.3.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 (53) hide show
  1. package/dist/cjs/api/dockviewGroupPanelApi.d.ts +10 -1
  2. package/dist/cjs/api/dockviewGroupPanelApi.js +16 -0
  3. package/dist/cjs/dnd/groupDragHandler.js +34 -12
  4. package/dist/cjs/dockview/components/titlebar/tabGroupIndicator.js +2 -2
  5. package/dist/cjs/dockview/components/titlebar/tabs.js +9 -2
  6. package/dist/cjs/dockview/components/titlebar/voidContainer.js +6 -0
  7. package/dist/cjs/dockview/dockviewComponent.d.ts +1 -0
  8. package/dist/cjs/dockview/dockviewComponent.js +187 -125
  9. package/dist/cjs/dockview/dockviewGroupPanelModel.d.ts +1 -0
  10. package/dist/cjs/dockview/dockviewGroupPanelModel.js +9 -0
  11. package/dist/cjs/dockview/dockviewPanel.js +5 -0
  12. package/dist/cjs/dockview/dockviewPanelModel.d.ts +2 -0
  13. package/dist/cjs/dockview/dockviewPanelModel.js +8 -0
  14. package/dist/cjs/dockview/framework.d.ts +8 -0
  15. package/dist/cjs/dockview/options.d.ts +13 -2
  16. package/dist/cjs/dockview/options.js +2 -0
  17. package/dist/cjs/dom.d.ts +5 -1
  18. package/dist/cjs/dom.js +14 -2
  19. package/dist/cjs/index.d.ts +1 -1
  20. package/dist/cjs/popoutWindow.d.ts +2 -0
  21. package/dist/cjs/popoutWindow.js +3 -1
  22. package/dist/dockview-core.js +156 -27
  23. package/dist/dockview-core.min.js +2 -2
  24. package/dist/dockview-core.min.js.map +1 -1
  25. package/dist/dockview-core.min.noStyle.js +2 -2
  26. package/dist/dockview-core.min.noStyle.js.map +1 -1
  27. package/dist/dockview-core.noStyle.js +156 -27
  28. package/dist/esm/api/dockviewGroupPanelApi.d.ts +10 -1
  29. package/dist/esm/api/dockviewGroupPanelApi.js +12 -0
  30. package/dist/esm/dnd/groupDragHandler.js +34 -12
  31. package/dist/esm/dockview/components/titlebar/tabGroupIndicator.js +2 -2
  32. package/dist/esm/dockview/components/titlebar/tabs.js +12 -2
  33. package/dist/esm/dockview/components/titlebar/voidContainer.js +6 -0
  34. package/dist/esm/dockview/dockviewComponent.d.ts +1 -0
  35. package/dist/esm/dockview/dockviewComponent.js +49 -7
  36. package/dist/esm/dockview/dockviewGroupPanelModel.d.ts +1 -0
  37. package/dist/esm/dockview/dockviewGroupPanelModel.js +9 -0
  38. package/dist/esm/dockview/dockviewPanel.js +5 -0
  39. package/dist/esm/dockview/dockviewPanelModel.d.ts +2 -0
  40. package/dist/esm/dockview/dockviewPanelModel.js +8 -0
  41. package/dist/esm/dockview/framework.d.ts +8 -0
  42. package/dist/esm/dockview/options.d.ts +13 -2
  43. package/dist/esm/dockview/options.js +2 -0
  44. package/dist/esm/dom.d.ts +5 -1
  45. package/dist/esm/dom.js +13 -2
  46. package/dist/esm/index.d.ts +1 -1
  47. package/dist/esm/popoutWindow.d.ts +2 -0
  48. package/dist/esm/popoutWindow.js +3 -1
  49. package/dist/package/main.cjs.js +156 -27
  50. package/dist/package/main.cjs.min.js +2 -2
  51. package/dist/package/main.esm.min.mjs +2 -2
  52. package/dist/package/main.esm.mjs +156 -27
  53. package/package.json +1 -1
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * dockview-core
3
- * @version 6.2.2
3
+ * @version 6.3.0
4
4
  * @link https://github.com/mathuo/dockview
5
5
  * @license MIT
6
6
  */
@@ -472,8 +472,10 @@ function quasiPreventDefault(event) {
472
472
  function quasiDefaultPrevented(event) {
473
473
  return event[QUASI_PREVENT_DEFAULT_KEY];
474
474
  }
475
- function addStyles(document, styleSheetList) {
475
+ function addStyles(document, styleSheetList, options = {}) {
476
476
  const styleSheets = Array.from(styleSheetList);
477
+ const { nonce } = options;
478
+ const resolvedNonce = typeof nonce === 'function' ? nonce(document) : nonce;
477
479
  for (const styleSheet of styleSheets) {
478
480
  if (styleSheet.href) {
479
481
  const link = document.createElement('link');
@@ -481,6 +483,10 @@ function addStyles(document, styleSheetList) {
481
483
  link.type = styleSheet.type;
482
484
  link.rel = 'stylesheet';
483
485
  document.head.appendChild(link);
486
+ // The <link> will load and apply its rules in the target
487
+ // document. Reading cssRules here would duplicate them
488
+ // (and throws for cross-origin sheets).
489
+ continue;
484
490
  }
485
491
  let cssTexts = [];
486
492
  try {
@@ -491,11 +497,16 @@ function addStyles(document, styleSheetList) {
491
497
  catch (err) {
492
498
  console.warn('dockview: failed to access stylesheet rules due to security restrictions', err);
493
499
  }
500
+ const fragment = document.createDocumentFragment();
494
501
  for (const rule of cssTexts) {
495
502
  const style = document.createElement('style');
503
+ if (resolvedNonce) {
504
+ style.setAttribute('nonce', resolvedNonce);
505
+ }
496
506
  style.appendChild(document.createTextNode(rule));
497
- document.head.appendChild(style);
507
+ fragment.appendChild(style);
498
508
  }
509
+ document.head.appendChild(fragment);
499
510
  }
500
511
  }
501
512
  function getDomNodePagePosition(domNode) {
@@ -5487,19 +5498,41 @@ class GroupDragHandler extends DragHandler {
5487
5498
  const bgColor = style.getPropertyValue('--dv-activegroup-visiblepanel-tab-background-color');
5488
5499
  const color = style.getPropertyValue('--dv-activegroup-visiblepanel-tab-color');
5489
5500
  if (dataTransfer) {
5490
- const ghostElement = document.createElement('div');
5491
- ghostElement.style.backgroundColor = bgColor;
5492
- ghostElement.style.color = color;
5493
- ghostElement.style.padding = '2px 8px';
5494
- ghostElement.style.height = '24px';
5495
- ghostElement.style.fontSize = '11px';
5496
- ghostElement.style.lineHeight = '20px';
5497
- ghostElement.style.borderRadius = '12px';
5498
- ghostElement.style.position = 'absolute';
5499
- ghostElement.style.pointerEvents = 'none';
5500
- ghostElement.style.top = '-9999px';
5501
- ghostElement.textContent = `Multiple Panels (${this.group.size})`;
5501
+ const createGhost = this.accessor.options.createGroupDragGhostComponent;
5502
+ let ghostElement;
5503
+ let customRenderer;
5504
+ if (createGhost) {
5505
+ customRenderer = createGhost(this.group);
5506
+ customRenderer.init({
5507
+ group: this.group,
5508
+ api: this.accessor.api,
5509
+ });
5510
+ ghostElement = customRenderer.element;
5511
+ ghostElement.style.position = 'absolute';
5512
+ ghostElement.style.pointerEvents = 'none';
5513
+ ghostElement.style.top = '-9999px';
5514
+ }
5515
+ else {
5516
+ ghostElement = document.createElement('div');
5517
+ ghostElement.style.backgroundColor = bgColor;
5518
+ ghostElement.style.color = color;
5519
+ ghostElement.style.padding = '2px 8px';
5520
+ ghostElement.style.height = '24px';
5521
+ ghostElement.style.fontSize = '11px';
5522
+ ghostElement.style.lineHeight = '20px';
5523
+ ghostElement.style.borderRadius = '12px';
5524
+ ghostElement.style.position = 'absolute';
5525
+ ghostElement.style.pointerEvents = 'none';
5526
+ ghostElement.style.top = '-9999px';
5527
+ ghostElement.textContent = `Multiple Panels (${this.group.size})`;
5528
+ }
5502
5529
  addGhostImage(dataTransfer, ghostElement, { y: -10, x: 30 });
5530
+ if (customRenderer === null || customRenderer === void 0 ? void 0 : customRenderer.dispose) {
5531
+ // addGhostImage removes the element from the DOM on the next
5532
+ // tick; dispose the framework renderer on the same schedule.
5533
+ const renderer = customRenderer;
5534
+ setTimeout(() => { var _a; return (_a = renderer.dispose) === null || _a === void 0 ? void 0 : _a.call(renderer); }, 0);
5535
+ }
5503
5536
  }
5504
5537
  return {
5505
5538
  dispose: () => {
@@ -5532,6 +5565,12 @@ class VoidContainer extends CompositeDisposable {
5532
5565
  this.dropTarget = new Droptarget(this._element, {
5533
5566
  acceptedTargetZones: ['center'],
5534
5567
  canDisplayOverlay: (event, position) => {
5568
+ if (this.group.api.locked) {
5569
+ // Dropping on the void/header space adds the panel
5570
+ // to this group, which `locked` is meant to prevent
5571
+ // (both `true` and `'no-drop-target'`).
5572
+ return false;
5573
+ }
5535
5574
  const data = getPanelData();
5536
5575
  if (data && this.accessor.id === data.viewId) {
5537
5576
  return true;
@@ -6113,7 +6152,7 @@ class WrapTabGroupIndicator extends BaseTabGroupIndicator {
6113
6152
  let svg = underline.firstElementChild;
6114
6153
  let path;
6115
6154
  if (!svg || svg.tagName !== 'svg') {
6116
- underline.innerHTML = '';
6155
+ underline.replaceChildren();
6117
6156
  svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
6118
6157
  svg.style.display = 'block';
6119
6158
  path = document.createElementNS('http://www.w3.org/2000/svg', 'path');
@@ -6211,7 +6250,7 @@ class NoneTabGroupIndicator extends BaseTabGroupIndicator {
6211
6250
  underline.style.display = '';
6212
6251
  // Clear any SVG content left over from a mode switch
6213
6252
  if (underline.firstElementChild) {
6214
- underline.innerHTML = '';
6253
+ underline.replaceChildren();
6215
6254
  }
6216
6255
  underline.style.backgroundColor = color;
6217
6256
  if (isVertical) {
@@ -7604,11 +7643,15 @@ class Tabs extends CompositeDisposable {
7604
7643
  if (!isInsideRange && !isJustBeforeGroup) {
7605
7644
  continue;
7606
7645
  }
7607
- if (isGroupDrag) {
7646
+ if (isGroupDrag && isInsideRange) {
7608
7647
  // A group cannot be dropped inside another group.
7609
7648
  // Snap the insertion index to just before or just
7610
7649
  // after this group based on cursor position relative
7611
- // to the group's midpoint.
7650
+ // to the group's midpoint. Only applies when the
7651
+ // insertion would land *inside* the group — for
7652
+ // `isJustBeforeGroup`, the index is already outside
7653
+ // (immediately left of the group) and is a valid
7654
+ // drop position, so leave it untouched (issue #1264).
7612
7655
  const groupMid = (firstIdx + lastIdx + 1) / 2;
7613
7656
  if (insertionIndex < groupMid) {
7614
7657
  insertionIndex = firstIdx;
@@ -7619,6 +7662,12 @@ class Tabs extends CompositeDisposable {
7619
7662
  // targetTabGroupId stays null
7620
7663
  break;
7621
7664
  }
7665
+ if (isGroupDrag && isJustBeforeGroup) {
7666
+ // Cursor is just before the group — accept this
7667
+ // index as-is. Groups can be dropped at the slot
7668
+ // immediately left of another group's first tab.
7669
+ break;
7670
+ }
7622
7671
  if (isJustBeforeGroup) {
7623
7672
  // Check whether only the source tab (or source group
7624
7673
  // tabs) sits between insertionIndex and firstIdx.
@@ -8460,6 +8509,7 @@ const PROPERTY_KEYS_DOCKVIEW = (() => {
8460
8509
  disableFloatingGroups: undefined,
8461
8510
  floatingGroupBounds: undefined,
8462
8511
  popoutUrl: undefined,
8512
+ nonce: undefined,
8463
8513
  defaultRenderer: undefined,
8464
8514
  defaultHeaderPosition: undefined,
8465
8515
  debug: undefined,
@@ -8475,6 +8525,7 @@ const PROPERTY_KEYS_DOCKVIEW = (() => {
8475
8525
  getTabContextMenuItems: undefined,
8476
8526
  getTabGroupChipContextMenuItems: undefined,
8477
8527
  createTabGroupChipComponent: undefined,
8528
+ createGroupDragGhostComponent: undefined,
8478
8529
  tabGroupColors: undefined,
8479
8530
  tabGroupAccent: undefined,
8480
8531
  };
@@ -9167,6 +9218,15 @@ class DockviewGroupPanelModel extends CompositeDisposable {
9167
9218
  refreshTabGroupAccent() {
9168
9219
  this.tabsContainer.refreshTabGroupAccent();
9169
9220
  }
9221
+ refreshWatermark() {
9222
+ var _a, _b;
9223
+ if (this.watermark) {
9224
+ this.watermark.element.remove();
9225
+ (_b = (_a = this.watermark).dispose) === null || _b === void 0 ? void 0 : _b.call(_a);
9226
+ this.watermark = undefined;
9227
+ }
9228
+ this.updateContainer();
9229
+ }
9170
9230
  getTabGroupForPanel(panelId) {
9171
9231
  return this._findTabGroupForPanel(panelId);
9172
9232
  }
@@ -9997,6 +10057,18 @@ class DockviewGroupPanelApiImpl extends GridviewPanelApiImpl {
9997
10057
  }
9998
10058
  return this._group.model.location;
9999
10059
  }
10060
+ get locked() {
10061
+ if (!this._group) {
10062
+ throw new Error(NOT_INITIALIZED_MESSAGE);
10063
+ }
10064
+ return this._group.locked;
10065
+ }
10066
+ set locked(value) {
10067
+ if (!this._group) {
10068
+ throw new Error(NOT_INITIALIZED_MESSAGE);
10069
+ }
10070
+ this._group.locked = value;
10071
+ }
10000
10072
  constructor(id, accessor) {
10001
10073
  super(id, '__dockviewgroup__');
10002
10074
  this.accessor = accessor;
@@ -10587,6 +10659,11 @@ class DockviewPanel extends CompositeDisposable {
10587
10659
  const didTitleChange = title !== this.title;
10588
10660
  if (didTitleChange) {
10589
10661
  this._title = title;
10662
+ // keep the view-model's cached init params in sync so that tab
10663
+ // renderers constructed lazily (e.g. the header overflow
10664
+ // dropdown via createTabRenderer) see the updated title
10665
+ // (#914).
10666
+ this.view.setTitle(title);
10590
10667
  this.api._onDidTitleChange.fire({ title });
10591
10668
  }
10592
10669
  }
@@ -10747,6 +10824,14 @@ class DockviewPanelModel {
10747
10824
  this.content.init(params);
10748
10825
  this.tab.init(Object.assign(Object.assign({}, params), { tabLocation: 'header' }));
10749
10826
  }
10827
+ setTitle(title) {
10828
+ // keep the cached init params in sync so that tab renderers created
10829
+ // lazily after the title changes (e.g. for the header overflow
10830
+ // dropdown) see the current title rather than the stale original.
10831
+ if (this._params) {
10832
+ this._params.title = title;
10833
+ }
10834
+ }
10750
10835
  layout(width, height) {
10751
10836
  var _a, _b;
10752
10837
  (_b = (_a = this.content).layout) === null || _b === void 0 ? void 0 : _b.call(_a, width, height);
@@ -11666,7 +11751,9 @@ class PopoutWindow extends CompositeDisposable {
11666
11751
  const externalDocument = externalWindow.document;
11667
11752
  externalDocument.title = document.title;
11668
11753
  externalDocument.body.appendChild(container);
11669
- addStyles(externalDocument, window.document.styleSheets);
11754
+ addStyles(externalDocument, window.document.styleSheets, {
11755
+ nonce: this.options.nonce,
11756
+ });
11670
11757
  /**
11671
11758
  * beforeunload must be registered after load for reasons I could not determine
11672
11759
  * otherwise the beforeunload event will not fire when the window is closed
@@ -12856,6 +12943,7 @@ class DockviewComponent extends BaseGrid {
12856
12943
  this._floatingGroups = [];
12857
12944
  this._popoutGroups = [];
12858
12945
  this._popoutRestorationPromise = Promise.resolve();
12946
+ this._popoutRestorationCleanups = new Set();
12859
12947
  this._onDidRemoveGroup = new Emitter();
12860
12948
  this.onDidRemoveGroup = this._onDidRemoveGroup.event;
12861
12949
  this._onDidAddGroup = new Emitter();
@@ -12961,6 +13049,14 @@ class DockviewComponent extends BaseGrid {
12961
13049
  this._bufferOnDidLayoutChange.fire();
12962
13050
  }), Disposable.from(() => {
12963
13051
  var _a;
13052
+ // Cancel any pending popout-restoration timers scheduled by
13053
+ // fromJSON so they don't open new browser windows after
13054
+ // dispose, and resolve their promises so callers awaiting
13055
+ // popoutRestorationPromise don't hang. See issue #851.
13056
+ for (const cleanup of [...this._popoutRestorationCleanups]) {
13057
+ cleanup();
13058
+ }
13059
+ this._popoutRestorationCleanups.clear();
12964
13060
  // iterate over a copy of the array since .dispose() mutates the original array
12965
13061
  for (const group of [...this._floatingGroups]) {
12966
13062
  group.dispose();
@@ -13057,7 +13153,7 @@ class DockviewComponent extends BaseGrid {
13057
13153
  return (_a = this._popoutPopupServices.get(group.id)) !== null && _a !== void 0 ? _a : this.popupService;
13058
13154
  }
13059
13155
  addPopoutGroup(itemToPopout, options) {
13060
- var _a, _b, _c, _d, _e;
13156
+ var _a, _b, _c, _d, _e, _f;
13061
13157
  if (itemToPopout instanceof DockviewGroupPanel &&
13062
13158
  itemToPopout.model.location.type === 'edge') {
13063
13159
  // edge groups are permanent structural elements and cannot be popped out
@@ -13092,6 +13188,7 @@ class DockviewComponent extends BaseGrid {
13092
13188
  height: box.height,
13093
13189
  onDidOpen: options === null || options === void 0 ? void 0 : options.onDidOpen,
13094
13190
  onWillClose: options === null || options === void 0 ? void 0 : options.onWillClose,
13191
+ nonce: (_f = this.options) === null || _f === void 0 ? void 0 : _f.nonce,
13095
13192
  });
13096
13193
  const popoutWindowDisposable = new CompositeDisposable(_window, _window.onDidClose(() => {
13097
13194
  popoutWindowDisposable.dispose();
@@ -13509,7 +13606,7 @@ class DockviewComponent extends BaseGrid {
13509
13606
  }
13510
13607
  }
13511
13608
  updateOptions(options) {
13512
- var _a, _b, _c;
13609
+ var _a, _b, _c, _d, _e;
13513
13610
  super.updateOptions(options);
13514
13611
  if ('floatingGroupBounds' in options) {
13515
13612
  for (const group of this._floatingGroups) {
@@ -13550,8 +13647,19 @@ class DockviewComponent extends BaseGrid {
13550
13647
  group.model.updateHeaderActions();
13551
13648
  }
13552
13649
  }
13650
+ if ('createWatermarkComponent' in options) {
13651
+ if (this._watermark) {
13652
+ this._watermark.element.parentElement.remove();
13653
+ (_d = (_c = this._watermark).dispose) === null || _d === void 0 ? void 0 : _d.call(_c);
13654
+ this._watermark = null;
13655
+ }
13656
+ this.updateWatermark();
13657
+ for (const group of this.groups) {
13658
+ group.model.refreshWatermark();
13659
+ }
13660
+ }
13553
13661
  if ('tabGroupColors' in options || 'tabGroupAccent' in options) {
13554
- this._tabGroupColorPalette.setEntries((_c = this._options.tabGroupColors) !== null && _c !== void 0 ? _c : DEFAULT_TAB_GROUP_COLORS);
13662
+ this._tabGroupColorPalette.setEntries((_e = this._options.tabGroupColors) !== null && _e !== void 0 ? _e : DEFAULT_TAB_GROUP_COLORS);
13555
13663
  this._tabGroupColorPalette.enabled =
13556
13664
  this._options.tabGroupAccent !== 'off';
13557
13665
  for (const group of this.groups) {
@@ -13977,7 +14085,23 @@ class DockviewComponent extends BaseGrid {
13977
14085
  const group = createGroupFromSerializedState(data);
13978
14086
  // Add a small delay for each popup after the first to avoid browser popup blocking
13979
14087
  const popoutPromise = new Promise((resolve) => {
13980
- setTimeout(() => {
14088
+ const cleanup = () => {
14089
+ this._popoutRestorationCleanups.delete(cleanup);
14090
+ clearTimeout(handle);
14091
+ resolve();
14092
+ };
14093
+ const handle = setTimeout(() => {
14094
+ this._popoutRestorationCleanups.delete(cleanup);
14095
+ // Guard against the component being disposed before
14096
+ // this timer fires. Under React StrictMode the
14097
+ // component is mounted -> disposed -> remounted, and
14098
+ // without this guard the first instance's queued
14099
+ // restoration would open a second popout window.
14100
+ // See issue #851.
14101
+ if (this.isDisposed) {
14102
+ resolve();
14103
+ return;
14104
+ }
13981
14105
  this.addPopoutGroup(group, {
13982
14106
  position: position !== null && position !== void 0 ? position : undefined,
13983
14107
  overridePopoutGroup: gridReferenceGroup
@@ -13990,6 +14114,7 @@ class DockviewComponent extends BaseGrid {
13990
14114
  });
13991
14115
  resolve();
13992
14116
  }, index * DESERIALIZATION_POPOUT_DELAY_MS); // 100ms delay between each popup
14117
+ this._popoutRestorationCleanups.add(cleanup);
13993
14118
  });
13994
14119
  popoutPromises.push(popoutPromise);
13995
14120
  });
@@ -14520,8 +14645,11 @@ class DockviewComponent extends BaseGrid {
14520
14645
  * the source group is a popout group with a single panel
14521
14646
  *
14522
14647
  * 1. remove the panel from the group without triggering any events
14523
- * 2. remove the popout group
14524
- * 3. create a new group at the requested location and add that panel
14648
+ * 2. remove the popout group — this may cascade-remove the empty
14649
+ * reference group it left behind in the main grid (see
14650
+ * doRemoveGroup for popout groups), which can shift grid indices
14651
+ * 3. recompute the target location now that the grid is stable
14652
+ * 4. create a new group at the recomputed location and add that panel
14525
14653
  */
14526
14654
  const popoutGroup = this._popoutGroups.find((group) => group.popoutGroup === sourceGroup);
14527
14655
  const removedPanel = this.movingLock(() => popoutGroup.popoutGroup.model.removePanel(popoutGroup.popoutGroup.panels[0], {
@@ -14529,7 +14657,8 @@ class DockviewComponent extends BaseGrid {
14529
14657
  skipSetActiveGroup: true,
14530
14658
  }));
14531
14659
  this.doRemoveGroup(sourceGroup, { skipActive: true });
14532
- const newGroup = this.createGroupAtLocation(targetLocation);
14660
+ const updatedTargetLocation = getRelativeLocation(this.gridview.orientation, getGridLocation(destinationGroup.element), destinationTarget);
14661
+ const newGroup = this.createGroupAtLocation(updatedTargetLocation);
14533
14662
  this.movingLock(() => newGroup.model.openPanel(removedPanel, {
14534
14663
  skipSetActive: true,
14535
14664
  }));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dockview-core",
3
- "version": "6.2.2",
3
+ "version": "6.3.0",
4
4
  "description": "Zero dependency layout manager supporting tabs, groups, grids and splitviews for vanilla TypeScript",
5
5
  "keywords": [
6
6
  "splitview",