UNPKG

@graphique/geom-line

Version:

For line charts and other lines

1 lines 58.8 kB
{"version":3,"sources":["../src/index.ts","../src/geomLine.tsx","../src/tooltip/Tooltip.tsx","../src/tooltip/DefaultTooltip.tsx","../src/tooltip/LineMarker.tsx","../src/types/index.ts","../src/legend/AppearanceLegend.tsx","../src/legend/CategoricalLegend.tsx"],"sourcesContent":["export { GeomLine, type GeomProps } from './geomLine'\nexport { Legend, type LegendProps } from './legend'\nexport { type GeomAes, Entrance, FocusType } from './types'\n","import React, {\n useEffect,\n useMemo,\n useRef,\n SVGAttributes,\n useState,\n CSSProperties,\n} from 'react'\nimport {\n useGG,\n themeState,\n tooltipState,\n generateID,\n EventArea,\n BrushAction,\n isDate,\n focusNodes,\n unfocusNodes,\n strokeScaleState,\n VisualEncodingTypes,\n defaultScheme,\n usePageVisibility,\n defaultDasharrays,\n strokeDasharrayState,\n} from '@graphique/graphique'\nimport { Animate } from 'react-move'\nimport { easeCubic } from 'd3-ease'\nimport { interpolate } from 'd3-interpolate'\nimport { interpolatePath } from 'd3-interpolate-path'\nimport { line, CurveFactory, curveLinear } from 'd3-shape'\nimport { scaleOrdinal } from 'd3-scale'\nimport { useAtom } from 'jotai'\nimport { LineMarker, Tooltip } from './tooltip'\nimport { Entrance, FocusType, type GeomAes } from './types'\n\nexport interface GeomProps<Datum> {\n /**\n * **data used by this Geom**\n *\n * This will overwrite top-level `data` passed to `GG` as it relates to mappings defined in `aes`.\n */\n data?: Datum[]\n /**\n * **functional mapping applied to `data` for this Geom**\n *\n * This extends the top-level `aes` passed to `GG`. Any repeated mappings defined here will take precedence within the Geom.\n */\n aes?: GeomAes<Datum>\n /** attributes passed to the underlying SVG elements */\n attr?: SVGAttributes<SVGPathElement>\n /** should this Geom have a tooltip associated with it (_default_: `true`) */\n showTooltip?: boolean\n /** determines what happens when brushing (clicking and dragging) over the drawing area */\n brushAction?: BrushAction\n /** used for programmatic zooming, should the zoom out button be hidden */\n isZoomedOut?: boolean\n /** [d3 curve](https://d3js.org/d3-shape/curve) factory imported from `d3-shape` (_default_: `curveLinear`) */\n curve?: CurveFactory\n /** should this Geom have a line marker for its focused data (_default_: `true`) */\n showLineMarker?: boolean\n /** radius in pixels of the line marker's points (_default_: `3.5`) */\n markerRadius?: number\n /** stroke color of the line marker's points (_default_: `\"#ffffff\"`) */\n markerStroke?: string\n /** where elements should start as they enter the drawing area (_default_: `Entrance.MIDPOINT`) */\n entrance?: Entrance\n /** determines how pointer events focus data (_default_: `FocusType.X`) */\n focusType?: FocusType\n /** styles applied to focused elements */\n focusedStyle?: CSSProperties\n /** styles applied to unfocused elements */\n unfocusedStyle?: CSSProperties\n /** callback called for mousemove events on the drawing area when focusing data */\n onDatumFocus?: (data: Datum[], index: number[]) => void\n /** callback called for click events on the drawing area when selecting focused data */\n onDatumSelection?: (data: Datum[], index: number[]) => void\n /** callback called for mouseleave events on the drawing area */\n onExit?: () => void\n /** should elements enter/update/exit with animated transitions */\n isAnimated?: boolean\n}\n\nconst GeomLine = <Datum,>({\n data: localData,\n aes: localAes,\n showTooltip = true,\n showLineMarker = true,\n brushAction,\n isZoomedOut,\n curve = curveLinear,\n entrance = Entrance.MIDPOINT,\n onDatumSelection,\n onDatumFocus,\n onExit,\n focusedStyle,\n unfocusedStyle,\n attr,\n markerRadius = 3.5,\n markerStroke = '#fff',\n focusType = FocusType.X,\n isAnimated = true,\n}: GeomProps<Datum>) => {\n const { ggState } = useGG<Datum>() || {}\n const { data, aes, scales, copiedScales, copiedData, height, id } =\n ggState || {}\n const [theme, setTheme] = useAtom(themeState)\n const [{ datum: tooltipDatum }] = useAtom(tooltipState)\n const [{ values: strokeScaleColors, domain: strokeDomain }] =\n useAtom(strokeScaleState)\n\n const [{ values: strokeDasharrays, domain: strokeDasharrayDomain }] =\n useAtom(strokeDasharrayState)\n\n const isVisible = usePageVisibility()\n\n const geomData = localData || data\n const geomAes = useMemo(() => {\n if (localAes) {\n return {\n ...aes,\n ...localAes,\n }\n }\n return aes as GeomAes<Datum>\n }, [aes, localAes])\n\n const allXUndefined = useMemo(() => {\n const undefinedX = geomData\n ? geomData.filter(\n (d) =>\n geomAes?.x &&\n (geomAes.x(d) === null ||\n typeof geomAes.x(d) === 'undefined' ||\n Number.isNaN(geomAes.x(d)?.valueOf()) ||\n (isDate(geomAes.x(d)) && geomAes.x(d)?.valueOf() === 0)),\n )\n : []\n return geomData && undefinedX.length === geomData.length\n }, [geomData, geomAes])\n\n const allYUndefined = useMemo(() => {\n const undefinedY = geomData\n ? geomData.filter(\n (d) =>\n geomAes?.y &&\n (geomAes.y(d) === null ||\n typeof geomAes.y(d) === 'undefined' ||\n Number.isNaN(geomAes.y(d)?.valueOf())),\n )\n : []\n return geomData && undefinedY.length === geomData.length\n }, [geomData])\n\n const { defaultStroke, animationDuration: duration } = theme\n\n const geomID = useMemo(() => generateID(), [])\n\n const [firstRender, setFirstRender] = useState(true)\n useEffect(() => {\n const timeout = setTimeout(() => setFirstRender(false), 0)\n return () => clearTimeout(timeout)\n }, [])\n\n const strokeGroups = useMemo(\n () =>\n geomAes?.stroke\n ? (Array.from(new Set(copiedData?.map(geomAes.stroke))) as string[])\n : undefined,\n [copiedData, geomAes],\n )\n\n const strokeDasharrayGroups = useMemo(\n () =>\n geomAes?.strokeDasharray\n ? (Array.from(\n new Set(copiedData?.map(geomAes?.strokeDasharray)),\n ) as string[])\n : undefined,\n [copiedData, geomAes],\n )\n\n const group = useMemo(\n () =>\n geomAes?.group ??\n geomAes?.stroke ??\n geomAes?.strokeDasharray ??\n scales?.groupAccessor,\n [geomAes, scales],\n )\n\n const groups = useMemo(\n () =>\n group\n ? (Array.from(new Set(geomData?.map(group))) as string[])\n : undefined,\n [geomData, group],\n )\n\n const geomStrokeScale = useMemo(() => {\n if (groups && geomAes.stroke) {\n return scaleOrdinal()\n .domain(strokeDomain || strokeGroups || groups)\n .range(\n (strokeScaleColors as string[]) || defaultScheme,\n ) as VisualEncodingTypes\n }\n return undefined\n }, [geomAes, strokeGroups, strokeScaleColors])\n\n const geomStrokeDasharrayScale = useMemo(() => {\n if (groups && geomAes.strokeDasharray) {\n return scaleOrdinal()\n .domain(strokeDasharrayDomain || strokeDasharrayGroups || groups)\n .range(\n (strokeDasharrays as string[]) || defaultDasharrays,\n ) as VisualEncodingTypes\n }\n return undefined\n }, [geomAes, strokeDasharrayGroups, strokeScaleColors])\n\n const baseAttr: SVGAttributes<SVGCircleElement> = {\n strokeWidth: 2.5,\n strokeOpacity: 1,\n }\n\n const geomAttr: SVGAttributes<SVGCircleElement> = {\n ...baseAttr,\n ...attr,\n }\n\n useEffect(() => {\n setTheme((prev) => ({\n ...prev,\n geoms: {\n ...prev.geoms,\n line: {\n strokeWidth: geomAttr.style?.strokeWidth || geomAttr.strokeWidth,\n strokeOpacity:\n geomAttr.style?.strokeOpacity || geomAttr.strokeOpacity,\n stroke: geomAttr.stroke,\n strokeScale: geomStrokeScale,\n strokeDasharrayScale: geomStrokeDasharrayScale,\n groupAccessor:\n geomAes.stroke ?? geomAes.strokeDasharray ?? geomAes?.group,\n usableGroups: strokeGroups ?? strokeDasharrayGroups,\n },\n },\n }))\n }, [setTheme, attr, geomStrokeScale, strokeGroups, geomAes])\n\n const x = useMemo(\n () => (d: Datum) =>\n scales?.xScale && geomAes?.x && scales.xScale(geomAes.x(d)),\n [scales, geomAes],\n )\n const y = useMemo(\n () => (d: Datum) =>\n geomAes?.y && scales?.yScale && scales.yScale(geomAes?.y(d)),\n [scales, geomAes],\n )\n\n const drawLine = useMemo(\n () =>\n line()\n .defined((d) => {\n const areDefined =\n typeof d[0] !== 'undefined' && typeof d[1] !== 'undefined'\n const areNumbers = !Number.isNaN(d[0]) && !Number.isNaN(d[1])\n return areDefined && areNumbers\n })\n .curve(curve),\n [curve],\n )\n\n const groupRef = useRef<SVGGElement>(null)\n const lines = groupRef.current?.getElementsByTagName('path')\n\n const baseStyles: CSSProperties = {\n transition: 'stroke-opacity 200ms',\n strokeOpacity: geomAttr.strokeOpacity,\n ...geomAttr.style,\n }\n\n const focusedStyles: CSSProperties = {\n ...baseStyles,\n ...focusedStyle,\n }\n\n const unfocusedStyles: CSSProperties = {\n ...baseStyles,\n fillOpacity: 0.2,\n strokeOpacity: 0.2,\n ...unfocusedStyle,\n }\n\n const isClosestFocusType = focusType === FocusType.CLOSEST\n\n const focusGroups = useMemo(() => {\n const hasStrokeGrouping = geomAes?.stroke || geomAes?.strokeDasharray\n if (geomAes.focusGroup && isClosestFocusType && hasStrokeGrouping) {\n const groupStroke =\n geomAes?.group ?? geomAes?.stroke ?? geomAes.strokeDasharray\n\n const expandedGroups = Array.from(\n new Set(\n geomData?.map((d) => `${geomAes.focusGroup!(d)}-${groupStroke?.(d)}`),\n ),\n )\n\n return expandedGroups\n }\n return groups\n }, [groups, strokeGroups, geomData, geomAes, isClosestFocusType])\n\n useEffect(() => {\n const thisDatum = tooltipDatum?.[0]\n\n if (\n thisDatum &&\n group &&\n groups &&\n focusGroups &&\n focusGroups?.length > 1 &&\n lines &&\n lines?.length > 0 &&\n isClosestFocusType\n ) {\n const datumGroup = `${\n geomAes.focusGroup ? geomAes.focusGroup(thisDatum) : group(thisDatum)\n }`\n\n const focusedIndex = focusGroups\n ?.map((g, i) => (g.includes(datumGroup) ? i : -1))\n .filter((v) => v >= 0)\n\n focusNodes({\n nodes: lines,\n focusedIndex,\n focusedStyles,\n unfocusedStyles,\n })\n } else if (lines && isClosestFocusType) {\n unfocusNodes({ nodes: lines, baseStyles })\n }\n }, [\n tooltipDatum,\n group,\n groups,\n focusGroups,\n geomAes,\n lines,\n focusType,\n firstRender,\n isClosestFocusType,\n ])\n\n // map through groups to draw a line for each group\n return (\n <>\n <g ref={groupRef}>\n {isVisible && !firstRender && !allXUndefined && !allYUndefined && (\n <>\n {geomData && groups && group ? (\n groups.map((g) => {\n const groupData = geomData.filter((d) => group(d) === g)\n\n const groupLineData = groupData.map((d) => [x(d), y(d)]) as []\n\n const thisKey = geomAes?.key?.(groupData[0]) ?? `${geomID}-${g}`\n\n const thisStrokeGroups =\n geomStrokeScale && geomAes?.stroke\n ? Array.from(\n new Set(\n groupData.map(\n (gd) => geomAes.stroke && geomAes.stroke(gd),\n ),\n ),\n )\n : undefined\n\n let thisStroke =\n geomAttr.stroke ||\n (geomStrokeScale && geomStrokeScale(g)) ||\n (copiedScales?.strokeScale\n ? copiedScales.strokeScale(g)\n : defaultStroke)\n\n if (thisStrokeGroups && geomStrokeScale) {\n thisStrokeGroups.forEach((fg) => {\n thisStroke = geomAttr.stroke || geomStrokeScale(fg)\n })\n }\n\n const thisDasharray =\n geomAttr.strokeDasharray ||\n (copiedScales?.strokeDasharrayScale\n ? copiedScales.strokeDasharrayScale(\n geomAes?.strokeDasharray?.(groupData[0]),\n )\n : geomAttr.strokeDasharray)\n\n return (\n <Animate\n key={thisKey}\n start={{\n path: drawLine(\n groupLineData.map((d: [any, any]) => {\n const yEntrancePos =\n entrance === Entrance.MIDPOINT\n ? (height || 0) / 2\n : d[1]\n const hasMissingY =\n d[1] === null || typeof d[1] === 'undefined'\n return [d[0], hasMissingY ? NaN : yEntrancePos]\n }),\n ),\n opacity: 0,\n }}\n enter={{\n path: isAnimated\n ? [drawLine(groupLineData)]\n : drawLine(groupLineData),\n opacity: isAnimated\n ? [geomAttr.opacity ?? 1]\n : geomAttr.opacity ?? 1,\n timing: { duration, ease: easeCubic },\n }}\n update={{\n path: isAnimated\n ? [drawLine(groupLineData)]\n : drawLine(groupLineData),\n opacity: isAnimated\n ? [geomAttr.opacity ?? 1]\n : geomAttr.opacity ?? 1,\n timing: {\n duration,\n ease: easeCubic,\n },\n }}\n leave={() => ({\n opacity: isAnimated ? [0] : 0,\n timing: { duration, ease: easeCubic },\n })}\n interpolation={(begValue, endValue, a) => {\n if (a === 'path') {\n return interpolatePath(begValue, endValue)\n }\n return interpolate(begValue, endValue)\n }}\n >\n {(state) => (\n <path\n d={state.path}\n opacity={state.opacity}\n stroke={thisStroke}\n strokeWidth={geomAttr.strokeWidth}\n strokeOpacity={geomAttr.strokeOpacity}\n strokeDasharray={thisDasharray}\n fill=\"none\"\n data-testid=\"__gg_geom_line\"\n style={{\n pointerEvents: 'none',\n }}\n clipPath={`url(#__gg_canvas_${id})`}\n // eslint-disable-next-line react/jsx-props-no-spreading\n {...attr}\n />\n )}\n </Animate>\n )\n })\n ) : (\n <Animate\n start={{\n path: drawLine(geomData?.map((d) => [x(d), y(d)]) as []),\n opacity: 0,\n }}\n enter={{\n path: isAnimated\n ? [drawLine(geomData?.map((d) => [x(d), y(d)]) as [])]\n : drawLine(geomData?.map((d) => [x(d), y(d)]) as []),\n opacity: isAnimated ? [1] : 1,\n timing: { duration },\n }}\n update={{\n path: isAnimated\n ? [drawLine(geomData?.map((d) => [x(d), y(d)]) as [])]\n : drawLine(geomData?.map((d) => [x(d), y(d)]) as []),\n opacity: isAnimated ? [1] : 1,\n timing: { duration, ease: easeCubic },\n }}\n leave={() => ({\n opacity: isAnimated ? [0] : 0,\n timing: { duration, ease: easeCubic },\n })}\n interpolation={(begValue, endValue, a) => {\n if (a === 'path') {\n return interpolatePath(begValue, endValue)\n }\n return interpolate(begValue, endValue)\n }}\n >\n {(state) => (\n <path\n d={state.path}\n opacity={state.opacity}\n stroke={geomAttr.stroke || defaultStroke}\n strokeWidth={geomAttr.strokeWidth}\n strokeOpacity={geomAttr.strokeOpacity}\n fill=\"none\"\n data-testid=\"__gg_geom_line\"\n clipPath={`url(#__gg_canvas_${id})`}\n // eslint-disable-next-line react/jsx-props-no-spreading\n {...attr}\n />\n )}\n </Animate>\n )}\n </>\n )}\n </g>\n {(showTooltip || brushAction) && (\n <>\n <EventArea\n data={geomData}\n aes={geomAes}\n group={isClosestFocusType ? undefined : 'x'}\n x={(v: Datum) => x(v)}\n y={isClosestFocusType ? y : () => 0}\n onMouseLeave={() => {\n if (lines) {\n unfocusNodes({ nodes: lines, baseStyles })\n }\n\n if (onExit) onExit()\n }}\n onClick={\n onDatumSelection\n ? ({ d, i }: { d: Datum[]; i: number[] }) => {\n onDatumSelection(d, i)\n }\n : undefined\n }\n onDatumFocus={onDatumFocus}\n showTooltip={showTooltip}\n brushAction={brushAction}\n isZoomedOut={isZoomedOut}\n />\n {showTooltip && (\n <>\n {showLineMarker && (\n <LineMarker\n x={x}\n y={y}\n markerRadius={markerRadius}\n markerStroke={markerStroke}\n aes={geomAes}\n />\n )}\n <Tooltip x={x} y={y} aes={geomAes} />\n </>\n )}\n </>\n )}\n </>\n )\n}\n\nGeomLine.displayName = 'GeomLine'\nexport { GeomLine }\n","import React, { useMemo } from 'react'\nimport {\n useGG,\n tooltipState,\n themeState,\n TooltipContent,\n XTooltip,\n YTooltip,\n TooltipContainer,\n TooltipProps,\n TooltipPosition,\n} from '@graphique/graphique'\nimport { useAtom } from 'jotai'\nimport { mean, min, max } from 'd3-array'\nimport { DefaultTooltip } from './DefaultTooltip'\nimport { type GeomAes } from '../types'\n\nexport { LineMarker } from './LineMarker'\n\ninterface Props<Datum> {\n x: (d: Datum) => number | undefined\n y: (d: Datum) => number | undefined\n aes: GeomAes<Datum>\n}\n\nexport const Tooltip = <Datum,>({ x, y, aes }: Props<Datum>) => {\n const { ggState } = useGG<Datum>() || {}\n const { id, copiedScales, width, height, margin, scales } = ggState || {\n height: 0,\n }\n\n const [{ datum, position, xAxis, xFormat, yFormat, content }] =\n useAtom<TooltipProps<Datum>>(tooltipState)\n const [{ geoms, defaultStroke }] = useAtom(themeState)\n\n const left = useMemo(\n () =>\n x\n ? min([datum && x(datum[0]), width - (margin?.right ?? 0)] as number[])\n : undefined,\n [datum, x, width],\n )\n const hasYVal = useMemo(() => (y ? datum?.some(y) : undefined), [datum, y])\n\n const datumInGroups = useMemo(() => {\n const groups =\n scales?.strokeScale?.domain() ??\n scales?.strokeDasharrayScale?.domain() ??\n geoms?.line?.usableGroups\n\n return groups\n ? datum?.filter((d) => {\n const group = geoms?.line?.groupAccessor?.(d)\n const inGroups = groups.includes(group as string)\n\n return inGroups\n })\n : datum\n }, [datum, geoms, scales])\n\n const meanYVal = useMemo(\n () => (y && datumInGroups && mean(datumInGroups.map(y))) || 0,\n [datumInGroups, y],\n )\n\n const xVal = useMemo(\n () => datum && datum[0] && aes?.x && aes.x(datum[0]),\n [datum, aes],\n )\n\n const cappedYVal = max([0, min([meanYVal, height]) as number]) as number\n\n const lineVals = useMemo(() => {\n const vals = datumInGroups\n ?.filter(\n (d) => aes?.y && typeof aes.y(d) !== 'undefined' && aes.y(d) !== null,\n )\n .map((md) => {\n const group =\n aes?.stroke?.(md) ??\n aes?.strokeDasharray?.(md) ??\n geoms?.line?.groupAccessor?.(md)\n\n const dashArray =\n geoms?.line?.strokeDasharray ??\n (aes.strokeDasharray && copiedScales?.strokeDasharrayScale\n ? copiedScales.strokeDasharrayScale(aes.strokeDasharray(md))\n : undefined)\n\n const stroke =\n geoms?.line?.stroke ??\n (aes.stroke && copiedScales?.strokeScale\n ? copiedScales.strokeScale(aes.stroke(md))\n : defaultStroke)\n\n const mark = (\n <svg width={18} height={8}>\n <line\n x1={0}\n x2={18}\n y1={4}\n y2={4}\n stroke={stroke}\n strokeDasharray={dashArray}\n strokeWidth={geoms?.line?.strokeWidth}\n strokeOpacity={geoms?.line?.strokeOpacity}\n />\n </svg>\n )\n return {\n datum,\n group,\n mark,\n x: xVal,\n y: aes?.y && aes.y(md),\n formattedY: aes?.y && (yFormat ? yFormat(aes.y(md)) : aes.y(md)),\n formattedX: xFormat ? xFormat(xVal) : xVal?.toString(),\n }\n })\n return vals as TooltipContent<Datum>[]\n }, [\n datum,\n datumInGroups,\n xVal,\n aes,\n yFormat,\n xFormat,\n copiedScales,\n geoms,\n defaultStroke,\n ])\n\n const tooltipValue = content ? (\n <div>{content(lineVals)}</div>\n ) : (\n <DefaultTooltip data={lineVals} hasXAxisTooltip={!!xAxis} />\n )\n\n return datum ? (\n <>\n {xAxis && margin && left && (\n <XTooltip\n id={id as string}\n left={left}\n top={-margin.bottom}\n value={\n typeof xAxis === 'boolean' ? (\n <TooltipContainer>{xFormat && xFormat(xVal)}</TooltipContainer>\n ) : (\n xAxis(xVal)\n )\n }\n />\n )}\n {left && hasYVal && (\n <YTooltip\n id={id as string}\n left={left}\n top={\n position === TooltipPosition.DATA ? -(height - cappedYVal) : -height\n }\n value={tooltipValue}\n />\n )}\n </>\n ) : null\n}\n","import React from 'react'\nimport {\n TooltipContent,\n TooltipContainer,\n formatMissing,\n themeState,\n} from '@graphique/graphique'\nimport { useAtom } from 'jotai'\n\nexport interface DefaultTooltipProps<Datum> {\n data: TooltipContent<Datum>[]\n hasXAxisTooltip?: boolean\n}\n\nexport const DefaultTooltip = <Datum,>({\n data,\n hasXAxisTooltip,\n}: DefaultTooltipProps<Datum>) => {\n const xVal = data && data[0] ? data[0]?.formattedX : undefined\n\n const [{ tooltip }] = useAtom(themeState)\n\n return data ? (\n <TooltipContainer>\n {!hasXAxisTooltip && xVal && (\n <div\n style={{\n marginTop: 2,\n marginBottom: data.length === 1 ? 2 : 6,\n fontSize: tooltip?.xLabel?.fontSize || tooltip?.font?.size,\n color: '#555',\n }}\n >\n {xVal}\n </div>\n )}\n {data.map((d, i) => {\n const formattedGroup = formatMissing(d.group)\n return (\n <div\n key={`group-tooltip-${d.label || formattedGroup}-${i.toString()}`}\n >\n <div\n style={{\n marginTop: 3,\n marginBottom: data.length < i + 1 ? 3 : 2,\n display: 'flex',\n alignItems: 'center',\n }}\n >\n {(d.label || d.group) && (\n <>\n {d.mark}\n <div\n style={{\n display: 'flex',\n alignItems: 'flex-end',\n marginLeft: 4,\n }}\n >\n <div style={{ marginRight: 5 }}>\n <span\n style={{\n fontSize:\n tooltip?.groupLabel?.fontSize ||\n tooltip?.font?.size,\n }}\n >\n {d.label || formattedGroup}{' '}\n </span>\n </div>\n </div>\n </>\n )}\n <div\n style={{\n fontWeight: 500,\n fontSize:\n tooltip?.yLabel?.fontSize ||\n (tooltip?.font?.size || 12) + 1,\n }}\n >\n {d.formattedY}\n </div>\n </div>\n </div>\n )\n })}\n </TooltipContainer>\n ) : null\n}\n","import React, { useMemo } from 'react'\nimport {\n useGG,\n tooltipState,\n themeState,\n formatMissing,\n} from '@graphique/graphique'\nimport { min } from 'd3-array'\nimport { useAtom } from 'jotai'\nimport { type GeomAes } from '../types'\n\nexport interface LineMarkerProps<Datum> {\n x: (d: Datum) => number | undefined\n y: (d: Datum) => number | undefined\n markerRadius: number\n markerStroke: string\n aes: GeomAes<Datum>\n}\n\nexport const LineMarker = <Datum,>({\n x,\n y,\n markerRadius,\n markerStroke,\n aes,\n}: LineMarkerProps<Datum>) => {\n const { ggState } = useGG<Datum>() || {}\n const { copiedScales, width, height, margin, id, scales } = ggState || {}\n\n const [{ datum }] = useAtom(tooltipState)\n const [{ defaultStroke, geoms }] = useAtom(themeState) || {}\n\n const { line } = geoms || {}\n\n const left = useMemo(\n () => min([datum && x(datum[0]), width - (margin?.right ?? 0)] as number[]),\n [datum, x, width],\n )\n\n return height && margin ? (\n <>\n {left && datum && (\n <g\n className={`__gg-tooltip-${id}`}\n style={{ transform: `translateX(${left}px)` }}\n >\n <line\n y1={height - margin.bottom}\n y2={margin.top}\n strokeDasharray={2}\n stroke=\"#888\"\n strokeWidth={1.5}\n style={{ pointerEvents: 'none' }}\n data-testid=\"__gg_geom_line_marker\"\n />\n {datum.map((d, i) => {\n const formattedGroup = copiedScales?.groupAccessor\n ? formatMissing(copiedScales?.groupAccessor(d))\n : '__group'\n\n const inRange =\n (y(d) as number) <= copiedScales?.yScale.range()[0] &&\n (y(d) as number) >= copiedScales?.yScale.range()[1]\n\n const inGroups = scales?.groups\n ? scales.groups.includes(formattedGroup)\n : true\n\n const thisFill =\n line?.stroke ||\n (copiedScales?.strokeScale && aes?.stroke\n ? copiedScales.strokeScale(aes.stroke(d))\n : defaultStroke)\n return (\n typeof y(d) !== 'undefined' &&\n inRange &&\n inGroups && (\n <g\n key={`group-marker-${\n d.label || formattedGroup\n }-${i.toString()}`}\n style={{ pointerEvents: 'none' }}\n >\n <circle\n r={markerRadius * 2 + 0.5}\n fill={thisFill}\n cy={y(d)}\n fillOpacity={Math.min(\n 0.5,\n Math.max(\n ((line?.strokeOpacity || 0.9) as number) - 0.35,\n 0,\n ),\n )}\n />\n <circle\n r={markerRadius}\n fill={thisFill}\n stroke={markerStroke}\n strokeWidth={markerRadius / 3.2}\n cy={y(d)}\n fillOpacity={line?.strokeOpacity || 0.9}\n strokeOpacity={0.7}\n data-testid=\"__gg_geom_line_marker_point\"\n />\n </g>\n )\n )\n })}\n </g>\n )}\n </>\n ) : null\n}\n","import { Aes, DataValue } from '@graphique/graphique'\n\nexport enum Entrance {\n DATA = 'data',\n MIDPOINT = 'midpoint',\n}\n\nexport enum FocusType {\n X = 'x',\n CLOSEST = 'closest',\n}\n\nexport type GeomAes<Datum> = Omit<Aes<Datum>, 'x' | 'fill' | 'size'> & {\n x?: DataValue<Datum>\n /** functional mapping to `data` used to create higher-level groups for focusing when combined with `group` Aes and `focusType=FocusType.CLOSEST` */\n focusGroup?: DataValue<Datum>\n}\n","import React, { CSSProperties } from 'react'\nimport {\n useGG,\n themeState,\n IScale,\n LegendOrientation,\n} from '@graphique/graphique'\nimport { useAtom } from 'jotai'\nimport { CategoricalLegend } from './CategoricalLegend'\n\nexport interface LegendProps {\n /** title of legend */\n title?: React.ReactNode\n /** additional styles passed to legend container */\n style?: CSSProperties\n /** determines vertical/horizontal orientation of legend members (_default_: `LegendOrientation.V`) */\n orientation?: LegendOrientation\n /** function for formatting legend member labels */\n format?: (v: string, i?: number) => string\n /** callback called for click events on legend members */\n onSelection?: (v: string) => void\n /** should legend member stroke symbols include stroke dasharrays (_default_: `true`) */\n ignoreDasharray?: boolean\n}\n\nexport const Legend = <Datum,>({\n title,\n style,\n orientation = LegendOrientation.V,\n format,\n onSelection,\n ignoreDasharray = false,\n}: LegendProps) => {\n const { ggState } = useGG<Datum>() || {}\n const { copiedScales, copiedData, aes } = ggState || {}\n const [{ font, geoms }] = useAtom(themeState)\n\n const { line } = geoms || {}\n const { groups } = copiedScales || {}\n\n const hasAppearanceAes =\n line?.strokeScale ||\n line?.strokeDasharrayScale ||\n aes?.fill ||\n aes?.stroke ||\n aes?.strokeDasharray\n\n const { fontSize } = { ...style }\n\n return hasAppearanceAes ? (\n <div\n style={{\n marginTop: 12,\n fontFamily: font?.family,\n ...style,\n }}\n >\n {title}\n {copiedData &&\n (copiedScales || line?.strokeScale) &&\n (groups || line?.usableGroups) ? (\n <CategoricalLegend\n legendData={copiedData}\n orientation={orientation}\n legendScales={\n {\n ...copiedScales,\n strokeScale: line ? line.strokeScale : copiedScales?.strokeScale,\n groups: line?.usableGroups,\n } as IScale<Datum>\n }\n labelFormat={format}\n fontSize={fontSize}\n onSelection={onSelection}\n ignoreDasharray={ignoreDasharray}\n />\n ) : null}\n </div>\n ) : null\n}\n","import React, { useState, useEffect, useMemo } from 'react'\nimport {\n useGG,\n themeState,\n strokeScaleState,\n strokeDasharrayState,\n formatMissing,\n IScale,\n LegendOrientation,\n} from '@graphique/graphique'\nimport { useAtom } from 'jotai'\n\nexport interface CategoricalLegendProps<Datum> {\n legendData: Datum[]\n legendScales: IScale<Datum>\n orientation?: LegendOrientation\n labelFormat?: (v: any, i: number) => string\n fontSize?: string | number\n onSelection?: (v: string) => void\n ignoreDasharray?: boolean\n}\n\nexport const CategoricalLegend = <Datum,>({\n legendData,\n legendScales,\n orientation,\n labelFormat,\n fontSize = 12,\n onSelection,\n ignoreDasharray,\n}: CategoricalLegendProps<Datum>) => {\n const [{ geoms, defaultStroke, legend }] = useAtom(themeState)\n const [{ domain: strokeDomain }] = useAtom(strokeScaleState)\n const [{ domain: dashArrayDomain }] = useAtom(strokeDasharrayState)\n\n const legendGroups = useMemo(\n () =>\n strokeDomain ||\n dashArrayDomain ||\n legendScales.groups ||\n legendScales.strokeScale?.domain() ||\n legendScales.strokeDasharrayScale?.domain(),\n [legendScales, strokeDomain, dashArrayDomain],\n )\n\n const [isFocused, setIsFocused] = useState<string[]>(\n geoms?.line?.usableGroups ?? [],\n )\n\n const { ggState, updateData } = useGG<Datum>() || {}\n const { data } = ggState || {}\n\n const [firstRender, setFirstRender] = useState(true)\n useEffect(() => {\n const timeout = setTimeout(() => setFirstRender(false), 0)\n return () => clearTimeout(timeout)\n }, [])\n\n useEffect(() => {\n setIsFocused(legendGroups ?? [])\n }, [])\n\n const getGroup = useMemo(\n () => geoms?.line?.groupAccessor || (() => undefined),\n [geoms],\n )\n\n useEffect(() => {\n const dataGroups = Array.from(new Set(data!.map(getGroup))) as []\n\n setIsFocused(dataGroups ?? [])\n }, [data, getGroup])\n\n const isHorizontal = orientation === LegendOrientation.H\n\n const toggleLegendGroup = (g: string) => {\n const prevFocused = isFocused\n let focusedGroups\n if (prevFocused.includes(g)) {\n if (prevFocused.length === 1) {\n focusedGroups = legendScales.groups as string[]\n } else {\n focusedGroups = prevFocused.filter((p) => p !== g)\n }\n } else {\n focusedGroups = [...prevFocused, g]\n }\n setIsFocused(focusedGroups)\n\n const includedGroups = Array.from(new Set(data?.map((d) => getGroup(d))))\n\n if (onSelection) {\n onSelection(g)\n }\n if (data && updateData) {\n let updatedData: Datum[]\n if (includedGroups.includes(g)) {\n if (includedGroups.length === 1) {\n updatedData = legendData\n } else {\n updatedData = data.filter((d) => getGroup(d) !== g)\n }\n } else {\n updatedData = legendData.filter(\n (d) => includedGroups.includes(getGroup(d)) || getGroup(d) === g,\n )\n }\n updateData(updatedData)\n }\n }\n\n return (\n <div\n style={{\n marginTop: 8,\n display: 'flex',\n flexDirection: !isHorizontal ? 'column' : 'row',\n flexWrap: 'wrap',\n alignItems: isHorizontal ? 'center' : undefined,\n }}\n data-testid=\"__gg_geom_line_legend\"\n >\n {geoms?.line &&\n legendGroups?.map((g: string, i, groups) => {\n const strokeOpacity = isFocused.includes(g)\n ? geoms?.line?.strokeOpacity\n : geoms?.line?.strokeOpacity || 1 * 0.5\n\n return (\n <div\n key={g}\n style={{\n display: 'flex',\n alignItems: 'center',\n marginBottom: isHorizontal ? 6 : 2,\n }}\n >\n <div\n tabIndex={0}\n role=\"button\"\n style={{\n cursor: 'pointer',\n marginRight: i < groups.length - 1 && isHorizontal ? 12 : 2,\n fontSize,\n opacity: isFocused.includes(g) ? 1 : 0.5,\n transition: 'opacity 200ms',\n display: 'flex',\n alignItems: 'center',\n }}\n onKeyDown={(e) => {\n if (['Enter', ' '].includes(e.key)) {\n toggleLegendGroup(g)\n }\n }}\n onClick={() => toggleLegendGroup(g)}\n >\n <div\n style={{\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n }}\n >\n <svg width={18} height={8}>\n <line\n x1={0}\n x2={18}\n y1={4}\n y2={4}\n stroke={\n geoms?.line?.stroke ||\n (legendScales.strokeScale\n ? legendScales.strokeScale(g)\n : defaultStroke)\n }\n strokeWidth={geoms?.line?.strokeWidth}\n strokeOpacity={firstRender ? 0 : strokeOpacity}\n strokeDasharray={\n legendScales.strokeDasharrayScale && !ignoreDasharray\n ? legendScales.strokeDasharrayScale(g)\n : undefined\n }\n style={{\n transition: 'stroke-opacity 500ms',\n }}\n />\n </svg>\n </div>\n <div\n style={{\n marginLeft: 4,\n fontSize,\n color: legend?.labelColor ?? 'currentcolor',\n }}\n >\n {labelFormat ? labelFormat(g, i) : formatMissing(g)}\n </div>\n </div>\n </div>\n )\n })}\n </div>\n )\n}\n"],"mappings":"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