@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
|
@@ -17,12 +17,21 @@ import type { GradientStop } from "../../theme/gradient";
|
|
|
17
17
|
import type { Vec3 } from "../../theme/palette";
|
|
18
18
|
import type { Theme } from "../../theme/theme";
|
|
19
19
|
import {
|
|
20
|
+
gradientLegendAutoFit,
|
|
21
|
+
legendAutoFit,
|
|
20
22
|
renderCategoricalLegend,
|
|
21
23
|
renderCategoricalLegendAt,
|
|
22
24
|
renderLegend,
|
|
25
|
+
renderLegendAt,
|
|
26
|
+
type LegendPaintView,
|
|
23
27
|
} from "../../axis/legend";
|
|
28
|
+
import {
|
|
29
|
+
legendSidebarWidth,
|
|
30
|
+
resolveLegendMode,
|
|
31
|
+
} from "../../interaction/legend-controller";
|
|
24
32
|
import type { TreeChartBase } from "./tree-chart";
|
|
25
33
|
import { drawTooltipBox } from "./draw-tooltip-box";
|
|
34
|
+
import { tooltipStyleOf } from "../../interaction/tooltip-grid";
|
|
26
35
|
|
|
27
36
|
/**
|
|
28
37
|
* Click target for one breadcrumb segment. Tree-chart hit-testing
|
|
@@ -112,23 +121,24 @@ export function renderTreeTooltip(
|
|
|
112
121
|
cssHeight: number,
|
|
113
122
|
fontFamily: string,
|
|
114
123
|
): void {
|
|
115
|
-
const
|
|
124
|
+
const grid =
|
|
116
125
|
chart._lazyTooltip.hoveredTarget === nodeId
|
|
117
|
-
? (chart._lazyTooltip.
|
|
126
|
+
? (chart._lazyTooltip.grid ?? [])
|
|
118
127
|
: [];
|
|
119
|
-
if (
|
|
128
|
+
if (grid.length === 0) {
|
|
120
129
|
return;
|
|
121
130
|
}
|
|
122
131
|
|
|
123
132
|
drawTooltipBox(
|
|
124
133
|
ctx,
|
|
125
134
|
chart._resolveTheme(),
|
|
126
|
-
|
|
135
|
+
grid,
|
|
127
136
|
cx,
|
|
128
137
|
cy,
|
|
129
138
|
cssWidth,
|
|
130
139
|
cssHeight,
|
|
131
140
|
fontFamily,
|
|
141
|
+
tooltipStyleOf(chart._pluginConfig),
|
|
132
142
|
);
|
|
133
143
|
}
|
|
134
144
|
|
|
@@ -159,50 +169,119 @@ export function renderTreeColorLegend(
|
|
|
159
169
|
cssHeight: number,
|
|
160
170
|
categoricalRect: PlotRect | null = null,
|
|
161
171
|
): void {
|
|
162
|
-
|
|
163
|
-
|
|
172
|
+
const cfg = chart._pluginConfig;
|
|
173
|
+
const hasCategorical =
|
|
174
|
+
chart._colorMode === "series" && chart._uniqueColorLabels.size > 1;
|
|
175
|
+
const hasNumeric =
|
|
176
|
+
chart._colorMode === "numeric" && chart._colorMin < chart._colorMax;
|
|
177
|
+
const mode = resolveLegendMode(
|
|
178
|
+
cfg,
|
|
179
|
+
hasCategorical ? chart._uniqueColorLabels.size : 0,
|
|
180
|
+
);
|
|
181
|
+
if (mode === "none" || (!hasCategorical && !hasNumeric)) {
|
|
182
|
+
chart._legend.clearPainted();
|
|
183
|
+
return;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
const floating = mode === "floating";
|
|
187
|
+
const view: LegendPaintView = {
|
|
188
|
+
mode: floating ? "floating" : "sidebar",
|
|
189
|
+
legend: chart._legend,
|
|
190
|
+
title: chart._colorName || "Legend",
|
|
191
|
+
opacity: cfg.legend_opacity,
|
|
192
|
+
};
|
|
193
|
+
const floatBox = floating
|
|
194
|
+
? chart._legend.floatingBox(
|
|
195
|
+
cfg,
|
|
196
|
+
cssWidth,
|
|
197
|
+
cssHeight,
|
|
198
|
+
hasCategorical
|
|
199
|
+
? legendAutoFit(
|
|
200
|
+
canvas,
|
|
201
|
+
theme,
|
|
202
|
+
chart._uniqueColorLabels.size,
|
|
203
|
+
() => chart._uniqueColorLabels.keys(),
|
|
204
|
+
{ title: view.title },
|
|
205
|
+
)
|
|
206
|
+
: gradientLegendAutoFit(
|
|
207
|
+
canvas,
|
|
208
|
+
theme,
|
|
209
|
+
{ min: chart._colorMin, max: chart._colorMax },
|
|
210
|
+
undefined,
|
|
211
|
+
view.title,
|
|
212
|
+
),
|
|
213
|
+
)
|
|
214
|
+
: null;
|
|
215
|
+
|
|
216
|
+
if (hasCategorical) {
|
|
217
|
+
if (floatBox) {
|
|
218
|
+
renderCategoricalLegendAt(
|
|
219
|
+
canvas,
|
|
220
|
+
floatBox,
|
|
221
|
+
chart._uniqueColorLabels,
|
|
222
|
+
palette,
|
|
223
|
+
theme,
|
|
224
|
+
view,
|
|
225
|
+
);
|
|
226
|
+
} else if (categoricalRect) {
|
|
164
227
|
renderCategoricalLegendAt(
|
|
165
228
|
canvas,
|
|
166
229
|
categoricalRect,
|
|
167
230
|
chart._uniqueColorLabels,
|
|
168
231
|
palette,
|
|
169
232
|
theme,
|
|
233
|
+
{ ...view, sidebarGutter: categoricalRect.width },
|
|
170
234
|
);
|
|
171
235
|
} else {
|
|
172
236
|
renderCategoricalLegend(
|
|
173
237
|
canvas,
|
|
174
|
-
syntheticLegendLayout(cssWidth, cssHeight),
|
|
238
|
+
syntheticLegendLayout(cssWidth, cssHeight, cfg),
|
|
175
239
|
chart._uniqueColorLabels,
|
|
176
240
|
palette,
|
|
177
241
|
theme,
|
|
242
|
+
view,
|
|
243
|
+
);
|
|
244
|
+
}
|
|
245
|
+
} else {
|
|
246
|
+
const colorDomain = {
|
|
247
|
+
min: chart._colorMin,
|
|
248
|
+
max: chart._colorMax,
|
|
249
|
+
label: chart._colorName,
|
|
250
|
+
};
|
|
251
|
+
const formatter = chart.getColumnFormatter(chart._colorName, "value");
|
|
252
|
+
if (floatBox) {
|
|
253
|
+
renderLegendAt(
|
|
254
|
+
canvas,
|
|
255
|
+
floatBox,
|
|
256
|
+
colorDomain,
|
|
257
|
+
stops,
|
|
258
|
+
theme,
|
|
259
|
+
formatter,
|
|
260
|
+
view,
|
|
261
|
+
);
|
|
262
|
+
} else {
|
|
263
|
+
renderLegend(
|
|
264
|
+
canvas,
|
|
265
|
+
syntheticLegendLayout(cssWidth, cssHeight, cfg),
|
|
266
|
+
colorDomain,
|
|
267
|
+
stops,
|
|
268
|
+
theme,
|
|
269
|
+
formatter,
|
|
270
|
+
view,
|
|
178
271
|
);
|
|
179
272
|
}
|
|
180
|
-
} else if (
|
|
181
|
-
chart._colorMode === "numeric" &&
|
|
182
|
-
chart._colorMin < chart._colorMax
|
|
183
|
-
) {
|
|
184
|
-
renderLegend(
|
|
185
|
-
canvas,
|
|
186
|
-
syntheticLegendLayout(cssWidth, cssHeight),
|
|
187
|
-
{
|
|
188
|
-
min: chart._colorMin,
|
|
189
|
-
max: chart._colorMax,
|
|
190
|
-
label: chart._colorName,
|
|
191
|
-
},
|
|
192
|
-
stops,
|
|
193
|
-
theme,
|
|
194
|
-
chart.getColumnFormatter(chart._colorName, "value"),
|
|
195
|
-
);
|
|
196
273
|
}
|
|
197
274
|
}
|
|
198
275
|
|
|
199
276
|
function syntheticLegendLayout(
|
|
200
277
|
cssWidth: number,
|
|
201
278
|
cssHeight: number,
|
|
279
|
+
cfg: TreeChartBase["_pluginConfig"],
|
|
202
280
|
): PlotLayout {
|
|
203
281
|
return new PlotLayout(cssWidth, cssHeight, {
|
|
204
282
|
hasXLabel: false,
|
|
205
283
|
hasYLabel: false,
|
|
206
284
|
hasLegend: true,
|
|
285
|
+
rightExtra: legendSidebarWidth(cfg, 80),
|
|
207
286
|
});
|
|
208
287
|
}
|
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
import type { TreeChartBase } from "./tree-chart";
|
|
14
14
|
import { NULL_NODE, ancestorNames } from "./node-store";
|
|
15
15
|
import { rebuildBreadcrumbs } from "./tree-data";
|
|
16
|
+
import { tooltipStyleOf } from "../../interaction/tooltip-grid";
|
|
16
17
|
|
|
17
18
|
/**
|
|
18
19
|
* Common subset of `TreemapChart` / `SunburstChart` reached by the
|
|
@@ -73,9 +74,9 @@ export async function emitTreeNodeEvent(
|
|
|
73
74
|
export async function buildTreeTooltipLines(
|
|
74
75
|
chart: TreeInteractChart,
|
|
75
76
|
nodeId: number,
|
|
76
|
-
): Promise<string[]> {
|
|
77
|
+
): Promise<string[][]> {
|
|
77
78
|
const store = chart._nodeStore;
|
|
78
|
-
const
|
|
79
|
+
const grid: string[][] = [];
|
|
79
80
|
|
|
80
81
|
const pathNames: string[] = [];
|
|
81
82
|
let p = nodeId;
|
|
@@ -86,19 +87,17 @@ export async function buildTreeTooltipLines(
|
|
|
86
87
|
|
|
87
88
|
pathNames.reverse();
|
|
88
89
|
if (pathNames.length > 0) {
|
|
89
|
-
|
|
90
|
+
grid.push([pathNames.join(" › ")]);
|
|
90
91
|
} else {
|
|
91
|
-
|
|
92
|
+
grid.push([store.name[nodeId]]);
|
|
92
93
|
}
|
|
93
94
|
|
|
94
95
|
const sizeFmt = chart.getColumnFormatter(chart._sizeName, "value");
|
|
95
|
-
|
|
96
|
+
grid.push(["Value", sizeFmt(store.value[nodeId])]);
|
|
96
97
|
|
|
97
98
|
if (chart._colorName && !isNaN(store.colorValue[nodeId])) {
|
|
98
99
|
const colorFmt = chart.getColumnFormatter(chart._colorName, "value");
|
|
99
|
-
|
|
100
|
-
`${chart._colorName}: ${colorFmt(store.colorValue[nodeId])}`,
|
|
101
|
-
);
|
|
100
|
+
grid.push([chart._colorName, colorFmt(store.colorValue[nodeId])]);
|
|
102
101
|
}
|
|
103
102
|
|
|
104
103
|
const rowIdx = store.leafRowIdx[nodeId];
|
|
@@ -117,20 +116,21 @@ export async function buildTreeTooltipLines(
|
|
|
117
116
|
}
|
|
118
117
|
|
|
119
118
|
if (typeof value === "number") {
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
119
|
+
grid.push([
|
|
120
|
+
name,
|
|
121
|
+
chart.getColumnFormatter(name, "value")(value),
|
|
122
|
+
]);
|
|
123
123
|
} else {
|
|
124
|
-
|
|
124
|
+
grid.push([name, String(value)]);
|
|
125
125
|
}
|
|
126
126
|
}
|
|
127
127
|
}
|
|
128
128
|
|
|
129
129
|
if (store.firstChild[nodeId] !== NULL_NODE) {
|
|
130
|
-
|
|
130
|
+
grid.push(["Children", String(store.childCount[nodeId])]);
|
|
131
131
|
}
|
|
132
132
|
|
|
133
|
-
return
|
|
133
|
+
return grid;
|
|
134
134
|
}
|
|
135
135
|
|
|
136
136
|
/**
|
|
@@ -150,19 +150,20 @@ export function showTreePinnedTooltip(
|
|
|
150
150
|
const cssWidth = chart._glManager?.cssWidth ?? 0;
|
|
151
151
|
const cssHeight = chart._glManager?.cssHeight ?? 0;
|
|
152
152
|
|
|
153
|
-
buildTreeTooltipLines(chart, nodeId).then((
|
|
153
|
+
buildTreeTooltipLines(chart, nodeId).then((grid) => {
|
|
154
154
|
if (chart._pinnedNodeId !== nodeId) {
|
|
155
155
|
return;
|
|
156
156
|
}
|
|
157
157
|
|
|
158
|
-
if (
|
|
158
|
+
if (grid.length === 0) {
|
|
159
159
|
return;
|
|
160
160
|
}
|
|
161
161
|
|
|
162
162
|
chart._tooltip.pin(
|
|
163
|
-
|
|
163
|
+
grid,
|
|
164
164
|
{ px: anchor.cx, py: anchor.cy },
|
|
165
165
|
{ cssWidth, cssHeight },
|
|
166
|
+
tooltipStyleOf(chart._pluginConfig),
|
|
166
167
|
);
|
|
167
168
|
});
|
|
168
169
|
|
|
@@ -13,6 +13,12 @@
|
|
|
13
13
|
import type { HeatmapChart } from "./heatmap";
|
|
14
14
|
import type { HeatmapCell } from "./heatmap-build";
|
|
15
15
|
import { renderCanvasTooltip } from "../../interaction/tooltip-controller";
|
|
16
|
+
import {
|
|
17
|
+
renderAxisHoverIndicators,
|
|
18
|
+
type AxisIndicator,
|
|
19
|
+
} from "../../interaction/axis-indicators";
|
|
20
|
+
import { computeNiceTicks, stepTickFormatter } from "../../layout/ticks";
|
|
21
|
+
import { tooltipStyleOf } from "../../interaction/tooltip-grid";
|
|
16
22
|
import type { CategoricalLevel } from "../../axis/categorical-axis";
|
|
17
23
|
|
|
18
24
|
/**
|
|
@@ -216,6 +222,8 @@ export function renderHeatmapTooltip(chart: HeatmapChart): void {
|
|
|
216
222
|
let xLevels: CategoricalLevel[];
|
|
217
223
|
let yLevels: CategoricalLevel[];
|
|
218
224
|
let xPositions: Float64Array | null;
|
|
225
|
+
let yPositions: Float64Array | null;
|
|
226
|
+
let xNumericDomain: { isDate?: boolean } | null;
|
|
219
227
|
let facetLabel: string | null = null;
|
|
220
228
|
|
|
221
229
|
if (chart._hoveredFacetIdx >= 0) {
|
|
@@ -228,6 +236,8 @@ export function renderHeatmapTooltip(chart: HeatmapChart): void {
|
|
|
228
236
|
xLevels = facet.pipeline.xLevels;
|
|
229
237
|
yLevels = facet.pipeline.yLevels;
|
|
230
238
|
xPositions = facet.pipeline.xPositions;
|
|
239
|
+
yPositions = facet.pipeline.yPositions;
|
|
240
|
+
xNumericDomain = facet.pipeline.xNumericDomain;
|
|
231
241
|
facetLabel = facet.label;
|
|
232
242
|
} else {
|
|
233
243
|
if (!chart._lastLayout) {
|
|
@@ -238,6 +248,8 @@ export function renderHeatmapTooltip(chart: HeatmapChart): void {
|
|
|
238
248
|
xLevels = chart._xLevels;
|
|
239
249
|
yLevels = chart._yLevels;
|
|
240
250
|
xPositions = chart._xPositions;
|
|
251
|
+
yPositions = chart._yPositions;
|
|
252
|
+
xNumericDomain = chart._xNumericDomain;
|
|
241
253
|
}
|
|
242
254
|
|
|
243
255
|
const cell = chart._hoveredCell;
|
|
@@ -247,9 +259,9 @@ export function renderHeatmapTooltip(chart: HeatmapChart): void {
|
|
|
247
259
|
// many pixels.
|
|
248
260
|
const pos = { px: chart._hoveredMouseX, py: chart._hoveredMouseY };
|
|
249
261
|
|
|
250
|
-
const
|
|
262
|
+
const grid: string[][] = [];
|
|
251
263
|
if (facetLabel) {
|
|
252
|
-
|
|
264
|
+
grid.push([facetLabel]);
|
|
253
265
|
}
|
|
254
266
|
|
|
255
267
|
const xPath =
|
|
@@ -261,23 +273,69 @@ export function renderHeatmapTooltip(chart: HeatmapChart): void {
|
|
|
261
273
|
: formatHierarchicalPath(xLevels, cell.xIdx);
|
|
262
274
|
const yPath = formatHierarchicalPath(yLevels, cell.yIdx);
|
|
263
275
|
if (xPath) {
|
|
264
|
-
|
|
276
|
+
grid.push([xPath]);
|
|
265
277
|
}
|
|
266
278
|
|
|
267
279
|
if (yPath) {
|
|
268
|
-
|
|
280
|
+
grid.push([yPath]);
|
|
269
281
|
}
|
|
270
282
|
|
|
271
283
|
const valueFmt = chart.getColumnFormatter(chart._columnSlots[0], "value");
|
|
272
|
-
|
|
284
|
+
grid.push(["Value", valueFmt(cell.value)]);
|
|
273
285
|
|
|
274
286
|
const theme = chart._resolveTheme();
|
|
287
|
+
const dpr = chart._glManager?.dpr ?? 1;
|
|
288
|
+
|
|
289
|
+
const cellPos = layout.dataToPixel(
|
|
290
|
+
xPositions ? xPositions[cell.xIdx] : cell.xIdx,
|
|
291
|
+
yPositions ? yPositions[cell.yIdx] : cell.yIdx,
|
|
292
|
+
);
|
|
293
|
+
const badgeX =
|
|
294
|
+
chart._xAxisMode.mode === "numeric" && xPositions
|
|
295
|
+
? (
|
|
296
|
+
chart.getColumnFormatter(chart._groupBy[0], "tick") ??
|
|
297
|
+
stepTickFormatter(
|
|
298
|
+
xNumericDomain?.isDate,
|
|
299
|
+
computeNiceTicks(layout.paddedXMin, layout.paddedXMax, 6),
|
|
300
|
+
)
|
|
301
|
+
)(xPositions[cell.xIdx])
|
|
302
|
+
: xPath;
|
|
303
|
+
|
|
304
|
+
const indicators: AxisIndicator[] = [];
|
|
305
|
+
if (badgeX) {
|
|
306
|
+
indicators.push({
|
|
307
|
+
side: "bottom",
|
|
308
|
+
px: cellPos.px,
|
|
309
|
+
py: cellPos.py,
|
|
310
|
+
text: badgeX,
|
|
311
|
+
});
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
if (yPath) {
|
|
315
|
+
indicators.push({
|
|
316
|
+
side: "left",
|
|
317
|
+
px: cellPos.px,
|
|
318
|
+
py: cellPos.py,
|
|
319
|
+
text: yPath,
|
|
320
|
+
});
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
renderAxisHoverIndicators(
|
|
324
|
+
chart._chromeCanvas,
|
|
325
|
+
layout,
|
|
326
|
+
theme,
|
|
327
|
+
dpr,
|
|
328
|
+
indicators,
|
|
329
|
+
chart._pluginConfig.tooltip_opacity,
|
|
330
|
+
);
|
|
331
|
+
|
|
275
332
|
renderCanvasTooltip(
|
|
276
333
|
chart._chromeCanvas,
|
|
277
334
|
pos,
|
|
278
|
-
|
|
335
|
+
grid,
|
|
279
336
|
layout,
|
|
280
337
|
theme,
|
|
281
|
-
|
|
338
|
+
dpr,
|
|
339
|
+
tooltipStyleOf(chart._pluginConfig),
|
|
282
340
|
);
|
|
283
341
|
}
|
|
@@ -47,7 +47,17 @@ const HEATMAP_Y_AXIS_OPTS: CategoricalYAxisOptions = {
|
|
|
47
47
|
skipLeafLevel: true,
|
|
48
48
|
};
|
|
49
49
|
|
|
50
|
-
import {
|
|
50
|
+
import {
|
|
51
|
+
gradientLegendAutoFit,
|
|
52
|
+
renderLegend,
|
|
53
|
+
renderLegendAt,
|
|
54
|
+
type LegendPaintView,
|
|
55
|
+
} from "../../axis/legend";
|
|
56
|
+
import {
|
|
57
|
+
legendRightGutter,
|
|
58
|
+
resolveLegendMode,
|
|
59
|
+
legendSidebarWidth,
|
|
60
|
+
} from "../../interaction/legend-controller";
|
|
51
61
|
import heatmapVert from "../../shaders/heatmap.vert.glsl";
|
|
52
62
|
import heatmapFrag from "../../shaders/heatmap.frag.glsl";
|
|
53
63
|
import { colorValueToT } from "../../theme/gradient";
|
|
@@ -110,6 +120,7 @@ export function renderHeatmapFrame(
|
|
|
110
120
|
// Measure both hierarchical axes *before* building the layout so the
|
|
111
121
|
// plot rect accounts for their footprints. Numeric axes get fixed
|
|
112
122
|
// gutters matching bar's branch (24px bottom, 55px left).
|
|
123
|
+
const rightExtra = legendRightGutter(chart._pluginConfig, true, 80, 0);
|
|
113
124
|
const estLeft = yNumeric
|
|
114
125
|
? 55
|
|
115
126
|
: measureCategoricalAxisWidth(yDomain, HEATMAP_Y_AXIS_OPTS);
|
|
@@ -117,7 +128,7 @@ export function renderHeatmapFrame(
|
|
|
117
128
|
? 24
|
|
118
129
|
: measureCategoricalAxisHeight(
|
|
119
130
|
xDomain,
|
|
120
|
-
Math.max(1, cssWidth - estLeft -
|
|
131
|
+
Math.max(1, cssWidth - estLeft - 30 - rightExtra),
|
|
121
132
|
);
|
|
122
133
|
|
|
123
134
|
const layout = new PlotLayout(cssWidth, cssHeight, {
|
|
@@ -126,6 +137,7 @@ export function renderHeatmapFrame(
|
|
|
126
137
|
hasLegend: true,
|
|
127
138
|
bottomExtra,
|
|
128
139
|
leftExtra: estLeft,
|
|
140
|
+
rightExtra,
|
|
129
141
|
});
|
|
130
142
|
chart._lastLayout = layout;
|
|
131
143
|
if (chart._zoomController) {
|
|
@@ -482,20 +494,58 @@ function paintHeatmapChromeOverlay(chart: HeatmapChart): void {
|
|
|
482
494
|
);
|
|
483
495
|
}
|
|
484
496
|
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
{
|
|
497
|
+
const legendMode = resolveLegendMode(chart._pluginConfig, 0);
|
|
498
|
+
if (legendMode === "none") {
|
|
499
|
+
chart._legend.clearPainted();
|
|
500
|
+
} else {
|
|
501
|
+
const colorDomain = {
|
|
491
502
|
min: chart._colorMin,
|
|
492
503
|
max: chart._colorMax,
|
|
493
504
|
label: chart._aggName,
|
|
494
|
-
}
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
505
|
+
};
|
|
506
|
+
const formatter = chart.getColumnFormatter(
|
|
507
|
+
chart._columnSlots[0],
|
|
508
|
+
"value",
|
|
509
|
+
);
|
|
510
|
+
const view: LegendPaintView = {
|
|
511
|
+
mode: legendMode === "floating" ? "floating" : "sidebar",
|
|
512
|
+
legend: chart._legend,
|
|
513
|
+
title: chart._aggName,
|
|
514
|
+
opacity: chart._pluginConfig.legend_opacity,
|
|
515
|
+
};
|
|
516
|
+
if (legendMode === "floating") {
|
|
517
|
+
renderLegendAt(
|
|
518
|
+
chart._chromeCanvas,
|
|
519
|
+
chart._legend.floatingBox(
|
|
520
|
+
chart._pluginConfig,
|
|
521
|
+
layout.cssWidth,
|
|
522
|
+
layout.cssHeight,
|
|
523
|
+
gradientLegendAutoFit(
|
|
524
|
+
chart._chromeCanvas,
|
|
525
|
+
theme,
|
|
526
|
+
colorDomain,
|
|
527
|
+
formatter,
|
|
528
|
+
chart._aggName,
|
|
529
|
+
),
|
|
530
|
+
),
|
|
531
|
+
colorDomain,
|
|
532
|
+
theme.gradientStops,
|
|
533
|
+
theme,
|
|
534
|
+
formatter,
|
|
535
|
+
view,
|
|
536
|
+
);
|
|
537
|
+
} else {
|
|
538
|
+
renderLegend(
|
|
539
|
+
chart._chromeCanvas,
|
|
540
|
+
layout,
|
|
541
|
+
colorDomain,
|
|
542
|
+
theme.gradientStops,
|
|
543
|
+
theme,
|
|
544
|
+
formatter,
|
|
545
|
+
view,
|
|
546
|
+
);
|
|
547
|
+
}
|
|
548
|
+
}
|
|
499
549
|
|
|
500
550
|
if (chart._hoveredCell) {
|
|
501
551
|
renderHeatmapTooltip(chart);
|
|
@@ -528,7 +578,8 @@ function renderFacetedHeatmap(
|
|
|
528
578
|
cssHeight,
|
|
529
579
|
xAxis: effectiveSharedX ? "outer" : "cell",
|
|
530
580
|
yAxis: effectiveSharedY ? "outer" : "cell",
|
|
531
|
-
hasLegend:
|
|
581
|
+
hasLegend: resolveLegendMode(chart._pluginConfig, 0) === "sidebar",
|
|
582
|
+
legendWidth: legendSidebarWidth(chart._pluginConfig, 96),
|
|
532
583
|
hasXLabel: chart._groupBy.length > 0,
|
|
533
584
|
hasYLabel: false,
|
|
534
585
|
gap: 8,
|
|
@@ -793,18 +844,34 @@ function renderFacetedHeatmapChromeOverlay(chart: HeatmapChart): void {
|
|
|
793
844
|
);
|
|
794
845
|
}
|
|
795
846
|
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
847
|
+
const legendMode = resolveLegendMode(chart._pluginConfig, 0);
|
|
848
|
+
const floating = legendMode === "floating";
|
|
849
|
+
const facetLayout = chart._facets[0].layout;
|
|
850
|
+
const legendAnchor = floating
|
|
851
|
+
? chart._legend.floatingBox(
|
|
852
|
+
chart._pluginConfig,
|
|
853
|
+
facetLayout.cssWidth,
|
|
854
|
+
facetLayout.cssHeight,
|
|
855
|
+
gradientLegendAutoFit(
|
|
856
|
+
chart._chromeCanvas,
|
|
857
|
+
theme,
|
|
858
|
+
{ min: chart._colorMin, max: chart._colorMax },
|
|
859
|
+
chart.getColumnFormatter(chart._columnSlots[0], "value"),
|
|
860
|
+
chart._aggName,
|
|
861
|
+
),
|
|
862
|
+
)
|
|
863
|
+
: grid.legendRect;
|
|
864
|
+
if (legendMode !== "none" && legendAnchor) {
|
|
800
865
|
renderLegendAt(
|
|
801
866
|
chart._chromeCanvas,
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
867
|
+
floating
|
|
868
|
+
? legendAnchor
|
|
869
|
+
: {
|
|
870
|
+
x: legendAnchor.x,
|
|
871
|
+
y: legendAnchor.y + 20,
|
|
872
|
+
width: legendAnchor.width,
|
|
873
|
+
height: Math.max(1, legendAnchor.height - 20),
|
|
874
|
+
},
|
|
808
875
|
{
|
|
809
876
|
min: chart._colorMin,
|
|
810
877
|
max: chart._colorMax,
|
|
@@ -813,7 +880,16 @@ function renderFacetedHeatmapChromeOverlay(chart: HeatmapChart): void {
|
|
|
813
880
|
theme.gradientStops,
|
|
814
881
|
theme,
|
|
815
882
|
chart.getColumnFormatter(chart._columnSlots[0], "value"),
|
|
883
|
+
{
|
|
884
|
+
mode: floating ? "floating" : "sidebar",
|
|
885
|
+
legend: chart._legend,
|
|
886
|
+
title: chart._aggName,
|
|
887
|
+
sidebarGutter: floating ? undefined : grid.legendRect?.width,
|
|
888
|
+
opacity: chart._pluginConfig.legend_opacity,
|
|
889
|
+
},
|
|
816
890
|
);
|
|
891
|
+
} else {
|
|
892
|
+
chart._legend.clearPainted();
|
|
817
893
|
}
|
|
818
894
|
|
|
819
895
|
if (chart._hoveredCell) {
|
package/src/ts/charts/map/map.ts
CHANGED
|
@@ -22,7 +22,7 @@ import type { Canvas2D, Context2D } from "../canvas-types";
|
|
|
22
22
|
import type { ZoomConfig } from "../../interaction/zoom-controller";
|
|
23
23
|
import type { PluginConfig } from "../chart";
|
|
24
24
|
import { TileLayer } from "../../map/tile-layer";
|
|
25
|
-
import {
|
|
25
|
+
import { TILE_SOURCES } from "../../map/tile-source";
|
|
26
26
|
import { lonLatToMercator } from "../../map/mercator";
|
|
27
27
|
import { getScaledContext } from "../../axis/canvas";
|
|
28
28
|
|
|
@@ -76,7 +76,7 @@ export class MapChart extends CartesianChart {
|
|
|
76
76
|
if (this._glManager) {
|
|
77
77
|
this._tileLayer.setSource(
|
|
78
78
|
this._glManager.gl,
|
|
79
|
-
|
|
79
|
+
TILE_SOURCES.sourceFor(cfg.map_tile_provider),
|
|
80
80
|
);
|
|
81
81
|
}
|
|
82
82
|
|
|
@@ -97,9 +97,7 @@ export class MapChart extends CartesianChart {
|
|
|
97
97
|
if (!this._tileLayer.source) {
|
|
98
98
|
this._tileLayer.setSource(
|
|
99
99
|
glManager.gl,
|
|
100
|
-
|
|
101
|
-
this._pluginConfig.map_tile_provider as TileProviderId,
|
|
102
|
-
),
|
|
100
|
+
TILE_SOURCES.sourceFor(this._pluginConfig.map_tile_provider),
|
|
103
101
|
);
|
|
104
102
|
this._tileLayer.setAlpha(this._pluginConfig.map_tile_alpha);
|
|
105
103
|
}
|
|
@@ -336,6 +336,14 @@ export interface SeriesPipelineResult {
|
|
|
336
336
|
* is the position. Indexed by `catIdx` (0..numCategories-1).
|
|
337
337
|
*/
|
|
338
338
|
categoryPositions: Float64Array | null;
|
|
339
|
+
|
|
340
|
+
/**
|
|
341
|
+
* One entry per (aggregate × split), in `k * P + p` order. INVARIANT:
|
|
342
|
+
* `series.length === aggregates.length * splitPrefixes.length` —
|
|
343
|
+
* consumers index `series[k * P]` directly (glyph runs, the
|
|
344
|
+
* `domain_mode: "expand"` axis signature), so an empty `series` MUST
|
|
345
|
+
* be paired with empty `aggregates` / `splitPrefixes`.
|
|
346
|
+
*/
|
|
339
347
|
series: SeriesInfo[];
|
|
340
348
|
|
|
341
349
|
/**
|
|
@@ -477,10 +485,14 @@ export function buildSeriesPipeline(
|
|
|
477
485
|
);
|
|
478
486
|
|
|
479
487
|
if (numCategories === 0) {
|
|
488
|
+
// NOT `aggregates` / `splitPrefixes`: `series` is empty here, and
|
|
489
|
+
// carrying the non-empty lists would break the `series.length ===
|
|
490
|
+
// M * P` invariant — `series[k * P]` consumers then read
|
|
491
|
+
// `undefined` (the `loadAndRender failed … reading 'axis'` crash).
|
|
492
|
+
// Same shape as the `aggregates.length === 0` return above, which
|
|
493
|
+
// every downstream path already renders as an empty chart.
|
|
480
494
|
return {
|
|
481
495
|
...empty,
|
|
482
|
-
aggregates,
|
|
483
|
-
splitPrefixes,
|
|
484
496
|
rowPaths,
|
|
485
497
|
rowOffset,
|
|
486
498
|
};
|