@astryxdesign/lab 0.1.4-canary.37c19d3 → 0.1.4-canary.39ffd21
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/ChartV2/Chart.d.ts +32 -0
- package/dist/ChartV2/Chart.d.ts.map +1 -0
- package/dist/ChartV2/Chart.js +281 -0
- package/dist/ChartV2/ChartAxis.d.ts +36 -0
- package/dist/ChartV2/ChartAxis.d.ts.map +1 -0
- package/dist/ChartV2/ChartAxis.js +161 -0
- package/dist/ChartV2/ChartGrid.d.ts +20 -0
- package/dist/ChartV2/ChartGrid.d.ts.map +1 -0
- package/dist/ChartV2/ChartGrid.js +76 -0
- package/dist/ChartV2/ChartLegend.d.ts +14 -0
- package/dist/ChartV2/ChartLegend.d.ts.map +1 -0
- package/dist/ChartV2/ChartLegend.js +54 -0
- package/dist/ChartV2/ChartSwatch.d.ts +36 -0
- package/dist/ChartV2/ChartSwatch.d.ts.map +1 -0
- package/dist/ChartV2/ChartSwatch.js +82 -0
- package/dist/ChartV2/ChartTooltip.d.ts +46 -0
- package/dist/ChartV2/ChartTooltip.d.ts.map +1 -0
- package/dist/ChartV2/ChartTooltip.js +267 -0
- package/dist/ChartV2/ChartV2Context.d.ts +4 -0
- package/dist/ChartV2/ChartV2Context.d.ts.map +1 -0
- package/dist/ChartV2/ChartV2Context.js +12 -0
- package/dist/ChartV2/index.d.ts +13 -0
- package/dist/ChartV2/index.d.ts.map +1 -0
- package/dist/ChartV2/index.js +10 -0
- package/dist/ChartV2/layout.d.ts +25 -0
- package/dist/ChartV2/layout.d.ts.map +1 -0
- package/dist/ChartV2/layout.js +195 -0
- package/dist/ChartV2/legend.d.ts +18 -0
- package/dist/ChartV2/legend.d.ts.map +1 -0
- package/dist/ChartV2/legend.js +24 -0
- package/dist/ChartV2/marks/area.d.ts +19 -0
- package/dist/ChartV2/marks/area.d.ts.map +1 -0
- package/dist/ChartV2/marks/area.js +109 -0
- package/dist/ChartV2/marks/band.d.ts +9 -0
- package/dist/ChartV2/marks/band.d.ts.map +1 -0
- package/dist/ChartV2/marks/band.js +59 -0
- package/dist/ChartV2/marks/bar.d.ts +17 -0
- package/dist/ChartV2/marks/bar.d.ts.map +1 -0
- package/dist/ChartV2/marks/bar.js +120 -0
- package/dist/ChartV2/marks/candlestick.d.ts +15 -0
- package/dist/ChartV2/marks/candlestick.d.ts.map +1 -0
- package/dist/ChartV2/marks/candlestick.js +83 -0
- package/dist/ChartV2/marks/dot.d.ts +15 -0
- package/dist/ChartV2/marks/dot.d.ts.map +1 -0
- package/dist/ChartV2/marks/dot.js +65 -0
- package/dist/ChartV2/marks/dotGL.d.ts +16 -0
- package/dist/ChartV2/marks/dotGL.d.ts.map +1 -0
- package/dist/ChartV2/marks/dotGL.js +178 -0
- package/dist/ChartV2/marks/dotGLInteractive.d.ts +30 -0
- package/dist/ChartV2/marks/dotGLInteractive.d.ts.map +1 -0
- package/dist/ChartV2/marks/dotGLInteractive.js +464 -0
- package/dist/ChartV2/marks/errorBar.d.ts +27 -0
- package/dist/ChartV2/marks/errorBar.d.ts.map +1 -0
- package/dist/ChartV2/marks/errorBar.js +87 -0
- package/dist/ChartV2/marks/heatmapGL.d.ts +23 -0
- package/dist/ChartV2/marks/heatmapGL.d.ts.map +1 -0
- package/dist/ChartV2/marks/heatmapGL.js +242 -0
- package/dist/ChartV2/marks/index.d.ts +13 -0
- package/dist/ChartV2/marks/index.d.ts.map +1 -0
- package/dist/ChartV2/marks/index.js +14 -0
- package/dist/ChartV2/marks/line.d.ts +18 -0
- package/dist/ChartV2/marks/line.d.ts.map +1 -0
- package/dist/ChartV2/marks/line.js +76 -0
- package/dist/ChartV2/marks/referenceLine.d.ts +35 -0
- package/dist/ChartV2/marks/referenceLine.d.ts.map +1 -0
- package/dist/ChartV2/marks/referenceLine.js +186 -0
- package/dist/ChartV2/marks/streamGL.d.ts +36 -0
- package/dist/ChartV2/marks/streamGL.d.ts.map +1 -0
- package/dist/ChartV2/marks/streamGL.js +204 -0
- package/dist/ChartV2/tooltip.d.ts +38 -0
- package/dist/ChartV2/tooltip.d.ts.map +1 -0
- package/dist/ChartV2/tooltip.js +54 -0
- package/dist/ChartV2/types.d.ts +115 -0
- package/dist/ChartV2/types.d.ts.map +1 -0
- package/dist/ChartV2/types.js +35 -0
- package/dist/CodeEditor/CodeEditor.d.ts +1 -4
- package/dist/CodeEditor/CodeEditor.d.ts.map +1 -1
- package/dist/CodeEditor/CodeEditor.js +0 -3
- package/dist/Sankey/SankeyChart.d.ts.map +1 -1
- package/dist/Sankey/SankeyChart.js +0 -3
- package/dist/Schedule/ListView.d.ts.map +1 -1
- package/dist/Schedule/ListView.js +4 -7
- package/dist/Schedule/MonthlyView.js +2 -3
- package/dist/Schedule/Schedule.d.ts +1 -3
- package/dist/Schedule/Schedule.d.ts.map +1 -1
- package/dist/Schedule/Schedule.js +4 -9
- package/dist/Schedule/TimeGridView.js +2 -3
- package/dist/Schedule/context.d.ts +0 -2
- package/dist/Schedule/context.d.ts.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -5
- package/dist/lab.css +15 -6
- package/package.json +2 -2
- package/src/ChartV2/Chart.tsx +348 -0
- package/src/ChartV2/ChartAxis.tsx +209 -0
- package/src/ChartV2/ChartGrid.tsx +99 -0
- package/src/ChartV2/ChartLegend.tsx +69 -0
- package/src/ChartV2/ChartSwatch.doc.mjs +17 -0
- package/src/ChartV2/ChartSwatch.tsx +86 -0
- package/src/ChartV2/ChartTooltip.tsx +382 -0
- package/src/ChartV2/ChartV2.doc.mjs +16 -0
- package/src/ChartV2/ChartV2Context.ts +15 -0
- package/src/ChartV2/index.ts +62 -0
- package/src/ChartV2/layout.ts +223 -0
- package/src/ChartV2/legend.ts +32 -0
- package/src/ChartV2/marks/area.tsx +122 -0
- package/src/ChartV2/marks/band.tsx +70 -0
- package/src/ChartV2/marks/bar.tsx +159 -0
- package/src/ChartV2/marks/candlestick.tsx +106 -0
- package/src/ChartV2/marks/dot.tsx +64 -0
- package/src/ChartV2/marks/dotGL.tsx +202 -0
- package/src/ChartV2/marks/dotGLInteractive.tsx +546 -0
- package/src/ChartV2/marks/errorBar.tsx +89 -0
- package/src/ChartV2/marks/heatmapGL.tsx +299 -0
- package/src/ChartV2/marks/index.ts +14 -0
- package/src/ChartV2/marks/line.tsx +94 -0
- package/src/ChartV2/marks/referenceLine.tsx +224 -0
- package/src/ChartV2/marks/streamGL.tsx +251 -0
- package/src/ChartV2/tooltip.ts +70 -0
- package/src/ChartV2/types.ts +129 -0
- package/src/CodeEditor/CodeEditor.tsx +0 -5
- package/src/Sankey/SankeyChart.tsx +0 -3
- package/src/Schedule/ListView.tsx +2 -5
- package/src/Schedule/MonthlyView.tsx +2 -2
- package/src/Schedule/Schedule.test.tsx +0 -39
- package/src/Schedule/Schedule.tsx +0 -8
- package/src/Schedule/TimeGridView.tsx +2 -2
- package/src/Schedule/context.tsx +0 -2
- package/src/index.ts +46 -5
- package/dist/InfoTip/InfoTip.d.ts +0 -58
- package/dist/InfoTip/InfoTip.d.ts.map +0 -1
- package/dist/InfoTip/InfoTip.js +0 -91
- package/dist/InfoTip/index.d.ts +0 -6
- package/dist/InfoTip/index.d.ts.map +0 -1
- package/dist/InfoTip/index.js +0 -8
- package/src/CodeEditor/CodeEditor.test.tsx +0 -21
- package/src/InfoTip/InfoTip.doc.mjs +0 -97
- package/src/InfoTip/InfoTip.test.tsx +0 -169
- package/src/InfoTip/InfoTip.tsx +0 -155
- package/src/InfoTip/index.ts +0 -10
- package/src/Sankey/SankeyChart.test.tsx +0 -77
|
@@ -0,0 +1,464 @@
|
|
|
1
|
+
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @file marks/dotGLInteractive.tsx
|
|
5
|
+
* @output WebGL scatter with GPU color-picking for O(1) nearest-point hover
|
|
6
|
+
*
|
|
7
|
+
* Each point gets a unique color encoding its index. On hover, readPixels
|
|
8
|
+
* from an offscreen framebuffer decodes the hovered index — no JS iteration.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { useRef, useEffect, useCallback, useState, useMemo } from 'react';
|
|
12
|
+
import { hexToGL } from "../../Chart/webgl.js";
|
|
13
|
+
import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
|
|
14
|
+
/** Encode a point index as an RGB color (supports up to 16.7M points) */
|
|
15
|
+
function indexToColor(i) {
|
|
16
|
+
const id = i + 1;
|
|
17
|
+
return [(id >> 16 & 0xff) / 255, (id >> 8 & 0xff) / 255, (id & 0xff) / 255];
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/** Decode an RGB pixel back to a point index. Returns -1 for background. */
|
|
21
|
+
function colorToIndex(r, g, b) {
|
|
22
|
+
if (r === 0 && g === 0 && b === 0) {
|
|
23
|
+
return -1;
|
|
24
|
+
}
|
|
25
|
+
return (r << 16 | g << 8 | b) - 1;
|
|
26
|
+
}
|
|
27
|
+
function compileShader(gl, type, src) {
|
|
28
|
+
const s = gl.createShader(type);
|
|
29
|
+
if (!s) {
|
|
30
|
+
return null;
|
|
31
|
+
}
|
|
32
|
+
gl.shaderSource(s, src);
|
|
33
|
+
gl.compileShader(s);
|
|
34
|
+
if (!gl.getShaderParameter(s, gl.COMPILE_STATUS)) {
|
|
35
|
+
gl.deleteShader(s);
|
|
36
|
+
return null;
|
|
37
|
+
}
|
|
38
|
+
return s;
|
|
39
|
+
}
|
|
40
|
+
function linkProgram(gl, vert, frag) {
|
|
41
|
+
const vs = compileShader(gl, gl.VERTEX_SHADER, vert);
|
|
42
|
+
const fs = compileShader(gl, gl.FRAGMENT_SHADER, frag);
|
|
43
|
+
if (!vs || !fs) {
|
|
44
|
+
return null;
|
|
45
|
+
}
|
|
46
|
+
const p = gl.createProgram();
|
|
47
|
+
if (!p) {
|
|
48
|
+
return null;
|
|
49
|
+
}
|
|
50
|
+
gl.attachShader(p, vs);
|
|
51
|
+
gl.attachShader(p, fs);
|
|
52
|
+
gl.linkProgram(p);
|
|
53
|
+
if (!gl.getProgramParameter(p, gl.LINK_STATUS)) {
|
|
54
|
+
gl.deleteProgram(p);
|
|
55
|
+
return null;
|
|
56
|
+
}
|
|
57
|
+
return p;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// Visible pass
|
|
61
|
+
const VERT_VIS = `
|
|
62
|
+
attribute vec2 a_position;
|
|
63
|
+
uniform vec2 u_resolution;
|
|
64
|
+
uniform float u_size;
|
|
65
|
+
void main() {
|
|
66
|
+
vec2 clip = (a_position / u_resolution) * 2.0 - 1.0;
|
|
67
|
+
gl_Position = vec4(clip.x, -clip.y, 0.0, 1.0);
|
|
68
|
+
gl_PointSize = u_size;
|
|
69
|
+
}
|
|
70
|
+
`;
|
|
71
|
+
const FRAG_VIS = `
|
|
72
|
+
precision mediump float;
|
|
73
|
+
uniform vec3 u_color;
|
|
74
|
+
uniform float u_opacity;
|
|
75
|
+
void main() {
|
|
76
|
+
vec2 coord = gl_PointCoord - vec2(0.5);
|
|
77
|
+
if (dot(coord, coord) > 0.25) discard;
|
|
78
|
+
gl_FragColor = vec4(u_color, u_opacity);
|
|
79
|
+
}
|
|
80
|
+
`;
|
|
81
|
+
|
|
82
|
+
// Pick pass — encode index as color
|
|
83
|
+
const VERT_PICK = `
|
|
84
|
+
attribute vec2 a_position;
|
|
85
|
+
attribute vec3 a_pickColor;
|
|
86
|
+
uniform vec2 u_resolution;
|
|
87
|
+
uniform float u_size;
|
|
88
|
+
varying vec3 v_pickColor;
|
|
89
|
+
void main() {
|
|
90
|
+
vec2 clip = (a_position / u_resolution) * 2.0 - 1.0;
|
|
91
|
+
gl_Position = vec4(clip.x, -clip.y, 0.0, 1.0);
|
|
92
|
+
gl_PointSize = u_size + 4.0;
|
|
93
|
+
v_pickColor = a_pickColor;
|
|
94
|
+
}
|
|
95
|
+
`;
|
|
96
|
+
const FRAG_PICK = `
|
|
97
|
+
precision mediump float;
|
|
98
|
+
varying vec3 v_pickColor;
|
|
99
|
+
void main() {
|
|
100
|
+
vec2 coord = gl_PointCoord - vec2(0.5);
|
|
101
|
+
if (dot(coord, coord) > 0.25) discard;
|
|
102
|
+
gl_FragColor = vec4(v_pickColor, 1.0);
|
|
103
|
+
}
|
|
104
|
+
`;
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Inline component — manages visible + pick canvases and hover state.
|
|
108
|
+
*/
|
|
109
|
+
function DotGLInteractiveCanvas({
|
|
110
|
+
resolved,
|
|
111
|
+
color,
|
|
112
|
+
size,
|
|
113
|
+
opacity,
|
|
114
|
+
width,
|
|
115
|
+
height,
|
|
116
|
+
data,
|
|
117
|
+
renderTooltip
|
|
118
|
+
}) {
|
|
119
|
+
const visCanvasRef = useRef(null);
|
|
120
|
+
const pickCanvasRef = useRef(null);
|
|
121
|
+
const [hoverIndex, setHoverIndex] = useState(-1);
|
|
122
|
+
const [mousePos, setMousePos] = useState(null);
|
|
123
|
+
const visGLRef = useRef(null);
|
|
124
|
+
const pickGLRef = useRef(null);
|
|
125
|
+
const positions = useMemo(() => {
|
|
126
|
+
const pos = [];
|
|
127
|
+
for (const p of resolved) {
|
|
128
|
+
pos.push(p.px, p.py);
|
|
129
|
+
}
|
|
130
|
+
return new Float32Array(pos);
|
|
131
|
+
}, [resolved]);
|
|
132
|
+
const pickColors = useMemo(() => {
|
|
133
|
+
const colors = [];
|
|
134
|
+
for (let i = 0; i < resolved.length; i++) {
|
|
135
|
+
const [r, g, b] = indexToColor(i);
|
|
136
|
+
colors.push(r, g, b);
|
|
137
|
+
}
|
|
138
|
+
return new Float32Array(colors);
|
|
139
|
+
}, [resolved.length]);
|
|
140
|
+
|
|
141
|
+
// Render both passes
|
|
142
|
+
useEffect(() => {
|
|
143
|
+
if (width <= 0 || height <= 0) {
|
|
144
|
+
return;
|
|
145
|
+
}
|
|
146
|
+
const dpr = window.devicePixelRatio || 1;
|
|
147
|
+
|
|
148
|
+
// Visible canvas
|
|
149
|
+
const visCanvas = visCanvasRef.current;
|
|
150
|
+
if (visCanvas) {
|
|
151
|
+
visCanvas.width = width * dpr;
|
|
152
|
+
visCanvas.height = height * dpr;
|
|
153
|
+
if (!visGLRef.current) {
|
|
154
|
+
const gl = visCanvas.getContext('webgl', {
|
|
155
|
+
alpha: true,
|
|
156
|
+
premultipliedAlpha: false,
|
|
157
|
+
antialias: true
|
|
158
|
+
});
|
|
159
|
+
const prog = gl ? linkProgram(gl, VERT_VIS, FRAG_VIS) : null;
|
|
160
|
+
if (gl && prog) {
|
|
161
|
+
visGLRef.current = {
|
|
162
|
+
gl,
|
|
163
|
+
prog
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
const vis = visGLRef.current;
|
|
168
|
+
if (vis) {
|
|
169
|
+
const {
|
|
170
|
+
gl,
|
|
171
|
+
prog
|
|
172
|
+
} = vis;
|
|
173
|
+
gl.viewport(0, 0, visCanvas.width, visCanvas.height);
|
|
174
|
+
gl.clearColor(0, 0, 0, 0);
|
|
175
|
+
gl.clear(gl.COLOR_BUFFER_BIT);
|
|
176
|
+
gl.enable(gl.BLEND);
|
|
177
|
+
gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);
|
|
178
|
+
gl.useProgram(prog);
|
|
179
|
+
const buf = gl.createBuffer();
|
|
180
|
+
gl.bindBuffer(gl.ARRAY_BUFFER, buf);
|
|
181
|
+
gl.bufferData(gl.ARRAY_BUFFER, positions, gl.STATIC_DRAW);
|
|
182
|
+
const aPos = gl.getAttribLocation(prog, 'a_position');
|
|
183
|
+
gl.enableVertexAttribArray(aPos);
|
|
184
|
+
gl.vertexAttribPointer(aPos, 2, gl.FLOAT, false, 0, 0);
|
|
185
|
+
const [r, g, b] = hexToGL(color);
|
|
186
|
+
gl.uniform2f(gl.getUniformLocation(prog, 'u_resolution'), width, height);
|
|
187
|
+
gl.uniform3f(gl.getUniformLocation(prog, 'u_color'), r, g, b);
|
|
188
|
+
gl.uniform1f(gl.getUniformLocation(prog, 'u_size'), size * dpr);
|
|
189
|
+
gl.uniform1f(gl.getUniformLocation(prog, 'u_opacity'), opacity);
|
|
190
|
+
gl.drawArrays(gl.POINTS, 0, positions.length / 2);
|
|
191
|
+
gl.deleteBuffer(buf);
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
// Pick canvas (offscreen)
|
|
196
|
+
const pickCanvas = pickCanvasRef.current;
|
|
197
|
+
if (pickCanvas) {
|
|
198
|
+
pickCanvas.width = width * dpr;
|
|
199
|
+
pickCanvas.height = height * dpr;
|
|
200
|
+
if (!pickGLRef.current) {
|
|
201
|
+
const gl = pickCanvas.getContext('webgl', {
|
|
202
|
+
alpha: false,
|
|
203
|
+
premultipliedAlpha: false,
|
|
204
|
+
antialias: false,
|
|
205
|
+
preserveDrawingBuffer: true
|
|
206
|
+
});
|
|
207
|
+
if (gl) {
|
|
208
|
+
const prog = linkProgram(gl, VERT_PICK, FRAG_PICK);
|
|
209
|
+
const fb = gl.createFramebuffer();
|
|
210
|
+
const tex = gl.createTexture();
|
|
211
|
+
if (prog && fb && tex) {
|
|
212
|
+
gl.bindTexture(gl.TEXTURE_2D, tex);
|
|
213
|
+
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, pickCanvas.width, pickCanvas.height, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);
|
|
214
|
+
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
|
|
215
|
+
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
|
|
216
|
+
gl.bindFramebuffer(gl.FRAMEBUFFER, fb);
|
|
217
|
+
gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, tex, 0);
|
|
218
|
+
gl.bindFramebuffer(gl.FRAMEBUFFER, null);
|
|
219
|
+
pickGLRef.current = {
|
|
220
|
+
gl,
|
|
221
|
+
prog,
|
|
222
|
+
fb,
|
|
223
|
+
tex
|
|
224
|
+
};
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
const pick = pickGLRef.current;
|
|
229
|
+
if (pick) {
|
|
230
|
+
const {
|
|
231
|
+
gl,
|
|
232
|
+
prog,
|
|
233
|
+
fb,
|
|
234
|
+
tex
|
|
235
|
+
} = pick;
|
|
236
|
+
gl.bindTexture(gl.TEXTURE_2D, tex);
|
|
237
|
+
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, pickCanvas.width, pickCanvas.height, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);
|
|
238
|
+
gl.bindFramebuffer(gl.FRAMEBUFFER, fb);
|
|
239
|
+
gl.viewport(0, 0, pickCanvas.width, pickCanvas.height);
|
|
240
|
+
gl.clearColor(0, 0, 0, 1);
|
|
241
|
+
gl.clear(gl.COLOR_BUFFER_BIT);
|
|
242
|
+
gl.disable(gl.BLEND);
|
|
243
|
+
gl.useProgram(prog);
|
|
244
|
+
const posBuf = gl.createBuffer();
|
|
245
|
+
gl.bindBuffer(gl.ARRAY_BUFFER, posBuf);
|
|
246
|
+
gl.bufferData(gl.ARRAY_BUFFER, positions, gl.STATIC_DRAW);
|
|
247
|
+
const aPos = gl.getAttribLocation(prog, 'a_position');
|
|
248
|
+
gl.enableVertexAttribArray(aPos);
|
|
249
|
+
gl.vertexAttribPointer(aPos, 2, gl.FLOAT, false, 0, 0);
|
|
250
|
+
const colBuf = gl.createBuffer();
|
|
251
|
+
gl.bindBuffer(gl.ARRAY_BUFFER, colBuf);
|
|
252
|
+
gl.bufferData(gl.ARRAY_BUFFER, pickColors, gl.STATIC_DRAW);
|
|
253
|
+
const aCol = gl.getAttribLocation(prog, 'a_pickColor');
|
|
254
|
+
gl.enableVertexAttribArray(aCol);
|
|
255
|
+
gl.vertexAttribPointer(aCol, 3, gl.FLOAT, false, 0, 0);
|
|
256
|
+
gl.uniform2f(gl.getUniformLocation(prog, 'u_resolution'), width, height);
|
|
257
|
+
gl.uniform1f(gl.getUniformLocation(prog, 'u_size'), (size + 4) * dpr);
|
|
258
|
+
gl.drawArrays(gl.POINTS, 0, positions.length / 2);
|
|
259
|
+
gl.deleteBuffer(posBuf);
|
|
260
|
+
gl.deleteBuffer(colBuf);
|
|
261
|
+
gl.bindFramebuffer(gl.FRAMEBUFFER, null);
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
}, [width, height, positions, pickColors, color, size, opacity]);
|
|
265
|
+
const handleMouseMove = useCallback(e => {
|
|
266
|
+
const pick = pickGLRef.current;
|
|
267
|
+
if (!pick) {
|
|
268
|
+
return;
|
|
269
|
+
}
|
|
270
|
+
const svg = e.currentTarget.ownerSVGElement;
|
|
271
|
+
if (!svg) {
|
|
272
|
+
return;
|
|
273
|
+
}
|
|
274
|
+
const pt = svg.createSVGPoint();
|
|
275
|
+
pt.x = e.clientX;
|
|
276
|
+
pt.y = e.clientY;
|
|
277
|
+
const local = pt.matrixTransform(e.currentTarget.getScreenCTM()?.inverse());
|
|
278
|
+
const dpr = window.devicePixelRatio || 1;
|
|
279
|
+
const px = Math.floor(local.x * dpr);
|
|
280
|
+
const py = Math.floor(local.y * dpr);
|
|
281
|
+
const {
|
|
282
|
+
gl,
|
|
283
|
+
fb
|
|
284
|
+
} = pick;
|
|
285
|
+
gl.bindFramebuffer(gl.FRAMEBUFFER, fb);
|
|
286
|
+
const pixel = new Uint8Array(4);
|
|
287
|
+
gl.readPixels(px, gl.canvas.height - py, 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, pixel);
|
|
288
|
+
gl.bindFramebuffer(gl.FRAMEBUFFER, null);
|
|
289
|
+
const idx = colorToIndex(pixel[0], pixel[1], pixel[2]);
|
|
290
|
+
setHoverIndex(idx);
|
|
291
|
+
setMousePos(idx >= 0 ? {
|
|
292
|
+
x: local.x,
|
|
293
|
+
y: local.y
|
|
294
|
+
} : null);
|
|
295
|
+
}, []);
|
|
296
|
+
const handleMouseLeave = useCallback(() => {
|
|
297
|
+
setHoverIndex(-1);
|
|
298
|
+
setMousePos(null);
|
|
299
|
+
}, []);
|
|
300
|
+
const datum = hoverIndex >= 0 && hoverIndex < data.length ? data[hoverIndex] : null;
|
|
301
|
+
const defaultTooltip = (d, i) => /*#__PURE__*/_jsxs("div", {
|
|
302
|
+
style: {
|
|
303
|
+
display: 'flex',
|
|
304
|
+
flexDirection: 'column',
|
|
305
|
+
gap: 2,
|
|
306
|
+
fontSize: 12
|
|
307
|
+
},
|
|
308
|
+
children: [/*#__PURE__*/_jsxs("div", {
|
|
309
|
+
style: {
|
|
310
|
+
fontWeight: 600,
|
|
311
|
+
color: 'var(--color-text-primary)'
|
|
312
|
+
},
|
|
313
|
+
children: ["Point ", i]
|
|
314
|
+
}), Object.entries(d).map(([k, v]) => /*#__PURE__*/_jsxs("div", {
|
|
315
|
+
children: [/*#__PURE__*/_jsxs("span", {
|
|
316
|
+
style: {
|
|
317
|
+
color: 'var(--color-text-secondary)'
|
|
318
|
+
},
|
|
319
|
+
children: [k, ":"]
|
|
320
|
+
}), ' ', /*#__PURE__*/_jsx("span", {
|
|
321
|
+
style: {
|
|
322
|
+
fontWeight: 500
|
|
323
|
+
},
|
|
324
|
+
children: String(v)
|
|
325
|
+
})]
|
|
326
|
+
}, k))]
|
|
327
|
+
});
|
|
328
|
+
return /*#__PURE__*/_jsxs("g", {
|
|
329
|
+
children: [/*#__PURE__*/_jsx("foreignObject", {
|
|
330
|
+
x: 0,
|
|
331
|
+
y: 0,
|
|
332
|
+
width: width,
|
|
333
|
+
height: height,
|
|
334
|
+
style: {
|
|
335
|
+
overflow: 'hidden'
|
|
336
|
+
},
|
|
337
|
+
children: /*#__PURE__*/_jsx("canvas", {
|
|
338
|
+
ref: visCanvasRef,
|
|
339
|
+
style: {
|
|
340
|
+
width,
|
|
341
|
+
height,
|
|
342
|
+
pointerEvents: 'none'
|
|
343
|
+
}
|
|
344
|
+
})
|
|
345
|
+
}), /*#__PURE__*/_jsx("foreignObject", {
|
|
346
|
+
x: 0,
|
|
347
|
+
y: 0,
|
|
348
|
+
width: 0,
|
|
349
|
+
height: 0,
|
|
350
|
+
style: {
|
|
351
|
+
overflow: 'hidden'
|
|
352
|
+
},
|
|
353
|
+
children: /*#__PURE__*/_jsx("canvas", {
|
|
354
|
+
ref: pickCanvasRef,
|
|
355
|
+
style: {
|
|
356
|
+
display: 'none'
|
|
357
|
+
}
|
|
358
|
+
})
|
|
359
|
+
}), /*#__PURE__*/_jsx("rect", {
|
|
360
|
+
x: 0,
|
|
361
|
+
y: 0,
|
|
362
|
+
width: width,
|
|
363
|
+
height: height,
|
|
364
|
+
fill: "transparent",
|
|
365
|
+
onMouseMove: handleMouseMove,
|
|
366
|
+
onMouseLeave: handleMouseLeave
|
|
367
|
+
}), datum && hoverIndex >= 0 && /*#__PURE__*/_jsx("circle", {
|
|
368
|
+
cx: positions[hoverIndex * 2],
|
|
369
|
+
cy: positions[hoverIndex * 2 + 1],
|
|
370
|
+
r: size / 2 + 3,
|
|
371
|
+
fill: "none",
|
|
372
|
+
stroke: color,
|
|
373
|
+
strokeWidth: 2,
|
|
374
|
+
strokeOpacity: 0.8,
|
|
375
|
+
pointerEvents: "none"
|
|
376
|
+
}), datum && mousePos && /*#__PURE__*/_jsx("foreignObject", {
|
|
377
|
+
x: mousePos.x + 12,
|
|
378
|
+
y: Math.max(0, mousePos.y - 40),
|
|
379
|
+
width: 200,
|
|
380
|
+
height: 120,
|
|
381
|
+
pointerEvents: "none",
|
|
382
|
+
style: {
|
|
383
|
+
overflow: 'visible'
|
|
384
|
+
},
|
|
385
|
+
children: /*#__PURE__*/_jsx("div", {
|
|
386
|
+
style: {
|
|
387
|
+
background: 'var(--color-background-popover)',
|
|
388
|
+
border: '1px solid var(--color-border)',
|
|
389
|
+
borderRadius: 8,
|
|
390
|
+
padding: '8px 12px',
|
|
391
|
+
boxShadow: 'var(--shadow-med)',
|
|
392
|
+
whiteSpace: 'nowrap',
|
|
393
|
+
width: 'fit-content'
|
|
394
|
+
},
|
|
395
|
+
children: renderTooltip ? renderTooltip(datum, hoverIndex) : defaultTooltip(datum, hoverIndex)
|
|
396
|
+
})
|
|
397
|
+
})]
|
|
398
|
+
});
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
/**
|
|
402
|
+
* WebGL scatter with GPU color-picking hover. O(1) hit detection via offscreen
|
|
403
|
+
* framebuffer — no JS iteration regardless of point count.
|
|
404
|
+
*
|
|
405
|
+
* @example
|
|
406
|
+
* ```
|
|
407
|
+
* series={[dotGLInteractive('value', {
|
|
408
|
+
* color: '#4f46e5',
|
|
409
|
+
* renderTooltip: (d, i) => <span>{d.name}: {d.value}</span>,
|
|
410
|
+
* })]}
|
|
411
|
+
* ```
|
|
412
|
+
*/
|
|
413
|
+
export function dotGLInteractive(dataKey, options) {
|
|
414
|
+
const {
|
|
415
|
+
color
|
|
416
|
+
} = options;
|
|
417
|
+
const size = options.size ?? 6;
|
|
418
|
+
const opacity = options.opacity ?? 0.8;
|
|
419
|
+
const renderTooltip = options.renderTooltip;
|
|
420
|
+
return {
|
|
421
|
+
type: 'dotGLInteractive',
|
|
422
|
+
key: dataKey,
|
|
423
|
+
dataKeys: [dataKey],
|
|
424
|
+
layout: {},
|
|
425
|
+
resolve(ctx) {
|
|
426
|
+
const {
|
|
427
|
+
data,
|
|
428
|
+
xKey,
|
|
429
|
+
xScale,
|
|
430
|
+
yScale
|
|
431
|
+
} = ctx;
|
|
432
|
+
const points = [];
|
|
433
|
+
for (let i = 0; i < data.length; i++) {
|
|
434
|
+
const d = data[i];
|
|
435
|
+
let px;
|
|
436
|
+
if ('bandwidth' in xScale) {
|
|
437
|
+
px = (xScale(String(d[xKey])) ?? 0) + xScale.bandwidth() / 2;
|
|
438
|
+
} else {
|
|
439
|
+
px = xScale(d[xKey]);
|
|
440
|
+
}
|
|
441
|
+
const v = typeof d[dataKey] === 'number' ? d[dataKey] : 0;
|
|
442
|
+
points.push({
|
|
443
|
+
px,
|
|
444
|
+
py: yScale(v),
|
|
445
|
+
py0: yScale(0),
|
|
446
|
+
dataIndex: i
|
|
447
|
+
});
|
|
448
|
+
}
|
|
449
|
+
return points;
|
|
450
|
+
},
|
|
451
|
+
render(resolved, ctx) {
|
|
452
|
+
return /*#__PURE__*/_jsx(DotGLInteractiveCanvas, {
|
|
453
|
+
resolved: resolved,
|
|
454
|
+
color: color,
|
|
455
|
+
size: size,
|
|
456
|
+
opacity: opacity,
|
|
457
|
+
width: ctx.width,
|
|
458
|
+
height: ctx.height,
|
|
459
|
+
data: ctx.data,
|
|
460
|
+
renderTooltip: renderTooltip
|
|
461
|
+
});
|
|
462
|
+
}
|
|
463
|
+
};
|
|
464
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file marks/errorBar.tsx
|
|
3
|
+
* @output Error bar (whisker) series — vertical high/low bounds
|
|
4
|
+
*/
|
|
5
|
+
import type { SeriesDef } from '../types';
|
|
6
|
+
export interface ErrorBarOptions {
|
|
7
|
+
/** Data key for the upper bound */
|
|
8
|
+
high: string;
|
|
9
|
+
/** Data key for the lower bound */
|
|
10
|
+
low: string;
|
|
11
|
+
/** Stroke color */
|
|
12
|
+
color?: string;
|
|
13
|
+
/** Stroke width */
|
|
14
|
+
strokeWidth?: number;
|
|
15
|
+
/** Width of the whisker caps in pixels */
|
|
16
|
+
capWidth?: number;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Error bars with whisker caps. Pair with bar() or dot().
|
|
20
|
+
*
|
|
21
|
+
* @example
|
|
22
|
+
* ```
|
|
23
|
+
* series={[bar('mean'), errorBar({high: 'upper95', low: 'lower95'})]}
|
|
24
|
+
* ```
|
|
25
|
+
*/
|
|
26
|
+
export declare function errorBar(options: ErrorBarOptions): SeriesDef;
|
|
27
|
+
//# sourceMappingURL=errorBar.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errorBar.d.ts","sourceRoot":"","sources":["../../../src/ChartV2/marks/errorBar.tsx"],"names":[],"mappings":"AAEA;;;GAGG;AAEH,OAAO,KAAK,EAAC,SAAS,EAAgB,MAAM,UAAU,CAAC;AAGvD,MAAM,WAAW,eAAe;IAC9B,mCAAmC;IACnC,IAAI,EAAE,MAAM,CAAC;IACb,mCAAmC;IACnC,GAAG,EAAE,MAAM,CAAC;IACZ,mBAAmB;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,mBAAmB;IACnB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,0CAA0C;IAC1C,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED;;;;;;;GAOG;AACH,wBAAgB,QAAQ,CAAC,OAAO,EAAE,eAAe,GAAG,SAAS,CAyD5D"}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* @file marks/errorBar.tsx
|
|
6
|
+
* @output Error bar (whisker) series — vertical high/low bounds
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Error bars with whisker caps. Pair with bar() or dot().
|
|
11
|
+
*
|
|
12
|
+
* @example
|
|
13
|
+
* ```
|
|
14
|
+
* series={[bar('mean'), errorBar({high: 'upper95', low: 'lower95'})]}
|
|
15
|
+
* ```
|
|
16
|
+
*/
|
|
17
|
+
export function errorBar(options) {
|
|
18
|
+
const {
|
|
19
|
+
high,
|
|
20
|
+
low
|
|
21
|
+
} = options;
|
|
22
|
+
const color = options.color ?? 'var(--color-text-primary)';
|
|
23
|
+
const strokeWidth = options.strokeWidth ?? 1.5;
|
|
24
|
+
const capWidth = options.capWidth ?? 8;
|
|
25
|
+
return {
|
|
26
|
+
type: 'errorBar',
|
|
27
|
+
key: `errorBar-${high}-${low}`,
|
|
28
|
+
dataKeys: [high, low],
|
|
29
|
+
layout: {},
|
|
30
|
+
resolve(ctx) {
|
|
31
|
+
const {
|
|
32
|
+
data,
|
|
33
|
+
xKey,
|
|
34
|
+
xScale,
|
|
35
|
+
yScale
|
|
36
|
+
} = ctx;
|
|
37
|
+
const points = [];
|
|
38
|
+
for (let i = 0; i < data.length; i++) {
|
|
39
|
+
const d = data[i];
|
|
40
|
+
let px;
|
|
41
|
+
if ('bandwidth' in xScale) {
|
|
42
|
+
px = (xScale(String(d[xKey])) ?? 0) + xScale.bandwidth() / 2;
|
|
43
|
+
} else {
|
|
44
|
+
px = xScale(d[xKey]);
|
|
45
|
+
}
|
|
46
|
+
const upper = typeof d[high] === 'number' ? d[high] : 0;
|
|
47
|
+
const lower = typeof d[low] === 'number' ? d[low] : 0;
|
|
48
|
+
points.push({
|
|
49
|
+
px,
|
|
50
|
+
py: yScale(upper),
|
|
51
|
+
py0: yScale(lower),
|
|
52
|
+
dataIndex: i
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
return points;
|
|
56
|
+
},
|
|
57
|
+
render(resolved) {
|
|
58
|
+
const half = capWidth / 2;
|
|
59
|
+
return /*#__PURE__*/_jsx("g", {
|
|
60
|
+
children: resolved.map((p, i) => /*#__PURE__*/_jsxs("g", {
|
|
61
|
+
children: [/*#__PURE__*/_jsx("line", {
|
|
62
|
+
x1: p.px,
|
|
63
|
+
x2: p.px,
|
|
64
|
+
y1: p.py,
|
|
65
|
+
y2: p.py0,
|
|
66
|
+
stroke: color,
|
|
67
|
+
strokeWidth: strokeWidth
|
|
68
|
+
}), /*#__PURE__*/_jsx("line", {
|
|
69
|
+
x1: p.px - half,
|
|
70
|
+
x2: p.px + half,
|
|
71
|
+
y1: p.py,
|
|
72
|
+
y2: p.py,
|
|
73
|
+
stroke: color,
|
|
74
|
+
strokeWidth: strokeWidth
|
|
75
|
+
}), /*#__PURE__*/_jsx("line", {
|
|
76
|
+
x1: p.px - half,
|
|
77
|
+
x2: p.px + half,
|
|
78
|
+
y1: p.py0,
|
|
79
|
+
y2: p.py0,
|
|
80
|
+
stroke: color,
|
|
81
|
+
strokeWidth: strokeWidth
|
|
82
|
+
})]
|
|
83
|
+
}, i))
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
};
|
|
87
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { SeriesDef } from '../types';
|
|
2
|
+
export interface HeatmapGLOptions {
|
|
3
|
+
xKey: string;
|
|
4
|
+
yKey: string;
|
|
5
|
+
valueKey: string;
|
|
6
|
+
colorRange: string[];
|
|
7
|
+
domain?: [number, number];
|
|
8
|
+
cellGap?: number;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* WebGL heatmap. Renders a 2D grid of colored cells using triangles.
|
|
12
|
+
* Manages its own y band scale for rows; uses the chart's x band scale for columns.
|
|
13
|
+
*
|
|
14
|
+
* @example
|
|
15
|
+
* ```
|
|
16
|
+
* series={[heatmapGL({
|
|
17
|
+
* xKey: 'hour', yKey: 'day', valueKey: 'count',
|
|
18
|
+
* colorRange: ['#f0f9ff', '#1e40af'],
|
|
19
|
+
* })]}
|
|
20
|
+
* ```
|
|
21
|
+
*/
|
|
22
|
+
export declare function heatmapGL(options: HeatmapGLOptions): SeriesDef;
|
|
23
|
+
//# sourceMappingURL=heatmapGL.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"heatmapGL.d.ts","sourceRoot":"","sources":["../../../src/ChartV2/marks/heatmapGL.tsx"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAC,SAAS,EAAgB,MAAM,UAAU,CAAC;AAWvD,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC1B,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAkND;;;;;;;;;;;GAWG;AACH,wBAAgB,SAAS,CAAC,OAAO,EAAE,gBAAgB,GAAG,SAAS,CAiD9D"}
|