UNPKG

@macrostrat/column-components

Version:

React rendering primitives for stratigraphic columns

126 lines (103 loc) 2.86 kB
import { useContext, useEffect, useRef } from "react"; import h from "./hyper"; import { select } from "d3-selection"; import { axisLeft } from "d3-axis"; import { ScaleContinuousNumeric, scaleLinear, ScaleLinear } from "d3-scale"; import { useColumn } from "./context"; interface ColumnAxisProps { ticks?: number; tickArguments?: any; tickValues?: any; tickFormat?: any; tickSize?: any; tickSizeInner?: any; tickSizeOuter?: any; tickPadding?: any; tickSpacing?: number; showLabel?: (d: any) => boolean; showDomain?: boolean; className?: string; } interface AgeAxisProps extends ColumnAxisProps { scale?: ScaleContinuousNumeric<number, number>; minTickSpacing?: number; } const __d3axisKeys = [ "ticks", "tickArguments", "tickValues", "tickFormat", "tickSize", "tickSizeInner", "tickSizeOuter", "tickPadding", ]; export function ColumnAxis(props: ColumnAxisProps) { const { scale } = useColumn(); return h(AgeAxis, { scale, ...props }); } export function AgeAxis(props: AgeAxisProps) { const { showLabel, className, showDomain = true, tickSpacing = 60, minTickSpacing = 20, scale, } = props; const range = scale.range(); const pixelHeight = Math.abs(range[0] - range[range.length - 1]); let tickValues: number[] = undefined; let ticks = Math.round(pixelHeight / tickSpacing); if (pixelHeight < 3 * tickSpacing) { // Push ticks towards extrema (we need more than 2 to be resolved) let t0: number[] = []; while (t0.length <= 2) { ticks += 1; t0 = scale.ticks(ticks); } tickValues = t0; if (pixelHeight < 2 * tickSpacing) { // Only show first and last ticks tickValues = [t0[0], t0[t0.length - 1]]; } } if (pixelHeight < minTickSpacing) { ticks = 1; tickValues = scale.ticks(1); // Get the last tick value only tickValues = [tickValues[0]]; } const defaultProps = { ticks, // Suppress domain endpoints tickSizeOuter: 0, tickValues, }; const ref = useRef(null); const axisRef = useRef(axisLeft()); const deps = __d3axisKeys.map((k) => props[k]); useEffect(() => { const el = ref.current; if (!el) return; axisRef.current.scale(scale); for (let k of __d3axisKeys) { const val = props[k] ?? defaultProps[k]; if (val == null) continue; axisRef.current[k](val); } const ax = select(el).call(axisRef.current); if (!showDomain) { ax.select(".domain").remove(); } ax.selectAll(".tick text").each(function (d) { if (!(showLabel?.(d) ?? true)) { select(this).attr("visibility", "hidden"); } }); return () => { select(el).selectAll("*").remove(); }; }, [scale, ref.current, showDomain, showLabel, ...deps]); return h("g.y.axis.column-axis", { className, ref }); }