UNPKG

@macrostrat/column-components

Version:

React rendering primitives for stratigraphic columns

151 lines (136 loc) 3.77 kB
import { scaleLinear, ScaleContinuousNumeric, ScaleLinear } from "d3-scale"; import React, { createContext, useContext, useMemo } from "react"; import h from "@macrostrat/hyper"; type HeightRange = number; type ColumnScale = ScaleContinuousNumeric<HeightRange, number> | any; type ColumnScaleClamped = ScaleContinuousNumeric<number, number>; export declare interface ColumnDivision { section_id: string; id: number; surface: number; bottom: number; top: number; // Extra properties that are there for legacy purposes flooding_surface_order?: number; grainsize?: string; covered?: boolean; // Used for boundary management control definite_boundary?: boolean; facies?: string; } enum ColumnAxisType { AGE = "age", HEIGHT = "height", DEPTH = "depth", ORDINAL = "ordinal", } export interface ColumnCtx<T extends ColumnDivision> { divisions: T[]; scaleClamped: ColumnScaleClamped; pixelsPerMeter: number; scale: ColumnScale; axisType?: ColumnAxisType; pixelHeight?: number; zoom: number; } export const ColumnContext = createContext<ColumnCtx<ColumnDivision>>({ scale: scaleLinear(), divisions: [], scaleClamped: scaleLinear().clamp(true), pixelsPerMeter: 1, zoom: 1, }); export interface ColumnProviderProps<T extends ColumnDivision> { pixelsPerMeter?: number; divisions: T[]; range?: HeightRange | any; height?: number; zoom?: number; width?: number; axisType?: ColumnAxisType; children?: any; scale?: ColumnScale; } function ColumnProvider<T extends ColumnDivision>( props: ColumnProviderProps<T>, ) { /** Lays out a column on its Y (height) axis. This component would be swapped to provide eventual generalization to a Wheeler-diagram (time-domain) framework. */ let { children, pixelsPerMeter = 20, zoom = 1, height, range, divisions = [], width = 150, axisType = ColumnAxisType.HEIGHT, scale: _scale, ...rest } = props; // Check if "rest" actually changed // This is a hack to avoid re-rendering the column // when the "rest" props change const restStr = JSON.stringify(rest); const restRef = React.useRef(null); if (restStr !== restRef.current) { restRef.current = restStr; if (Object.keys(rest).length > 0) { console.warn( "Passing extra properties to ColumnProvider is deprecated:", rest, ); } } //# Calculate correct range and height // Range overrides height if set const value: ColumnCtx<T> = useMemo(() => { if (range != null) { height = Math.abs(range[1] - range[0]); } else { range = [0, height]; } // same as the old `innerHeight` let scale = _scale; let pixelHeight: number; if (scale == null) { pixelHeight = height * pixelsPerMeter * zoom; scale = scaleLinear().domain(range).range([pixelHeight, 0]); } else { pixelHeight = Math.abs(scale.range()[1] - scale.range()[0]); // Remove any offset that might exist from paddings, scale breaks, etc. const r1 = scale.range().map((d) => d - scale.range()[0]); scale = _scale.copy().range(r1); } const scaleClamped = scale.copy().clamp(true); return { pixelsPerMeter, pixelHeight, zoom, range, height, scale, scaleClamped, divisions, width, axisType, ...rest, }; }, [ axisType, height, pixelsPerMeter, range, zoom, divisions, width, restRef.current, ]); return h(ColumnContext.Provider, { value }, children); } const useColumn = () => useContext(ColumnContext); const useColumnDivisions = () => useContext(ColumnContext).divisions; export { ColumnProvider, ColumnAxisType, useColumnDivisions, useColumn };