@perspective-dev/workspace 4.0.1 → 4.1.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.
- 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 +479 -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
|
+
);
|
|
431
|
+
|
|
432
|
+
// const widgets = await Promise.all(tasks);
|
|
433
|
+
|
|
434
|
+
// );
|
|
272
435
|
|
|
273
|
-
|
|
274
|
-
|
|
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,31 @@ export class PerspectiveWorkspace extends SplitPanel {
|
|
|
657
814
|
},
|
|
658
815
|
);
|
|
659
816
|
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
args
|
|
669
|
-
|
|
817
|
+
(async () => {
|
|
818
|
+
for (const table of (
|
|
819
|
+
await Promise.all(
|
|
820
|
+
this.client.map((client) =>
|
|
821
|
+
client.get_hosted_table_names(),
|
|
822
|
+
),
|
|
823
|
+
)
|
|
824
|
+
).map((x) => x.flatMap((x: any) => x))) {
|
|
825
|
+
let args;
|
|
826
|
+
if (widget !== null) {
|
|
827
|
+
args = {
|
|
828
|
+
table,
|
|
829
|
+
widget_name:
|
|
830
|
+
widget.viewer.getAttribute("slot"),
|
|
831
|
+
};
|
|
832
|
+
} else {
|
|
833
|
+
args = { table };
|
|
834
|
+
}
|
|
670
835
|
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
836
|
+
submenu.insertItem(0, {
|
|
837
|
+
command: "workspace:new",
|
|
838
|
+
args,
|
|
839
|
+
});
|
|
840
|
+
}
|
|
841
|
+
})();
|
|
676
842
|
|
|
677
843
|
const widgets = PerspectiveDockPanel.getWidgets(
|
|
678
844
|
this.dockpanel.saveLayout(),
|
|
@@ -815,38 +981,43 @@ export class PerspectiveWorkspace extends SplitPanel {
|
|
|
815
981
|
this.setRelativeSizes(sizes);
|
|
816
982
|
}
|
|
817
983
|
|
|
818
|
-
addViewer(
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
if (!this.masterPanel.isAttached) {
|
|
827
|
-
this.setupMasterPanel(DEFAULT_WORKSPACE_SIZE);
|
|
984
|
+
async addViewer(
|
|
985
|
+
config: psp_viewer.ViewerConfigUpdate,
|
|
986
|
+
is_global_filter?: boolean,
|
|
987
|
+
) {
|
|
988
|
+
await this._mutex.lock(async () => {
|
|
989
|
+
if (this.dockpanel.mode === "single-document") {
|
|
990
|
+
const _task = this._maximizedWidget!.viewer.toggleConfig(false);
|
|
991
|
+
this._unmaximize();
|
|
828
992
|
}
|
|
829
993
|
|
|
830
|
-
this.
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
994
|
+
const widget = await this._createWidgetAndNode({ config });
|
|
995
|
+
if (is_global_filter) {
|
|
996
|
+
if (!this.masterPanel.isAttached) {
|
|
997
|
+
this.setupMasterPanel(DEFAULT_WORKSPACE_SIZE);
|
|
998
|
+
}
|
|
999
|
+
|
|
1000
|
+
this.masterPanel.addWidget(widget);
|
|
1001
|
+
} else {
|
|
1002
|
+
if (!this.detailPanel.isAttached) {
|
|
1003
|
+
this.addWidget(this.detailPanel);
|
|
1004
|
+
}
|
|
1005
|
+
this.dockpanel.addWidget(widget, { mode: "split-right" });
|
|
834
1006
|
}
|
|
835
|
-
this.dockpanel.addWidget(widget, { mode: "split-right" });
|
|
836
|
-
}
|
|
837
1007
|
|
|
838
|
-
|
|
1008
|
+
this.update();
|
|
1009
|
+
});
|
|
839
1010
|
}
|
|
840
1011
|
|
|
841
1012
|
/*********************************************************************
|
|
842
1013
|
* Widget helper methods
|
|
843
1014
|
*/
|
|
844
1015
|
|
|
845
|
-
_createWidgetAndNode({
|
|
1016
|
+
async _createWidgetAndNode({
|
|
846
1017
|
config,
|
|
847
1018
|
slot: slotname,
|
|
848
1019
|
}: {
|
|
849
|
-
config:
|
|
1020
|
+
config: psp_viewer.ViewerConfigUpdate;
|
|
850
1021
|
slot?: string;
|
|
851
1022
|
}) {
|
|
852
1023
|
const node = this._createNode(slotname);
|
|
@@ -861,11 +1032,19 @@ export class PerspectiveWorkspace extends SplitPanel {
|
|
|
861
1032
|
viewer.setAttribute("table", table);
|
|
862
1033
|
}
|
|
863
1034
|
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
1035
|
+
for (const client of this.client) {
|
|
1036
|
+
const tables = await client.get_hosted_table_names();
|
|
1037
|
+
if (table && tables.indexOf(table) > -1) {
|
|
1038
|
+
await viewer.load(client);
|
|
1039
|
+
return await this._createWidget({
|
|
1040
|
+
config,
|
|
1041
|
+
elem: node as HTMLElement,
|
|
1042
|
+
viewer,
|
|
1043
|
+
});
|
|
1044
|
+
}
|
|
1045
|
+
}
|
|
1046
|
+
|
|
1047
|
+
throw new Error(`Table "${table}" not found`);
|
|
869
1048
|
}
|
|
870
1049
|
|
|
871
1050
|
_gen_id() {
|
|
@@ -895,12 +1074,12 @@ export class PerspectiveWorkspace extends SplitPanel {
|
|
|
895
1074
|
return node as HTMLElement;
|
|
896
1075
|
}
|
|
897
1076
|
|
|
898
|
-
_createWidget({
|
|
1077
|
+
async _createWidget({
|
|
899
1078
|
config,
|
|
900
1079
|
elem,
|
|
901
1080
|
viewer,
|
|
902
1081
|
}: {
|
|
903
|
-
config
|
|
1082
|
+
config?: psp_viewer.ViewerConfigUpdate;
|
|
904
1083
|
elem?: Element;
|
|
905
1084
|
viewer: HTMLPerspectiveViewerElement;
|
|
906
1085
|
}) {
|
|
@@ -915,25 +1094,24 @@ export class PerspectiveWorkspace extends SplitPanel {
|
|
|
915
1094
|
}
|
|
916
1095
|
}
|
|
917
1096
|
|
|
918
|
-
const
|
|
919
|
-
viewer.
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
const widget = new PerspectiveViewerWidget({ node, viewer });
|
|
923
|
-
widget.task = (async () => {
|
|
924
|
-
if (table) {
|
|
925
|
-
widget.load(table);
|
|
1097
|
+
const onAttach = () => {
|
|
1098
|
+
if (widget.viewer.parentElement !== this.element) {
|
|
1099
|
+
this.element.appendChild(widget.viewer);
|
|
926
1100
|
}
|
|
927
1101
|
|
|
1102
|
+
const event = new CustomEvent("workspace-new-view", {
|
|
1103
|
+
detail: { config, widget },
|
|
1104
|
+
});
|
|
1105
|
+
|
|
1106
|
+
this.element.dispatchEvent(event);
|
|
1107
|
+
};
|
|
1108
|
+
|
|
1109
|
+
const widget = new PerspectiveViewerWidget({ node, viewer, onAttach });
|
|
1110
|
+
if (config) {
|
|
928
1111
|
await widget.restore(config);
|
|
929
|
-
}
|
|
1112
|
+
}
|
|
930
1113
|
|
|
931
|
-
const event = new CustomEvent("workspace-new-view", {
|
|
932
|
-
detail: { config, widget },
|
|
933
|
-
});
|
|
934
|
-
this.element.dispatchEvent(event);
|
|
935
1114
|
widget.title.closable = true;
|
|
936
|
-
this.element.appendChild(widget.viewer);
|
|
937
1115
|
this._addWidgetEventListeners(widget);
|
|
938
1116
|
return widget;
|
|
939
1117
|
}
|
|
@@ -943,12 +1121,6 @@ export class PerspectiveWorkspace extends SplitPanel {
|
|
|
943
1121
|
this.listeners.get(widget)!();
|
|
944
1122
|
}
|
|
945
1123
|
|
|
946
|
-
const settings = (event: CustomEvent) => {
|
|
947
|
-
if (!event.detail && this.dockpanel.mode === "single-document") {
|
|
948
|
-
this._unmaximize();
|
|
949
|
-
}
|
|
950
|
-
};
|
|
951
|
-
|
|
952
1124
|
const contextMenu = (event: MouseEvent) =>
|
|
953
1125
|
this.showContextMenu(widget, event);
|
|
954
1126
|
|
|
@@ -962,20 +1134,86 @@ export class PerspectiveWorkspace extends SplitPanel {
|
|
|
962
1134
|
: event.detail;
|
|
963
1135
|
|
|
964
1136
|
widget.title.label = config.title;
|
|
1137
|
+
widget._title = config.title;
|
|
965
1138
|
widget._is_pivoted = config.group_by?.length > 0;
|
|
966
1139
|
};
|
|
967
1140
|
|
|
968
1141
|
widget.node.addEventListener("contextmenu", contextMenu);
|
|
969
|
-
|
|
1142
|
+
|
|
1143
|
+
// Settings
|
|
1144
|
+
const settings_before = (event: CustomEvent) => {
|
|
1145
|
+
if (event.detail && this.dockpanel.mode !== "single-document") {
|
|
1146
|
+
this._maximize(widget);
|
|
1147
|
+
}
|
|
1148
|
+
};
|
|
1149
|
+
|
|
1150
|
+
const settings_after = (event: CustomEvent) => {
|
|
1151
|
+
if (!event.detail && this.dockpanel.mode === "single-document") {
|
|
1152
|
+
this._unmaximize();
|
|
1153
|
+
}
|
|
1154
|
+
};
|
|
1155
|
+
|
|
1156
|
+
widget.viewer.addEventListener(
|
|
1157
|
+
"perspective-status-indicator-click",
|
|
1158
|
+
(event) => {
|
|
1159
|
+
widget._titlebar_callback?.(event as MouseEvent);
|
|
1160
|
+
},
|
|
1161
|
+
);
|
|
1162
|
+
|
|
1163
|
+
widget.viewer.addEventListener(
|
|
1164
|
+
"perspective-toggle-settings-before",
|
|
1165
|
+
settings_before,
|
|
1166
|
+
);
|
|
1167
|
+
|
|
1168
|
+
widget.viewer.addEventListener(
|
|
1169
|
+
"perspective-toggle-settings",
|
|
1170
|
+
settings_after,
|
|
1171
|
+
);
|
|
1172
|
+
|
|
1173
|
+
const delete_before = () => {
|
|
1174
|
+
if (!widget._deleted) {
|
|
1175
|
+
widget._deleted = true;
|
|
1176
|
+
widget.close();
|
|
1177
|
+
}
|
|
1178
|
+
};
|
|
1179
|
+
|
|
1180
|
+
const delete_after = (event: CustomEvent) => {
|
|
1181
|
+
widget._titlebar?.handleEvent(event.detail as PointerEvent);
|
|
1182
|
+
};
|
|
1183
|
+
|
|
1184
|
+
widget.viewer.addEventListener(
|
|
1185
|
+
"perspective-table-delete-before",
|
|
1186
|
+
delete_before,
|
|
1187
|
+
);
|
|
1188
|
+
|
|
1189
|
+
widget.viewer.addEventListener(
|
|
1190
|
+
"perspective-statusbar-pointerdown",
|
|
1191
|
+
delete_after,
|
|
1192
|
+
);
|
|
970
1193
|
|
|
971
1194
|
// @ts-ignore
|
|
972
1195
|
widget.viewer.addEventListener("perspective-config-update", updated);
|
|
973
1196
|
|
|
974
1197
|
this.listeners.set(widget, () => {
|
|
975
1198
|
widget.node.removeEventListener("contextmenu", contextMenu);
|
|
1199
|
+
widget.viewer.removeEventListener(
|
|
1200
|
+
"perspective-table-delete-before",
|
|
1201
|
+
delete_before,
|
|
1202
|
+
);
|
|
1203
|
+
|
|
1204
|
+
widget.viewer.removeEventListener(
|
|
1205
|
+
"perspective-table-delete",
|
|
1206
|
+
delete_after,
|
|
1207
|
+
);
|
|
1208
|
+
|
|
1209
|
+
widget.viewer.removeEventListener(
|
|
1210
|
+
"perspective-toggle-settings",
|
|
1211
|
+
settings_before,
|
|
1212
|
+
);
|
|
1213
|
+
|
|
976
1214
|
widget.viewer.removeEventListener(
|
|
977
1215
|
"perspective-toggle-settings",
|
|
978
|
-
|
|
1216
|
+
settings_after,
|
|
979
1217
|
);
|
|
980
1218
|
|
|
981
1219
|
// @ts-ignore
|
|
@@ -1007,7 +1245,11 @@ export class PerspectiveWorkspace extends SplitPanel {
|
|
|
1007
1245
|
*
|
|
1008
1246
|
*/
|
|
1009
1247
|
|
|
1248
|
+
_throttle?: DebouncedFuncLeading<() => Promise<void>>;
|
|
1249
|
+
|
|
1010
1250
|
async workspaceUpdated() {
|
|
1251
|
+
// if (!this._throttle) {
|
|
1252
|
+
// this._throttle = throttle(async () => {
|
|
1011
1253
|
const layout = await this.save();
|
|
1012
1254
|
if (layout) {
|
|
1013
1255
|
if (this._last_updated_state) {
|
|
@@ -1017,18 +1259,17 @@ export class PerspectiveWorkspace extends SplitPanel {
|
|
|
1017
1259
|
}
|
|
1018
1260
|
|
|
1019
1261
|
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
|
-
});
|
|
1262
|
+
layout as any as PerspectiveWorkspaceConfig;
|
|
1026
1263
|
|
|
1027
1264
|
this.element.dispatchEvent(
|
|
1028
1265
|
new CustomEvent("workspace-layout-update", {
|
|
1029
|
-
detail: {
|
|
1266
|
+
detail: { layout },
|
|
1030
1267
|
}),
|
|
1031
1268
|
);
|
|
1032
1269
|
}
|
|
1270
|
+
// }, 0);
|
|
1271
|
+
// }
|
|
1272
|
+
|
|
1273
|
+
// this._throttle();
|
|
1033
1274
|
}
|
|
1034
1275
|
}
|