@perspective-dev/workspace 4.0.1 → 4.1.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.
@@ -12,22 +12,21 @@
12
12
 
13
13
  import { MessageLoop } from "@lumino/messaging";
14
14
  import { Widget } from "@lumino/widgets";
15
- import { HTMLPerspectiveViewerElement } from "@perspective-dev/viewer";
15
+ import type { HTMLPerspectiveViewerElement } from "@perspective-dev/viewer";
16
16
  import type * as psp from "@perspective-dev/client";
17
+ import type * as psp_viewer from "@perspective-dev/viewer";
18
+ import * as msg from "@lumino/messaging";
17
19
 
18
- export { PerspectiveWorkspace } from "./workspace";
20
+ export { PerspectiveWorkspace, addViewer, genId } from "./workspace";
19
21
  export { PerspectiveViewerWidget } from "./workspace/widget";
20
-
21
- import "./external";
22
- import {
23
- PerspectiveWorkspace,
24
- PerspectiveWorkspaceConfig,
25
- ViewerConfigUpdateExt,
26
- } from "./workspace";
22
+ export * from "./extensions";
23
+ import { PerspectiveWorkspace, PerspectiveWorkspaceConfig } from "./workspace";
27
24
  import { bindTemplate, CustomElementProto } from "./utils/custom_elements";
28
25
  import style from "../../build/css/workspace.css";
29
26
  import template from "../html/workspace.html";
30
27
 
28
+ export { PerspectiveWorkspaceConfig };
29
+
31
30
  /**
32
31
  * A Custom Element for coordinating a set of `<perspective-viewer>` light DOM
33
32
  * children. `<perspective-workspace>` is built on Lumino.js to allow a more
@@ -46,24 +45,13 @@ import template from "../html/workspace.html";
46
45
  * express your initial view simply:
47
46
  *
48
47
  * ```html
49
- * <perspective-workspace>
50
- * <perspective-viewer
51
- * name="View One"
52
- * table="superstore">
53
- * </perspective-viewer>
54
- * <perspective-viewer
55
- * name="View Two"
56
- * table="superstore">
57
- * </perspective-viewer>
58
- * </perspective-workspace>
48
+ * <perspective-workspace></perspective-workspace>
59
49
  * ```
60
50
  *
61
51
  * You can also use the DOM API in Javascript:
62
52
  *
63
53
  * ```javascript
64
54
  * const workspace = document.createElement("perspective-workspace");
65
- * const viewer = document.createElement("perspective-viewer");
66
- * workspace.appendChild(viewer);
67
55
  * document.body.appendChild(workspace);
68
56
  * ```
69
57
  *
@@ -113,7 +101,7 @@ export class HTMLPerspectiveWorkspaceElement extends HTMLElement {
113
101
  * const workspace = document.querySelector("perspective-workspace");
114
102
  * localStorage.set("CONFIG", JSON.stringify(workspace.save()));
115
103
  */
116
- save() {
104
+ save(): Promise<PerspectiveWorkspaceConfig> {
117
105
  return this.workspace!.save();
118
106
  }
119
107
 
@@ -137,15 +125,15 @@ export class HTMLPerspectiveWorkspaceElement extends HTMLElement {
137
125
  * // Add `Table` separately.
138
126
  * workspace.tables.set("superstore", await worker.table(data));
139
127
  */
140
- async restore(layout: PerspectiveWorkspaceConfig<string>) {
128
+ async restore(layout: PerspectiveWorkspaceConfig) {
141
129
  await this.workspace!.restore(layout);
142
130
  }
143
131
 
144
132
  async clear() {
145
133
  await this.restore({
146
134
  sizes: [],
147
- master: { sizes: [] },
148
- detail: { sizes: [] },
135
+ master: undefined,
136
+ detail: { main: null },
149
137
  viewers: {},
150
138
  });
151
139
  }
@@ -156,75 +144,41 @@ export class HTMLPerspectiveWorkspaceElement extends HTMLElement {
156
144
  * are applied.
157
145
  */
158
146
  async flush() {
159
- await Promise.all(
160
- Array.from(this.querySelectorAll("perspective-viewer")).map((x) => {
161
- const psp_widget = x as HTMLPerspectiveViewerElement;
162
- return psp_widget.flush();
163
- }),
164
- );
147
+ if (!this.workspace) {
148
+ return;
149
+ }
150
+
151
+ msg.MessageLoop.flush();
152
+ await new Promise((x) => requestAnimationFrame(x));
153
+ await this.workspace._mutex.lock(async () => {
154
+ await Promise.all(
155
+ Array.from(this.querySelectorAll("perspective-viewer")).map(
156
+ (x) => {
157
+ const psp_widget = x as HTMLPerspectiveViewerElement;
158
+ return psp_widget
159
+ .flush()
160
+ .then(() => psp_widget.flush());
161
+ },
162
+ ),
163
+ );
164
+ });
165
165
  }
166
166
 
167
167
  /**
168
168
  * Add a new viewer to the workspace for a given `ViewerConfigUpdateExt`.
169
169
  * @param config
170
170
  */
171
- async addViewer(config: ViewerConfigUpdateExt) {
172
- this.workspace!.addViewer(config);
173
- await this.flush();
174
- }
175
-
176
- /**
177
- * Add a new `Table` to the workspace, so that it can be bound by viewers.
178
- * Each `Table` is identified by a unique `name`.
179
- */
180
- async addTable(name: string, table: Promise<psp.Table>) {
181
- this.workspace!.addTable(name, table);
171
+ async addViewer(config: psp_viewer.ViewerConfigUpdate) {
172
+ await this.workspace!.addViewer(config);
182
173
  await this.flush();
183
174
  }
184
175
 
185
- /**
186
- * Deleta a table by name from this workspace
187
- * @param name
188
- * @returns
189
- */
190
- getTable(name: string) {
191
- return this.workspace!.getTable(name);
192
- }
193
-
194
- /**
195
- * Replace a `Table` by name. As `Table` doe snot guarantee the same
196
- * structure, this will wipe the viewer's state.
197
- * @param name
198
- * @param table
199
- */
200
- async replaceTable(name: string, table: Promise<psp.Table>) {
201
- this.workspace!.replaceTable(name, table);
202
- await this.flush();
203
- }
204
-
205
- /**
206
- * Remove a `Table` by name.
207
- * @param name
208
- * @returns
209
- */
210
- removeTable(name: string) {
211
- return this.workspace!.removeTable(name);
212
- }
213
-
214
- /**
215
- * A `Map()` of `perspective.Table()` by name. The names set here will auto
216
- * wire any child `perspective-viewer` elements in this Workspace's subtree,
217
- * by looking up their respective `table` attribute.
218
- *
219
- * Calling methods on this `Map()` may have side-effects, as
220
- * `PerspectiveViewerHTMLElement.load()` is called when a new `Table()` is
221
- * set with a name matching an existing child `perspective-viewer`.
222
- *
223
- * @readonly
224
- * @memberof HTMLPerspectiveWorkspaceElement
225
- */
226
- get tables() {
227
- return this.workspace!.tables;
176
+ async load(client: psp.Client | Promise<psp.Client>) {
177
+ if (this.workspace) {
178
+ this.workspace.client.push(await client);
179
+ } else {
180
+ throw new Error("Workspace not mounted");
181
+ }
228
182
  }
229
183
 
230
184
  /**
@@ -269,27 +223,33 @@ export class HTMLPerspectiveWorkspaceElement extends HTMLElement {
269
223
  */
270
224
 
271
225
  private _light_dom_changed() {
272
- const viewers = Array.from(
273
- this.childNodes,
274
- ) as HTMLPerspectiveViewerElement[];
226
+ if (!this.workspace) {
227
+ return;
228
+ }
275
229
 
276
- for (const viewer of viewers) {
277
- if (viewer.nodeType !== Node.ELEMENT_NODE) {
278
- continue;
279
- }
230
+ this.workspace._mutex.lock(async () => {
231
+ const viewers = Array.from(
232
+ this.childNodes,
233
+ ) as HTMLPerspectiveViewerElement[];
280
234
 
281
- if (viewer.tagName !== "PERSPECTIVE-VIEWER") {
282
- console.warn("Not a <perspective-viewer>");
283
- continue;
284
- }
235
+ for (const viewer of viewers) {
236
+ if (viewer.nodeType !== Node.ELEMENT_NODE) {
237
+ continue;
238
+ }
285
239
 
286
- this.workspace!.update_widget_for_viewer(
287
- viewer as HTMLPerspectiveViewerElement,
288
- );
289
- }
240
+ if (viewer.tagName !== "PERSPECTIVE-VIEWER") {
241
+ console.warn("Not a <perspective-viewer>");
242
+ continue;
243
+ }
244
+
245
+ let _task = this.workspace!.update_widget_for_viewer(
246
+ viewer as HTMLPerspectiveViewerElement,
247
+ );
248
+ }
290
249
 
291
- this.workspace!.remove_unslotted_widgets(viewers);
292
- this.workspace!.update_details_panel(viewers);
250
+ this.workspace!.remove_unslotted_widgets(viewers);
251
+ this.workspace!.update_details_panel(viewers);
252
+ });
293
253
  }
294
254
 
295
255
  private _register_light_dom_listener() {
@@ -11,12 +11,12 @@
11
11
  // ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
12
12
 
13
13
  import { CommandRegistry } from "@lumino/commands";
14
- import { Menu, Widget } from "@lumino/widgets";
14
+ import { Widget } from "@lumino/widgets";
15
15
  import { Signal } from "@lumino/signaling";
16
16
 
17
17
  import type {
18
- HTMLPerspectiveViewerCopyMenu,
19
- HTMLPerspectiveViewerExportMenu,
18
+ HTMLPerspectiveViewerCopyMenuElement,
19
+ HTMLPerspectiveViewerExportMenuElement,
20
20
  } from "@perspective-dev/viewer";
21
21
 
22
22
  import type { PerspectiveWorkspace } from "./workspace";
@@ -32,14 +32,14 @@ export const createCommands = (
32
32
  execute: async (args) => {
33
33
  const menu = document.createElement(
34
34
  "perspective-export-menu",
35
- ) as unknown as HTMLPerspectiveViewerExportMenu;
35
+ ) as unknown as HTMLPerspectiveViewerExportMenuElement;
36
36
 
37
37
  workspace.apply_indicator_theme();
38
38
  const widget = workspace.getWidgetByName(
39
39
  args.widget_name as string,
40
40
  )!;
41
41
 
42
- menu.set_model(widget.viewer.get_model());
42
+ menu.__set_model(widget.viewer.__get_model());
43
43
  menu.open(indicator);
44
44
  workspace.get_context_menu()?.init_overlay?.();
45
45
  menu.addEventListener("blur", () => {
@@ -74,13 +74,13 @@ export const createCommands = (
74
74
  execute: async (args) => {
75
75
  const menu = document.createElement(
76
76
  "perspective-copy-menu",
77
- ) as HTMLPerspectiveViewerCopyMenu;
77
+ ) as HTMLPerspectiveViewerCopyMenuElement;
78
78
 
79
79
  workspace.apply_indicator_theme();
80
80
  const widget = workspace.getWidgetByName(
81
81
  args.widget_name as string,
82
82
  )!;
83
- menu.set_model(widget.viewer.get_model());
83
+ menu.__set_model(widget.viewer.__get_model());
84
84
 
85
85
  menu.open(indicator);
86
86
  workspace.get_context_menu()?.init_overlay?.();
@@ -112,9 +112,11 @@ export const createCommands = (
112
112
  });
113
113
 
114
114
  commands.addCommand("workspace:new", {
115
- execute: (args) => {
116
- const widget = workspace._createWidgetAndNode({
117
- config: { table: args.table as string },
115
+ execute: async (args) => {
116
+ const widget = await workspace._createWidgetAndNode({
117
+ config: {
118
+ table: args.table as string,
119
+ },
118
120
  slot: undefined,
119
121
  });
120
122
 
@@ -140,7 +142,7 @@ export const createCommands = (
140
142
  )!;
141
143
 
142
144
  const config = await target_widget.save();
143
- const new_widget = workspace._createWidgetAndNode({
145
+ const new_widget = await workspace._createWidgetAndNode({
144
146
  config,
145
147
  slot: undefined,
146
148
  });
@@ -184,19 +186,19 @@ export const createCommands = (
184
186
  throw new Error(`No widget ${widget_name}`);
185
187
  }
186
188
 
187
- if (!widget.viewer.hasAttribute("settings")) {
188
- workspace._maximize(widget);
189
- requestAnimationFrame(() => widget.viewer.toggleConfig());
190
- } else {
191
- widget.viewer.toggleConfig();
192
- }
189
+ // if (!widget.viewer.hasAttribute("settings")) {
190
+ // workspace._maximize(widget);
191
+ requestAnimationFrame(() => widget.viewer.toggleConfig());
192
+ // } else {
193
+ // widget.viewer.toggleConfig();
194
+ // }
193
195
  },
194
196
  isVisible: (args) => {
195
197
  const widget = workspace.getWidgetByName(
196
198
  args.widget_name as string,
197
- )!;
199
+ );
198
200
 
199
- return widget.parent! === (workspace.get_dock_panel() as Widget)
201
+ return widget?.parent! === (workspace.get_dock_panel() as Widget)
200
202
  ? true
201
203
  : false;
202
204
  },
@@ -220,8 +222,8 @@ export const createCommands = (
220
222
  ),
221
223
  // iconClass: "menu-duplicate",
222
224
  isVisible: (args) => {
223
- return workspace.getWidgetByName(args.widget_name as string)!
224
- .parent! === (workspace.get_dock_panel() as Widget)
225
+ return workspace.getWidgetByName(args.widget_name as string)
226
+ ?.parent! === (workspace.get_dock_panel() as Widget)
225
227
  ? true
226
228
  : false;
227
229
  },
@@ -36,11 +36,13 @@ class PerspectiveDockPanelRenderer extends DockPanel.Renderer {
36
36
 
37
37
  // @ts-ignore: extending a private member `_onTabDetachRequested`
38
38
  export class PerspectiveDockPanel extends DockPanel {
39
+ _workspace: PerspectiveWorkspace;
39
40
  constructor(workspace: PerspectiveWorkspace) {
40
41
  super({ renderer: new PerspectiveDockPanelRenderer(workspace) });
41
42
 
42
43
  // @ts-ignore: accessing a private member `_renderer`
43
44
  this._renderer.dock = this;
45
+ this._workspace = workspace;
44
46
  }
45
47
 
46
48
  _onTabDetachRequested(
@@ -64,7 +66,7 @@ export class PerspectiveDockPanel extends DockPanel {
64
66
  // @ts-ignore: accessing a private member `_drag`
65
67
  const drag = this._drag;
66
68
  if (drag) {
67
- drag.dragImage?.parentElement.removeChild(drag.dragImage);
69
+ drag.dragImage?.parentElement?.removeChild?.(drag.dragImage);
68
70
  drag.dragImage = null;
69
71
  drag._promise.then(() => {
70
72
  if (!widget.node.isConnected) {
@@ -107,12 +109,12 @@ export class PerspectiveDockPanel extends DockPanel {
107
109
  return super.widgets() as IterableIterator<PerspectiveViewerWidget>;
108
110
  }
109
111
 
110
- static mapWidgets(
111
- widgetFunc: (widget: any) => any,
112
+ static async mapWidgets(
113
+ widgetFunc: (widget: any) => Promise<any>,
112
114
  layout: any,
113
- ): DockPanel.ILayoutConfig {
115
+ ): Promise<DockPanel.ILayoutConfig> {
114
116
  if (!!layout.main) {
115
- layout.main = PerspectiveDockPanel.mapAreaWidgets(
117
+ layout.main = await PerspectiveDockPanel.mapAreaWidgets(
116
118
  widgetFunc,
117
119
  layout.main,
118
120
  );
@@ -121,18 +123,22 @@ export class PerspectiveDockPanel extends DockPanel {
121
123
  return layout;
122
124
  }
123
125
 
124
- static mapAreaWidgets(
125
- widgetFunc: (widget: any) => any,
126
+ static async mapAreaWidgets(
127
+ widgetFunc: (widget: any) => Promise<any>,
126
128
  layout: DockLayout.AreaConfig,
127
- ): DockLayout.AreaConfig {
129
+ ): Promise<DockLayout.AreaConfig> {
128
130
  if (layout.hasOwnProperty("children")) {
129
131
  const split_panel = layout as DockLayout.ISplitAreaConfig;
130
- split_panel.children = split_panel.children.map((widget) =>
131
- PerspectiveDockPanel.mapAreaWidgets(widgetFunc, widget),
132
+ split_panel.children = await Promise.all(
133
+ split_panel.children.map((widget) =>
134
+ PerspectiveDockPanel.mapAreaWidgets(widgetFunc, widget),
135
+ ),
132
136
  );
133
137
  } else if (layout.hasOwnProperty("widgets")) {
134
138
  const tab_panel = layout as DockLayout.ITabAreaConfig;
135
- tab_panel.widgets = tab_panel.widgets.map(widgetFunc);
139
+ tab_panel.widgets = await Promise.all(
140
+ tab_panel.widgets.map(widgetFunc),
141
+ );
136
142
  }
137
143
 
138
144
  return layout;
@@ -140,6 +146,11 @@ export class PerspectiveDockPanel extends DockPanel {
140
146
 
141
147
  onAfterAttach() {
142
148
  this.spacing =
143
- parseInt(window.getComputedStyle(this.node).padding) || 0;
149
+ parseInt(
150
+ window
151
+ .getComputedStyle(this._workspace.element)
152
+ .getPropertyValue("--workspace-spacing"),
153
+ ) || 0;
154
+ 1;
144
155
  }
145
156
  }
@@ -62,6 +62,7 @@ export class PerspectiveTabBar extends TabBar<any> {
62
62
  let content = new Array();
63
63
  for (let i = 0, n = titles.length; i < n; ++i) {
64
64
  let title = titles[i];
65
+ title.owner._titlebar = this;
65
66
  let current = title === currentTitle;
66
67
  let otherTitles = titles.filter((x) => x !== currentTitle);
67
68
  let onClick;
@@ -132,7 +133,12 @@ export class PerspectiveTabBar extends TabBar<any> {
132
133
  mnemonic: 0,
133
134
  });
134
135
 
135
- const box = (event.target as HTMLElement).getBoundingClientRect();
136
+ const box = (
137
+ (event.target as HTMLElement).shadowRoot?.querySelector(
138
+ "#status_reconnect",
139
+ ) as HTMLElement
140
+ ).getBoundingClientRect();
141
+
136
142
  const outer_box = this._workspace.element.getBoundingClientRect();
137
143
  this._menu.open(box.x - outer_box.x, box.y + box.height - outer_box.y);
138
144
  this._menu.aboutToClose.connect(() => {
@@ -53,39 +53,22 @@ export class PerspectiveTabBarRenderer extends TabBar.Renderer {
53
53
  const dataset = this.createTabDataset(data);
54
54
  const more: h.Child[] = [];
55
55
  if (onclick) {
56
- more.push(
57
- h.div(
58
- { onclick, className: "bookmarks-button" },
59
- h.div({ className: "bookmarks" }),
60
- ),
61
- );
56
+ data.title.owner.setCallback(onclick);
57
+ data.title.owner.addClass("bookmarks-container");
58
+ } else {
59
+ data.title.owner.setCallback(undefined);
60
+ data.title.owner.removeClass("bookmarks-container");
62
61
  }
63
62
 
64
63
  return h.li(
65
64
  { key, className, title, style, dataset },
66
- this.renderDragHandle(),
67
- ...more,
68
- this.renderLabel(data),
69
- this.renderCloseIcon(),
65
+ // this.renderDragHandle(),
70
66
  );
71
67
  }
72
68
 
73
- renderDragHandle() {
74
- return h.div({
75
- className: "drag-handle",
76
- });
77
- }
78
-
79
- // renderConfigIcon() {
69
+ // renderDragHandle() {
80
70
  // return h.div({
81
- // className: "lm-TabBar-tabConfigIcon",
82
- // id: TabBarItems.Config,
71
+ // className: "drag-handle",
83
72
  // });
84
73
  // }
85
-
86
- renderCloseIcon() {
87
- return h.div({
88
- className: "lm-TabBar-tabCloseIcon" + " p-TabBar-tabCloseIcon",
89
- });
90
- }
91
74
  }
@@ -15,26 +15,31 @@ import { Message } from "@lumino/messaging";
15
15
 
16
16
  import type * as psp_viewer from "@perspective-dev/viewer";
17
17
  import type * as psp from "@perspective-dev/client";
18
+ import { PerspectiveTabBar } from "./tabbar";
18
19
 
19
20
  interface IPerspectiveViewerWidgetOptions {
20
21
  node: HTMLElement;
21
22
  viewer: psp_viewer.HTMLPerspectiveViewerElement;
23
+ onAttach?: () => void;
22
24
  }
23
25
 
24
26
  export class PerspectiveViewerWidget extends Widget {
25
27
  viewer: psp_viewer.HTMLPerspectiveViewerElement;
26
28
  _title: string;
27
- _is_table_loaded: boolean;
28
29
  _is_pivoted: boolean;
29
30
  _restore_config?: () => Promise<void>;
30
- task?: Promise<void>;
31
+ _onAttach?: () => void;
32
+ _titlebar?: PerspectiveTabBar;
33
+ _deleted: boolean;
34
+ _titlebar_callback?: (event: MouseEvent) => {};
31
35
 
32
- constructor({ viewer, node }: IPerspectiveViewerWidgetOptions) {
36
+ constructor({ viewer, node, onAttach }: IPerspectiveViewerWidgetOptions) {
33
37
  super({ node });
34
38
  this.viewer = viewer;
35
39
  this._title = "";
36
- this._is_table_loaded = false;
37
40
  this._is_pivoted = false;
41
+ this._onAttach = onAttach;
42
+ this._deleted = false;
38
43
  }
39
44
 
40
45
  get name(): string {
@@ -46,7 +51,6 @@ export class PerspectiveViewerWidget extends Widget {
46
51
  }
47
52
 
48
53
  async load(table: psp.Table | Promise<psp.Table>) {
49
- this._is_table_loaded = true;
50
54
  let promises = [this.viewer.load(table)];
51
55
  if (this._restore_config) {
52
56
  promises.push(this._restore_config());
@@ -55,47 +59,52 @@ export class PerspectiveViewerWidget extends Widget {
55
59
  await Promise.all(promises);
56
60
  }
57
61
 
58
- restore(
59
- config: psp_viewer.ViewerConfigUpdate & {
60
- table: string;
61
- },
62
- ) {
63
- const { table, ...viewerConfig } = config;
62
+ restore(config: psp_viewer.ViewerConfigUpdate) {
64
63
  this._title = config.title as string;
65
64
  this.title.label = config.title as string;
66
- if (table) {
67
- this.viewer.setAttribute("table", table);
68
- }
69
-
70
- const restore_config = () => this.viewer.restore({ ...viewerConfig });
71
- if (this._is_table_loaded) {
72
- return restore_config();
73
- } else {
74
- this._restore_config = restore_config;
75
- }
65
+ this._is_pivoted = (config.group_by?.length || 0) > 0;
66
+ return this.viewer.restore({ ...config });
76
67
  }
77
68
 
78
69
  async save() {
79
70
  let config = {
80
71
  ...(await this.viewer.save()),
81
- table: this.viewer.getAttribute("table"),
82
72
  };
83
73
 
84
74
  delete config["settings"];
85
75
  return config;
86
76
  }
87
77
 
78
+ addClass(name: string) {
79
+ super.addClass(name);
80
+ this.viewer?.classList?.add?.(name);
81
+ }
82
+
88
83
  removeClass(name: string) {
89
84
  super.removeClass(name);
90
- this.viewer && this.viewer.classList.remove(name);
85
+ this.viewer?.classList?.remove?.(name);
91
86
  }
92
87
 
93
- async onCloseRequest(msg: Message) {
94
- super.onCloseRequest(msg);
95
- if (this.viewer.parentElement) {
96
- this.viewer.parentElement.removeChild(this.viewer);
97
- }
88
+ setCallback(callback?: (event: MouseEvent) => {}) {
89
+ this._titlebar_callback = callback;
90
+ }
98
91
 
99
- await this.viewer.delete();
92
+ protected onAfterAttach(msg: Message) {
93
+ super.onAfterAttach(msg);
94
+ this._onAttach?.();
95
+ }
96
+
97
+ onCloseRequest(msg: Message) {
98
+ super.onCloseRequest(msg);
99
+ return (async () => {
100
+ if (this.viewer.parentElement) {
101
+ this.viewer.parentElement.removeChild(this.viewer);
102
+ }
103
+
104
+ if (!this._deleted) {
105
+ await this.viewer.delete();
106
+ this._deleted = true;
107
+ }
108
+ })();
100
109
  }
101
110
  }