diff --git a/website/src/components/ThemeSelector.tsx b/website/src/components/ThemeSelector.tsx
index 4eee1ad8d2..5611f70fdd 100644
--- a/website/src/components/ThemeSelector.tsx
+++ b/website/src/components/ThemeSelector.tsx
@@ -1,7 +1,7 @@
import React, { useCallback, useMemo } from 'react'
import styled, { useTheme } from 'styled-components'
import { useSetTheme } from '../theming/SwitchableThemeProvider'
-import { Switch } from './controls/Switch'
+import { Switch } from './controls/ui'
const ThemeSelector = () => {
const theme = useTheme()
diff --git a/website/src/components/controls/ControlsGroup.tsx b/website/src/components/controls/ControlsGroup.tsx
index 5c5bf11caf..6ef5e7d3b4 100644
--- a/website/src/components/controls/ControlsGroup.tsx
+++ b/website/src/components/controls/ControlsGroup.tsx
@@ -1,31 +1,39 @@
import React, { memo, useCallback } from 'react'
import get from 'lodash/get'
import snakeCase from 'lodash/snakeCase'
-import { ArrayControl } from './ArrayControl'
-import { ObjectControl } from './ObjectControl'
-import { SwitchControl } from './SwitchControl'
-import { SwitchableRangeControl } from './SwitchableRangeControl'
-import { ColorsControl } from './ColorsControl'
-import { QuantizeColorsControl } from './QuantizeColorsControl'
-import { ColorPickerControl } from './ColorPickerControl'
-import { TextControl } from './TextControl'
-import { RadioControl } from './RadioControl'
-import { RangeControl } from './RangeControl'
-import { ChoicesControl } from './ChoicesControl'
-import { BoxAnchorControl } from './BoxAnchorControl'
-import { MarginControl } from './MarginControl'
-import { OpacityControl } from './OpacityControl'
-import { LineWidthControl } from './LineWidthControl'
-import { MotionConfigControl } from './MotionConfigControl'
-import { NumberArrayControl } from './NumberArrayControl'
-import { AngleControl } from './AngleControl'
-import { OrdinalColorsControl } from './OrdinalColorsControl'
-import { InheritedColorControl } from './InheritedColorControl'
-import { BlendModeControl } from './BlendModeControl'
-import PropertyDocumentation from './PropertyDocumentation'
-import { ValueFormatControl } from './ValueFormatControl'
-import { AnnotationsControl } from './AnnotationsControl'
import { ChartProperty, Flavor } from '../../types'
+import { ControlContext } from './types'
+import {
+ ArrayControl,
+ ObjectControl,
+ SwitchControl,
+ SwitchableRangeControl,
+ TextControl,
+ RadioControl,
+ RangeControl,
+ ChoicesControl,
+ NumberArrayControl,
+ PropertyDocumentation,
+} from './generics'
+import {
+ BoxAnchorControl,
+ MarginControl,
+ LineWidthControl,
+ MotionConfigControl,
+ AngleControl,
+ ValueFormatControl,
+ AnnotationsControl,
+} from './specialized'
+import {
+ BlendModeControl,
+ ContinuousColorsControl,
+ ColorPickerControl,
+ ColorsControl,
+ OrdinalColorsControl,
+ OpacityControl,
+ InheritedColorControl,
+ QuantizeColorsControl,
+} from './colors'
export const shouldRenderProperty = (property: ChartProperty, currentSettings: any) => {
if (typeof property.when !== 'function') return true
@@ -39,7 +47,7 @@ interface ControlSwitcherProps {
currentFlavor: Flavor
settings: any
onChange: any
- context: any
+ context?: ControlContext
}
const ControlSwitcher = memo(
@@ -88,6 +96,7 @@ const ControlSwitcher = memo(
property={property}
flavors={flavors}
currentFlavor={currentFlavor}
+ context={context}
/>
)
}
@@ -175,6 +184,7 @@ const ControlSwitcher = memo(
flavors={flavors}
currentFlavor={currentFlavor}
value={value}
+ context={context}
onChange={handleChange}
/>
)
@@ -417,6 +427,20 @@ const ControlSwitcher = memo(
/>
)
+ case 'continuous_colors':
+ return (
+
+ )
+
default:
throw new Error(
`invalid control type: ${controlConfig!.type} for property: ${property.name}`
@@ -432,7 +456,7 @@ interface ControlsGroupProps {
controls: ChartProperty[]
settings: any
onChange: any
- context?: any
+ context?: ControlContext
}
export const ControlsGroup = ({
@@ -445,9 +469,9 @@ export const ControlsGroup = ({
context,
}: ControlsGroupProps) => (
<>
- {controls.map(control => (
+ {controls.map((control, index) => (
({
label: mode,
@@ -16,9 +16,9 @@ interface BlendModeControlProps {
flavors: Flavor[]
currentFlavor: Flavor
config: BlendModeControlConfig
- value: string
- onChange: (value: string) => void
- context?: any
+ value: CssMixBlendMode
+ onChange: (blendMode: CssMixBlendMode) => void
+ context?: ControlContext
}
export const BlendModeControl = ({ config, ...props }: BlendModeControlProps) => (
diff --git a/website/src/components/controls/ColorPickerControl.tsx b/website/src/components/controls/colors/ColorPickerControl.tsx
similarity index 83%
rename from website/src/components/controls/ColorPickerControl.tsx
rename to website/src/components/controls/colors/ColorPickerControl.tsx
index da2ceaa203..abba078399 100644
--- a/website/src/components/controls/ColorPickerControl.tsx
+++ b/website/src/components/controls/colors/ColorPickerControl.tsx
@@ -1,9 +1,7 @@
import React, { useCallback } from 'react'
-import { Control } from './Control'
-import { PropertyHeader } from './PropertyHeader'
-import { Help } from './Help'
-import { Flavor } from '../../types'
-import { ColorPickerControlConfig } from './types'
+import { Flavor } from '../../../types'
+import { ColorPickerControlConfig, ControlContext } from '../types'
+import { Control, PropertyHeader, Help } from '../ui'
interface ColorPickerControlProps {
id: string
@@ -13,7 +11,7 @@ interface ColorPickerControlProps {
config: ColorPickerControlConfig
value: string
onChange: (value: string) => void
- context: any
+ context?: ControlContext
}
export const ColorPickerControl = ({
diff --git a/website/src/components/controls/ColorsControl.tsx b/website/src/components/controls/colors/ColorsControl.tsx
similarity index 89%
rename from website/src/components/controls/ColorsControl.tsx
rename to website/src/components/controls/colors/ColorsControl.tsx
index 3953f3f074..7ad4c76fc1 100644
--- a/website/src/components/controls/ColorsControl.tsx
+++ b/website/src/components/controls/colors/ColorsControl.tsx
@@ -8,13 +8,10 @@ import {
} from '@nivo/colors'
// @ts-ignore
import { components } from 'react-select'
+import { ChartProperty, Flavor } from '../../../types'
+import { ColorsControlConfig, ControlContext } from '../types'
+import { Control, PropertyHeader, Help, Select } from '../ui'
import { ColorsControlItem } from './ColorsControlItem'
-import { Control } from './Control'
-import { PropertyHeader } from './PropertyHeader'
-import { Help } from './Help'
-import Select from './Select'
-import { ChartProperty, Flavor } from '../../types'
-import { ColorsControlConfig } from './types'
const colors = colorSchemeIds.map(id => ({
id,
@@ -46,7 +43,7 @@ interface ColorsControlProps {
onChange: any
value: string
config: ColorsControlConfig
- context?: any
+ context?: ControlContext
}
export const ColorsControl = ({
diff --git a/website/src/components/controls/ColorsControlItem.tsx b/website/src/components/controls/colors/ColorsControlItem.tsx
similarity index 97%
rename from website/src/components/controls/ColorsControlItem.tsx
rename to website/src/components/controls/colors/ColorsControlItem.tsx
index eaa002e0b9..296c8ab004 100644
--- a/website/src/components/controls/ColorsControlItem.tsx
+++ b/website/src/components/controls/colors/ColorsControlItem.tsx
@@ -19,7 +19,7 @@ const Name = styled.span`
font-weight: 500;
font-size: 0.8rem;
margin-right: 14px;
- width: 130px;
+ width: 160px;
`
const Sample = styled.div`
diff --git a/website/src/components/controls/colors/ContinuousColorsControl.tsx b/website/src/components/controls/colors/ContinuousColorsControl.tsx
new file mode 100644
index 0000000000..1955e120cc
--- /dev/null
+++ b/website/src/components/controls/colors/ContinuousColorsControl.tsx
@@ -0,0 +1,202 @@
+import React, { useCallback, useState, useMemo } from 'react'
+import { upperFirst } from 'lodash'
+import {
+ ContinuousColorScaleConfig,
+ sequentialColorSchemeIds,
+ ColorInterpolatorId,
+ divergingColorSchemeIds,
+ divergingColorScaleDefaults,
+ quantizeColorScaleDefaults,
+} from '@nivo/colors'
+import { ChartProperty, Flavor } from '../../../types'
+import { ContinuousColorsControlConfig, ControlContext, ObjectControlConfig } from '../types'
+import { ObjectControl } from '../generics'
+import { humanizeColorSchemeId } from './humanizeColorSchemeId'
+
+interface ContinuousColorsControlProps {
+ id: string
+ property: ChartProperty
+ flavors: Flavor[]
+ currentFlavor: Flavor
+ config: ContinuousColorsControlConfig
+ value: ContinuousColorScaleConfig
+ onChange: (config: ContinuousColorScaleConfig) => void
+ context?: ControlContext
+}
+
+const scaleTypes = ['sequential', 'diverging', 'quantize']
+const scaleTypeChoices = scaleTypes.map(type => ({
+ label: upperFirst(type),
+ value: type,
+}))
+
+const schemeChoices: {
+ label: string
+ value: ColorInterpolatorId
+}[] = []
+sequentialColorSchemeIds.forEach(schemeId => {
+ schemeChoices.push({
+ label: `Sequential: ${humanizeColorSchemeId(schemeId)}`,
+ value: schemeId,
+ })
+})
+divergingColorSchemeIds.forEach(schemeId => {
+ schemeChoices.push({
+ label: `Diverging: ${humanizeColorSchemeId(schemeId)}`,
+ value: schemeId,
+ })
+})
+
+const helpByType: Record = {
+ sequential: `
+ The sequential color scale maps colors linearly from min to max value.
+ It is intended to be used with a sequential color scheme,
+ but also supports others.
+ `,
+ diverging: `
+ The diverging color scale maps colors from min to max value,
+ with a diverging point which can be configured via \`divergeAt\`.
+ It is intended to be used with a diverging color scheme,
+ but also supports others.
+ `,
+ quantize: `
+ The quantize color scale maps colors from min to max value
+ to a discrete color range, dividing the domain into uniform segments.
+ You can either use a predefined color scheme or pass a
+ custom array of colors.
+ `,
+}
+
+export const ContinuousColorsControl = ({
+ id,
+ property,
+ flavors,
+ currentFlavor,
+ value,
+ onChange,
+ context,
+}: ContinuousColorsControlProps) => {
+ const [lastDivergeAtValue, setLastDivergeAtValue] = useState(
+ 'divergeAt' in value ? value.divergeAt : divergingColorScaleDefaults.divergeAt
+ )
+ const [lastStepsValue, setLastStepsValue] = useState(
+ 'steps' in value ? value.steps : quantizeColorScaleDefaults.steps
+ )
+
+ const objectProperty: Omit & {
+ control: ObjectControlConfig
+ } = useMemo(() => {
+ return {
+ ...property,
+ control: {
+ type: 'object',
+ isOpenedByDefault: true,
+ props: [
+ {
+ key: 'type',
+ type: `'sequential' | 'diverging' | 'quantize'`,
+ required: true,
+ help: helpByType[value.type],
+ control: {
+ type: 'choices',
+ choices: scaleTypeChoices,
+ },
+ },
+ {
+ key: 'scheme',
+ type: 'string',
+ control: {
+ type: 'choices',
+ choices: schemeChoices,
+ },
+ },
+ {
+ key: 'minValue',
+ type: 'number',
+ help: 'If omitted, will use the min value from the data.',
+ },
+ {
+ key: 'maxValue',
+ type: 'number',
+ help: 'If omitted, will use the max value from the data.',
+ },
+ {
+ key: 'divergeAt',
+ type: 'number',
+ help: 'Define the divergence point between min & max values (0~1).',
+ when: config => config.type === 'diverging',
+ defaultValue: divergingColorScaleDefaults.divergeAt,
+ control: {
+ type: 'range',
+ min: 0,
+ max: 1,
+ step: 0.05,
+ },
+ },
+ {
+ key: 'steps',
+ type: 'number',
+ help: `
+ Customize the number of steps you want to use for a \`quantize\` scale
+ unless you specify a custom array of colors, in which case the number of
+ steps is equal to the number of colors you defined.
+ `,
+ when: config => config.type === 'quantize',
+ defaultValue: quantizeColorScaleDefaults.steps,
+ control: {
+ type: 'range',
+ min: 2,
+ max: 16,
+ },
+ },
+ ],
+ },
+ }
+ }, [value.type, flavors])
+
+ const handleChange = useCallback(
+ ({ divergeAt, steps, ...genericProps }: any) => {
+ let fixedValue = genericProps
+
+ if (fixedValue.type === 'diverging') {
+ if (divergeAt === undefined) {
+ fixedValue = {
+ ...fixedValue,
+ divergeAt: lastDivergeAtValue,
+ }
+ } else {
+ fixedValue = { ...fixedValue, divergeAt }
+ setLastDivergeAtValue(divergeAt)
+ }
+ }
+
+ if (fixedValue.type === 'quantize') {
+ if (steps === undefined) {
+ fixedValue = {
+ ...fixedValue,
+ steps: lastStepsValue,
+ }
+ } else {
+ fixedValue = { ...fixedValue, steps }
+ setLastStepsValue(steps)
+ }
+ }
+
+ onChange(fixedValue)
+ },
+ [onChange, lastDivergeAtValue, setLastDivergeAtValue, lastStepsValue, setLastStepsValue]
+ )
+
+ return (
+
+ )
+}
diff --git a/website/src/components/controls/InheritedColorControl.tsx b/website/src/components/controls/colors/InheritedColorControl.tsx
similarity index 95%
rename from website/src/components/controls/InheritedColorControl.tsx
rename to website/src/components/controls/colors/InheritedColorControl.tsx
index fb932336ed..b53196220e 100644
--- a/website/src/components/controls/InheritedColorControl.tsx
+++ b/website/src/components/controls/colors/InheritedColorControl.tsx
@@ -3,13 +3,10 @@ import isString from 'lodash/isString'
import isPlainObject from 'lodash/isPlainObject'
import styled from 'styled-components'
import { InheritedColorConfig } from '@nivo/colors'
-import { Control } from './Control'
-import { PropertyHeader } from './PropertyHeader'
-import { Help } from './Help'
-import Select from './Select'
-import InheritedColorModifierControl from './InheritedColorModifierControl'
-import { ChartProperty, Flavor } from '../../types'
-import { InheritedColorControlConfig } from './types'
+import { ChartProperty, Flavor } from '../../../types'
+import { ControlContext, InheritedColorControlConfig } from '../types'
+import { Control, PropertyHeader, Help, Select } from '../ui'
+import { InheritedColorModifierControl } from './InheritedColorModifierControl'
const themeProperties = ['background', 'grid.line.stroke', 'labels.text.fill'].map(prop => ({
label: prop,
@@ -26,7 +23,7 @@ interface InheritedColorControlProps {
value: InheritedColorConfig
config: InheritedColorControlConfig
onChange: (value: InheritedColorConfig) => any
- context?: any
+ context?: ControlContext
}
export const InheritedColorControl = ({
diff --git a/website/src/components/controls/InheritedColorModifierControl.tsx b/website/src/components/controls/colors/InheritedColorModifierControl.tsx
similarity index 74%
rename from website/src/components/controls/InheritedColorModifierControl.tsx
rename to website/src/components/controls/colors/InheritedColorModifierControl.tsx
index 8b828bacd9..d030cc661a 100644
--- a/website/src/components/controls/InheritedColorModifierControl.tsx
+++ b/website/src/components/controls/colors/InheritedColorModifierControl.tsx
@@ -1,7 +1,6 @@
import React from 'react'
import styled from 'styled-components'
-import Select from './Select'
-import { TextInput } from './TextInput'
+import { TextInput, Select } from '../ui'
const modifierTypes = ['brighter', 'darker', 'opacity'].map(prop => ({
label: prop,
@@ -9,19 +8,20 @@ const modifierTypes = ['brighter', 'darker', 'opacity'].map(prop => ({
}))
interface InheritedColorModifierControlProps {
- /*
- modifier: PropTypes.array.isRequired,
- onChange: PropTypes.func.isRequired,
- */
+ modifier: any[]
+ onChange: (modifier: any[]) => void
}
-const InheritedColorModifierControl = ({ modifier, onChange }) => {
+export const InheritedColorModifierControl = ({
+ modifier,
+ onChange,
+}: InheritedColorModifierControlProps) => {
return (