@pharos-ds/charts 0.0.0-stage → 1.0.10

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 (139) hide show
  1. package/CHANGELOG.md +73 -0
  2. package/LICENSE +24 -0
  3. package/README.md +117 -2
  4. package/dist/Chart.d.ts +62 -0
  5. package/dist/Chart.d.ts.map +1 -0
  6. package/dist/Chart.js +401 -0
  7. package/dist/ChartAxis.d.ts +37 -0
  8. package/dist/ChartAxis.d.ts.map +1 -0
  9. package/dist/ChartAxis.js +233 -0
  10. package/dist/ChartContext.d.ts +4 -0
  11. package/dist/ChartContext.d.ts.map +1 -0
  12. package/dist/ChartContext.js +15 -0
  13. package/dist/ChartGrid.d.ts +26 -0
  14. package/dist/ChartGrid.d.ts.map +1 -0
  15. package/dist/ChartGrid.js +118 -0
  16. package/dist/ChartLegend.d.ts +14 -0
  17. package/dist/ChartLegend.d.ts.map +1 -0
  18. package/dist/ChartLegend.js +62 -0
  19. package/dist/ChartSwatch.d.ts +36 -0
  20. package/dist/ChartSwatch.d.ts.map +1 -0
  21. package/dist/ChartSwatch.js +85 -0
  22. package/dist/ChartTooltip.d.ts +46 -0
  23. package/dist/ChartTooltip.d.ts.map +1 -0
  24. package/dist/ChartTooltip.js +330 -0
  25. package/dist/charts.css +33 -0
  26. package/dist/dataTable.d.ts +56 -0
  27. package/dist/dataTable.d.ts.map +1 -0
  28. package/dist/dataTable.js +88 -0
  29. package/dist/formatters.d.ts +37 -0
  30. package/dist/formatters.d.ts.map +1 -0
  31. package/dist/formatters.js +156 -0
  32. package/dist/getChartColors.d.ts +67 -0
  33. package/dist/getChartColors.d.ts.map +1 -0
  34. package/dist/getChartColors.js +191 -0
  35. package/dist/index.d.ts +17 -0
  36. package/dist/index.d.ts.map +1 -0
  37. package/dist/index.js +19 -0
  38. package/dist/layout.d.ts +40 -0
  39. package/dist/layout.d.ts.map +1 -0
  40. package/dist/layout.js +322 -0
  41. package/dist/legend.d.ts +17 -0
  42. package/dist/legend.d.ts.map +1 -0
  43. package/dist/legend.js +40 -0
  44. package/dist/markColor.d.ts +20 -0
  45. package/dist/markColor.d.ts.map +1 -0
  46. package/dist/markColor.js +56 -0
  47. package/dist/marks/area.d.ts +19 -0
  48. package/dist/marks/area.d.ts.map +1 -0
  49. package/dist/marks/area.js +121 -0
  50. package/dist/marks/band.d.ts +9 -0
  51. package/dist/marks/band.d.ts.map +1 -0
  52. package/dist/marks/band.js +64 -0
  53. package/dist/marks/bar.d.ts +21 -0
  54. package/dist/marks/bar.d.ts.map +1 -0
  55. package/dist/marks/bar.js +161 -0
  56. package/dist/marks/candlestick.d.ts +16 -0
  57. package/dist/marks/candlestick.d.ts.map +1 -0
  58. package/dist/marks/candlestick.js +110 -0
  59. package/dist/marks/dot.d.ts +19 -0
  60. package/dist/marks/dot.d.ts.map +1 -0
  61. package/dist/marks/dot.js +98 -0
  62. package/dist/marks/dotGL.d.ts +16 -0
  63. package/dist/marks/dotGL.d.ts.map +1 -0
  64. package/dist/marks/dotGL.js +231 -0
  65. package/dist/marks/dotGLInteractive.d.ts +35 -0
  66. package/dist/marks/dotGLInteractive.d.ts.map +1 -0
  67. package/dist/marks/dotGLInteractive.js +584 -0
  68. package/dist/marks/errorBar.d.ts +29 -0
  69. package/dist/marks/errorBar.d.ts.map +1 -0
  70. package/dist/marks/errorBar.js +95 -0
  71. package/dist/marks/heatmapGL.d.ts +27 -0
  72. package/dist/marks/heatmapGL.d.ts.map +1 -0
  73. package/dist/marks/heatmapGL.js +335 -0
  74. package/dist/marks/index.d.ts +13 -0
  75. package/dist/marks/index.d.ts.map +1 -0
  76. package/dist/marks/index.js +14 -0
  77. package/dist/marks/line.d.ts +18 -0
  78. package/dist/marks/line.d.ts.map +1 -0
  79. package/dist/marks/line.js +83 -0
  80. package/dist/marks/referenceLine.d.ts +37 -0
  81. package/dist/marks/referenceLine.d.ts.map +1 -0
  82. package/dist/marks/referenceLine.js +204 -0
  83. package/dist/marks/streamGL.d.ts +41 -0
  84. package/dist/marks/streamGL.d.ts.map +1 -0
  85. package/dist/marks/streamGL.js +317 -0
  86. package/dist/tooltip.d.ts +38 -0
  87. package/dist/tooltip.d.ts.map +1 -0
  88. package/dist/tooltip.js +57 -0
  89. package/dist/types.d.ts +193 -0
  90. package/dist/types.d.ts.map +1 -0
  91. package/dist/types.js +70 -0
  92. package/dist/useChartColors.d.ts +16 -0
  93. package/dist/useChartColors.d.ts.map +1 -0
  94. package/dist/useChartColors.js +37 -0
  95. package/dist/utils.d.ts +15 -0
  96. package/dist/utils.d.ts.map +1 -0
  97. package/dist/utils.js +31 -0
  98. package/dist/webgl.d.ts +77 -0
  99. package/dist/webgl.d.ts.map +1 -0
  100. package/dist/webgl.js +235 -0
  101. package/package.json +71 -3
  102. package/src/Chart.doc.mjs +174 -0
  103. package/src/Chart.test.tsx +273 -0
  104. package/src/Chart.tsx +535 -0
  105. package/src/ChartAxis.tsx +289 -0
  106. package/src/ChartContext.ts +18 -0
  107. package/src/ChartGrid.tsx +136 -0
  108. package/src/ChartLegend.tsx +77 -0
  109. package/src/ChartSwatch.doc.mjs +17 -0
  110. package/src/ChartSwatch.tsx +89 -0
  111. package/src/ChartTooltip.tsx +452 -0
  112. package/src/charts.css.d.ts +3 -0
  113. package/src/dataTable.test.ts +86 -0
  114. package/src/dataTable.ts +124 -0
  115. package/src/formatters.ts +167 -0
  116. package/src/getChartColors.test.ts +114 -0
  117. package/src/getChartColors.ts +282 -0
  118. package/src/index.ts +88 -0
  119. package/src/layout.ts +372 -0
  120. package/src/legend.ts +44 -0
  121. package/src/markColor.ts +70 -0
  122. package/src/marks/area.tsx +142 -0
  123. package/src/marks/band.tsx +86 -0
  124. package/src/marks/bar.tsx +210 -0
  125. package/src/marks/candlestick.tsx +128 -0
  126. package/src/marks/dot.tsx +119 -0
  127. package/src/marks/dotGL.tsx +274 -0
  128. package/src/marks/dotGLInteractive.tsx +660 -0
  129. package/src/marks/errorBar.tsx +119 -0
  130. package/src/marks/heatmapGL.tsx +392 -0
  131. package/src/marks/index.ts +17 -0
  132. package/src/marks/line.tsx +118 -0
  133. package/src/marks/referenceLine.tsx +252 -0
  134. package/src/marks/streamGL.tsx +381 -0
  135. package/src/tooltip.ts +73 -0
  136. package/src/types.ts +212 -0
  137. package/src/useChartColors.ts +41 -0
  138. package/src/utils.ts +38 -0
  139. package/src/webgl.ts +262 -0
@@ -0,0 +1,274 @@
1
+ // Copyright (c) Meta Platforms, Inc. and affiliates.
2
+
3
+ /**
4
+ * @file marks/dotGL.tsx
5
+ * @output WebGL scatter — canvas overlay, single draw call
6
+ *
7
+ * GL context, program, and vertex buffer are created once and reused across
8
+ * redraws; all are released (and the context eagerly lost) on unmount. Handles
9
+ * context loss/restore and skips non-finite points.
10
+ */
11
+
12
+ import {useRef, useEffect, useMemo, useState} from 'react';
13
+ import type {SeriesDef, ResolvedPoint} from '../types';
14
+ import type {ScaleBand} from 'd3-scale';
15
+ import {
16
+ hexToGL,
17
+ getWebGLContext,
18
+ setupGLState,
19
+ sizeCanvas,
20
+ mountCanvasOverSVG,
21
+ createProgram,
22
+ registerContextLossHandlers,
23
+ loseContext,
24
+ POINT_SIZE_COMPENSATION,
25
+ } from '../webgl';
26
+
27
+ export interface DotGLOptions {
28
+ color: string;
29
+ size?: number;
30
+ opacity?: number;
31
+ }
32
+
33
+ const VERT = `
34
+ attribute vec2 a_position;
35
+ uniform vec2 u_resolution;
36
+ uniform float u_size;
37
+ void main() {
38
+ vec2 clip = (a_position / u_resolution) * 2.0 - 1.0;
39
+ gl_Position = vec4(clip.x, -clip.y, 0.0, 1.0);
40
+ gl_PointSize = u_size * ${POINT_SIZE_COMPENSATION.toFixed(6)};
41
+ }
42
+ `;
43
+
44
+ const FRAG = `
45
+ precision mediump float;
46
+ uniform vec3 u_color;
47
+ uniform float u_opacity;
48
+ void main() {
49
+ vec2 coord = gl_PointCoord - vec2(0.5);
50
+ float dist = length(coord);
51
+ if (dist > 0.5) discard;
52
+ float edge = 1.0 - smoothstep(0.48, 0.5, dist);
53
+ float a = u_opacity * edge;
54
+ gl_FragColor = vec4(u_color * a, a);
55
+ }
56
+ `;
57
+
58
+ type DotGLLocations = {
59
+ aPosition: number;
60
+ uResolution: WebGLUniformLocation | null;
61
+ uColor: WebGLUniformLocation | null;
62
+ uSize: WebGLUniformLocation | null;
63
+ uOpacity: WebGLUniformLocation | null;
64
+ };
65
+
66
+ /**
67
+ * Inline component returned by render() — owns the WebGL canvas lifecycle. The
68
+ * canvas is mounted as a sibling of the SVG (via a marker <g>) for sharp Retina
69
+ * output without foreignObject blur.
70
+ */
71
+ function DotGLCanvas({
72
+ resolved,
73
+ color,
74
+ size,
75
+ opacity,
76
+ width,
77
+ height,
78
+ }: {
79
+ resolved: ResolvedPoint[];
80
+ color: string;
81
+ size: number;
82
+ opacity: number;
83
+ width: number;
84
+ height: number;
85
+ }) {
86
+ const canvasRef = useRef<HTMLCanvasElement | null>(null);
87
+ const glRef = useRef<WebGLRenderingContext | null>(null);
88
+ const programRef = useRef<WebGLProgram | null>(null);
89
+ const bufferRef = useRef<WebGLBuffer | null>(null);
90
+ const locsRef = useRef<DotGLLocations | null>(null);
91
+ const markerRef = useRef<SVGGElement>(null);
92
+ // Bumped on context restore to force the draw effect to rebuild + repaint.
93
+ const [contextEpoch, setContextEpoch] = useState(0);
94
+
95
+ const positions = useMemo(() => {
96
+ const out: number[] = [];
97
+ for (const p of resolved) {
98
+ if (Number.isFinite(p.px) && Number.isFinite(p.py)) {
99
+ out.push(p.px, p.py);
100
+ }
101
+ }
102
+ return new Float32Array(out);
103
+ }, [resolved]);
104
+
105
+ // Mount the canvas outside the SVG and wire context-loss recovery. The canvas
106
+ // element persists across resizes; only its DOM attachment is re-established.
107
+ useEffect(() => {
108
+ const marker = markerRef.current;
109
+ if (!marker) {
110
+ return;
111
+ }
112
+ if (!canvasRef.current) {
113
+ canvasRef.current = document.createElement('canvas');
114
+ }
115
+ const canvas = canvasRef.current;
116
+ const detach = mountCanvasOverSVG(marker, canvas, width, height);
117
+ const unregister = registerContextLossHandlers(
118
+ canvas,
119
+ () => {
120
+ glRef.current = null;
121
+ programRef.current = null;
122
+ bufferRef.current = null;
123
+ locsRef.current = null;
124
+ },
125
+ () => setContextEpoch(e => e + 1),
126
+ );
127
+ return () => {
128
+ unregister();
129
+ detach?.();
130
+ };
131
+ }, [width, height]);
132
+
133
+ // Draw. The context, program, and buffer are created lazily once and reused;
134
+ // only vertex data + uniforms are re-uploaded per redraw.
135
+ useEffect(() => {
136
+ const canvas = canvasRef.current;
137
+ if (!canvas || width <= 0 || height <= 0) {
138
+ return;
139
+ }
140
+
141
+ const dpr = sizeCanvas(canvas, width, height);
142
+
143
+ if (!glRef.current) {
144
+ glRef.current = getWebGLContext(canvas);
145
+ }
146
+ const gl = glRef.current;
147
+ if (!gl || gl.isContextLost()) {
148
+ return;
149
+ }
150
+
151
+ if (!programRef.current) {
152
+ programRef.current = createProgram(gl, VERT, FRAG);
153
+ locsRef.current = null;
154
+ }
155
+ const program = programRef.current;
156
+ if (!program) {
157
+ return;
158
+ }
159
+
160
+ if (!bufferRef.current) {
161
+ bufferRef.current = gl.createBuffer();
162
+ }
163
+ const buffer = bufferRef.current;
164
+ if (!buffer) {
165
+ return;
166
+ }
167
+
168
+ if (!locsRef.current) {
169
+ locsRef.current = {
170
+ aPosition: gl.getAttribLocation(program, 'a_position'),
171
+ uResolution: gl.getUniformLocation(program, 'u_resolution'),
172
+ uColor: gl.getUniformLocation(program, 'u_color'),
173
+ uSize: gl.getUniformLocation(program, 'u_size'),
174
+ uOpacity: gl.getUniformLocation(program, 'u_opacity'),
175
+ };
176
+ }
177
+ const locs = locsRef.current;
178
+
179
+ gl.viewport(0, 0, canvas.width, canvas.height);
180
+ setupGLState(gl);
181
+ gl.useProgram(program);
182
+
183
+ gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
184
+ gl.bufferData(gl.ARRAY_BUFFER, positions, gl.STATIC_DRAW);
185
+ gl.enableVertexAttribArray(locs.aPosition);
186
+ gl.vertexAttribPointer(locs.aPosition, 2, gl.FLOAT, false, 0, 0);
187
+
188
+ const [r, g, b] = hexToGL(color);
189
+ gl.uniform2f(locs.uResolution, width, height);
190
+ gl.uniform3f(locs.uColor, r, g, b);
191
+ gl.uniform1f(locs.uSize, size * dpr);
192
+ gl.uniform1f(locs.uOpacity, opacity);
193
+
194
+ gl.drawArrays(gl.POINTS, 0, positions.length / 2);
195
+ }, [width, height, color, size, opacity, positions, contextEpoch]);
196
+
197
+ // Release all GL resources on unmount.
198
+ useEffect(() => {
199
+ return () => {
200
+ const gl = glRef.current;
201
+ if (gl) {
202
+ if (bufferRef.current) {
203
+ gl.deleteBuffer(bufferRef.current);
204
+ }
205
+ if (programRef.current) {
206
+ gl.deleteProgram(programRef.current);
207
+ }
208
+ loseContext(gl);
209
+ }
210
+ glRef.current = null;
211
+ programRef.current = null;
212
+ bufferRef.current = null;
213
+ locsRef.current = null;
214
+ };
215
+ }, []);
216
+
217
+ if (width <= 0 || height <= 0) {
218
+ return null;
219
+ }
220
+ return <g ref={markerRef} />;
221
+ }
222
+
223
+ /**
224
+ * WebGL scatter plot. Canvas mounted outside SVG for sharp Retina rendering.
225
+ *
226
+ * @example
227
+ * ```
228
+ * series={[dotGL('value', {color: '#4f46e5'})]}
229
+ * ```
230
+ */
231
+ export function dotGL(dataKey: string, options: DotGLOptions): SeriesDef {
232
+ const {color} = options;
233
+ const size = options.size ?? 6;
234
+ const opacity = options.opacity ?? 0.8;
235
+
236
+ return {
237
+ type: 'dotGL',
238
+ key: dataKey,
239
+ dataKeys: [dataKey],
240
+ layout: {},
241
+
242
+ resolve(ctx) {
243
+ const {data, xKey, xScale, yScale} = ctx;
244
+ const points: ResolvedPoint[] = [];
245
+ for (let i = 0; i < data.length; i++) {
246
+ const d = data[i];
247
+ let px: number;
248
+ if ('bandwidth' in xScale) {
249
+ px =
250
+ ((xScale as ScaleBand<string>)(String(d[xKey])) ?? 0) +
251
+ (xScale as ScaleBand<string>).bandwidth() / 2;
252
+ } else {
253
+ px = xScale(d[xKey] as number);
254
+ }
255
+ const v = typeof d[dataKey] === 'number' ? (d[dataKey] as number) : 0;
256
+ points.push({px, py: yScale(v), py0: yScale(0), dataIndex: i});
257
+ }
258
+ return points;
259
+ },
260
+
261
+ render(resolved, ctx) {
262
+ return (
263
+ <DotGLCanvas
264
+ resolved={resolved}
265
+ color={color}
266
+ size={size}
267
+ opacity={opacity}
268
+ width={ctx.width}
269
+ height={ctx.height}
270
+ />
271
+ );
272
+ },
273
+ };
274
+ }