@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.
- package/LICENSE.md +193 -0
- package/dist/cdn/perspective-viewer-charts.js +3 -0
- package/dist/cdn/perspective-viewer-charts.js.map +7 -0
- package/dist/esm/axis/axis-primitives.d.ts +24 -0
- package/dist/esm/axis/bar-axis.d.ts +51 -0
- package/dist/esm/axis/canvas.d.ts +24 -0
- package/dist/esm/axis/categorical-axis-core.d.ts +42 -0
- package/dist/esm/axis/categorical-axis.d.ts +27 -0
- package/dist/esm/axis/facet-chrome.d.ts +13 -0
- package/dist/esm/axis/label-geometry.d.ts +41 -0
- package/dist/esm/axis/legend.d.ts +44 -0
- package/dist/esm/axis/numeric-axis.d.ts +20 -0
- package/dist/esm/charts/candlestick/candlestick-build.d.ts +129 -0
- package/dist/esm/charts/candlestick/candlestick-interact.d.ts +10 -0
- package/dist/esm/charts/candlestick/candlestick-render.d.ts +24 -0
- package/dist/esm/charts/candlestick/candlestick.d.ts +144 -0
- package/dist/esm/charts/candlestick/glyphs/draw-candlesticks.d.ts +36 -0
- package/dist/esm/charts/candlestick/glyphs/draw-ohlc.d.ts +33 -0
- package/dist/esm/charts/canvas-types.d.ts +15 -0
- package/dist/esm/charts/cartesian/cartesian-build.d.ts +14 -0
- package/dist/esm/charts/cartesian/cartesian-interact.d.ts +20 -0
- package/dist/esm/charts/cartesian/cartesian-render.d.ts +26 -0
- package/dist/esm/charts/cartesian/cartesian.d.ts +239 -0
- package/dist/esm/charts/cartesian/glyph.d.ts +53 -0
- package/dist/esm/charts/cartesian/glyphs/density.d.ts +142 -0
- package/dist/esm/charts/cartesian/glyphs/lines.d.ts +23 -0
- package/dist/esm/charts/cartesian/glyphs/points.d.ts +24 -0
- package/dist/esm/charts/cartesian/label-interner.d.ts +21 -0
- package/dist/esm/charts/cartesian/tooltip-lines.d.ts +11 -0
- package/dist/esm/charts/chart-base.d.ts +402 -0
- package/dist/esm/charts/chart.d.ts +338 -0
- package/dist/esm/charts/common/band-layout.d.ts +32 -0
- package/dist/esm/charts/common/categorical-y-chart.d.ts +53 -0
- package/dist/esm/charts/common/category-axis-resolver.d.ts +90 -0
- package/dist/esm/charts/common/chrome-cache.d.ts +18 -0
- package/dist/esm/charts/common/draw-tooltip-box.d.ts +9 -0
- package/dist/esm/charts/common/leaf-color.d.ts +33 -0
- package/dist/esm/charts/common/node-store.d.ts +81 -0
- package/dist/esm/charts/common/tree-chart.d.ts +48 -0
- package/dist/esm/charts/common/tree-chrome.d.ts +31 -0
- package/dist/esm/charts/common/tree-data.d.ts +54 -0
- package/dist/esm/charts/common/visible-extent.d.ts +51 -0
- package/dist/esm/charts/heatmap/heatmap-build.d.ts +86 -0
- package/dist/esm/charts/heatmap/heatmap-interact.d.ts +19 -0
- package/dist/esm/charts/heatmap/heatmap-render.d.ts +19 -0
- package/dist/esm/charts/heatmap/heatmap-y-axis.d.ts +46 -0
- package/dist/esm/charts/heatmap/heatmap.d.ts +117 -0
- package/dist/esm/charts/map/map.d.ts +67 -0
- package/dist/esm/charts/registry.d.ts +14 -0
- package/dist/esm/charts/series/glyphs/draw-areas.d.ts +30 -0
- package/dist/esm/charts/series/glyphs/draw-bars.d.ts +15 -0
- package/dist/esm/charts/series/glyphs/draw-lines.d.ts +34 -0
- package/dist/esm/charts/series/glyphs/draw-scatter.d.ts +33 -0
- package/dist/esm/charts/series/series-build.d.ts +228 -0
- package/dist/esm/charts/series/series-interact.d.ts +35 -0
- package/dist/esm/charts/series/series-render.d.ts +41 -0
- package/dist/esm/charts/series/series-type.d.ts +49 -0
- package/dist/esm/charts/series/series.d.ts +317 -0
- package/dist/esm/charts/sunburst/sunburst-interact.d.ts +7 -0
- package/dist/esm/charts/sunburst/sunburst-layout.d.ts +33 -0
- package/dist/esm/charts/sunburst/sunburst-render.d.ts +22 -0
- package/dist/esm/charts/sunburst/sunburst.d.ts +85 -0
- package/dist/esm/charts/treemap/treemap-interact.d.ts +12 -0
- package/dist/esm/charts/treemap/treemap-layout.d.ts +28 -0
- package/dist/esm/charts/treemap/treemap-render.d.ts +18 -0
- package/dist/esm/charts/treemap/treemap.d.ts +74 -0
- package/dist/esm/config.d.ts +27 -0
- package/dist/esm/data/lazy-row.d.ts +32 -0
- package/dist/esm/data/split-groups.d.ts +20 -0
- package/dist/esm/data/view-reader.d.ts +35 -0
- package/dist/esm/event-detail.d.ts +28 -0
- package/dist/esm/index.d.ts +3 -0
- package/dist/esm/interaction/hit-test.d.ts +30 -0
- package/dist/esm/interaction/host-sink-dom.d.ts +19 -0
- package/dist/esm/interaction/host-sink-message.d.ts +46 -0
- package/dist/esm/interaction/lazy-tooltip.d.ts +61 -0
- package/dist/esm/interaction/raw-event-forwarder.d.ts +27 -0
- package/dist/esm/interaction/spatial-grid.d.ts +15 -0
- package/dist/esm/interaction/tooltip-controller.d.ts +193 -0
- package/dist/esm/interaction/zoom-controller.d.ts +106 -0
- package/dist/esm/interaction/zoom-router.d.ts +48 -0
- package/dist/esm/layout/facet-grid.d.ts +126 -0
- package/dist/esm/layout/plot-layout.d.ts +104 -0
- package/dist/esm/layout/ticks.d.ts +17 -0
- package/dist/esm/map/mercator.d.ts +102 -0
- package/dist/esm/map/tile-cache.d.ts +38 -0
- package/dist/esm/map/tile-layer.d.ts +66 -0
- package/dist/esm/map/tile-loader.d.ts +52 -0
- package/dist/esm/map/tile-source.d.ts +66 -0
- package/dist/esm/perspective-viewer-charts.js +3 -0
- package/dist/esm/perspective-viewer-charts.js.map +7 -0
- package/dist/esm/plugin/charts.d.ts +40 -0
- package/dist/esm/plugin/plugin.d.ts +95 -0
- package/dist/esm/render/scheduler.d.ts +41 -0
- package/dist/esm/theme/gradient.d.ts +48 -0
- package/dist/esm/theme/palette.d.ts +13 -0
- package/dist/esm/theme/theme-snapshot.d.ts +7 -0
- package/dist/esm/theme/theme.d.ts +53 -0
- package/dist/esm/transport/protocol.d.ts +430 -0
- package/dist/esm/transport/renderer-transport.d.ts +201 -0
- package/dist/esm/utils/css.d.ts +1 -0
- package/dist/esm/utils/font-snapshot.d.ts +50 -0
- package/dist/esm/webgl/buffer-pool.d.ts +62 -0
- package/dist/esm/webgl/context-manager.d.ts +184 -0
- package/dist/esm/webgl/gradient-texture.d.ts +17 -0
- package/dist/esm/webgl/instanced-attrs.d.ts +44 -0
- package/dist/esm/webgl/plot-frame.d.ts +39 -0
- package/dist/esm/webgl/program-cache.d.ts +13 -0
- package/dist/esm/webgl/shader-manifest.d.ts +53 -0
- package/dist/esm/webgl/shader-registry.d.ts +22 -0
- package/dist/esm/worker/boot.d.ts +0 -0
- package/dist/esm/worker/dispatch.d.ts +9 -0
- package/dist/esm/worker/font-loader.d.ts +2 -0
- package/dist/esm/worker/renderer.worker.d.ts +115 -0
- package/dist/esm/worker/session-host.d.ts +26 -0
- package/package.json +47 -0
- package/src/css/perspective-viewer-charts.css +95 -0
- package/src/ts/axis/axis-primitives.ts +125 -0
- package/src/ts/axis/bar-axis.ts +345 -0
- package/src/ts/axis/canvas.ts +64 -0
- package/src/ts/axis/categorical-axis-core.ts +125 -0
- package/src/ts/axis/categorical-axis.ts +716 -0
- package/src/ts/axis/facet-chrome.ts +42 -0
- package/src/ts/axis/label-geometry.ts +188 -0
- package/src/ts/axis/legend.ts +218 -0
- package/src/ts/axis/numeric-axis.ts +353 -0
- package/src/ts/charts/candlestick/candlestick-build.ts +516 -0
- package/src/ts/charts/candlestick/candlestick-interact.ts +256 -0
- package/src/ts/charts/candlestick/candlestick-render.ts +387 -0
- package/src/ts/charts/candlestick/candlestick.ts +367 -0
- package/src/ts/charts/candlestick/glyphs/draw-candlesticks.ts +432 -0
- package/src/ts/charts/candlestick/glyphs/draw-ohlc.ts +317 -0
- package/src/ts/charts/canvas-types.ts +30 -0
- package/src/ts/charts/cartesian/cartesian-build.ts +616 -0
- package/src/ts/charts/cartesian/cartesian-interact.ts +355 -0
- package/src/ts/charts/cartesian/cartesian-render.ts +948 -0
- package/src/ts/charts/cartesian/cartesian.ts +469 -0
- package/src/ts/charts/cartesian/glyph.ts +81 -0
- package/src/ts/charts/cartesian/glyphs/density.ts +1263 -0
- package/src/ts/charts/cartesian/glyphs/lines.ts +320 -0
- package/src/ts/charts/cartesian/glyphs/points.ts +239 -0
- package/src/ts/charts/cartesian/label-interner.ts +56 -0
- package/src/ts/charts/cartesian/tooltip-lines.ts +80 -0
- package/src/ts/charts/chart-base.ts +840 -0
- package/src/ts/charts/chart.ts +427 -0
- package/src/ts/charts/common/band-layout.ts +63 -0
- package/src/ts/charts/common/categorical-y-chart.ts +81 -0
- package/src/ts/charts/common/category-axis-resolver.ts +314 -0
- package/src/ts/charts/common/chrome-cache.ts +79 -0
- package/src/ts/charts/common/draw-tooltip-box.ts +84 -0
- package/src/ts/charts/common/leaf-color.ts +92 -0
- package/src/ts/charts/common/node-store.ts +235 -0
- package/src/ts/charts/common/tree-chart.ts +76 -0
- package/src/ts/charts/common/tree-chrome.ts +123 -0
- package/src/ts/charts/common/tree-data.ts +623 -0
- package/src/ts/charts/common/visible-extent.ts +112 -0
- package/src/ts/charts/heatmap/heatmap-build.ts +426 -0
- package/src/ts/charts/heatmap/heatmap-interact.ts +274 -0
- package/src/ts/charts/heatmap/heatmap-render.ts +815 -0
- package/src/ts/charts/heatmap/heatmap-y-axis.ts +351 -0
- package/src/ts/charts/heatmap/heatmap.ts +368 -0
- package/src/ts/charts/map/map.ts +201 -0
- package/src/ts/charts/registry.ts +65 -0
- package/src/ts/charts/series/glyphs/draw-areas.ts +331 -0
- package/src/ts/charts/series/glyphs/draw-bars.ts +113 -0
- package/src/ts/charts/series/glyphs/draw-lines.ts +320 -0
- package/src/ts/charts/series/glyphs/draw-scatter.ts +328 -0
- package/src/ts/charts/series/series-build.ts +848 -0
- package/src/ts/charts/series/series-interact.ts +604 -0
- package/src/ts/charts/series/series-render.ts +1109 -0
- package/src/ts/charts/series/series-type.ts +99 -0
- package/src/ts/charts/series/series.ts +794 -0
- package/src/ts/charts/sunburst/sunburst-interact.ts +460 -0
- package/src/ts/charts/sunburst/sunburst-layout.ts +238 -0
- package/src/ts/charts/sunburst/sunburst-render.ts +887 -0
- package/src/ts/charts/sunburst/sunburst.ts +248 -0
- package/src/ts/charts/treemap/treemap-interact.ts +445 -0
- package/src/ts/charts/treemap/treemap-layout.ts +328 -0
- package/src/ts/charts/treemap/treemap-render.ts +886 -0
- package/src/ts/charts/treemap/treemap.ts +247 -0
- package/src/ts/config.ts +41 -0
- package/src/ts/data/lazy-row.ts +140 -0
- package/src/ts/data/split-groups.ts +97 -0
- package/src/ts/data/view-reader.ts +107 -0
- package/src/ts/event-detail.ts +44 -0
- package/src/ts/index.ts +53 -0
- package/src/ts/interaction/hit-test.ts +106 -0
- package/src/ts/interaction/host-sink-dom.ts +85 -0
- package/src/ts/interaction/host-sink-message.ts +75 -0
- package/src/ts/interaction/lazy-tooltip.ts +102 -0
- package/src/ts/interaction/raw-event-forwarder.ts +175 -0
- package/src/ts/interaction/spatial-grid.ts +100 -0
- package/src/ts/interaction/tooltip-controller.ts +407 -0
- package/src/ts/interaction/zoom-controller.ts +468 -0
- package/src/ts/interaction/zoom-router.ts +230 -0
- package/src/ts/layout/facet-grid.ts +346 -0
- package/src/ts/layout/plot-layout.ts +277 -0
- package/src/ts/layout/ticks.ts +168 -0
- package/src/ts/map/mercator.ts +204 -0
- package/src/ts/map/tile-cache.ts +96 -0
- package/src/ts/map/tile-layer.ts +382 -0
- package/src/ts/map/tile-loader.ts +143 -0
- package/src/ts/map/tile-source.ts +156 -0
- package/src/ts/plugin/charts.ts +286 -0
- package/src/ts/plugin/plugin.ts +668 -0
- package/src/ts/render/scheduler.ts +339 -0
- package/src/ts/shaders/area.frag.glsl +20 -0
- package/src/ts/shaders/area.vert.glsl +19 -0
- package/src/ts/shaders/bar.frag.glsl +25 -0
- package/src/ts/shaders/bar.vert.glsl +60 -0
- package/src/ts/shaders/candlestick-body.frag.glsl +19 -0
- package/src/ts/shaders/candlestick-body.vert.glsl +34 -0
- package/src/ts/shaders/density-extreme.frag.glsl +30 -0
- package/src/ts/shaders/density-mrt.frag.glsl +44 -0
- package/src/ts/shaders/density-mrt.vert.glsl +48 -0
- package/src/ts/shaders/density-resolve.frag.glsl +89 -0
- package/src/ts/shaders/density-resolve.vert.glsl +23 -0
- package/src/ts/shaders/density-splat.frag.glsl +34 -0
- package/src/ts/shaders/density-splat.vert.glsl +52 -0
- package/src/ts/shaders/gridline.frag.glsl +18 -0
- package/src/ts/shaders/gridline.vert.glsl +18 -0
- package/src/ts/shaders/heatmap.frag.glsl +23 -0
- package/src/ts/shaders/heatmap.vert.glsl +42 -0
- package/src/ts/shaders/line-uniform.frag.glsl +26 -0
- package/src/ts/shaders/line-uniform.vert.glsl +54 -0
- package/src/ts/shaders/line.frag.glsl +28 -0
- package/src/ts/shaders/line.vert.glsl +87 -0
- package/src/ts/shaders/scatter.frag.glsl +39 -0
- package/src/ts/shaders/scatter.vert.glsl +67 -0
- package/src/ts/shaders/sunburst-arc.frag.glsl +19 -0
- package/src/ts/shaders/sunburst-arc.vert.glsl +79 -0
- package/src/ts/shaders/tile.frag.glsl +27 -0
- package/src/ts/shaders/tile.vert.glsl +35 -0
- package/src/ts/shaders/treemap.frag.glsl +19 -0
- package/src/ts/shaders/treemap.vert.glsl +25 -0
- package/src/ts/shaders/y-scatter.frag.glsl +30 -0
- package/src/ts/shaders/y-scatter.vert.glsl +31 -0
- package/src/ts/theme/gradient.ts +312 -0
- package/src/ts/theme/palette.ts +64 -0
- package/src/ts/theme/theme-snapshot.ts +66 -0
- package/src/ts/theme/theme.ts +166 -0
- package/src/ts/transport/protocol.ts +497 -0
- package/src/ts/transport/renderer-transport.ts +788 -0
- package/src/ts/utils/css.ts +36 -0
- package/src/ts/utils/font-snapshot.ts +159 -0
- package/src/ts/webgl/buffer-pool.ts +163 -0
- package/src/ts/webgl/context-manager.ts +414 -0
- package/src/ts/webgl/gradient-texture.ts +84 -0
- package/src/ts/webgl/instanced-attrs.ts +139 -0
- package/src/ts/webgl/plot-frame.ts +91 -0
- package/src/ts/webgl/program-cache.ts +46 -0
- package/src/ts/webgl/shader-manifest.ts +148 -0
- package/src/ts/webgl/shader-registry.ts +97 -0
- package/src/ts/worker/boot.ts +22 -0
- package/src/ts/worker/dispatch.ts +99 -0
- package/src/ts/worker/font-loader.ts +89 -0
- package/src/ts/worker/renderer.worker.ts +734 -0
- package/src/ts/worker/session-host.ts +118 -0
package/src/ts/index.ts
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
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 CHARTS from "./plugin/charts";
|
|
14
|
+
import { HTMLPerspectiveViewerWebGLPluginElement } from "./plugin/plugin";
|
|
15
|
+
|
|
16
|
+
export type { PerspectiveClickDetail } from "./event-detail";
|
|
17
|
+
export { PerspectiveSelectDetail } from "./event-detail";
|
|
18
|
+
|
|
19
|
+
export function register(...plugin_names: string[]) {
|
|
20
|
+
const plugins = new Set(
|
|
21
|
+
plugin_names.length > 0
|
|
22
|
+
? plugin_names
|
|
23
|
+
: CHARTS.map((chart) => chart.name),
|
|
24
|
+
);
|
|
25
|
+
|
|
26
|
+
CHARTS.forEach((chart) => {
|
|
27
|
+
if (plugins.has(chart.name)) {
|
|
28
|
+
const tagName = `perspective-viewer-charts-${chart.tag}`;
|
|
29
|
+
|
|
30
|
+
// Each registered tag is a thin subclass that pins
|
|
31
|
+
// `_chartType` so `draw()` / `save()` / etc. know which
|
|
32
|
+
// `ChartTypeConfig` they're driving. The chart impl
|
|
33
|
+
// class itself lives in the worker bundle — only
|
|
34
|
+
// `ChartTypeConfig.tag` crosses the host/renderer
|
|
35
|
+
// boundary, and the renderer constructs the impl from
|
|
36
|
+
// its own `CHART_IMPLS` registry.
|
|
37
|
+
customElements.define(
|
|
38
|
+
tagName,
|
|
39
|
+
class extends HTMLPerspectiveViewerWebGLPluginElement {
|
|
40
|
+
_chartType = chart;
|
|
41
|
+
static _chartType = chart;
|
|
42
|
+
},
|
|
43
|
+
);
|
|
44
|
+
|
|
45
|
+
customElements.whenDefined("perspective-viewer").then(async () => {
|
|
46
|
+
const Viewer = customElements.get("perspective-viewer") as any;
|
|
47
|
+
await Viewer.registerPlugin(tagName);
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
register();
|
|
@@ -0,0 +1,106 @@
|
|
|
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 { SpatialGrid } from "./spatial-grid";
|
|
14
|
+
|
|
15
|
+
export interface HitBounds {
|
|
16
|
+
xMin: number;
|
|
17
|
+
xMax: number;
|
|
18
|
+
yMin: number;
|
|
19
|
+
yMax: number;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Wraps SpatialGrid with dirty-flag bookkeeping + the cell-size heuristic
|
|
24
|
+
* (~√n cells), so chart code no longer repeats the insertion loop scaffold.
|
|
25
|
+
* Callers drive the iteration through `rebuild` and the `visit` callback,
|
|
26
|
+
* which lets scatter (flat array) and line (series-indexed array) share
|
|
27
|
+
* the same code path.
|
|
28
|
+
*/
|
|
29
|
+
export class SpatialHitTester {
|
|
30
|
+
private _grid: SpatialGrid | null = null;
|
|
31
|
+
private _dirty = true;
|
|
32
|
+
|
|
33
|
+
markDirty(): void {
|
|
34
|
+
this._dirty = true;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
get isDirty(): boolean {
|
|
38
|
+
return this._dirty;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Rebuild the grid. `forEachPoint` is called once with an insert
|
|
43
|
+
* function; the caller drives iteration. Cleared when `pointCount`
|
|
44
|
+
* is zero.
|
|
45
|
+
*/
|
|
46
|
+
rebuild(
|
|
47
|
+
bounds: HitBounds,
|
|
48
|
+
pointCount: number,
|
|
49
|
+
forEachPoint: (
|
|
50
|
+
insert: (idx: number, x: number, y: number) => void,
|
|
51
|
+
) => void,
|
|
52
|
+
): void {
|
|
53
|
+
if (pointCount === 0) {
|
|
54
|
+
this._grid = null;
|
|
55
|
+
this._dirty = false;
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const xRange = bounds.xMax - bounds.xMin || 1;
|
|
60
|
+
const yRange = bounds.yMax - bounds.yMin || 1;
|
|
61
|
+
const avgRange = (xRange + yRange) / 2;
|
|
62
|
+
const cellSize = avgRange / Math.max(1, Math.sqrt(pointCount));
|
|
63
|
+
const grid = new SpatialGrid(
|
|
64
|
+
bounds.xMin,
|
|
65
|
+
bounds.xMax,
|
|
66
|
+
bounds.yMin,
|
|
67
|
+
bounds.yMax,
|
|
68
|
+
cellSize,
|
|
69
|
+
);
|
|
70
|
+
forEachPoint((idx, x, y) => grid.insert(idx, x, y));
|
|
71
|
+
this._grid = grid;
|
|
72
|
+
this._dirty = false;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Query the nearest point within `radiusPx` of (dataX, dataY).
|
|
77
|
+
*/
|
|
78
|
+
query(
|
|
79
|
+
dataX: number,
|
|
80
|
+
dataY: number,
|
|
81
|
+
radiusPx: number,
|
|
82
|
+
pxPerDataX: number,
|
|
83
|
+
pxPerDataY: number,
|
|
84
|
+
xData: Float32Array | null,
|
|
85
|
+
yData: Float32Array | null,
|
|
86
|
+
): number {
|
|
87
|
+
if (!this._grid || !xData || !yData) {
|
|
88
|
+
return -1;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
return this._grid.query(
|
|
92
|
+
dataX,
|
|
93
|
+
dataY,
|
|
94
|
+
radiusPx,
|
|
95
|
+
pxPerDataX,
|
|
96
|
+
pxPerDataY,
|
|
97
|
+
xData,
|
|
98
|
+
yData,
|
|
99
|
+
);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
clear(): void {
|
|
103
|
+
this._grid = null;
|
|
104
|
+
this._dirty = true;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
@@ -0,0 +1,85 @@
|
|
|
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 { CssBounds, HostSink } from "./tooltip-controller";
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Host-side `HostSink` that materializes pinned tooltips as a `<div>`
|
|
17
|
+
* next to the GL canvas, and applies cursor changes to the canvas's
|
|
18
|
+
* own `style.cursor`. Host-only — depends on `document` /
|
|
19
|
+
* `getComputedStyle`.
|
|
20
|
+
*/
|
|
21
|
+
export class DomHostSink implements HostSink {
|
|
22
|
+
private _glCanvas: HTMLCanvasElement;
|
|
23
|
+
private _parent: HTMLElement;
|
|
24
|
+
private _div: HTMLDivElement | null = null;
|
|
25
|
+
|
|
26
|
+
constructor(glCanvas: HTMLCanvasElement, parent: HTMLElement) {
|
|
27
|
+
this._glCanvas = glCanvas;
|
|
28
|
+
this._parent = parent;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
pin(
|
|
32
|
+
lines: string[],
|
|
33
|
+
pos: { px: number; py: number },
|
|
34
|
+
bounds: CssBounds,
|
|
35
|
+
): void {
|
|
36
|
+
this.dismiss();
|
|
37
|
+
const div = document.createElement("div");
|
|
38
|
+
|
|
39
|
+
div.className = "webgl-tooltip";
|
|
40
|
+
div.style.maxHeight = `${Math.round(bounds.cssHeight * 0.6)}px`;
|
|
41
|
+
div.textContent = lines.join("\n");
|
|
42
|
+
|
|
43
|
+
if (getComputedStyle(this._parent).position === "static") {
|
|
44
|
+
this._parent.style.position = "relative";
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
div.style.left = "-9999px";
|
|
48
|
+
div.style.top = "0px";
|
|
49
|
+
this._parent.appendChild(div);
|
|
50
|
+
this._div = div;
|
|
51
|
+
const divW = div.getBoundingClientRect().width;
|
|
52
|
+
const divH = div.getBoundingClientRect().height;
|
|
53
|
+
let tx = pos.px + 12;
|
|
54
|
+
let ty = pos.py - divH - 8;
|
|
55
|
+
if (tx + divW > bounds.cssWidth) {
|
|
56
|
+
tx = pos.px - divW - 12;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
if (tx < 0) {
|
|
60
|
+
tx = 4;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
if (ty < 0) {
|
|
64
|
+
ty = pos.py + 12;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
if (ty + divH > bounds.cssHeight) {
|
|
68
|
+
ty = bounds.cssHeight - divH - 4;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
div.style.left = `${tx}px`;
|
|
72
|
+
div.style.top = `${ty}px`;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
dismiss(): void {
|
|
76
|
+
if (this._div) {
|
|
77
|
+
this._div.remove();
|
|
78
|
+
this._div = null;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
setCursor(cursor: string): void {
|
|
83
|
+
this._glCanvas.style.cursor = cursor;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
@@ -0,0 +1,75 @@
|
|
|
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 {
|
|
14
|
+
CssBounds,
|
|
15
|
+
HostSink,
|
|
16
|
+
UserClickPayload,
|
|
17
|
+
UserSelectPayload,
|
|
18
|
+
} from "./tooltip-controller";
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Envelope shape sent by `MessageHostSink`. The transport translates
|
|
22
|
+
* each one into a corresponding `WorkerMsg` (`pinTooltip` /
|
|
23
|
+
* `dismissTooltip` / `setCursor` / `userClick` / `userSelect`).
|
|
24
|
+
*/
|
|
25
|
+
export type HostSinkEnvelope =
|
|
26
|
+
| {
|
|
27
|
+
kind: "pin";
|
|
28
|
+
payload: {
|
|
29
|
+
lines: string[];
|
|
30
|
+
pos: { px: number; py: number };
|
|
31
|
+
bounds: CssBounds;
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
| { kind: "dismiss" }
|
|
35
|
+
| { kind: "setCursor"; cursor: string }
|
|
36
|
+
| { kind: "userClick"; payload: UserClickPayload }
|
|
37
|
+
| { kind: "userSelect"; payload: UserSelectPayload };
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* `HostSink` that posts pin / dismiss / setCursor / user-event intents
|
|
41
|
+
* over a `postMessage`-style channel. The host-side transport listens
|
|
42
|
+
* for these envelopes and drives a `DomHostSink` for pin/dismiss and
|
|
43
|
+
* dispatches `CustomEvent`s on the viewer for user events.
|
|
44
|
+
*/
|
|
45
|
+
export class MessageHostSink implements HostSink {
|
|
46
|
+
private _send: (msg: HostSinkEnvelope) => void;
|
|
47
|
+
|
|
48
|
+
constructor(send: (msg: HostSinkEnvelope) => void) {
|
|
49
|
+
this._send = send;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
pin(
|
|
53
|
+
lines: string[],
|
|
54
|
+
pos: { px: number; py: number },
|
|
55
|
+
bounds: CssBounds,
|
|
56
|
+
): void {
|
|
57
|
+
this._send({ kind: "pin", payload: { lines, pos, bounds } });
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
dismiss(): void {
|
|
61
|
+
this._send({ kind: "dismiss" });
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
setCursor(cursor: string): void {
|
|
65
|
+
this._send({ kind: "setCursor", cursor });
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
emitUserClick(payload: UserClickPayload): void {
|
|
69
|
+
this._send({ kind: "userClick", payload });
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
emitUserSelect(payload: UserSelectPayload): void {
|
|
73
|
+
this._send({ kind: "userSelect", payload });
|
|
74
|
+
}
|
|
75
|
+
}
|
|
@@ -0,0 +1,102 @@
|
|
|
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
|
+
* Serial-checked async tooltip cache.
|
|
15
|
+
*
|
|
16
|
+
* Every chart family that paints tooltip text from a lazy row fetch
|
|
17
|
+
* needs the same dance: each hover (or pin) bumps a serial; the async
|
|
18
|
+
* line-build resolves later; if the user has moved on by then the
|
|
19
|
+
* resolved lines must be discarded so we don't paint stale text.
|
|
20
|
+
*
|
|
21
|
+
* Hit-test logic stays per-chart — that genuinely diverges (spatial
|
|
22
|
+
* grid for cartesian, walk-visible-nodes for treemap, angular for
|
|
23
|
+
* sunburst). The serial discipline is what was getting copy-pasted.
|
|
24
|
+
*
|
|
25
|
+
* `Target` is whatever identity the chart uses for hover/pin (flat
|
|
26
|
+
* slot index, node id, etc.); it's stored on `hoveredTarget` so the
|
|
27
|
+
* render path can tell whether cached `lines` belong to the currently
|
|
28
|
+
* hovered entity (some charts paint tooltip text only when both
|
|
29
|
+
* agree).
|
|
30
|
+
*/
|
|
31
|
+
export class LazyTooltip<Target> {
|
|
32
|
+
/**
|
|
33
|
+
* Cached lines for the latest committed hover, or `null`.
|
|
34
|
+
*/
|
|
35
|
+
lines: string[] | null = null;
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Identity of the entity `lines` describe. `null` when cleared.
|
|
39
|
+
*/
|
|
40
|
+
hoveredTarget: Target | null = null;
|
|
41
|
+
|
|
42
|
+
private _hoverSerial = 0;
|
|
43
|
+
private _pinSerial = 0;
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Begin a new hover. Call this only when the hovered entity has
|
|
47
|
+
* actually changed. Clears the cached lines, records the new
|
|
48
|
+
* target, bumps the hover serial, and returns the new value — pass
|
|
49
|
+
* it back to {@link commitHover} from your async resolver to gate
|
|
50
|
+
* the write.
|
|
51
|
+
*/
|
|
52
|
+
beginHover(target: Target): number {
|
|
53
|
+
this.lines = null;
|
|
54
|
+
this.hoveredTarget = target;
|
|
55
|
+
return ++this._hoverSerial;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Commit a freshly-resolved line list for `serial`. Returns true
|
|
60
|
+
* when the write happened (caller should repaint), false when the
|
|
61
|
+
* serial was stale.
|
|
62
|
+
*/
|
|
63
|
+
commitHover(serial: number, lines: string[]): boolean {
|
|
64
|
+
if (serial !== this._hoverSerial) {
|
|
65
|
+
return false;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
this.lines = lines;
|
|
69
|
+
return true;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Clear hover state (mouse left, view changed, etc.).
|
|
74
|
+
*/
|
|
75
|
+
clearHover(): void {
|
|
76
|
+
this.lines = null;
|
|
77
|
+
this.hoveredTarget = null;
|
|
78
|
+
this._hoverSerial++;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Begin a pin operation. Returns the serial; pass it to
|
|
83
|
+
* {@link isPinFresh} from your async resolver.
|
|
84
|
+
*/
|
|
85
|
+
beginPin(): number {
|
|
86
|
+
return ++this._pinSerial;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* True when `serial` still names the latest pin attempt.
|
|
91
|
+
*/
|
|
92
|
+
isPinFresh(serial: number): boolean {
|
|
93
|
+
return serial === this._pinSerial;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Bump the pin serial without starting a new pin (e.g. dismiss).
|
|
98
|
+
*/
|
|
99
|
+
invalidatePin(): void {
|
|
100
|
+
this._pinSerial++;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
@@ -0,0 +1,175 @@
|
|
|
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 { InteractionEvent } from "../transport/protocol";
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Worker-mode counterpart to {@link ZoomRouter}. Captures wheel /
|
|
17
|
+
* pointer events on the GL canvas, normalizes coords to canvas-relative
|
|
18
|
+
* CSS pixels, and emits semantic {@link InteractionEvent}s to the
|
|
19
|
+
* Renderer over its transport. The Renderer (running in a Web Worker)
|
|
20
|
+
* owns the `ZoomController`s and runs the actual hit-test + apply
|
|
21
|
+
* logic — see `applyWheel` / `applyPan` in `zoom-router.ts`.
|
|
22
|
+
*
|
|
23
|
+
* Pointer capture is set on `pointerdown` and released on `pointerup`
|
|
24
|
+
* so drags continue to deliver `pointermove` events when the cursor
|
|
25
|
+
* leaves the canvas, matching the in-process `ZoomRouter` behavior.
|
|
26
|
+
*/
|
|
27
|
+
export class RawEventForwarder {
|
|
28
|
+
private _element: HTMLElement | null = null;
|
|
29
|
+
private _emit: ((e: InteractionEvent) => void) | null = null;
|
|
30
|
+
private _pointerId: number | null = null;
|
|
31
|
+
|
|
32
|
+
private _onWheel: ((e: WheelEvent) => void) | null = null;
|
|
33
|
+
private _onPointerDown: ((e: PointerEvent) => void) | null = null;
|
|
34
|
+
private _onPointerMove: ((e: PointerEvent) => void) | null = null;
|
|
35
|
+
private _onPointerUp: ((e: PointerEvent) => void) | null = null;
|
|
36
|
+
private _onPointerLeave: (() => void) | null = null;
|
|
37
|
+
private _onClick: ((e: MouseEvent) => void) | null = null;
|
|
38
|
+
private _onDblClick: ((e: MouseEvent) => void) | null = null;
|
|
39
|
+
|
|
40
|
+
attach(element: HTMLElement, emit: (e: InteractionEvent) => void): void {
|
|
41
|
+
this.detach();
|
|
42
|
+
this._element = element;
|
|
43
|
+
this._emit = emit;
|
|
44
|
+
|
|
45
|
+
this._onWheel = (e: WheelEvent) => {
|
|
46
|
+
const rect = element.getBoundingClientRect();
|
|
47
|
+
const mx = e.clientX - rect.left;
|
|
48
|
+
const my = e.clientY - rect.top;
|
|
49
|
+
|
|
50
|
+
// Match `ZoomRouter`: only consume the wheel event if the
|
|
51
|
+
// cursor is over the canvas. `preventDefault` is fired
|
|
52
|
+
// unconditionally so the page does not scroll while the
|
|
53
|
+
// chart is hovered — the worker may still no-op if the
|
|
54
|
+
// cursor is outside any facet's plot rect.
|
|
55
|
+
e.preventDefault();
|
|
56
|
+
emit({ type: "wheel", mx, my, deltaY: e.deltaY });
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
this._onPointerDown = (e: PointerEvent) => {
|
|
60
|
+
const rect = element.getBoundingClientRect();
|
|
61
|
+
const mx = e.clientX - rect.left;
|
|
62
|
+
const my = e.clientY - rect.top;
|
|
63
|
+
element.setPointerCapture(e.pointerId);
|
|
64
|
+
this._pointerId = e.pointerId;
|
|
65
|
+
emit({ type: "pointerdown", mx, my, pointerId: e.pointerId });
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
this._onPointerMove = (e: PointerEvent) => {
|
|
69
|
+
const rect = element.getBoundingClientRect();
|
|
70
|
+
const mx = e.clientX - rect.left;
|
|
71
|
+
const my = e.clientY - rect.top;
|
|
72
|
+
emit({ type: "pointermove", mx, my });
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
this._onPointerUp = (e: PointerEvent) => {
|
|
76
|
+
if (this._pointerId !== null) {
|
|
77
|
+
try {
|
|
78
|
+
element.releasePointerCapture(this._pointerId);
|
|
79
|
+
} catch {
|
|
80
|
+
// capture may have already been released
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
this._pointerId = null;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
void e;
|
|
87
|
+
emit({ type: "pointerup" });
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
// Pointerleave gives the renderer a single signal that the
|
|
91
|
+
// cursor truly exited the canvas — used for tooltip dismissal.
|
|
92
|
+
// Hover + drag both ride the `pointermove` stream above, so no
|
|
93
|
+
// parallel `mousemove` channel exists.
|
|
94
|
+
this._onPointerLeave = () => {
|
|
95
|
+
emit({ type: "pointerleave" });
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
this._onClick = (e: MouseEvent) => {
|
|
99
|
+
const rect = element.getBoundingClientRect();
|
|
100
|
+
emit({
|
|
101
|
+
type: "click",
|
|
102
|
+
mx: e.clientX - rect.left,
|
|
103
|
+
my: e.clientY - rect.top,
|
|
104
|
+
});
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
this._onDblClick = (e: MouseEvent) => {
|
|
108
|
+
const rect = element.getBoundingClientRect();
|
|
109
|
+
emit({
|
|
110
|
+
type: "dblclick",
|
|
111
|
+
mx: e.clientX - rect.left,
|
|
112
|
+
my: e.clientY - rect.top,
|
|
113
|
+
});
|
|
114
|
+
};
|
|
115
|
+
|
|
116
|
+
element.addEventListener("wheel", this._onWheel, { passive: false });
|
|
117
|
+
element.addEventListener("pointerdown", this._onPointerDown);
|
|
118
|
+
element.addEventListener("pointermove", this._onPointerMove);
|
|
119
|
+
element.addEventListener("pointerup", this._onPointerUp);
|
|
120
|
+
element.addEventListener("pointerleave", this._onPointerLeave);
|
|
121
|
+
element.addEventListener("click", this._onClick);
|
|
122
|
+
element.addEventListener("dblclick", this._onDblClick);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
detach(): void {
|
|
126
|
+
if (this._element) {
|
|
127
|
+
if (this._onWheel) {
|
|
128
|
+
this._element.removeEventListener("wheel", this._onWheel);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
if (this._onPointerDown) {
|
|
132
|
+
this._element.removeEventListener(
|
|
133
|
+
"pointerdown",
|
|
134
|
+
this._onPointerDown,
|
|
135
|
+
);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
if (this._onPointerMove) {
|
|
139
|
+
this._element.removeEventListener(
|
|
140
|
+
"pointermove",
|
|
141
|
+
this._onPointerMove,
|
|
142
|
+
);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
if (this._onPointerUp) {
|
|
146
|
+
this._element.removeEventListener(
|
|
147
|
+
"pointerup",
|
|
148
|
+
this._onPointerUp,
|
|
149
|
+
);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
if (this._onPointerLeave) {
|
|
153
|
+
this._element.removeEventListener(
|
|
154
|
+
"pointerleave",
|
|
155
|
+
this._onPointerLeave,
|
|
156
|
+
);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
if (this._onClick) {
|
|
160
|
+
this._element.removeEventListener("click", this._onClick);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
if (this._onDblClick) {
|
|
164
|
+
this._element.removeEventListener("dblclick", this._onDblClick);
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
this._element = null;
|
|
169
|
+
this._emit = null;
|
|
170
|
+
this._pointerId = null;
|
|
171
|
+
this._onPointerLeave = null;
|
|
172
|
+
this._onClick = null;
|
|
173
|
+
this._onDblClick = null;
|
|
174
|
+
}
|
|
175
|
+
}
|