@perspective-dev/viewer-charts 5.2.0 → 5.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (109) hide show
  1. package/dist/cdn/perspective-viewer-charts.js +1 -2
  2. package/dist/cdn/perspective-viewer-charts.js.map +4 -4
  3. package/dist/esm/axis/legend.d.ts +53 -5
  4. package/dist/esm/axis/map-ticks.d.ts +15 -0
  5. package/dist/esm/charts/candlestick/candlestick-interact.d.ts +2 -6
  6. package/dist/esm/charts/candlestick/candlestick-render.d.ts +3 -0
  7. package/dist/esm/charts/cartesian/cartesian.d.ts +7 -0
  8. package/dist/esm/charts/cartesian/glyph.d.ts +2 -1
  9. package/dist/esm/charts/cartesian/glyphs/density.d.ts +2 -1
  10. package/dist/esm/charts/cartesian/glyphs/lines.d.ts +2 -1
  11. package/dist/esm/charts/cartesian/glyphs/points.d.ts +2 -1
  12. package/dist/esm/charts/cartesian/tooltip-lines.d.ts +1 -1
  13. package/dist/esm/charts/chart-base.d.ts +24 -9
  14. package/dist/esm/charts/chart.d.ts +81 -6
  15. package/dist/esm/charts/common/category-axis-resolver.d.ts +1 -1
  16. package/dist/esm/charts/common/draw-tooltip-box.d.ts +2 -1
  17. package/dist/esm/charts/common/tree-chart.d.ts +1 -0
  18. package/dist/esm/charts/common/tree-interact.d.ts +1 -1
  19. package/dist/esm/charts/heatmap/heatmap.d.ts +1 -0
  20. package/dist/esm/charts/series/series-build.d.ts +7 -0
  21. package/dist/esm/charts/series/series-interact.d.ts +1 -1
  22. package/dist/esm/charts/series/series-type.d.ts +18 -0
  23. package/dist/esm/charts/series/series.d.ts +10 -1
  24. package/dist/esm/charts/sunburst/sunburst-render.d.ts +4 -0
  25. package/dist/esm/index.d.ts +2 -0
  26. package/dist/esm/interaction/axis-indicators.d.ts +11 -0
  27. package/dist/esm/interaction/host-sink-dom.d.ts +3 -2
  28. package/dist/esm/interaction/host-sink-message.d.ts +3 -2
  29. package/dist/esm/interaction/lazy-tooltip.d.ts +2 -2
  30. package/dist/esm/interaction/legend-controller.d.ts +246 -0
  31. package/dist/esm/interaction/tooltip-controller.d.ts +20 -95
  32. package/dist/esm/interaction/tooltip-grid.d.ts +33 -0
  33. package/dist/esm/layout/facet-grid.d.ts +6 -0
  34. package/dist/esm/layout/ticks.d.ts +1 -0
  35. package/dist/esm/map/tile-source.d.ts +77 -8
  36. package/dist/esm/perspective-viewer-charts.js +1 -2
  37. package/dist/esm/perspective-viewer-charts.js.map +4 -4
  38. package/dist/esm/plugin/charts.d.ts +2 -0
  39. package/dist/esm/plugin/format-defaults.d.ts +9 -0
  40. package/dist/esm/plugin/plugin.d.ts +28 -1
  41. package/dist/esm/theme/gradient.d.ts +32 -0
  42. package/dist/esm/theme/overrides.d.ts +19 -0
  43. package/dist/esm/transport/protocol.d.ts +58 -5
  44. package/dist/esm/transport/renderer-transport.d.ts +3 -0
  45. package/dist/esm/utils/css.d.ts +4 -0
  46. package/dist/esm/worker/renderer.worker.d.ts +46 -1
  47. package/package.json +1 -1
  48. package/src/css/perspective-viewer-charts.css +0 -1
  49. package/src/ts/axis/bar-axis.ts +2 -7
  50. package/src/ts/axis/legend.ts +351 -24
  51. package/src/ts/axis/map-ticks.ts +94 -0
  52. package/src/ts/axis/numeric-axis.ts +2 -9
  53. package/src/ts/charts/candlestick/candlestick-build.ts +1 -0
  54. package/src/ts/charts/candlestick/candlestick-interact.ts +46 -40
  55. package/src/ts/charts/candlestick/candlestick-render.ts +81 -10
  56. package/src/ts/charts/candlestick/candlestick.ts +4 -3
  57. package/src/ts/charts/cartesian/cartesian-interact.ts +9 -3
  58. package/src/ts/charts/cartesian/cartesian-render.ts +374 -75
  59. package/src/ts/charts/cartesian/cartesian.ts +13 -0
  60. package/src/ts/charts/cartesian/glyph.ts +6 -2
  61. package/src/ts/charts/cartesian/glyphs/density.ts +2 -2
  62. package/src/ts/charts/cartesian/glyphs/lines.ts +15 -11
  63. package/src/ts/charts/cartesian/glyphs/points.ts +9 -5
  64. package/src/ts/charts/cartesian/tooltip-lines.ts +8 -8
  65. package/src/ts/charts/chart-base.ts +76 -42
  66. package/src/ts/charts/chart.ts +111 -7
  67. package/src/ts/charts/common/category-axis-resolver.ts +44 -5
  68. package/src/ts/charts/common/draw-tooltip-box.ts +13 -30
  69. package/src/ts/charts/common/tree-chart.ts +4 -0
  70. package/src/ts/charts/common/tree-chrome.ts +102 -23
  71. package/src/ts/charts/common/tree-interact.ts +18 -17
  72. package/src/ts/charts/heatmap/heatmap-build.ts +7 -1
  73. package/src/ts/charts/heatmap/heatmap-interact.ts +75 -8
  74. package/src/ts/charts/heatmap/heatmap-render.ts +105 -24
  75. package/src/ts/charts/heatmap/heatmap.ts +8 -3
  76. package/src/ts/charts/map/map.ts +3 -5
  77. package/src/ts/charts/series/series-build.ts +15 -2
  78. package/src/ts/charts/series/series-interact.ts +14 -24
  79. package/src/ts/charts/series/series-render.ts +310 -132
  80. package/src/ts/charts/series/series-type.ts +65 -0
  81. package/src/ts/charts/series/series.ts +31 -8
  82. package/src/ts/charts/sunburst/sunburst-render.ts +16 -1
  83. package/src/ts/charts/treemap/treemap-render.ts +12 -1
  84. package/src/ts/data/view-reader.ts +1 -4
  85. package/src/ts/index.ts +2 -0
  86. package/src/ts/interaction/axis-indicators.ts +137 -0
  87. package/src/ts/interaction/host-sink-dom.ts +66 -2
  88. package/src/ts/interaction/host-sink-message.ts +8 -2
  89. package/src/ts/interaction/lazy-tooltip.ts +5 -5
  90. package/src/ts/interaction/legend-controller.ts +1084 -0
  91. package/src/ts/interaction/tooltip-controller.ts +43 -131
  92. package/src/ts/interaction/tooltip-grid.ts +187 -0
  93. package/src/ts/layout/facet-grid.ts +8 -1
  94. package/src/ts/layout/ticks.ts +48 -12
  95. package/src/ts/map/tile-loader.ts +16 -1
  96. package/src/ts/map/tile-source.ts +199 -53
  97. package/src/ts/map/tile-sources.json +18 -0
  98. package/src/ts/plugin/charts.ts +40 -6
  99. package/src/ts/plugin/format-defaults.ts +37 -0
  100. package/src/ts/plugin/plugin.ts +330 -76
  101. package/src/ts/shaders/line.vert.glsl +5 -2
  102. package/src/ts/shaders/scatter.vert.glsl +0 -7
  103. package/src/ts/theme/gradient.ts +265 -1
  104. package/src/ts/theme/overrides.ts +76 -0
  105. package/src/ts/transport/protocol.ts +60 -4
  106. package/src/ts/transport/renderer-transport.ts +107 -14
  107. package/src/ts/utils/css.ts +12 -0
  108. package/src/ts/worker/dispatch.ts +6 -2
  109. package/src/ts/worker/renderer.worker.ts +191 -7
@@ -12,6 +12,7 @@
12
12
 
13
13
  import type { CandlestickChart } from "./candlestick";
14
14
  import { renderCandlestickChromeOverlay } from "./candlestick-render";
15
+ import { tooltipStyleOf } from "../../interaction/tooltip-grid";
15
16
 
16
17
  /**
17
18
  * Pixels of horizontal slack around the wick centerline so narrow
@@ -163,24 +164,24 @@ export function showCandlestickPinnedTooltip(
163
164
  return;
164
165
  }
165
166
 
166
- const lines = buildCandlestickTooltipLines(chart, idx);
167
- if (lines.length === 0) {
167
+ const grid = buildCandlestickTooltipLines(chart, idx);
168
+ if (grid.length === 0) {
168
169
  return;
169
170
  }
170
171
 
171
172
  const xCenter = candles.xCenter[idx];
172
173
  const yMid = (candles.high[idx] + candles.low[idx]) / 2;
173
174
  const pos = chart._lastLayout.dataToPixel(xCenter, yMid);
174
-
175
- // CSS bounds come from the chart's own layout, which is populated
176
- // by the render path regardless of where the chart runs.
177
175
  const cssWidth = chart._lastLayout.cssWidth;
178
176
  const cssHeight = chart._lastLayout.cssHeight;
179
177
 
180
- chart._tooltip.pin(lines, pos, { cssWidth, cssHeight });
178
+ chart._tooltip.pin(
179
+ grid,
180
+ pos,
181
+ { cssWidth, cssHeight },
182
+ tooltipStyleOf(chart._pluginConfig),
183
+ );
181
184
 
182
- // Pinning hides the inline hover tooltip but does not change the
183
- // WebGL pass — only the chrome overlay needs to redraw.
184
185
  chart._hoveredIdx = -1;
185
186
  renderCandlestickChromeOverlay(chart);
186
187
  }
@@ -190,19 +191,14 @@ export function dismissCandlestickPinnedTooltip(chart: CandlestickChart): void {
190
191
  chart._pinnedIdx = -1;
191
192
  }
192
193
 
193
- /**
194
- * Build tooltip lines for candle at index `idx` in the columnar
195
- * storage. Indexed access avoids materializing a `CandleRecord` POJO
196
- * on the hot tooltip path.
197
- */
198
194
  export function buildCandlestickTooltipLines(
199
195
  chart: CandlestickChart,
200
196
  idx: number,
201
- ): string[] {
202
- const lines: string[] = [];
197
+ ): string[][] {
198
+ const grid: string[][] = [];
203
199
  const candles = chart._candles;
204
200
  if (idx < 0 || idx >= candles.count) {
205
- return lines;
201
+ return grid;
206
202
  }
207
203
 
208
204
  const catIdx = candles.catIdx[idx];
@@ -212,6 +208,34 @@ export function buildCandlestickTooltipLines(
212
208
  const high = candles.high[idx];
213
209
  const low = candles.low[idx];
214
210
 
211
+ const categoryText = candleCategoryText(chart, catIdx);
212
+ if (categoryText !== "") {
213
+ grid.push([categoryText]);
214
+ }
215
+
216
+ if (splitIdx >= 0 && chart._splitPrefixes.length > 1) {
217
+ const prefix = chart._splitPrefixes[splitIdx];
218
+ if (prefix) {
219
+ grid.push([prefix]);
220
+ }
221
+ }
222
+
223
+ const openFmt = chart.getColumnFormatter(chart._columnSlots[0], "value");
224
+ const closeFmt = chart.getColumnFormatter(chart._columnSlots[1], "value");
225
+ const highFmt = chart.getColumnFormatter(chart._columnSlots[2], "value");
226
+ const lowFmt = chart.getColumnFormatter(chart._columnSlots[3], "value");
227
+ grid.push(["Open", openFmt(open)]);
228
+ grid.push(["Close", closeFmt(close)]);
229
+ grid.push(["High", highFmt(high)]);
230
+ grid.push(["Low", lowFmt(low)]);
231
+
232
+ return grid;
233
+ }
234
+
235
+ export function candleCategoryText(
236
+ chart: CandlestickChart,
237
+ catIdx: number,
238
+ ): string {
215
239
  if (
216
240
  chart._categoryAxisMode === "numeric" &&
217
241
  chart._numericCategoryDomain &&
@@ -219,8 +243,10 @@ export function buildCandlestickTooltipLines(
219
243
  ) {
220
244
  const v = chart._categoryPositions[catIdx];
221
245
  const xColumn = chart._groupBy[0];
222
- lines.push(chart.getColumnFormatter(xColumn, "value")(v));
223
- } else if (chart._rowPaths.length > 0) {
246
+ return chart.getColumnFormatter(xColumn, "value")(v);
247
+ }
248
+
249
+ if (chart._rowPaths.length > 0) {
224
250
  const parts: string[] = [];
225
251
  for (const rp of chart._rowPaths) {
226
252
  const s = rp.labels[catIdx] ?? "";
@@ -229,28 +255,8 @@ export function buildCandlestickTooltipLines(
229
255
  }
230
256
  }
231
257
 
232
- if (parts.length > 0) {
233
- lines.push(parts.join(" › "));
234
- }
235
- } else {
236
- lines.push(`Row ${catIdx + chart._rowOffset}`);
237
- }
238
-
239
- if (splitIdx >= 0 && chart._splitPrefixes.length > 1) {
240
- const prefix = chart._splitPrefixes[splitIdx];
241
- if (prefix) {
242
- lines.push(prefix);
243
- }
258
+ return parts.join(" › ");
244
259
  }
245
260
 
246
- const openFmt = chart.getColumnFormatter(chart._columnSlots[0], "value");
247
- const closeFmt = chart.getColumnFormatter(chart._columnSlots[1], "value");
248
- const highFmt = chart.getColumnFormatter(chart._columnSlots[2], "value");
249
- const lowFmt = chart.getColumnFormatter(chart._columnSlots[3], "value");
250
- lines.push(`Open: ${openFmt(open)}`);
251
- lines.push(`Close: ${closeFmt(close)}`);
252
- lines.push(`High: ${highFmt(high)}`);
253
- lines.push(`Low: ${lowFmt(low)}`);
254
-
255
- return lines;
261
+ return `Row ${catIdx + chart._rowOffset}`;
256
262
  }
@@ -16,7 +16,12 @@ import { PlotLayout } from "../../layout/plot-layout";
16
16
  import { sampleGradient } from "../../theme/gradient";
17
17
  import { renderInPlotFrame } from "../../webgl/plot-frame";
18
18
  import { renderCanvasTooltip } from "../../interaction/tooltip-controller";
19
- import { computeNiceTicks } from "../../layout/ticks";
19
+ import { tooltipStyleOf } from "../../interaction/tooltip-grid";
20
+ import {
21
+ renderAxisHoverIndicators,
22
+ type AxisIndicator,
23
+ } from "../../interaction/axis-indicators";
24
+ import { computeNiceTicks, stepTickFormatter } from "../../layout/ticks";
20
25
  import { type AxisDomain } from "../../axis/numeric-axis";
21
26
  import {
22
27
  renderBarAxesChrome,
@@ -27,7 +32,10 @@ import {
27
32
  measureCategoricalAxisHeight,
28
33
  type CategoricalDomain,
29
34
  } from "../../axis/categorical-axis";
30
- import { buildCandlestickTooltipLines } from "./candlestick-interact";
35
+ import {
36
+ buildCandlestickTooltipLines,
37
+ candleCategoryText,
38
+ } from "./candlestick-interact";
31
39
  import {
32
40
  computeVisibleExtent,
33
41
  type VisibleExtent,
@@ -230,7 +238,15 @@ export function renderCandlestickFrame(
230
238
  chart._defer2D(() => renderCandlestickChromeOverlay(chart));
231
239
  }
232
240
 
241
+ /**
242
+ * Draw axes chrome + (optional) tooltip onto the overlay canvas.
243
+ */
233
244
  export function renderCandlestickChromeOverlay(chart: CandlestickChart): void {
245
+ paintCandlestickChromeOverlay(chart);
246
+ chart.presentOverlay();
247
+ }
248
+
249
+ function paintCandlestickChromeOverlay(chart: CandlestickChart): void {
234
250
  if (
235
251
  !chart._chromeCanvas ||
236
252
  !chart._lastLayout ||
@@ -313,18 +329,73 @@ function renderCandlestickTooltip(chart: CandlestickChart): void {
313
329
  const pos = layout.dataToPixel(xCenter, yMid);
314
330
  const lines = buildCandlestickTooltipLines(chart, i);
315
331
  const theme = chart._resolveTheme();
316
- renderCanvasTooltip(
332
+ const dpr = chart._glManager?.dpr ?? 1;
333
+
334
+ let valueColumn = chart._columnSlots[1];
335
+ let value = candles.close[i];
336
+ if (valueColumn == null || !Number.isFinite(value)) {
337
+ valueColumn =
338
+ chart._columnSlots[0] ??
339
+ chart._columnSlots[2] ??
340
+ chart._columnSlots[3];
341
+ value = candles.open[i];
342
+ }
343
+
344
+ const datumPos = layout.dataToPixel(xCenter, value);
345
+ const indicators: AxisIndicator[] = [];
346
+
347
+ let categoryText: string;
348
+ if (
349
+ chart._categoryAxisMode === "numeric" &&
350
+ chart._numericCategoryDomain &&
351
+ chart._categoryPositions
352
+ ) {
353
+ const catFmt =
354
+ chart.getColumnFormatter(chart._groupBy[0], "tick") ??
355
+ stepTickFormatter(
356
+ chart._numericCategoryDomain.isDate,
357
+ chart._lastCatTicks,
358
+ );
359
+ categoryText = catFmt(chart._categoryPositions[candles.catIdx[i]]);
360
+ } else {
361
+ categoryText = candleCategoryText(chart, candles.catIdx[i]);
362
+ }
363
+
364
+ if (categoryText !== "") {
365
+ indicators.push({
366
+ side: "bottom",
367
+ px: datumPos.px,
368
+ py: datumPos.py,
369
+ text: categoryText,
370
+ });
371
+ }
372
+
373
+ if (Number.isFinite(value)) {
374
+ const valueFmt =
375
+ chart.getColumnFormatter(valueColumn, "tick") ??
376
+ stepTickFormatter(chart._lastYDomain?.isDate, chart._lastYTicks);
377
+ indicators.push({
378
+ side: "left",
379
+ px: datumPos.px,
380
+ py: datumPos.py,
381
+ text: valueFmt(value),
382
+ });
383
+ }
384
+
385
+ renderAxisHoverIndicators(
317
386
  chart._chromeCanvas,
318
- pos,
319
- lines,
320
387
  layout,
321
388
  theme,
322
- chart._glManager?.dpr ?? 1,
323
- {
324
- crosshair: false,
325
- highlightRadius: 0,
326
- },
389
+ dpr,
390
+ indicators,
391
+ chart._pluginConfig.tooltip_opacity,
327
392
  );
393
+
394
+ renderCanvasTooltip(chart._chromeCanvas, pos, lines, layout, theme, dpr, {
395
+ ...tooltipStyleOf(chart._pluginConfig),
396
+ crosshair: false,
397
+ highlightRadius: 0,
398
+ });
328
399
  }
329
400
 
330
401
  /**
@@ -212,9 +212,10 @@ export class CandlestickChart extends CategoricalYChart {
212
212
 
213
213
  const catIdx = this._candles.catIdx[idx];
214
214
  const splitIdx = this._candles.splitIdx[idx];
215
- const groupByValues: (string | null)[] = this._rowPaths.map(
216
- (level) => level.labels[catIdx] ?? null,
217
- );
215
+ const groupByValues: (string | number | null)[] =
216
+ this._categoryAxisMode === "numeric" && this._categoryPositions
217
+ ? [this._categoryPositions[catIdx] ?? null]
218
+ : this._rowPaths.map((level) => level.labels[catIdx] ?? null);
218
219
  const splitKey = this._splitPrefixes[splitIdx] ?? "";
219
220
  const splitByValues =
220
221
  this._splitBy.length > 0 && splitKey !== ""
@@ -12,6 +12,7 @@
12
12
 
13
13
  import type { CartesianChart } from "./cartesian";
14
14
  import { renderCartesianChromeOverlay } from "./cartesian-render";
15
+ import { tooltipStyleOf } from "../../interaction/tooltip-grid";
15
16
 
16
17
  const TOOLTIP_RADIUS_PX = 24;
17
18
 
@@ -311,7 +312,7 @@ export function showCartesianPinnedTooltip(
311
312
  );
312
313
 
313
314
  const serial = chart._lazyTooltip.beginPin();
314
- chart.glyph.buildTooltipLines(chart, pointIdx).then((lines) => {
315
+ chart.glyph.buildTooltipLines(chart, pointIdx).then((grid) => {
315
316
  // Abandon the pin if the user moved on (another pin/dismiss
316
317
  // between click and resolve) or the underlying view changed.
317
318
  if (!chart._lazyTooltip.isPinFresh(serial)) {
@@ -322,11 +323,16 @@ export function showCartesianPinnedTooltip(
322
323
  return;
323
324
  }
324
325
 
325
- if (lines.length === 0) {
326
+ if (grid.length === 0) {
326
327
  return;
327
328
  }
328
329
 
329
- chart._tooltip.pin(lines, pos, layout);
330
+ chart._tooltip.pin(
331
+ grid,
332
+ pos,
333
+ layout,
334
+ tooltipStyleOf(chart._pluginConfig),
335
+ );
330
336
  });
331
337
 
332
338
  chart._hoveredIndex = -1;