@nivo/arcs
Version:
1 lines • 77.4 kB
Source Map (JSON)
{"version":3,"file":"nivo-arcs.mjs","sources":["../src/arc_labels/ArcLabel.tsx","../src/utils.ts","../src/arcTransitionMode.ts","../src/centers.ts","../src/arc_labels/ArcLabelsLayer.tsx","../src/arc_labels/canvas.ts","../src/arc_labels/useArcLabels.ts","../src/arc_labels/index.ts","../src/arc_link_labels/ArcLinkLabel.tsx","../src/arc_link_labels/compute.ts","../src/arc_link_labels/useArcLinkLabelsTransition.ts","../src/arc_link_labels/ArcLinkLabelsLayer.tsx","../src/arc_link_labels/canvas.ts","../src/arc_link_labels/useArcLinks.ts","../src/arc_link_labels/useArcLinkLabels.ts","../src/arc_link_labels/index.ts","../src/ArcLine.tsx","../src/ArcShape.tsx","../src/interpolateArc.ts","../src/useArcsTransition.ts","../src/ArcsLayer.tsx","../src/boundingBox.ts","../src/interactivity.ts","../src/useAnimatedArc.ts","../src/useArcGenerator.ts"],"sourcesContent":["import { CSSProperties } from 'react'\nimport { SpringValue, Interpolation, animated } from '@react-spring/web'\nimport { useTheme } from '@nivo/theming'\nimport { Text } from '@nivo/text'\nimport { DatumWithArcAndColor } from '../types'\n\nconst staticStyle: CSSProperties = {\n pointerEvents: 'none',\n}\n\nexport interface ArcLabelProps<Datum extends DatumWithArcAndColor> {\n datum: Datum\n label: string\n style: {\n progress: SpringValue<number>\n transform: Interpolation<string>\n textColor: string\n }\n}\n\nexport const ArcLabel = <Datum extends DatumWithArcAndColor>({\n label,\n style,\n}: ArcLabelProps<Datum>) => {\n const theme = useTheme()\n\n return (\n <animated.g transform={style.transform} opacity={style.progress} style={staticStyle}>\n <Text\n textAnchor=\"middle\"\n dominantBaseline=\"central\"\n style={{\n ...theme.labels.text,\n fill: style.textColor,\n }}\n >\n {label}\n </Text>\n </animated.g>\n )\n}\n","import { useMemo } from 'react'\nimport { radiansToDegrees, positionFromAngle, degreesToRadians } from '@nivo/core'\nimport { DatumWithArc } from './types'\n\n/**\n * Make sure an angle (expressed in radians)\n * always falls in the range 0~2*PI.\n */\nexport const getNormalizedAngle = (angle: number) => {\n let normalizedAngle = angle % (Math.PI * 2)\n if (normalizedAngle < 0) {\n normalizedAngle += Math.PI * 2\n }\n\n return normalizedAngle\n}\n\n/**\n * Filter out arcs with a length below `skipAngle`.\n */\nexport const filterDataBySkipAngle = <Datum extends DatumWithArc>(\n data: Datum[],\n skipAngle: number\n) =>\n data.filter(\n datum => Math.abs(radiansToDegrees(datum.arc.endAngle - datum.arc.startAngle)) >= skipAngle\n )\n\n/**\n * Memoized version of `filterDataBySkipAngle`.\n */\nexport const useFilteredDataBySkipAngle = <Datum extends DatumWithArc>(\n data: Datum[],\n skipAngle: number\n) => useMemo(() => filterDataBySkipAngle(data, skipAngle), [data, skipAngle])\n\nexport const svgEllipticalArcCommand = (\n radius: number,\n largeArcFlag: 0 | 1,\n sweepFlag: 0 | 1,\n x: number,\n y: number\n) =>\n [\n 'A',\n radius,\n radius,\n 0, // x-axis-rotation\n largeArcFlag,\n sweepFlag,\n x,\n y,\n ].join(' ')\n\nexport const generateSvgArc = (\n radius: number,\n originalStartAngle: number,\n originalEndAngle: number\n): string => {\n const startAngle = Math.min(originalStartAngle, originalEndAngle)\n const endAngle = Math.max(originalStartAngle, originalEndAngle)\n\n const start = positionFromAngle(degreesToRadians(endAngle), radius)\n const end = positionFromAngle(degreesToRadians(startAngle), radius)\n\n // we have a full circle, we cannot use a single elliptical arc\n // to draw it, so we use 2 in that case.\n if (endAngle - startAngle >= 360) {\n const mid = positionFromAngle(degreesToRadians(startAngle + 180), radius)\n\n return [\n `M ${start.x} ${start.y}`,\n svgEllipticalArcCommand(radius, 1, 1, mid.x, mid.y),\n `M ${start.x} ${start.y}`,\n svgEllipticalArcCommand(radius, 1, 0, mid.x, mid.y),\n ].join(' ')\n }\n\n const largeArcFlag = endAngle - startAngle <= 180 ? 0 : 1\n\n return [\n `M ${start.x} ${start.y}`,\n svgEllipticalArcCommand(radius, largeArcFlag, 0, end.x, end.y),\n ].join(' ')\n}\n","import { Arc, DatumWithArc } from './types'\nimport { useMemo } from 'react'\n\nexport interface ArcTransitionModeConfig {\n enter: (arc: Arc) => Arc\n update: (arc: Arc) => Arc\n leave: (arc: Arc) => Arc\n}\n\nexport const arcTransitionModes = [\n 'startAngle',\n 'middleAngle',\n 'endAngle',\n 'innerRadius',\n 'centerRadius',\n 'outerRadius',\n 'pushIn',\n 'pushOut',\n] as const\nexport type ArcTransitionMode = (typeof arcTransitionModes)[number]\n\nexport const arcTransitionModeById: Record<ArcTransitionMode, ArcTransitionModeConfig> = {\n startAngle: {\n enter: (arc: Arc) => ({\n ...arc,\n endAngle: arc.startAngle,\n }),\n update: (arc: Arc) => arc,\n leave: (arc: Arc) => ({\n ...arc,\n startAngle: arc.endAngle,\n }),\n },\n middleAngle: {\n enter: (arc: Arc) => {\n const middleAngle = arc.startAngle + (arc.endAngle - arc.startAngle) / 2\n\n return {\n ...arc,\n startAngle: middleAngle,\n endAngle: middleAngle,\n }\n },\n update: (arc: Arc) => arc,\n leave: (arc: Arc) => {\n const middleAngle = arc.startAngle + (arc.endAngle - arc.startAngle) / 2\n\n return {\n ...arc,\n startAngle: middleAngle,\n endAngle: middleAngle,\n }\n },\n },\n endAngle: {\n enter: (arc: Arc) => ({\n ...arc,\n startAngle: arc.endAngle,\n }),\n update: (arc: Arc) => arc,\n leave: (arc: Arc) => ({\n ...arc,\n endAngle: arc.startAngle,\n }),\n },\n innerRadius: {\n enter: (arc: Arc) => ({\n ...arc,\n outerRadius: arc.innerRadius,\n }),\n update: (arc: Arc) => arc,\n leave: (arc: Arc) => ({\n ...arc,\n innerRadius: arc.outerRadius,\n }),\n },\n centerRadius: {\n enter: (arc: Arc) => {\n const centerRadius = arc.innerRadius + (arc.outerRadius - arc.innerRadius) / 2\n\n return {\n ...arc,\n innerRadius: centerRadius,\n outerRadius: centerRadius,\n }\n },\n update: (arc: Arc) => arc,\n leave: (arc: Arc) => {\n const centerRadius = arc.innerRadius + (arc.outerRadius - arc.innerRadius) / 2\n\n return {\n ...arc,\n innerRadius: centerRadius,\n outerRadius: centerRadius,\n }\n },\n },\n outerRadius: {\n enter: (arc: Arc) => ({\n ...arc,\n innerRadius: arc.outerRadius,\n }),\n update: (arc: Arc) => arc,\n leave: (arc: Arc) => ({\n ...arc,\n outerRadius: arc.innerRadius,\n }),\n },\n pushIn: {\n enter: (arc: Arc) => ({\n ...arc,\n innerRadius: arc.innerRadius - arc.outerRadius + arc.innerRadius,\n outerRadius: arc.innerRadius,\n }),\n update: (arc: Arc) => arc,\n leave: (arc: Arc) => ({\n ...arc,\n innerRadius: arc.outerRadius,\n outerRadius: arc.outerRadius + arc.outerRadius - arc.innerRadius,\n }),\n },\n pushOut: {\n enter: (arc: Arc) => ({\n ...arc,\n innerRadius: arc.outerRadius,\n outerRadius: arc.outerRadius + arc.outerRadius - arc.innerRadius,\n }),\n update: (arc: Arc) => arc,\n leave: (arc: Arc) => ({\n ...arc,\n innerRadius: arc.innerRadius - arc.outerRadius + arc.innerRadius,\n outerRadius: arc.innerRadius,\n }),\n },\n}\n\nexport interface TransitionExtra<\n Datum extends DatumWithArc,\n ExtraProps extends Record<string, any> = Record<string, never>,\n> {\n enter: (datum: Datum) => ExtraProps\n update: (datum: Datum) => ExtraProps\n leave: (datum: Datum) => ExtraProps\n}\n\nexport type ArcTransitionProps<ExtraProps extends Record<string, any> = Record<string, never>> =\n Arc & {\n progress: number\n } & ExtraProps\n\nexport const useArcTransitionMode = <\n Datum extends DatumWithArc,\n ExtraProps extends Record<string, any> = Record<string, never>,\n>(\n mode: ArcTransitionMode,\n extraTransition?: TransitionExtra<Datum, ExtraProps>\n) =>\n useMemo(() => {\n const transitionMode = arcTransitionModeById[mode]\n\n return {\n enter: (datum: Datum) =>\n ({\n progress: 0,\n ...transitionMode.enter(datum.arc),\n ...(extraTransition ? extraTransition.enter(datum) : {}),\n }) as ArcTransitionProps<ExtraProps>,\n update: (datum: Datum) =>\n ({\n progress: 1,\n ...transitionMode.update(datum.arc),\n ...(extraTransition ? extraTransition.update(datum) : {}),\n }) as ArcTransitionProps<ExtraProps>,\n leave: (datum: Datum) =>\n ({\n progress: 0,\n ...transitionMode.leave(datum.arc),\n ...(extraTransition ? extraTransition.leave(datum) : {}),\n }) as ArcTransitionProps<ExtraProps>,\n }\n }, [mode, extraTransition])\n","import { useMemo } from 'react'\nimport { useTransition, to, SpringValue, TransitionFn } from '@react-spring/web'\nimport { midAngle, positionFromAngle, useMotionConfig } from '@nivo/core'\nimport { Arc, DatumWithArc, Point } from './types'\nimport { filterDataBySkipAngle } from './utils'\nimport {\n ArcTransitionMode,\n ArcTransitionProps,\n TransitionExtra,\n useArcTransitionMode,\n} from './arcTransitionMode'\n\nexport const computeArcCenter = (arc: Arc, offset: number): Point => {\n const angle = midAngle(arc) - Math.PI / 2\n const radius = arc.innerRadius + (arc.outerRadius - arc.innerRadius) * offset\n\n return positionFromAngle(angle, radius)\n}\n\nexport const interpolateArcCenter =\n (offset: number) =>\n (\n startAngleValue: SpringValue<number>,\n endAngleValue: SpringValue<number>,\n innerRadiusValue: SpringValue<number>,\n outerRadiusValue: SpringValue<number>\n ) =>\n to(\n [startAngleValue, endAngleValue, innerRadiusValue, outerRadiusValue],\n (startAngle, endAngle, innerRadius, outerRadius) => {\n const centroid = computeArcCenter(\n { startAngle, endAngle, innerRadius, outerRadius },\n offset\n )\n\n return `translate(${centroid.x},${centroid.y})`\n }\n )\n\nexport const useArcCentersTransition = <\n Datum extends DatumWithArc,\n ExtraProps extends Record<string, any> = Record<string, never>,\n>(\n data: Datum[],\n // define where the centers should be placed,\n // 0.0: inner radius\n // 0.5: center\n // 1.0: outer radius\n offset = 0.5,\n mode: ArcTransitionMode = 'innerRadius',\n extra?: TransitionExtra<Datum, ExtraProps>\n) => {\n const { animate, config: springConfig } = useMotionConfig()\n\n const phases = useArcTransitionMode<Datum, ExtraProps>(mode, extra)\n\n const transition = useTransition<Datum, ArcTransitionProps<ExtraProps>>(data, {\n keys: datum => datum.id,\n initial: phases.update,\n from: phases.enter,\n enter: phases.update,\n update: phases.update,\n leave: phases.leave,\n config: springConfig,\n immediate: !animate,\n }) as unknown as TransitionFn<Datum, ArcTransitionProps<ExtraProps>>\n\n return {\n transition,\n interpolate: interpolateArcCenter(offset),\n }\n}\n\nexport interface ArcCenter<Datum extends DatumWithArc> extends Point {\n data: Datum\n}\n\n/**\n * Compute an array of arc centers from an array of data containing arcs.\n *\n * If you plan to animate those, you could use `useArcCentersTransition`\n * instead, you could use the returned array with react-spring `useTransition`,\n * but this would lead to cartesian transitions (x/y), while `useArcCentersTransition`\n * will generate proper transitions using radius/angle.\n */\nexport const useArcCenters = <\n Datum extends DatumWithArc,\n ExtraProps extends Record<string, any> = Record<string, never>,\n>({\n data,\n offset = 0.5,\n skipAngle = 0,\n computeExtraProps = () => ({}) as ExtraProps,\n}: {\n data: Datum[]\n // define where the centers should be placed,\n // 0.0: inner radius\n // 0.5: center\n // 1.0: outer radius\n offset?: number\n // arcs with a length below this (end angle - start angle in degrees)\n // are going to be excluded, this can typically be used to avoid having\n // overlapping labels.\n skipAngle?: number\n // this can be used to append extra properties to the centers,\n // can be used to compute a color/label for example.\n computeExtraProps?: (datum: Datum) => ExtraProps\n}): (ArcCenter<Datum> & ExtraProps)[] =>\n useMemo(\n () =>\n filterDataBySkipAngle<Datum>(data, skipAngle)\n // compute position and extra props for each eligible datum\n .map(datum => {\n const position = computeArcCenter(datum.arc, offset)\n\n return {\n ...computeExtraProps(datum),\n x: position.x,\n y: position.y,\n data: datum,\n }\n }),\n [data, offset, skipAngle, computeExtraProps]\n )\n","import { createElement, useMemo } from 'react'\nimport { PropertyAccessor, usePropertyAccessor, radiansToDegrees } from '@nivo/core'\nimport { useTheme } from '@nivo/theming'\nimport { useInheritedColor } from '@nivo/colors'\nimport { useArcCentersTransition } from '../centers'\nimport { ArcTransitionMode } from '../arcTransitionMode'\nimport { DatumWithArcAndColor } from '../types'\nimport { ArcLabelsProps } from './props'\nimport { ArcLabel, ArcLabelProps } from './ArcLabel'\n\nexport type ArcLabelComponent<Datum extends DatumWithArcAndColor> = (\n props: ArcLabelProps<Datum>\n) => JSX.Element\n\ninterface ArcLabelsLayerProps<Datum extends DatumWithArcAndColor> {\n center: [number, number]\n data: Datum[]\n label: PropertyAccessor<Datum, string>\n radiusOffset: ArcLabelsProps<Datum>['arcLabelsRadiusOffset']\n skipAngle: ArcLabelsProps<Datum>['arcLabelsSkipAngle']\n skipRadius: ArcLabelsProps<Datum>['arcLabelsSkipRadius']\n textColor: ArcLabelsProps<Datum>['arcLabelsTextColor']\n transitionMode: ArcTransitionMode\n component?: ArcLabelsProps<Datum>['arcLabelsComponent']\n}\n\nexport const ArcLabelsLayer = <Datum extends DatumWithArcAndColor>({\n center,\n data,\n transitionMode,\n label: labelAccessor,\n radiusOffset,\n skipAngle,\n skipRadius,\n textColor,\n component = ArcLabel,\n}: ArcLabelsLayerProps<Datum>) => {\n const getLabel = usePropertyAccessor<Datum, string>(labelAccessor)\n const theme = useTheme()\n const getTextColor = useInheritedColor<Datum>(textColor, theme)\n\n const filteredData = useMemo(\n () =>\n data.filter(datum => {\n const angle = Math.abs(radiansToDegrees(datum.arc.endAngle - datum.arc.startAngle))\n const radius = Math.abs(datum.arc.outerRadius - datum.arc.innerRadius)\n\n return angle >= skipAngle && radius >= skipRadius\n }),\n [data, skipAngle, skipRadius]\n )\n\n const { transition, interpolate } = useArcCentersTransition<Datum>(\n filteredData,\n radiusOffset,\n transitionMode\n )\n\n const Label: ArcLabelComponent<Datum> = component\n\n return (\n <g transform={`translate(${center[0]},${center[1]})`}>\n {transition((transitionProps, datum) => {\n return createElement(Label, {\n key: datum.id,\n datum,\n label: getLabel(datum),\n style: {\n progress: transitionProps.progress,\n transform: interpolate(\n transitionProps.startAngle,\n transitionProps.endAngle,\n transitionProps.innerRadius,\n transitionProps.outerRadius\n ),\n textColor: getTextColor(datum),\n },\n })\n })}\n </g>\n )\n}\n","import { Theme } from '@nivo/theming'\nimport { setCanvasFont, drawCanvasText } from '@nivo/text'\nimport { DatumWithArcAndColor } from '../types'\nimport { ArcLabel } from './useArcLabels'\n\nexport const drawCanvasArcLabels = <Datum extends DatumWithArcAndColor>(\n ctx: CanvasRenderingContext2D,\n labels: ArcLabel<Datum>[],\n theme: Theme\n) => {\n setCanvasFont(ctx, theme.labels.text)\n ctx.textAlign = 'center'\n ctx.textBaseline = 'middle'\n\n labels.forEach(label => {\n drawCanvasText(\n ctx,\n {\n ...theme.labels.text,\n fill: label.textColor,\n },\n String(label.label),\n label.x,\n label.y\n )\n })\n}\n","import { useCallback } from 'react'\nimport { PropertyAccessor, usePropertyAccessor } from '@nivo/core'\nimport { useTheme } from '@nivo/theming'\nimport { InheritedColorConfig, useInheritedColor } from '@nivo/colors'\nimport { DatumWithArcAndColor } from '../types'\nimport { useArcCenters, ArcCenter } from '../centers'\n\nexport interface ArcLabel<Datum extends DatumWithArcAndColor> extends ArcCenter<Datum> {\n label: string\n textColor: string\n}\n\n/**\n * Compute arc labels, please note that the datum should\n * contain a color in order to be able to compute the label text color.\n *\n * Please see `useArcCenters` for a more detailed explanation\n * about the parameters.\n */\nexport const useArcLabels = <Datum extends DatumWithArcAndColor>({\n data,\n offset,\n skipAngle,\n label,\n textColor,\n}: {\n data: Datum[]\n offset?: number\n skipAngle?: number\n label: PropertyAccessor<Datum, string>\n textColor: InheritedColorConfig<Datum>\n}) => {\n const getLabel = usePropertyAccessor<Datum, string>(label)\n\n const theme = useTheme()\n const getTextColor = useInheritedColor<Datum>(textColor, theme)\n\n const computeExtraProps = useCallback(\n (datum: Datum) => {\n return {\n label: getLabel(datum),\n textColor: getTextColor(datum),\n }\n },\n [getLabel, getTextColor]\n )\n\n return useArcCenters<Datum, Omit<ArcLabel<Datum>, keyof ArcCenter<Datum>>>({\n data,\n offset,\n skipAngle,\n computeExtraProps,\n })\n}\n","import { ArcLabel } from './ArcLabel'\n\nexport const ArcLabelComponent = ArcLabel\nexport * from './ArcLabelsLayer'\nexport * from './canvas'\nexport * from './props'\nexport * from './useArcLabels'\n","import { SpringValue, Interpolation, animated } from '@react-spring/web'\nimport { useTheme } from '@nivo/theming'\nimport { Text } from '@nivo/text'\nimport { DatumWithArcAndColor } from '../types'\n\nexport interface ArcLinkLabelProps<Datum extends DatumWithArcAndColor> {\n datum: Datum\n label: string\n style: {\n path: Interpolation<string>\n thickness: number\n textPosition: Interpolation<string>\n textAnchor: Interpolation<'start' | 'end'>\n linkColor: SpringValue<string>\n opacity: SpringValue<number>\n textColor: SpringValue<string>\n }\n}\n\nexport const ArcLinkLabel = <Datum extends DatumWithArcAndColor>({\n label,\n style,\n}: ArcLinkLabelProps<Datum>) => {\n const theme = useTheme()\n\n return (\n <animated.g opacity={style.opacity}>\n <animated.path\n fill=\"none\"\n stroke={style.linkColor}\n strokeWidth={style.thickness}\n d={style.path}\n />\n <Text\n transform={style.textPosition}\n textAnchor={style.textAnchor}\n dominantBaseline=\"central\"\n style={{\n ...theme.labels.text,\n fill: style.textColor,\n }}\n >\n {label}\n </Text>\n </animated.g>\n )\n}\n","import { positionFromAngle } from '@nivo/core'\nimport { Arc, Point } from '../types'\nimport { getNormalizedAngle } from '../utils'\nimport { ArcLink } from './types'\n\n/**\n * Compute text anchor for a given arc.\n *\n * `computeArcLink` already computes a `side`, but when using\n * `react-spring`, you cannot have a single interpolation\n * returning several output values, so we need to compute\n * them in separate interpolations.\n */\nexport const computeArcLinkTextAnchor = (arc: Arc): 'start' | 'end' => {\n const centerAngle = getNormalizedAngle(\n arc.startAngle + (arc.endAngle - arc.startAngle) / 2 - Math.PI / 2\n )\n\n if (centerAngle < Math.PI / 2 || centerAngle > Math.PI * 1.5) {\n return 'start'\n }\n\n return 'end'\n}\n\n/**\n * Compute the link of a single arc, returning its points,\n * please note that points coordinates are relative to\n * the center of the arc.\n */\nexport const computeArcLink = (\n arc: Arc,\n offset: number,\n diagonalLength: number,\n straightLength: number\n): ArcLink => {\n const centerAngle = getNormalizedAngle(\n arc.startAngle + (arc.endAngle - arc.startAngle) / 2 - Math.PI / 2\n )\n const point0: Point = positionFromAngle(centerAngle, arc.outerRadius + offset)\n const point1: Point = positionFromAngle(centerAngle, arc.outerRadius + offset + diagonalLength)\n\n let side: ArcLink['side']\n let point2: Point\n if (centerAngle < Math.PI / 2 || centerAngle > Math.PI * 1.5) {\n side = 'after'\n point2 = {\n x: point1.x + straightLength,\n y: point1.y,\n }\n } else {\n side = 'before'\n point2 = {\n x: point1.x - straightLength,\n y: point1.y,\n }\n }\n\n return {\n side,\n points: [point0, point1, point2],\n }\n}\n","import { useMemo } from 'react'\nimport { SpringValue, useTransition, to } from '@react-spring/web'\nimport { line } from 'd3-shape'\nimport { useMotionConfig } from '@nivo/core'\nimport { useTheme } from '@nivo/theming'\nimport { InheritedColorConfig, useInheritedColor } from '@nivo/colors'\nimport { DatumWithArcAndColor, Point } from '../types'\nimport { useFilteredDataBySkipAngle } from '../utils'\nimport { computeArcLink, computeArcLinkTextAnchor } from './compute'\n\nconst lineGenerator = line<Point>()\n .x(d => d.x)\n .y(d => d.y)\n\ntype AnimatedProps = {\n startAngle: number\n endAngle: number\n innerRadius: number\n outerRadius: number\n offset: number\n diagonalLength: number\n straightLength: number\n textOffset: number\n linkColor: string\n textColor: string\n opacity: number\n}\n\nconst useTransitionPhases = <Datum extends DatumWithArcAndColor>({\n offset,\n diagonalLength,\n straightLength,\n textOffset,\n getLinkColor,\n getTextColor,\n}: Pick<AnimatedProps, 'offset' | 'diagonalLength' | 'straightLength' | 'textOffset'> & {\n getLinkColor: (datum: Datum) => string\n getTextColor: (datum: Datum) => string\n}): Record<'enter' | 'update' | 'leave', (datum: Datum) => AnimatedProps> =>\n useMemo(\n () => ({\n enter: (datum: Datum) => ({\n startAngle: datum.arc.startAngle,\n endAngle: datum.arc.endAngle,\n innerRadius: datum.arc.innerRadius,\n outerRadius: datum.arc.outerRadius,\n offset,\n diagonalLength: 0,\n straightLength: 0,\n textOffset,\n linkColor: getLinkColor(datum),\n textColor: getTextColor(datum),\n opacity: 0,\n }),\n update: (d: Datum) => ({\n startAngle: d.arc.startAngle,\n endAngle: d.arc.endAngle,\n innerRadius: d.arc.innerRadius,\n outerRadius: d.arc.outerRadius,\n offset,\n diagonalLength,\n straightLength,\n textOffset,\n linkColor: getLinkColor(d),\n textColor: getTextColor(d),\n opacity: 1,\n }),\n leave: (d: Datum) => ({\n startAngle: d.arc.startAngle,\n endAngle: d.arc.endAngle,\n innerRadius: d.arc.innerRadius,\n outerRadius: d.arc.outerRadius,\n offset,\n diagonalLength: 0,\n straightLength: 0,\n textOffset,\n linkColor: getLinkColor(d),\n textColor: getTextColor(d),\n opacity: 0,\n }),\n }),\n [diagonalLength, straightLength, textOffset, getLinkColor, getTextColor, offset]\n )\n\nconst interpolateLink = (\n startAngleValue: SpringValue<AnimatedProps['startAngle']>,\n endAngleValue: SpringValue<AnimatedProps['endAngle']>,\n innerRadiusValue: SpringValue<AnimatedProps['innerRadius']>,\n outerRadiusValue: SpringValue<AnimatedProps['outerRadius']>,\n offsetValue: SpringValue<AnimatedProps['offset']>,\n diagonalLengthValue: SpringValue<AnimatedProps['diagonalLength']>,\n straightLengthValue: SpringValue<AnimatedProps['straightLength']>\n) =>\n to(\n [\n startAngleValue,\n endAngleValue,\n innerRadiusValue,\n outerRadiusValue,\n offsetValue,\n diagonalLengthValue,\n straightLengthValue,\n ],\n (\n startAngle,\n endAngle,\n innerRadius,\n outerRadius,\n offset,\n diagonalLengthAnimated,\n straightLengthAnimated\n ) => {\n const { points } = computeArcLink(\n {\n startAngle,\n endAngle,\n innerRadius,\n outerRadius,\n },\n offset,\n diagonalLengthAnimated,\n straightLengthAnimated\n )\n\n return lineGenerator(points)\n }\n )\n\nconst interpolateTextAnchor = (\n startAngleValue: SpringValue<AnimatedProps['startAngle']>,\n endAngleValue: SpringValue<AnimatedProps['endAngle']>,\n innerRadiusValue: SpringValue<AnimatedProps['innerRadius']>,\n outerRadiusValue: SpringValue<AnimatedProps['outerRadius']>\n) =>\n to(\n [startAngleValue, endAngleValue, innerRadiusValue, outerRadiusValue],\n (startAngle, endAngle, innerRadius, outerRadius) => {\n return computeArcLinkTextAnchor({\n startAngle,\n endAngle,\n innerRadius,\n outerRadius,\n })\n }\n )\n\n/**\n * Interpolating the text position involves almost the same computation\n * as `interpolateLink`, unfortunately `react-spring` does not support\n * multiple output values from a single interpolation.\n *\n * We should revise this if `react-spring` adds this feature at some point.\n */\nconst interpolateTextPosition = (\n startAngleValue: SpringValue<AnimatedProps['startAngle']>,\n endAngleValue: SpringValue<AnimatedProps['endAngle']>,\n innerRadiusValue: SpringValue<AnimatedProps['innerRadius']>,\n outerRadiusValue: SpringValue<AnimatedProps['outerRadius']>,\n offsetValue: SpringValue<AnimatedProps['offset']>,\n diagonalLengthValue: SpringValue<AnimatedProps['diagonalLength']>,\n straightLengthValue: SpringValue<AnimatedProps['straightLength']>,\n textOffsetValue: SpringValue<AnimatedProps['textOffset']>\n) =>\n to(\n [\n startAngleValue,\n endAngleValue,\n innerRadiusValue,\n outerRadiusValue,\n offsetValue,\n diagonalLengthValue,\n straightLengthValue,\n textOffsetValue,\n ],\n (\n startAngle,\n endAngle,\n innerRadius,\n outerRadius,\n offset,\n diagonalLengthAnimated,\n straightLengthAnimated,\n textOffset\n ) => {\n const { points, side } = computeArcLink(\n {\n startAngle,\n endAngle,\n innerRadius,\n outerRadius,\n },\n offset,\n diagonalLengthAnimated,\n straightLengthAnimated\n )\n\n const position = points[2]\n if (side === 'before') {\n position.x -= textOffset\n } else {\n position.x += textOffset\n }\n\n return `translate(${position.x},${position.y})`\n }\n )\n\n/**\n * This hook can be used to animate a group of arc link labels,\n * if you just want to compute the labels, please use `useArcLinkLabels`.\n */\nexport const useArcLinkLabelsTransition = <Datum extends DatumWithArcAndColor>({\n data,\n offset = 0,\n diagonalLength,\n straightLength,\n skipAngle = 0,\n textOffset,\n linkColor,\n textColor,\n}: {\n data: Datum[]\n offset?: number\n diagonalLength: number\n straightLength: number\n skipAngle?: number\n textOffset: number\n linkColor: InheritedColorConfig<Datum>\n textColor: InheritedColorConfig<Datum>\n}) => {\n const { animate, config: springConfig } = useMotionConfig()\n\n const theme = useTheme()\n const getLinkColor = useInheritedColor<Datum>(linkColor, theme)\n const getTextColor = useInheritedColor<Datum>(textColor, theme)\n\n const filteredData = useFilteredDataBySkipAngle<Datum>(data, skipAngle)\n const transitionPhases = useTransitionPhases<Datum>({\n offset,\n diagonalLength,\n straightLength,\n textOffset,\n getLinkColor,\n getTextColor,\n })\n\n const transition = useTransition<Datum, AnimatedProps>(filteredData, {\n keys: datum => datum.id,\n initial: transitionPhases.update,\n from: transitionPhases.enter,\n enter: transitionPhases.update,\n update: transitionPhases.update,\n leave: transitionPhases.leave,\n config: springConfig,\n immediate: !animate,\n })\n\n return {\n transition,\n interpolateLink,\n interpolateTextAnchor,\n interpolateTextPosition,\n }\n}\n","import { createElement } from 'react'\nimport { usePropertyAccessor } from '@nivo/core'\nimport { DatumWithArcAndColor } from '../types'\nimport { useArcLinkLabelsTransition } from './useArcLinkLabelsTransition'\nimport { ArcLinkLabelsProps } from './props'\nimport { ArcLinkLabel, ArcLinkLabelProps } from './ArcLinkLabel'\n\nexport type ArcLinkLabelComponent<Datum extends DatumWithArcAndColor> = (\n props: ArcLinkLabelProps<Datum>\n) => JSX.Element\n\ninterface ArcLinkLabelsLayerProps<Datum extends DatumWithArcAndColor> {\n center: [number, number]\n data: Datum[]\n label: ArcLinkLabelsProps<Datum>['arcLinkLabel']\n skipAngle: ArcLinkLabelsProps<Datum>['arcLinkLabelsSkipAngle']\n offset: ArcLinkLabelsProps<Datum>['arcLinkLabelsOffset']\n diagonalLength: ArcLinkLabelsProps<Datum>['arcLinkLabelsDiagonalLength']\n straightLength: ArcLinkLabelsProps<Datum>['arcLinkLabelsStraightLength']\n strokeWidth: ArcLinkLabelsProps<Datum>['arcLinkLabelsThickness']\n textOffset: ArcLinkLabelsProps<Datum>['arcLinkLabelsTextOffset']\n textColor: ArcLinkLabelsProps<Datum>['arcLinkLabelsTextColor']\n linkColor: ArcLinkLabelsProps<Datum>['arcLinkLabelsColor']\n component?: ArcLinkLabelComponent<Datum>\n}\n\nexport const ArcLinkLabelsLayer = <Datum extends DatumWithArcAndColor>({\n center,\n data,\n label: labelAccessor,\n skipAngle,\n offset,\n diagonalLength,\n straightLength,\n strokeWidth,\n textOffset,\n textColor,\n linkColor,\n component = ArcLinkLabel,\n}: ArcLinkLabelsLayerProps<Datum>) => {\n const getLabel = usePropertyAccessor<Datum, string>(labelAccessor)\n\n const { transition, interpolateLink, interpolateTextAnchor, interpolateTextPosition } =\n useArcLinkLabelsTransition<Datum>({\n data,\n skipAngle,\n offset,\n diagonalLength,\n straightLength,\n textOffset,\n linkColor,\n textColor,\n })\n\n const Label: ArcLinkLabelComponent<Datum> = component\n\n return (\n <g transform={`translate(${center[0]},${center[1]})`}>\n {transition((transitionProps, datum) => {\n return createElement(Label, {\n key: datum.id,\n datum,\n label: getLabel(datum),\n style: {\n ...transitionProps,\n thickness: strokeWidth,\n path: interpolateLink(\n transitionProps.startAngle,\n transitionProps.endAngle,\n transitionProps.innerRadius,\n transitionProps.outerRadius,\n transitionProps.offset,\n transitionProps.diagonalLength,\n transitionProps.straightLength\n ),\n textAnchor: interpolateTextAnchor(\n transitionProps.startAngle,\n transitionProps.endAngle,\n transitionProps.innerRadius,\n transitionProps.outerRadius\n ),\n textPosition: interpolateTextPosition(\n transitionProps.startAngle,\n transitionProps.endAngle,\n transitionProps.innerRadius,\n transitionProps.outerRadius,\n transitionProps.offset,\n transitionProps.diagonalLength,\n transitionProps.straightLength,\n transitionProps.textOffset\n ),\n },\n })\n })}\n </g>\n )\n}\n","import {\n // @ts-expect-error no types\n textPropsByEngine,\n} from '@nivo/core'\nimport { Theme } from '@nivo/theming'\nimport { setCanvasFont, drawCanvasText } from '@nivo/text'\nimport { DatumWithArcAndColor } from '../types'\nimport { ArcLinkLabel } from './types'\n\nexport const drawCanvasArcLinkLabels = <Datum extends DatumWithArcAndColor>(\n ctx: CanvasRenderingContext2D,\n labels: ArcLinkLabel<Datum>[],\n theme: Theme,\n strokeWidth: number\n) => {\n ctx.textBaseline = 'middle'\n setCanvasFont(ctx, theme.labels.text)\n\n labels.forEach(label => {\n ctx.textAlign = textPropsByEngine.canvas.align[label.textAnchor]\n drawCanvasText(\n ctx,\n {\n ...theme.labels.text,\n fill: label.textColor,\n },\n String(label.label),\n label.x,\n label.y\n )\n\n ctx.beginPath()\n ctx.strokeStyle = label.linkColor\n ctx.lineWidth = strokeWidth\n label.points.forEach((point, index) => {\n if (index === 0) ctx.moveTo(point.x, point.y)\n else ctx.lineTo(point.x, point.y)\n })\n ctx.stroke()\n })\n}\n","import { useMemo } from 'react'\nimport { radiansToDegrees } from '@nivo/core'\nimport { DatumWithArc } from '../types'\nimport { ArcLinkWithDatum } from './types'\nimport { computeArcLink } from './compute'\n\n/**\n * Compute links for an array of data containing arcs.\n *\n * This is typically used to create labels for arcs,\n * and it's used for the `useArcLinkLabels` hook.\n */\nexport const useArcLinks = <\n Datum extends DatumWithArc,\n ExtraProps extends Record<string, any> = Record<string, any>,\n>({\n data,\n skipAngle = 0,\n offset = 0.5,\n diagonalLength,\n straightLength,\n computeExtraProps = () => ({}) as ExtraProps,\n}: {\n data: Datum[]\n // arcs with a length below this (end angle - start angle in degrees)\n // are going to be excluded, this can typically be used to avoid having\n // overlapping labels.\n skipAngle?: number\n // offset from arc outer radius in pixels\n offset?: number\n // length of the diagonal segment of the link\n diagonalLength: number\n // length of the straight segment of the link\n straightLength: number\n // this can be used to append extra properties to the links,\n // can be used to compute a color/label for example.\n computeExtraProps?: (datum: ArcLinkWithDatum<Datum>) => ExtraProps\n}): (ArcLinkWithDatum<Datum> & ExtraProps)[] => {\n const links = useMemo(\n () =>\n data\n // filter out arcs with a length below `skipAngle`\n .filter(\n datum =>\n Math.abs(radiansToDegrees(datum.arc.endAngle - datum.arc.startAngle)) >=\n skipAngle\n )\n // compute the link for each eligible arc\n .map(datum => ({\n ...computeArcLink(datum.arc, offset, diagonalLength, straightLength),\n data: datum,\n })),\n [data, skipAngle, offset, diagonalLength, straightLength]\n )\n\n // splitting memoization of links and extra props can be more efficient,\n // this way if only `computeExtraProps` changes, we skip links computation.\n return useMemo(\n () =>\n links.map(link => ({\n ...computeExtraProps(link),\n ...link,\n })),\n [links, computeExtraProps]\n )\n}\n","import { useCallback } from 'react'\nimport { PropertyAccessor, usePropertyAccessor } from '@nivo/core'\nimport { useTheme } from '@nivo/theming'\nimport { InheritedColorConfig, useInheritedColor } from '@nivo/colors'\nimport { DatumWithArcAndColor } from '../types'\nimport { ArcLinkWithDatum, ArcLinkLabel } from './types'\nimport { useArcLinks } from './useArcLinks'\n\n/**\n * Compute arc link labels, please note that the datum should\n * contain a color in order to be able to compute the link/label text color.\n *\n * Please see `useArcLinks` for a more detailed explanation\n * about the parameters.\n */\nexport const useArcLinkLabels = <Datum extends DatumWithArcAndColor>({\n data,\n skipAngle,\n offset,\n diagonalLength,\n straightLength,\n textOffset = 0,\n label,\n linkColor,\n textColor,\n}: {\n data: Datum[]\n skipAngle?: number\n offset?: number\n diagonalLength: number\n straightLength: number\n textOffset: number\n label: PropertyAccessor<Datum, string>\n linkColor: InheritedColorConfig<Datum>\n textColor: InheritedColorConfig<Datum>\n}) => {\n const getLabel = usePropertyAccessor<Datum, string>(label)\n\n const theme = useTheme()\n const getLinkColor = useInheritedColor<Datum>(linkColor, theme)\n const getTextColor = useInheritedColor<Datum>(textColor, theme)\n\n const computeExtraProps = useCallback(\n (link: ArcLinkWithDatum<Datum>) => {\n const position = {\n x: link.points[2].x,\n y: link.points[2].y,\n }\n let textAnchor: ArcLinkLabel<Datum>['textAnchor']\n if (link.side === 'before') {\n position.x -= textOffset\n textAnchor = 'end'\n } else {\n position.x += textOffset\n textAnchor = 'start'\n }\n\n return {\n ...position,\n label: getLabel(link.data),\n linkColor: getLinkColor(link.data),\n textAnchor,\n textColor: getTextColor(link.data),\n }\n },\n [getLabel, getLinkColor, getTextColor, textOffset]\n )\n\n return useArcLinks<Datum, Omit<ArcLinkLabel<Datum>, keyof ArcLinkWithDatum<Datum>>>({\n data,\n skipAngle,\n offset,\n diagonalLength,\n straightLength,\n computeExtraProps,\n })\n}\n","import { ArcLinkLabel } from './ArcLinkLabel'\n\nexport const ArcLinkLabelComponent = ArcLinkLabel\nexport * from './ArcLinkLabelsLayer'\nexport * from './canvas'\nexport * from './compute'\nexport * from './props'\nexport * from './types'\nexport * from './useArcLinkLabels'\nexport * from './useArcLinkLabelsTransition'\nexport * from './useArcLinks'\n","import { animated, to, AnimatedProps } from '@react-spring/web'\nimport { ExtractProps } from '@nivo/core'\nimport { generateSvgArc } from './utils'\n\ntype ArcLineProps = {\n animated: AnimatedProps<{\n radius: number\n startAngle: number\n endAngle: number\n opacity: number\n }>\n} & ExtractProps<typeof animated.path>\n\nexport const ArcLine = ({ animated: animatedProps, ...rest }: ArcLineProps) => (\n <animated.path\n d={to(\n [animatedProps.radius, animatedProps.startAngle, animatedProps.endAngle],\n (radius, start, end) => generateSvgArc(radius, start, end)\n )}\n {...rest}\n />\n)\n","import { useCallback, MouseEvent } from 'react'\nimport { SpringValue, Interpolation, animated } from '@react-spring/web'\nimport { DatumWithArcAndColor } from './types'\n\nexport type ArcMouseHandler<Datum extends DatumWithArcAndColor> = (\n datum: Datum,\n event: MouseEvent<SVGPathElement>\n) => void\n\nexport interface ArcShapeProps<Datum extends DatumWithArcAndColor> {\n datum: Datum\n style: {\n opacity: SpringValue<number>\n color: SpringValue<string>\n borderWidth: number\n borderColor: SpringValue<string>\n path: Interpolation<string>\n }\n onClick?: ArcMouseHandler<Datum>\n onMouseEnter?: ArcMouseHandler<Datum>\n onMouseMove?: ArcMouseHandler<Datum>\n onMouseLeave?: ArcMouseHandler<Datum>\n}\n\n/**\n * A simple arc component to be used typically with an `ArcsLayer`.\n *\n * Please note that the component accepts `SpringValue`s instead of\n * regular values to support animations.\n */\nexport const ArcShape = <Datum extends DatumWithArcAndColor>({\n datum,\n style,\n onClick,\n onMouseEnter,\n onMouseMove,\n onMouseLeave,\n}: ArcShapeProps<Datum>) => {\n const handleClick = useCallback(\n (event: MouseEvent<SVGPathElement>) => onClick?.(datum, event),\n [onClick, datum]\n )\n\n const handleMouseEnter = useCallback(\n (event: MouseEvent<SVGPathElement>) => onMouseEnter?.(datum, event),\n [onMouseEnter, datum]\n )\n\n const handleMouseMove = useCallback(\n (event: MouseEvent<SVGPathElement>) => onMouseMove?.(datum, event),\n [onMouseMove, datum]\n )\n\n const handleMouseLeave = useCallback(\n (event: MouseEvent<SVGPathElement>) => onMouseLeave?.(datum, event),\n [onMouseLeave, datum]\n )\n\n return (\n <animated.path\n d={style.path}\n opacity={style.opacity}\n fill={datum.fill || style.color}\n stroke={style.borderColor}\n strokeWidth={style.borderWidth}\n onClick={onClick ? handleClick : undefined}\n onMouseEnter={onMouseEnter ? handleMouseEnter : undefined}\n onMouseMove={onMouseMove ? handleMouseMove : undefined}\n onMouseLeave={onMouseLeave ? handleMouseLeave : undefined}\n data-testid={`arc.${datum.id}`}\n />\n )\n}\n","import { to, SpringValue } from '@react-spring/web'\nimport { ArcGenerator } from './types'\n\n/**\n * Directly animating paths for arcs leads to sub-optimal results\n * as the interpolation is going to be linear while we deal with polar coordinates,\n * this interpolator is going to generate proper arc transitions.\n * It should be used with the `useAnimatedArc` or `useArcsTransition` hooks.\n */\nexport const interpolateArc = (\n startAngleValue: SpringValue<number>,\n endAngleValue: SpringValue<number>,\n innerRadiusValue: SpringValue<number>,\n outerRadiusValue: SpringValue<number>,\n arcGenerator: ArcGenerator\n) =>\n to(\n [startAngleValue, endAngleValue, innerRadiusValue, outerRadiusValue],\n (startAngle, endAngle, innerRadius, outerRadius) => {\n return arcGenerator({\n startAngle,\n endAngle,\n innerRadius: Math.max(0, innerRadius),\n outerRadius: Math.max(0, outerRadius),\n })\n }\n )\n","import { TransitionFn, useTransition } from '@react-spring/web'\nimport { useMotionConfig } from '@nivo/core'\nimport { DatumWithArc } from './types'\nimport {\n ArcTransitionMode,\n useArcTransitionMode,\n TransitionExtra,\n ArcTransitionProps,\n} from './arcTransitionMode'\nimport { interpolateArc } from './interpolateArc'\n\n/**\n * This hook can be used to animate a group of arcs,\n * if you want to animate a single arc,\n * please have a look at the `useAnimatedArc` hook.\n */\nexport const useArcsTransition = <\n Datum extends DatumWithArc,\n ExtraProps extends Record<string, any> = Record<string, never>,\n>(\n data: Datum[],\n mode: ArcTransitionMode = 'innerRadius',\n extra?: TransitionExtra<Datum, ExtraProps>\n) => {\n const { animate, config: springConfig } = useMotionConfig()\n\n const phases = useArcTransitionMode<Datum, ExtraProps>(mode, extra)\n\n const transition = useTransition<Datum, ArcTransitionProps<ExtraProps>>(data, {\n keys: datum => datum.id,\n initial: phases.update,\n from: phases.enter,\n enter: phases.update,\n update: phases.update,\n leave: phases.leave,\n config: springConfig,\n immediate: !animate,\n }) as unknown as TransitionFn<Datum, ArcTransitionProps<ExtraProps>>\n\n return {\n transition,\n interpolate: interpolateArc,\n }\n}\n","import { createElement } from 'react'\nimport { useTheme } from '@nivo/theming'\nimport { InheritedColorConfig, useInheritedColor } from '@nivo/colors'\nimport { DatumWithArcAndColor, ArcGenerator } from './types'\nimport { useArcsTransition } from './useArcsTransition'\nimport { ArcTransitionMode } from './arcTransitionMode'\nimport { ArcMouseHandler, ArcShape, ArcShapeProps } from './ArcShape'\n\nexport type ArcComponent<Datum extends DatumWithArcAndColor> = (\n props: ArcShapeProps<Datum>\n) => JSX.Element\n\ninterface ArcsLayerProps<Datum extends DatumWithArcAndColor> {\n center: [number, number]\n data: Datum[]\n arcGenerator: ArcGenerator\n borderWidth: number\n borderColor: InheritedColorConfig<Datum>\n onClick?: ArcMouseHandler<Datum>\n onMouseEnter?: ArcMouseHandler<Datum>\n onMouseMove?: ArcMouseHandler<Datum>\n onMouseLeave?: ArcMouseHandler<Datum>\n transitionMode: ArcTransitionMode\n component?: ArcComponent<Datum>\n}\n\nexport const ArcsLayer = <Datum extends DatumWithArcAndColor>({\n center,\n data,\n arcGenerator,\n borderWidth,\n borderColor,\n onClick,\n onMouseEnter,\n onMouseMove,\n onMouseLeave,\n transitionMode,\n component = ArcShape,\n}: ArcsLayerProps<Datum>) => {\n const theme = useTheme()\n const getBorderColor = useInheritedColor<Datum>(borderColor, theme)\n\n const { transition, interpolate } = useArcsTransition<\n Datum,\n {\n opacity: number\n color: string\n borderColor: string\n }\n >(data, transitionMode, {\n enter: datum => ({\n opacity: 0,\n color: datum.color,\n borderColor: getBorderColor(datum),\n }),\n update: datum => ({\n opacity: 1,\n color: datum.color,\n borderColor: getBorderColor(datum),\n }),\n leave: datum => ({\n opacity: 0,\n color: datum.color,\n borderColor: getBorderColor(datum),\n }),\n })\n\n const Arc: ArcComponent<Datum> = component\n\n return (\n <g transform={`translate(${center[0]},${center[1]})`}>\n {transition((transitionProps, datum) => {\n return createElement(Arc, {\n key: datum.id,\n datum,\n style: {\n ...transitionProps,\n