@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.
- package/dist/cdn/perspective-workspace.js +7 -7
- package/dist/cdn/perspective-workspace.js.map +4 -4
- package/dist/css/pro-dark.css +1 -1
- package/dist/css/pro.css +1 -1
- package/dist/esm/extensions.d.ts +23 -0
- package/dist/esm/perspective-workspace.d.ts +10 -63
- package/dist/esm/perspective-workspace.js +3 -3
- package/dist/esm/perspective-workspace.js.map +4 -4
- package/dist/esm/workspace/dockpanel.d.ts +3 -2
- package/dist/esm/workspace/tabbarrenderer.d.ts +0 -2
- package/dist/esm/workspace/widget.d.ts +11 -6
- package/dist/esm/workspace/workspace.d.ts +43 -36
- package/package.json +2 -2
- package/src/less/dockpanel.less +11 -1
- package/src/less/tabbar.less +40 -17
- package/src/less/viewer.less +26 -1
- package/src/less/widget.less +3 -1
- package/src/less/workspace.less +1 -1
- package/src/svg/bookmark-icon.svg +2 -2
- package/src/themes/pro-dark.less +3 -2
- package/src/themes/pro.less +11 -2
- package/src/ts/extensions.ts +51 -0
- package/src/ts/external.js +3 -0
- package/src/ts/perspective-workspace.ts +62 -102
- package/src/ts/workspace/commands.ts +23 -21
- package/src/ts/workspace/dockpanel.ts +23 -12
- package/src/ts/workspace/tabbar.ts +7 -1
- package/src/ts/workspace/tabbarrenderer.ts +8 -25
- package/src/ts/workspace/widget.ts +38 -29
- package/src/ts/workspace/workspace.ts +477 -238
|
@@ -12,58 +12,209 @@
|
|
|
12
12
|
|
|
13
13
|
import { find, toArray } from "@lumino/algorithm";
|
|
14
14
|
import { CommandRegistry } from "@lumino/commands";
|
|
15
|
+
|
|
15
16
|
import { SplitPanel, Panel, DockPanel } from "@lumino/widgets";
|
|
16
17
|
import uniqBy from "lodash/uniqBy";
|
|
17
|
-
import { DebouncedFunc, isEqual } from "lodash";
|
|
18
|
+
import { DebouncedFunc, DebouncedFuncLeading, isEqual } from "lodash";
|
|
19
|
+
import { throttle } from "lodash";
|
|
18
20
|
import debounce from "lodash/debounce";
|
|
19
21
|
import type {
|
|
20
22
|
HTMLPerspectiveViewerElement,
|
|
21
23
|
ViewerConfigUpdate,
|
|
22
24
|
} from "@perspective-dev/viewer";
|
|
23
25
|
import type * as psp from "@perspective-dev/client";
|
|
26
|
+
import type * as psp_viewer from "@perspective-dev/viewer";
|
|
24
27
|
import injectedStyles from "../../../build/css/injected.css";
|
|
25
28
|
import { PerspectiveDockPanel } from "./dockpanel";
|
|
26
29
|
import { WorkspaceMenu } from "./menu";
|
|
27
30
|
import { createCommands } from "./commands";
|
|
28
31
|
import { PerspectiveViewerWidget } from "./widget";
|
|
29
|
-
import { ObservableMap } from "../utils/observable_map";
|
|
30
32
|
|
|
31
|
-
|
|
33
|
+
class AsyncMutex {
|
|
34
|
+
_lock: Promise<unknown> | null;
|
|
32
35
|
|
|
33
|
-
|
|
36
|
+
constructor() {
|
|
37
|
+
this._lock = null;
|
|
38
|
+
}
|
|
34
39
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
40
|
+
lock<A>(continuation: () => Promise<A>): Promise<A> {
|
|
41
|
+
if (this._lock !== null) {
|
|
42
|
+
return this._lock.then(() => this.lock(continuation));
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
this._lock = new Promise((x, y) =>
|
|
46
|
+
continuation()
|
|
47
|
+
.then((z) => {
|
|
48
|
+
this._lock = null;
|
|
49
|
+
x(z);
|
|
50
|
+
})
|
|
51
|
+
.catch((e) => {
|
|
52
|
+
this._lock = null;
|
|
53
|
+
y(e);
|
|
54
|
+
}),
|
|
55
|
+
);
|
|
56
|
+
|
|
57
|
+
return this._lock as Promise<A>;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export type PerspectiveSplitArea = {
|
|
62
|
+
type: "split-area";
|
|
38
63
|
sizes: number[];
|
|
64
|
+
orientation: "horizontal" | "vertical";
|
|
65
|
+
children: PerspectiveLayout[];
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
export type PerspectiveTabArea = {
|
|
69
|
+
type: "tab-area";
|
|
70
|
+
currentIndex: number;
|
|
71
|
+
widgets: string[];
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
export type PerspectiveLayout = PerspectiveSplitArea | PerspectiveTabArea;
|
|
75
|
+
|
|
76
|
+
export interface PerspectiveWorkspaceConfig {
|
|
77
|
+
sizes: number[];
|
|
78
|
+
viewers: Record<string, psp_viewer.ViewerConfigUpdate>;
|
|
79
|
+
detail: { main: PerspectiveLayout | null };
|
|
80
|
+
master?: {
|
|
81
|
+
sizes: number[];
|
|
82
|
+
widgets: string[];
|
|
83
|
+
};
|
|
39
84
|
}
|
|
40
85
|
|
|
41
|
-
|
|
42
|
-
|
|
86
|
+
const DEFAULT_WORKSPACE_SIZE = [1, 3];
|
|
87
|
+
let ID_COUNTER = 0;
|
|
88
|
+
|
|
89
|
+
export function genId(workspace: PerspectiveWorkspaceConfig) {
|
|
90
|
+
let i = `PERSPECTIVE_GENERATED_ID_${ID_COUNTER++}`;
|
|
91
|
+
if (Object.keys(workspace.viewers).includes(i)) {
|
|
92
|
+
i = genId(workspace);
|
|
93
|
+
}
|
|
94
|
+
return i;
|
|
43
95
|
}
|
|
44
96
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
97
|
+
/// This function takes a workspace config and viewer config and adds the
|
|
98
|
+
/// viewer config to the workspace config, returning a new workspace config.
|
|
99
|
+
/// This is a slightly different algorithm from the Lumino one,
|
|
100
|
+
/// which will be used on internal workspace actions (such as duplication).
|
|
101
|
+
/// It currently attaches the viewer using a split-right style,
|
|
102
|
+
/// (see Lumino docklayout.ts for documentation on insert modes).
|
|
103
|
+
export function addViewer(
|
|
104
|
+
workspace: PerspectiveWorkspaceConfig,
|
|
105
|
+
config: psp_viewer.ViewerConfigUpdate,
|
|
106
|
+
id: string,
|
|
107
|
+
): PerspectiveWorkspaceConfig {
|
|
108
|
+
const GOLDEN_RATIO = 0.618;
|
|
109
|
+
/// ensures that the sum of the input is 1
|
|
110
|
+
/// keeps the relative size of the elements
|
|
111
|
+
function normalize(sizes: number[]) {
|
|
112
|
+
const sum = sizes.reduce((a, b) => a + b, 0);
|
|
113
|
+
return sum === 1 ? sizes : sizes.map((size) => size / sum);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
if (workspace.detail.main === null) {
|
|
117
|
+
return {
|
|
118
|
+
sizes: workspace.sizes,
|
|
119
|
+
viewers: {
|
|
120
|
+
...workspace.viewers,
|
|
121
|
+
[id]: config,
|
|
122
|
+
},
|
|
123
|
+
detail: {
|
|
124
|
+
main: {
|
|
125
|
+
type: "split-area",
|
|
126
|
+
sizes: [1],
|
|
127
|
+
orientation: "horizontal",
|
|
128
|
+
children: [
|
|
129
|
+
{
|
|
130
|
+
type: "tab-area",
|
|
131
|
+
currentIndex: 0,
|
|
132
|
+
widgets: [id],
|
|
133
|
+
},
|
|
134
|
+
],
|
|
135
|
+
},
|
|
136
|
+
},
|
|
137
|
+
master: workspace.master,
|
|
138
|
+
};
|
|
139
|
+
} else if (
|
|
140
|
+
workspace.detail.main.type === "tab-area" ||
|
|
141
|
+
(workspace.detail.main.type === "split-area" &&
|
|
142
|
+
workspace.detail.main.orientation === "vertical")
|
|
143
|
+
) {
|
|
144
|
+
return {
|
|
145
|
+
sizes: workspace.sizes,
|
|
146
|
+
viewers: {
|
|
147
|
+
...workspace.viewers,
|
|
148
|
+
[id]: config,
|
|
149
|
+
},
|
|
150
|
+
detail: {
|
|
151
|
+
main: {
|
|
152
|
+
type: "split-area",
|
|
153
|
+
sizes: [0.5, 0.5],
|
|
154
|
+
orientation: "horizontal",
|
|
155
|
+
children: [
|
|
156
|
+
workspace.detail.main,
|
|
157
|
+
{
|
|
158
|
+
type: "tab-area",
|
|
159
|
+
currentIndex: 0,
|
|
160
|
+
widgets: [id],
|
|
161
|
+
},
|
|
162
|
+
],
|
|
163
|
+
},
|
|
164
|
+
},
|
|
165
|
+
master: workspace.master,
|
|
166
|
+
};
|
|
167
|
+
} else if (
|
|
168
|
+
workspace.detail.main.type === "split-area" &&
|
|
169
|
+
workspace.detail.main.orientation === "horizontal"
|
|
170
|
+
) {
|
|
171
|
+
return {
|
|
172
|
+
sizes: workspace.sizes,
|
|
173
|
+
viewers: {
|
|
174
|
+
...workspace.viewers,
|
|
175
|
+
[id]: config,
|
|
176
|
+
},
|
|
177
|
+
detail: {
|
|
178
|
+
main: {
|
|
179
|
+
type: "split-area",
|
|
180
|
+
sizes: normalize([
|
|
181
|
+
...normalize(workspace.detail.main.sizes),
|
|
182
|
+
GOLDEN_RATIO,
|
|
183
|
+
]),
|
|
184
|
+
orientation: "horizontal",
|
|
185
|
+
children: [
|
|
186
|
+
...workspace.detail.main.children,
|
|
187
|
+
{
|
|
188
|
+
type: "tab-area",
|
|
189
|
+
currentIndex: 0,
|
|
190
|
+
widgets: [id],
|
|
191
|
+
},
|
|
192
|
+
],
|
|
193
|
+
},
|
|
194
|
+
},
|
|
195
|
+
master: workspace.master,
|
|
196
|
+
};
|
|
197
|
+
} else {
|
|
198
|
+
throw new Error("Unknown workspace state");
|
|
199
|
+
}
|
|
50
200
|
}
|
|
51
201
|
|
|
52
202
|
export class PerspectiveWorkspace extends SplitPanel {
|
|
53
203
|
private dockpanel: PerspectiveDockPanel;
|
|
54
204
|
private detailPanel: Panel;
|
|
55
205
|
private masterPanel: SplitPanel;
|
|
206
|
+
client: psp.Client[];
|
|
56
207
|
element: HTMLElement;
|
|
57
208
|
menu_elem: HTMLElement;
|
|
58
|
-
private _tables: ObservableMap<string, psp.Table | Promise<psp.Table>>;
|
|
59
209
|
private listeners: WeakMap<PerspectiveViewerWidget, () => void>;
|
|
60
210
|
private indicator: HTMLElement;
|
|
61
211
|
private commands: CommandRegistry;
|
|
62
212
|
private _menu?: WorkspaceMenu;
|
|
63
|
-
private _minimizedLayoutSlots?: DockPanel.ILayoutConfig
|
|
213
|
+
private _minimizedLayoutSlots?: Promise<DockPanel.ILayoutConfig>;
|
|
64
214
|
private _minimizedLayout?: DockPanel.ILayoutConfig;
|
|
65
215
|
private _maximizedWidget?: PerspectiveViewerWidget;
|
|
66
|
-
private _last_updated_state?: PerspectiveWorkspaceConfig
|
|
216
|
+
private _last_updated_state?: PerspectiveWorkspaceConfig;
|
|
217
|
+
_mutex: AsyncMutex;
|
|
67
218
|
// private _context_menu?: Menu & { init_overlay?: () => void };
|
|
68
219
|
|
|
69
220
|
constructor(element: HTMLElement) {
|
|
@@ -76,17 +227,15 @@ export class PerspectiveWorkspace extends SplitPanel {
|
|
|
76
227
|
this.detailPanel.addWidget(this.dockpanel);
|
|
77
228
|
this.masterPanel = new SplitPanel({ orientation: "vertical" });
|
|
78
229
|
this.masterPanel.addClass("master-panel");
|
|
230
|
+
this._mutex = new AsyncMutex();
|
|
79
231
|
this.dockpanel.layoutModified.connect(() => {
|
|
80
232
|
this.workspaceUpdated();
|
|
81
233
|
});
|
|
82
234
|
|
|
83
235
|
this.addWidget(this.detailPanel);
|
|
84
|
-
this.spacing = 6;
|
|
85
236
|
this.element = element;
|
|
86
237
|
this.listeners = new WeakMap();
|
|
87
|
-
this.
|
|
88
|
-
this._tables.addSetListener(this._set_listener.bind(this));
|
|
89
|
-
this._tables.addDeleteListener(this._delete_listener.bind(this));
|
|
238
|
+
this.client = [];
|
|
90
239
|
this.indicator = this.init_indicator();
|
|
91
240
|
this.commands = createCommands(this, this.indicator);
|
|
92
241
|
this.menu_elem = document.createElement("perspective-workspace-menu");
|
|
@@ -112,6 +261,10 @@ export class PerspectiveWorkspace extends SplitPanel {
|
|
|
112
261
|
}
|
|
113
262
|
|
|
114
263
|
init_indicator() {
|
|
264
|
+
const exists = document.querySelector("body > perspective-indicator");
|
|
265
|
+
if (exists) {
|
|
266
|
+
return exists as HTMLElement;
|
|
267
|
+
}
|
|
115
268
|
const indicator = document.createElement("perspective-indicator");
|
|
116
269
|
indicator.style.position = "fixed";
|
|
117
270
|
indicator.style.pointerEvents = "none";
|
|
@@ -136,147 +289,158 @@ export class PerspectiveWorkspace extends SplitPanel {
|
|
|
136
289
|
*
|
|
137
290
|
*/
|
|
138
291
|
|
|
139
|
-
|
|
140
|
-
this.
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
async save() {
|
|
160
|
-
const is_settings = this.dockpanel.mode === "single-document";
|
|
161
|
-
let detail = is_settings
|
|
162
|
-
? this._minimizedLayoutSlots
|
|
163
|
-
: PerspectiveDockPanel.mapWidgets(
|
|
164
|
-
(widget) =>
|
|
165
|
-
// this.getWidgetByName(widget)!.viewer.getAttribute("slot")
|
|
166
|
-
(widget as PerspectiveViewerWidget).viewer.getAttribute(
|
|
167
|
-
"slot",
|
|
168
|
-
),
|
|
169
|
-
this.dockpanel.saveLayout(),
|
|
170
|
-
);
|
|
171
|
-
|
|
172
|
-
const layout = {
|
|
173
|
-
sizes: [...this.relativeSizes()],
|
|
174
|
-
detail,
|
|
175
|
-
master: undefined as
|
|
176
|
-
| { widgets: string[]; sizes: number[] }
|
|
177
|
-
| undefined,
|
|
178
|
-
};
|
|
179
|
-
|
|
180
|
-
if (this.masterPanel.isAttached) {
|
|
181
|
-
const master = {
|
|
182
|
-
widgets: this.masterPanel.widgets.map(
|
|
183
|
-
(widget) =>
|
|
184
|
-
(widget as PerspectiveViewerWidget).viewer.getAttribute(
|
|
185
|
-
"slot",
|
|
186
|
-
)!,
|
|
187
|
-
),
|
|
188
|
-
sizes: [...this.masterPanel.relativeSizes()],
|
|
292
|
+
async save(): Promise<PerspectiveWorkspaceConfig> {
|
|
293
|
+
return await this._mutex.lock(async () => {
|
|
294
|
+
const is_settings = this.dockpanel.mode === "single-document";
|
|
295
|
+
let detail = is_settings
|
|
296
|
+
? await this._minimizedLayoutSlots
|
|
297
|
+
: await PerspectiveDockPanel.mapWidgets(
|
|
298
|
+
async (widget) =>
|
|
299
|
+
(
|
|
300
|
+
widget as PerspectiveViewerWidget
|
|
301
|
+
).viewer.getAttribute("slot"),
|
|
302
|
+
this.dockpanel.saveLayout(),
|
|
303
|
+
);
|
|
304
|
+
|
|
305
|
+
const layout: PerspectiveWorkspaceConfig = {
|
|
306
|
+
sizes: [...this.relativeSizes()],
|
|
307
|
+
detail: detail as { main: PerspectiveLayout },
|
|
308
|
+
viewers: {},
|
|
309
|
+
master: undefined as
|
|
310
|
+
| { widgets: string[]; sizes: number[] }
|
|
311
|
+
| undefined,
|
|
189
312
|
};
|
|
190
|
-
layout.master = master;
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
const viewers: Record<string, ViewerConfigUpdate> = {};
|
|
194
|
-
for (const widget of this.masterPanel.widgets) {
|
|
195
|
-
const psp_widget = widget as PerspectiveViewerWidget;
|
|
196
|
-
viewers[psp_widget.viewer.getAttribute("slot")!] =
|
|
197
|
-
await psp_widget.save();
|
|
198
|
-
}
|
|
199
313
|
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
314
|
+
if (this.masterPanel.isAttached) {
|
|
315
|
+
const master = {
|
|
316
|
+
widgets: this.masterPanel.widgets.map(
|
|
317
|
+
(widget) =>
|
|
318
|
+
(
|
|
319
|
+
widget as PerspectiveViewerWidget
|
|
320
|
+
).viewer.getAttribute("slot")!,
|
|
321
|
+
),
|
|
322
|
+
sizes: [...this.masterPanel.relativeSizes()],
|
|
323
|
+
};
|
|
324
|
+
|
|
325
|
+
layout.master = master;
|
|
326
|
+
}
|
|
203
327
|
|
|
204
|
-
|
|
205
|
-
|
|
328
|
+
// const viewers: Record<string, ViewerConfigUpdate> = {};
|
|
329
|
+
for (const widget of this.masterPanel.widgets) {
|
|
206
330
|
const psp_widget = widget as PerspectiveViewerWidget;
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
331
|
+
layout.viewers[psp_widget.viewer.getAttribute("slot")!] =
|
|
332
|
+
await psp_widget.save();
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
const widgets = PerspectiveDockPanel.getWidgets(
|
|
336
|
+
is_settings
|
|
337
|
+
? this._minimizedLayout!
|
|
338
|
+
: this.dockpanel.saveLayout(),
|
|
339
|
+
);
|
|
340
|
+
|
|
341
|
+
await Promise.all(
|
|
342
|
+
widgets.map(async (widget) => {
|
|
343
|
+
const psp_widget = widget as PerspectiveViewerWidget;
|
|
344
|
+
const slot = psp_widget.viewer.getAttribute("slot")!;
|
|
345
|
+
layout.viewers[slot] = await psp_widget.save();
|
|
346
|
+
layout.viewers[slot]!.settings = false;
|
|
347
|
+
}),
|
|
348
|
+
);
|
|
212
349
|
|
|
213
|
-
|
|
350
|
+
return layout;
|
|
351
|
+
});
|
|
214
352
|
}
|
|
215
353
|
|
|
216
|
-
async restore(value: PerspectiveWorkspaceConfig
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
354
|
+
async restore(value: PerspectiveWorkspaceConfig) {
|
|
355
|
+
await this._mutex.lock(async () => {
|
|
356
|
+
const {
|
|
357
|
+
sizes,
|
|
358
|
+
master,
|
|
359
|
+
detail,
|
|
360
|
+
viewers: viewer_configs = {},
|
|
361
|
+
} = structuredClone(value);
|
|
362
|
+
|
|
363
|
+
if (master && master.widgets!.length > 0) {
|
|
364
|
+
this.setupMasterPanel(sizes || DEFAULT_WORKSPACE_SIZE);
|
|
365
|
+
} else {
|
|
366
|
+
if (this.masterPanel.isAttached) {
|
|
367
|
+
this.detailPanel.removeClass("has-master-panel");
|
|
368
|
+
this.masterPanel.close();
|
|
369
|
+
}
|
|
223
370
|
|
|
224
|
-
|
|
225
|
-
this.setupMasterPanel(sizes || DEFAULT_WORKSPACE_SIZE);
|
|
226
|
-
} else {
|
|
227
|
-
if (this.masterPanel.isAttached) {
|
|
228
|
-
this.detailPanel.removeClass("has-master-panel");
|
|
229
|
-
this.masterPanel.close();
|
|
371
|
+
this.addWidget(this.detailPanel);
|
|
230
372
|
}
|
|
231
373
|
|
|
232
|
-
|
|
233
|
-
}
|
|
374
|
+
let tasks: Promise<void>[] = [];
|
|
234
375
|
|
|
235
|
-
|
|
376
|
+
// Using ES generators as context managers ..
|
|
377
|
+
for (const viewers of this._capture_viewers()) {
|
|
378
|
+
for (const widgets of this._capture_widgets()) {
|
|
379
|
+
for (const v of viewers) {
|
|
380
|
+
v.removeAttribute("class");
|
|
381
|
+
}
|
|
236
382
|
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
383
|
+
const callback = this._restore_callback.bind(
|
|
384
|
+
this,
|
|
385
|
+
viewer_configs,
|
|
386
|
+
viewers,
|
|
387
|
+
widgets,
|
|
388
|
+
);
|
|
243
389
|
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
390
|
+
if (detail) {
|
|
391
|
+
const detailLayout =
|
|
392
|
+
await PerspectiveDockPanel.mapWidgets(
|
|
393
|
+
(name: string) =>
|
|
394
|
+
callback.bind(this, false)(name),
|
|
395
|
+
detail,
|
|
396
|
+
);
|
|
397
|
+
|
|
398
|
+
this.dockpanel.mode = "multiple-document";
|
|
399
|
+
this.dockpanel.restoreLayout(detailLayout);
|
|
400
|
+
tasks = tasks.concat(
|
|
401
|
+
PerspectiveDockPanel.getWidgets(detailLayout).map(
|
|
402
|
+
(x) =>
|
|
403
|
+
(
|
|
404
|
+
x as PerspectiveViewerWidget
|
|
405
|
+
).viewer.flush(),
|
|
406
|
+
),
|
|
407
|
+
);
|
|
408
|
+
}
|
|
250
409
|
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
(name: string) => callback.bind(this, false)(name),
|
|
254
|
-
detail,
|
|
255
|
-
);
|
|
410
|
+
if (master) {
|
|
411
|
+
// tasks = tasks.concat(
|
|
256
412
|
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
(
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
}
|
|
413
|
+
const tasks2: any[] = [],
|
|
414
|
+
names: string[] = [];
|
|
415
|
+
master.widgets!.map((name) => {
|
|
416
|
+
names.push(name);
|
|
417
|
+
tasks2.push(callback.bind(this, true)(name));
|
|
418
|
+
return name;
|
|
419
|
+
});
|
|
265
420
|
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
421
|
+
// return name;
|
|
422
|
+
tasks.push(
|
|
423
|
+
Promise.all(tasks2).then((x) => {
|
|
424
|
+
master.widgets = master.widgets!.map((name) => {
|
|
425
|
+
const idx = names.indexOf(name);
|
|
426
|
+
const task = x[idx];
|
|
427
|
+
return task;
|
|
428
|
+
});
|
|
429
|
+
}),
|
|
430
|
+
);
|
|
272
431
|
|
|
273
|
-
|
|
274
|
-
|
|
432
|
+
// const widgets = await Promise.all(tasks);
|
|
433
|
+
|
|
434
|
+
// );
|
|
435
|
+
|
|
436
|
+
master.sizes &&
|
|
437
|
+
this.masterPanel.setRelativeSizes(master.sizes);
|
|
438
|
+
}
|
|
275
439
|
}
|
|
276
440
|
}
|
|
277
|
-
}
|
|
278
441
|
|
|
279
|
-
|
|
442
|
+
await Promise.all(tasks);
|
|
443
|
+
});
|
|
280
444
|
}
|
|
281
445
|
|
|
282
446
|
*_capture_widgets() {
|
|
@@ -313,8 +477,8 @@ export class PerspectiveWorkspace extends SplitPanel {
|
|
|
313
477
|
}
|
|
314
478
|
}
|
|
315
479
|
|
|
316
|
-
_restore_callback(
|
|
317
|
-
viewers: Record<string,
|
|
480
|
+
async _restore_callback(
|
|
481
|
+
viewers: Record<string, psp_viewer.ViewerConfigUpdate>,
|
|
318
482
|
starting_viewers: HTMLPerspectiveViewerElement[],
|
|
319
483
|
starting_widgets: PerspectiveViewerWidget[],
|
|
320
484
|
master: boolean,
|
|
@@ -331,16 +495,15 @@ export class PerspectiveWorkspace extends SplitPanel {
|
|
|
331
495
|
if (viewer) {
|
|
332
496
|
widget = starting_widgets.find((x) => x.viewer === viewer);
|
|
333
497
|
if (widget) {
|
|
334
|
-
widget.
|
|
335
|
-
widget.restore({ ...viewer_config });
|
|
498
|
+
await widget.restore({ ...viewer_config });
|
|
336
499
|
} else {
|
|
337
|
-
widget = this._createWidget({
|
|
500
|
+
widget = await this._createWidget({
|
|
338
501
|
config: { ...viewer_config },
|
|
339
502
|
viewer,
|
|
340
503
|
});
|
|
341
504
|
}
|
|
342
505
|
} else if (viewer_config) {
|
|
343
|
-
widget = this._createWidgetAndNode({
|
|
506
|
+
widget = await this._createWidgetAndNode({
|
|
344
507
|
config: { ...viewer_config },
|
|
345
508
|
slot: widgetName,
|
|
346
509
|
});
|
|
@@ -380,13 +543,6 @@ export class PerspectiveWorkspace extends SplitPanel {
|
|
|
380
543
|
} else {
|
|
381
544
|
this._validate(table);
|
|
382
545
|
}
|
|
383
|
-
|
|
384
|
-
this.getAllWidgets().forEach((widget) => {
|
|
385
|
-
const psp_widget = widget as PerspectiveViewerWidget;
|
|
386
|
-
if (psp_widget.viewer.getAttribute("table") === name) {
|
|
387
|
-
psp_widget.load(table);
|
|
388
|
-
}
|
|
389
|
-
});
|
|
390
546
|
}
|
|
391
547
|
|
|
392
548
|
_delete_listener(name: string) {
|
|
@@ -398,7 +554,7 @@ export class PerspectiveWorkspace extends SplitPanel {
|
|
|
398
554
|
});
|
|
399
555
|
}
|
|
400
556
|
|
|
401
|
-
update_widget_for_viewer(viewer: HTMLPerspectiveViewerElement) {
|
|
557
|
+
async update_widget_for_viewer(viewer: HTMLPerspectiveViewerElement) {
|
|
402
558
|
let slot_name = viewer.getAttribute("slot");
|
|
403
559
|
if (!slot_name) {
|
|
404
560
|
slot_name = this._gen_id();
|
|
@@ -413,10 +569,8 @@ export class PerspectiveWorkspace extends SplitPanel {
|
|
|
413
569
|
`Undocked ${viewer.outerHTML}, creating default layout`,
|
|
414
570
|
);
|
|
415
571
|
|
|
416
|
-
const widget = this._createWidget({
|
|
417
|
-
config: {
|
|
418
|
-
table: viewer.getAttribute("table")!,
|
|
419
|
-
},
|
|
572
|
+
const widget = await this._createWidget({
|
|
573
|
+
// config: {},
|
|
420
574
|
viewer,
|
|
421
575
|
});
|
|
422
576
|
|
|
@@ -462,7 +616,7 @@ export class PerspectiveWorkspace extends SplitPanel {
|
|
|
462
616
|
const config = await widget.save();
|
|
463
617
|
config.settings = false;
|
|
464
618
|
config.title = config.title ? `${config.title} (*)` : "";
|
|
465
|
-
const duplicate = this._createWidgetAndNode({
|
|
619
|
+
const duplicate = await this._createWidgetAndNode({
|
|
466
620
|
config,
|
|
467
621
|
slot: undefined,
|
|
468
622
|
});
|
|
@@ -472,7 +626,7 @@ export class PerspectiveWorkspace extends SplitPanel {
|
|
|
472
626
|
ref: widget,
|
|
473
627
|
});
|
|
474
628
|
|
|
475
|
-
await duplicate.
|
|
629
|
+
await duplicate.viewer.flush();
|
|
476
630
|
}
|
|
477
631
|
|
|
478
632
|
toggleMasterDetail(widget: PerspectiveViewerWidget) {
|
|
@@ -498,12 +652,14 @@ export class PerspectiveWorkspace extends SplitPanel {
|
|
|
498
652
|
|
|
499
653
|
_maximize(widget: PerspectiveViewerWidget) {
|
|
500
654
|
widget.viewer.classList.add("widget-maximize");
|
|
501
|
-
this._minimizedLayout
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
widget
|
|
505
|
-
|
|
506
|
-
|
|
655
|
+
if (!this._minimizedLayout) {
|
|
656
|
+
this._minimizedLayout = this.dockpanel.saveLayout();
|
|
657
|
+
this._minimizedLayoutSlots = PerspectiveDockPanel.mapWidgets(
|
|
658
|
+
async (widget: PerspectiveViewerWidget) =>
|
|
659
|
+
widget.viewer.getAttribute("slot"),
|
|
660
|
+
this.dockpanel.saveLayout(),
|
|
661
|
+
);
|
|
662
|
+
}
|
|
507
663
|
|
|
508
664
|
this._maximizedWidget = widget;
|
|
509
665
|
this.dockpanel.mode = "single-document";
|
|
@@ -514,6 +670,7 @@ export class PerspectiveWorkspace extends SplitPanel {
|
|
|
514
670
|
this._maximizedWidget!.viewer.classList.remove("widget-maximize");
|
|
515
671
|
this.dockpanel.mode = "multiple-document";
|
|
516
672
|
this.dockpanel.restoreLayout(this._minimizedLayout!);
|
|
673
|
+
this._minimizedLayout = undefined;
|
|
517
674
|
}
|
|
518
675
|
|
|
519
676
|
toggleSingleDocument(widget: PerspectiveViewerWidget) {
|
|
@@ -657,22 +814,29 @@ export class PerspectiveWorkspace extends SplitPanel {
|
|
|
657
814
|
},
|
|
658
815
|
);
|
|
659
816
|
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
table,
|
|
665
|
-
widget_name: widget.viewer.getAttribute("slot"),
|
|
666
|
-
};
|
|
667
|
-
} else {
|
|
668
|
-
args = { table };
|
|
669
|
-
}
|
|
817
|
+
(async () => {
|
|
818
|
+
const names = await Promise.all(
|
|
819
|
+
this.client.map((c) => c.get_hosted_table_names()),
|
|
820
|
+
).then((x) => x.flat());
|
|
670
821
|
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
822
|
+
for (const table of names) {
|
|
823
|
+
let args;
|
|
824
|
+
if (widget !== null) {
|
|
825
|
+
args = {
|
|
826
|
+
table,
|
|
827
|
+
widget_name:
|
|
828
|
+
widget.viewer.getAttribute("slot"),
|
|
829
|
+
};
|
|
830
|
+
} else {
|
|
831
|
+
args = { table };
|
|
832
|
+
}
|
|
833
|
+
|
|
834
|
+
submenu.insertItem(0, {
|
|
835
|
+
command: "workspace:new",
|
|
836
|
+
args,
|
|
837
|
+
});
|
|
838
|
+
}
|
|
839
|
+
})();
|
|
676
840
|
|
|
677
841
|
const widgets = PerspectiveDockPanel.getWidgets(
|
|
678
842
|
this.dockpanel.saveLayout(),
|
|
@@ -815,38 +979,43 @@ export class PerspectiveWorkspace extends SplitPanel {
|
|
|
815
979
|
this.setRelativeSizes(sizes);
|
|
816
980
|
}
|
|
817
981
|
|
|
818
|
-
addViewer(
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
if (!this.masterPanel.isAttached) {
|
|
827
|
-
this.setupMasterPanel(DEFAULT_WORKSPACE_SIZE);
|
|
982
|
+
async addViewer(
|
|
983
|
+
config: psp_viewer.ViewerConfigUpdate,
|
|
984
|
+
is_global_filter?: boolean,
|
|
985
|
+
) {
|
|
986
|
+
await this._mutex.lock(async () => {
|
|
987
|
+
if (this.dockpanel.mode === "single-document") {
|
|
988
|
+
const _task = this._maximizedWidget!.viewer.toggleConfig(false);
|
|
989
|
+
this._unmaximize();
|
|
828
990
|
}
|
|
829
991
|
|
|
830
|
-
this.
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
992
|
+
const widget = await this._createWidgetAndNode({ config });
|
|
993
|
+
if (is_global_filter) {
|
|
994
|
+
if (!this.masterPanel.isAttached) {
|
|
995
|
+
this.setupMasterPanel(DEFAULT_WORKSPACE_SIZE);
|
|
996
|
+
}
|
|
997
|
+
|
|
998
|
+
this.masterPanel.addWidget(widget);
|
|
999
|
+
} else {
|
|
1000
|
+
if (!this.detailPanel.isAttached) {
|
|
1001
|
+
this.addWidget(this.detailPanel);
|
|
1002
|
+
}
|
|
1003
|
+
this.dockpanel.addWidget(widget, { mode: "split-right" });
|
|
834
1004
|
}
|
|
835
|
-
this.dockpanel.addWidget(widget, { mode: "split-right" });
|
|
836
|
-
}
|
|
837
1005
|
|
|
838
|
-
|
|
1006
|
+
this.update();
|
|
1007
|
+
});
|
|
839
1008
|
}
|
|
840
1009
|
|
|
841
1010
|
/*********************************************************************
|
|
842
1011
|
* Widget helper methods
|
|
843
1012
|
*/
|
|
844
1013
|
|
|
845
|
-
_createWidgetAndNode({
|
|
1014
|
+
async _createWidgetAndNode({
|
|
846
1015
|
config,
|
|
847
1016
|
slot: slotname,
|
|
848
1017
|
}: {
|
|
849
|
-
config:
|
|
1018
|
+
config: psp_viewer.ViewerConfigUpdate;
|
|
850
1019
|
slot?: string;
|
|
851
1020
|
}) {
|
|
852
1021
|
const node = this._createNode(slotname);
|
|
@@ -861,11 +1030,19 @@ export class PerspectiveWorkspace extends SplitPanel {
|
|
|
861
1030
|
viewer.setAttribute("table", table);
|
|
862
1031
|
}
|
|
863
1032
|
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
1033
|
+
for (const client of this.client) {
|
|
1034
|
+
const tables = await client.get_hosted_table_names();
|
|
1035
|
+
if (table && tables.indexOf(table) > -1) {
|
|
1036
|
+
await viewer.load(client);
|
|
1037
|
+
return await this._createWidget({
|
|
1038
|
+
config,
|
|
1039
|
+
elem: node as HTMLElement,
|
|
1040
|
+
viewer,
|
|
1041
|
+
});
|
|
1042
|
+
}
|
|
1043
|
+
}
|
|
1044
|
+
|
|
1045
|
+
throw new Error(`Table "${table}" not found`);
|
|
869
1046
|
}
|
|
870
1047
|
|
|
871
1048
|
_gen_id() {
|
|
@@ -895,12 +1072,12 @@ export class PerspectiveWorkspace extends SplitPanel {
|
|
|
895
1072
|
return node as HTMLElement;
|
|
896
1073
|
}
|
|
897
1074
|
|
|
898
|
-
_createWidget({
|
|
1075
|
+
async _createWidget({
|
|
899
1076
|
config,
|
|
900
1077
|
elem,
|
|
901
1078
|
viewer,
|
|
902
1079
|
}: {
|
|
903
|
-
config
|
|
1080
|
+
config?: psp_viewer.ViewerConfigUpdate;
|
|
904
1081
|
elem?: Element;
|
|
905
1082
|
viewer: HTMLPerspectiveViewerElement;
|
|
906
1083
|
}) {
|
|
@@ -915,25 +1092,24 @@ export class PerspectiveWorkspace extends SplitPanel {
|
|
|
915
1092
|
}
|
|
916
1093
|
}
|
|
917
1094
|
|
|
918
|
-
const
|
|
919
|
-
viewer.
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
const widget = new PerspectiveViewerWidget({ node, viewer });
|
|
923
|
-
widget.task = (async () => {
|
|
924
|
-
if (table) {
|
|
925
|
-
widget.load(table);
|
|
1095
|
+
const onAttach = () => {
|
|
1096
|
+
if (widget.viewer.parentElement !== this.element) {
|
|
1097
|
+
this.element.appendChild(widget.viewer);
|
|
926
1098
|
}
|
|
927
1099
|
|
|
1100
|
+
const event = new CustomEvent("workspace-new-view", {
|
|
1101
|
+
detail: { config, widget },
|
|
1102
|
+
});
|
|
1103
|
+
|
|
1104
|
+
this.element.dispatchEvent(event);
|
|
1105
|
+
};
|
|
1106
|
+
|
|
1107
|
+
const widget = new PerspectiveViewerWidget({ node, viewer, onAttach });
|
|
1108
|
+
if (config) {
|
|
928
1109
|
await widget.restore(config);
|
|
929
|
-
}
|
|
1110
|
+
}
|
|
930
1111
|
|
|
931
|
-
const event = new CustomEvent("workspace-new-view", {
|
|
932
|
-
detail: { config, widget },
|
|
933
|
-
});
|
|
934
|
-
this.element.dispatchEvent(event);
|
|
935
1112
|
widget.title.closable = true;
|
|
936
|
-
this.element.appendChild(widget.viewer);
|
|
937
1113
|
this._addWidgetEventListeners(widget);
|
|
938
1114
|
return widget;
|
|
939
1115
|
}
|
|
@@ -943,12 +1119,6 @@ export class PerspectiveWorkspace extends SplitPanel {
|
|
|
943
1119
|
this.listeners.get(widget)!();
|
|
944
1120
|
}
|
|
945
1121
|
|
|
946
|
-
const settings = (event: CustomEvent) => {
|
|
947
|
-
if (!event.detail && this.dockpanel.mode === "single-document") {
|
|
948
|
-
this._unmaximize();
|
|
949
|
-
}
|
|
950
|
-
};
|
|
951
|
-
|
|
952
1122
|
const contextMenu = (event: MouseEvent) =>
|
|
953
1123
|
this.showContextMenu(widget, event);
|
|
954
1124
|
|
|
@@ -962,20 +1132,86 @@ export class PerspectiveWorkspace extends SplitPanel {
|
|
|
962
1132
|
: event.detail;
|
|
963
1133
|
|
|
964
1134
|
widget.title.label = config.title;
|
|
1135
|
+
widget._title = config.title;
|
|
965
1136
|
widget._is_pivoted = config.group_by?.length > 0;
|
|
966
1137
|
};
|
|
967
1138
|
|
|
968
1139
|
widget.node.addEventListener("contextmenu", contextMenu);
|
|
969
|
-
|
|
1140
|
+
|
|
1141
|
+
// Settings
|
|
1142
|
+
const settings_before = (event: CustomEvent) => {
|
|
1143
|
+
if (event.detail && this.dockpanel.mode !== "single-document") {
|
|
1144
|
+
this._maximize(widget);
|
|
1145
|
+
}
|
|
1146
|
+
};
|
|
1147
|
+
|
|
1148
|
+
const settings_after = (event: CustomEvent) => {
|
|
1149
|
+
if (!event.detail && this.dockpanel.mode === "single-document") {
|
|
1150
|
+
this._unmaximize();
|
|
1151
|
+
}
|
|
1152
|
+
};
|
|
1153
|
+
|
|
1154
|
+
widget.viewer.addEventListener(
|
|
1155
|
+
"perspective-status-indicator-click",
|
|
1156
|
+
(event) => {
|
|
1157
|
+
widget._titlebar_callback?.(event as MouseEvent);
|
|
1158
|
+
},
|
|
1159
|
+
);
|
|
1160
|
+
|
|
1161
|
+
widget.viewer.addEventListener(
|
|
1162
|
+
"perspective-toggle-settings-before",
|
|
1163
|
+
settings_before,
|
|
1164
|
+
);
|
|
1165
|
+
|
|
1166
|
+
widget.viewer.addEventListener(
|
|
1167
|
+
"perspective-toggle-settings",
|
|
1168
|
+
settings_after,
|
|
1169
|
+
);
|
|
1170
|
+
|
|
1171
|
+
const delete_before = () => {
|
|
1172
|
+
if (!widget._deleted) {
|
|
1173
|
+
widget._deleted = true;
|
|
1174
|
+
widget.close();
|
|
1175
|
+
}
|
|
1176
|
+
};
|
|
1177
|
+
|
|
1178
|
+
const delete_after = (event: CustomEvent) => {
|
|
1179
|
+
widget._titlebar?.handleEvent(event.detail as PointerEvent);
|
|
1180
|
+
};
|
|
1181
|
+
|
|
1182
|
+
widget.viewer.addEventListener(
|
|
1183
|
+
"perspective-table-delete-before",
|
|
1184
|
+
delete_before,
|
|
1185
|
+
);
|
|
1186
|
+
|
|
1187
|
+
widget.viewer.addEventListener(
|
|
1188
|
+
"perspective-statusbar-pointerdown",
|
|
1189
|
+
delete_after,
|
|
1190
|
+
);
|
|
970
1191
|
|
|
971
1192
|
// @ts-ignore
|
|
972
1193
|
widget.viewer.addEventListener("perspective-config-update", updated);
|
|
973
1194
|
|
|
974
1195
|
this.listeners.set(widget, () => {
|
|
975
1196
|
widget.node.removeEventListener("contextmenu", contextMenu);
|
|
1197
|
+
widget.viewer.removeEventListener(
|
|
1198
|
+
"perspective-table-delete-before",
|
|
1199
|
+
delete_before,
|
|
1200
|
+
);
|
|
1201
|
+
|
|
1202
|
+
widget.viewer.removeEventListener(
|
|
1203
|
+
"perspective-table-delete",
|
|
1204
|
+
delete_after,
|
|
1205
|
+
);
|
|
1206
|
+
|
|
1207
|
+
widget.viewer.removeEventListener(
|
|
1208
|
+
"perspective-toggle-settings",
|
|
1209
|
+
settings_before,
|
|
1210
|
+
);
|
|
1211
|
+
|
|
976
1212
|
widget.viewer.removeEventListener(
|
|
977
1213
|
"perspective-toggle-settings",
|
|
978
|
-
|
|
1214
|
+
settings_after,
|
|
979
1215
|
);
|
|
980
1216
|
|
|
981
1217
|
// @ts-ignore
|
|
@@ -1007,7 +1243,11 @@ export class PerspectiveWorkspace extends SplitPanel {
|
|
|
1007
1243
|
*
|
|
1008
1244
|
*/
|
|
1009
1245
|
|
|
1246
|
+
_throttle?: DebouncedFuncLeading<() => Promise<void>>;
|
|
1247
|
+
|
|
1010
1248
|
async workspaceUpdated() {
|
|
1249
|
+
// if (!this._throttle) {
|
|
1250
|
+
// this._throttle = throttle(async () => {
|
|
1011
1251
|
const layout = await this.save();
|
|
1012
1252
|
if (layout) {
|
|
1013
1253
|
if (this._last_updated_state) {
|
|
@@ -1017,18 +1257,17 @@ export class PerspectiveWorkspace extends SplitPanel {
|
|
|
1017
1257
|
}
|
|
1018
1258
|
|
|
1019
1259
|
this._last_updated_state =
|
|
1020
|
-
layout as any as PerspectiveWorkspaceConfig
|
|
1021
|
-
|
|
1022
|
-
const tables: Record<string, psp.Table | Promise<psp.Table>> = {};
|
|
1023
|
-
this.tables.forEach((value, key) => {
|
|
1024
|
-
tables[key] = value;
|
|
1025
|
-
});
|
|
1260
|
+
layout as any as PerspectiveWorkspaceConfig;
|
|
1026
1261
|
|
|
1027
1262
|
this.element.dispatchEvent(
|
|
1028
1263
|
new CustomEvent("workspace-layout-update", {
|
|
1029
|
-
detail: {
|
|
1264
|
+
detail: { layout },
|
|
1030
1265
|
}),
|
|
1031
1266
|
);
|
|
1032
1267
|
}
|
|
1268
|
+
// }, 0);
|
|
1269
|
+
// }
|
|
1270
|
+
|
|
1271
|
+
// this._throttle();
|
|
1033
1272
|
}
|
|
1034
1273
|
}
|