@perspective-dev/viewer-charts 4.3.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 (258) hide show
  1. package/LICENSE.md +193 -0
  2. package/dist/cdn/perspective-viewer-charts.js +3 -0
  3. package/dist/cdn/perspective-viewer-charts.js.map +7 -0
  4. package/dist/esm/axis/axis-primitives.d.ts +24 -0
  5. package/dist/esm/axis/bar-axis.d.ts +51 -0
  6. package/dist/esm/axis/canvas.d.ts +24 -0
  7. package/dist/esm/axis/categorical-axis-core.d.ts +42 -0
  8. package/dist/esm/axis/categorical-axis.d.ts +27 -0
  9. package/dist/esm/axis/facet-chrome.d.ts +13 -0
  10. package/dist/esm/axis/label-geometry.d.ts +41 -0
  11. package/dist/esm/axis/legend.d.ts +44 -0
  12. package/dist/esm/axis/numeric-axis.d.ts +20 -0
  13. package/dist/esm/charts/candlestick/candlestick-build.d.ts +129 -0
  14. package/dist/esm/charts/candlestick/candlestick-interact.d.ts +10 -0
  15. package/dist/esm/charts/candlestick/candlestick-render.d.ts +24 -0
  16. package/dist/esm/charts/candlestick/candlestick.d.ts +144 -0
  17. package/dist/esm/charts/candlestick/glyphs/draw-candlesticks.d.ts +36 -0
  18. package/dist/esm/charts/candlestick/glyphs/draw-ohlc.d.ts +33 -0
  19. package/dist/esm/charts/canvas-types.d.ts +15 -0
  20. package/dist/esm/charts/cartesian/cartesian-build.d.ts +14 -0
  21. package/dist/esm/charts/cartesian/cartesian-interact.d.ts +20 -0
  22. package/dist/esm/charts/cartesian/cartesian-render.d.ts +26 -0
  23. package/dist/esm/charts/cartesian/cartesian.d.ts +239 -0
  24. package/dist/esm/charts/cartesian/glyph.d.ts +53 -0
  25. package/dist/esm/charts/cartesian/glyphs/density.d.ts +142 -0
  26. package/dist/esm/charts/cartesian/glyphs/lines.d.ts +23 -0
  27. package/dist/esm/charts/cartesian/glyphs/points.d.ts +24 -0
  28. package/dist/esm/charts/cartesian/label-interner.d.ts +21 -0
  29. package/dist/esm/charts/cartesian/tooltip-lines.d.ts +11 -0
  30. package/dist/esm/charts/chart-base.d.ts +402 -0
  31. package/dist/esm/charts/chart.d.ts +338 -0
  32. package/dist/esm/charts/common/band-layout.d.ts +32 -0
  33. package/dist/esm/charts/common/categorical-y-chart.d.ts +53 -0
  34. package/dist/esm/charts/common/category-axis-resolver.d.ts +90 -0
  35. package/dist/esm/charts/common/chrome-cache.d.ts +18 -0
  36. package/dist/esm/charts/common/draw-tooltip-box.d.ts +9 -0
  37. package/dist/esm/charts/common/leaf-color.d.ts +33 -0
  38. package/dist/esm/charts/common/node-store.d.ts +81 -0
  39. package/dist/esm/charts/common/tree-chart.d.ts +48 -0
  40. package/dist/esm/charts/common/tree-chrome.d.ts +31 -0
  41. package/dist/esm/charts/common/tree-data.d.ts +54 -0
  42. package/dist/esm/charts/common/visible-extent.d.ts +51 -0
  43. package/dist/esm/charts/heatmap/heatmap-build.d.ts +86 -0
  44. package/dist/esm/charts/heatmap/heatmap-interact.d.ts +19 -0
  45. package/dist/esm/charts/heatmap/heatmap-render.d.ts +19 -0
  46. package/dist/esm/charts/heatmap/heatmap-y-axis.d.ts +46 -0
  47. package/dist/esm/charts/heatmap/heatmap.d.ts +117 -0
  48. package/dist/esm/charts/map/map.d.ts +67 -0
  49. package/dist/esm/charts/registry.d.ts +14 -0
  50. package/dist/esm/charts/series/glyphs/draw-areas.d.ts +30 -0
  51. package/dist/esm/charts/series/glyphs/draw-bars.d.ts +15 -0
  52. package/dist/esm/charts/series/glyphs/draw-lines.d.ts +34 -0
  53. package/dist/esm/charts/series/glyphs/draw-scatter.d.ts +33 -0
  54. package/dist/esm/charts/series/series-build.d.ts +228 -0
  55. package/dist/esm/charts/series/series-interact.d.ts +35 -0
  56. package/dist/esm/charts/series/series-render.d.ts +41 -0
  57. package/dist/esm/charts/series/series-type.d.ts +49 -0
  58. package/dist/esm/charts/series/series.d.ts +317 -0
  59. package/dist/esm/charts/sunburst/sunburst-interact.d.ts +7 -0
  60. package/dist/esm/charts/sunburst/sunburst-layout.d.ts +33 -0
  61. package/dist/esm/charts/sunburst/sunburst-render.d.ts +22 -0
  62. package/dist/esm/charts/sunburst/sunburst.d.ts +85 -0
  63. package/dist/esm/charts/treemap/treemap-interact.d.ts +12 -0
  64. package/dist/esm/charts/treemap/treemap-layout.d.ts +28 -0
  65. package/dist/esm/charts/treemap/treemap-render.d.ts +18 -0
  66. package/dist/esm/charts/treemap/treemap.d.ts +74 -0
  67. package/dist/esm/config.d.ts +27 -0
  68. package/dist/esm/data/lazy-row.d.ts +32 -0
  69. package/dist/esm/data/split-groups.d.ts +20 -0
  70. package/dist/esm/data/view-reader.d.ts +35 -0
  71. package/dist/esm/event-detail.d.ts +28 -0
  72. package/dist/esm/index.d.ts +3 -0
  73. package/dist/esm/interaction/hit-test.d.ts +30 -0
  74. package/dist/esm/interaction/host-sink-dom.d.ts +19 -0
  75. package/dist/esm/interaction/host-sink-message.d.ts +46 -0
  76. package/dist/esm/interaction/lazy-tooltip.d.ts +61 -0
  77. package/dist/esm/interaction/raw-event-forwarder.d.ts +27 -0
  78. package/dist/esm/interaction/spatial-grid.d.ts +15 -0
  79. package/dist/esm/interaction/tooltip-controller.d.ts +193 -0
  80. package/dist/esm/interaction/zoom-controller.d.ts +106 -0
  81. package/dist/esm/interaction/zoom-router.d.ts +48 -0
  82. package/dist/esm/layout/facet-grid.d.ts +126 -0
  83. package/dist/esm/layout/plot-layout.d.ts +104 -0
  84. package/dist/esm/layout/ticks.d.ts +17 -0
  85. package/dist/esm/map/mercator.d.ts +102 -0
  86. package/dist/esm/map/tile-cache.d.ts +38 -0
  87. package/dist/esm/map/tile-layer.d.ts +66 -0
  88. package/dist/esm/map/tile-loader.d.ts +52 -0
  89. package/dist/esm/map/tile-source.d.ts +66 -0
  90. package/dist/esm/perspective-viewer-charts.js +3 -0
  91. package/dist/esm/perspective-viewer-charts.js.map +7 -0
  92. package/dist/esm/plugin/charts.d.ts +40 -0
  93. package/dist/esm/plugin/plugin.d.ts +95 -0
  94. package/dist/esm/render/scheduler.d.ts +41 -0
  95. package/dist/esm/theme/gradient.d.ts +48 -0
  96. package/dist/esm/theme/palette.d.ts +13 -0
  97. package/dist/esm/theme/theme-snapshot.d.ts +7 -0
  98. package/dist/esm/theme/theme.d.ts +53 -0
  99. package/dist/esm/transport/protocol.d.ts +430 -0
  100. package/dist/esm/transport/renderer-transport.d.ts +201 -0
  101. package/dist/esm/utils/css.d.ts +1 -0
  102. package/dist/esm/utils/font-snapshot.d.ts +50 -0
  103. package/dist/esm/webgl/buffer-pool.d.ts +62 -0
  104. package/dist/esm/webgl/context-manager.d.ts +184 -0
  105. package/dist/esm/webgl/gradient-texture.d.ts +17 -0
  106. package/dist/esm/webgl/instanced-attrs.d.ts +44 -0
  107. package/dist/esm/webgl/plot-frame.d.ts +39 -0
  108. package/dist/esm/webgl/program-cache.d.ts +13 -0
  109. package/dist/esm/webgl/shader-manifest.d.ts +53 -0
  110. package/dist/esm/webgl/shader-registry.d.ts +22 -0
  111. package/dist/esm/worker/boot.d.ts +0 -0
  112. package/dist/esm/worker/dispatch.d.ts +9 -0
  113. package/dist/esm/worker/font-loader.d.ts +2 -0
  114. package/dist/esm/worker/renderer.worker.d.ts +115 -0
  115. package/dist/esm/worker/session-host.d.ts +26 -0
  116. package/package.json +47 -0
  117. package/src/css/perspective-viewer-charts.css +95 -0
  118. package/src/ts/axis/axis-primitives.ts +125 -0
  119. package/src/ts/axis/bar-axis.ts +345 -0
  120. package/src/ts/axis/canvas.ts +64 -0
  121. package/src/ts/axis/categorical-axis-core.ts +125 -0
  122. package/src/ts/axis/categorical-axis.ts +716 -0
  123. package/src/ts/axis/facet-chrome.ts +42 -0
  124. package/src/ts/axis/label-geometry.ts +188 -0
  125. package/src/ts/axis/legend.ts +218 -0
  126. package/src/ts/axis/numeric-axis.ts +353 -0
  127. package/src/ts/charts/candlestick/candlestick-build.ts +516 -0
  128. package/src/ts/charts/candlestick/candlestick-interact.ts +256 -0
  129. package/src/ts/charts/candlestick/candlestick-render.ts +387 -0
  130. package/src/ts/charts/candlestick/candlestick.ts +367 -0
  131. package/src/ts/charts/candlestick/glyphs/draw-candlesticks.ts +432 -0
  132. package/src/ts/charts/candlestick/glyphs/draw-ohlc.ts +317 -0
  133. package/src/ts/charts/canvas-types.ts +30 -0
  134. package/src/ts/charts/cartesian/cartesian-build.ts +616 -0
  135. package/src/ts/charts/cartesian/cartesian-interact.ts +355 -0
  136. package/src/ts/charts/cartesian/cartesian-render.ts +948 -0
  137. package/src/ts/charts/cartesian/cartesian.ts +469 -0
  138. package/src/ts/charts/cartesian/glyph.ts +81 -0
  139. package/src/ts/charts/cartesian/glyphs/density.ts +1263 -0
  140. package/src/ts/charts/cartesian/glyphs/lines.ts +320 -0
  141. package/src/ts/charts/cartesian/glyphs/points.ts +239 -0
  142. package/src/ts/charts/cartesian/label-interner.ts +56 -0
  143. package/src/ts/charts/cartesian/tooltip-lines.ts +80 -0
  144. package/src/ts/charts/chart-base.ts +840 -0
  145. package/src/ts/charts/chart.ts +427 -0
  146. package/src/ts/charts/common/band-layout.ts +63 -0
  147. package/src/ts/charts/common/categorical-y-chart.ts +81 -0
  148. package/src/ts/charts/common/category-axis-resolver.ts +314 -0
  149. package/src/ts/charts/common/chrome-cache.ts +79 -0
  150. package/src/ts/charts/common/draw-tooltip-box.ts +84 -0
  151. package/src/ts/charts/common/leaf-color.ts +92 -0
  152. package/src/ts/charts/common/node-store.ts +235 -0
  153. package/src/ts/charts/common/tree-chart.ts +76 -0
  154. package/src/ts/charts/common/tree-chrome.ts +123 -0
  155. package/src/ts/charts/common/tree-data.ts +623 -0
  156. package/src/ts/charts/common/visible-extent.ts +112 -0
  157. package/src/ts/charts/heatmap/heatmap-build.ts +426 -0
  158. package/src/ts/charts/heatmap/heatmap-interact.ts +274 -0
  159. package/src/ts/charts/heatmap/heatmap-render.ts +815 -0
  160. package/src/ts/charts/heatmap/heatmap-y-axis.ts +351 -0
  161. package/src/ts/charts/heatmap/heatmap.ts +368 -0
  162. package/src/ts/charts/map/map.ts +201 -0
  163. package/src/ts/charts/registry.ts +65 -0
  164. package/src/ts/charts/series/glyphs/draw-areas.ts +331 -0
  165. package/src/ts/charts/series/glyphs/draw-bars.ts +113 -0
  166. package/src/ts/charts/series/glyphs/draw-lines.ts +320 -0
  167. package/src/ts/charts/series/glyphs/draw-scatter.ts +328 -0
  168. package/src/ts/charts/series/series-build.ts +848 -0
  169. package/src/ts/charts/series/series-interact.ts +604 -0
  170. package/src/ts/charts/series/series-render.ts +1109 -0
  171. package/src/ts/charts/series/series-type.ts +99 -0
  172. package/src/ts/charts/series/series.ts +794 -0
  173. package/src/ts/charts/sunburst/sunburst-interact.ts +460 -0
  174. package/src/ts/charts/sunburst/sunburst-layout.ts +238 -0
  175. package/src/ts/charts/sunburst/sunburst-render.ts +887 -0
  176. package/src/ts/charts/sunburst/sunburst.ts +248 -0
  177. package/src/ts/charts/treemap/treemap-interact.ts +445 -0
  178. package/src/ts/charts/treemap/treemap-layout.ts +328 -0
  179. package/src/ts/charts/treemap/treemap-render.ts +886 -0
  180. package/src/ts/charts/treemap/treemap.ts +247 -0
  181. package/src/ts/config.ts +41 -0
  182. package/src/ts/data/lazy-row.ts +140 -0
  183. package/src/ts/data/split-groups.ts +97 -0
  184. package/src/ts/data/view-reader.ts +107 -0
  185. package/src/ts/event-detail.ts +44 -0
  186. package/src/ts/index.ts +53 -0
  187. package/src/ts/interaction/hit-test.ts +106 -0
  188. package/src/ts/interaction/host-sink-dom.ts +85 -0
  189. package/src/ts/interaction/host-sink-message.ts +75 -0
  190. package/src/ts/interaction/lazy-tooltip.ts +102 -0
  191. package/src/ts/interaction/raw-event-forwarder.ts +175 -0
  192. package/src/ts/interaction/spatial-grid.ts +100 -0
  193. package/src/ts/interaction/tooltip-controller.ts +407 -0
  194. package/src/ts/interaction/zoom-controller.ts +468 -0
  195. package/src/ts/interaction/zoom-router.ts +230 -0
  196. package/src/ts/layout/facet-grid.ts +346 -0
  197. package/src/ts/layout/plot-layout.ts +277 -0
  198. package/src/ts/layout/ticks.ts +168 -0
  199. package/src/ts/map/mercator.ts +204 -0
  200. package/src/ts/map/tile-cache.ts +96 -0
  201. package/src/ts/map/tile-layer.ts +382 -0
  202. package/src/ts/map/tile-loader.ts +143 -0
  203. package/src/ts/map/tile-source.ts +156 -0
  204. package/src/ts/plugin/charts.ts +286 -0
  205. package/src/ts/plugin/plugin.ts +668 -0
  206. package/src/ts/render/scheduler.ts +339 -0
  207. package/src/ts/shaders/area.frag.glsl +20 -0
  208. package/src/ts/shaders/area.vert.glsl +19 -0
  209. package/src/ts/shaders/bar.frag.glsl +25 -0
  210. package/src/ts/shaders/bar.vert.glsl +60 -0
  211. package/src/ts/shaders/candlestick-body.frag.glsl +19 -0
  212. package/src/ts/shaders/candlestick-body.vert.glsl +34 -0
  213. package/src/ts/shaders/density-extreme.frag.glsl +30 -0
  214. package/src/ts/shaders/density-mrt.frag.glsl +44 -0
  215. package/src/ts/shaders/density-mrt.vert.glsl +48 -0
  216. package/src/ts/shaders/density-resolve.frag.glsl +89 -0
  217. package/src/ts/shaders/density-resolve.vert.glsl +23 -0
  218. package/src/ts/shaders/density-splat.frag.glsl +34 -0
  219. package/src/ts/shaders/density-splat.vert.glsl +52 -0
  220. package/src/ts/shaders/gridline.frag.glsl +18 -0
  221. package/src/ts/shaders/gridline.vert.glsl +18 -0
  222. package/src/ts/shaders/heatmap.frag.glsl +23 -0
  223. package/src/ts/shaders/heatmap.vert.glsl +42 -0
  224. package/src/ts/shaders/line-uniform.frag.glsl +26 -0
  225. package/src/ts/shaders/line-uniform.vert.glsl +54 -0
  226. package/src/ts/shaders/line.frag.glsl +28 -0
  227. package/src/ts/shaders/line.vert.glsl +87 -0
  228. package/src/ts/shaders/scatter.frag.glsl +39 -0
  229. package/src/ts/shaders/scatter.vert.glsl +67 -0
  230. package/src/ts/shaders/sunburst-arc.frag.glsl +19 -0
  231. package/src/ts/shaders/sunburst-arc.vert.glsl +79 -0
  232. package/src/ts/shaders/tile.frag.glsl +27 -0
  233. package/src/ts/shaders/tile.vert.glsl +35 -0
  234. package/src/ts/shaders/treemap.frag.glsl +19 -0
  235. package/src/ts/shaders/treemap.vert.glsl +25 -0
  236. package/src/ts/shaders/y-scatter.frag.glsl +30 -0
  237. package/src/ts/shaders/y-scatter.vert.glsl +31 -0
  238. package/src/ts/theme/gradient.ts +312 -0
  239. package/src/ts/theme/palette.ts +64 -0
  240. package/src/ts/theme/theme-snapshot.ts +66 -0
  241. package/src/ts/theme/theme.ts +166 -0
  242. package/src/ts/transport/protocol.ts +497 -0
  243. package/src/ts/transport/renderer-transport.ts +788 -0
  244. package/src/ts/utils/css.ts +36 -0
  245. package/src/ts/utils/font-snapshot.ts +159 -0
  246. package/src/ts/webgl/buffer-pool.ts +163 -0
  247. package/src/ts/webgl/context-manager.ts +414 -0
  248. package/src/ts/webgl/gradient-texture.ts +84 -0
  249. package/src/ts/webgl/instanced-attrs.ts +139 -0
  250. package/src/ts/webgl/plot-frame.ts +91 -0
  251. package/src/ts/webgl/program-cache.ts +46 -0
  252. package/src/ts/webgl/shader-manifest.ts +148 -0
  253. package/src/ts/webgl/shader-registry.ts +97 -0
  254. package/src/ts/worker/boot.ts +22 -0
  255. package/src/ts/worker/dispatch.ts +99 -0
  256. package/src/ts/worker/font-loader.ts +89 -0
  257. package/src/ts/worker/renderer.worker.ts +734 -0
  258. package/src/ts/worker/session-host.ts +118 -0
@@ -0,0 +1,668 @@
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 type { View } from "@perspective-dev/client";
14
+ import type {
15
+ HTMLPerspectiveViewerElement,
16
+ IPerspectiveViewerPlugin,
17
+ PluginStaticConfig,
18
+ } from "@perspective-dev/viewer";
19
+ import { ChartTypeConfig, PluginConfigField } from "./charts";
20
+ import style from "../../css/perspective-viewer-charts.css";
21
+ import {
22
+ DEFAULT_FACET_CONFIG,
23
+ DEFAULT_PLUGIN_CONFIG,
24
+ type FacetConfig,
25
+ type PluginConfig,
26
+ } from "../charts/chart";
27
+ import { RawEventForwarder } from "../interaction/raw-event-forwarder";
28
+ import { RendererTransport } from "../transport/renderer-transport";
29
+ import { RENDER_BLIT_MODE } from "../config";
30
+
31
+ /**
32
+ * Facet-rendering defaults shared by every chart. Per-chart overrides
33
+ * arrive through `plugin_config` (`facet_mode` + `facet_zoom_mode`);
34
+ * the remaining fields (`shared_x_axis`, `shared_y_axis`,
35
+ * `coordinated_tooltip`, `facet_padding`) are not yet user-configurable
36
+ * — flip the defaults in `DEFAULT_FACET_CONFIG` to change globally.
37
+ */
38
+ const FACET_CONFIG_DEFAULTS: FacetConfig = { ...DEFAULT_FACET_CONFIG };
39
+
40
+ /**
41
+ * Build a UI control spec for one plugin-config field. Mirrors the
42
+ * shape `column_config_schema` already returns (datagrid). Numeric
43
+ * fields get a `Number` control with min/max clamps; fractions get a
44
+ * 0..1 range; enums + booleans pass through their variant list.
45
+ */
46
+ function fieldSpec(
47
+ key: PluginConfigField,
48
+ defaults: PluginConfig,
49
+ ): Record<string, unknown> & { kind: string } {
50
+ switch (key) {
51
+ case "auto_alt_y_axis":
52
+ return { kind: "Bool", key, default: defaults.auto_alt_y_axis };
53
+ case "include_zero":
54
+ return { kind: "Bool", key, default: defaults.include_zero };
55
+ case "domain_mode":
56
+ return {
57
+ kind: "Enum",
58
+ key,
59
+ default: defaults.domain_mode,
60
+ variants: [
61
+ { value: "fit", label: "Fit" },
62
+ { value: "expand", label: "Expand" },
63
+ ],
64
+ };
65
+ case "facet_mode":
66
+ return {
67
+ kind: "Enum",
68
+ key,
69
+ default: DEFAULT_PLUGIN_CONFIG.facet_mode,
70
+ variants: [
71
+ { value: "grid", label: "Grid" },
72
+ { value: "overlay", label: "Overlay" },
73
+ ],
74
+ };
75
+ case "facet_zoom_mode":
76
+ return {
77
+ kind: "Enum",
78
+ key,
79
+ default: DEFAULT_PLUGIN_CONFIG.facet_zoom_mode,
80
+ variants: [
81
+ { value: "shared", label: "Shared" },
82
+ { value: "independent", label: "Independent" },
83
+ ],
84
+ };
85
+ case "series_zoom_mode":
86
+ return {
87
+ kind: "Enum",
88
+ key,
89
+ default: DEFAULT_PLUGIN_CONFIG.series_zoom_mode,
90
+ variants: [
91
+ { value: "dynamic", label: "Dynamic" },
92
+ { value: "fixed", label: "Fixed" },
93
+ ],
94
+ };
95
+ case "line_width_px":
96
+ return {
97
+ kind: "Number",
98
+ key,
99
+ default: DEFAULT_PLUGIN_CONFIG.line_width_px,
100
+ min: 0.5,
101
+ step: 0.5,
102
+ max: 16,
103
+ };
104
+ case "point_size_px":
105
+ return {
106
+ kind: "Number",
107
+ key,
108
+ default: DEFAULT_PLUGIN_CONFIG.point_size_px,
109
+ min: 1,
110
+ max: 32,
111
+ };
112
+ case "band_inner_frac":
113
+ return {
114
+ kind: "Number",
115
+ key,
116
+ default: DEFAULT_PLUGIN_CONFIG.band_inner_frac,
117
+ min: 0.1,
118
+ max: 1,
119
+ step: 0.01,
120
+ };
121
+ case "bar_inner_pad":
122
+ return {
123
+ kind: "Number",
124
+ key,
125
+ default: DEFAULT_PLUGIN_CONFIG.bar_inner_pad,
126
+ min: 0,
127
+ max: 0.9,
128
+ step: 0.01,
129
+ };
130
+ case "wick_width_px":
131
+ return {
132
+ kind: "Number",
133
+ key,
134
+ default: DEFAULT_PLUGIN_CONFIG.wick_width_px,
135
+ min: 0.5,
136
+ step: 0.5,
137
+ max: 8,
138
+ };
139
+ case "ohlc_line_width_px":
140
+ return {
141
+ kind: "Number",
142
+ key,
143
+ default: DEFAULT_PLUGIN_CONFIG.ohlc_line_width_px,
144
+ min: 0.5,
145
+ step: 0.5,
146
+ max: 8,
147
+ };
148
+ case "gradient_radius_px":
149
+ return {
150
+ kind: "Number",
151
+ key,
152
+ default: DEFAULT_PLUGIN_CONFIG.gradient_radius_px,
153
+ min: 2,
154
+ step: 1,
155
+ max: 256,
156
+ };
157
+ case "gradient_intensity":
158
+ return {
159
+ kind: "Number",
160
+ key,
161
+ default: DEFAULT_PLUGIN_CONFIG.gradient_intensity,
162
+ min: 0.05,
163
+ step: 0.05,
164
+ max: 4,
165
+ };
166
+ case "gradient_heat_max":
167
+ return {
168
+ kind: "Number",
169
+ key,
170
+ default: DEFAULT_PLUGIN_CONFIG.gradient_heat_max,
171
+ min: 0.1,
172
+ step: 0.1,
173
+ max: 64,
174
+ };
175
+ case "gradient_color_mode":
176
+ return {
177
+ kind: "Enum",
178
+ key,
179
+ default: DEFAULT_PLUGIN_CONFIG.gradient_color_mode,
180
+ variants: [
181
+ { value: "mean", label: "Mean (density-weighted)" },
182
+ { value: "density", label: "Density only" },
183
+ { value: "extreme", label: "Extremes" },
184
+ { value: "signed", label: "Signed sum" },
185
+ ],
186
+ };
187
+ case "map_tile_provider":
188
+ return {
189
+ kind: "Enum",
190
+ key,
191
+ default: DEFAULT_PLUGIN_CONFIG.map_tile_provider,
192
+ variants: [
193
+ { value: "carto-positron", label: "Light (Positron)" },
194
+ { value: "carto-dark-matter", label: "Dark Matter" },
195
+ { value: "carto-voyager", label: "Voyager" },
196
+ ],
197
+ };
198
+ case "map_tile_alpha":
199
+ return {
200
+ kind: "Number",
201
+ key,
202
+ default: DEFAULT_PLUGIN_CONFIG.map_tile_alpha,
203
+ min: 0,
204
+ max: 1,
205
+ step: 0.05,
206
+ };
207
+ }
208
+ }
209
+
210
+ const GLOBAL_STYLES = (() => {
211
+ const sheet = new CSSStyleSheet();
212
+ sheet.replaceSync(style);
213
+ return [sheet];
214
+ })();
215
+
216
+ export class HTMLPerspectiveViewerWebGLPluginElement
217
+ extends HTMLElement
218
+ implements IPerspectiveViewerPlugin
219
+ {
220
+ declare _chartType: ChartTypeConfig;
221
+
222
+ private _initialized = false;
223
+ private _glCanvas!: HTMLCanvasElement;
224
+ private _gridlineCanvas!: HTMLCanvasElement;
225
+ private _chromeCanvas!: HTMLCanvasElement;
226
+ private _renderer: RendererTransport | null = null;
227
+ private _rendererPromise: Promise<RendererTransport> | null = null;
228
+ private _rawEventForwarder: RawEventForwarder | null = null;
229
+ private _generation = 0;
230
+ private _renderBlitMode: "direct" | "blit" = RENDER_BLIT_MODE;
231
+ private _resetClickAbort: AbortController | null = null;
232
+
233
+ /**
234
+ * Plugin-scoped global config. Seeded lazily from
235
+ * `_effectiveDefaults()` (which folds
236
+ * `_chartType.plugin_field_defaults` over `DEFAULT_PLUGIN_CONFIG`)
237
+ * because base-class field initializers run before the subclass
238
+ * `_chartType` assignment. `restore({ plugin_config })` merges
239
+ * incoming values on top of the same effective defaults so fields
240
+ * the host omits fall back to the chart-type default
241
+ * (`include_zero = true` for Y Bar / Y Area / X Bar, `false`
242
+ * elsewhere). Held on the element (not just inside the worker) so
243
+ * a `_buildRenderer` triggered after a `restore` ships the
244
+ * resolved values in the `InitMsg`.
245
+ */
246
+ private _pluginConfigStore: PluginConfig | null = null;
247
+
248
+ private get _pluginConfig(): PluginConfig {
249
+ if (!this._pluginConfigStore) {
250
+ this._pluginConfigStore = this._effectiveDefaults();
251
+ }
252
+
253
+ return this._pluginConfigStore;
254
+ }
255
+
256
+ private set _pluginConfig(value: PluginConfig) {
257
+ this._pluginConfigStore = value;
258
+ }
259
+
260
+ private _effectiveDefaults(): PluginConfig {
261
+ return {
262
+ ...DEFAULT_PLUGIN_CONFIG,
263
+ ...(this._chartType.plugin_field_defaults ?? {}),
264
+ };
265
+ }
266
+
267
+ connectedCallback() {
268
+ if (!this._initialized) {
269
+ this.attachShadow({ mode: "open" });
270
+ for (const sheet of GLOBAL_STYLES) {
271
+ this.shadowRoot!.adoptedStyleSheets.push(sheet);
272
+ }
273
+
274
+ this.shadowRoot!.innerHTML =
275
+ `<div class="webgl-container">` +
276
+ `<div class="zoom-controls">` +
277
+ `<button class="zoom-reset">Reset Zoom</button>` +
278
+ `</div>` +
279
+ `</div>`;
280
+
281
+ this._initialized = true;
282
+ }
283
+
284
+ if (!this._glCanvas?.isConnected) {
285
+ this._buildCanvasStack();
286
+ }
287
+ }
288
+
289
+ private _buildCanvasStack(): void {
290
+ const container = this.shadowRoot!.querySelector(".webgl-container")!;
291
+ container.insertAdjacentHTML(
292
+ "afterbegin",
293
+ `<canvas class="webgl-gridlines"></canvas>` +
294
+ `<canvas class="webgl-canvas"></canvas>` +
295
+ `<canvas class="webgl-chrome"></canvas>`,
296
+ );
297
+
298
+ this._glCanvas =
299
+ container.querySelector<HTMLCanvasElement>(".webgl-canvas")!;
300
+ this._gridlineCanvas =
301
+ container.querySelector<HTMLCanvasElement>(".webgl-gridlines")!;
302
+ this._chromeCanvas =
303
+ container.querySelector<HTMLCanvasElement>(".webgl-chrome")!;
304
+ }
305
+
306
+ private _clearCanvasStack(): void {
307
+ const container = this.shadowRoot?.querySelector(".webgl-container");
308
+ if (container) {
309
+ for (const c of Array.from(container.querySelectorAll("canvas"))) {
310
+ c.remove();
311
+ }
312
+ }
313
+
314
+ this._glCanvas = null!;
315
+ this._gridlineCanvas = null!;
316
+ this._chromeCanvas = null!;
317
+ }
318
+
319
+ /**
320
+ * Fires when the host (`<perspective-viewer>`) removes this plugin
321
+ * from the DOM on chart-type switch — see
322
+ * `renderer/activate.rs::remove_inactive_plugin`. Without this,
323
+ * inactive plugin instances retain their `RendererTransport`
324
+ * (worker + WebGL context + compiled shader programs) until the
325
+ * entire viewer is torn down, so a user cycling all 12 chart kinds
326
+ * holds 12 GL contexts per viewer and routinely exceeds the
327
+ * browser's per-page context cap (~16) in workspaces.
328
+ */
329
+ disconnectedCallback() {
330
+ this.delete();
331
+ this._clearCanvasStack();
332
+ }
333
+
334
+ /**
335
+ * Lazy renderer construction. Memoizes the in-flight `init()`
336
+ * promise so concurrent `draw()` calls during async setup await
337
+ * the same initialization rather than racing.
338
+ */
339
+ private _ensureRenderer(view: View): Promise<RendererTransport> {
340
+ if (!this._initialized) {
341
+ this.connectedCallback();
342
+ }
343
+
344
+ if (this._rendererPromise) {
345
+ return this._rendererPromise;
346
+ }
347
+
348
+ this._rendererPromise = this._buildRenderer(view).then((r) => {
349
+ this._renderer = r;
350
+ this._setupInteraction(r);
351
+ return r;
352
+ });
353
+
354
+ return this._rendererPromise;
355
+ }
356
+
357
+ /**
358
+ * Capture raw DOM events on the GL canvas with `RawEventForwarder`
359
+ * and post them over the control channel. The renderer dispatches
360
+ * them through its own resolver + `applyWheel` / `applyPan` for
361
+ * zoom/pan, and through `TooltipController` virtual dispatch for
362
+ * hover/click; `zoomChanged` updates push back so the reset-zoom
363
+ * button visibility tracks the renderer-side state.
364
+ *
365
+ * The `zoomChanged` callback was wired at `RendererTransport`
366
+ * construction time; here we just attach the event forwarder and
367
+ * the reset-button click handler.
368
+ */
369
+ private _setupInteraction(renderer: RendererTransport): void {
370
+ if (this._rawEventForwarder) {
371
+ return;
372
+ }
373
+
374
+ const zoomControls = this.shadowRoot!.querySelector(
375
+ ".zoom-controls",
376
+ ) as HTMLDivElement | null;
377
+
378
+ this._rawEventForwarder = new RawEventForwarder();
379
+ this._rawEventForwarder.attach(this._glCanvas, (event) => {
380
+ renderer.forwardInteraction(event);
381
+ });
382
+
383
+ const resetBtn = this.shadowRoot!.querySelector(".zoom-reset");
384
+ if (resetBtn) {
385
+ this._resetClickAbort = new AbortController();
386
+ resetBtn.addEventListener(
387
+ "click",
388
+ () => {
389
+ renderer.resetAllZooms();
390
+ if (zoomControls) {
391
+ zoomControls.classList.remove("visible");
392
+ }
393
+ },
394
+ { signal: this._resetClickAbort.signal },
395
+ );
396
+ }
397
+ }
398
+
399
+ private async _buildRenderer(view: View): Promise<RendererTransport> {
400
+ const viewer = this.parentElement as HTMLPerspectiveViewerElement;
401
+ const client = await viewer.getClient();
402
+ const viewer_class = customElements.get("perspective-viewer");
403
+ const clientWasm = viewer_class.get_wasm_module();
404
+ const clientWorkerURL = viewer_class.get_worker_url();
405
+ const table = await viewer?.getTable?.();
406
+ const tableName: string | undefined = table
407
+ ? await table.get_name()
408
+ : undefined;
409
+
410
+ const zoomControls = this.shadowRoot!.querySelector(
411
+ ".zoom-controls",
412
+ ) as HTMLDivElement | null;
413
+
414
+ const transport = new RendererTransport({
415
+ client,
416
+ view,
417
+ tableName,
418
+ clientWorkerURL,
419
+ clientWasm,
420
+ chartTag: this._chartType.tag,
421
+ maxCells: this._chartType.max_cells,
422
+ precompileShaders: true,
423
+ onZoomChanged: (isDefault: boolean) => {
424
+ if (zoomControls) {
425
+ zoomControls.classList.toggle("visible", !isDefault);
426
+ }
427
+ },
428
+ });
429
+
430
+ await transport.init({
431
+ gl: this._glCanvas,
432
+ gridlines: this._gridlineCanvas,
433
+ chrome: this._chromeCanvas,
434
+ facetConfig: {
435
+ ...FACET_CONFIG_DEFAULTS,
436
+ facet_mode: this._pluginConfig.facet_mode,
437
+ zoom_mode: this._pluginConfig.facet_zoom_mode,
438
+ },
439
+ pluginConfig: this._pluginConfig,
440
+ defaultChartType: this._chartType.default_chart_type,
441
+ renderBlitMode: this._renderBlitMode,
442
+ });
443
+
444
+ return transport;
445
+ }
446
+
447
+ setBlitMode(mode: "direct" | "blit") {
448
+ console.assert(this._initialized, "Already initialized");
449
+ this._renderBlitMode = mode;
450
+ }
451
+
452
+ get_static_config(): PluginStaticConfig {
453
+ return {
454
+ name: this._chartType.name,
455
+ category: this._chartType.category,
456
+ select_mode: this._chartType.selectMode,
457
+ min_config_columns: this._chartType.initial.count,
458
+ config_column_names: this._chartType.initial.names,
459
+ max_cells: this._chartType.max_cells,
460
+ max_columns: this._chartType.max_columns,
461
+ group_rollup_modes: ["flat"],
462
+ priority: 0,
463
+ can_render_column_styles:
464
+ !!this._chartType.default_chart_type ||
465
+ this._chartType.category === "Cartesian Charts",
466
+ };
467
+ }
468
+
469
+ column_config_schema(
470
+ column_type: string,
471
+ _group: string | undefined,
472
+ _column_name: string,
473
+ current_value: Record<string, unknown> | null,
474
+ _viewer_config?: { group_by?: string[]; group_rollup_mode?: string },
475
+ ) {
476
+ const fields: Array<Record<string, unknown> & { kind: string }> = [];
477
+
478
+ // Y-series plugins expose the per-column chart_type picker; non-Y
479
+ // plugins leave `default_chart_type` unset.
480
+ const def = this._chartType.default_chart_type;
481
+ if (def && (column_type === "integer" || column_type === "float")) {
482
+ fields.push({
483
+ kind: "Enum",
484
+ key: "chart_type",
485
+ default: def,
486
+ variants: [
487
+ { value: "bar", label: "Bar" },
488
+ { value: "line", label: "Line" },
489
+ { value: "scatter", label: "Scatter" },
490
+ { value: "area", label: "Area" },
491
+ ],
492
+ });
493
+
494
+ const effective_chart_type =
495
+ (current_value?.chart_type as string | undefined) ?? def;
496
+
497
+ const supports_stack =
498
+ effective_chart_type === "bar" ||
499
+ effective_chart_type === "area";
500
+
501
+ if (supports_stack) {
502
+ fields.push({
503
+ kind: "Bool",
504
+ key: "stack",
505
+ default: supports_stack,
506
+ });
507
+ }
508
+
509
+ const is_series_glyph =
510
+ def === "bar" ||
511
+ def === "line" ||
512
+ def === "scatter" ||
513
+ def === "area";
514
+
515
+ if (is_series_glyph) {
516
+ fields.push({
517
+ kind: "Bool",
518
+ key: "alt_axis",
519
+ default: false,
520
+ });
521
+ }
522
+ }
523
+
524
+ // Per-column formatter widgets. Surfaced for every chart type so
525
+ // axes / tooltips / legends honor the user's format choice.
526
+ if (column_type === "integer" || column_type === "float") {
527
+ fields.push({ kind: "NumberFormat" });
528
+ } else if (column_type === "date" || column_type === "datetime") {
529
+ fields.push({ kind: "DatetimeFormat" });
530
+ }
531
+
532
+ return { fields };
533
+ }
534
+
535
+ plugin_config_schema(_view_config?: {
536
+ group_by?: string[];
537
+ group_rollup_mode?: string;
538
+ }) {
539
+ const defaults = this._effectiveDefaults();
540
+ const fields = this._chartType.applicable_plugin_fields.map((key) =>
541
+ fieldSpec(key, defaults),
542
+ );
543
+
544
+ return { fields };
545
+ }
546
+
547
+ async draw(view: View): Promise<void> {
548
+ // `draw` always indicates a view-level change (pivots, columns,
549
+ // filters, sorts, schema, …) — invalidate the `domain_mode:
550
+ // "expand"` accumulator so the new view's extent starts fresh.
551
+ // `update` (data-only redraw on the same view) shares
552
+ // `_drawImpl` but skips this reset.
553
+ this._renderer?.resetExpandedDomain();
554
+ this._renderer?.resetAllZooms();
555
+ return this._drawImpl(view);
556
+ }
557
+
558
+ async update(view: View): Promise<void> {
559
+ return this._drawImpl(view);
560
+ }
561
+
562
+ private async _drawImpl(view: View): Promise<void> {
563
+ const gen = ++this._generation;
564
+ const renderer = await this._ensureRenderer(view);
565
+ if (this._generation !== gen) {
566
+ return;
567
+ }
568
+
569
+ renderer.setView(view);
570
+ renderer.setBufferMaxCapacity(this._chartType.max_cells);
571
+ const viewer = this
572
+ .parentElement as HTMLPerspectiveViewerElement | null;
573
+ const viewerConfig = (await viewer?.getViewConfig?.()) ?? {};
574
+ if (this._generation !== gen) {
575
+ return;
576
+ }
577
+
578
+ await renderer.loadAndRender({
579
+ viewerConfig: {
580
+ group_by: viewerConfig?.group_by ?? [],
581
+ split_by: viewerConfig?.split_by ?? [],
582
+ columns: viewerConfig?.columns ?? [],
583
+ },
584
+ options: { float32: true },
585
+ });
586
+ }
587
+
588
+ async clear(): Promise<void> {
589
+ this._generation++;
590
+ this._renderer?.clear();
591
+ }
592
+
593
+ async resize(): Promise<void> {
594
+ this._renderer?.resize();
595
+ }
596
+
597
+ restyle() {
598
+ this._renderer?.invalidateTheme();
599
+ return 5;
600
+ }
601
+
602
+ save() {
603
+ const state: any = {};
604
+ const zoom = this._renderer?.saveZoom();
605
+ if (zoom) {
606
+ state.zoom = zoom;
607
+ }
608
+
609
+ // Only emit the keys this chart actually consumes.
610
+ const cfg: Partial<PluginConfig> = {};
611
+ for (const key of this._chartType.applicable_plugin_fields) {
612
+ // `key` is `PluginConfigField` = `keyof PluginConfig`, so this
613
+ // indexed assignment is type-safe without a cast.
614
+ (cfg[key] as PluginConfig[typeof key]) = this._pluginConfig[key];
615
+ }
616
+
617
+ if (Object.keys(cfg).length > 0) {
618
+ state.plugin_config = cfg;
619
+ }
620
+
621
+ return state;
622
+ }
623
+
624
+ async render(view: View): Promise<Blob> {
625
+ await this._ensureRenderer(view);
626
+ await this.draw(view);
627
+ return this._renderer!.snapshotPng();
628
+ }
629
+
630
+ restore(config: any, columns_config?: Record<string, any>) {
631
+ if (config?.zoom) {
632
+ this._renderer?.restoreZoom(config.zoom);
633
+ }
634
+
635
+ // Merge incoming plugin_config on top of the `chart_type`
636
+ // effective defaults so a partial restore (UI emits only
637
+ // changed fields) keeps untouched defaults in place — and
638
+ // chart-type overrides (e.g. `include_zero=true` for Y Bar /
639
+ // Y Area / X Bar) survive when the host elides their values.
640
+ this._pluginConfig = {
641
+ ...this._effectiveDefaults(),
642
+ ...config,
643
+ };
644
+
645
+ this._renderer?.setPluginConfig(this._pluginConfig);
646
+ this._renderer?.setColumnsConfig(columns_config ?? {});
647
+ }
648
+
649
+ delete() {
650
+ this._generation++;
651
+ if (this._rawEventForwarder) {
652
+ this._rawEventForwarder.detach();
653
+ this._rawEventForwarder = null;
654
+ }
655
+
656
+ if (this._resetClickAbort) {
657
+ this._resetClickAbort.abort();
658
+ this._resetClickAbort = null;
659
+ }
660
+
661
+ if (this._renderer) {
662
+ this._renderer.destroy();
663
+ this._renderer = null;
664
+ }
665
+
666
+ this._rendererPromise = null;
667
+ }
668
+ }