@perspective-dev/viewer-charts 5.3.0 → 5.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cdn/perspective-viewer-charts.js +1 -2
- package/dist/cdn/perspective-viewer-charts.js.map +4 -4
- package/dist/esm/axis/legend.d.ts +53 -5
- package/dist/esm/axis/map-ticks.d.ts +15 -0
- package/dist/esm/charts/candlestick/candlestick-interact.d.ts +2 -6
- package/dist/esm/charts/cartesian/cartesian.d.ts +6 -0
- package/dist/esm/charts/cartesian/glyph.d.ts +2 -1
- package/dist/esm/charts/cartesian/glyphs/density.d.ts +2 -1
- package/dist/esm/charts/cartesian/glyphs/lines.d.ts +2 -1
- package/dist/esm/charts/cartesian/glyphs/points.d.ts +2 -1
- package/dist/esm/charts/cartesian/tooltip-lines.d.ts +1 -1
- package/dist/esm/charts/chart-base.d.ts +2 -0
- package/dist/esm/charts/chart.d.ts +77 -6
- package/dist/esm/charts/common/draw-tooltip-box.d.ts +2 -1
- package/dist/esm/charts/common/tree-interact.d.ts +1 -1
- package/dist/esm/charts/series/series-build.d.ts +7 -0
- package/dist/esm/charts/series/series-interact.d.ts +1 -1
- package/dist/esm/charts/series/series.d.ts +6 -0
- package/dist/esm/index.d.ts +2 -0
- package/dist/esm/interaction/axis-indicators.d.ts +11 -0
- package/dist/esm/interaction/host-sink-dom.d.ts +3 -2
- package/dist/esm/interaction/host-sink-message.d.ts +3 -2
- package/dist/esm/interaction/lazy-tooltip.d.ts +2 -2
- package/dist/esm/interaction/legend-controller.d.ts +246 -0
- package/dist/esm/interaction/tooltip-controller.d.ts +20 -95
- package/dist/esm/interaction/tooltip-grid.d.ts +33 -0
- package/dist/esm/layout/facet-grid.d.ts +6 -0
- package/dist/esm/layout/ticks.d.ts +1 -0
- package/dist/esm/map/tile-source.d.ts +77 -8
- package/dist/esm/perspective-viewer-charts.js +1 -2
- package/dist/esm/perspective-viewer-charts.js.map +4 -4
- package/dist/esm/plugin/charts.d.ts +2 -0
- package/dist/esm/plugin/format-defaults.d.ts +9 -0
- package/dist/esm/plugin/plugin.d.ts +20 -0
- package/dist/esm/theme/gradient.d.ts +1 -0
- package/dist/esm/transport/protocol.d.ts +45 -3
- package/dist/esm/transport/renderer-transport.d.ts +3 -0
- package/dist/esm/worker/renderer.worker.d.ts +19 -1
- package/package.json +6 -6
- package/src/css/perspective-viewer-charts.css +0 -1
- package/src/ts/axis/bar-axis.ts +2 -7
- package/src/ts/axis/legend.ts +351 -24
- package/src/ts/axis/map-ticks.ts +94 -0
- package/src/ts/axis/numeric-axis.ts +2 -9
- package/src/ts/charts/candlestick/candlestick-interact.ts +46 -40
- package/src/ts/charts/candlestick/candlestick-render.ts +73 -10
- package/src/ts/charts/cartesian/cartesian-interact.ts +9 -3
- package/src/ts/charts/cartesian/cartesian-render.ts +369 -75
- package/src/ts/charts/cartesian/cartesian.ts +9 -0
- package/src/ts/charts/cartesian/glyph.ts +6 -2
- package/src/ts/charts/cartesian/glyphs/density.ts +2 -2
- package/src/ts/charts/cartesian/glyphs/lines.ts +15 -11
- package/src/ts/charts/cartesian/glyphs/points.ts +9 -5
- package/src/ts/charts/cartesian/tooltip-lines.ts +8 -8
- package/src/ts/charts/chart-base.ts +34 -32
- package/src/ts/charts/chart.ts +106 -7
- package/src/ts/charts/common/draw-tooltip-box.ts +13 -30
- package/src/ts/charts/common/tree-chrome.ts +102 -23
- package/src/ts/charts/common/tree-interact.ts +18 -17
- package/src/ts/charts/heatmap/heatmap-interact.ts +65 -7
- package/src/ts/charts/heatmap/heatmap-render.ts +100 -24
- package/src/ts/charts/map/map.ts +3 -5
- package/src/ts/charts/series/series-build.ts +14 -2
- package/src/ts/charts/series/series-interact.ts +12 -11
- package/src/ts/charts/series/series-render.ts +305 -132
- package/src/ts/charts/series/series.ts +10 -0
- package/src/ts/charts/sunburst/sunburst-render.ts +7 -1
- package/src/ts/charts/treemap/treemap-render.ts +7 -1
- package/src/ts/data/view-reader.ts +1 -4
- package/src/ts/index.ts +2 -0
- package/src/ts/interaction/axis-indicators.ts +137 -0
- package/src/ts/interaction/host-sink-dom.ts +66 -2
- package/src/ts/interaction/host-sink-message.ts +8 -2
- package/src/ts/interaction/lazy-tooltip.ts +5 -5
- package/src/ts/interaction/legend-controller.ts +1084 -0
- package/src/ts/interaction/tooltip-controller.ts +43 -131
- package/src/ts/interaction/tooltip-grid.ts +187 -0
- package/src/ts/layout/facet-grid.ts +8 -1
- package/src/ts/layout/ticks.ts +12 -0
- package/src/ts/map/tile-loader.ts +16 -1
- package/src/ts/map/tile-source.ts +199 -53
- package/src/ts/map/tile-sources.json +18 -0
- package/src/ts/plugin/charts.ts +40 -6
- package/src/ts/plugin/format-defaults.ts +37 -0
- package/src/ts/plugin/plugin.ts +236 -67
- package/src/ts/shaders/line.vert.glsl +5 -2
- package/src/ts/shaders/scatter.vert.glsl +0 -7
- package/src/ts/theme/gradient.ts +24 -0
- package/src/ts/transport/protocol.ts +46 -2
- package/src/ts/transport/renderer-transport.ts +83 -4
- package/src/ts/worker/dispatch.ts +6 -2
- package/src/ts/worker/renderer.worker.ts +78 -2
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
import { type PluginConfig } from "../charts/chart";
|
|
2
|
+
import type { PlotRect } from "../layout/plot-layout";
|
|
3
|
+
import type { InteractionEvent } from "../transport/protocol";
|
|
4
|
+
/** Minimum legend width for any mode (drag clamp + config clamp). */
|
|
5
|
+
export declare const LEGEND_MIN_WIDTH = 48;
|
|
6
|
+
/** Maximum configurable legend width (further clamped to canvas/2). */
|
|
7
|
+
export declare const LEGEND_MAX_WIDTH = 512;
|
|
8
|
+
/** Minimum floating-panel height. */
|
|
9
|
+
export declare const LEGEND_MIN_HEIGHT = 48;
|
|
10
|
+
/** Floating-panel header strip height (title + move grip). */
|
|
11
|
+
export declare const LEGEND_HEADER_H = 18;
|
|
12
|
+
/** Entry row height shared by every swatch-list legend painter. */
|
|
13
|
+
export declare const LEGEND_LINE_HEIGHT = 18;
|
|
14
|
+
/**
|
|
15
|
+
* Floating-frame chrome, as consumed from the panel box by
|
|
16
|
+
* `paintFloatingLegendFrame`: `PAD_L` insets the content from the left
|
|
17
|
+
* edge, `W` is the total width the frame takes, and `H` the total
|
|
18
|
+
* height it takes BELOW the header (split evenly above/below). The
|
|
19
|
+
* auto-size math is the frame's inverse, so both must read the same
|
|
20
|
+
* constants — a drifted pair sizes a panel that clips its own last row.
|
|
21
|
+
*/
|
|
22
|
+
export declare const LEGEND_FRAME_PAD_L = 8;
|
|
23
|
+
export declare const LEGEND_FRAME_W = 12;
|
|
24
|
+
export declare const LEGEND_FRAME_H = 8;
|
|
25
|
+
/** Width the header title is laid out against: `box.width - PAD`. */
|
|
26
|
+
export declare const LEGEND_TITLE_PAD = 16;
|
|
27
|
+
/**
|
|
28
|
+
* Entry-row chrome left of the label text: swatch (10) + gap (6).
|
|
29
|
+
* Gradient legends substitute their own (bar 16 + gap 5).
|
|
30
|
+
*/
|
|
31
|
+
export declare const LEGEND_ENTRY_LEADING = 16;
|
|
32
|
+
/**
|
|
33
|
+
* Sidebar gutter width when the sidebar legend is active: the
|
|
34
|
+
* configured `legend_width_px`, or `legacy` (the chart family's
|
|
35
|
+
* historical gutter constant) when the config is 0 (auto).
|
|
36
|
+
*/
|
|
37
|
+
export declare function legendSidebarWidth(cfg: PluginConfig, legacy: number): number;
|
|
38
|
+
/**
|
|
39
|
+
* `legend_mode` with `"auto"` resolved away — the only form the
|
|
40
|
+
* layout/paint/interaction paths consume. Painters record the resolved
|
|
41
|
+
* mode in `setPainted`, so the controller's drag semantics follow it
|
|
42
|
+
* with no separate resolution.
|
|
43
|
+
*/
|
|
44
|
+
export type ResolvedLegendMode = "sidebar" | "none" | "floating";
|
|
45
|
+
/**
|
|
46
|
+
* `"auto"` resolves to `"floating"` when every legend entry fits the
|
|
47
|
+
* DEFAULT floating panel (160 px ≈ header + 7 rows) without scrolling —
|
|
48
|
+
* a compact overlay that frees the whole gutter — and to `"sidebar"`
|
|
49
|
+
* otherwise, where a long list gets dedicated, scrollable space instead
|
|
50
|
+
* of occluding the plot. Continuous gradient legends have no entry
|
|
51
|
+
* list; callers pass `entryCount = 0` and they resolve to floating.
|
|
52
|
+
*/
|
|
53
|
+
export declare const AUTO_FLOATING_MAX_ROWS = 7;
|
|
54
|
+
export declare function resolveLegendMode(cfg: PluginConfig, entryCount: number): ResolvedLegendMode;
|
|
55
|
+
/**
|
|
56
|
+
* Right-margin width a plot layout should reserve for the legend.
|
|
57
|
+
* `legacy` is the family's historical `hasLegend` gutter (80 for
|
|
58
|
+
* single-plot layouts, 96 for facet grids). Modes `"none"` and
|
|
59
|
+
* `"floating"` collapse the gutter to the no-legend breathing margin —
|
|
60
|
+
* the plot widens and the floating panel overlays it. `entryCount`
|
|
61
|
+
* feeds the `"auto"` resolution ({@link resolveLegendMode}); pass the
|
|
62
|
+
* same count the legend painter will enumerate.
|
|
63
|
+
*/
|
|
64
|
+
export declare function legendRightGutter(cfg: PluginConfig, hasLegend: boolean, legacy?: number, entryCount?: number): number;
|
|
65
|
+
/**
|
|
66
|
+
* Tree-chart variant of {@link legendRightGutter}: trees reserve `0`
|
|
67
|
+
* (fully flush cells) rather than a 16px breathing margin when no
|
|
68
|
+
* sidebar legend is present.
|
|
69
|
+
*/
|
|
70
|
+
export declare function legendTreeGutter(cfg: PluginConfig, hasLegend: boolean, legacy: number, entryCount?: number): number;
|
|
71
|
+
/**
|
|
72
|
+
* Content measurements a floating panel needs to size itself in
|
|
73
|
+
* `legend_size_mode: "auto"`. Supplied by the call site (which owns the
|
|
74
|
+
* chrome canvas and the label set) and consumed ONLY by that mode, so a
|
|
75
|
+
* `"fixed"` panel — and every sidebar legend — pays nothing.
|
|
76
|
+
*/
|
|
77
|
+
export interface LegendAutoFit {
|
|
78
|
+
/**
|
|
79
|
+
* Entry rows the painter will enumerate. `0` marks a continuous
|
|
80
|
+
* gradient legend, which has no row list and keeps the default
|
|
81
|
+
* panel height.
|
|
82
|
+
*/
|
|
83
|
+
entryCount: number;
|
|
84
|
+
/**
|
|
85
|
+
* Panel width in CSS px that fits the widest entry label AND the
|
|
86
|
+
* header title, frame chrome included. Invoked at most once per
|
|
87
|
+
* `floatingBox` call; see `legendAutoFit`.
|
|
88
|
+
*/
|
|
89
|
+
boxWidth?: () => number;
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Cursor-zone classification for a point over the painted legend.
|
|
93
|
+
* Sidebar legends expose only `resize-w` (their left edge) plus
|
|
94
|
+
* `scrollbar` / `body`; floating panels add the E/S/SE handles and the
|
|
95
|
+
* `move` grip (header strip — the body also moves, gated by
|
|
96
|
+
* {@link MOVE_THRESHOLD} so entry clicks still land).
|
|
97
|
+
*/
|
|
98
|
+
export type LegendHitZone = "none" | "body" | "move" | "scrollbar" | "resize-w" | "resize-e" | "resize-s" | "resize-se";
|
|
99
|
+
/**
|
|
100
|
+
* Frame snapshot the legend painters hand back after painting: the
|
|
101
|
+
* panel's outer box, the scrollable content rect inside it, and the
|
|
102
|
+
* full (unclipped) content height. Hit-testing and wheel/drag routing
|
|
103
|
+
* all consult the LAST PAINTED geometry — between paints the on-screen
|
|
104
|
+
* pixels are exactly this snapshot, so events resolve against what the
|
|
105
|
+
* user actually sees.
|
|
106
|
+
*/
|
|
107
|
+
export interface PaintedLegend {
|
|
108
|
+
mode: "sidebar" | "floating";
|
|
109
|
+
box: PlotRect;
|
|
110
|
+
content: PlotRect;
|
|
111
|
+
contentHeight: number;
|
|
112
|
+
/**
|
|
113
|
+
* The resolved right-gutter width this frame (sidebar mode only).
|
|
114
|
+
* The width-drag applies its delta to this — NOT to `box.width`,
|
|
115
|
+
* which excludes the gutter's padding — so a drag that starts on
|
|
116
|
+
* an auto-width (config 0) legend continues from the family's
|
|
117
|
+
* legacy width without a jump.
|
|
118
|
+
*/
|
|
119
|
+
sidebarGutter?: number;
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Per-event services the worker renderer supplies. The controller is
|
|
123
|
+
* pure state — it never reaches into the chart or transport itself.
|
|
124
|
+
*/
|
|
125
|
+
export interface LegendEventCtx {
|
|
126
|
+
/**
|
|
127
|
+
* The chart's live plugin config. Drag gestures mutate the legend
|
|
128
|
+
* fields on this object directly (the worker-local copy), so the
|
|
129
|
+
* next paint lays out from the in-progress values; the persisted
|
|
130
|
+
* delta is posted once at pointerup.
|
|
131
|
+
*/
|
|
132
|
+
cfg: PluginConfig;
|
|
133
|
+
cssWidth: number;
|
|
134
|
+
cssHeight: number;
|
|
135
|
+
/**
|
|
136
|
+
* Visible toggleable entry rects (series legends). A floating-panel
|
|
137
|
+
* click that lands on one passes through to the chart's own legend
|
|
138
|
+
* click handler instead of being consumed.
|
|
139
|
+
*/
|
|
140
|
+
legendRects: ReadonlyArray<{
|
|
141
|
+
rect: PlotRect;
|
|
142
|
+
}>;
|
|
143
|
+
/**
|
|
144
|
+
* Schedule a repaint. `relayout: true` when the gesture changed the
|
|
145
|
+
* plot geometry (sidebar width) and a full GL pass is required;
|
|
146
|
+
* `false` for chrome-only changes (scroll, floating move/resize).
|
|
147
|
+
*/
|
|
148
|
+
repaint(relayout: boolean): void;
|
|
149
|
+
/** Post a persisted config delta (pointerup, changed fields only). */
|
|
150
|
+
postDelta(fields: Partial<PluginConfig>): void;
|
|
151
|
+
setCursor(cursor: string): void;
|
|
152
|
+
/** Dismiss any active plot hover (cursor entered the legend). */
|
|
153
|
+
dispatchLeave(): void;
|
|
154
|
+
}
|
|
155
|
+
/**
|
|
156
|
+
* Single owner of legend interaction state: last-painted geometry,
|
|
157
|
+
* scroll offset, hover cursor, and the drag state machine. One
|
|
158
|
+
* instance per chart (`AbstractChart._legend`); the worker renderer
|
|
159
|
+
* consults {@link handleEvent} FIRST for every forwarded interaction
|
|
160
|
+
* event, so legend gestures structurally preempt plot zoom / pan /
|
|
161
|
+
* tooltip routing rather than racing them.
|
|
162
|
+
*
|
|
163
|
+
* The scroll offset is transient (never persisted); geometry fields
|
|
164
|
+
* (`legend_size_mode`, `legend_width_px`, `legend_height_px`,
|
|
165
|
+
* `legend_x`, `legend_y`) round-trip through `plugin_config`.
|
|
166
|
+
*/
|
|
167
|
+
export declare class LegendController {
|
|
168
|
+
private _painted;
|
|
169
|
+
private _scroll;
|
|
170
|
+
private _drag;
|
|
171
|
+
private _suppressClick;
|
|
172
|
+
private _hoverInside;
|
|
173
|
+
private _lastCursor;
|
|
174
|
+
/**
|
|
175
|
+
* Clamp and return the scroll offset for this frame. Painters call
|
|
176
|
+
* this before drawing the entry window so the offset is always
|
|
177
|
+
* valid against the CURRENT content/viewport pair (content can
|
|
178
|
+
* shrink between frames — data updates, panel resizes).
|
|
179
|
+
*/
|
|
180
|
+
clampScroll(viewHeight: number, contentHeight: number): number;
|
|
181
|
+
/** Record this frame's painted geometry. */
|
|
182
|
+
setPainted(p: PaintedLegend): void;
|
|
183
|
+
/**
|
|
184
|
+
* Record that no legend was painted this frame (mode `"none"`, or
|
|
185
|
+
* no legend content). Kills every hit zone until the next paint.
|
|
186
|
+
*/
|
|
187
|
+
clearPainted(): void;
|
|
188
|
+
/**
|
|
189
|
+
* Resolve the floating panel's outer box from config. Offsets are
|
|
190
|
+
* normalized to the free span and measured from the
|
|
191
|
+
* `legend_anchor` corner, so every `legend_x`/`legend_y` in [0, 1]
|
|
192
|
+
* yields a fully on-canvas box at any canvas size.
|
|
193
|
+
*
|
|
194
|
+
* `fit` drives `legend_size_mode: "auto"` (see {@link LegendAutoFit});
|
|
195
|
+
* omitting it in auto mode falls back to the fixed-mode defaults,
|
|
196
|
+
* so a call site that cannot measure still paints a sane panel. The
|
|
197
|
+
* canvas clamps apply to BOTH modes — an auto panel never grows
|
|
198
|
+
* past half the canvas width or its full height, it scrolls
|
|
199
|
+
* instead.
|
|
200
|
+
*/
|
|
201
|
+
floatingBox(cfg: PluginConfig, cssWidth: number, cssHeight: number, fit?: LegendAutoFit): PlotRect;
|
|
202
|
+
/**
|
|
203
|
+
* Called by `setPluginConfig` BEFORE the incoming config replaces
|
|
204
|
+
* the current one. A restore whose legend fields EQUAL the current
|
|
205
|
+
* values is the persistence echo of a completed drag — a no-op
|
|
206
|
+
* here, so scroll and any in-flight gesture survive. Different
|
|
207
|
+
* values (the user typed in the settings form, or a restore raced
|
|
208
|
+
* in) win unconditionally: any in-flight drag is cancelled.
|
|
209
|
+
*/
|
|
210
|
+
reconcileConfig(prev: PluginConfig, next: PluginConfig): void;
|
|
211
|
+
cancelGesture(): void;
|
|
212
|
+
/**
|
|
213
|
+
* Route one forwarded interaction event. Returns `true` when the
|
|
214
|
+
* event was consumed (the caller must not forward it to zoom /
|
|
215
|
+
* tooltip routing).
|
|
216
|
+
*/
|
|
217
|
+
handleEvent(event: InteractionEvent, ctx: LegendEventCtx): boolean;
|
|
218
|
+
/** Classify a canvas-CSS-px point against the last painted legend. */
|
|
219
|
+
hitTest(mx: number, my: number): LegendHitZone;
|
|
220
|
+
private _scrollable;
|
|
221
|
+
private _inScrollbar;
|
|
222
|
+
private _onWheel;
|
|
223
|
+
private _onPointerDown;
|
|
224
|
+
private _onPointerMove;
|
|
225
|
+
private _onPointerUp;
|
|
226
|
+
private _onDblClick;
|
|
227
|
+
private _onClick;
|
|
228
|
+
private _applyDrag;
|
|
229
|
+
/**
|
|
230
|
+
* First write of a resize gesture on an auto-sized floating panel:
|
|
231
|
+
* switch it to `"fixed"`. Without this the gesture would be inert —
|
|
232
|
+
* the next paint recomputes the content size and discards it.
|
|
233
|
+
*
|
|
234
|
+
* BOTH dimensions are seeded from the box on screen, not just the
|
|
235
|
+
* dragged one: the saved `legend_width_px` / `legend_height_px` are
|
|
236
|
+
* whatever the panel had before it went auto, so adopting them
|
|
237
|
+
* wholesale would jump the edge the user is NOT dragging.
|
|
238
|
+
*/
|
|
239
|
+
private _freezeAutoSize;
|
|
240
|
+
/** Bottom-edge resize: new height with the top edge held fixed. */
|
|
241
|
+
private _applySouthResize;
|
|
242
|
+
private _writeAnchoredPos;
|
|
243
|
+
private _setScroll;
|
|
244
|
+
private static _cursorFor;
|
|
245
|
+
private _setCursor;
|
|
246
|
+
}
|
|
@@ -1,9 +1,7 @@
|
|
|
1
1
|
import type { Canvas2D } from "../charts/canvas-types";
|
|
2
2
|
import type { PlotLayout } from "../layout/plot-layout";
|
|
3
3
|
import type { Theme } from "../theme/theme";
|
|
4
|
-
|
|
5
|
-
* Minimal positioning input — PlotLayout satisfies this.
|
|
6
|
-
*/
|
|
4
|
+
import { type TooltipContent, type TooltipStyle } from "./tooltip-grid";
|
|
7
5
|
export interface CssBounds {
|
|
8
6
|
cssWidth: number;
|
|
9
7
|
cssHeight: number;
|
|
@@ -11,7 +9,7 @@ export interface CssBounds {
|
|
|
11
9
|
export interface TooltipCallbacks {
|
|
12
10
|
/**
|
|
13
11
|
* RAF-throttled mouse position in CSS pixels, relative to the GL
|
|
14
|
-
* canvas
|
|
12
|
+
* canvas.
|
|
15
13
|
*/
|
|
16
14
|
onHover(mx: number, my: number): void;
|
|
17
15
|
/**
|
|
@@ -19,8 +17,7 @@ export interface TooltipCallbacks {
|
|
|
19
17
|
*/
|
|
20
18
|
onLeave(): void;
|
|
21
19
|
/**
|
|
22
|
-
* Fires on click with mouse position.
|
|
23
|
-
* (skipping the default pin/dismiss flow — used for legend clicks).
|
|
20
|
+
* Fires on click with mouse position.
|
|
24
21
|
*/
|
|
25
22
|
onClickPre?(mx: number, my: number): boolean;
|
|
26
23
|
/**
|
|
@@ -28,74 +25,33 @@ export interface TooltipCallbacks {
|
|
|
28
25
|
*/
|
|
29
26
|
onPin?(mx: number, my: number): void;
|
|
30
27
|
/**
|
|
31
|
-
* Fires on dblclick (treemap drill-up gesture).
|
|
32
|
-
* that don't bind a handler simply ignore the event.
|
|
28
|
+
* Fires on dblclick (treemap drill-up gesture).
|
|
33
29
|
*/
|
|
34
30
|
onDblClick?(mx: number, my: number): void;
|
|
35
31
|
/**
|
|
36
|
-
* Fires when an active pin is dismissed by a click
|
|
37
|
-
* already-pinned target (the "click again to unpin" gesture in
|
|
38
|
-
* `dispatchClick`). Chart impls hook this to emit a
|
|
39
|
-
* `perspective-global-filter` with `selected: false`. Does *not*
|
|
40
|
-
* fire on the implicit dismiss inside `pin()` that replaces an
|
|
41
|
-
* existing pin — that path is followed by a fresh `onPin` which
|
|
42
|
-
* emits its own `selected: true`.
|
|
32
|
+
* Fires when an active pin is dismissed by a click.
|
|
43
33
|
*/
|
|
44
34
|
onUnpin?(): void;
|
|
45
35
|
}
|
|
46
36
|
export interface RenderTooltipOptions {
|
|
47
|
-
/**
|
|
48
|
-
* Draw a dashed crosshair at `pos`. Used by scatter/line.
|
|
49
|
-
*/
|
|
50
37
|
crosshair?: boolean;
|
|
51
|
-
/**
|
|
52
|
-
* Draw a ring of `radius` CSS pixels at `pos`. Used to highlight a
|
|
53
|
-
* hovered point. Omit for bars (where the bar itself highlights).
|
|
54
|
-
*/
|
|
55
38
|
highlightRadius?: number;
|
|
39
|
+
maxColumnPx?: number;
|
|
40
|
+
opacity?: number;
|
|
56
41
|
}
|
|
57
42
|
/**
|
|
58
|
-
* Side-channel from the chart back to the host's DOM.
|
|
59
|
-
* into a sink rather than touching the DOM itself; the host
|
|
60
|
-
* materializes the actual visual (`<div>` for pinned tooltip, cursor
|
|
61
|
-
* mutation on the GL canvas).
|
|
62
|
-
*
|
|
63
|
-
* - `MessageHostSink` (in worker/) — forwards calls over a
|
|
64
|
-
* `postMessage`-shaped channel back to
|
|
65
|
-
* the host.
|
|
66
|
-
* - `DomHostSink` (in host transport) — receives the matching
|
|
67
|
-
* envelopes and applies them to the DOM.
|
|
68
|
-
*
|
|
69
|
-
* The controller's `_pinned` flag is the source of truth for whether
|
|
70
|
-
* hover updates are gated; the sink only owns the visual artifact.
|
|
43
|
+
* Side-channel from the chart back to the host's DOM.
|
|
71
44
|
*/
|
|
72
45
|
export interface HostSink {
|
|
73
|
-
pin(
|
|
46
|
+
pin(grid: TooltipContent, pos: {
|
|
74
47
|
px: number;
|
|
75
48
|
py: number;
|
|
76
|
-
}, bounds: CssBounds): void;
|
|
49
|
+
}, bounds: CssBounds, style?: TooltipStyle): void;
|
|
77
50
|
dismiss(): void;
|
|
78
51
|
setCursor(cursor: string): void;
|
|
79
|
-
/**
|
|
80
|
-
* Forward a `perspective-click` to the host. Optional — only the
|
|
81
|
-
* worker-bound `MessageHostSink` implements it; `DomHostSink` (the
|
|
82
|
-
* host-side consumer of pin/dismiss) never sees user-event calls,
|
|
83
|
-
* so omits the implementation.
|
|
84
|
-
*/
|
|
85
52
|
emitUserClick?(detail: UserClickPayload): void;
|
|
86
|
-
/**
|
|
87
|
-
* Forward a `perspective-global-filter` to the host with the
|
|
88
|
-
* `selected: true` / `selected: false` semantics. The host owns the
|
|
89
|
-
* `removeConfigs` history (mirrors datagrid's
|
|
90
|
-
* `model._last_insert_configs`); the sink only ships the new state.
|
|
91
|
-
*/
|
|
92
53
|
emitUserSelect?(payload: UserSelectPayload): void;
|
|
93
54
|
}
|
|
94
|
-
/**
|
|
95
|
-
* Plain-object payload for `HostSink.emitUserClick`. Matches
|
|
96
|
-
* `PerspectiveClickDetail` byte-for-byte; defined locally to avoid a
|
|
97
|
-
* cycle through `event-detail.ts`.
|
|
98
|
-
*/
|
|
99
55
|
export interface UserClickPayload {
|
|
100
56
|
row: Record<string, unknown>;
|
|
101
57
|
column_names: string[];
|
|
@@ -103,11 +59,6 @@ export interface UserClickPayload {
|
|
|
103
59
|
filter?: unknown[];
|
|
104
60
|
};
|
|
105
61
|
}
|
|
106
|
-
/**
|
|
107
|
-
* Plain-object payload for `HostSink.emitUserSelect`. The host
|
|
108
|
-
* transport reconstructs a `PerspectiveSelectDetail` class instance
|
|
109
|
-
* from this plus its cached `_lastInsertConfig`.
|
|
110
|
-
*/
|
|
111
62
|
export interface UserSelectPayload {
|
|
112
63
|
selected: boolean;
|
|
113
64
|
row: Record<string, unknown>;
|
|
@@ -118,13 +69,7 @@ export interface UserSelectPayload {
|
|
|
118
69
|
}
|
|
119
70
|
/**
|
|
120
71
|
* Owns the hover/click/dblclick state machine and the pinned-tooltip
|
|
121
|
-
* lifecycle.
|
|
122
|
-
* `dispatchHover` / `dispatchLeave` / `dispatchClick` /
|
|
123
|
-
* `dispatchDblClick` — the host's `RawEventForwarder` captures DOM
|
|
124
|
-
* events on the GL canvas and posts them as `InteractionEvent`s.
|
|
125
|
-
*
|
|
126
|
-
* Pinning + cursor changes go through a {@link HostSink} so the actual
|
|
127
|
-
* DOM mutations happen host-side regardless of where the chart runs.
|
|
72
|
+
* lifecycle.
|
|
128
73
|
*/
|
|
129
74
|
export declare class TooltipController {
|
|
130
75
|
private _callbacks;
|
|
@@ -134,60 +79,40 @@ export declare class TooltipController {
|
|
|
134
79
|
private _pinned;
|
|
135
80
|
get isPinned(): boolean;
|
|
136
81
|
/**
|
|
137
|
-
* Replace the active host sink.
|
|
138
|
-
* prior sink so we never leak a pinned artifact across resets —
|
|
139
|
-
* though in practice each chart instance uses one sink for its
|
|
140
|
-
* lifetime.
|
|
82
|
+
* Replace the active host sink.
|
|
141
83
|
*/
|
|
142
84
|
setHost(sink: HostSink): void;
|
|
143
85
|
/**
|
|
144
|
-
* Forward a cursor change to the host.
|
|
145
|
-
* installed (chart constructed without a transport).
|
|
86
|
+
* Forward a cursor change to the host.
|
|
146
87
|
*/
|
|
147
88
|
setCursor(cursor: string): void;
|
|
148
89
|
/**
|
|
149
|
-
* Install the chart's tooltip callbacks.
|
|
150
|
-
* controller via `dispatchHover` / `dispatchLeave` /
|
|
151
|
-
* `dispatchClick` / `dispatchDblClick`; this controller never
|
|
152
|
-
* touches the DOM directly.
|
|
90
|
+
* Install the chart's tooltip callbacks.
|
|
153
91
|
*/
|
|
154
92
|
attach(callbacks: TooltipCallbacks): void;
|
|
155
93
|
detach(): void;
|
|
156
94
|
/**
|
|
157
95
|
* Schedule an `onHover` callback for the given canvas-relative
|
|
158
|
-
* coords.
|
|
159
|
-
* pointer streams don't backlog the chart's hit-test path.
|
|
160
|
-
*
|
|
161
|
-
* Workers ship with `requestAnimationFrame` (DedicatedWorkerGlobalScope
|
|
162
|
-
* exposes it for OffscreenCanvas painting), so the same coalescer
|
|
163
|
-
* works in both modes. We fall back to setTimeout if RAF is missing
|
|
164
|
-
* (e.g. node tests without a polyfill).
|
|
96
|
+
* coords.
|
|
165
97
|
*/
|
|
166
98
|
dispatchHover(mx: number, my: number): void;
|
|
167
99
|
dispatchLeave(): void;
|
|
168
100
|
dispatchClick(mx: number, my: number): void;
|
|
169
101
|
dispatchDblClick(mx: number, my: number): void;
|
|
170
102
|
/**
|
|
171
|
-
* Pin a tooltip (or replace an active one).
|
|
172
|
-
* configured sink and flips the controller's pinned flag so hover
|
|
173
|
-
* dispatch is suppressed until dismissal.
|
|
103
|
+
* Pin a tooltip (or replace an active one).
|
|
174
104
|
*/
|
|
175
|
-
pin(
|
|
105
|
+
pin(grid: TooltipContent, pos: {
|
|
176
106
|
px: number;
|
|
177
107
|
py: number;
|
|
178
|
-
}, bounds: CssBounds): void;
|
|
108
|
+
}, bounds: CssBounds, style?: TooltipStyle): void;
|
|
179
109
|
dismiss(): void;
|
|
180
110
|
}
|
|
181
111
|
/**
|
|
182
112
|
* Paint a canvas tooltip (crosshair, highlight ring, box + text) onto
|
|
183
|
-
* `canvas`.
|
|
184
|
-
* identity transform on entry and restores prior state on exit, so it
|
|
185
|
-
* composes cleanly with other chrome painters that may have already
|
|
186
|
-
* called `initCanvas` on the same canvas — re-applying `scale(dpr,dpr)`
|
|
187
|
-
* blind would double-scale in that case, misplacing the tooltip
|
|
188
|
-
* proportionally to its distance from the origin.
|
|
113
|
+
* `canvas`.
|
|
189
114
|
*/
|
|
190
115
|
export declare function renderCanvasTooltip(canvas: Canvas2D | null, pos: {
|
|
191
116
|
px: number;
|
|
192
117
|
py: number;
|
|
193
|
-
},
|
|
118
|
+
}, grid: TooltipContent, layout: PlotLayout, theme: Theme, dpr: number, options?: RenderTooltipOptions): void;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import type { Context2D } from "../charts/canvas-types";
|
|
2
|
+
import type { Theme } from "../theme/theme";
|
|
3
|
+
export type TooltipRow = string[];
|
|
4
|
+
export type TooltipContent = TooltipRow[];
|
|
5
|
+
export interface TooltipStyle {
|
|
6
|
+
opacity: number;
|
|
7
|
+
maxColumnPx: number;
|
|
8
|
+
}
|
|
9
|
+
export declare const DEFAULT_TOOLTIP_STYLE: TooltipStyle;
|
|
10
|
+
export declare function tooltipStyleOf(cfg: {
|
|
11
|
+
tooltip_opacity: number;
|
|
12
|
+
tooltip_max_column_px: number;
|
|
13
|
+
}): TooltipStyle;
|
|
14
|
+
export declare const NUMERIC_RE: RegExp;
|
|
15
|
+
export declare const TOOLTIP_ROW_HEIGHT = 16;
|
|
16
|
+
export declare const TOOLTIP_PADDING = 8;
|
|
17
|
+
export declare const TOOLTIP_COL_GAP = 8;
|
|
18
|
+
export interface MeasuredTooltipGrid {
|
|
19
|
+
rows: string[][];
|
|
20
|
+
colWidths: number[];
|
|
21
|
+
colRightAlign: boolean[];
|
|
22
|
+
boxW: number;
|
|
23
|
+
boxH: number;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Measure a tooltip grid under the current `ctx.font`.
|
|
27
|
+
*/
|
|
28
|
+
export declare function measureTooltipGrid(ctx: Context2D, grid: TooltipContent, maxColumnPx: number): MeasuredTooltipGrid;
|
|
29
|
+
/**
|
|
30
|
+
* Paint a measured tooltip grid at `(tx, ty)`.
|
|
31
|
+
*/
|
|
32
|
+
export declare function paintTooltipGrid(ctx: Context2D, measured: MeasuredTooltipGrid, tx: number, ty: number, theme: Theme, opacity: number): void;
|
|
33
|
+
export declare function ellipsize(ctx: Context2D, text: string, maxW: number): string;
|
|
@@ -32,6 +32,12 @@ export interface FacetGridOptions {
|
|
|
32
32
|
* Reserve a right gutter for a single shared legend.
|
|
33
33
|
*/
|
|
34
34
|
hasLegend?: boolean;
|
|
35
|
+
/**
|
|
36
|
+
* Width of the legend gutter when `hasLegend` is set. Defaults to
|
|
37
|
+
* the historical `LEGEND_GUTTER` (96). Callers resolve this from
|
|
38
|
+
* `plugin_config.legend_width_px` via `legendSidebarWidth`.
|
|
39
|
+
*/
|
|
40
|
+
legendWidth?: number;
|
|
35
41
|
/** Axis-label allowance (consumed only when the corresponding axis
|
|
36
42
|
* mode produces a gutter — outer band or per-cell). */
|
|
37
43
|
hasXLabel?: boolean;
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
* @param targetCount - Desired number of ticks (approximate)
|
|
6
6
|
*/
|
|
7
7
|
export declare function computeNiceTicks(min: number, max: number, targetCount: number): number[];
|
|
8
|
+
export declare function stepTickFormatter(isDate: boolean | undefined, ticks: number[] | null | undefined): (v: number) => string;
|
|
8
9
|
/**
|
|
9
10
|
* Format a numeric tick value for display.
|
|
10
11
|
* Uses K/M/B suffixes for large numbers, fixed decimals for small.
|
|
@@ -53,14 +53,83 @@ export declare class TemplatedTileSource implements TileSource {
|
|
|
53
53
|
urlFor(z: number, x: number, y: number): string;
|
|
54
54
|
}
|
|
55
55
|
/**
|
|
56
|
-
*
|
|
57
|
-
*
|
|
58
|
-
*
|
|
56
|
+
* Declarative description of one raster XYZ tile provider — the shape
|
|
57
|
+
* of an entry in [tile-sources.json] and of the argument to
|
|
58
|
+
* `registerTileSource`. Everything the renderer needs to construct a
|
|
59
|
+
* concrete {@link TileSource} lives here; the TypeScript is generic
|
|
60
|
+
* over these entries and hardcodes no provider URLs.
|
|
59
61
|
*/
|
|
60
|
-
export
|
|
62
|
+
export interface TileSourceSpec {
|
|
63
|
+
/** Registry key; the persisted `map_tile_provider` value. */
|
|
64
|
+
readonly id: string;
|
|
65
|
+
/** Human-readable enum-variant label on the settings panel. */
|
|
66
|
+
readonly label: string;
|
|
67
|
+
/**
|
|
68
|
+
* URL template with `{z}`/`{x}`/`{y}` placeholders and optional
|
|
69
|
+
* `{s}` subdomain rotation. A provider that needs an API key
|
|
70
|
+
* embeds it directly in the template it registers — keys never
|
|
71
|
+
* pass through `plugin_config`, so they never appear in `save()`
|
|
72
|
+
* output.
|
|
73
|
+
*/
|
|
74
|
+
readonly template: string;
|
|
75
|
+
/** `{s}` rotation pool; required non-empty iff `template` has `{s}`. */
|
|
76
|
+
readonly subdomains: readonly string[];
|
|
77
|
+
/** See {@link TileSource.attribution}. */
|
|
78
|
+
readonly attribution: string;
|
|
79
|
+
/** See {@link TileSource.tileSize}. Default 256. */
|
|
80
|
+
readonly tile_size: number;
|
|
81
|
+
/** See {@link TileSource.maxZoom}. Default 19. */
|
|
82
|
+
readonly max_zoom: number;
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Validate + normalize an untrusted spec (a JSON entry or a
|
|
86
|
+
* `registerTileSource` argument). Throws `TypeError` naming the
|
|
87
|
+
* offending field; returns a frozen spec with defaults applied.
|
|
88
|
+
*/
|
|
89
|
+
export declare function parseTileSourceSpec(input: unknown): TileSourceSpec;
|
|
90
|
+
/**
|
|
91
|
+
* The set of known tile providers: the bundled entries from
|
|
92
|
+
* [tile-sources.json] plus any registered at runtime via
|
|
93
|
+
* `registerTileSource`. One instance exists per JS realm ({@link
|
|
94
|
+
* TILE_SOURCES}) — the plugin realm's is the source of truth for the
|
|
95
|
+
* settings-panel enum; the worker realm's resolves ids at render time.
|
|
96
|
+
* The realms need no eager mirroring: every `setPluginConfig` / `init`
|
|
97
|
+
* control message carries the resolved spec for the config's
|
|
98
|
+
* `map_tile_provider` alongside the config itself, so a worker can
|
|
99
|
+
* never hold a config whose spec it lacks.
|
|
100
|
+
*/
|
|
101
|
+
export declare class TileSourceRegistry {
|
|
102
|
+
private _specs;
|
|
103
|
+
constructor(seed: readonly unknown[]);
|
|
104
|
+
/**
|
|
105
|
+
* Add (or replace, by id) a provider. Validates via
|
|
106
|
+
* {@link parseTileSourceSpec} and returns the normalized spec.
|
|
107
|
+
* Replacement is safe for live charts: the cache identity below is
|
|
108
|
+
* content-derived, so a changed template yields a new cache key
|
|
109
|
+
* and the tile layer drops its stale textures on next bind.
|
|
110
|
+
*/
|
|
111
|
+
register(input: unknown): TileSourceSpec;
|
|
112
|
+
/** Every known spec, bundled first, in insertion order. */
|
|
113
|
+
list(): readonly TileSourceSpec[];
|
|
114
|
+
/** The spec registered under `id`, or `undefined` if unknown. */
|
|
115
|
+
specFor(id: string): TileSourceSpec | undefined;
|
|
116
|
+
/**
|
|
117
|
+
* Resolve a `map_tile_provider` id to a concrete `TileSource`.
|
|
118
|
+
* Unknown ids fall back to the first bundled entry so a
|
|
119
|
+
* misconfigured `plugin_config` never produces a blank map — and
|
|
120
|
+
* so a config restored *before* its custom source is registered
|
|
121
|
+
* degrades to the default basemap until the next config forward
|
|
122
|
+
* carries the registered spec.
|
|
123
|
+
*/
|
|
124
|
+
sourceFor(id: string): TileSource;
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Realm-wide provider registry, seeded from [tile-sources.json]. A
|
|
128
|
+
* malformed bundled entry fails loudly here, at first import.
|
|
129
|
+
*/
|
|
130
|
+
export declare const TILE_SOURCES: TileSourceRegistry;
|
|
61
131
|
/**
|
|
62
|
-
*
|
|
63
|
-
*
|
|
64
|
-
* `_pluginConfig` never produces a blank map.
|
|
132
|
+
* Read-only listing of every known tile provider (bundled + runtime-
|
|
133
|
+
* registered), in settings-panel enum order. Public package export.
|
|
65
134
|
*/
|
|
66
|
-
export declare function
|
|
135
|
+
export declare function tileSources(): readonly TileSourceSpec[];
|