@pluot/react 0.1.16 → 0.1.17

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/src/types.ts ADDED
@@ -0,0 +1,378 @@
1
+ import type { ReactElement } from "react";
2
+ import type {
3
+ AspectRatioMode,
4
+ AspectRatioAlignmentMode,
5
+ CameraMatrix,
6
+ StoreInput,
7
+ StoresInput,
8
+ StoresOutput,
9
+ } from "@pluot/core";
10
+
11
+ // TODO: auto-generate the types that mirror Rust structs/enums:
12
+ // https://github.com/keller-mark/pluot/issues/133
13
+
14
+ // === Plot params ===
15
+
16
+ /** Mirrors the Rust `ViewMode` enum (serde-renamed to lowercase). */
17
+ export type ViewMode = "2d" | "3d";
18
+
19
+ /** Mirrors the Rust `GraphicsFormat` enum. */
20
+ export type GraphicsFormat = "Raster" | "Vector";
21
+
22
+ /** Mirrors the adjacently-tagged Rust `PlotParams` enum discriminant. */
23
+ export type PlotType = "LayeredPlot";
24
+
25
+ /**
26
+ * One entry of `PlotParams.layers`, mirroring the adjacently-tagged Rust
27
+ * `LayerParams` enum: `{ "layer_type": "PointLayer", "layer_params": { ... } }`.
28
+ *
29
+ * `layer_type` must name a registered layer (see the `LayerParams` enum in
30
+ * `crates/pluot/src/render_params.rs`), and the shape of `layer_params`
31
+ * depends on which layer was named.
32
+ */
33
+ export type LayerParams = {
34
+ layer_type: string;
35
+ layer_params: Record<string, unknown>;
36
+ };
37
+
38
+ /** Mirrors the Rust `LayeredPlotRenderParams` struct. */
39
+ export type PlotParams = {
40
+ layers: LayerParams[];
41
+ };
42
+
43
+ /**
44
+ * The object handed to the wasm `render_wasm` / `pick_wasm` functions.
45
+ * Mirrors the Rust `RenderParams` struct (snake_case, unlike the props of
46
+ * the {@link PluotProps} React API).
47
+ */
48
+ export type RenderParams = {
49
+ schema_version: string | null;
50
+ width: number;
51
+ height: number;
52
+ format: GraphicsFormat;
53
+ margin_top: number;
54
+ margin_right: number;
55
+ margin_bottom: number;
56
+ margin_left: number;
57
+ device_pixel_ratio: number;
58
+ aspect_ratio_mode: AspectRatioMode;
59
+ aspect_ratio_alignment_mode: AspectRatioAlignmentMode;
60
+ view_mode: ViewMode;
61
+ pickable: boolean;
62
+ camera_view: CameraMatrix | null;
63
+ plot_id: string;
64
+ plot_type: PlotType;
65
+ stores: StoresOutput | undefined;
66
+ plot_params: PlotParams;
67
+ /** In milliseconds. Has no effect when `wait_for_store_gets` is false. */
68
+ timeout: number | null;
69
+ wait_for_store_gets: boolean;
70
+ cache_enabled: boolean;
71
+ svg_compression_enabled: boolean;
72
+ svg_include_document: boolean;
73
+ };
74
+
75
+ // === Picking results ===
76
+
77
+ /** Mirrors the Rust `ScreenCoord` struct. Y increases upwards. */
78
+ export type ScreenCoord = {
79
+ x: number;
80
+ y: number;
81
+ };
82
+
83
+ /** Mirrors the externally-tagged Rust `DataCoord` enum. */
84
+ export type DataCoord =
85
+ | { TwoD: { x: number; y: number } }
86
+ | { ThreeD: { x: number; y: number; z: number } };
87
+
88
+ /** Mirrors the Rust `LayerPickingResult` struct. */
89
+ export type LayerPickingResult = {
90
+ layer_id: string;
91
+ info: Record<string, string>;
92
+ };
93
+
94
+ /**
95
+ * Mirrors the Rust `PickingResult` struct, after normalization
96
+ * of the `info` Maps (produced by serde-wasm-bindgen) to plain objects.
97
+ *
98
+ * Note: `serde_wasm_bindgen` serializes a Rust `None` as `undefined`
99
+ * (not `null`), so `data_coord` is absent rather than null when picking
100
+ * did not resolve to a data coordinate.
101
+ */
102
+ export type PickingResult = {
103
+ data_coord: DataCoord | undefined;
104
+ screen_coord: ScreenCoord;
105
+ layer_results: LayerPickingResult[];
106
+ };
107
+
108
+ /**
109
+ * The un-normalized shape that `pick_wasm` actually resolves to. It is typed
110
+ * `any` on the wasm-bindgen side, so this type is what documents the wire
111
+ * format: `serde_wasm_bindgen` converts the Rust `HashMap` behind `info` into
112
+ * a JS `Map`, which {@link PickingResult} flattens to a plain object.
113
+ */
114
+ export type RawLayerPickingResult = Omit<LayerPickingResult, "info"> & {
115
+ info: Map<string, string>;
116
+ };
117
+
118
+ export type RawPickingResult = Omit<PickingResult, "layer_results"> & {
119
+ layer_results: RawLayerPickingResult[];
120
+ };
121
+
122
+ // === Tooltip ===
123
+
124
+ /**
125
+ * What a {@link PluotProps.onHover} callback may return for the tooltip to
126
+ * render. A plain object is rendered as a key/value table when `asTable`
127
+ * is set, and as pretty-printed JSON otherwise.
128
+ */
129
+ export type TooltipContent =
130
+ | string
131
+ | number
132
+ | ReactElement
133
+ | Record<string, unknown>
134
+ | null
135
+ | undefined;
136
+
137
+ export type TooltipProps = {
138
+ content: TooltipContent;
139
+ /** Render a plain-object `content` as a two-column key/value table. */
140
+ asTable?: boolean;
141
+ };
142
+
143
+ /** The hovered point plus the tooltip content to show for it. */
144
+ export type HoverInfo = {
145
+ content: TooltipContent;
146
+ /** Mouse position in the coordinate space of the outer (width x height) container. */
147
+ mouseX: number;
148
+ mouseY: number;
149
+ };
150
+
151
+ // === Brushing ===
152
+
153
+ /**
154
+ * Which representation of a {@link BrushVertex} is authoritative for an axis.
155
+ *
156
+ * - `Pixels`: relative to the top-left of the outer (width x height) container,
157
+ * with Y increasing downwards (the DOM/SVG convention). Unaffected by the camera.
158
+ * - `Data`: the data coordinate under the current camera, as reported by
159
+ * `getBounds`, with Y increasing upwards. A brush in this mode is pinned to the
160
+ * data, so it moves on screen as the user zooms/pans.
161
+ * - `Normalized`: a 0-to-1 fraction of the brushable region, with Y increasing
162
+ * upwards (0 at the bottom edge, 1 at the top edge). Unaffected by the camera.
163
+ */
164
+ export type BrushUnitsMode = "Pixels" | "Data" | "Normalized";
165
+
166
+ // For each brushed rect/polygon vertex,
167
+ // we represent it using all units modes simultaneously.
168
+ // Only the representation matching `brushUnitsModeX`/`brushUnitsModeY` is
169
+ // authoritative; the other two are derived from it and are recomputed whenever
170
+ // the camera, the container size, or the margins change.
171
+ export type BrushVertex = {
172
+ // Data unitsMode.
173
+ x_data: number,
174
+ y_data: number,
175
+ // Pixels unitsMode.
176
+ x_pixels: number,
177
+ y_pixels: number,
178
+ // Normalized unitsMode.
179
+ x_normalized: number,
180
+ y_normalized: number,
181
+ };
182
+
183
+ /**
184
+ * The shape the user draws, and which the resulting {@link BrushState} holds.
185
+ *
186
+ * - `Rect`: click and drag to draw a rectangle.
187
+ * - `Polygon`: click and drag to draw a lasso, defining vertices as the user
188
+ * drags. The number of vertices is limited by using lodash-es throttle.
189
+ * - `RangeX`: select a horizontal range. The overlay renders as a rectangle
190
+ * which takes up the full brush height, according to the brush margins.
191
+ * - `RangeY`: select a vertical range. The overlay renders as a rectangle
192
+ * which takes up the full brush width, according to the brush margins.
193
+ */
194
+ export type BrushMode = 'Rect' | 'Polygon' | 'RangeX' | 'RangeY';
195
+
196
+ /** The axis-aligned modes, all of which are stored as four rectangle corners. */
197
+ export type RectLikeBrushMode = Exclude<BrushMode, 'Polygon'>;
198
+
199
+ export type BrushState = {
200
+ // Is the user still drawing, or have they completed their drag interaction?
201
+ status: 'Drawing' | 'Complete';
202
+ shape: BrushMode,
203
+ // For every shape but Polygon, always four corners ordered clockwise in pixel
204
+ // space starting from the top-left, so corner `i` is diagonally opposite
205
+ // corner `(i + 2) % 4`.
206
+ // For RangeX and RangeY, the axis that is not being selected always spans the
207
+ // full brushable extent, so it is re-pinned whenever that extent changes.
208
+ vertices: BrushVertex[],
209
+ };
210
+
211
+ /**
212
+ * The value of {@link PluotProps.brush} meaning "controlled, but nothing is
213
+ * brushed right now".
214
+ *
215
+ * `undefined` cannot play this role: a prop that was never passed is
216
+ * indistinguishable from one explicitly set to `undefined`, and an absent
217
+ * `brush` has to mean uncontrolled. A parent that controls the brush therefore
218
+ * passes `NO_BRUSH` rather than `undefined` to show no brush, which keeps it
219
+ * controlled across the empty state instead of silently handing control back.
220
+ */
221
+ export const NO_BRUSH = "NoBrush";
222
+ export type NoBrush = typeof NO_BRUSH;
223
+
224
+ // TODO: On the rust side, define a Brushable.brush trait, analogous to Pickable.pick.
225
+ export type BrushResult = {
226
+ // Similar to picking, upon brush, the Rust side can return a per-layer Map with essentially any data
227
+ // (such as the list of entity IDs within the brushed region).
228
+ // The rust side can also return a new rect/polygon to "snap"/quantize to.
229
+ // TODO: fill in the rest of this struct.
230
+ };
231
+
232
+ // === Component props ===
233
+
234
+ export type PluotProps = {
235
+ /**
236
+ * The schema version used to generate the plot, for forward compatibility.
237
+ * A mismatch with the Rust crate version logs a warning.
238
+ */
239
+ schemaVersion?: string | null;
240
+ /** Width of the plot, in pixels. */
241
+ width: number;
242
+ /** Height of the plot, in pixels. */
243
+ height: number;
244
+ /**
245
+ * Unique-per-page plot ID, used to key caches of intermediate values.
246
+ * Also the default store name when `store` is used without `storeName`.
247
+ */
248
+ plotId: string;
249
+ plotType: PlotType;
250
+ plotParams: PlotParams;
251
+
252
+ /**
253
+ * A single Zarr store: a URL string, a zarrita store instance,
254
+ * or already-derived `ZarrStoreInfo` metadata.
255
+ * Mutually exclusive with `stores`.
256
+ */
257
+ store?: StoreInput;
258
+ /** The name to register `store` under. Defaults to `plotId`. */
259
+ storeName?: string;
260
+ /** Multiple Zarr stores, keyed by store name. Mutually exclusive with `store`. */
261
+ stores?: StoresInput;
262
+ /**
263
+ * Whether to register the store(s) with the wasm module.
264
+ * Set to false when they have already been registered elsewhere.
265
+ */
266
+ registerStores?: boolean;
267
+
268
+ viewMode?: ViewMode;
269
+ format?: GraphicsFormat;
270
+ marginTop?: number;
271
+ marginRight?: number;
272
+ marginBottom?: number;
273
+ marginLeft?: number;
274
+ aspectRatioMode?: AspectRatioMode;
275
+ aspectRatioAlignmentMode?: AspectRatioAlignmentMode;
276
+ /** Outline the margin box and the plot area, to help debug layout. */
277
+ debugMargins?: boolean;
278
+ backgroundColor?: string;
279
+
280
+ /** Lower bound (in ms) of the exponential backoff between bailed-early renders. */
281
+ minTimeout?: number;
282
+ /** Upper bound (in ms) of the exponential backoff between bailed-early renders. */
283
+ maxTimeout?: number;
284
+ /** Whether a new render may start while a previous one is still in flight. */
285
+ allowSimultaneousRenders?: boolean;
286
+
287
+ /**
288
+ * The 4x4 camera matrix. Without `setCameraMatrix`, this is treated as the
289
+ * initial value only, and the camera is managed internally.
290
+ */
291
+ cameraMatrix?: CameraMatrix | null;
292
+ /** Provide to take control of the camera matrix. */
293
+ setCameraMatrix?: ((cameraMatrix: CameraMatrix) => void) | null;
294
+
295
+ /** Whether clicking should run a picking query and call `onClick`. */
296
+ enableClick?: boolean;
297
+ /** Whether hovering should run a picking query and show a tooltip via `onHover`. */
298
+ enableTooltip?: boolean;
299
+ onClick?: ((result: PickingResult) => void) | null;
300
+ onHover?: ((result: PickingResult) => TooltipContent) | null;
301
+
302
+ // Brushing supports both a rectangular brush and a lasso (i.e., polygonal) brush.
303
+ // We draw a brush overlay as an SVG to indicate the drawn rect/polygon (both during the draw interactions and following completion).
304
+ // The brush overlay consists of either a rectangle with circle elements at its corner vertices,
305
+ // or a circle element at each polygon vertex, with lines connecting the polygon vertices.
306
+
307
+ // When the brush units mode is "Data", the brushed overlay rect/polygon is dependent on the camera matrix and responds to camera state updates.
308
+ // As the user zooms/pans, the overlay updates if the unitsMode is "Data" in either the X, Y, or XY directions.
309
+ // Both default to "Data".
310
+ brushUnitsModeX?: BrushUnitsMode;
311
+ brushUnitsModeY?: BrushUnitsMode;
312
+
313
+ // The brush margins restrict the brushable region to within the specified brush bounds.
314
+ // Each defaults to the corresponding layer margin, so by default the brushable region is the layer region.
315
+ // However, when brushUnitsModeY is "Data", we ignore brushMarginTop and brushMarginBottom, and instead the layer (i.e., camera) bounds (marginTop and marginBottom) take precedence.
316
+ brushMarginTop?: number;
317
+ brushMarginBottom?: number;
318
+ // However, when brushUnitsModeX is "Data", we ignore brushMarginLeft and brushMarginRight, and instead the layer (i.e., camera) bounds (marginLeft and marginRight) take precedence.
319
+ brushMarginLeft?: number;
320
+ brushMarginRight?: number;
321
+
322
+ // When true, the user can draw a brush rect/polygon by long-clicking and then dragging.
323
+ enableBrushCreate?: boolean;
324
+ // When true, the user can modify the vertices of persisted brushes (uncontrolled) or brushes passed via `brush` prop (controlled) by interacting with the overlay.
325
+ // For Rect, RangeX and RangeY, the user can also drag a side of the overlay to extend the brush in that direction alone.
326
+ // A range brush only exposes the two sides on the axis it selects, since the other axis always spans the whole brushable region.
327
+ enableBrushEdit?: boolean;
328
+ // When true, we display a clear button upon hovering the brush rect/polygon, to allow the user to clear/cancel the brush.
329
+ enableBrushClear?: boolean;
330
+
331
+ // Long-click of 1.5s to trigger a brushing interaction. If the user long-clicks for this amount of milliseconds, then they can being drawing the brush rect/lasso.
332
+ // Only relevant when enableBrushCreate is true.
333
+ // By default, 1500 ms.
334
+ brushDelay?: number;
335
+
336
+ // When a user has begun to click-and-hold for this amount of ms, we render a small circle at the current mouse cursor position, and animate the circle "filling" by rendering a wedge (slice of pie) with a larger angle until the wedge fills the whole pie (finishing at the specified brushDelay duration).
337
+ // Only relevant when enableBrushCreate is true.
338
+ // By default, 250ms.
339
+ maybeBrushDelay?: number;
340
+
341
+ // If true, the brush overlay should remain after the drag interaction.
342
+ // If false, the brush overlay should be removed upon the end of the drag interaction, after calling onBrushEnd.
343
+ persistBrush?: boolean;
344
+
345
+ // Which shape the user draws. By default, "Rect".
346
+ brushMode?: BrushMode;
347
+
348
+ // Color of the brush overlay (outline and vertex/edge handles). The fill uses
349
+ // this same color at reduced opacity. By default, "#3b6ea5".
350
+ brushColor?: string;
351
+
352
+ // For brushing, we support both controlled and uncontrolled (similar to the cameraMatrix/setCameraMatrix).
353
+ // When controlled, the parent provides the brush state (rect/polygon vertices) or `NO_BRUSH`.
354
+ // When uncontrolled, the value of `brush` is `null` (or the prop is omitted), so the brush state will be managed internally.
355
+ // When controlled via parent, we ignore the persistBrush prop; instead, the brush persists while the BrushState is specified/present.
356
+ // If null or absent, we take this to mean uncontrolled.
357
+ // If a BrushState object or `NO_BRUSH` is provided, we take this to mean controlled.
358
+ // A controlled parent must use `NO_BRUSH` rather than `undefined` for the empty
359
+ // state, since `undefined` is indistinguishable from the prop being omitted and
360
+ // would hand control back mid-interaction, resurfacing whatever the internal
361
+ // (uncontrolled) state last held.
362
+ // Note that when controlled, enableBrushCreate can be false (the user cannot long-click to draw a new brush),
363
+ // but the parent may still provide a brush value.
364
+ // When controlled, we only emit onBrush/onBrushEnd for internally-triggered updates (e.g., if enableBrushEdit is true) or clearing (e.g., if enableBrushClear is true).
365
+ brush?: BrushState | NoBrush | null;
366
+
367
+ // Called on drag interactions, as the user is drawing the brush rect/polygon.
368
+ // Also called if the brushed rect/polygon is edited (e.g., by dragging a vertex of a persisted brush, if enableBrushEdit is true).
369
+ // Note: until the Rust `Brushable` trait exists, there is nothing to snap to, so
370
+ // `snappedState` is the same object as `state` and the returned `BrushResult` is ignored.
371
+ onBrush?: (state: BrushState, snappedState: BrushState) => BrushResult,
372
+ // Called at the conclusion of the drag interaction, with the final (i.e., complete) brush rect/polygon.
373
+ onBrushEnd?: (state: BrushState, snappedState: BrushState) => BrushResult,
374
+
375
+ // Called upon the user cancelling the brush, e.g., by clicking a clear button which appears when hovering the drawn rect/polygon.
376
+ onBrushClear?: (state: BrushState) => void,
377
+
378
+ };