@perspective-dev/viewer-charts 5.5.0 → 5.6.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/README.md +59 -0
- package/dist/cdn/perspective-viewer-charts.js +1 -1
- package/dist/cdn/perspective-viewer-charts.js.map +4 -4
- package/dist/esm/event-detail.d.ts +1 -1
- package/dist/esm/perspective-viewer-charts.js +1 -1
- package/dist/esm/perspective-viewer-charts.js.map +3 -3
- package/dist/esm/plugin/format-defaults.d.ts +1 -1
- package/dist/esm/plugin/plugin.d.ts +1 -1
- package/dist/esm/webgl/context-manager.d.ts +6 -6
- package/package.json +32 -7
- package/src/ts/charts/cartesian/glyphs/lines.ts +3 -33
- package/src/ts/charts/chart-base.ts +1 -1
- package/src/ts/event-detail.ts +1 -1
- package/src/ts/plugin/format-defaults.ts +1 -1
- package/src/ts/plugin/plugin.ts +25 -17
- package/src/ts/render/scheduler.ts +4 -6
- package/src/ts/webgl/context-manager.ts +16 -14
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { DateFormatConfig, NumberFormatConfig } from "@perspective-dev/viewer/
|
|
1
|
+
import type { DateFormatConfig, NumberFormatConfig } from "@perspective-dev/viewer/column-format";
|
|
2
2
|
/**
|
|
3
3
|
* The chart plugin's default per-column formats, declared to the viewer in
|
|
4
4
|
* `column_config_schema()` and applied by the render path under sparse or
|
|
@@ -112,7 +112,7 @@ export declare class HTMLPerspectiveViewerWebGLPluginElement extends HTMLElement
|
|
|
112
112
|
group_by?: string[];
|
|
113
113
|
split_by?: string[];
|
|
114
114
|
group_rollup_mode?: string;
|
|
115
|
-
}): {
|
|
115
|
+
}, _plugin_config?: Record<string, unknown> | null, role?: "column" | "group_by" | "split_by"): {
|
|
116
116
|
fields: (Record<string, unknown> & {
|
|
117
117
|
kind: string;
|
|
118
118
|
})[];
|
|
@@ -141,17 +141,17 @@ export declare class WebGLContextManager {
|
|
|
141
141
|
*/
|
|
142
142
|
setFrameCallback(cb: ((bitmap: ImageBitmap) => void) | null): void;
|
|
143
143
|
/**
|
|
144
|
-
* Ship the current canvas contents as an `ImageBitmap` to
|
|
145
|
-
* when a frame callback is registered and the surface is an
|
|
144
|
+
* Ship a copy of the current canvas contents as an `ImageBitmap` to
|
|
145
|
+
* the host when a frame callback is registered and the surface is an
|
|
146
146
|
* `OffscreenCanvas`. Otherwise no-op (direct mode paints straight to
|
|
147
147
|
* the visible drawing buffer).
|
|
148
148
|
*/
|
|
149
|
-
endFrame(): void
|
|
149
|
+
endFrame(): Promise<void>;
|
|
150
150
|
/**
|
|
151
151
|
* Resolve when every GL command submitted up to this call has been
|
|
152
|
-
* executed by the GPU
|
|
153
|
-
*
|
|
154
|
-
*
|
|
152
|
+
* executed by the GPU, polling a WebGL2 fence with a flush on every
|
|
153
|
+
* poll and falling back to a blocking `gl.finish()` on WebGL1 or on
|
|
154
|
+
* `WAIT_FAILED`.
|
|
155
155
|
*/
|
|
156
156
|
awaitGpuFence(): Promise<void>;
|
|
157
157
|
ensureBufferCapacity(totalRows: number): void;
|
package/package.json
CHANGED
|
@@ -1,7 +1,32 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@perspective-dev/viewer-charts",
|
|
3
|
-
"version": "5.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "5.6.0",
|
|
4
|
+
"description": "WebGL charts for `<perspective-viewer>`: bar, line, scatter, heatmap, treemap, sunburst, candlestick and map plugins for large, streaming datasets.",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"perspective",
|
|
7
|
+
"datagrid",
|
|
8
|
+
"data-grid",
|
|
9
|
+
"pivot-table",
|
|
10
|
+
"pivot",
|
|
11
|
+
"charts",
|
|
12
|
+
"visualization",
|
|
13
|
+
"dashboard",
|
|
14
|
+
"analytics",
|
|
15
|
+
"bi",
|
|
16
|
+
"streaming",
|
|
17
|
+
"real-time",
|
|
18
|
+
"wasm",
|
|
19
|
+
"webassembly",
|
|
20
|
+
"arrow",
|
|
21
|
+
"webgl",
|
|
22
|
+
"chart",
|
|
23
|
+
"heatmap",
|
|
24
|
+
"treemap",
|
|
25
|
+
"candlestick",
|
|
26
|
+
"map",
|
|
27
|
+
"web-component"
|
|
28
|
+
],
|
|
29
|
+
"homepage": "https://perspective-dev.github.io",
|
|
5
30
|
"unpkg": "./dist/cdn/perspective-viewer-charts.js",
|
|
6
31
|
"jsdelivr": "./dist/cdn/perspective-viewer-charts.js",
|
|
7
32
|
"type": "module",
|
|
@@ -31,14 +56,14 @@
|
|
|
31
56
|
"author": "",
|
|
32
57
|
"license": "Apache-2.0",
|
|
33
58
|
"devDependencies": {
|
|
59
|
+
"@perspective-dev/client": "^5.6.0",
|
|
60
|
+
"@perspective-dev/viewer": "^5.6.0",
|
|
61
|
+
"@perspective-dev/esbuild-plugin": "^5.6.0",
|
|
62
|
+
"@perspective-dev/test": "^5.6.0",
|
|
34
63
|
"@types/node": ">=22",
|
|
35
64
|
"lightningcss": "^1.29.0",
|
|
36
65
|
"typescript": ">=5 <6",
|
|
37
|
-
"webpack-glsl-minify": "1.5.0"
|
|
38
|
-
"@perspective-dev/client": "^5.5.0",
|
|
39
|
-
"@perspective-dev/test": "^5.5.0",
|
|
40
|
-
"@perspective-dev/esbuild-plugin": "^5.5.0",
|
|
41
|
-
"@perspective-dev/viewer": "^5.5.0"
|
|
66
|
+
"webpack-glsl-minify": "1.5.0"
|
|
42
67
|
},
|
|
43
68
|
"scripts": {
|
|
44
69
|
"build": "node ./build.mjs",
|
|
@@ -20,7 +20,7 @@ import {
|
|
|
20
20
|
} from "../../../webgl/instanced-attrs";
|
|
21
21
|
import { compileProgram } from "../../../webgl/program-cache";
|
|
22
22
|
import { colorRangePivot } from "../../../theme/gradient";
|
|
23
|
-
import {
|
|
23
|
+
import { buildPointRowTooltipLines } from "../tooltip-lines";
|
|
24
24
|
import lineVert from "../../../shaders/line.vert.glsl";
|
|
25
25
|
import lineFrag from "../../../shaders/line.frag.glsl";
|
|
26
26
|
|
|
@@ -122,41 +122,11 @@ export class LineGlyph implements Glyph {
|
|
|
122
122
|
|
|
123
123
|
// helpers
|
|
124
124
|
|
|
125
|
-
|
|
125
|
+
buildTooltipLines(
|
|
126
126
|
chart: CartesianChart,
|
|
127
127
|
flatIdx: number,
|
|
128
128
|
): Promise<string[][]> {
|
|
129
|
-
|
|
130
|
-
if (!chart._xData || !chart._yData) {
|
|
131
|
-
return grid;
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
if (chart._splitGroups.length > 0 && chart._seriesCapacity > 0) {
|
|
135
|
-
const seriesIdx = Math.floor(flatIdx / chart._seriesCapacity);
|
|
136
|
-
const sg = chart._splitGroups[seriesIdx];
|
|
137
|
-
if (sg) {
|
|
138
|
-
grid.push([sg.prefix]);
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
const xVal = chart._xData[flatIdx];
|
|
143
|
-
const yVal = chart._yData[flatIdx];
|
|
144
|
-
|
|
145
|
-
const xType = chart._columnTypes[chart._xLabel] || "";
|
|
146
|
-
const xIsDate = xType === "date" || xType === "datetime";
|
|
147
|
-
const xFormatted = xIsDate
|
|
148
|
-
? formatDateTickValue(xVal)
|
|
149
|
-
: formatTickValue(xVal);
|
|
150
|
-
grid.push([chart._xLabel || "Row", xFormatted]);
|
|
151
|
-
|
|
152
|
-
const yType = chart._columnTypes[chart._yLabel] || "";
|
|
153
|
-
const yIsDate = yType === "date" || yType === "datetime";
|
|
154
|
-
const yFormatted = yIsDate
|
|
155
|
-
? formatDateTickValue(yVal)
|
|
156
|
-
: formatTickValue(yVal);
|
|
157
|
-
grid.push([chart._yLabel, yFormatted]);
|
|
158
|
-
|
|
159
|
-
return grid;
|
|
129
|
+
return buildPointRowTooltipLines(chart, flatIdx);
|
|
160
130
|
}
|
|
161
131
|
|
|
162
132
|
tooltipOptions() {
|
|
@@ -18,7 +18,7 @@ import {
|
|
|
18
18
|
sourceColumn,
|
|
19
19
|
type NumberFormatConfig,
|
|
20
20
|
type DateFormatConfig,
|
|
21
|
-
} from "@perspective-dev/viewer/
|
|
21
|
+
} from "@perspective-dev/viewer/column-format";
|
|
22
22
|
import type { ColumnDataMap } from "../data/view-reader";
|
|
23
23
|
import { LazyRowFetcher } from "../data/lazy-row";
|
|
24
24
|
import {
|
package/src/ts/event-detail.ts
CHANGED
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
import type {
|
|
14
14
|
DateFormatConfig,
|
|
15
15
|
NumberFormatConfig,
|
|
16
|
-
} from "@perspective-dev/viewer/
|
|
16
|
+
} from "@perspective-dev/viewer/column-format";
|
|
17
17
|
|
|
18
18
|
/**
|
|
19
19
|
* The chart plugin's default per-column formats, declared to the viewer in
|
package/src/ts/plugin/plugin.ts
CHANGED
|
@@ -255,6 +255,24 @@ const GLOBAL_STYLES = (() => {
|
|
|
255
255
|
*/
|
|
256
256
|
let BLIT_MODE: "direct" | "blit" = RENDER_BLIT_MODE;
|
|
257
257
|
|
|
258
|
+
/**
|
|
259
|
+
* A column's `date_format` / `number_format` controls, which are the only ones
|
|
260
|
+
* that apply to a value rendered as an axis tick or tooltip label.
|
|
261
|
+
*/
|
|
262
|
+
function format_fields(
|
|
263
|
+
column_type: string,
|
|
264
|
+
): Array<Record<string, unknown> & { kind: string }> {
|
|
265
|
+
if (column_type === "integer" || column_type === "float") {
|
|
266
|
+
return [{ kind: "NumberFormat", default: CHART_NUMBER_DEFAULTS }];
|
|
267
|
+
} else if (column_type === "datetime") {
|
|
268
|
+
return [{ kind: "DatetimeFormat", default: CHART_DATETIME_DEFAULTS }];
|
|
269
|
+
} else if (column_type === "date") {
|
|
270
|
+
return [{ kind: "DatetimeFormat", default: CHART_DATE_DEFAULTS }];
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
return [];
|
|
274
|
+
}
|
|
275
|
+
|
|
258
276
|
export class HTMLPerspectiveViewerWebGLPluginElement
|
|
259
277
|
extends HTMLElement
|
|
260
278
|
implements IPerspectiveViewerPlugin
|
|
@@ -601,9 +619,15 @@ export class HTMLPerspectiveViewerWebGLPluginElement
|
|
|
601
619
|
split_by?: string[];
|
|
602
620
|
group_rollup_mode?: string;
|
|
603
621
|
},
|
|
622
|
+
_plugin_config?: Record<string, unknown> | null,
|
|
623
|
+
role?: "column" | "group_by" | "split_by",
|
|
604
624
|
) {
|
|
605
625
|
const fields: Array<Record<string, unknown> & { kind: string }> = [];
|
|
606
626
|
|
|
627
|
+
if (role === "group_by" || role === "split_by") {
|
|
628
|
+
return { fields: format_fields(column_type) };
|
|
629
|
+
}
|
|
630
|
+
|
|
607
631
|
if (group === "Color") {
|
|
608
632
|
const numeric_gradient =
|
|
609
633
|
this._chartType.category === "Hierarchical Charts"
|
|
@@ -721,23 +745,7 @@ export class HTMLPerspectiveViewerWebGLPluginElement
|
|
|
721
745
|
|
|
722
746
|
// Per-column formatter widgets. Surfaced for every chart type so
|
|
723
747
|
// axes / tooltips / legends honor the user's format choice.
|
|
724
|
-
|
|
725
|
-
fields.push({
|
|
726
|
-
kind: "NumberFormat",
|
|
727
|
-
default: CHART_NUMBER_DEFAULTS,
|
|
728
|
-
});
|
|
729
|
-
} else if (column_type === "datetime") {
|
|
730
|
-
fields.push({
|
|
731
|
-
kind: "DatetimeFormat",
|
|
732
|
-
default: CHART_DATETIME_DEFAULTS,
|
|
733
|
-
});
|
|
734
|
-
} else if (column_type === "date") {
|
|
735
|
-
fields.push({
|
|
736
|
-
kind: "DatetimeFormat",
|
|
737
|
-
default: CHART_DATE_DEFAULTS,
|
|
738
|
-
});
|
|
739
|
-
}
|
|
740
|
-
|
|
748
|
+
fields.push(...format_fields(column_type));
|
|
741
749
|
return { fields };
|
|
742
750
|
}
|
|
743
751
|
|
|
@@ -380,18 +380,16 @@ async function present(entry: Entry): Promise<void> {
|
|
|
380
380
|
// on resize. Failures fall through to the same present-failed
|
|
381
381
|
// path as the fence.
|
|
382
382
|
entry.render2D();
|
|
383
|
-
entry.glManager.endFrame();
|
|
383
|
+
await entry.glManager.endFrame();
|
|
384
384
|
for (const w of entry.waiters) {
|
|
385
385
|
w.resolve();
|
|
386
386
|
}
|
|
387
387
|
} catch (err) {
|
|
388
388
|
console.error("scheduler: present failed", err);
|
|
389
|
-
// Still
|
|
390
|
-
//
|
|
391
|
-
// it would leave a stale image bound to a context the host
|
|
392
|
-
// already considers presented.
|
|
389
|
+
// Still ship a frame so the host does not hold a stale image for
|
|
390
|
+
// a context it already considers presented.
|
|
393
391
|
try {
|
|
394
|
-
entry.glManager.endFrame();
|
|
392
|
+
await entry.glManager.endFrame();
|
|
395
393
|
} catch {
|
|
396
394
|
// Swallow: already in a failure path.
|
|
397
395
|
}
|
|
@@ -156,8 +156,8 @@ export class WebGLContextManager {
|
|
|
156
156
|
this._cssHeight = cssHeight;
|
|
157
157
|
this._dpr = dpr;
|
|
158
158
|
|
|
159
|
-
const width = Math.round(cssWidth * dpr);
|
|
160
|
-
const height = Math.round(cssHeight * dpr);
|
|
159
|
+
const width = Math.max(1, Math.round(cssWidth * dpr));
|
|
160
|
+
const height = Math.max(1, Math.round(cssHeight * dpr));
|
|
161
161
|
const canvas = this._ctx.canvas;
|
|
162
162
|
if (canvas.width !== width || canvas.height !== height) {
|
|
163
163
|
canvas.width = width;
|
|
@@ -284,19 +284,19 @@ export class WebGLContextManager {
|
|
|
284
284
|
}
|
|
285
285
|
|
|
286
286
|
/**
|
|
287
|
-
* Ship the current canvas contents as an `ImageBitmap` to
|
|
288
|
-
* when a frame callback is registered and the surface is an
|
|
287
|
+
* Ship a copy of the current canvas contents as an `ImageBitmap` to
|
|
288
|
+
* the host when a frame callback is registered and the surface is an
|
|
289
289
|
* `OffscreenCanvas`. Otherwise no-op (direct mode paints straight to
|
|
290
290
|
* the visible drawing buffer).
|
|
291
291
|
*/
|
|
292
|
-
endFrame(): void {
|
|
292
|
+
async endFrame(): Promise<void> {
|
|
293
293
|
if (!this._frameCallback) {
|
|
294
294
|
return;
|
|
295
295
|
}
|
|
296
296
|
|
|
297
297
|
// A context lost mid-frame (our own `destroy`, or a browser
|
|
298
|
-
// eviction between paint and present) makes
|
|
299
|
-
//
|
|
298
|
+
// eviction between paint and present) makes the bitmap copy
|
|
299
|
+
// throw. Bail quietly.
|
|
300
300
|
if (this.isContextLost()) {
|
|
301
301
|
return;
|
|
302
302
|
}
|
|
@@ -311,15 +311,15 @@ export class WebGLContextManager {
|
|
|
311
311
|
return;
|
|
312
312
|
}
|
|
313
313
|
|
|
314
|
-
const bitmap = (canvas as OffscreenCanvas)
|
|
314
|
+
const bitmap = await createImageBitmap(canvas as OffscreenCanvas);
|
|
315
315
|
this._frameCallback(bitmap);
|
|
316
316
|
}
|
|
317
317
|
|
|
318
318
|
/**
|
|
319
319
|
* Resolve when every GL command submitted up to this call has been
|
|
320
|
-
* executed by the GPU
|
|
321
|
-
*
|
|
322
|
-
*
|
|
320
|
+
* executed by the GPU, polling a WebGL2 fence with a flush on every
|
|
321
|
+
* poll and falling back to a blocking `gl.finish()` on WebGL1 or on
|
|
322
|
+
* `WAIT_FAILED`.
|
|
323
323
|
*/
|
|
324
324
|
async awaitGpuFence(): Promise<void> {
|
|
325
325
|
if (!this._ctx.isWebGL2) {
|
|
@@ -335,9 +335,12 @@ export class WebGLContextManager {
|
|
|
335
335
|
}
|
|
336
336
|
|
|
337
337
|
try {
|
|
338
|
-
let flags: GLbitfield = gl.SYNC_FLUSH_COMMANDS_BIT;
|
|
339
338
|
while (true) {
|
|
340
|
-
const status = gl.clientWaitSync(
|
|
339
|
+
const status = gl.clientWaitSync(
|
|
340
|
+
fence,
|
|
341
|
+
gl.SYNC_FLUSH_COMMANDS_BIT,
|
|
342
|
+
0,
|
|
343
|
+
);
|
|
341
344
|
if (
|
|
342
345
|
status === gl.ALREADY_SIGNALED ||
|
|
343
346
|
status === gl.CONDITION_SATISFIED
|
|
@@ -350,7 +353,6 @@ export class WebGLContextManager {
|
|
|
350
353
|
return;
|
|
351
354
|
}
|
|
352
355
|
|
|
353
|
-
flags = 0;
|
|
354
356
|
await this._yieldToTask();
|
|
355
357
|
|
|
356
358
|
if (this._destroyed) {
|