import React, { useState, useEffect, useMemo } from 'react' import { useGG, themeState, strokeScaleState, strokeDasharrayState, formatMissing, IScale, LegendOrientation, } from '@graphique/graphique' import { useAtom } from 'jotai' export interface CategoricalLegendProps { legendData: Datum[] legendScales: IScale orientation?: LegendOrientation labelFormat?: (v: any, i: number) => string fontSize?: string | number onSelection?: (v: string) => void ignoreDasharray?: boolean } export const CategoricalLegend = ({ legendData, legendScales, orientation, labelFormat, fontSize = 12, onSelection, ignoreDasharray, }: CategoricalLegendProps) => { const [{ geoms, defaultStroke, legend }] = useAtom(themeState) const [{ domain: strokeDomain }] = useAtom(strokeScaleState) const [{ domain: dashArrayDomain }] = useAtom(strokeDasharrayState) const legendGroups = useMemo( () => strokeDomain || dashArrayDomain || legendScales.groups || legendScales.strokeScale?.domain() || legendScales.strokeDasharrayScale?.domain(), [legendScales, strokeDomain, dashArrayDomain], ) const [isFocused, setIsFocused] = useState( geoms?.line?.usableGroups ?? [], ) const { ggState, updateData } = useGG() || {} const { data } = ggState || {} const [firstRender, setFirstRender] = useState(true) useEffect(() => { const timeout = setTimeout(() => setFirstRender(false), 0) return () => clearTimeout(timeout) }, []) useEffect(() => { setIsFocused(legendGroups ?? []) }, []) const getGroup = useMemo( () => geoms?.line?.groupAccessor || (() => undefined), [geoms], ) useEffect(() => { const dataGroups = Array.from(new Set(data!.map(getGroup))) as [] setIsFocused(dataGroups ?? []) }, [data, getGroup]) const isHorizontal = orientation === LegendOrientation.H const toggleLegendGroup = (g: string) => { const prevFocused = isFocused let focusedGroups if (prevFocused.includes(g)) { if (prevFocused.length === 1) { focusedGroups = legendScales.groups as string[] } else { focusedGroups = prevFocused.filter((p) => p !== g) } } else { focusedGroups = [...prevFocused, g] } setIsFocused(focusedGroups) const includedGroups = Array.from(new Set(data?.map((d) => getGroup(d)))) if (onSelection) { onSelection(g) } if (data && updateData) { let updatedData: Datum[] if (includedGroups.includes(g)) { if (includedGroups.length === 1) { updatedData = legendData } else { updatedData = data.filter((d) => getGroup(d) !== g) } } else { updatedData = legendData.filter( (d) => includedGroups.includes(getGroup(d)) || getGroup(d) === g, ) } updateData(updatedData) } } return (
{geoms?.line && legendGroups?.map((g: string, i, groups) => { const strokeOpacity = isFocused.includes(g) ? geoms?.line?.strokeOpacity : geoms?.line?.strokeOpacity || 1 * 0.5 return (
{ if (['Enter', ' '].includes(e.key)) { toggleLegendGroup(g) } }} onClick={() => toggleLegendGroup(g)} >
{labelFormat ? labelFormat(g, i) : formatMissing(g)}
) })}
) }