From 90958bdc3296075af571977c65c7eb878566918f Mon Sep 17 00:00:00 2001 From: plouc Date: Tue, 15 Dec 2020 12:31:47 +0900 Subject: [PATCH] feat(core): improve property accessor utilities --- packages/bar/src/enhance.js | 4 +-- packages/circle-packing/src/enhance.js | 4 +-- packages/core/index.d.ts | 25 ++++++++++++------- packages/core/src/hocs/withHierarchy.js | 4 +-- packages/core/src/hooks/useValueFormatter.js | 4 +-- packages/core/src/lib/propertiesConverters.js | 8 +++--- packages/heatmap/src/hooks.js | 4 +-- packages/radar/src/Radar.js | 4 +-- packages/sunburst/src/hooks.ts | 6 ++--- packages/sunburst/src/types.ts | 15 ++++++++--- 10 files changed, 47 insertions(+), 31 deletions(-) diff --git a/packages/bar/src/enhance.js b/packages/bar/src/enhance.js index d304d3d355..42899b186c 100644 --- a/packages/bar/src/enhance.js +++ b/packages/bar/src/enhance.js @@ -14,7 +14,7 @@ import { withTheme, withDimensions, withMotion, - getAccessorFor, + getPropertyAccessor, getLabelGenerator, } from '@nivo/core' import { getOrdinalColorScale, getInheritedColorGenerator } from '@nivo/colors' @@ -30,7 +30,7 @@ export default Component => getColor: getOrdinalColorScale(colors, colorBy), })), withPropsOnChange(['indexBy'], ({ indexBy }) => ({ - getIndex: getAccessorFor(indexBy), + getIndex: getPropertyAccessor(indexBy), })), withPropsOnChange(['labelTextColor', 'theme'], ({ labelTextColor, theme }) => ({ getLabelTextColor: getInheritedColorGenerator(labelTextColor, theme), diff --git a/packages/circle-packing/src/enhance.js b/packages/circle-packing/src/enhance.js index a3e004d2a2..c0bcaf794c 100644 --- a/packages/circle-packing/src/enhance.js +++ b/packages/circle-packing/src/enhance.js @@ -17,7 +17,7 @@ import { withDimensions, withTheme, withMotion, - getAccessorFor, + getPropertyAccessor, getLabelGenerator, bindDefs, } from '@nivo/core' @@ -37,7 +37,7 @@ const commonEnhancers = [ })), withPropsOnChange(['identity'], ({ identity }) => ({ - getIdentity: getAccessorFor(identity), + getIdentity: getPropertyAccessor(identity), })), // border diff --git a/packages/core/index.d.ts b/packages/core/index.d.ts index 5af6eaf09f..81a53f9726 100644 --- a/packages/core/index.d.ts +++ b/packages/core/index.d.ts @@ -1,6 +1,5 @@ import * as React from 'react' import { OpaqueInterpolation, SpringConfig } from 'react-spring' -import { number } from 'prop-types' declare module '@nivo/core' { export type DatumValue = string | number | Date @@ -279,10 +278,6 @@ declare module '@nivo/core' { export type DatumPropertyAccessor = (datum: RawDatum) => T - export function getAccessorFor( - directive: string | number | DatumPropertyAccessor - ): DatumPropertyAccessor - export function useDimensions( width: number, height: number, @@ -335,11 +330,23 @@ declare module '@nivo/core' { y: number } - export type ValueFormat = + export type ValueFormat = // d3 formatter | string // explicit formatting function - | ((value: V) => string) - export function getValueFormatter(format?: ValueFormat): (value: V) => string - export function useValueFormatter(format?: ValueFormat): (value: V) => string + | ((value: Value) => string) + export function getValueFormatter(format?: ValueFormat): (value: Value) => string + export function useValueFormatter(format?: ValueFormat): (value: Value) => string + + export type PropertyAccessor = + // path to use with `lodash.get()` + | string + // explicit accessor function + | ((datum: Datum) => Value) + export function getPropertyAccessor( + accessor: PropertyAccessor + ): (datum: Datum) => Value + export function usePropertyAccessor( + accessor: PropertyAccessor + ): (datum: Datum) => Value } diff --git a/packages/core/src/hocs/withHierarchy.js b/packages/core/src/hocs/withHierarchy.js index fc0c175f56..05fd166c28 100644 --- a/packages/core/src/hocs/withHierarchy.js +++ b/packages/core/src/hocs/withHierarchy.js @@ -12,7 +12,7 @@ import defaultProps from 'recompose/defaultProps' import setPropTypes from 'recompose/setPropTypes' import withPropsOnChange from 'recompose/withPropsOnChange' import { hierarchy } from 'd3-hierarchy' -import { getAccessorFor } from '../lib/propertiesConverters' +import { getPropertyAccessor } from '../lib/propertiesConverters' /** * This HOC watch hierarchical data props change @@ -35,6 +35,6 @@ export default ({ [valueKey]: PropTypes.oneOfType([PropTypes.string, PropTypes.func]).isRequired, }), withPropsOnChange([srcKey, valueKey], props => ({ - [destKey]: hierarchy(props[srcKey]).sum(getAccessorFor(props[valueKey])), + [destKey]: hierarchy(props[srcKey]).sum(getPropertyAccessor(props[valueKey])), })) ) diff --git a/packages/core/src/hooks/useValueFormatter.js b/packages/core/src/hooks/useValueFormatter.js index 638f4fc9a5..e315f083ab 100644 --- a/packages/core/src/hooks/useValueFormatter.js +++ b/packages/core/src/hooks/useValueFormatter.js @@ -9,11 +9,11 @@ export const getValueFormatter = format => { if (typeof format === 'string') { // time format specifier if (format.indexOf('time:') === 0) { - return `${d3TimeFormat(format.slice('5'))}` + return d3TimeFormat(format.slice('5')) } // standard format specifier - return `${d3Format(format)}` + return d3Format(format) } // no formatting diff --git a/packages/core/src/lib/propertiesConverters.js b/packages/core/src/lib/propertiesConverters.js index eeec218cbe..4f9ea3fb1b 100644 --- a/packages/core/src/lib/propertiesConverters.js +++ b/packages/core/src/lib/propertiesConverters.js @@ -1,6 +1,7 @@ import isFunction from 'lodash/isFunction' import get from 'lodash/get' import { format } from 'd3-format' +import { useMemo } from 'react' export const getLabelGenerator = (_label, labelFormat) => { const getRawLabel = isFunction(_label) ? _label : d => get(d, _label) @@ -13,7 +14,8 @@ export const getLabelGenerator = (_label, labelFormat) => { return getRawLabel } -export const getAccessorFor = directive => - isFunction(directive) ? directive : d => get(d, directive) +export const getPropertyAccessor = accessor => + isFunction(accessor) ? accessor : d => get(d, accessor) -export const getAccessorOrValue = value => (isFunction(value) ? value : () => value) +export const usePropertyAccessor = accessor => + useMemo(() => getPropertyAccessor(accessor), [accessor]) diff --git a/packages/heatmap/src/hooks.js b/packages/heatmap/src/hooks.js index 65c1d809ce..6fbd521b06 100644 --- a/packages/heatmap/src/hooks.js +++ b/packages/heatmap/src/hooks.js @@ -1,6 +1,6 @@ import { useState, useMemo } from 'react' import { scaleOrdinal, scaleLinear } from 'd3-scale' -import { useTheme, getAccessorFor, guessQuantizeColorScale } from '@nivo/core' +import { useTheme, usePropertyAccessor, guessQuantizeColorScale } from '@nivo/core' import { useInheritedColor } from '@nivo/colors' const computeX = (column, cellWidth, padding) => { @@ -83,7 +83,7 @@ export const useHeatMap = ({ }) => { const [currentCellId, setCurrentCellId] = useState(null) - const getIndex = useMemo(() => getAccessorFor(indexBy), [indexBy]) + const getIndex = usePropertyAccessor(indexBy) const indices = useMemo(() => data.map(getIndex), [data, getIndex]) const layoutConfig = useMemo(() => { diff --git a/packages/radar/src/Radar.js b/packages/radar/src/Radar.js index 89d4a1f10a..d4e5cea83e 100644 --- a/packages/radar/src/Radar.js +++ b/packages/radar/src/Radar.js @@ -13,7 +13,7 @@ import { useTheme, useCurveInterpolation, useDimensions, - getAccessorFor, + usePropertyAccessor, SvgWrapper, } from '@nivo/core' import { useOrdinalColorScale } from '@nivo/colors' @@ -58,7 +58,7 @@ const Radar = memo( legends, role, }) => { - const getIndex = useMemo(() => getAccessorFor(indexBy), [indexBy]) + const getIndex = usePropertyAccessor(indexBy) const indices = useMemo(() => data.map(getIndex), [data, getIndex]) const { margin, innerWidth, innerHeight, outerWidth, outerHeight } = useDimensions( diff --git a/packages/sunburst/src/hooks.ts b/packages/sunburst/src/hooks.ts index 976644180b..8a642004a1 100644 --- a/packages/sunburst/src/hooks.ts +++ b/packages/sunburst/src/hooks.ts @@ -2,7 +2,7 @@ import pick from 'lodash/pick' import sortBy from 'lodash/sortBy' import cloneDeep from 'lodash/cloneDeep' import React, { createElement, useCallback, useMemo } from 'react' -import { getAccessorFor, useTheme, useValueFormatter } from '@nivo/core' +import { usePropertyAccessor, useTheme, useValueFormatter } from '@nivo/core' import { arc, Arc } from 'd3-shape' import { useOrdinalColorScale, useInheritedColor } from '@nivo/colors' import { useTooltip } from '@nivo/tooltip' @@ -108,8 +108,8 @@ export const useSunburst = ({ datum: NormalizedDatum ) => string - const getId = useMemo(() => getAccessorFor(id), [id]) - const getValue = useMemo(() => getAccessorFor(value), [value]) + const getId = usePropertyAccessor(id) + const getValue = usePropertyAccessor(value) const formatValue = useValueFormatter(valueFormat) diff --git a/packages/sunburst/src/types.ts b/packages/sunburst/src/types.ts index 411ce2f3a8..c73193ca5e 100644 --- a/packages/sunburst/src/types.ts +++ b/packages/sunburst/src/types.ts @@ -1,12 +1,19 @@ import { Arc } from 'd3-shape' import { HierarchyRectangularNode } from 'd3-hierarchy' import { OrdinalColorScaleConfig, InheritedColorConfig } from '@nivo/colors' -import { Theme, Dimensions, Box, ValueFormat, SvgDefsAndFill, ModernMotionProps } from '@nivo/core' +import { + Theme, + Dimensions, + Box, + ValueFormat, + SvgDefsAndFill, + ModernMotionProps, + PropertyAccessor, +} from '@nivo/core' export type DatumId = string | number export type DatumValue = number -export type DatumPropertyAccessor = (datum: RawDatum) => T export type LabelAccessorFunction = (datum: RawDatum) => string | number export type SunburstLayerId = 'slices' | 'sliceLabels' @@ -25,8 +32,8 @@ export type SunburstLayer = SunburstLayerId | SunburstCustomLayer { data: RawDatum - id?: string | number | DatumPropertyAccessor - value?: string | number | DatumPropertyAccessor + id?: PropertyAccessor + value?: PropertyAccessor valueFormat?: ValueFormat }