@perspective-dev/viewer 4.5.2 → 5.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.
Files changed (227) hide show
  1. package/dist/cdn/perspective-viewer.js +20 -1
  2. package/dist/cdn/perspective-viewer.js.map +4 -4
  3. package/dist/css/botanical.css +1 -1
  4. package/dist/css/dracula.css +1 -1
  5. package/dist/css/gruvbox-dark.css +1 -1
  6. package/dist/css/gruvbox.css +1 -1
  7. package/dist/css/icons.css +1 -1
  8. package/dist/css/intl/de.css +1 -1
  9. package/dist/css/intl/es.css +1 -1
  10. package/dist/css/intl/fr.css +1 -1
  11. package/dist/css/intl/ja.css +1 -1
  12. package/dist/css/intl/pt.css +1 -1
  13. package/dist/css/intl/zh.css +1 -1
  14. package/dist/css/intl.css +1 -1
  15. package/dist/css/monokai.css +1 -1
  16. package/dist/css/phosphor.css +1 -1
  17. package/dist/css/pro-dark.css +1 -1
  18. package/dist/css/pro.css +1 -1
  19. package/dist/css/solarized-dark.css +1 -1
  20. package/dist/css/solarized.css +1 -1
  21. package/dist/css/themes.css +1 -1
  22. package/dist/css/vaporwave.css +1 -1
  23. package/dist/esm/extensions.d.ts +22 -3
  24. package/dist/esm/perspective-viewer.d.ts +9 -0
  25. package/dist/esm/perspective-viewer.inline.js +20 -1
  26. package/dist/esm/perspective-viewer.inline.js.map +4 -4
  27. package/dist/esm/perspective-viewer.js +20 -1
  28. package/dist/esm/perspective-viewer.js.map +4 -4
  29. package/dist/esm/plugin.d.ts +71 -5
  30. package/dist/esm/ts-rs/ClientOptions.d.ts +7 -0
  31. package/dist/esm/ts-rs/ExportMethod.d.ts +1 -0
  32. package/dist/esm/ts-rs/ExportOptions.d.ts +9 -0
  33. package/dist/esm/ts-rs/GetClientOptions.d.ts +8 -0
  34. package/dist/esm/ts-rs/GetTableOptions.d.ts +8 -0
  35. package/dist/esm/ts-rs/Layout.d.ts +11 -0
  36. package/dist/esm/ts-rs/Orientation.d.ts +4 -0
  37. package/dist/esm/ts-rs/PanelOptions.d.ts +7 -0
  38. package/dist/esm/ts-rs/PanelViewerConfig.d.ts +41 -0
  39. package/dist/esm/ts-rs/SplitLayout.d.ts +7 -0
  40. package/dist/esm/ts-rs/TabLayout.d.ts +10 -0
  41. package/dist/esm/ts-rs/ViewerConfig.d.ts +8 -2
  42. package/dist/esm/ts-rs/ViewerConfigUpdate.d.ts +9 -0
  43. package/dist/esm/ts-rs/WindowAggregate.d.ts +1 -0
  44. package/dist/esm/ts-rs/WindowSort.d.ts +7 -0
  45. package/dist/esm/ts-rs/WindowSortDir.d.ts +6 -0
  46. package/dist/esm/ts-rs/WindowSpec.d.ts +46 -0
  47. package/dist/esm/ts-rs/Windows.d.ts +10 -0
  48. package/dist/esm/ts-rs/WorkspaceConfig.d.ts +40 -0
  49. package/dist/esm/ts-rs/WorkspaceConfigUpdate.d.ts +28 -0
  50. package/dist/wasm/perspective-viewer.d.ts +299 -78
  51. package/dist/wasm/perspective-viewer.js +544 -186
  52. package/dist/wasm/perspective-viewer.wasm +0 -0
  53. package/dist/wasm/perspective-viewer.wasm.d.ts +34 -26
  54. package/dist/wasm/snippets/perspective-viewer-8a8c7d5d696b43ae/inline0.js +6 -0
  55. package/dist/wasm/snippets/{perspective-viewer-93064d0d79855d97/inline3.js → perspective-viewer-8a8c7d5d696b43ae/inline4.js} +5 -0
  56. package/package.json +3 -2
  57. package/src/css/_context_menu.css +14 -0
  58. package/src/css/column-selector.css +1 -0
  59. package/src/css/column-settings-panel.css +68 -3
  60. package/src/css/column-style.css +4 -3
  61. package/src/css/config-selector.css +22 -1
  62. package/src/css/containers/context-menu.css +107 -0
  63. package/src/css/containers/tabs.css +2 -2
  64. package/src/css/panel-tab.css +193 -0
  65. package/src/css/plugin-selector.css +7 -10
  66. package/src/css/status-bar.css +75 -81
  67. package/src/css/viewer.css +188 -70
  68. package/src/rust/components/column_dropdown.rs +40 -30
  69. package/src/rust/components/column_selector/active_column.rs +28 -45
  70. package/src/rust/components/column_selector/aggregate_selector.rs +4 -7
  71. package/src/rust/components/column_selector/column_selector_column_row.rs +73 -0
  72. package/src/rust/components/column_selector/config_selector.rs +40 -82
  73. package/src/rust/components/column_selector/expr_edit_button.rs +7 -1
  74. package/src/rust/components/column_selector/filter_column.rs +5 -15
  75. package/src/rust/components/column_selector/inactive_column.rs +11 -9
  76. package/src/rust/components/column_selector/sort_column.rs +3 -6
  77. package/src/rust/components/column_selector.rs +25 -14
  78. package/src/rust/components/column_settings_sidebar/style_tab.rs +2 -1
  79. package/src/rust/components/column_settings_sidebar/window_tab.rs +34 -0
  80. package/src/rust/components/column_settings_sidebar.rs +163 -16
  81. package/src/rust/components/containers/dragdrop_list.rs +73 -3
  82. package/src/rust/components/containers/sidebar.rs +99 -22
  83. package/src/rust/components/containers/split_panel.rs +181 -62
  84. package/src/rust/components/context_menu.rs +130 -0
  85. package/src/rust/components/editable_header.rs +2 -0
  86. package/src/rust/components/form/debug.rs +3 -0
  87. package/src/rust/components/global_filter_bar.rs +76 -0
  88. package/src/rust/components/main_panel/frame_theme.rs +186 -0
  89. package/src/rust/components/main_panel/msg.rs +62 -0
  90. package/src/rust/components/main_panel/presize.rs +220 -0
  91. package/src/rust/components/main_panel/reconcile.rs +224 -0
  92. package/src/rust/components/main_panel/render.rs +385 -0
  93. package/src/rust/components/main_panel/update.rs +184 -0
  94. package/src/rust/components/main_panel.rs +316 -71
  95. package/src/rust/components/mod.rs +5 -0
  96. package/src/rust/components/panel_menu.rs +493 -0
  97. package/src/rust/components/panel_tab.rs +510 -0
  98. package/src/rust/components/plugin_tab.rs +1 -1
  99. package/src/rust/components/portal.rs +7 -0
  100. package/src/rust/components/settings_panel.rs +13 -5
  101. package/src/rust/components/status_bar.rs +110 -178
  102. package/src/rust/components/status_indicator.rs +6 -9
  103. package/src/rust/components/style/mod.rs +5 -0
  104. package/src/rust/components/style/style_provider.rs +2 -0
  105. package/src/rust/components/viewer/filters.rs +133 -0
  106. package/src/rust/components/viewer/msg.rs +182 -0
  107. package/src/rust/components/viewer/panels.rs +326 -0
  108. package/src/rust/components/viewer/render.rs +255 -0
  109. package/src/rust/components/viewer/settings.rs +434 -0
  110. package/src/rust/components/viewer/snapshots.rs +134 -0
  111. package/src/rust/components/viewer/wiring.rs +505 -0
  112. package/src/rust/components/viewer.rs +245 -756
  113. package/src/rust/components/window_editor.rs +1013 -0
  114. package/src/rust/config/export_method.rs +1 -1
  115. package/src/rust/config/mod.rs +4 -0
  116. package/src/rust/config/options.rs +71 -0
  117. package/src/rust/config/viewer_config.rs +24 -2
  118. package/src/rust/config/workspace_config.rs +92 -0
  119. package/src/rust/custom_elements/copy_dropdown.rs +3 -1
  120. package/src/rust/custom_elements/debug_plugin.rs +20 -1
  121. package/src/rust/custom_elements/export_dropdown.rs +3 -1
  122. package/src/rust/custom_elements/viewer.rs +1015 -286
  123. package/src/rust/custom_events.rs +212 -61
  124. package/src/rust/js/mod.rs +2 -0
  125. package/src/rust/js/plugin.rs +297 -10
  126. package/src/rust/js/regular_layout.rs +258 -0
  127. package/src/rust/lib.rs +24 -2
  128. package/src/rust/presentation/column_locator.rs +16 -1
  129. package/src/rust/presentation.rs +66 -33
  130. package/src/rust/queries/columns_iter_set.rs +7 -1
  131. package/src/rust/queries/get_viewer_config.rs +29 -11
  132. package/src/rust/queries/plugin_column_styles.rs +2 -2
  133. package/src/rust/renderer/activate.rs +41 -14
  134. package/src/rust/renderer/dispatch.rs +463 -0
  135. package/src/rust/renderer/plugin_config.rs +495 -0
  136. package/src/rust/renderer/registry.rs +18 -6
  137. package/src/rust/renderer/render_timer.rs +29 -10
  138. package/src/rust/renderer.rs +384 -838
  139. package/src/rust/session/column_defaults_update.rs +26 -8
  140. package/src/rust/session/drag_drop_update.rs +18 -0
  141. package/src/rust/session/metadata.rs +66 -9
  142. package/src/rust/session/replace_expression_update.rs +1 -0
  143. package/src/rust/session/view_subscription.rs +30 -15
  144. package/src/rust/session.rs +755 -264
  145. package/src/rust/tasks/apply_global_filters.rs +89 -0
  146. package/src/rust/tasks/auto_pause.rs +171 -0
  147. package/src/rust/tasks/copy_export.rs +29 -19
  148. package/src/rust/tasks/create_panel.rs +243 -0
  149. package/src/rust/tasks/dismiss_render_warning.rs +3 -1
  150. package/src/rust/tasks/edit_expression.rs +4 -4
  151. package/src/rust/tasks/edit_window.rs +155 -0
  152. package/src/rust/tasks/eject.rs +64 -23
  153. package/src/rust/tasks/mod.rs +30 -4
  154. package/src/rust/tasks/pipeline.rs +411 -0
  155. package/src/rust/tasks/presize_panels.rs +324 -0
  156. package/src/rust/tasks/reset_all.rs +22 -12
  157. package/src/rust/tasks/resize_observer.rs +124 -87
  158. package/src/rust/tasks/restore_and_render.rs +80 -86
  159. package/src/rust/tasks/restore_panel.rs +145 -0
  160. package/src/rust/tasks/send_column_config.rs +5 -2
  161. package/src/rust/tasks/send_plugin_config.rs +5 -2
  162. package/src/rust/tasks/set_edit_mode.rs +59 -0
  163. package/src/rust/tasks/sync_update_panels.rs +152 -0
  164. package/src/rust/tasks/table_lifecycle.rs +158 -0
  165. package/src/rust/tasks/update_theme.rs +56 -12
  166. package/src/rust/utils/browser/dragdrop.rs +12 -0
  167. package/src/rust/utils/browser/tests/debounce.rs +49 -0
  168. package/src/rust/utils/completion.rs +56 -0
  169. package/src/rust/utils/custom_element.rs +5 -0
  170. package/src/rust/utils/debounce.rs +189 -17
  171. package/src/rust/utils/mod.rs +6 -0
  172. package/src/rust/utils/pending_effects.rs +62 -0
  173. package/src/rust/utils/spawn.rs +33 -0
  174. package/src/rust/utils/weak_scope.rs +0 -10
  175. package/src/rust/workspace.rs +846 -0
  176. package/src/svg/active-indicator.svg +3 -0
  177. package/src/svg/pin-icon.svg +5 -0
  178. package/src/themes/botanical.css +5 -2
  179. package/src/themes/defaults.css +54 -2
  180. package/src/themes/dracula.css +6 -3
  181. package/src/themes/gruvbox-dark.css +5 -2
  182. package/src/themes/gruvbox.css +5 -2
  183. package/src/themes/icons.css +5 -2
  184. package/src/themes/intl/de.css +7 -0
  185. package/src/themes/intl/es.css +7 -0
  186. package/src/themes/intl/fr.css +7 -0
  187. package/src/themes/intl/ja.css +7 -0
  188. package/src/themes/intl/pt.css +7 -0
  189. package/src/themes/intl/zh.css +7 -0
  190. package/src/themes/intl.css +10 -1
  191. package/src/themes/monokai.css +5 -2
  192. package/src/themes/phosphor.css +5 -2
  193. package/src/themes/pro-dark.css +5 -2
  194. package/src/themes/pro.css +2 -1
  195. package/src/themes/solarized-dark.css +5 -2
  196. package/src/themes/solarized.css +5 -2
  197. package/src/themes/vaporwave.css +5 -2
  198. package/src/ts/bootstrap.ts +8 -7
  199. package/src/ts/extensions.ts +30 -2
  200. package/src/ts/perspective-viewer.ts +10 -0
  201. package/src/ts/plugin.ts +72 -5
  202. package/src/ts/ts-rs/ClientOptions.ts +7 -0
  203. package/src/ts/ts-rs/ExportMethod.ts +3 -0
  204. package/src/ts/ts-rs/ExportOptions.ts +8 -0
  205. package/src/ts/ts-rs/GetClientOptions.ts +7 -0
  206. package/src/ts/ts-rs/GetTableOptions.ts +7 -0
  207. package/src/ts/ts-rs/Layout.ts +9 -0
  208. package/src/ts/ts-rs/Orientation.ts +6 -0
  209. package/src/ts/ts-rs/PanelOptions.ts +7 -0
  210. package/src/ts/ts-rs/PanelViewerConfig.ts +16 -0
  211. package/src/ts/ts-rs/SplitLayout.ts +5 -0
  212. package/src/ts/ts-rs/TabLayout.ts +11 -0
  213. package/src/ts/ts-rs/ViewerConfig.ts +7 -2
  214. package/src/ts/ts-rs/ViewerConfigUpdate.ts +9 -0
  215. package/src/ts/ts-rs/WindowAggregate.ts +3 -0
  216. package/src/ts/ts-rs/WindowSort.ts +9 -0
  217. package/src/ts/ts-rs/WindowSortDir.ts +8 -0
  218. package/src/ts/ts-rs/WindowSpec.ts +46 -0
  219. package/src/ts/ts-rs/Windows.ts +10 -0
  220. package/src/ts/ts-rs/WorkspaceConfig.ts +36 -0
  221. package/src/ts/ts-rs/WorkspaceConfigUpdate.ts +24 -0
  222. package/src/rust/tasks/intersection_observer.rs +0 -98
  223. package/src/rust/tasks/update_and_render.rs +0 -110
  224. /package/dist/wasm/snippets/{perspective-viewer-93064d0d79855d97/inline0.js → perspective-viewer-8a8c7d5d696b43ae/inline1.js} +0 -0
  225. /package/dist/wasm/snippets/{perspective-viewer-93064d0d79855d97/inline1.js → perspective-viewer-8a8c7d5d696b43ae/inline2.js} +0 -0
  226. /package/dist/wasm/snippets/{perspective-viewer-93064d0d79855d97/inline2.js → perspective-viewer-8a8c7d5d696b43ae/inline3.js} +0 -0
  227. /package/dist/wasm/snippets/{perspective-viewer-93064d0d79855d97/inline4.js → perspective-viewer-8a8c7d5d696b43ae/inline5.js} +0 -0
package/src/ts/plugin.ts CHANGED
@@ -28,6 +28,36 @@ import { PluginStaticConfig } from "./ts-rs/PluginStaticConfig.js";
28
28
  * level (if packaged as a library), or during application init to ensure global
29
29
  * availability of a plugin.
30
30
  *
31
+ * ## Dispatch contract
32
+ *
33
+ * The host dispatches each method for exactly one reason, and never
34
+ * defensively — implementations may rely on these meanings:
35
+ *
36
+ * - `draw(view)` — `view` is NEW to this plugin: a new engine `View` was
37
+ * constructed, or this plugin was freshly selected and owes its first
38
+ * render. Safe to reset zoom/scroll/selection state.
39
+ * - `update(view)` — same `View`, but a plugin-visible input genuinely
40
+ * changed: new data (`View.on_update`), an effective view-config delta
41
+ * without a rebuild, a *changed* plugin/columns config just delivered
42
+ * via `restore()`, changed CSS just applied via `restyle()`, a
43
+ * render-limits change, or an explicit public `viewer.restore()` call
44
+ * (the no-op-restore refresh affordance). Host-internal operations that
45
+ * change none of these dispatch nothing.
46
+ * - `resize(view)` — geometry or visibility changed (including panel
47
+ * activation); repaint from retained state, no data or CSS re-read.
48
+ * - `restyle()` — the effective theme genuinely changed; when a
49
+ * `draw`/`update` is part of the same operation, `restyle()` is called
50
+ * immediately BEFORE it, so one render pass paints in the new theme.
51
+ * - `restore()`/`save()` — state transfer only. Plugins must NOT render
52
+ * from `restore()` (a changed restore is always followed by exactly one
53
+ * `update`) and must NOT call host APIs from inside it — a
54
+ * `restore()` echo re-enters the host's public surface and forces
55
+ * a redundant render (the classic double-render-on-load bug).
56
+ *
57
+ * Rendering methods (`draw`, `update`, `resize`, `render`, `clear`) and
58
+ * `delete` are serialized per element — the host never overlaps them, and
59
+ * each call runs to completion before the next begins.
60
+ *
31
61
  * @example
32
62
  * ```javascript
33
63
  * const BasePlugin = customElements.get("perspective-viewer-plugin");
@@ -68,6 +98,14 @@ export interface IPerspectiveViewerPlugin {
68
98
  * provision to cancel a render in progress per se, calling a method on
69
99
  * a `View` which has been deleted will throw an exception.
70
100
  *
101
+ * The host invokes `draw` ONLY when `view` is NEW to this plugin — a
102
+ * new engine `View` was constructed for a config change, or this plugin
103
+ * element was freshly selected and owes its first render of the bound
104
+ * `View` — so implementations may treat it as "new data shape" and
105
+ * reset zoom/scroll/selection-domain state. Repaints of the same
106
+ * `View` arrive as `update()` (data or style refresh) or `resize()`
107
+ * (geometry/chrome), never `draw()`.
108
+ *
71
109
  * @example
72
110
  * ```
73
111
  * async draw(view: perspective.View): Promise<void> {
@@ -79,9 +117,9 @@ export interface IPerspectiveViewerPlugin {
79
117
  draw(view: View): Promise<void>;
80
118
 
81
119
  /**
82
- * Draw under the assumption that the `ViewConfig` has not changed since
83
- * the previous call to `draw()`, but the underlying data has. Defaults to
84
- * dispatch to `draw()`.
120
+ * Draw under the assumption that the `View` has not changed since the
121
+ * previous call to `draw()`, but some plugin-visible input has.
122
+ * Defaults to dispatch to `draw()`.
85
123
  *
86
124
  * @example
87
125
  * ```javascript
@@ -109,21 +147,50 @@ export interface IPerspectiveViewerPlugin {
109
147
 
110
148
  /**
111
149
  * Like `update()`, but for when the dimensions of the plugin have changed
112
- * and the underlying data has not.
150
+ * and the underlying data has not — a repaint from retained state,
151
+ * invoked ONLY when geometry or visibility changed: box resizes,
152
+ * presize sweeps, and the panel-ACTIVATION chrome nudge (multi-panel),
153
+ * so implementations should repaint activation-dependent chrome here
154
+ * and no-op while hidden (`offsetParent == null`).
113
155
  */
114
156
  resize(view: View): Promise<void>;
115
157
 
116
158
  /**
117
159
  * Notify the plugin that the style environment has changed. Useful for
118
- * plugins which read CSS styles via `window.getComputedStyle()`.
160
+ * plugins which read CSS styles via `window.getComputedStyle()` — this
161
+ * is the ONLY point at which a plugin is expected to (re-)read them
162
+ * (besides its first `draw()`), so implementations may cache computed
163
+ * styles between `restyle()` calls.
119
164
  */
120
165
  restyle(): void;
121
166
 
122
167
  /**
123
168
  * Restore this plugin to a state previously returned by `save()`.
169
+ *
170
+ * State transfer ONLY — implementations must not render from
171
+ * `restore()` (the host follows a restore that genuinely changed
172
+ * plugin state with exactly one `update()` in the same serialized
173
+ * sequence), and must not call host `<perspective-viewer>` APIs from
174
+ * inside it: an echoed `restore()` re-enters the host's public
175
+ * surface where it is indistinguishable from a user call and forces a
176
+ * redundant render. Host API calls to persist plugin state belong to
177
+ * genuine user-gesture handlers (e.g. a toolbar click), never to the
178
+ * `restore()` delivery path.
124
179
  */
125
180
  restore(config: any): void;
126
181
 
182
+ /**
183
+ * OPTIONAL — clear any visible user selection state (highlighted rows,
184
+ * pinned tooltips) WITHOUT emitting selection events. The host
185
+ * `<perspective-viewer>` invokes this when an element-level global
186
+ * filter contributed by this plugin's selection is removed from the
187
+ * global filter bar, so the selection visual can't outlive the filter
188
+ * it produced. Called under the host's per-panel render serialization
189
+ * (like `draw`), so implementations may redraw. Plugins without a
190
+ * selection UI may omit it.
191
+ */
192
+ deselect?(): Promise<void>;
193
+
127
194
  /**
128
195
  * Free any resources acquired by this plugin and prepare to be deleted.
129
196
  */
@@ -0,0 +1,7 @@
1
+ // This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
2
+
3
+ /**
4
+ * The `eject` argument: the loaded client to remove by name; the active
5
+ * panel's client when omitted.
6
+ */
7
+ export type ClientOptions = { client?: string, };
@@ -0,0 +1,3 @@
1
+ // This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
2
+
3
+ export type ExportMethod = "csv" | "csv-all" | "csv-selected" | "json" | "json-all" | "json-selected" | "ndjson" | "ndjson-all" | "ndjson-selected" | "html" | "plugin" | "arrow" | "arrow-selected" | "arrow-all" | "json-config";
@@ -0,0 +1,8 @@
1
+ // This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
2
+ import type { ExportMethod } from "./ExportMethod.js";
3
+
4
+ /**
5
+ * The `download` / `export` / `copy` argument: the `ExportMethod` and target
6
+ * panel.
7
+ */
8
+ export type ExportOptions = { method?: ExportMethod, panel?: string, };
@@ -0,0 +1,7 @@
1
+ // This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
2
+
3
+ /**
4
+ * The `getClient` argument: whether to `wait` for a `Client`, and the target
5
+ * panel.
6
+ */
7
+ export type GetClientOptions = { wait?: boolean, panel?: string, };
@@ -0,0 +1,7 @@
1
+ // This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
2
+
3
+ /**
4
+ * The `getTable` argument: whether to `wait` for a `Table`, and the target
5
+ * panel.
6
+ */
7
+ export type GetTableOptions = { wait?: boolean, panel?: string, };
@@ -0,0 +1,9 @@
1
+ // This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
2
+ import type { SplitLayout } from "./SplitLayout.js";
3
+ import type { TabLayout } from "./TabLayout.js";
4
+
5
+ /**
6
+ * A serde mirror of `regular-layout`'s serialized layout tree, as returned by
7
+ * [`RegularLayout::save`] and accepted by [`RegularLayout::restore`].
8
+ */
9
+ export type Layout = { "type": "split-layout" } & SplitLayout | { "type": "tab-layout" } & TabLayout;
@@ -0,0 +1,6 @@
1
+ // This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
2
+
3
+ /**
4
+ * The orientation of a [`SplitLayout`].
5
+ */
6
+ export type Orientation = "horizontal" | "vertical";
@@ -0,0 +1,7 @@
1
+ // This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
2
+
3
+ /**
4
+ * Selects the target panel of a panel-scoped `<perspective-viewer>` method;
5
+ * the active panel when `panel` is omitted.
6
+ */
7
+ export type PanelOptions = { panel?: string, };
@@ -0,0 +1,16 @@
1
+ // This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
2
+ import type { Aggregate } from "./Aggregate.js";
3
+ import type { Expressions } from "./Expressions.js";
4
+ import type { Filter } from "./Filter.js";
5
+ import type { FilterReducer } from "./FilterReducer.js";
6
+ import type { GroupRollupMode } from "./GroupRollupMode.js";
7
+ import type { Sort } from "./Sort.js";
8
+ import type { Windows } from "./Windows.js";
9
+ import type { JsonValue } from "./serde_json/JsonValue.js";
10
+
11
+ /**
12
+ * The per-panel state of a [`ViewerConfig`] — everything except the
13
+ * element-level `settings` flag. This is the `panels` entry type of the
14
+ * whole-element config format.
15
+ */
16
+ export type PanelViewerConfig<V = string> = { version: V, columns_config: { [key in string]?: { [key in string]?: JsonValue } }, plugin: string, plugin_config: { [key in string]?: JsonValue }, table: string | null, theme: string | null, title: string | null, group_by: Array<string>, split_by: Array<string>, sort: Array<Sort>, filter: Array<Filter>, group_rollup_mode: GroupRollupMode, filter_op?: FilterReducer, expressions: Expressions, windows?: Windows, columns: Array<string | null>, aggregates: { [key in string]?: Aggregate }, group_by_depth?: number | null, };
@@ -0,0 +1,5 @@
1
+ // This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
2
+ import type { Layout } from "./Layout.js";
3
+ import type { Orientation } from "./Orientation.js";
4
+
5
+ export type SplitLayout = { children: Array<Layout>, sizes: Array<number>, orientation: Orientation, };
@@ -0,0 +1,11 @@
1
+ // This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
2
+
3
+ export type TabLayout = {
4
+ /**
5
+ * Panel ids (slot names) in this stack.
6
+ */
7
+ tabs: Array<string>,
8
+ /**
9
+ * Index of the selected tab within `tabs`.
10
+ */
11
+ selected?: number, };
@@ -5,10 +5,15 @@ import type { Filter } from "./Filter.js";
5
5
  import type { FilterReducer } from "./FilterReducer.js";
6
6
  import type { GroupRollupMode } from "./GroupRollupMode.js";
7
7
  import type { Sort } from "./Sort.js";
8
+ import type { Windows } from "./Windows.js";
8
9
  import type { JsonValue } from "./serde_json/JsonValue.js";
9
10
 
10
11
  /**
11
12
  * The state of an entire `custom_elements::PerspectiveViewerElement` component
12
- * and its `Plugin`.
13
+ * and its `Plugin`: the element-level `settings` flag plus the per-panel
14
+ * [`PanelViewerConfig`]. The split exists so the whole-element config format
15
+ * can serialize panel entries *without* a `settings` key (it is element-level
16
+ * state there, carried by the top-level `active` field instead), while the
17
+ * single-panel format flattens back to the legacy shape.
13
18
  */
14
- export type ViewerConfig<V = string> = { version: V, columns_config: { [key in string]?: { [key in string]?: JsonValue } }, plugin: string, plugin_config: { [key in string]?: JsonValue }, settings: boolean, table: string | null, theme: string | null, title: string | null, group_by: Array<string>, split_by: Array<string>, sort: Array<Sort>, filter: Array<Filter>, group_rollup_mode: GroupRollupMode, filter_op?: FilterReducer, expressions: Expressions, columns: Array<string | null>, aggregates: { [key in string]?: Aggregate }, group_by_depth?: number | null, };
19
+ export type ViewerConfig<V = string> = { settings: boolean, version: V, columns_config: { [key in string]?: { [key in string]?: JsonValue } }, plugin: string, plugin_config: { [key in string]?: JsonValue }, table: string | null, theme: string | null, title: string | null, group_by: Array<string>, split_by: Array<string>, sort: Array<Sort>, filter: Array<Filter>, group_rollup_mode: GroupRollupMode, filter_op?: FilterReducer, expressions: Expressions, windows?: Windows, columns: Array<string | null>, aggregates: { [key in string]?: Aggregate }, group_by_depth?: number | null, };
@@ -6,6 +6,7 @@ import type { FilterReducer } from "./FilterReducer.js";
6
6
  import type { GroupRollupMode } from "./GroupRollupMode.js";
7
7
  import type { OptionalUpdate } from "./OptionalUpdate.js";
8
8
  import type { Sort } from "./Sort.js";
9
+ import type { Windows } from "./Windows.js";
9
10
  import type { JsonValue } from "./serde_json/JsonValue.js";
10
11
 
11
12
  export type ViewerConfigUpdate = { version?: OptionalUpdate<string>, plugin?: OptionalUpdate<string>, title?: OptionalUpdate<string>, table?: OptionalUpdate<string>, theme?: OptionalUpdate<string>, settings?: OptionalUpdate<boolean>, plugin_config?: OptionalUpdate<{ [key in string]?: JsonValue }>, columns_config?: OptionalUpdate<{ [key in string]?: { [key in string]?: JsonValue } }>,
@@ -74,6 +75,14 @@ sort?: Array<Sort>,
74
75
  * panel.
75
76
  */
76
77
  expressions?: Expressions,
78
+ /**
79
+ * The `windows` property declares ordered, partitioned rolling
80
+ * computations (moving aggregates, cumulative sums) as _new_ columns
81
+ * keyed by output alias (`{"name": {...spec}}`, symmetric with
82
+ * `expressions`), analogous to SQL window functions. See
83
+ * [`crate::config::WindowSpec`].
84
+ */
85
+ windows?: Windows,
77
86
  /**
78
87
  * Aggregates perform a calculation over an entire column, and are
79
88
  * displayed when one or more [Group By](#group-by) are applied to the
@@ -0,0 +1,3 @@
1
+ // This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
2
+
3
+ export type WindowAggregate = "sum" | "avg" | "count" | "min" | "max" | "stddev" | "var" | "first" | "last" | "lag" | "lead" | "diff" | "rate" | "ema";
@@ -0,0 +1,9 @@
1
+ // This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
2
+ import type { WindowSortDir } from "./WindowSortDir.js";
3
+
4
+ /**
5
+ * The `Table` column which orders each partition, with its direction -
6
+ * serialized as a two-element array (`["ts", "desc"]`), symmetric with the
7
+ * `ViewConfig`'s `sort` field.
8
+ */
9
+ export type WindowSort = [string, WindowSortDir];
@@ -0,0 +1,8 @@
1
+ // This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
2
+
3
+ /**
4
+ * A window's order direction. A dedicated two-variant enum rather than
5
+ * [`crate::config::SortDir`] - the column-sort extras (`col asc`, `abs`,
6
+ * `none`) are meaningless inside a window frame and unrepresentable here.
7
+ */
8
+ export type WindowSortDir = "asc" | "desc";
@@ -0,0 +1,46 @@
1
+ // This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
2
+ import type { WindowAggregate } from "./WindowAggregate.js";
3
+ import type { WindowSort } from "./WindowSort.js";
4
+
5
+ /**
6
+ * The serialized form of [`WindowSpec`].
7
+ */
8
+ export type WindowSpec = {
9
+ /**
10
+ * The input column - either a real `Table` column or an expression
11
+ * alias from the same `ViewConfig`.
12
+ */
13
+ column: string, aggregate: WindowAggregate,
14
+ /**
15
+ * Columns whose distinct value tuples partition the rows; empty
16
+ * partitions the whole `Table` as one group.
17
+ */
18
+ partition_by?: Array<string>,
19
+ /**
20
+ * The `Table` column which orders each partition, and the direction.
21
+ */
22
+ order_by?: WindowSort | null,
23
+ /**
24
+ * A frame of the `rows` preceding each row, plus the row itself.
25
+ * Mutually exclusive with `range` and `cumulative`.
26
+ */
27
+ rows?: number | null,
28
+ /**
29
+ * A frame of the rows whose `order_by` value lies within `range` of
30
+ * each row's, requiring a numeric or temporal `order_by`. Mutually
31
+ * exclusive with `rows` and `cumulative`.
32
+ */
33
+ range?: number | null,
34
+ /**
35
+ * `true` frames all rows from the partition start through each row.
36
+ * Default can be omitted.
37
+ */
38
+ cumulative?: boolean | null,
39
+ /**
40
+ * Row offset for `lag`/`lead` (default 1).
41
+ */
42
+ offset?: number | null,
43
+ /**
44
+ * Smoothing factor in `(0, 1]` for `ema`.
45
+ */
46
+ alpha?: number | null, };
@@ -0,0 +1,10 @@
1
+ // This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
2
+ import type { WindowSpec } from "./WindowSpec.js";
3
+
4
+ /**
5
+ * The window columns of a `ViewConfig`, keyed by output column alias -
6
+ * symmetric with `expressions` (`{"name": {...spec}}`). An alias must not
7
+ * collide with a `Table` column, expression alias, or another window's
8
+ * key.
9
+ */
10
+ export type Windows = { [key in string]?: WindowSpec };
@@ -0,0 +1,36 @@
1
+ // This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
2
+ import type { Filter } from "./Filter.js";
3
+ import type { Layout } from "./Layout.js";
4
+ import type { PanelViewerConfig } from "./PanelViewerConfig.js";
5
+
6
+ /**
7
+ * The whole-element config format (`{version, active?, layout, panels}`) —
8
+ * the multi-panel counterpart of the single-panel [`ViewerConfig`] — as
9
+ * emitted by [`PerspectiveViewerElement::save`].
10
+ *
11
+ * - `panels` entries are [`PanelViewerConfig`]s: per-panel state only, no
12
+ * `settings` key (element-level state).
13
+ * - `active` names the panel targeted by the *open* settings sidebar; it is
14
+ * omitted when the sidebar is closed.
15
+ */
16
+ export type WorkspaceConfig = { version: string, active?: string, layout: Layout | null,
17
+ /**
18
+ * `BTreeMap` (not `HashMap`) so `save()` serializes panels in a
19
+ * DETERMINISTIC (sorted) key order — a fresh `HashMap` per call
20
+ * iterates in a per-instance random order, which made consecutive
21
+ * `save()` outputs byte-unstable.
22
+ */
23
+ panels: { [key in string]?: PanelViewerConfig<string> },
24
+ /**
25
+ * The element-level global (master/detail cross-) filters. A transient
26
+ * overlay on every detail panel's view — persisted here, never in a
27
+ * per-panel entry. Omitted when empty.
28
+ */
29
+ global_filters?: Array<Filter>,
30
+ /**
31
+ * The MASTER (filter-source) panels' ids, referencing `panels` keys.
32
+ * Roles are layout state (like the panel arrangement), so they persist;
33
+ * which master contributed which clause does not — restored
34
+ * `global_filters` are one unattributed bucket. Omitted when empty.
35
+ */
36
+ masters?: Array<string>, };
@@ -0,0 +1,24 @@
1
+ // This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
2
+ import type { Filter } from "./Filter.js";
3
+ import type { Layout } from "./Layout.js";
4
+ import type { ViewerConfigUpdate } from "./ViewerConfigUpdate.js";
5
+
6
+ /**
7
+ * The parse target of a whole-element config in
8
+ * [`PerspectiveViewerElement::restore`]. Mirrors [`WorkspaceConfig`],
9
+ * but `panels` entries are full [`ViewerConfigUpdate`]s so a stray per-panel
10
+ * `settings` can be detected (warned, then ignored — `create_panel` strips
11
+ * it).
12
+ */
13
+ export type WorkspaceConfigUpdate = { active?: string, layout?: Layout, panels: { [key in string]?: ViewerConfigUpdate },
14
+ /**
15
+ * The element-level global (master/detail cross-) filters to re-apply as
16
+ * a transient overlay on every DETAIL panel. Restored as one
17
+ * unattributed bucket: the next selection on any master replaces it.
18
+ */
19
+ global_filters?: Array<Filter>,
20
+ /**
21
+ * The master (filter-source) panels, by saved `panels` key. An id not in
22
+ * `panels` warns and is dropped.
23
+ */
24
+ masters?: Array<string>, };
@@ -1,98 +0,0 @@
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
- use wasm_bindgen::JsCast;
14
- use wasm_bindgen::prelude::*;
15
- use web_sys::*;
16
-
17
- use crate::config::ViewerConfigUpdate;
18
- use crate::js::*;
19
- use crate::presentation::Presentation;
20
- use crate::renderer::*;
21
- use crate::session::Session;
22
- use crate::utils::*;
23
- use crate::*;
24
-
25
- pub struct IntersectionObserverHandle {
26
- elem: HtmlElement,
27
- observer: IntersectionObserver,
28
- _callback: Closure<dyn FnMut(js_sys::Array)>,
29
- }
30
-
31
- impl IntersectionObserverHandle {
32
- pub fn new(
33
- elem: &HtmlElement,
34
- presentation: &Presentation,
35
- session: &Session,
36
- renderer: &Renderer,
37
- ) -> Self {
38
- clone!(session, renderer, presentation);
39
- let _callback = Closure::new(move |xs: js_sys::Array| {
40
- // https://stackoverflow.com/questions/53862160/intersectionobserver-multiple-entries
41
- let intersect = xs
42
- .pop()
43
- .unchecked_into::<IntersectionObserverEntry>()
44
- .is_intersecting();
45
-
46
- clone!(session, renderer, presentation);
47
- let state = IntersectionObserverState {
48
- presentation,
49
- session,
50
- renderer,
51
- };
52
-
53
- ApiFuture::spawn(state.set_pause(intersect));
54
- });
55
-
56
- let func = _callback.as_ref().unchecked_ref::<js_sys::Function>();
57
- let observer = IntersectionObserver::new(func);
58
- observer.observe(elem);
59
- Self {
60
- elem: elem.clone(),
61
- _callback,
62
- observer,
63
- }
64
- }
65
- }
66
-
67
- impl Drop for IntersectionObserverHandle {
68
- fn drop(&mut self) {
69
- self.observer.unobserve(&self.elem);
70
- }
71
- }
72
-
73
- struct IntersectionObserverState {
74
- session: Session,
75
- renderer: Renderer,
76
- presentation: Presentation,
77
- }
78
-
79
- impl IntersectionObserverState {
80
- async fn set_pause(self, intersect: bool) -> ApiResult<()> {
81
- if intersect {
82
- if self.session.set_pause(false) {
83
- super::restore_and_render(
84
- &self.session,
85
- &self.renderer,
86
- &self.presentation,
87
- ViewerConfigUpdate::default(),
88
- async move { Ok(()) },
89
- )
90
- .await?;
91
- }
92
- } else {
93
- self.session.set_pause(true);
94
- };
95
-
96
- Ok(())
97
- }
98
- }
@@ -1,110 +0,0 @@
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
- //! Apply a [`ViewConfigUpdate`] to the active [`Session`], validate it, then
14
- //! draw with the active [`Renderer`]. The companion `*_callback` helpers
15
- //! return [`yew::Callback`]s suitable for wiring as Yew child-component
16
- //! props.
17
-
18
- use perspective_client::config::ViewConfigUpdate;
19
- use yew::prelude::*;
20
-
21
- use crate::renderer::Renderer;
22
- use crate::session::Session;
23
- use crate::utils::*;
24
- use crate::*;
25
-
26
- /// Create a [`Callback`] that renders from the current `View` and `Plugin`.
27
- pub fn render_callback(session: &Session, renderer: &Renderer) -> Callback<()> {
28
- clone!(session, renderer);
29
- Callback::from(move |_| {
30
- clone!(session, renderer);
31
- ApiFuture::spawn(async move {
32
- renderer.draw(async { Ok(session.get_view()) }).await?;
33
- Ok(())
34
- })
35
- })
36
- }
37
-
38
- /// Create a [`Callback`] that resizes from the current `View` and `Plugin`.
39
- pub fn resize_callback(session: &Session, renderer: &Renderer) -> Callback<()> {
40
- clone!(session, renderer);
41
- Callback::from(move |_| {
42
- clone!(renderer, session);
43
- ApiFuture::spawn(async move {
44
- if !renderer.is_plugin_activated()? {
45
- update_and_render_inner(session, renderer).await?
46
- } else {
47
- renderer.resize().await?;
48
- }
49
-
50
- Ok(())
51
- })
52
- })
53
- }
54
-
55
- /// Apply a `ViewConfigUpdate` to the current `View` and render.
56
- pub fn update_and_render(
57
- session: &Session,
58
- renderer: &Renderer,
59
- update: ViewConfigUpdate,
60
- ) -> ApiResult<ApiFuture<()>> {
61
- session.update_view_config(update)?;
62
- clone!(session, renderer);
63
- Ok(ApiFuture::new(update_and_render_inner(session, renderer)))
64
- }
65
-
66
- /// Re-render the current `View` and `Plugin` without applying a new
67
- /// `ViewConfigUpdate`.
68
- pub fn just_render(session: &Session, renderer: &Renderer) -> ApiResult<ApiFuture<()>> {
69
- clone!(session, renderer);
70
- Ok(ApiFuture::new(update_and_render_inner(session, renderer)))
71
- }
72
-
73
- #[tracing::instrument(level = "debug", skip(session, renderer))]
74
- async fn update_and_render_inner(session: Session, renderer: Renderer) -> ApiResult<()> {
75
- // The previous call which acquired the lock errored, so skip this render
76
- if session.get_error().is_some() {
77
- return Ok(());
78
- }
79
-
80
- let plugin_swapped = renderer.apply_pending_plugin()?;
81
-
82
- // Validate + create the view BEFORE the plugin-swap materialize
83
- // so the schema query sees fresh `expression_schema` /
84
- // `view_schema` and `resolve_abs_max` has a bound view that
85
- // knows about any new expression columns.
86
- let view = session.validate().await?.create_view().await?;
87
-
88
- if plugin_swapped {
89
- // `commit_plugin_idx` already restored the new plugin from its
90
- // raw bucket; re-run with the materialized snapshot so any
91
- // `include: true` schema defaults (e.g. Datagrid's
92
- // `fg_gradient` when `number_fg_mode = "bar"`) make it into
93
- // the plugin's state before the first draw. The Session is in
94
- // scope here but not at `commit_plugin_idx`'s call sites in
95
- // the column-selector tree, so we do the second restore at
96
- // the caller instead of plumbing `&Session` through every
97
- // `apply_pending_plugin` site.
98
- let view_config_snapshot = session.get_view_config().clone();
99
- let plugin_token = wasm_bindgen::JsValue::from_serde_ext(&renderer.get_plugin_config())
100
- .unwrap_or(wasm_bindgen::JsValue::NULL);
101
- let columns_config = renderer
102
- .all_columns_configs_materialized(&view_config_snapshot, &session)
103
- .await;
104
- renderer
105
- .get_active_plugin()?
106
- .restore(&plugin_token, Some(&columns_config))?;
107
- }
108
-
109
- renderer.draw(async { Ok(view) }).await
110
- }