dockview-core 6.2.1 → 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 (55) 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/overlay/overlayRenderContainer.js +13 -3
  21. package/dist/cjs/popoutWindow.d.ts +2 -0
  22. package/dist/cjs/popoutWindow.js +3 -1
  23. package/dist/dockview-core.js +169 -30
  24. package/dist/dockview-core.min.js +2 -2
  25. package/dist/dockview-core.min.js.map +1 -1
  26. package/dist/dockview-core.min.noStyle.js +2 -2
  27. package/dist/dockview-core.min.noStyle.js.map +1 -1
  28. package/dist/dockview-core.noStyle.js +169 -30
  29. package/dist/esm/api/dockviewGroupPanelApi.d.ts +10 -1
  30. package/dist/esm/api/dockviewGroupPanelApi.js +12 -0
  31. package/dist/esm/dnd/groupDragHandler.js +34 -12
  32. package/dist/esm/dockview/components/titlebar/tabGroupIndicator.js +2 -2
  33. package/dist/esm/dockview/components/titlebar/tabs.js +12 -2
  34. package/dist/esm/dockview/components/titlebar/voidContainer.js +6 -0
  35. package/dist/esm/dockview/dockviewComponent.d.ts +1 -0
  36. package/dist/esm/dockview/dockviewComponent.js +49 -7
  37. package/dist/esm/dockview/dockviewGroupPanelModel.d.ts +1 -0
  38. package/dist/esm/dockview/dockviewGroupPanelModel.js +9 -0
  39. package/dist/esm/dockview/dockviewPanel.js +5 -0
  40. package/dist/esm/dockview/dockviewPanelModel.d.ts +2 -0
  41. package/dist/esm/dockview/dockviewPanelModel.js +8 -0
  42. package/dist/esm/dockview/framework.d.ts +8 -0
  43. package/dist/esm/dockview/options.d.ts +13 -2
  44. package/dist/esm/dockview/options.js +2 -0
  45. package/dist/esm/dom.d.ts +5 -1
  46. package/dist/esm/dom.js +13 -2
  47. package/dist/esm/index.d.ts +1 -1
  48. package/dist/esm/overlay/overlayRenderContainer.js +13 -3
  49. package/dist/esm/popoutWindow.d.ts +2 -0
  50. package/dist/esm/popoutWindow.js +3 -1
  51. package/dist/package/main.cjs.js +169 -30
  52. package/dist/package/main.cjs.min.js +2 -2
  53. package/dist/package/main.esm.min.mjs +2 -2
  54. package/dist/package/main.esm.mjs +169 -30
  55. package/package.json +1 -1
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * dockview-core
3
- * @version 6.2.1
3
+ * @version 6.3.0
4
4
  * @link https://github.com/mathuo/dockview
5
5
  * @license MIT
6
6
  */
@@ -508,8 +508,10 @@
508
508
  function quasiDefaultPrevented(event) {
509
509
  return event[QUASI_PREVENT_DEFAULT_KEY];
510
510
  }
511
- function addStyles(document, styleSheetList) {
511
+ function addStyles(document, styleSheetList, options = {}) {
512
512
  const styleSheets = Array.from(styleSheetList);
513
+ const { nonce } = options;
514
+ const resolvedNonce = typeof nonce === 'function' ? nonce(document) : nonce;
513
515
  for (const styleSheet of styleSheets) {
514
516
  if (styleSheet.href) {
515
517
  const link = document.createElement('link');
@@ -517,6 +519,10 @@
517
519
  link.type = styleSheet.type;
518
520
  link.rel = 'stylesheet';
519
521
  document.head.appendChild(link);
522
+ // The <link> will load and apply its rules in the target
523
+ // document. Reading cssRules here would duplicate them
524
+ // (and throws for cross-origin sheets).
525
+ continue;
520
526
  }
521
527
  let cssTexts = [];
522
528
  try {
@@ -527,11 +533,16 @@
527
533
  catch (err) {
528
534
  console.warn('dockview: failed to access stylesheet rules due to security restrictions', err);
529
535
  }
536
+ const fragment = document.createDocumentFragment();
530
537
  for (const rule of cssTexts) {
531
538
  const style = document.createElement('style');
539
+ if (resolvedNonce) {
540
+ style.setAttribute('nonce', resolvedNonce);
541
+ }
532
542
  style.appendChild(document.createTextNode(rule));
533
- document.head.appendChild(style);
543
+ fragment.appendChild(style);
534
544
  }
545
+ document.head.appendChild(fragment);
535
546
  }
536
547
  }
537
548
  function getDomNodePagePosition(domNode) {
@@ -5523,19 +5534,41 @@
5523
5534
  const bgColor = style.getPropertyValue('--dv-activegroup-visiblepanel-tab-background-color');
5524
5535
  const color = style.getPropertyValue('--dv-activegroup-visiblepanel-tab-color');
5525
5536
  if (dataTransfer) {
5526
- const ghostElement = document.createElement('div');
5527
- ghostElement.style.backgroundColor = bgColor;
5528
- ghostElement.style.color = color;
5529
- ghostElement.style.padding = '2px 8px';
5530
- ghostElement.style.height = '24px';
5531
- ghostElement.style.fontSize = '11px';
5532
- ghostElement.style.lineHeight = '20px';
5533
- ghostElement.style.borderRadius = '12px';
5534
- ghostElement.style.position = 'absolute';
5535
- ghostElement.style.pointerEvents = 'none';
5536
- ghostElement.style.top = '-9999px';
5537
- ghostElement.textContent = `Multiple Panels (${this.group.size})`;
5537
+ const createGhost = this.accessor.options.createGroupDragGhostComponent;
5538
+ let ghostElement;
5539
+ let customRenderer;
5540
+ if (createGhost) {
5541
+ customRenderer = createGhost(this.group);
5542
+ customRenderer.init({
5543
+ group: this.group,
5544
+ api: this.accessor.api,
5545
+ });
5546
+ ghostElement = customRenderer.element;
5547
+ ghostElement.style.position = 'absolute';
5548
+ ghostElement.style.pointerEvents = 'none';
5549
+ ghostElement.style.top = '-9999px';
5550
+ }
5551
+ else {
5552
+ ghostElement = document.createElement('div');
5553
+ ghostElement.style.backgroundColor = bgColor;
5554
+ ghostElement.style.color = color;
5555
+ ghostElement.style.padding = '2px 8px';
5556
+ ghostElement.style.height = '24px';
5557
+ ghostElement.style.fontSize = '11px';
5558
+ ghostElement.style.lineHeight = '20px';
5559
+ ghostElement.style.borderRadius = '12px';
5560
+ ghostElement.style.position = 'absolute';
5561
+ ghostElement.style.pointerEvents = 'none';
5562
+ ghostElement.style.top = '-9999px';
5563
+ ghostElement.textContent = `Multiple Panels (${this.group.size})`;
5564
+ }
5538
5565
  addGhostImage(dataTransfer, ghostElement, { y: -10, x: 30 });
5566
+ if (customRenderer === null || customRenderer === void 0 ? void 0 : customRenderer.dispose) {
5567
+ // addGhostImage removes the element from the DOM on the next
5568
+ // tick; dispose the framework renderer on the same schedule.
5569
+ const renderer = customRenderer;
5570
+ setTimeout(() => { var _a; return (_a = renderer.dispose) === null || _a === void 0 ? void 0 : _a.call(renderer); }, 0);
5571
+ }
5539
5572
  }
5540
5573
  return {
5541
5574
  dispose: () => {
@@ -5568,6 +5601,12 @@
5568
5601
  this.dropTarget = new Droptarget(this._element, {
5569
5602
  acceptedTargetZones: ['center'],
5570
5603
  canDisplayOverlay: (event, position) => {
5604
+ if (this.group.api.locked) {
5605
+ // Dropping on the void/header space adds the panel
5606
+ // to this group, which `locked` is meant to prevent
5607
+ // (both `true` and `'no-drop-target'`).
5608
+ return false;
5609
+ }
5571
5610
  const data = getPanelData();
5572
5611
  if (data && this.accessor.id === data.viewId) {
5573
5612
  return true;
@@ -6149,7 +6188,7 @@
6149
6188
  let svg = underline.firstElementChild;
6150
6189
  let path;
6151
6190
  if (!svg || svg.tagName !== 'svg') {
6152
- underline.innerHTML = '';
6191
+ underline.replaceChildren();
6153
6192
  svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
6154
6193
  svg.style.display = 'block';
6155
6194
  path = document.createElementNS('http://www.w3.org/2000/svg', 'path');
@@ -6247,7 +6286,7 @@
6247
6286
  underline.style.display = '';
6248
6287
  // Clear any SVG content left over from a mode switch
6249
6288
  if (underline.firstElementChild) {
6250
- underline.innerHTML = '';
6289
+ underline.replaceChildren();
6251
6290
  }
6252
6291
  underline.style.backgroundColor = color;
6253
6292
  if (isVertical) {
@@ -7640,11 +7679,15 @@
7640
7679
  if (!isInsideRange && !isJustBeforeGroup) {
7641
7680
  continue;
7642
7681
  }
7643
- if (isGroupDrag) {
7682
+ if (isGroupDrag && isInsideRange) {
7644
7683
  // A group cannot be dropped inside another group.
7645
7684
  // Snap the insertion index to just before or just
7646
7685
  // after this group based on cursor position relative
7647
- // to the group's midpoint.
7686
+ // to the group's midpoint. Only applies when the
7687
+ // insertion would land *inside* the group — for
7688
+ // `isJustBeforeGroup`, the index is already outside
7689
+ // (immediately left of the group) and is a valid
7690
+ // drop position, so leave it untouched (issue #1264).
7648
7691
  const groupMid = (firstIdx + lastIdx + 1) / 2;
7649
7692
  if (insertionIndex < groupMid) {
7650
7693
  insertionIndex = firstIdx;
@@ -7655,6 +7698,12 @@
7655
7698
  // targetTabGroupId stays null
7656
7699
  break;
7657
7700
  }
7701
+ if (isGroupDrag && isJustBeforeGroup) {
7702
+ // Cursor is just before the group — accept this
7703
+ // index as-is. Groups can be dropped at the slot
7704
+ // immediately left of another group's first tab.
7705
+ break;
7706
+ }
7658
7707
  if (isJustBeforeGroup) {
7659
7708
  // Check whether only the source tab (or source group
7660
7709
  // tabs) sits between insertionIndex and firstIdx.
@@ -8496,6 +8545,7 @@
8496
8545
  disableFloatingGroups: undefined,
8497
8546
  floatingGroupBounds: undefined,
8498
8547
  popoutUrl: undefined,
8548
+ nonce: undefined,
8499
8549
  defaultRenderer: undefined,
8500
8550
  defaultHeaderPosition: undefined,
8501
8551
  debug: undefined,
@@ -8511,6 +8561,7 @@
8511
8561
  getTabContextMenuItems: undefined,
8512
8562
  getTabGroupChipContextMenuItems: undefined,
8513
8563
  createTabGroupChipComponent: undefined,
8564
+ createGroupDragGhostComponent: undefined,
8514
8565
  tabGroupColors: undefined,
8515
8566
  tabGroupAccent: undefined,
8516
8567
  };
@@ -9203,6 +9254,15 @@
9203
9254
  refreshTabGroupAccent() {
9204
9255
  this.tabsContainer.refreshTabGroupAccent();
9205
9256
  }
9257
+ refreshWatermark() {
9258
+ var _a, _b;
9259
+ if (this.watermark) {
9260
+ this.watermark.element.remove();
9261
+ (_b = (_a = this.watermark).dispose) === null || _b === void 0 ? void 0 : _b.call(_a);
9262
+ this.watermark = undefined;
9263
+ }
9264
+ this.updateContainer();
9265
+ }
9206
9266
  getTabGroupForPanel(panelId) {
9207
9267
  return this._findTabGroupForPanel(panelId);
9208
9268
  }
@@ -10033,6 +10093,18 @@
10033
10093
  }
10034
10094
  return this._group.model.location;
10035
10095
  }
10096
+ get locked() {
10097
+ if (!this._group) {
10098
+ throw new Error(NOT_INITIALIZED_MESSAGE);
10099
+ }
10100
+ return this._group.locked;
10101
+ }
10102
+ set locked(value) {
10103
+ if (!this._group) {
10104
+ throw new Error(NOT_INITIALIZED_MESSAGE);
10105
+ }
10106
+ this._group.locked = value;
10107
+ }
10036
10108
  constructor(id, accessor) {
10037
10109
  super(id, '__dockviewgroup__');
10038
10110
  this.accessor = accessor;
@@ -10623,6 +10695,11 @@
10623
10695
  const didTitleChange = title !== this.title;
10624
10696
  if (didTitleChange) {
10625
10697
  this._title = title;
10698
+ // keep the view-model's cached init params in sync so that tab
10699
+ // renderers constructed lazily (e.g. the header overflow
10700
+ // dropdown via createTabRenderer) see the updated title
10701
+ // (#914).
10702
+ this.view.setTitle(title);
10626
10703
  this.api._onDidTitleChange.fire({ title });
10627
10704
  }
10628
10705
  }
@@ -10783,6 +10860,14 @@
10783
10860
  this.content.init(params);
10784
10861
  this.tab.init(Object.assign(Object.assign({}, params), { tabLocation: 'header' }));
10785
10862
  }
10863
+ setTitle(title) {
10864
+ // keep the cached init params in sync so that tab renderers created
10865
+ // lazily after the title changes (e.g. for the header overflow
10866
+ // dropdown) see the current title rather than the stale original.
10867
+ if (this._params) {
10868
+ this._params.title = title;
10869
+ }
10870
+ }
10786
10871
  layout(width, height) {
10787
10872
  var _a, _b;
10788
10873
  (_b = (_a = this.content).layout) === null || _b === void 0 ? void 0 : _b.call(_a, width, height);
@@ -11422,10 +11507,20 @@
11422
11507
  focusContainer.style.top = `${top}px`;
11423
11508
  focusContainer.style.width = `${width}px`;
11424
11509
  focusContainer.style.height = `${height}px`;
11425
- // Reveal after the first position is applied (was hidden to
11426
- // prevent a flash at 0,0 before the initial layout fires).
11427
- if (focusContainer.style.visibility === 'hidden') {
11510
+ // Sync visibility/pointer-events with the panel's current
11511
+ // visibility at paint time. visibilityChanged() may have
11512
+ // flipped to hidden between scheduling this rAF and now;
11513
+ // unconditionally clearing `visibility:hidden` here would
11514
+ // leave a hidden panel visually visible at a stale position,
11515
+ // because onDidDimensionsChange skips non-visible panels and
11516
+ // never recomputes their box on subsequent resizes.
11517
+ if (panel.api.isVisible) {
11428
11518
  focusContainer.style.visibility = '';
11519
+ focusContainer.style.pointerEvents = '';
11520
+ }
11521
+ else {
11522
+ focusContainer.style.visibility = 'hidden';
11523
+ focusContainer.style.pointerEvents = 'none';
11429
11524
  }
11430
11525
  toggleClass(focusContainer, 'dv-render-overlay-float', panel.group.api.location.type === 'floating');
11431
11526
  });
@@ -11692,7 +11787,9 @@
11692
11787
  const externalDocument = externalWindow.document;
11693
11788
  externalDocument.title = document.title;
11694
11789
  externalDocument.body.appendChild(container);
11695
- addStyles(externalDocument, window.document.styleSheets);
11790
+ addStyles(externalDocument, window.document.styleSheets, {
11791
+ nonce: this.options.nonce,
11792
+ });
11696
11793
  /**
11697
11794
  * beforeunload must be registered after load for reasons I could not determine
11698
11795
  * otherwise the beforeunload event will not fire when the window is closed
@@ -12882,6 +12979,7 @@
12882
12979
  this._floatingGroups = [];
12883
12980
  this._popoutGroups = [];
12884
12981
  this._popoutRestorationPromise = Promise.resolve();
12982
+ this._popoutRestorationCleanups = new Set();
12885
12983
  this._onDidRemoveGroup = new Emitter();
12886
12984
  this.onDidRemoveGroup = this._onDidRemoveGroup.event;
12887
12985
  this._onDidAddGroup = new Emitter();
@@ -12987,6 +13085,14 @@
12987
13085
  this._bufferOnDidLayoutChange.fire();
12988
13086
  }), exports.DockviewDisposable.from(() => {
12989
13087
  var _a;
13088
+ // Cancel any pending popout-restoration timers scheduled by
13089
+ // fromJSON so they don't open new browser windows after
13090
+ // dispose, and resolve their promises so callers awaiting
13091
+ // popoutRestorationPromise don't hang. See issue #851.
13092
+ for (const cleanup of [...this._popoutRestorationCleanups]) {
13093
+ cleanup();
13094
+ }
13095
+ this._popoutRestorationCleanups.clear();
12990
13096
  // iterate over a copy of the array since .dispose() mutates the original array
12991
13097
  for (const group of [...this._floatingGroups]) {
12992
13098
  group.dispose();
@@ -13083,7 +13189,7 @@
13083
13189
  return (_a = this._popoutPopupServices.get(group.id)) !== null && _a !== void 0 ? _a : this.popupService;
13084
13190
  }
13085
13191
  addPopoutGroup(itemToPopout, options) {
13086
- var _a, _b, _c, _d, _e;
13192
+ var _a, _b, _c, _d, _e, _f;
13087
13193
  if (itemToPopout instanceof DockviewGroupPanel &&
13088
13194
  itemToPopout.model.location.type === 'edge') {
13089
13195
  // edge groups are permanent structural elements and cannot be popped out
@@ -13118,6 +13224,7 @@
13118
13224
  height: box.height,
13119
13225
  onDidOpen: options === null || options === void 0 ? void 0 : options.onDidOpen,
13120
13226
  onWillClose: options === null || options === void 0 ? void 0 : options.onWillClose,
13227
+ nonce: (_f = this.options) === null || _f === void 0 ? void 0 : _f.nonce,
13121
13228
  });
13122
13229
  const popoutWindowDisposable = new CompositeDisposable(_window, _window.onDidClose(() => {
13123
13230
  popoutWindowDisposable.dispose();
@@ -13535,7 +13642,7 @@
13535
13642
  }
13536
13643
  }
13537
13644
  updateOptions(options) {
13538
- var _a, _b, _c;
13645
+ var _a, _b, _c, _d, _e;
13539
13646
  super.updateOptions(options);
13540
13647
  if ('floatingGroupBounds' in options) {
13541
13648
  for (const group of this._floatingGroups) {
@@ -13576,8 +13683,19 @@
13576
13683
  group.model.updateHeaderActions();
13577
13684
  }
13578
13685
  }
13686
+ if ('createWatermarkComponent' in options) {
13687
+ if (this._watermark) {
13688
+ this._watermark.element.parentElement.remove();
13689
+ (_d = (_c = this._watermark).dispose) === null || _d === void 0 ? void 0 : _d.call(_c);
13690
+ this._watermark = null;
13691
+ }
13692
+ this.updateWatermark();
13693
+ for (const group of this.groups) {
13694
+ group.model.refreshWatermark();
13695
+ }
13696
+ }
13579
13697
  if ('tabGroupColors' in options || 'tabGroupAccent' in options) {
13580
- this._tabGroupColorPalette.setEntries((_c = this._options.tabGroupColors) !== null && _c !== void 0 ? _c : DEFAULT_TAB_GROUP_COLORS);
13698
+ this._tabGroupColorPalette.setEntries((_e = this._options.tabGroupColors) !== null && _e !== void 0 ? _e : DEFAULT_TAB_GROUP_COLORS);
13581
13699
  this._tabGroupColorPalette.enabled =
13582
13700
  this._options.tabGroupAccent !== 'off';
13583
13701
  for (const group of this.groups) {
@@ -14003,7 +14121,23 @@
14003
14121
  const group = createGroupFromSerializedState(data);
14004
14122
  // Add a small delay for each popup after the first to avoid browser popup blocking
14005
14123
  const popoutPromise = new Promise((resolve) => {
14006
- setTimeout(() => {
14124
+ const cleanup = () => {
14125
+ this._popoutRestorationCleanups.delete(cleanup);
14126
+ clearTimeout(handle);
14127
+ resolve();
14128
+ };
14129
+ const handle = setTimeout(() => {
14130
+ this._popoutRestorationCleanups.delete(cleanup);
14131
+ // Guard against the component being disposed before
14132
+ // this timer fires. Under React StrictMode the
14133
+ // component is mounted -> disposed -> remounted, and
14134
+ // without this guard the first instance's queued
14135
+ // restoration would open a second popout window.
14136
+ // See issue #851.
14137
+ if (this.isDisposed) {
14138
+ resolve();
14139
+ return;
14140
+ }
14007
14141
  this.addPopoutGroup(group, {
14008
14142
  position: position !== null && position !== void 0 ? position : undefined,
14009
14143
  overridePopoutGroup: gridReferenceGroup
@@ -14016,6 +14150,7 @@
14016
14150
  });
14017
14151
  resolve();
14018
14152
  }, index * DESERIALIZATION_POPOUT_DELAY_MS); // 100ms delay between each popup
14153
+ this._popoutRestorationCleanups.add(cleanup);
14019
14154
  });
14020
14155
  popoutPromises.push(popoutPromise);
14021
14156
  });
@@ -14546,8 +14681,11 @@
14546
14681
  * the source group is a popout group with a single panel
14547
14682
  *
14548
14683
  * 1. remove the panel from the group without triggering any events
14549
- * 2. remove the popout group
14550
- * 3. create a new group at the requested location and add that panel
14684
+ * 2. remove the popout group — this may cascade-remove the empty
14685
+ * reference group it left behind in the main grid (see
14686
+ * doRemoveGroup for popout groups), which can shift grid indices
14687
+ * 3. recompute the target location now that the grid is stable
14688
+ * 4. create a new group at the recomputed location and add that panel
14551
14689
  */
14552
14690
  const popoutGroup = this._popoutGroups.find((group) => group.popoutGroup === sourceGroup);
14553
14691
  const removedPanel = this.movingLock(() => popoutGroup.popoutGroup.model.removePanel(popoutGroup.popoutGroup.panels[0], {
@@ -14555,7 +14693,8 @@
14555
14693
  skipSetActiveGroup: true,
14556
14694
  }));
14557
14695
  this.doRemoveGroup(sourceGroup, { skipActive: true });
14558
- const newGroup = this.createGroupAtLocation(targetLocation);
14696
+ const updatedTargetLocation = getRelativeLocation(this.gridview.orientation, getGridLocation(destinationGroup.element), destinationTarget);
14697
+ const newGroup = this.createGroupAtLocation(updatedTargetLocation);
14559
14698
  this.movingLock(() => newGroup.model.openPanel(removedPanel, {
14560
14699
  skipSetActive: true,
14561
14700
  }));