From 7bb42c688c718c68d5d734dbf401156cc04da496 Mon Sep 17 00:00:00 2001 From: plouc Date: Wed, 11 Nov 2020 08:06:43 +0900 Subject: [PATCH] feat(colors): migrate ordinalColorScale to TypeScript --- packages/colors/index.d.ts | 95 -------------- packages/colors/src/index.ts | 4 +- packages/colors/src/inheritedColor.ts | 64 ++++----- packages/colors/src/ordinalColorScale.js | 96 -------------- packages/colors/src/ordinalColorScale.ts | 157 +++++++++++++++++++++++ 5 files changed, 192 insertions(+), 224 deletions(-) delete mode 100644 packages/colors/index.d.ts delete mode 100644 packages/colors/src/ordinalColorScale.js create mode 100644 packages/colors/src/ordinalColorScale.ts diff --git a/packages/colors/index.d.ts b/packages/colors/index.d.ts deleted file mode 100644 index 2fa2d39cc2..0000000000 --- a/packages/colors/index.d.ts +++ /dev/null @@ -1,95 +0,0 @@ -import * as React from 'react' - -declare module '@nivo/colors' { - export type CategoricalColorSchemeId = - | 'nivo' - | 'category10' - | 'accent' - | 'dark2' - | 'paired' - | 'pastel1' - | 'pastel2' - | 'set1' - | 'set2' - | 'set3' - - export type DivergingColorSchemeId = - | 'brown_blueGreen' - | 'purpleRed_green' - | 'pink_yellowGreen' - | 'purple_orange' - | 'red_blue' - | 'red_grey' - | 'red_yellow_blue' - | 'red_yellow_green' - | 'spectral' - - export type SequentialColorSchemeId = - // single hue - | 'blues' - | 'greens' - | 'greys' - | 'oranges' - | 'purples' - | 'reds' - // multi hue - | 'blue_green' - | 'blue_purple' - | 'green_blue' - | 'orange_red' - | 'purple_blue_green' - | 'purple_blue' - | 'purple_red' - | 'red_purple' - | 'yellow_green_blue' - | 'yellow_green' - | 'yellow_orange_brown' - | 'yellow_orange_red' - - export type DatumIdentityFunction = (datum: D) => string | number - export type DatumIdentity = string | DatumIdentityFunction - - export type ColorSchemeId = - | CategoricalColorSchemeId - | DivergingColorSchemeId - | SequentialColorSchemeId - - export interface DatumColorInstruction { - datum: string - } - export interface SchemeColorInstruction { - scheme: ColorSchemeId - // size is useful for diverging & sequential colors, - // as they are array of array, whereas categorical colors - // are simple arrays, if the size isn't specified, - // the bigger array will be selected, this means the 11th - // for diverging colors and 9th for sequential ones. - size?: number - } - export type CustomColorFunction = (datum: D) => string - - export type OrdinalColorsInstruction = - | DatumColorInstruction - | SchemeColorInstruction - | CustomColorFunction - | string[] - | string - - export type OrdinalColorScale = (datum: D) => string - - export type ColorIdentityFunction = (datum: D) => string | number - - export function getOrdinalColorScale( - instruction: OrdinalColorsInstruction, - identity: string | ColorIdentityFunction - ): OrdinalColorScale - - export type ColorModifierType = 'brighter' | 'darker' | 'opacity' - export type ColorModifier = [ColorModifierType, number] - export type InheritedColorFunction = (datum: D) => string - export type InheritedColorProp = - | string - | { theme: string } - | { from: string; modifiers?: ColorModifier[] } - | InheritedColorFunction -} diff --git a/packages/colors/src/index.ts b/packages/colors/src/index.ts index 92eef362fd..9ad2b7e42c 100644 --- a/packages/colors/src/index.ts +++ b/packages/colors/src/index.ts @@ -1,5 +1,5 @@ export * from './schemes' -export * from './ordinalColorScale' export * from './inheritedColor' -export * from './props' export * from './motion' +export * from './ordinalColorScale' +export * from './props' diff --git a/packages/colors/src/inheritedColor.ts b/packages/colors/src/inheritedColor.ts index ff69ac0f9d..162fd58eae 100644 --- a/packages/colors/src/inheritedColor.ts +++ b/packages/colors/src/inheritedColor.ts @@ -13,35 +13,35 @@ export type ColorModifier = ColorModifierBrightness | ColorModifierDarkness | Co export type ColorModifierFunction = (color: RGBColor) => RGBColor -export type InheritedColorPlain = string +export type InheritedColorConfigStaticColor = string -export type InheritedColorFunction = (d: Datum) => string +export type InheritedColorConfigCustomFunction = (d: Datum) => string -export interface InheritedColorFromTheme { +export interface InheritedColorConfigFromTheme { theme: string } -export interface InheritedColorFromContext { +export interface InheritedColorConfigFromContext { from: string modifiers?: ColorModifier[] } -export type InheritedColor = - | InheritedColorPlain - | InheritedColorFunction - | InheritedColorFromTheme - | InheritedColorFromContext +export type InheritedColorConfig = + | InheritedColorConfigStaticColor + | InheritedColorConfigCustomFunction + | InheritedColorConfigFromTheme + | InheritedColorConfigFromContext -const isInheritedColorFromTheme = ( - instruction: InheritedColor -): instruction is InheritedColorFromTheme => { - return (instruction as InheritedColorFromTheme).theme !== undefined +const isInheritedColorConfigFromTheme = ( + config: InheritedColorConfig +): config is InheritedColorConfigFromTheme => { + return (config as InheritedColorConfigFromTheme).theme !== undefined } -const isInheritedColorFromContext = ( - instruction: InheritedColor -): instruction is InheritedColorFromContext => { - return (instruction as InheritedColorFromContext).from !== undefined +const isInheritedColorConfigFromContext = ( + config: InheritedColorConfig +): config is InheritedColorConfigFromContext => { + return (config as InheritedColorConfigFromContext).from !== undefined } /** @@ -56,36 +56,36 @@ const isInheritedColorFromContext = ( * - static color */ export const getInheritedColorGenerator = ( - instruction: InheritedColor, + config: InheritedColorConfig, theme?: Theme ) => { // user provided function - if (typeof instruction === 'function') { - return (datum: Datum) => instruction(datum) + if (typeof config === 'function') { + return (datum: Datum) => config(datum) } - if (isPlainObject(instruction)) { + if (isPlainObject(config)) { // use color from theme - if (isInheritedColorFromTheme(instruction)) { + if (isInheritedColorConfigFromTheme(config)) { if (theme === undefined) { throw new Error(`Unable to use color from theme as no theme was provided`) } - const themeColor = get(theme, instruction.theme) + const themeColor = get(theme, config.theme) if (themeColor === undefined) { - throw new Error(`Color from theme is undefined at path: '${instruction.theme}'`) + throw new Error(`Color from theme is undefined at path: '${config.theme}'`) } return () => themeColor } // use color from parent with optional color modifiers - if (isInheritedColorFromContext(instruction)) { - const getColor = (d: Datum) => get(d, instruction.from) + if (isInheritedColorConfigFromContext(config)) { + const getColor = (d: Datum) => get(d, config.from) - if (Array.isArray(instruction.modifiers)) { + if (Array.isArray(config.modifiers)) { const modifiers: ColorModifierFunction[] = [] - for (const modifier of instruction.modifiers) { + for (const modifier of config.modifiers) { const [modifierType, amount] = modifier if (modifierType === 'brighter') { modifiers.push(color => color.brighter(amount)) @@ -122,8 +122,10 @@ export const getInheritedColorGenerator = ( } // use provided color statically - return () => instruction as string + return () => config as string } -export const useInheritedColor = (instruction: InheritedColor, theme?: Theme) => - useMemo(() => getInheritedColorGenerator(instruction, theme), [instruction, theme]) +export const useInheritedColor = ( + config: InheritedColorConfig, + theme?: Theme +) => useMemo(() => getInheritedColorGenerator(config, theme), [config, theme]) diff --git a/packages/colors/src/ordinalColorScale.js b/packages/colors/src/ordinalColorScale.js deleted file mode 100644 index 9e1f8a0bf5..0000000000 --- a/packages/colors/src/ordinalColorScale.js +++ /dev/null @@ -1,96 +0,0 @@ -import { useMemo } from 'react' -import get from 'lodash.get' -import isPlainObject from 'lodash.isplainobject' -import { scaleOrdinal } from 'd3-scale' -import { - colorSchemes, - isCategoricalColorScheme, - isSequentialColorScheme, - isDivergingColorScheme, -} from './schemes' - -/** - * Compute an ordinal color scale - */ -export const getOrdinalColorScale = (instruction, identity) => { - console.log(instruction, identity) - // user defined function - if (typeof instruction === 'function') return instruction - - // compute accessor to the datum identity - const getIdentity = typeof identity === 'function' ? identity : d => get(d, identity) - - // user defined color array - if (Array.isArray(instruction)) { - const scale = scaleOrdinal(instruction) - const generator = d => scale(getIdentity(d)) - generator.scale = scale - - return generator - } - - if (isPlainObject(instruction)) { - // use color from current datum - if (instruction.datum !== undefined) { - return datum => get(datum, instruction.datum) - } - - // ordinal scale from predefined scheme - if (instruction.scheme !== undefined) { - // categorical color scheme - if (isCategoricalColorScheme(instruction.scheme)) { - const scale = scaleOrdinal(colorSchemes[instruction.scheme]) - const generator = d => scale(getIdentity(d)) - generator.scale = scale - - return generator - } - - // Diverging color schemes support a size k ranging from 3 to 11 - if (isDivergingColorScheme(instruction.scheme)) { - if ( - instruction.size !== undefined && - (instruction.size < 3 || instruction.size > 11) - ) { - throw new Error( - `Invalid size '${instruction.size}' for diverging color scheme '${instruction.scheme}', must be between 3~11` - ) - } - - const scale = scaleOrdinal(colorSchemes[instruction.scheme][instruction.size || 11]) - const generator = d => scale(getIdentity(d)) - generator.scale = scale - - return generator - } - - // Sequential, single-hue color schemes support a size k ranging from 3 to 9. - // Sequential, multi-hue color schemes support a size k ranging from 3 to 9. - if (isSequentialColorScheme(instruction.scheme)) { - if ( - instruction.size !== undefined && - (instruction.size < 3 || instruction.size > 9) - ) { - throw new Error( - `Invalid size '${instruction.size}' for sequential color scheme '${instruction.scheme}', must be between 3~9` - ) - } - - const scale = scaleOrdinal(colorSchemes[instruction.scheme][instruction.size || 9]) - const generator = d => scale(getIdentity(d)) - generator.scale = scale - - return generator - } - } - - throw new Error( - `Invalid colors, when using an object, you should either pass a 'datum' or a 'scheme' property` - ) - } - - return () => instruction -} - -export const useOrdinalColorScale = (instruction, identity) => - useMemo(() => getOrdinalColorScale(instruction, identity), [instruction, identity]) diff --git a/packages/colors/src/ordinalColorScale.ts b/packages/colors/src/ordinalColorScale.ts new file mode 100644 index 0000000000..ee64e6f3e6 --- /dev/null +++ b/packages/colors/src/ordinalColorScale.ts @@ -0,0 +1,157 @@ +import { useMemo } from 'react' +import { get, isPlainObject } from 'lodash' +import { scaleOrdinal } from 'd3-scale' +import { + ColorSchemeId, + colorSchemes, + isCategoricalColorScheme, + isSequentialColorScheme, + isDivergingColorScheme, +} from './schemes' + +/** + * Static color. + */ +export type OrdinalColorScaleConfigStaticColor = string + +/** + * User defined function, receiving the current datum. + */ +export type OrdinalColorScaleConfigCustomFunction = (d: Datum) => string + +/** + * Pre-defined color scheme. + */ +export interface OrdinalColorScaleConfigScheme { + scheme: ColorSchemeId + // size is useful for diverging & sequential colors, + // as they are array of array, whereas categorical colors + // are simple arrays, if the size isn't specified, + // the bigger array will be selected, this means the 11th + // for diverging colors and 9th for sequential ones. + size?: number +} + +/** + * User defined colors. + */ +export type OrdinalColorScaleConfigCustomColors = string[] + +/** + * Get color from datum. + */ +export interface OrdinalColorScaleConfigDatumProperty { + // path to the color property + datum: string +} + +export type OrdinalColorScaleConfig = + | OrdinalColorScaleConfigStaticColor + | OrdinalColorScaleConfigCustomFunction + | OrdinalColorScaleConfigScheme + | OrdinalColorScaleConfigCustomColors + | OrdinalColorScaleConfigDatumProperty + +const isOrdinalColorScaleConfigScheme = ( + config: OrdinalColorScaleConfig +): config is OrdinalColorScaleConfigScheme => { + return (config as OrdinalColorScaleConfigScheme).scheme !== undefined +} + +const isOrdinalColorScaleConfigDatumProperty = ( + config: OrdinalColorScaleConfig +): config is OrdinalColorScaleConfigDatumProperty => { + return (config as OrdinalColorScaleConfigDatumProperty).datum !== undefined +} + +export type DatumIdentityAccessor = (datum: Datum) => string | number + +export type OrdinalColorScale = (d: Datum) => string + +/** + * Compute an ordinal color scale + */ +export const getOrdinalColorScale = ( + config: OrdinalColorScaleConfig, + identity?: string | DatumIdentityAccessor +): OrdinalColorScale => { + // user defined function + if (typeof config === 'function') { + return config + } + + // compute accessor to the datum identity + const getIdentity = + typeof identity === 'function' ? identity : (datum: Datum) => get(datum, identity as string) + + // user defined color array + if (Array.isArray(config)) { + const scale = scaleOrdinal(config) + const generator = (datum: Datum) => scale(getIdentity(datum)) + generator.scale = scale + + return generator as OrdinalColorScale + } + + if (isPlainObject(config)) { + // use color from current datum + if (isOrdinalColorScaleConfigDatumProperty(config)) { + return (datum: Datum) => get(datum, config.datum) + } + + // ordinal scale from predefined scheme + if (isOrdinalColorScaleConfigScheme(config)) { + // categorical color scheme + if (isCategoricalColorScheme(config.scheme)) { + const scale = scaleOrdinal(colorSchemes[config.scheme]) + const generator = (datum: Datum) => scale(getIdentity(datum)) + generator.scale = scale + + return generator as OrdinalColorScale + } + + // Diverging color schemes support a size k ranging from 3 to 11 + if (isDivergingColorScheme(config.scheme)) { + if (config.size !== undefined && (config.size < 3 || config.size > 11)) { + throw new Error( + `Invalid size '${config.size}' for diverging color scheme '${config.scheme}', must be between 3~11` + ) + } + + const scale = scaleOrdinal(colorSchemes[config.scheme][config.size || 11]) + const generator = (d: Datum) => scale(getIdentity(d)) + generator.scale = scale + + return generator as OrdinalColorScale + } + + // Sequential, single-hue color schemes support a size k ranging from 3 to 9. + // Sequential, multi-hue color schemes support a size k ranging from 3 to 9. + if (isSequentialColorScheme(config.scheme)) { + if (config.size !== undefined && (config.size < 3 || config.size > 9)) { + throw new Error( + `Invalid size '${config.size}' for sequential color scheme '${config.scheme}', must be between 3~9` + ) + } + + const scale = scaleOrdinal(colorSchemes[config.scheme][config.size || 9]) + const generator = (d: Datum) => scale(getIdentity(d)) + generator.scale = scale + + return generator as OrdinalColorScale + } + } + + throw new Error( + `Invalid colors, when using an object, you should either pass a 'datum' or a 'scheme' property` + ) + } + + // static color + return () => config as string +} + +export const useOrdinalColorScale = ( + config: OrdinalColorScaleConfig, + identity: string | DatumIdentityAccessor +) => useMemo(() => getOrdinalColorScale(config, identity), [config, identity])