packages/core/src/features/viz/lib/reference-band.tsx
This is the source snapshot used to build these API details. View this revision on GitHub.
1 import { Path, resolveSignalValue, type SignalValue } from "@pibbl/core";
2 import type { SignalStyle } from "@pibbl/core/internal";
3 import type { AxisStyle } from "./types.js";
4 import type { ChartLineProps } from "./chart-lines.js";
5 import { lookupScale } from "./scale-context.js";
6 import { opacity } from "./paint.js";
7 /** A continuous domain interval painted across an explicit local pixel span. See {@link ReferenceBand}. */
8 export interface ReferenceBandProps extends Omit<ChartLineProps, "style"> {
9 /** Two finite in-domain numeric endpoints; either order, including equal values. */
10 readonly values: SignalValue<readonly [number, number]>;
11 /** Fill defaults to #dce5e8; opacity defaults to 0.35. */
12 readonly style?: SignalValue<SignalStyle<Pick<AxisStyle, "fill" | "opacity">>>;
13 }
14 /** Paint a decorative interval without owning layout, labels, or hit geometry.
15 * @param props - Scale geometry and presentation. See {@link ReferenceBandProps}.
16 * @returns Decorative chart content in the caller's coordinate space.
17 */
18 export function ReferenceBand(props: ReferenceBandProps) {
19 if (props.direction !== "horizontal" && props.direction !== "vertical") throw new TypeError("ReferenceBand direction must be horizontal or vertical.");
20 const source = lookupScale(props.scale, "ReferenceBand");
21 if (!source) throw new TypeError("ReferenceBand requires a scale ID.");
22 const scale = source.get();
23 if (scale.type === "point" || scale.type === "band") throw new TypeError("ReferenceBand requires a continuous scale.");
24 const values = resolveSignalValue(props.values), span = resolveSignalValue(props.span);
25 for (const pair of [values, span]) if (!Array.isArray(pair) || pair.length !== 2 || !pair.every(value => typeof value === "number" && Number.isFinite(value))) throw new RangeError("ReferenceBand values and span require two finite endpoints.");
26 const lo = Math.min(...scale.domain), hi = Math.max(...scale.domain);
27 if (values.some(value => value < lo || value > hi)) throw new RangeError("ReferenceBand values must be within the scale domain.");
28 const a = scale.map(values[0]), b = scale.map(values[1]);
29 const start = Math.min(...span), length = Math.abs(span[1] - span[0]);
30 if (!Number.isFinite(length)) throw new RangeError("ReferenceBand span length must be finite.");
31 const path = new Path2D();
32 if (props.direction === "horizontal") path.rect(start, Math.min(a, b), length, Math.abs(b - a));
33 else path.rect(Math.min(a, b), start, Math.abs(b - a), length);
34 const style = resolveSignalValue(props.style) ?? {};
35 return <Path pointerEvents="none" style={{ d: path, fill: resolveSignalValue(style.fill) ?? "#dce5e8", opacity: opacity(resolveSignalValue(style.opacity) ?? 0.35, "ReferenceBand") }} />;
36 }
37
Documentation version
Section titled “Documentation version”Documentation built with @pibbl/core 0.0.2, revision 2dccb19. ALPHA — NOT FOR PRODUCTION USE.