dockview-core 6.0.7 → 6.2.1

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.
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * dockview-core
3
- * @version 6.0.7
3
+ * @version 6.2.1
4
4
  * @link https://github.com/mathuo/dockview
5
5
  * @license MIT
6
6
  */
@@ -78,6 +78,63 @@
78
78
  return paneTransfer.getData(PaneTransfer.prototype)[0];
79
79
  }
80
80
 
81
+ exports.DockviewDisposable = void 0;
82
+ (function (Disposable) {
83
+ Disposable.NONE = {
84
+ dispose: () => {
85
+ // noop
86
+ },
87
+ };
88
+ function from(func) {
89
+ return {
90
+ dispose: () => {
91
+ func();
92
+ },
93
+ };
94
+ }
95
+ Disposable.from = from;
96
+ })(exports.DockviewDisposable || (exports.DockviewDisposable = {}));
97
+ class CompositeDisposable {
98
+ get isDisposed() {
99
+ return this._isDisposed;
100
+ }
101
+ constructor(...args) {
102
+ this._isDisposed = false;
103
+ this._disposables = new Set(args);
104
+ }
105
+ addDisposables(...args) {
106
+ args.forEach((arg) => this._disposables.add(arg));
107
+ }
108
+ removeDisposable(disposable) {
109
+ this._disposables.delete(disposable);
110
+ }
111
+ dispose() {
112
+ if (this._isDisposed) {
113
+ return;
114
+ }
115
+ this._isDisposed = true;
116
+ this._disposables.forEach((arg) => arg.dispose());
117
+ this._disposables.clear();
118
+ }
119
+ }
120
+ class MutableDisposable {
121
+ constructor() {
122
+ this._disposable = exports.DockviewDisposable.NONE;
123
+ }
124
+ set value(disposable) {
125
+ if (this._disposable) {
126
+ this._disposable.dispose();
127
+ }
128
+ this._disposable = disposable;
129
+ }
130
+ dispose() {
131
+ if (this._disposable) {
132
+ this._disposable.dispose();
133
+ this._disposable = exports.DockviewDisposable.NONE;
134
+ }
135
+ }
136
+ }
137
+
81
138
  exports.DockviewEvent = void 0;
82
139
  (function (Event) {
83
140
  Event.any = (...children) => {
@@ -165,6 +222,7 @@
165
222
  this.options = options;
166
223
  this._listeners = [];
167
224
  this._disposed = false;
225
+ this._pauseTokens = new Set();
168
226
  }
169
227
  get event() {
170
228
  if (!this._event) {
@@ -193,6 +251,11 @@
193
251
  }
194
252
  fire(e) {
195
253
  var _a;
254
+ if (this._pauseTokens.size > 0) {
255
+ // while paused, the event is dropped entirely — `_last` is not
256
+ // updated, so replay subscribers won't see values fired during a pause
257
+ return;
258
+ }
196
259
  if ((_a = this.options) === null || _a === void 0 ? void 0 : _a.replay) {
197
260
  this._last = e;
198
261
  }
@@ -200,6 +263,11 @@
200
263
  listener.callback(e);
201
264
  }
202
265
  }
266
+ pause() {
267
+ const token = {};
268
+ this._pauseTokens.add(token);
269
+ return exports.DockviewDisposable.from(() => this._pauseTokens.delete(token));
270
+ }
203
271
  dispose() {
204
272
  if (!this._disposed) {
205
273
  this._disposed = true;
@@ -281,63 +349,6 @@
281
349
  }
282
350
  }
283
351
 
284
- exports.DockviewDisposable = void 0;
285
- (function (Disposable) {
286
- Disposable.NONE = {
287
- dispose: () => {
288
- // noop
289
- },
290
- };
291
- function from(func) {
292
- return {
293
- dispose: () => {
294
- func();
295
- },
296
- };
297
- }
298
- Disposable.from = from;
299
- })(exports.DockviewDisposable || (exports.DockviewDisposable = {}));
300
- class CompositeDisposable {
301
- get isDisposed() {
302
- return this._isDisposed;
303
- }
304
- constructor(...args) {
305
- this._isDisposed = false;
306
- this._disposables = new Set(args);
307
- }
308
- addDisposables(...args) {
309
- args.forEach((arg) => this._disposables.add(arg));
310
- }
311
- removeDisposable(disposable) {
312
- this._disposables.delete(disposable);
313
- }
314
- dispose() {
315
- if (this._isDisposed) {
316
- return;
317
- }
318
- this._isDisposed = true;
319
- this._disposables.forEach((arg) => arg.dispose());
320
- this._disposables.clear();
321
- }
322
- }
323
- class MutableDisposable {
324
- constructor() {
325
- this._disposable = exports.DockviewDisposable.NONE;
326
- }
327
- set value(disposable) {
328
- if (this._disposable) {
329
- this._disposable.dispose();
330
- }
331
- this._disposable = disposable;
332
- }
333
- dispose() {
334
- if (this._disposable) {
335
- this._disposable.dispose();
336
- this._disposable = exports.DockviewDisposable.NONE;
337
- }
338
- }
339
- }
340
-
341
352
  class OverflowObserver extends CompositeDisposable {
342
353
  constructor(el) {
343
354
  super();
@@ -2413,34 +2424,45 @@
2413
2424
  */
2414
2425
  maxmizedViewLocation = getGridLocation(maximizedView.element);
2415
2426
  }
2416
- if (this.hasMaximizedView()) {
2417
- /**
2418
- * the saved layout cannot be in its maxmized state otherwise all of the underlying
2419
- * view dimensions will be wrong
2420
- *
2421
- * To counteract this we temporaily remove the maximized view to compute the serialized output
2422
- * of the grid before adding back the maxmized view as to not alter the layout from the users
2423
- * perspective when `.toJSON()` is called
2424
- */
2425
- this.exitMaximizedView();
2426
- }
2427
- const root = serializeBranchNode(this.getView(), this.orientation);
2428
- const resullt = {
2429
- root,
2430
- width: this.width,
2431
- height: this.height,
2432
- orientation: this.orientation,
2433
- };
2434
- if (maxmizedViewLocation) {
2435
- resullt.maximizedNode = {
2436
- location: maxmizedViewLocation,
2427
+ /**
2428
+ * We pause the onDidMaximizedNodeChange events because this method needs to
2429
+ * call `this.exitMaximizedView()`. We don't want this to invoke any listeners
2430
+ * since we undo it before leaving this method
2431
+ */
2432
+ const pauseToken = this._onDidMaximizedNodeChange.pause();
2433
+ try {
2434
+ if (this.hasMaximizedView()) {
2435
+ /**
2436
+ * the saved layout cannot be in its maxmized state otherwise all of the underlying
2437
+ * view dimensions will be wrong
2438
+ *
2439
+ * To counteract this we temporaily remove the maximized view to compute the serialized output
2440
+ * of the grid before adding back the maxmized view as to not alter the layout from the users
2441
+ * perspective when `.toJSON()` is called
2442
+ */
2443
+ this.exitMaximizedView();
2444
+ }
2445
+ const root = serializeBranchNode(this.getView(), this.orientation);
2446
+ const result = {
2447
+ root,
2448
+ width: this.width,
2449
+ height: this.height,
2450
+ orientation: this.orientation,
2437
2451
  };
2452
+ if (maxmizedViewLocation) {
2453
+ result.maximizedNode = {
2454
+ location: maxmizedViewLocation,
2455
+ };
2456
+ }
2457
+ if (maximizedView) {
2458
+ // replace any maximzied view that was removed for serialization purposes
2459
+ this.maximizeView(maximizedView);
2460
+ }
2461
+ return result;
2438
2462
  }
2439
- if (maximizedView) {
2440
- // replace any maximzied view that was removed for serialization purposes
2441
- this.maximizeView(maximizedView);
2463
+ finally {
2464
+ pauseToken.dispose();
2442
2465
  }
2443
- return resullt;
2444
2466
  }
2445
2467
  dispose() {
2446
2468
  this.disposable.dispose();
@@ -11382,8 +11404,12 @@
11382
11404
  if (panel.api.isVisible) {
11383
11405
  this.positionCache.invalidate();
11384
11406
  resize();
11407
+ focusContainer.style.pointerEvents = '';
11408
+ }
11409
+ else {
11410
+ focusContainer.style.visibility = 'hidden';
11411
+ focusContainer.style.pointerEvents = 'none';
11385
11412
  }
11386
- focusContainer.style.display = panel.api.isVisible ? '' : 'none';
11387
11413
  };
11388
11414
  const observerDisposable = new MutableDisposable();
11389
11415
  const correctLayerPosition = () => {
@@ -11419,7 +11445,7 @@
11419
11445
  * the dnd events for the expect behaviours to continue to occur in terms of dnd
11420
11446
  *
11421
11447
  * the dnd observer does not need to be conditional on whether the panel is visible since
11422
- * non-visible panels are 'display: none' and in such case the dnd observer will not fire.
11448
+ * non-visible panels have 'pointer-events: none' and in such case the dnd observer will not fire.
11423
11449
  */
11424
11450
  new DragAndDropObserver(focusContainer, {
11425
11451
  onDragEnd: (e) => {
@@ -11513,6 +11539,25 @@
11513
11539
  return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
11514
11540
  };
11515
11541
 
11542
+ /**
11543
+ * Reject popout URLs that aren't same-origin http(s). Blocks `javascript:`,
11544
+ * `data:`, `blob:`, `vbscript:`, and cross-origin URLs that would otherwise
11545
+ * execute in a context the browser still associates with the opener via
11546
+ * `window.opener`.
11547
+ */
11548
+ function assertSameOriginPopoutUrl(url) {
11549
+ let resolved;
11550
+ try {
11551
+ resolved = new URL(url, window.location.href);
11552
+ }
11553
+ catch (_a) {
11554
+ throw new Error(`dockview: invalid popout URL: ${url}`);
11555
+ }
11556
+ const protocolOk = resolved.protocol === 'http:' || resolved.protocol === 'https:';
11557
+ if (!protocolOk || resolved.origin !== window.location.origin) {
11558
+ throw new Error(`dockview: popout URL must be same-origin http(s); got: ${url}`);
11559
+ }
11560
+ }
11516
11561
  class PopoutWindow extends CompositeDisposable {
11517
11562
  get window() {
11518
11563
  var _a, _b;
@@ -11564,6 +11609,7 @@
11564
11609
  throw new Error('instance of popout window is already open');
11565
11610
  }
11566
11611
  const url = `${this.options.url}`;
11612
+ assertSameOriginPopoutUrl(url);
11567
11613
  const features = Object.entries({
11568
11614
  top: this.options.top,
11569
11615
  left: this.options.left,
@@ -45,6 +45,7 @@ export declare class Emitter<T> implements IDisposable {
45
45
  private _last?;
46
46
  private _listeners;
47
47
  private _disposed;
48
+ private readonly _pauseTokens;
48
49
  static ENABLE_TRACKING: boolean;
49
50
  static readonly MEMORY_LEAK_WATCHER: LeakageMonitor;
50
51
  static setLeakageMonitorEnabled(isEnabled: boolean): void;
@@ -52,6 +53,7 @@ export declare class Emitter<T> implements IDisposable {
52
53
  constructor(options?: EmitterOptions | undefined);
53
54
  get event(): Event<T>;
54
55
  fire(e: T): void;
56
+ pause(): IDisposable;
55
57
  dispose(): void;
56
58
  }
57
59
  export declare function addDisposableListener<K extends keyof WindowEventMap>(element: Window, type: K, listener: (this: Window, ev: WindowEventMap[K]) => any, options?: boolean | AddEventListenerOptions): IDisposable;
@@ -1,3 +1,4 @@
1
+ import { Disposable } from './lifecycle';
1
2
  export var Event;
2
3
  (function (Event) {
3
4
  Event.any = (...children) => {
@@ -85,6 +86,7 @@ export class Emitter {
85
86
  this.options = options;
86
87
  this._listeners = [];
87
88
  this._disposed = false;
89
+ this._pauseTokens = new Set();
88
90
  }
89
91
  get event() {
90
92
  if (!this._event) {
@@ -118,6 +120,11 @@ export class Emitter {
118
120
  }
119
121
  fire(e) {
120
122
  var _a;
123
+ if (this._pauseTokens.size > 0) {
124
+ // while paused, the event is dropped entirely — `_last` is not
125
+ // updated, so replay subscribers won't see values fired during a pause
126
+ return;
127
+ }
121
128
  if ((_a = this.options) === null || _a === void 0 ? void 0 : _a.replay) {
122
129
  this._last = e;
123
130
  }
@@ -125,6 +132,11 @@ export class Emitter {
125
132
  listener.callback(e);
126
133
  }
127
134
  }
135
+ pause() {
136
+ const token = {};
137
+ this._pauseTokens.add(token);
138
+ return Disposable.from(() => this._pauseTokens.delete(token));
139
+ }
128
140
  dispose() {
129
141
  if (!this._disposed) {
130
142
  this._disposed = true;
@@ -277,34 +277,45 @@ export class Gridview {
277
277
  */
278
278
  maxmizedViewLocation = getGridLocation(maximizedView.element);
279
279
  }
280
- if (this.hasMaximizedView()) {
281
- /**
282
- * the saved layout cannot be in its maxmized state otherwise all of the underlying
283
- * view dimensions will be wrong
284
- *
285
- * To counteract this we temporaily remove the maximized view to compute the serialized output
286
- * of the grid before adding back the maxmized view as to not alter the layout from the users
287
- * perspective when `.toJSON()` is called
288
- */
289
- this.exitMaximizedView();
290
- }
291
- const root = serializeBranchNode(this.getView(), this.orientation);
292
- const resullt = {
293
- root,
294
- width: this.width,
295
- height: this.height,
296
- orientation: this.orientation,
297
- };
298
- if (maxmizedViewLocation) {
299
- resullt.maximizedNode = {
300
- location: maxmizedViewLocation,
280
+ /**
281
+ * We pause the onDidMaximizedNodeChange events because this method needs to
282
+ * call `this.exitMaximizedView()`. We don't want this to invoke any listeners
283
+ * since we undo it before leaving this method
284
+ */
285
+ const pauseToken = this._onDidMaximizedNodeChange.pause();
286
+ try {
287
+ if (this.hasMaximizedView()) {
288
+ /**
289
+ * the saved layout cannot be in its maxmized state otherwise all of the underlying
290
+ * view dimensions will be wrong
291
+ *
292
+ * To counteract this we temporaily remove the maximized view to compute the serialized output
293
+ * of the grid before adding back the maxmized view as to not alter the layout from the users
294
+ * perspective when `.toJSON()` is called
295
+ */
296
+ this.exitMaximizedView();
297
+ }
298
+ const root = serializeBranchNode(this.getView(), this.orientation);
299
+ const result = {
300
+ root,
301
+ width: this.width,
302
+ height: this.height,
303
+ orientation: this.orientation,
301
304
  };
305
+ if (maxmizedViewLocation) {
306
+ result.maximizedNode = {
307
+ location: maxmizedViewLocation,
308
+ };
309
+ }
310
+ if (maximizedView) {
311
+ // replace any maximzied view that was removed for serialization purposes
312
+ this.maximizeView(maximizedView);
313
+ }
314
+ return result;
302
315
  }
303
- if (maximizedView) {
304
- // replace any maximzied view that was removed for serialization purposes
305
- this.maximizeView(maximizedView);
316
+ finally {
317
+ pauseToken.dispose();
306
318
  }
307
- return resullt;
308
319
  }
309
320
  dispose() {
310
321
  this.disposable.dispose();
@@ -136,8 +136,12 @@ export class OverlayRenderContainer extends CompositeDisposable {
136
136
  if (panel.api.isVisible) {
137
137
  this.positionCache.invalidate();
138
138
  resize();
139
+ focusContainer.style.pointerEvents = '';
140
+ }
141
+ else {
142
+ focusContainer.style.visibility = 'hidden';
143
+ focusContainer.style.pointerEvents = 'none';
139
144
  }
140
- focusContainer.style.display = panel.api.isVisible ? '' : 'none';
141
145
  };
142
146
  const observerDisposable = new MutableDisposable();
143
147
  const correctLayerPosition = () => {
@@ -173,7 +177,7 @@ export class OverlayRenderContainer extends CompositeDisposable {
173
177
  * the dnd events for the expect behaviours to continue to occur in terms of dnd
174
178
  *
175
179
  * the dnd observer does not need to be conditional on whether the panel is visible since
176
- * non-visible panels are 'display: none' and in such case the dnd observer will not fire.
180
+ * non-visible panels have 'pointer-events: none' and in such case the dnd observer will not fire.
177
181
  */
178
182
  new DragAndDropObserver(focusContainer, {
179
183
  onDragEnd: (e) => {
@@ -11,6 +11,13 @@ export type PopoutWindowOptions = {
11
11
  window: Window;
12
12
  }) => void;
13
13
  } & Box;
14
+ /**
15
+ * Reject popout URLs that aren't same-origin http(s). Blocks `javascript:`,
16
+ * `data:`, `blob:`, `vbscript:`, and cross-origin URLs that would otherwise
17
+ * execute in a context the browser still associates with the opener via
18
+ * `window.opener`.
19
+ */
20
+ export declare function assertSameOriginPopoutUrl(url: string): void;
14
21
  export declare class PopoutWindow extends CompositeDisposable {
15
22
  private readonly target;
16
23
  private readonly className;
@@ -10,6 +10,25 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
10
10
  import { addStyles } from './dom';
11
11
  import { Emitter, addDisposableListener } from './events';
12
12
  import { CompositeDisposable, Disposable } from './lifecycle';
13
+ /**
14
+ * Reject popout URLs that aren't same-origin http(s). Blocks `javascript:`,
15
+ * `data:`, `blob:`, `vbscript:`, and cross-origin URLs that would otherwise
16
+ * execute in a context the browser still associates with the opener via
17
+ * `window.opener`.
18
+ */
19
+ export function assertSameOriginPopoutUrl(url) {
20
+ let resolved;
21
+ try {
22
+ resolved = new URL(url, window.location.href);
23
+ }
24
+ catch (_a) {
25
+ throw new Error(`dockview: invalid popout URL: ${url}`);
26
+ }
27
+ const protocolOk = resolved.protocol === 'http:' || resolved.protocol === 'https:';
28
+ if (!protocolOk || resolved.origin !== window.location.origin) {
29
+ throw new Error(`dockview: popout URL must be same-origin http(s); got: ${url}`);
30
+ }
31
+ }
13
32
  export class PopoutWindow extends CompositeDisposable {
14
33
  get window() {
15
34
  var _a, _b;
@@ -61,6 +80,7 @@ export class PopoutWindow extends CompositeDisposable {
61
80
  throw new Error('instance of popout window is already open');
62
81
  }
63
82
  const url = `${this.options.url}`;
83
+ assertSameOriginPopoutUrl(url);
64
84
  const features = Object.entries({
65
85
  top: this.options.top,
66
86
  left: this.options.left,