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
@@ -272,6 +272,7 @@ export declare class DockviewGroupPanelModel extends CompositeDisposable impleme
272
272
  getTabGroups(): readonly ITabGroup[];
273
273
  updateTabGroups(): void;
274
274
  refreshTabGroupAccent(): void;
275
+ refreshWatermark(): void;
275
276
  getTabGroupForPanel(panelId: string): ITabGroup | undefined;
276
277
  private _findTabGroupForPanel;
277
278
  private _removeTabGroupInternal;
@@ -721,6 +721,15 @@ var DockviewGroupPanelModel = /** @class */ (function (_super) {
721
721
  DockviewGroupPanelModel.prototype.refreshTabGroupAccent = function () {
722
722
  this.tabsContainer.refreshTabGroupAccent();
723
723
  };
724
+ DockviewGroupPanelModel.prototype.refreshWatermark = function () {
725
+ var _a, _b;
726
+ if (this.watermark) {
727
+ this.watermark.element.remove();
728
+ (_b = (_a = this.watermark).dispose) === null || _b === void 0 ? void 0 : _b.call(_a);
729
+ this.watermark = undefined;
730
+ }
731
+ this.updateContainer();
732
+ };
724
733
  DockviewGroupPanelModel.prototype.getTabGroupForPanel = function (panelId) {
725
734
  return this._findTabGroupForPanel(panelId);
726
735
  };
@@ -159,6 +159,11 @@ var DockviewPanel = /** @class */ (function (_super) {
159
159
  var didTitleChange = title !== this.title;
160
160
  if (didTitleChange) {
161
161
  this._title = title;
162
+ // keep the view-model's cached init params in sync so that tab
163
+ // renderers constructed lazily (e.g. the header overflow
164
+ // dropdown via createTabRenderer) see the updated title
165
+ // (#914).
166
+ this.view.setTitle(title);
162
167
  this.api._onDidTitleChange.fire({ title: title });
163
168
  }
164
169
  };
@@ -12,6 +12,7 @@ export interface IDockviewPanelModel extends IDisposable {
12
12
  layout(width: number, height: number): void;
13
13
  init(params: GroupPanelPartInitParameters): void;
14
14
  createTabRenderer(tabLocation: TabLocation): ITabRenderer;
15
+ setTitle(title: string): void;
15
16
  }
16
17
  export declare class DockviewPanelModel implements IDockviewPanelModel {
17
18
  private readonly accessor;
@@ -27,6 +28,7 @@ export declare class DockviewPanelModel implements IDockviewPanelModel {
27
28
  constructor(accessor: IDockviewComponent, id: string, contentComponent: string, tabComponent?: string | undefined);
28
29
  createTabRenderer(tabLocation: TabLocation): ITabRenderer;
29
30
  init(params: GroupPanelPartInitParameters): void;
31
+ setTitle(title: string): void;
30
32
  layout(width: number, height: number): void;
31
33
  update(event: PanelUpdateEvent): void;
32
34
  dispose(): void;
@@ -52,6 +52,14 @@ var DockviewPanelModel = /** @class */ (function () {
52
52
  this.content.init(params);
53
53
  this.tab.init(__assign(__assign({}, params), { tabLocation: 'header' }));
54
54
  };
55
+ DockviewPanelModel.prototype.setTitle = function (title) {
56
+ // keep the cached init params in sync so that tab renderers created
57
+ // lazily after the title changes (e.g. for the header overflow
58
+ // dropdown) see the current title rather than the stale original.
59
+ if (this._params) {
60
+ this._params.title = title;
61
+ }
62
+ };
55
63
  DockviewPanelModel.prototype.layout = function (width, height) {
56
64
  var _a, _b;
57
65
  (_b = (_a = this.content).layout) === null || _b === void 0 ? void 0 : _b.call(_a, width, height);
@@ -55,3 +55,11 @@ export interface ITabGroupChipRenderer {
55
55
  }): void;
56
56
  dispose(): void;
57
57
  }
58
+ export interface IGroupDragGhostRenderer {
59
+ readonly element: HTMLElement;
60
+ init(params: {
61
+ group: IDockviewGroupPanel;
62
+ api: DockviewApi;
63
+ }): void;
64
+ dispose?(): void;
65
+ }
@@ -11,12 +11,14 @@ import { GroupOptions } from './dockviewGroupPanelModel';
11
11
  import { DockviewGroupDropLocation } from './events';
12
12
  import { IDockviewPanel } from './dockviewPanel';
13
13
  import { DockviewPanelRenderer } from '../overlay/overlayRenderContainer';
14
- import { IGroupHeaderProps, ITabGroupChipRenderer } from './framework';
14
+ import { IGroupDragGhostRenderer, IGroupHeaderProps, ITabGroupChipRenderer } from './framework';
15
15
  import { FloatingGroupOptions } from './dockviewComponent';
16
16
  import { Contraints } from '../gridview/gridviewPanel';
17
17
  import { AcceptableEvent, IAcceptableEvent } from '../events';
18
18
  import { DockviewTheme } from './theme';
19
19
  import { ITabGroup } from './tabGroup';
20
+ import { CspNonceProvider } from '../dom';
21
+ export { CspNonceProvider };
20
22
  import { DockviewTabGroupColorEntry } from './tabGroupAccent';
21
23
  export interface IHeaderActionsRenderer extends IDisposable {
22
24
  readonly element: HTMLElement;
@@ -89,6 +91,7 @@ export interface DockviewOptions {
89
91
  minimumWidthWithinViewport?: number;
90
92
  };
91
93
  popoutUrl?: string;
94
+ nonce?: CspNonceProvider;
92
95
  defaultRenderer?: DockviewPanelRenderer;
93
96
  defaultHeaderPosition?: DockviewHeaderPosition;
94
97
  debug?: boolean;
@@ -140,6 +143,15 @@ export interface DockviewOptions {
140
143
  * If not provided, the default chip renderer is used.
141
144
  */
142
145
  createTabGroupChipComponent?: (tabGroup: ITabGroup) => ITabGroupChipRenderer;
146
+ /**
147
+ * Factory to create the custom ghost element shown while dragging a
148
+ * group of panels (the small floating chip that follows the cursor).
149
+ *
150
+ * If not provided, a default ghost rendering `"Multiple Panels (N)"`
151
+ * is used. Supplying a factory replaces the entire default ghost,
152
+ * enabling i18n / custom visuals.
153
+ */
154
+ createGroupDragGhostComponent?: (group: DockviewGroupPanel) => IGroupDragGhostRenderer;
143
155
  /**
144
156
  * Replace the built-in tab group color palette with a user-defined list.
145
157
  *
@@ -292,4 +304,3 @@ export interface MovementOptions extends MovementOptions2 {
292
304
  includePanel?: boolean;
293
305
  group?: DockviewGroupPanel;
294
306
  }
295
- export {};
@@ -47,6 +47,7 @@ exports.PROPERTY_KEYS_DOCKVIEW = (function () {
47
47
  disableFloatingGroups: undefined,
48
48
  floatingGroupBounds: undefined,
49
49
  popoutUrl: undefined,
50
+ nonce: undefined,
50
51
  defaultRenderer: undefined,
51
52
  defaultHeaderPosition: undefined,
52
53
  debug: undefined,
@@ -62,6 +63,7 @@ exports.PROPERTY_KEYS_DOCKVIEW = (function () {
62
63
  getTabContextMenuItems: undefined,
63
64
  getTabGroupChipContextMenuItems: undefined,
64
65
  createTabGroupChipComponent: undefined,
66
+ createGroupDragGhostComponent: undefined,
65
67
  tabGroupColors: undefined,
66
68
  tabGroupAccent: undefined,
67
69
  };
package/dist/cjs/dom.d.ts CHANGED
@@ -24,7 +24,11 @@ export interface IFocusTracker extends IDisposable {
24
24
  export declare function trackFocus(element: HTMLElement): IFocusTracker;
25
25
  export declare function quasiPreventDefault(event: Event): void;
26
26
  export declare function quasiDefaultPrevented(event: Event): boolean;
27
- export declare function addStyles(document: Document, styleSheetList: StyleSheetList): void;
27
+ export type CspNonceProvider = string | ((targetDocument: Document) => string | undefined);
28
+ export interface AddStylesOptions {
29
+ nonce?: CspNonceProvider;
30
+ }
31
+ export declare function addStyles(document: Document, styleSheetList: StyleSheetList, options?: AddStylesOptions): void;
28
32
  export declare function getDomNodePagePosition(domNode: Element): {
29
33
  left: number;
30
34
  top: number;
package/dist/cjs/dom.js CHANGED
@@ -218,9 +218,12 @@ function quasiPreventDefault(event) {
218
218
  function quasiDefaultPrevented(event) {
219
219
  return event[QUASI_PREVENT_DEFAULT_KEY];
220
220
  }
221
- function addStyles(document, styleSheetList) {
221
+ function addStyles(document, styleSheetList, options) {
222
222
  var e_3, _a, e_4, _b;
223
+ if (options === void 0) { options = {}; }
223
224
  var styleSheets = Array.from(styleSheetList);
225
+ var nonce = options.nonce;
226
+ var resolvedNonce = typeof nonce === 'function' ? nonce(document) : nonce;
224
227
  try {
225
228
  for (var styleSheets_1 = __values(styleSheets), styleSheets_1_1 = styleSheets_1.next(); !styleSheets_1_1.done; styleSheets_1_1 = styleSheets_1.next()) {
226
229
  var styleSheet = styleSheets_1_1.value;
@@ -230,6 +233,10 @@ function addStyles(document, styleSheetList) {
230
233
  link.type = styleSheet.type;
231
234
  link.rel = 'stylesheet';
232
235
  document.head.appendChild(link);
236
+ // The <link> will load and apply its rules in the target
237
+ // document. Reading cssRules here would duplicate them
238
+ // (and throws for cross-origin sheets).
239
+ continue;
233
240
  }
234
241
  var cssTexts = [];
235
242
  try {
@@ -240,12 +247,16 @@ function addStyles(document, styleSheetList) {
240
247
  catch (err) {
241
248
  console.warn('dockview: failed to access stylesheet rules due to security restrictions', err);
242
249
  }
250
+ var fragment = document.createDocumentFragment();
243
251
  try {
244
252
  for (var cssTexts_1 = (e_4 = void 0, __values(cssTexts)), cssTexts_1_1 = cssTexts_1.next(); !cssTexts_1_1.done; cssTexts_1_1 = cssTexts_1.next()) {
245
253
  var rule = cssTexts_1_1.value;
246
254
  var style = document.createElement('style');
255
+ if (resolvedNonce) {
256
+ style.setAttribute('nonce', resolvedNonce);
257
+ }
247
258
  style.appendChild(document.createTextNode(rule));
248
- document.head.appendChild(style);
259
+ fragment.appendChild(style);
249
260
  }
250
261
  }
251
262
  catch (e_4_1) { e_4 = { error: e_4_1 }; }
@@ -255,6 +266,7 @@ function addStyles(document, styleSheetList) {
255
266
  }
256
267
  finally { if (e_4) throw e_4.error; }
257
268
  }
269
+ document.head.appendChild(fragment);
258
270
  }
259
271
  }
260
272
  catch (e_3_1) { e_3 = { error: e_3_1 }; }
@@ -21,7 +21,7 @@ export { DockviewWillShowOverlayLocationEvent, DockviewTabGroupChangeEvent, Dock
21
21
  export { TabDragEvent, GroupDragEvent, } from './dockview/components/titlebar/tabsContainer';
22
22
  export * from './dockview/types';
23
23
  export * from './dockview/dockviewGroupPanel';
24
- export { IGroupPanelBaseProps, IDockviewPanelHeaderProps, IDockviewPanelProps, IDockviewHeaderActionsProps, IGroupHeaderProps, IWatermarkPanelProps, DockviewReadyEvent, ITabGroupChipRenderer, } from './dockview/framework';
24
+ export { IGroupPanelBaseProps, IDockviewPanelHeaderProps, IDockviewPanelProps, IDockviewHeaderActionsProps, IGroupHeaderProps, IWatermarkPanelProps, DockviewReadyEvent, ITabGroupChipRenderer, IGroupDragGhostRenderer, } from './dockview/framework';
25
25
  export * from './dockview/options';
26
26
  export * from './dockview/theme';
27
27
  export * from './dockview/dockviewPanel';
@@ -1,3 +1,4 @@
1
+ import { CspNonceProvider } from './dom';
1
2
  import { CompositeDisposable } from './lifecycle';
2
3
  import { Box } from './types';
3
4
  export type PopoutWindowOptions = {
@@ -10,6 +11,7 @@ export type PopoutWindowOptions = {
10
11
  id: string;
11
12
  window: Window;
12
13
  }) => void;
14
+ nonce?: CspNonceProvider;
13
15
  } & Box;
14
16
  /**
15
17
  * Reject popout URLs that aren't same-origin http(s). Blocks `javascript:`,
@@ -204,7 +204,9 @@ var PopoutWindow = /** @class */ (function (_super) {
204
204
  var externalDocument = externalWindow.document;
205
205
  externalDocument.title = document.title;
206
206
  externalDocument.body.appendChild(container);
207
- (0, dom_1.addStyles)(externalDocument, window.document.styleSheets);
207
+ (0, dom_1.addStyles)(externalDocument, window.document.styleSheets, {
208
+ nonce: _this.options.nonce,
209
+ });
208
210
  /**
209
211
  * beforeunload must be registered after load for reasons I could not determine
210
212
  * otherwise the beforeunload event will not fire when the window is closed
@@ -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
  */
@@ -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);
@@ -11702,7 +11787,9 @@
11702
11787
  const externalDocument = externalWindow.document;
11703
11788
  externalDocument.title = document.title;
11704
11789
  externalDocument.body.appendChild(container);
11705
- addStyles(externalDocument, window.document.styleSheets);
11790
+ addStyles(externalDocument, window.document.styleSheets, {
11791
+ nonce: this.options.nonce,
11792
+ });
11706
11793
  /**
11707
11794
  * beforeunload must be registered after load for reasons I could not determine
11708
11795
  * otherwise the beforeunload event will not fire when the window is closed
@@ -12892,6 +12979,7 @@
12892
12979
  this._floatingGroups = [];
12893
12980
  this._popoutGroups = [];
12894
12981
  this._popoutRestorationPromise = Promise.resolve();
12982
+ this._popoutRestorationCleanups = new Set();
12895
12983
  this._onDidRemoveGroup = new Emitter();
12896
12984
  this.onDidRemoveGroup = this._onDidRemoveGroup.event;
12897
12985
  this._onDidAddGroup = new Emitter();
@@ -12997,6 +13085,14 @@
12997
13085
  this._bufferOnDidLayoutChange.fire();
12998
13086
  }), exports.DockviewDisposable.from(() => {
12999
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();
13000
13096
  // iterate over a copy of the array since .dispose() mutates the original array
13001
13097
  for (const group of [...this._floatingGroups]) {
13002
13098
  group.dispose();
@@ -13093,7 +13189,7 @@
13093
13189
  return (_a = this._popoutPopupServices.get(group.id)) !== null && _a !== void 0 ? _a : this.popupService;
13094
13190
  }
13095
13191
  addPopoutGroup(itemToPopout, options) {
13096
- var _a, _b, _c, _d, _e;
13192
+ var _a, _b, _c, _d, _e, _f;
13097
13193
  if (itemToPopout instanceof DockviewGroupPanel &&
13098
13194
  itemToPopout.model.location.type === 'edge') {
13099
13195
  // edge groups are permanent structural elements and cannot be popped out
@@ -13128,6 +13224,7 @@
13128
13224
  height: box.height,
13129
13225
  onDidOpen: options === null || options === void 0 ? void 0 : options.onDidOpen,
13130
13226
  onWillClose: options === null || options === void 0 ? void 0 : options.onWillClose,
13227
+ nonce: (_f = this.options) === null || _f === void 0 ? void 0 : _f.nonce,
13131
13228
  });
13132
13229
  const popoutWindowDisposable = new CompositeDisposable(_window, _window.onDidClose(() => {
13133
13230
  popoutWindowDisposable.dispose();
@@ -13545,7 +13642,7 @@
13545
13642
  }
13546
13643
  }
13547
13644
  updateOptions(options) {
13548
- var _a, _b, _c;
13645
+ var _a, _b, _c, _d, _e;
13549
13646
  super.updateOptions(options);
13550
13647
  if ('floatingGroupBounds' in options) {
13551
13648
  for (const group of this._floatingGroups) {
@@ -13586,8 +13683,19 @@
13586
13683
  group.model.updateHeaderActions();
13587
13684
  }
13588
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
+ }
13589
13697
  if ('tabGroupColors' in options || 'tabGroupAccent' in options) {
13590
- 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);
13591
13699
  this._tabGroupColorPalette.enabled =
13592
13700
  this._options.tabGroupAccent !== 'off';
13593
13701
  for (const group of this.groups) {
@@ -14013,7 +14121,23 @@
14013
14121
  const group = createGroupFromSerializedState(data);
14014
14122
  // Add a small delay for each popup after the first to avoid browser popup blocking
14015
14123
  const popoutPromise = new Promise((resolve) => {
14016
- 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
+ }
14017
14141
  this.addPopoutGroup(group, {
14018
14142
  position: position !== null && position !== void 0 ? position : undefined,
14019
14143
  overridePopoutGroup: gridReferenceGroup
@@ -14026,6 +14150,7 @@
14026
14150
  });
14027
14151
  resolve();
14028
14152
  }, index * DESERIALIZATION_POPOUT_DELAY_MS); // 100ms delay between each popup
14153
+ this._popoutRestorationCleanups.add(cleanup);
14029
14154
  });
14030
14155
  popoutPromises.push(popoutPromise);
14031
14156
  });
@@ -14556,8 +14681,11 @@
14556
14681
  * the source group is a popout group with a single panel
14557
14682
  *
14558
14683
  * 1. remove the panel from the group without triggering any events
14559
- * 2. remove the popout group
14560
- * 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
14561
14689
  */
14562
14690
  const popoutGroup = this._popoutGroups.find((group) => group.popoutGroup === sourceGroup);
14563
14691
  const removedPanel = this.movingLock(() => popoutGroup.popoutGroup.model.removePanel(popoutGroup.popoutGroup.panels[0], {
@@ -14565,7 +14693,8 @@
14565
14693
  skipSetActiveGroup: true,
14566
14694
  }));
14567
14695
  this.doRemoveGroup(sourceGroup, { skipActive: true });
14568
- const newGroup = this.createGroupAtLocation(targetLocation);
14696
+ const updatedTargetLocation = getRelativeLocation(this.gridview.orientation, getGridLocation(destinationGroup.element), destinationTarget);
14697
+ const newGroup = this.createGroupAtLocation(updatedTargetLocation);
14569
14698
  this.movingLock(() => newGroup.model.openPanel(removedPanel, {
14570
14699
  skipSetActive: true,
14571
14700
  }));