Skip to content

packages/core/src/features/viz/lib/reference-band.tsx

Read as Markdown

This is the source snapshot used to build these API details. View this revision on GitHub.

Back to reference

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 built with @pibbl/core 0.0.2, revision 272a94a. ALPHA — NOT FOR PRODUCTION USE.