@perspective-dev/workspace 4.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. package/LICENSE.md +193 -0
  2. package/dist/cdn/perspective-workspace.js +45 -0
  3. package/dist/cdn/perspective-workspace.js.map +7 -0
  4. package/dist/css/pro-dark.css +1 -0
  5. package/dist/css/pro.css +1 -0
  6. package/dist/esm/perspective-workspace.d.ts +181 -0
  7. package/dist/esm/perspective-workspace.js +14 -0
  8. package/dist/esm/perspective-workspace.js.map +7 -0
  9. package/dist/esm/utils/custom_elements.d.ts +14 -0
  10. package/dist/esm/utils/observable_map.d.ts +9 -0
  11. package/dist/esm/workspace/commands.d.ts +3 -0
  12. package/dist/esm/workspace/dockpanel.d.ts +13 -0
  13. package/dist/esm/workspace/index.d.ts +1 -0
  14. package/dist/esm/workspace/menu.d.ts +11 -0
  15. package/dist/esm/workspace/tabbar.d.ts +25 -0
  16. package/dist/esm/workspace/tabbarrenderer.d.ts +19 -0
  17. package/dist/esm/workspace/widget.d.ts +27 -0
  18. package/dist/esm/workspace/workspace.d.ts +123 -0
  19. package/package.json +55 -0
  20. package/src/html/workspace.html +11 -0
  21. package/src/less/dockpanel.less +95 -0
  22. package/src/less/injected.less +14 -0
  23. package/src/less/menu.less +128 -0
  24. package/src/less/tabbar.less +366 -0
  25. package/src/less/viewer.less +86 -0
  26. package/src/less/widget.less +40 -0
  27. package/src/less/workspace.less +70 -0
  28. package/src/svg/bookmark-icon.svg +4 -0
  29. package/src/svg/drag-handle.svg +10 -0
  30. package/src/themes/pro-dark.less +139 -0
  31. package/src/themes/pro.less +93 -0
  32. package/src/ts/external.d.ts +21 -0
  33. package/src/ts/external.js +11 -0
  34. package/src/ts/perspective-workspace.ts +306 -0
  35. package/src/ts/utils/custom_elements.ts +95 -0
  36. package/src/ts/utils/observable_map.ts +39 -0
  37. package/src/ts/workspace/commands.ts +269 -0
  38. package/src/ts/workspace/dockpanel.ts +145 -0
  39. package/src/ts/workspace/index.ts +13 -0
  40. package/src/ts/workspace/menu.ts +213 -0
  41. package/src/ts/workspace/tabbar.ts +237 -0
  42. package/src/ts/workspace/tabbarrenderer.ts +91 -0
  43. package/src/ts/workspace/widget.ts +101 -0
  44. package/src/ts/workspace/workspace.ts +1056 -0
@@ -0,0 +1,139 @@
1
+ // ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
2
+ // ┃ ██████ ██████ ██████ █ █ █ █ █ █▄ ▀███ █ ┃
3
+ // ┃ ▄▄▄▄▄█ █▄▄▄▄▄ ▄▄▄▄▄█ ▀▀▀▀▀█▀▀▀▀▀ █ ▀▀▀▀▀█ ████████▌▐███ ███▄ ▀█ █ ▀▀▀▀▀ ┃
4
+ // ┃ █▀▀▀▀▀ █▀▀▀▀▀ █▀██▀▀ ▄▄▄▄▄ █ ▄▄▄▄▄█ ▄▄▄▄▄█ ████████▌▐███ █████▄ █ ▄▄▄▄▄ ┃
5
+ // ┃ █ ██████ █ ▀█▄ █ ██████ █ ███▌▐███ ███████▄ █ ┃
6
+ // ┣━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫
7
+ // ┃ Copyright (c) 2017, the Perspective Authors. ┃
8
+ // ┃ ╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌ ┃
9
+ // ┃ This file is part of the Perspective library, distributed under the terms ┃
10
+ // ┃ of the [Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0). ┃
11
+ // ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
12
+
13
+ @grey60: #c5c9d0;
14
+ @grey500: #3b3f46;
15
+ @grey600: #2f3136;
16
+ @grey700: #2a2c2f;
17
+ @grey800: #242526;
18
+
19
+ perspective-workspace,
20
+ perspective-workspace[theme="Pro Dark"],
21
+ perspective-indicator[theme="Pro Dark"] {
22
+ --theme-name: "Pro Dark";
23
+ }
24
+
25
+ perspective-workspace perspective-viewer {
26
+ --status-bar--height: 39px;
27
+ }
28
+
29
+ perspective-workspace perspective-viewer[settings] {
30
+ --modal-panel--margin: -4px 0 -4px 0;
31
+ --status-bar--border-radius: 6px 0 0 0;
32
+ --main-column--margin: 3px 0 3px 3px;
33
+ --main-column--border: 1px solid var(--inactive--color);
34
+ --main-column--border-width: 1px 0px 1px 1px;
35
+ --main-column--border-radius: 6px 0 0 6px;
36
+ --settings-button--margin: 10px 0 0 0;
37
+ }
38
+
39
+ perspective-workspace {
40
+ @include perspective-workspace-pro-base;
41
+ @include perspective-viewer-pro-dark--colors;
42
+
43
+ background-color: #000202;
44
+ color: white;
45
+ // --workspace-master--background-color: @grey800;
46
+ --workspace-tabbar--background-color: @grey800;
47
+ // --workspace-tabbar--border: 1px solid @grey500;
48
+ // --workspace-tabbar--border-color: @grey500;
49
+ --workspace-secondary--color: @grey60;
50
+ --workspace-tabbar--border: 1px solid var(--inactive--color);
51
+ --workspace-tabbar--border-width: 0px 1px 1px 1px;
52
+ --workspace-tabbar--border-radius: 6px;
53
+ --workspace-tabbar--border-color: var(--inactive--color);
54
+ --workspace-tabbar-tab--border-width: 1px 1px 0 1px;
55
+ }
56
+
57
+ perspective-viewer[theme="Pro Dark"].workspace-master-widget {
58
+ --plugin--background: @grey800;
59
+ }
60
+
61
+ perspective-workspace-menu {
62
+ font-family:
63
+ "ui-monospace", "SFMono-Regular", "SF Mono", "Menlo", "Consolas",
64
+ "Liberation Mono", monospace;
65
+ font-weight: 300;
66
+ background: @grey700 !important;
67
+ color: white !important;
68
+ border: 1px solid @grey500 !important;
69
+ }
70
+
71
+ @mixin perspective-workspace-pro-base {
72
+ font-family:
73
+ "ui-monospace", "SFMono-Regular", "SF Mono", "Menlo", "Consolas",
74
+ "Liberation Mono", monospace;
75
+ --open-settings-button--content: "expand_more";
76
+ --close-settings-button--content: "expand_less";
77
+ --close-button--content: "\2715";
78
+ --master-divider--background-color: #243136;
79
+ --menu-maximize--content: "fullscreen";
80
+ --menu-minimize--content: "fullscreen_exit";
81
+ --menu-duplicate--content: "call_split";
82
+ --menu-master--content: "cast";
83
+ --menu-detail--content: "notes";
84
+ --menu-export--content: "file_download";
85
+ --menu-copy--content: "file_copy";
86
+ --menu-reset--content: "autorenew";
87
+ --menu-link--content: "link";
88
+ --menu-unlink--content: "link_off";
89
+ --menu-newmenu--content: "add";
90
+ --menu-close--content: "close";
91
+ --menu-new--content: "description";
92
+ --menu-newview--content: "file_copy";
93
+
94
+ --workspace-tabbar--border: 1px solid var(--inactive--color);
95
+ --workspace-tabbar--border-width: 0px 1px 1px 1px;
96
+ --workspace-tabbar--border-radius: 6px;
97
+ --workspace-tabbar--border-color: var(--inactive--color);
98
+ --workspace-tabbar-tab--border-width: 1px 1px 0px 1px;
99
+
100
+ // Workspace
101
+ --column-drag-handle--mask-image: url("../svg/drag-handle.svg");
102
+ --bookmarks--mask-image: url("../svg/bookmark-icon.svg");
103
+ }
104
+
105
+ @mixin perspective-viewer-pro-dark--colors {
106
+ background-color: #242526;
107
+ --workspace-split-panel-handle--background-color: @grey800;
108
+ color: white;
109
+ --icon--color: white;
110
+ --active--color: #2770a9;
111
+ --error--color: #ff9485;
112
+ --inactive--color: #61656e;
113
+ --inactive--border-color: #4c505b;
114
+ --plugin--background: #242526;
115
+ --modal-target--background: rgba(255, 255, 255, 0.05);
116
+ --active--background: rgba(39, 113, 170, 0.5);
117
+ --expression--operator-color: #c5c9d0;
118
+ --expression--function-color: #22a0ce;
119
+ --expression--error-color: rgb(255, 136, 136);
120
+ --calendar--filter: invert(1);
121
+ --warning--color: #242526;
122
+ --warning--background: var(--icon--color);
123
+
124
+ --select-arrow--background-image: var(
125
+ --select-arrow-light--background-image
126
+ );
127
+
128
+ --select-arrow--hover--background-image: var(
129
+ --select-arrow-dark--background-image
130
+ );
131
+
132
+ // Syntax
133
+ --code-editor-symbol--color: white;
134
+ --code-editor-literal--color: #7dc3f0;
135
+ --code-editor-operator--color: rgb(23, 166, 123);
136
+ --code-editor-comment--color: rgb(204, 120, 48);
137
+ --code-editor-column--color: #e18ee1;
138
+ // --code-editor-unknown--color: rgb(204, 120, 48);
139
+ }
@@ -0,0 +1,93 @@
1
+ // ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
2
+ // ┃ ██████ ██████ ██████ █ █ █ █ █ █▄ ▀███ █ ┃
3
+ // ┃ ▄▄▄▄▄█ █▄▄▄▄▄ ▄▄▄▄▄█ ▀▀▀▀▀█▀▀▀▀▀ █ ▀▀▀▀▀█ ████████▌▐███ ███▄ ▀█ █ ▀▀▀▀▀ ┃
4
+ // ┃ █▀▀▀▀▀ █▀▀▀▀▀ █▀██▀▀ ▄▄▄▄▄ █ ▄▄▄▄▄█ ▄▄▄▄▄█ ████████▌▐███ █████▄ █ ▄▄▄▄▄ ┃
5
+ // ┃ █ ██████ █ ▀█▄ █ ██████ █ ███▌▐███ ███████▄ █ ┃
6
+ // ┣━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫
7
+ // ┃ Copyright (c) 2017, the Perspective Authors. ┃
8
+ // ┃ ╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌ ┃
9
+ // ┃ This file is part of the Perspective library, distributed under the terms ┃
10
+ // ┃ of the [Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0). ┃
11
+ // ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
12
+
13
+ @import url("ref://pro.less");
14
+
15
+ perspective-workspace,
16
+ perspective-workspace[theme="Pro Light"],
17
+ perspective-indicator[theme="Pro Light"] {
18
+ --theme-name: "Pro Light";
19
+ }
20
+
21
+ perspective-workspace {
22
+ @include perspective-workspace-pro-base;
23
+ @include perspective-viewer-pro--colors;
24
+ background-color: #dadada;
25
+ }
26
+
27
+ perspective-workspace perspective-viewer[settings] {
28
+ --modal-panel--margin: -4px 0 -4px 0;
29
+ --status-bar--border-radius: 6px 0 0 0;
30
+ --main-column--margin: 3px 0 3px 3px;
31
+ --main-column--border: 1px solid var(--inactive--color);
32
+ --main-column--border-width: 1px 0px 1px 1px;
33
+ --main-column--border-radius: 6px 0 0 6px;
34
+ --settings-button--margin: 10px 0 0 0;
35
+ }
36
+
37
+ perspective-workspace perspective-viewer {
38
+ --status-bar--height: 39px;
39
+ }
40
+
41
+ perspective-viewer[theme="Pro Light"].workspace-master-widget {
42
+ background-color: #f2f4f6;
43
+ --plugin--background: #f2f4f6;
44
+ regular-table {
45
+ td,
46
+ th {
47
+ border-color: #e0e4e9;
48
+ }
49
+ }
50
+ }
51
+
52
+ @mixin perspective-workspace-pro-base {
53
+ font-family:
54
+ "ui-monospace", "SFMono-Regular", "SF Mono", "Menlo", "Consolas",
55
+ "Liberation Mono", monospace;
56
+ --open-settings-button--content: "expand_more";
57
+ --close-settings-button--content: "expand_less";
58
+ --close-button--content: "\2715";
59
+ --master-divider--background-color: #243136;
60
+ --menu-maximize--content: "fullscreen";
61
+ --menu-minimize--content: "fullscreen_exit";
62
+ --menu-duplicate--content: "call_split";
63
+ --menu-master--content: "cast";
64
+ --menu-detail--content: "notes";
65
+ --menu-export--content: "file_download";
66
+ --menu-copy--content: "file_copy";
67
+ --menu-reset--content: "autorenew";
68
+ --menu-link--content: "link";
69
+ --menu-unlink--content: "link_off";
70
+ --menu-newmenu--content: "add";
71
+ --menu-close--content: "close";
72
+ --menu-new--content: "description";
73
+ --menu-newview--content: "file_copy";
74
+ --workspace-split-panel-handle--background-color: #f2f4f6;
75
+
76
+ --workspace-tabbar--border: 1px solid var(--inactive--color);
77
+ --workspace-tabbar--border-width: 0px 1px 1px 1px;
78
+ --workspace-tabbar--border-radius: 6px;
79
+ --workspace-tabbar--border-color: var(--inactive--color);
80
+ --workspace-tabbar-tab--border-width: 1px 1px 0px 1px;
81
+
82
+ // Workspace
83
+ --column-drag-handle--mask-image: url("../svg/drag-handle.svg");
84
+ --bookmarks--mask-image: url("../svg/bookmark-icon.svg");
85
+ }
86
+
87
+ perspective-workspace-menu {
88
+ font-family:
89
+ "ui-monospace", "SFMono-Regular", "SF Mono", "Menlo", "Consolas",
90
+ "Liberation Mono", monospace;
91
+ font-weight: 300;
92
+ color: #161616;
93
+ }
@@ -0,0 +1,21 @@
1
+ // ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
2
+ // ┃ ██████ ██████ ██████ █ █ █ █ █ █▄ ▀███ █ ┃
3
+ // ┃ ▄▄▄▄▄█ █▄▄▄▄▄ ▄▄▄▄▄█ ▀▀▀▀▀█▀▀▀▀▀ █ ▀▀▀▀▀█ ████████▌▐███ ███▄ ▀█ █ ▀▀▀▀▀ ┃
4
+ // ┃ █▀▀▀▀▀ █▀▀▀▀▀ █▀██▀▀ ▄▄▄▄▄ █ ▄▄▄▄▄█ ▄▄▄▄▄█ ████████▌▐███ █████▄ █ ▄▄▄▄▄ ┃
5
+ // ┃ █ ██████ █ ▀█▄ █ ██████ █ ███▌▐███ ███████▄ █ ┃
6
+ // ┣━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫
7
+ // ┃ Copyright (c) 2017, the Perspective Authors. ┃
8
+ // ┃ ╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌ ┃
9
+ // ┃ This file is part of the Perspective library, distributed under the terms ┃
10
+ // ┃ of the [Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0). ┃
11
+ // ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
12
+
13
+ declare module "*.css" {
14
+ const content: string;
15
+ export default content;
16
+ }
17
+
18
+ declare module "*.html" {
19
+ const content: string;
20
+ export default content;
21
+ }
@@ -0,0 +1,11 @@
1
+ // ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
2
+ // ┃ ██████ ██████ ██████ █ █ █ █ █ █▄ ▀███ █ ┃
3
+ // ┃ ▄▄▄▄▄█ █▄▄▄▄▄ ▄▄▄▄▄█ ▀▀▀▀▀█▀▀▀▀▀ █ ▀▀▀▀▀█ ████████▌▐███ ███▄ ▀█ █ ▀▀▀▀▀ ┃
4
+ // ┃ █▀▀▀▀▀ █▀▀▀▀▀ █▀██▀▀ ▄▄▄▄▄ █ ▄▄▄▄▄█ ▄▄▄▄▄█ ████████▌▐███ █████▄ █ ▄▄▄▄▄ ┃
5
+ // ┃ █ ██████ █ ▀█▄ █ ██████ █ ███▌▐███ ███████▄ █ ┃
6
+ // ┣━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫
7
+ // ┃ Copyright (c) 2017, the Perspective Authors. ┃
8
+ // ┃ ╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌ ┃
9
+ // ┃ This file is part of the Perspective library, distributed under the terms ┃
10
+ // ┃ of the [Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0). ┃
11
+ // ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
@@ -0,0 +1,306 @@
1
+ // ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
2
+ // ┃ ██████ ██████ ██████ █ █ █ █ █ █▄ ▀███ █ ┃
3
+ // ┃ ▄▄▄▄▄█ █▄▄▄▄▄ ▄▄▄▄▄█ ▀▀▀▀▀█▀▀▀▀▀ █ ▀▀▀▀▀█ ████████▌▐███ ███▄ ▀█ █ ▀▀▀▀▀ ┃
4
+ // ┃ █▀▀▀▀▀ █▀▀▀▀▀ █▀██▀▀ ▄▄▄▄▄ █ ▄▄▄▄▄█ ▄▄▄▄▄█ ████████▌▐███ █████▄ █ ▄▄▄▄▄ ┃
5
+ // ┃ █ ██████ █ ▀█▄ █ ██████ █ ███▌▐███ ███████▄ █ ┃
6
+ // ┣━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫
7
+ // ┃ Copyright (c) 2017, the Perspective Authors. ┃
8
+ // ┃ ╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌ ┃
9
+ // ┃ This file is part of the Perspective library, distributed under the terms ┃
10
+ // ┃ of the [Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0). ┃
11
+ // ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
12
+
13
+ import { MessageLoop } from "@lumino/messaging";
14
+ import { Widget } from "@lumino/widgets";
15
+ import { HTMLPerspectiveViewerElement } from "@perspective-dev/viewer";
16
+ import type * as psp from "@perspective-dev/client";
17
+
18
+ export { PerspectiveWorkspace } from "./workspace";
19
+ export { PerspectiveViewerWidget } from "./workspace/widget";
20
+
21
+ import "./external";
22
+ import {
23
+ PerspectiveWorkspace,
24
+ PerspectiveWorkspaceConfig,
25
+ ViewerConfigUpdateExt,
26
+ } from "./workspace";
27
+ import { bindTemplate, CustomElementProto } from "./utils/custom_elements";
28
+ import style from "../../build/css/workspace.css";
29
+ import template from "../html/workspace.html";
30
+
31
+ /**
32
+ * A Custom Element for coordinating a set of `<perspective-viewer>` light DOM
33
+ * children. `<perspective-workspace>` is built on Lumino.js to allow a more
34
+ * app-like experience than `<perspective-viewer>`, providing
35
+ * these features additionally:
36
+ *
37
+ * - Docking, arranging, tabbing and max/min-ing of `<perspective-viewer>`s.
38
+ * - Trivial `<perspective-viewer>` duplication.
39
+ * - Global Filter sidebar, for using selection state of a pivot table to filter
40
+ * siblings.
41
+ * - Total persistence/serializable state and child state.
42
+ * - Easy sharing/ownership of `Table()` among different `<perspective-viewer>`.
43
+ * - A cool DOM-reactive API.
44
+ *
45
+ * There are a few ways to use this Custom Element. In plain HTML, you can
46
+ * express your initial view simply:
47
+ *
48
+ * ```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>
59
+ * ```
60
+ *
61
+ * You can also use the DOM API in Javascript:
62
+ *
63
+ * ```javascript
64
+ * const workspace = document.createElement("perspective-workspace");
65
+ * const viewer = document.createElement("perspective-viewer");
66
+ * workspace.appendChild(viewer);
67
+ * document.body.appendChild(workspace);
68
+ * ```
69
+ *
70
+ * This will yield a `<perspective-workspace> with the default layout. To load
71
+ * a `Table()`, add it to `tables` via the `Map()` API where it will be
72
+ * auto-wired into all matching `<perspective-viewer>`s immediately:
73
+ *
74
+ * ```javascript
75
+ * workspace.tables.set("superstore", await worker.table(my_data));
76
+ * ```
77
+ *
78
+ *
79
+ */
80
+ export class HTMLPerspectiveWorkspaceElement extends HTMLElement {
81
+ private workspace?: PerspectiveWorkspace;
82
+ private _resize_observer?: ResizeObserver;
83
+
84
+ constructor() {
85
+ super();
86
+ this.setAutoSize(true);
87
+ }
88
+
89
+ /***************************************************************************
90
+ *
91
+ * Public
92
+ *
93
+ */
94
+
95
+ /**
96
+ * Persists this `<perspective-workspace>` to a token `Object`. This object
97
+ * is JSON serializable and describes the state of the Workspace and it's
98
+ * child `<perspective-viewer>` elements. Some important keys:
99
+ *
100
+ * - `viewers`: The serialized state of `<perspective-viewer>` children,
101
+ * named by `slot`.
102
+ * - `detail`: The main layout.
103
+ * - `master`: The contents of the Global Filter sidebar.
104
+ *
105
+ * While the `table` attribute is persisted for each `perspective-viewer`,
106
+ * `Table`s themselves must be added to the `tables` property `Map()`
107
+ * separately.
108
+ *
109
+ * @return {Object} A configuration token, compatible with
110
+ * `restore(config)`.
111
+ * @example
112
+ * // Save this layout to local storage
113
+ * const workspace = document.querySelector("perspective-workspace");
114
+ * localStorage.set("CONFIG", JSON.stringify(workspace.save()));
115
+ */
116
+ save() {
117
+ return this.workspace!.save();
118
+ }
119
+
120
+ /**
121
+ * Restore this `<perspective-workspace>` to a previous state captured by
122
+ * `save()`. Calling this method will completely rewrite this element's
123
+ * `innerHTML`, but may reuse `<perspective-viewer>` children depending
124
+ * on the `slot` attribute names. However, it should always be possible
125
+ * to recreate any given state from within the UI itself, as the attributes
126
+ * on `<perspective-viewer>` itself create immutable views.
127
+ *
128
+ * While the `table` attribute is set for each `perspective-viewer`,
129
+ * `Table`s themselves must be added to the `tables` property `Map()`
130
+ * separately.
131
+ * @param {Object} config A configuration token, as returned by `save()`.
132
+ * @example
133
+ * // Restore this layout from local storage
134
+ * const workspace = document.querySelector("perspective-workspace");
135
+ * workspace.restore(JSON.parse(localStorage.get("CONFIG"));
136
+ *
137
+ * // Add `Table` separately.
138
+ * workspace.tables.set("superstore", await worker.table(data));
139
+ */
140
+ async restore(layout: PerspectiveWorkspaceConfig<string>) {
141
+ await this.workspace!.restore(layout);
142
+ }
143
+
144
+ async clear() {
145
+ await this.restore({
146
+ sizes: [],
147
+ master: { sizes: [] },
148
+ detail: { sizes: [] },
149
+ viewers: {},
150
+ });
151
+ }
152
+
153
+ /**
154
+ * Await all asynchronous tasks for all viewers in this workspace. This is
155
+ * useful to make sure asynchonous side effects of synchronous methods calls
156
+ * are applied.
157
+ */
158
+ 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
+ );
165
+ }
166
+
167
+ /**
168
+ * Add a new viewer to the workspace for a given `ViewerConfigUpdateExt`.
169
+ * @param config
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);
182
+ await this.flush();
183
+ }
184
+
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;
228
+ }
229
+
230
+ /**
231
+ * Invalidate this component's dimensions and recalculate.
232
+ */
233
+ async resize() {
234
+ this.workspace!.update();
235
+ await this.flush();
236
+ }
237
+
238
+ /**
239
+ * Set whether this workspace element should auto-size itself via a
240
+ * `ResizeObserver`.
241
+ */
242
+ setAutoSize(is_auto_size: boolean) {
243
+ this._resize_observer?.unobserve(this);
244
+ this._resize_observer = undefined;
245
+ if (is_auto_size) {
246
+ this._resize_observer = new ResizeObserver((...args) =>
247
+ this.workspace?.update(),
248
+ );
249
+
250
+ this._resize_observer.observe(this);
251
+ }
252
+ }
253
+
254
+ connectedCallback() {
255
+ if (!this.workspace) {
256
+ const container = this.shadowRoot!.querySelector("#container")!;
257
+ this.workspace = new PerspectiveWorkspace(this);
258
+ this._register_light_dom_listener();
259
+ MessageLoop.sendMessage(this.workspace, Widget.Msg.BeforeAttach);
260
+ container.insertBefore(this.workspace.node, null);
261
+ MessageLoop.sendMessage(this.workspace, Widget.Msg.AfterAttach);
262
+ }
263
+ }
264
+
265
+ /***************************************************************************
266
+ *
267
+ * Private
268
+ *
269
+ */
270
+
271
+ private _light_dom_changed() {
272
+ const viewers = Array.from(
273
+ this.childNodes,
274
+ ) as HTMLPerspectiveViewerElement[];
275
+
276
+ for (const viewer of viewers) {
277
+ if (viewer.nodeType !== Node.ELEMENT_NODE) {
278
+ continue;
279
+ }
280
+
281
+ if (viewer.tagName !== "PERSPECTIVE-VIEWER") {
282
+ console.warn("Not a <perspective-viewer>");
283
+ continue;
284
+ }
285
+
286
+ this.workspace!.update_widget_for_viewer(
287
+ viewer as HTMLPerspectiveViewerElement,
288
+ );
289
+ }
290
+
291
+ this.workspace!.remove_unslotted_widgets(viewers);
292
+ this.workspace!.update_details_panel(viewers);
293
+ }
294
+
295
+ private _register_light_dom_listener() {
296
+ let observer = new MutationObserver(this._light_dom_changed.bind(this));
297
+ let config = { attributes: false, childList: true, subtree: false };
298
+ observer.observe(this, config);
299
+ this._light_dom_changed();
300
+ }
301
+ }
302
+
303
+ bindTemplate(
304
+ template,
305
+ style,
306
+ )(HTMLPerspectiveWorkspaceElement as unknown as CustomElementProto);
@@ -0,0 +1,95 @@
1
+ // ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
2
+ // ┃ ██████ ██████ ██████ █ █ █ █ █ █▄ ▀███ █ ┃
3
+ // ┃ ▄▄▄▄▄█ █▄▄▄▄▄ ▄▄▄▄▄█ ▀▀▀▀▀█▀▀▀▀▀ █ ▀▀▀▀▀█ ████████▌▐███ ███▄ ▀█ █ ▀▀▀▀▀ ┃
4
+ // ┃ █▀▀▀▀▀ █▀▀▀▀▀ █▀██▀▀ ▄▄▄▄▄ █ ▄▄▄▄▄█ ▄▄▄▄▄█ ████████▌▐███ █████▄ █ ▄▄▄▄▄ ┃
5
+ // ┃ █ ██████ █ ▀█▄ █ ██████ █ ███▌▐███ ███████▄ █ ┃
6
+ // ┣━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫
7
+ // ┃ Copyright (c) 2017, the Perspective Authors. ┃
8
+ // ┃ ╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌ ┃
9
+ // ┃ This file is part of the Perspective library, distributed under the terms ┃
10
+ // ┃ of the [Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0). ┃
11
+ // ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
12
+
13
+ /**
14
+ * Instantiate a Template DOM object from an HTML text string.
15
+ *
16
+ * Params
17
+ * ------
18
+ * template : An HTML string representing a template.
19
+ *
20
+ * Returns
21
+ * -------
22
+ * A Template DOM object.
23
+ */
24
+ function importTemplate(template: string): HTMLTemplateElement {
25
+ const div = document.createElement("div");
26
+ div.innerHTML = template;
27
+ return Array.prototype.slice.call(div.children)[0];
28
+ }
29
+
30
+ export interface CustomElementProto extends CustomElementConstructor {
31
+ connectedCallback(): void;
32
+ }
33
+
34
+ /**
35
+ * A simple tool for creating Web Components v0.
36
+ *
37
+ * Params
38
+ * ------
39
+ * template : An HTML string representing a template. Should have an 'id'
40
+ * attribute which will become the new Web Component's tag name.
41
+ * proto : The new Web Component's prototype object, as per spec.
42
+ */
43
+ export function registerElement(
44
+ templateString: string,
45
+ styleString: string,
46
+ proto: CustomElementProto,
47
+ ) {
48
+ const template = importTemplate(templateString);
49
+ if (styleString) {
50
+ template.innerHTML =
51
+ `<style>${styleString.toString()}</style>` + template.innerHTML;
52
+ }
53
+
54
+ const _perspective_element = class extends proto {
55
+ private _initialized: boolean;
56
+ private _initializing: boolean;
57
+
58
+ constructor() {
59
+ super();
60
+ this._initialized = false;
61
+ this._initializing = false;
62
+ }
63
+
64
+ connectedCallback() {
65
+ if (this._initialized) {
66
+ return;
67
+ }
68
+
69
+ this._initializing = true;
70
+ const node = document.importNode(template.content, true);
71
+ const root = this.attachShadow({ mode: "open" });
72
+ root.appendChild(node);
73
+ if (proto.prototype.connectedCallback) {
74
+ proto.prototype.connectedCallback.call(this);
75
+ }
76
+
77
+ this._initializing = false;
78
+ this._initialized = true;
79
+ }
80
+
81
+ static get observedAttributes() {
82
+ return Object.getOwnPropertyNames(proto.prototype);
83
+ }
84
+ };
85
+
86
+ let name = template.getAttribute("id")!;
87
+ window.customElements.define(name, _perspective_element);
88
+ }
89
+
90
+ export function bindTemplate(template: string, ...styleStrings: string[]) {
91
+ const style = styleStrings.map((x) => x.toString()).join("\n");
92
+ return function (cls: CustomElementProto) {
93
+ return registerElement(template, style, cls);
94
+ };
95
+ }