import type { Domain, NumArray, NumericType } from '@h5web/shared'; import { assertDefined, assertLength, formatTooltipErr, formatTooltipVal, ScaleType, } from '@h5web/shared'; import type { NdArray } from 'ndarray'; import type { ReactElement, ReactNode } from 'react'; import { useMemo } from 'react'; import type { DefaultInteractionsConfig } from '../../interactions/DefaultInteractions'; import DefaultInteractions from '../../interactions/DefaultInteractions'; import ResetZoomButton from '../../toolbar/floating/ResetZoomButton'; import { useAxisDomain, useAxisValues, useValueToIndexScale, useCssColors, } from '../hooks'; import type { AxisParams } from '../models'; import TooltipMesh from '../shared/TooltipMesh'; import VisCanvas from '../shared/VisCanvas'; import { DEFAULT_DOMAIN, extendDomain, formatNumType } from '../utils'; import DataCurve from './DataCurve'; import styles from './LineVis.module.css'; import type { AuxiliaryParams, TooltipData } from './models'; import { CurveType } from './models'; interface Props { dataArray: NdArray; domain: Domain | undefined; scaleType?: ScaleType; curveType?: CurveType; showGrid?: boolean; abscissaParams?: AxisParams; ordinateLabel?: string; title?: string; dtype?: NumericType; errorsArray?: NdArray; showErrors?: boolean; auxiliaries?: AuxiliaryParams[]; renderTooltip?: (data: TooltipData) => ReactElement; children?: ReactNode; interactions?: DefaultInteractionsConfig; } function LineVis(props: Props) { const { dataArray, domain, curveType = CurveType.LineOnly, showGrid = true, scaleType = ScaleType.Linear, abscissaParams = {}, ordinateLabel, title, dtype, errorsArray, showErrors = false, auxiliaries = [], renderTooltip, children, interactions, } = props; const { label: abscissaLabel, value: abscissaValue, scaleType: abscissaScaleType, } = abscissaParams; assertLength(abscissaValue, dataArray.size, 'abscissa'); assertLength(errorsArray, dataArray.size, 'error'); auxiliaries.forEach(({ label, array }, index) => assertLength(array, dataArray.size, `'${label || index}' auxiliary`) ); const abscissas = useAxisValues(abscissaValue, dataArray.size); const abscissaToIndex = useValueToIndexScale(abscissas, true); const abscissaDomain = useAxisDomain(abscissas, abscissaScaleType, 0.01); assertDefined(abscissaDomain, 'Abscissas have undefined domain'); const dataDomain = useMemo(() => { return domain ? extendDomain(domain, 0.05, scaleType) : DEFAULT_DOMAIN; }, [scaleType, domain]); const [[curveColor, auxColorList], rootRef] = useCssColors([ '--curve-color', '--aux-colors', ]); const auxColors = auxColorList.split(',').map((col) => col.trim()); // support comma-separated list of colors return (
{ const xi = abscissaToIndex(x); const abscissa = abscissas[xi]; if (renderTooltip) { return renderTooltip({ abscissa, xi, x }); } const value = dataArray.get(xi); const error = errorsArray?.get(xi); return ( <> {`${abscissaLabel ?? 'x'} = ${formatTooltipVal(abscissa)}`}
{auxiliaries.length > 0 && ( )} {formatTooltipVal(value)} {error !== undefined && ` ±${formatTooltipErr(error)}`} {dtype && {` (${formatNumType(dtype)})`}}
{auxiliaries.map(({ label, array, errors }, index) => (
{label ? `${label} = ` : ''} {formatTooltipVal(array.get(xi))} {errors && ` ±${formatTooltipErr(errors.get(xi))}`}
))} ); }} /> {auxiliaries.map(({ array, label, errors }, i) => ( ))} {children}
); } export type { Props as LineVisProps }; export default LineVis;