@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
package/src/ts/plugin/plugin.ts
CHANGED
|
@@ -17,7 +17,12 @@ import type {
|
|
|
17
17
|
IPerspectiveViewerPlugin,
|
|
18
18
|
PluginStaticConfig,
|
|
19
19
|
} from "@perspective-dev/viewer";
|
|
20
|
-
import {
|
|
20
|
+
import {
|
|
21
|
+
ChartTypeConfig,
|
|
22
|
+
LEGEND_FIELDS,
|
|
23
|
+
TOOLTIP_FIELDS,
|
|
24
|
+
PluginConfigField,
|
|
25
|
+
} from "./charts";
|
|
21
26
|
import style from "../../css/perspective-viewer-charts.css";
|
|
22
27
|
import {
|
|
23
28
|
DEFAULT_FACET_CONFIG,
|
|
@@ -25,8 +30,14 @@ import {
|
|
|
25
30
|
type FacetConfig,
|
|
26
31
|
type PluginConfig,
|
|
27
32
|
} from "../charts/chart";
|
|
33
|
+
import {
|
|
34
|
+
CHART_DATE_DEFAULTS,
|
|
35
|
+
CHART_DATETIME_DEFAULTS,
|
|
36
|
+
CHART_NUMBER_DEFAULTS,
|
|
37
|
+
} from "./format-defaults";
|
|
28
38
|
import { RawEventForwarder } from "../interaction/raw-event-forwarder";
|
|
29
39
|
import { RendererTransport } from "../transport/renderer-transport";
|
|
40
|
+
import { TILE_SOURCES, type TileSourceSpec } from "../map/tile-source";
|
|
30
41
|
import { RENDER_BLIT_MODE } from "../config";
|
|
31
42
|
import { snapshotThemeVars } from "../theme/theme-snapshot";
|
|
32
43
|
import { resolveThemeFromVars, type Theme } from "../theme/theme";
|
|
@@ -42,6 +53,26 @@ import { vec3ToHexColor } from "../utils/css";
|
|
|
42
53
|
*/
|
|
43
54
|
const FACET_CONFIG_DEFAULTS: FacetConfig = { ...DEFAULT_FACET_CONFIG };
|
|
44
55
|
|
|
56
|
+
/**
|
|
57
|
+
* Register a raster XYZ tile provider at runtime. The provider joins
|
|
58
|
+
* the bundled [map/tile-sources.json] entries in the settings panel's
|
|
59
|
+
* `map_tile_provider` enum and resolves on any map chart from its next
|
|
60
|
+
* config forward onward — the resolved spec rides every outgoing
|
|
61
|
+
* `setPluginConfig` / `init` message alongside the config that names
|
|
62
|
+
* it, so no separate registry-sync operation exists. Re-registering an
|
|
63
|
+
* id replaces it (a changed template drops cached tiles via the
|
|
64
|
+
* content-derived cache identity); a chart currently displaying that
|
|
65
|
+
* id picks the replacement up on its next `restore()`. Throws
|
|
66
|
+
* `TypeError` on a malformed spec. Returns the normalized spec.
|
|
67
|
+
*
|
|
68
|
+
* A key-gated provider is registered with the key embedded in its
|
|
69
|
+
* `template` — keys never enter `plugin_config`, so they never appear
|
|
70
|
+
* in `save()` output.
|
|
71
|
+
*/
|
|
72
|
+
export function registerTileSource(spec: unknown): TileSourceSpec {
|
|
73
|
+
return TILE_SOURCES.register(spec);
|
|
74
|
+
}
|
|
75
|
+
|
|
45
76
|
/**
|
|
46
77
|
* Static UI-control spec per `plugin_config` field. Mirrors the shape
|
|
47
78
|
* `column_config_schema` already returns (datagrid). The runtime default
|
|
@@ -57,72 +88,157 @@ type FieldSpec =
|
|
|
57
88
|
}
|
|
58
89
|
| { kind: "Number"; min: number; max: number; step?: number };
|
|
59
90
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
91
|
+
/**
|
|
92
|
+
* A `FieldSpec` entry may be a thunk when its contents depend on
|
|
93
|
+
* runtime state — `map_tile_provider`'s variants come from the tile-
|
|
94
|
+
* source registry, which grows via `registerTileSource`, so the enum
|
|
95
|
+
* must be resolved per `plugin_config_schema()` call rather than at
|
|
96
|
+
* module init.
|
|
97
|
+
*/
|
|
98
|
+
const FIELD_SCHEMAS: Record<PluginConfigField, FieldSpec | (() => FieldSpec)> =
|
|
99
|
+
{
|
|
100
|
+
auto_alt_y_axis: { kind: "Bool" },
|
|
101
|
+
include_zero: { kind: "Bool" },
|
|
102
|
+
domain_mode: {
|
|
103
|
+
kind: "Enum",
|
|
104
|
+
variants: [
|
|
105
|
+
{ value: "fit", label: "Fit" },
|
|
106
|
+
{ value: "expand", label: "Expand" },
|
|
107
|
+
],
|
|
108
|
+
},
|
|
109
|
+
facet_mode: {
|
|
110
|
+
kind: "Enum",
|
|
111
|
+
variants: [
|
|
112
|
+
{ value: "grid", label: "Grid" },
|
|
113
|
+
{ value: "overlay", label: "Overlay" },
|
|
114
|
+
],
|
|
115
|
+
},
|
|
116
|
+
facet_zoom_mode: {
|
|
117
|
+
kind: "Enum",
|
|
118
|
+
variants: [
|
|
119
|
+
{ value: "shared", label: "Shared" },
|
|
120
|
+
{ value: "independent", label: "Independent" },
|
|
121
|
+
],
|
|
122
|
+
},
|
|
123
|
+
series_zoom_mode: {
|
|
124
|
+
kind: "Enum",
|
|
125
|
+
variants: [
|
|
126
|
+
{ value: "dynamic", label: "Dynamic" },
|
|
127
|
+
{ value: "fixed", label: "Fixed" },
|
|
128
|
+
],
|
|
129
|
+
},
|
|
130
|
+
line_width_px: { kind: "Number", min: 0.5, step: 0.5, max: 16 },
|
|
131
|
+
point_size_px: { kind: "Number", min: 1, max: 32 },
|
|
132
|
+
band_inner_frac: { kind: "Number", min: 0.1, max: 1, step: 0.01 },
|
|
133
|
+
bar_inner_pad: { kind: "Number", min: 0, max: 0.9, step: 0.01 },
|
|
134
|
+
wick_width_px: { kind: "Number", min: 0.5, step: 0.5, max: 8 },
|
|
135
|
+
ohlc_line_width_px: { kind: "Number", min: 0.5, step: 0.5, max: 8 },
|
|
136
|
+
gradient_radius_px: { kind: "Number", min: 2, step: 1, max: 256 },
|
|
137
|
+
gradient_intensity: { kind: "Number", min: 0.05, step: 0.05, max: 4 },
|
|
138
|
+
gradient_heat_max: { kind: "Number", min: 0.1, step: 0.1, max: 64 },
|
|
139
|
+
gradient_color_mode: {
|
|
140
|
+
kind: "Enum",
|
|
141
|
+
variants: [
|
|
142
|
+
{ value: "mean", label: "Mean (density-weighted)" },
|
|
143
|
+
{ value: "density", label: "Density only" },
|
|
144
|
+
{ value: "extreme", label: "Extremes" },
|
|
145
|
+
{ value: "signed", label: "Signed sum" },
|
|
146
|
+
],
|
|
147
|
+
},
|
|
148
|
+
map_tile_provider: () => ({
|
|
149
|
+
kind: "Enum",
|
|
150
|
+
variants: TILE_SOURCES.list().map((s) => ({
|
|
151
|
+
value: s.id,
|
|
152
|
+
label: s.label,
|
|
153
|
+
})),
|
|
154
|
+
}),
|
|
155
|
+
map_tile_alpha: { kind: "Number", min: 0, max: 1, step: 0.05 },
|
|
156
|
+
numeric_axes: { kind: "Bool" },
|
|
157
|
+
legend_mode: {
|
|
158
|
+
kind: "Enum",
|
|
159
|
+
variants: [
|
|
160
|
+
{ value: "auto", label: "Auto" },
|
|
161
|
+
{ value: "sidebar", label: "Sidebar" },
|
|
162
|
+
{ value: "none", label: "None" },
|
|
163
|
+
{ value: "floating", label: "Floating" },
|
|
164
|
+
],
|
|
165
|
+
},
|
|
166
|
+
legend_size_mode: {
|
|
167
|
+
kind: "Enum",
|
|
168
|
+
variants: [
|
|
169
|
+
{ value: "auto", label: "Auto" },
|
|
170
|
+
{ value: "fixed", label: "Fixed" },
|
|
171
|
+
],
|
|
172
|
+
},
|
|
173
|
+
// 0 = auto (the chart family's historical gutter width).
|
|
174
|
+
legend_width_px: { kind: "Number", min: 0, max: 512, step: 1 },
|
|
175
|
+
legend_height_px: { kind: "Number", min: 48, max: 1024, step: 1 },
|
|
176
|
+
legend_anchor: {
|
|
177
|
+
kind: "Enum",
|
|
178
|
+
variants: [
|
|
179
|
+
{ value: "top-right", label: "Top Right" },
|
|
180
|
+
{ value: "top-left", label: "Top Left" },
|
|
181
|
+
{ value: "bottom-right", label: "Bottom Right" },
|
|
182
|
+
{ value: "bottom-left", label: "Bottom Left" },
|
|
183
|
+
],
|
|
184
|
+
},
|
|
185
|
+
legend_x: { kind: "Number", min: 0, max: 1, step: 0.01 },
|
|
186
|
+
legend_y: { kind: "Number", min: 0, max: 1, step: 0.01 },
|
|
187
|
+
legend_opacity: { kind: "Number", min: 0, max: 1, step: 0.05 },
|
|
188
|
+
tooltip_max_column_px: { kind: "Number", min: 40, max: 512, step: 1 },
|
|
189
|
+
tooltip_opacity: { kind: "Number", min: 0, max: 1, step: 0.05 },
|
|
190
|
+
};
|
|
191
|
+
|
|
192
|
+
function fieldSpec(
|
|
193
|
+
key: PluginConfigField,
|
|
194
|
+
defaults: PluginConfig,
|
|
195
|
+
): Record<string, unknown> & { kind: string } {
|
|
196
|
+
const entry = FIELD_SCHEMAS[key];
|
|
197
|
+
const spec = typeof entry === "function" ? entry() : entry;
|
|
198
|
+
return { ...spec, key, default: defaults[key] };
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
const PLUGIN_FIELD_GROUPS: ReadonlyArray<{
|
|
202
|
+
key: string;
|
|
203
|
+
fields: readonly PluginConfigField[];
|
|
204
|
+
}> = [
|
|
205
|
+
{
|
|
206
|
+
key: "axes",
|
|
207
|
+
fields: [
|
|
208
|
+
"auto_alt_y_axis",
|
|
209
|
+
"include_zero",
|
|
210
|
+
"domain_mode",
|
|
211
|
+
"numeric_axes",
|
|
82
212
|
],
|
|
83
213
|
},
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
{ value: "dynamic", label: "Dynamic" },
|
|
88
|
-
{ value: "fixed", label: "Fixed" },
|
|
89
|
-
],
|
|
214
|
+
{
|
|
215
|
+
key: "facets",
|
|
216
|
+
fields: ["facet_mode", "facet_zoom_mode", "series_zoom_mode"],
|
|
90
217
|
},
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
gradient_color_mode: {
|
|
101
|
-
kind: "Enum",
|
|
102
|
-
variants: [
|
|
103
|
-
{ value: "mean", label: "Mean (density-weighted)" },
|
|
104
|
-
{ value: "density", label: "Density only" },
|
|
105
|
-
{ value: "extreme", label: "Extremes" },
|
|
106
|
-
{ value: "signed", label: "Signed sum" },
|
|
218
|
+
{
|
|
219
|
+
key: "glyph",
|
|
220
|
+
fields: [
|
|
221
|
+
"line_width_px",
|
|
222
|
+
"point_size_px",
|
|
223
|
+
"band_inner_frac",
|
|
224
|
+
"bar_inner_pad",
|
|
225
|
+
"wick_width_px",
|
|
226
|
+
"ohlc_line_width_px",
|
|
107
227
|
],
|
|
108
228
|
},
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
229
|
+
{
|
|
230
|
+
key: "density",
|
|
231
|
+
fields: [
|
|
232
|
+
"gradient_color_mode",
|
|
233
|
+
"gradient_radius_px",
|
|
234
|
+
"gradient_intensity",
|
|
235
|
+
"gradient_heat_max",
|
|
115
236
|
],
|
|
116
237
|
},
|
|
117
|
-
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
key: PluginConfigField,
|
|
122
|
-
defaults: PluginConfig,
|
|
123
|
-
): Record<string, unknown> & { kind: string } {
|
|
124
|
-
return { ...FIELD_SCHEMAS[key], key, default: defaults[key] };
|
|
125
|
-
}
|
|
238
|
+
{ key: "basemap", fields: ["map_tile_provider", "map_tile_alpha"] },
|
|
239
|
+
{ key: "legend", fields: LEGEND_FIELDS },
|
|
240
|
+
{ key: "tooltip", fields: TOOLTIP_FIELDS },
|
|
241
|
+
];
|
|
126
242
|
|
|
127
243
|
const GLOBAL_STYLES = (() => {
|
|
128
244
|
const sheet = new CSSStyleSheet();
|
|
@@ -399,6 +515,19 @@ export class HTMLPerspectiveViewerWebGLPluginElement
|
|
|
399
515
|
zoomControls.classList.toggle("visible", !isDefault);
|
|
400
516
|
}
|
|
401
517
|
},
|
|
518
|
+
onPluginConfigDelta: (fields) => {
|
|
519
|
+
this._pluginConfig = { ...this._pluginConfig, ...fields };
|
|
520
|
+
const host = this
|
|
521
|
+
.parentElement as HTMLPerspectiveViewerElement | null;
|
|
522
|
+
(
|
|
523
|
+
host?.restore(
|
|
524
|
+
{ plugin_config: fields },
|
|
525
|
+
panel ? { panel } : undefined,
|
|
526
|
+
) as Promise<void> | undefined
|
|
527
|
+
)?.catch((e: unknown) => {
|
|
528
|
+
console.error("legend config persistence failed", e);
|
|
529
|
+
});
|
|
530
|
+
},
|
|
402
531
|
});
|
|
403
532
|
|
|
404
533
|
await transport.init({
|
|
@@ -437,6 +566,14 @@ export class HTMLPerspectiveViewerWebGLPluginElement
|
|
|
437
566
|
BLIT_MODE = mode;
|
|
438
567
|
}
|
|
439
568
|
|
|
569
|
+
static registerTileSource(spec: unknown): TileSourceSpec {
|
|
570
|
+
return registerTileSource(spec);
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
static tileSources(): readonly TileSourceSpec[] {
|
|
574
|
+
return TILE_SOURCES.list();
|
|
575
|
+
}
|
|
576
|
+
|
|
440
577
|
get_static_config(): PluginStaticConfig {
|
|
441
578
|
return {
|
|
442
579
|
name: this._chartType.name,
|
|
@@ -592,9 +729,20 @@ export class HTMLPerspectiveViewerWebGLPluginElement
|
|
|
592
729
|
// Per-column formatter widgets. Surfaced for every chart type so
|
|
593
730
|
// axes / tooltips / legends honor the user's format choice.
|
|
594
731
|
if (column_type === "integer" || column_type === "float") {
|
|
595
|
-
fields.push({
|
|
596
|
-
|
|
597
|
-
|
|
732
|
+
fields.push({
|
|
733
|
+
kind: "NumberFormat",
|
|
734
|
+
default: CHART_NUMBER_DEFAULTS,
|
|
735
|
+
});
|
|
736
|
+
} else if (column_type === "datetime") {
|
|
737
|
+
fields.push({
|
|
738
|
+
kind: "DatetimeFormat",
|
|
739
|
+
default: CHART_DATETIME_DEFAULTS,
|
|
740
|
+
});
|
|
741
|
+
} else if (column_type === "date") {
|
|
742
|
+
fields.push({
|
|
743
|
+
kind: "DatetimeFormat",
|
|
744
|
+
default: CHART_DATE_DEFAULTS,
|
|
745
|
+
});
|
|
598
746
|
}
|
|
599
747
|
|
|
600
748
|
return { fields };
|
|
@@ -605,11 +753,32 @@ export class HTMLPerspectiveViewerWebGLPluginElement
|
|
|
605
753
|
group_rollup_mode?: string;
|
|
606
754
|
}) {
|
|
607
755
|
const defaults = this._effectiveDefaults();
|
|
608
|
-
const
|
|
609
|
-
|
|
610
|
-
|
|
756
|
+
const applicable = this._chartType.applicable_plugin_fields;
|
|
757
|
+
const present = new Set(applicable);
|
|
758
|
+
|
|
759
|
+
const grouped = new Set<PluginConfigField>();
|
|
760
|
+
const groups: Array<Record<string, unknown> & { kind: string }> = [];
|
|
761
|
+
for (const group of PLUGIN_FIELD_GROUPS) {
|
|
762
|
+
const members = group.fields.filter((k) => present.has(k));
|
|
763
|
+
if (members.length >= 1) {
|
|
764
|
+
for (const k of members) {
|
|
765
|
+
grouped.add(k);
|
|
766
|
+
}
|
|
611
767
|
|
|
612
|
-
|
|
768
|
+
groups.push({
|
|
769
|
+
kind: "Group",
|
|
770
|
+
key: group.key,
|
|
771
|
+
fields: members.map((k) => fieldSpec(k, defaults)),
|
|
772
|
+
});
|
|
773
|
+
}
|
|
774
|
+
}
|
|
775
|
+
|
|
776
|
+
const fields: Array<Record<string, unknown> & { kind: string }> =
|
|
777
|
+
applicable
|
|
778
|
+
.filter((k) => !grouped.has(k))
|
|
779
|
+
.map((k) => fieldSpec(k, defaults));
|
|
780
|
+
|
|
781
|
+
return { fields: [...fields, ...groups] };
|
|
613
782
|
}
|
|
614
783
|
|
|
615
784
|
private _resolvedTheme(): Theme {
|
|
@@ -21,8 +21,11 @@
|
|
|
21
21
|
// `a_color_start` / `a_color_end` carry the segment endpoints' raw
|
|
22
22
|
// color values (numeric data value for gradient, dictionary index for
|
|
23
23
|
// categorical). The gradient LUT is sampled using the same mapping the
|
|
24
|
-
// scatter shader uses — `
|
|
25
|
-
//
|
|
24
|
+
// scatter shader uses — `u_color_range` arrives PRE-SHAPED by the host:
|
|
25
|
+
// the symmetric sign-pivot range (`colorRangePivot`) for numeric
|
|
26
|
+
// columns, so linear normalization matches the CPU `colorValueToT`
|
|
27
|
+
// used by the legend / heatmap / tree charts; raw `[0, N-1]` extents
|
|
28
|
+
// for categorical index domains. The two endpoints' colors are
|
|
26
29
|
// averaged so the segment reads as a single chord in gradient space.
|
|
27
30
|
attribute vec2 a_start;
|
|
28
31
|
attribute vec2 a_end;
|
|
@@ -47,13 +47,6 @@ void main() {
|
|
|
47
47
|
|
|
48
48
|
v_point_size = gl_PointSize;
|
|
49
49
|
|
|
50
|
-
// Color-t mapping. Linear across `[cmin, cmax]` for single-sign
|
|
51
|
-
// domains (which includes categorical `[0, N-1]` split / string
|
|
52
|
-
// indices, so the colors match `interpolatePalette`'s even sampling
|
|
53
|
-
// used by the legend). When the domain actually crosses zero we
|
|
54
|
-
// switch to sign-aware so the value 0 always lands at the 50% stop
|
|
55
|
-
// of the diverging gradient — matching heatmap and the Canvas2D
|
|
56
|
-
// tooltip paths.
|
|
57
50
|
float cmin = u_color_range.x;
|
|
58
51
|
float cmax = u_color_range.y;
|
|
59
52
|
if(cmax <= cmin) {
|
package/src/ts/theme/gradient.ts
CHANGED
|
@@ -497,6 +497,30 @@ export function colorValueToT(
|
|
|
497
497
|
return t < 0 ? 0 : t > 1 ? 1 : t;
|
|
498
498
|
}
|
|
499
499
|
|
|
500
|
+
export function colorRangePivot(
|
|
501
|
+
colorMin: number,
|
|
502
|
+
colorMax: number,
|
|
503
|
+
): [number, number] {
|
|
504
|
+
if (!isFinite(colorMin) || !isFinite(colorMax) || colorMin >= colorMax) {
|
|
505
|
+
return [0, 0];
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
let denom: number;
|
|
509
|
+
if (colorMin >= 0) {
|
|
510
|
+
denom = colorMax;
|
|
511
|
+
} else if (colorMax <= 0) {
|
|
512
|
+
denom = -colorMin;
|
|
513
|
+
} else {
|
|
514
|
+
denom = Math.max(-colorMin, colorMax);
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
if (denom <= 0) {
|
|
518
|
+
return [0, 0];
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
return [-denom, denom];
|
|
522
|
+
}
|
|
523
|
+
|
|
500
524
|
/**
|
|
501
525
|
* Convert a discrete series palette (from `--psp-charts--series-N--color`)
|
|
502
526
|
* into a `GradientStop[]` with stops at `i / (N - 1)`. The resulting
|
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
import type { FacetConfig, PluginConfig } from "../charts/chart";
|
|
14
14
|
import type { PerspectiveClickDetail } from "../event-detail";
|
|
15
15
|
import type { ThemeSnapshot } from "../theme/theme";
|
|
16
|
+
import type { TileSourceSpec } from "../map/tile-source";
|
|
16
17
|
import type { ViewConfig } from "@perspective-dev/client";
|
|
17
18
|
|
|
18
19
|
export type { ThemeSnapshot };
|
|
@@ -53,6 +54,7 @@ export type WorkerMsg =
|
|
|
53
54
|
| SetCursorMsg
|
|
54
55
|
| UserClickMsg
|
|
55
56
|
| UserSelectMsg
|
|
57
|
+
| PluginConfigDeltaMsg
|
|
56
58
|
| LoadAndRenderAckMsg
|
|
57
59
|
| ResizeAckMsg
|
|
58
60
|
| FrameBitmapMsg
|
|
@@ -185,6 +187,17 @@ export interface InitMsg {
|
|
|
185
187
|
columnsConfig?: Record<string, any>;
|
|
186
188
|
defaultChartType?: string;
|
|
187
189
|
|
|
190
|
+
/**
|
|
191
|
+
* Resolved spec for `pluginConfig.map_tile_provider`, when the
|
|
192
|
+
* plugin realm's registry knows the id — see
|
|
193
|
+
* {@link SetPluginConfigMsg.tileSource} for the invariant. Applied
|
|
194
|
+
* to the worker realm's registry before the chart impl is
|
|
195
|
+
* constructed. Bundled [map/tile-sources.json] entries ship inside
|
|
196
|
+
* the worker bundle, so this matters only for runtime-registered
|
|
197
|
+
* providers.
|
|
198
|
+
*/
|
|
199
|
+
tileSource?: TileSourceSpec;
|
|
200
|
+
|
|
188
201
|
/**
|
|
189
202
|
* Pre-resolved CSS-variable theme snapshot from the host.
|
|
190
203
|
*/
|
|
@@ -271,6 +284,18 @@ export interface SetColumnsConfigMsg {
|
|
|
271
284
|
export interface SetPluginConfigMsg {
|
|
272
285
|
kind: "setPluginConfig";
|
|
273
286
|
cfg: PluginConfig;
|
|
287
|
+
|
|
288
|
+
/**
|
|
289
|
+
* Resolved spec for `cfg.map_tile_provider`, when the plugin
|
|
290
|
+
* realm's registry knows the id. Riding the config keeps the two
|
|
291
|
+
* realms' registries convergent with NO eager mirroring: the
|
|
292
|
+
* worker registers this spec before applying `cfg`, so it can
|
|
293
|
+
* never hold a config whose provider it cannot resolve —
|
|
294
|
+
* regardless of when `registerTileSource` ran relative to
|
|
295
|
+
* renderer construction. Absent for unknown ids (the worker falls
|
|
296
|
+
* back to the default basemap).
|
|
297
|
+
*/
|
|
298
|
+
tileSource?: TileSourceSpec;
|
|
274
299
|
}
|
|
275
300
|
|
|
276
301
|
export interface SetBufferMaxCapacityMsg {
|
|
@@ -317,6 +342,7 @@ export interface LoadAndRenderMsg {
|
|
|
317
342
|
export interface LoadAndRenderAckMsg {
|
|
318
343
|
kind: "loadAndRenderAck";
|
|
319
344
|
msgId: number;
|
|
345
|
+
error?: string;
|
|
320
346
|
}
|
|
321
347
|
|
|
322
348
|
export interface RedrawMsg {
|
|
@@ -417,7 +443,8 @@ export interface SnapshotPngReqMsg {
|
|
|
417
443
|
export interface SnapshotPngReplyMsg {
|
|
418
444
|
kind: "snapshotPngReply";
|
|
419
445
|
requestId: number;
|
|
420
|
-
blob
|
|
446
|
+
blob?: Blob;
|
|
447
|
+
error?: string;
|
|
421
448
|
}
|
|
422
449
|
|
|
423
450
|
export interface DestroyMsg {
|
|
@@ -481,7 +508,9 @@ export interface ErrorMsg {
|
|
|
481
508
|
*/
|
|
482
509
|
export interface PinTooltipMsg {
|
|
483
510
|
kind: "pinTooltip";
|
|
484
|
-
|
|
511
|
+
|
|
512
|
+
grid: string[][];
|
|
513
|
+
style: { opacity: number; maxColumnPx: number };
|
|
485
514
|
pos: { px: number; py: number };
|
|
486
515
|
bounds: { cssWidth: number; cssHeight: number };
|
|
487
516
|
}
|
|
@@ -501,6 +530,21 @@ export interface SetCursorMsg {
|
|
|
501
530
|
cursor: string;
|
|
502
531
|
}
|
|
503
532
|
|
|
533
|
+
/**
|
|
534
|
+
* Renderer → host: a completed legend gesture (sidebar width drag,
|
|
535
|
+
* floating move / resize) produced new values for the legend's
|
|
536
|
+
* `plugin_config` fields. Posted ONCE per gesture, at pointerup — never
|
|
537
|
+
* per pointermove — with only the fields the gesture changed. The host
|
|
538
|
+
* plugin persists them through the viewer's public `restore` surface
|
|
539
|
+
* (a user-gesture echo), which merges the host bucket, refreshes the
|
|
540
|
+
* settings form, and echoes one `setPluginConfig` back with the same
|
|
541
|
+
* values (a no-op by the worker's legend-field equality guard).
|
|
542
|
+
*/
|
|
543
|
+
export interface PluginConfigDeltaMsg {
|
|
544
|
+
kind: "pluginConfigDelta";
|
|
545
|
+
fields: Record<string, string | number | boolean>;
|
|
546
|
+
}
|
|
547
|
+
|
|
504
548
|
/**
|
|
505
549
|
* Renderer → host: a user click landed on a chart glyph. Host
|
|
506
550
|
* re-dispatches as `CustomEvent<PerspectiveClickDetail>` on the
|