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
@@ -162,6 +162,7 @@ export class DockviewComponent extends BaseGrid {
162
162
  this._floatingGroups = [];
163
163
  this._popoutGroups = [];
164
164
  this._popoutRestorationPromise = Promise.resolve();
165
+ this._popoutRestorationCleanups = new Set();
165
166
  this._onDidRemoveGroup = new Emitter();
166
167
  this.onDidRemoveGroup = this._onDidRemoveGroup.event;
167
168
  this._onDidAddGroup = new Emitter();
@@ -267,6 +268,14 @@ export class DockviewComponent extends BaseGrid {
267
268
  this._bufferOnDidLayoutChange.fire();
268
269
  }), Disposable.from(() => {
269
270
  var _a;
271
+ // Cancel any pending popout-restoration timers scheduled by
272
+ // fromJSON so they don't open new browser windows after
273
+ // dispose, and resolve their promises so callers awaiting
274
+ // popoutRestorationPromise don't hang. See issue #851.
275
+ for (const cleanup of [...this._popoutRestorationCleanups]) {
276
+ cleanup();
277
+ }
278
+ this._popoutRestorationCleanups.clear();
270
279
  // iterate over a copy of the array since .dispose() mutates the original array
271
280
  for (const group of [...this._floatingGroups]) {
272
281
  group.dispose();
@@ -366,7 +375,7 @@ export class DockviewComponent extends BaseGrid {
366
375
  return (_a = this._popoutPopupServices.get(group.id)) !== null && _a !== void 0 ? _a : this.popupService;
367
376
  }
368
377
  addPopoutGroup(itemToPopout, options) {
369
- var _a, _b, _c, _d, _e;
378
+ var _a, _b, _c, _d, _e, _f;
370
379
  if (itemToPopout instanceof DockviewGroupPanel &&
371
380
  itemToPopout.model.location.type === 'edge') {
372
381
  // edge groups are permanent structural elements and cannot be popped out
@@ -401,6 +410,7 @@ export class DockviewComponent extends BaseGrid {
401
410
  height: box.height,
402
411
  onDidOpen: options === null || options === void 0 ? void 0 : options.onDidOpen,
403
412
  onWillClose: options === null || options === void 0 ? void 0 : options.onWillClose,
413
+ nonce: (_f = this.options) === null || _f === void 0 ? void 0 : _f.nonce,
404
414
  });
405
415
  const popoutWindowDisposable = new CompositeDisposable(_window, _window.onDidClose(() => {
406
416
  popoutWindowDisposable.dispose();
@@ -820,7 +830,7 @@ export class DockviewComponent extends BaseGrid {
820
830
  }
821
831
  }
822
832
  updateOptions(options) {
823
- var _a, _b, _c;
833
+ var _a, _b, _c, _d, _e;
824
834
  super.updateOptions(options);
825
835
  if ('floatingGroupBounds' in options) {
826
836
  for (const group of this._floatingGroups) {
@@ -861,8 +871,19 @@ export class DockviewComponent extends BaseGrid {
861
871
  group.model.updateHeaderActions();
862
872
  }
863
873
  }
874
+ if ('createWatermarkComponent' in options) {
875
+ if (this._watermark) {
876
+ this._watermark.element.parentElement.remove();
877
+ (_d = (_c = this._watermark).dispose) === null || _d === void 0 ? void 0 : _d.call(_c);
878
+ this._watermark = null;
879
+ }
880
+ this.updateWatermark();
881
+ for (const group of this.groups) {
882
+ group.model.refreshWatermark();
883
+ }
884
+ }
864
885
  if ('tabGroupColors' in options || 'tabGroupAccent' in options) {
865
- this._tabGroupColorPalette.setEntries((_c = this._options.tabGroupColors) !== null && _c !== void 0 ? _c : DEFAULT_TAB_GROUP_COLORS);
886
+ this._tabGroupColorPalette.setEntries((_e = this._options.tabGroupColors) !== null && _e !== void 0 ? _e : DEFAULT_TAB_GROUP_COLORS);
866
887
  this._tabGroupColorPalette.enabled =
867
888
  this._options.tabGroupAccent !== 'off';
868
889
  for (const group of this.groups) {
@@ -1288,7 +1309,23 @@ export class DockviewComponent extends BaseGrid {
1288
1309
  const group = createGroupFromSerializedState(data);
1289
1310
  // Add a small delay for each popup after the first to avoid browser popup blocking
1290
1311
  const popoutPromise = new Promise((resolve) => {
1291
- setTimeout(() => {
1312
+ const cleanup = () => {
1313
+ this._popoutRestorationCleanups.delete(cleanup);
1314
+ clearTimeout(handle);
1315
+ resolve();
1316
+ };
1317
+ const handle = setTimeout(() => {
1318
+ this._popoutRestorationCleanups.delete(cleanup);
1319
+ // Guard against the component being disposed before
1320
+ // this timer fires. Under React StrictMode the
1321
+ // component is mounted -> disposed -> remounted, and
1322
+ // without this guard the first instance's queued
1323
+ // restoration would open a second popout window.
1324
+ // See issue #851.
1325
+ if (this.isDisposed) {
1326
+ resolve();
1327
+ return;
1328
+ }
1292
1329
  this.addPopoutGroup(group, {
1293
1330
  position: position !== null && position !== void 0 ? position : undefined,
1294
1331
  overridePopoutGroup: gridReferenceGroup
@@ -1301,6 +1338,7 @@ export class DockviewComponent extends BaseGrid {
1301
1338
  });
1302
1339
  resolve();
1303
1340
  }, index * DESERIALIZATION_POPOUT_DELAY_MS); // 100ms delay between each popup
1341
+ this._popoutRestorationCleanups.add(cleanup);
1304
1342
  });
1305
1343
  popoutPromises.push(popoutPromise);
1306
1344
  });
@@ -1831,8 +1869,11 @@ export class DockviewComponent extends BaseGrid {
1831
1869
  * the source group is a popout group with a single panel
1832
1870
  *
1833
1871
  * 1. remove the panel from the group without triggering any events
1834
- * 2. remove the popout group
1835
- * 3. create a new group at the requested location and add that panel
1872
+ * 2. remove the popout group — this may cascade-remove the empty
1873
+ * reference group it left behind in the main grid (see
1874
+ * doRemoveGroup for popout groups), which can shift grid indices
1875
+ * 3. recompute the target location now that the grid is stable
1876
+ * 4. create a new group at the recomputed location and add that panel
1836
1877
  */
1837
1878
  const popoutGroup = this._popoutGroups.find((group) => group.popoutGroup === sourceGroup);
1838
1879
  const removedPanel = this.movingLock(() => popoutGroup.popoutGroup.model.removePanel(popoutGroup.popoutGroup.panels[0], {
@@ -1840,7 +1881,8 @@ export class DockviewComponent extends BaseGrid {
1840
1881
  skipSetActiveGroup: true,
1841
1882
  }));
1842
1883
  this.doRemoveGroup(sourceGroup, { skipActive: true });
1843
- const newGroup = this.createGroupAtLocation(targetLocation);
1884
+ const updatedTargetLocation = getRelativeLocation(this.gridview.orientation, getGridLocation(destinationGroup.element), destinationTarget);
1885
+ const newGroup = this.createGroupAtLocation(updatedTargetLocation);
1844
1886
  this.movingLock(() => newGroup.model.openPanel(removedPanel, {
1845
1887
  skipSetActive: true,
1846
1888
  }));
@@ -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;
@@ -527,6 +527,15 @@ export class DockviewGroupPanelModel extends CompositeDisposable {
527
527
  refreshTabGroupAccent() {
528
528
  this.tabsContainer.refreshTabGroupAccent();
529
529
  }
530
+ refreshWatermark() {
531
+ var _a, _b;
532
+ if (this.watermark) {
533
+ this.watermark.element.remove();
534
+ (_b = (_a = this.watermark).dispose) === null || _b === void 0 ? void 0 : _b.call(_a);
535
+ this.watermark = undefined;
536
+ }
537
+ this.updateContainer();
538
+ }
530
539
  getTabGroupForPanel(panelId) {
531
540
  return this._findTabGroupForPanel(panelId);
532
541
  }
@@ -85,6 +85,11 @@ export class DockviewPanel extends CompositeDisposable {
85
85
  const didTitleChange = title !== this.title;
86
86
  if (didTitleChange) {
87
87
  this._title = title;
88
+ // keep the view-model's cached init params in sync so that tab
89
+ // renderers constructed lazily (e.g. the header overflow
90
+ // dropdown via createTabRenderer) see the updated title
91
+ // (#914).
92
+ this.view.setTitle(title);
88
93
  this.api._onDidTitleChange.fire({ title });
89
94
  }
90
95
  }
@@ -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;
@@ -30,6 +30,14 @@ export class DockviewPanelModel {
30
30
  this.content.init(params);
31
31
  this.tab.init(Object.assign(Object.assign({}, params), { tabLocation: 'header' }));
32
32
  }
33
+ setTitle(title) {
34
+ // keep the cached init params in sync so that tab renderers created
35
+ // lazily after the title changes (e.g. for the header overflow
36
+ // dropdown) see the current title rather than the stale original.
37
+ if (this._params) {
38
+ this._params.title = title;
39
+ }
40
+ }
33
41
  layout(width, height) {
34
42
  var _a, _b;
35
43
  (_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 {};
@@ -21,6 +21,7 @@ export const PROPERTY_KEYS_DOCKVIEW = (() => {
21
21
  disableFloatingGroups: undefined,
22
22
  floatingGroupBounds: undefined,
23
23
  popoutUrl: undefined,
24
+ nonce: undefined,
24
25
  defaultRenderer: undefined,
25
26
  defaultHeaderPosition: undefined,
26
27
  debug: undefined,
@@ -36,6 +37,7 @@ export const PROPERTY_KEYS_DOCKVIEW = (() => {
36
37
  getTabContextMenuItems: undefined,
37
38
  getTabGroupChipContextMenuItems: undefined,
38
39
  createTabGroupChipComponent: undefined,
40
+ createGroupDragGhostComponent: undefined,
39
41
  tabGroupColors: undefined,
40
42
  tabGroupAccent: undefined,
41
43
  };
package/dist/esm/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/esm/dom.js CHANGED
@@ -132,8 +132,10 @@ export function quasiPreventDefault(event) {
132
132
  export function quasiDefaultPrevented(event) {
133
133
  return event[QUASI_PREVENT_DEFAULT_KEY];
134
134
  }
135
- export function addStyles(document, styleSheetList) {
135
+ export function addStyles(document, styleSheetList, options = {}) {
136
136
  const styleSheets = Array.from(styleSheetList);
137
+ const { nonce } = options;
138
+ const resolvedNonce = typeof nonce === 'function' ? nonce(document) : nonce;
137
139
  for (const styleSheet of styleSheets) {
138
140
  if (styleSheet.href) {
139
141
  const link = document.createElement('link');
@@ -141,6 +143,10 @@ export function addStyles(document, styleSheetList) {
141
143
  link.type = styleSheet.type;
142
144
  link.rel = 'stylesheet';
143
145
  document.head.appendChild(link);
146
+ // The <link> will load and apply its rules in the target
147
+ // document. Reading cssRules here would duplicate them
148
+ // (and throws for cross-origin sheets).
149
+ continue;
144
150
  }
145
151
  let cssTexts = [];
146
152
  try {
@@ -151,11 +157,16 @@ export function addStyles(document, styleSheetList) {
151
157
  catch (err) {
152
158
  console.warn('dockview: failed to access stylesheet rules due to security restrictions', err);
153
159
  }
160
+ const fragment = document.createDocumentFragment();
154
161
  for (const rule of cssTexts) {
155
162
  const style = document.createElement('style');
163
+ if (resolvedNonce) {
164
+ style.setAttribute('nonce', resolvedNonce);
165
+ }
156
166
  style.appendChild(document.createTextNode(rule));
157
- document.head.appendChild(style);
167
+ fragment.appendChild(style);
158
168
  }
169
+ document.head.appendChild(fragment);
159
170
  }
160
171
  }
161
172
  export function getDomNodePagePosition(domNode) {
@@ -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:`,
@@ -133,7 +133,9 @@ export class PopoutWindow extends CompositeDisposable {
133
133
  const externalDocument = externalWindow.document;
134
134
  externalDocument.title = document.title;
135
135
  externalDocument.body.appendChild(container);
136
- addStyles(externalDocument, window.document.styleSheets);
136
+ addStyles(externalDocument, window.document.styleSheets, {
137
+ nonce: this.options.nonce,
138
+ });
137
139
  /**
138
140
  * beforeunload must be registered after load for reasons I could not determine
139
141
  * otherwise the beforeunload event will not fire when the window is closed