Skip to content

Commit

Permalink
feat(website): add the ability to control react-spring config
Browse files Browse the repository at this point in the history
  • Loading branch information
plouc committed Jun 17, 2020
1 parent 7055d3d commit 457ebfa
Show file tree
Hide file tree
Showing 5 changed files with 217 additions and 4 deletions.
8 changes: 5 additions & 3 deletions packages/funnel/src/PartLabel.js
Expand Up @@ -8,16 +8,18 @@
*/
import React from 'react'
import PropTypes from 'prop-types'
import { config, useSpring, animated } from 'react-spring'
import { useTheme } from '@nivo/core'
import { useSpring, animated } from 'react-spring'
import { useTheme, useMotionConfig } from '@nivo/core'

export const PartLabel = ({ part }) => {
const theme = useTheme()
const { animate, config: motionConfig } = useMotionConfig()

const animatedProps = useSpring({
transform: `translate(${part.x}, ${part.y})`,
color: part.labelColor,
config: config.wobbly,
config: motionConfig,
immediate: !animate,
})

return (
Expand Down
14 changes: 14 additions & 0 deletions website/src/components/controls/ControlsGroup.js
Expand Up @@ -25,6 +25,7 @@ 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'
Expand Down Expand Up @@ -242,6 +243,19 @@ const ControlSwitcher = memo(
/>
)

case 'motionConfig':
return (
<MotionConfigControl
id={id}
property={property}
flavors={flavors}
currentFlavor={currentFlavor}
options={options}
value={value}
onChange={handleChange}
/>
)

case 'opacity':
return (
<OpacityControl
Expand Down
186 changes: 186 additions & 0 deletions website/src/components/controls/MotionConfigControl.js
@@ -0,0 +1,186 @@
/*
* This file is part of the nivo project.
*
* (c) 2016 Raphaël Benitte
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
import React, { memo, useCallback, useState } from 'react'
import { config as springConfig } from 'react-spring'
import { isString } from 'lodash'
import styled from 'styled-components'
import Control from './Control'
import PropertyHeader from './PropertyHeader'
import TextInput from './TextInput'
import Radio from './Radio'
import Select from './Select'
import Switch from './Switch'
import { Help } from './styled'

const Row = styled.div`
display: grid;
grid-template-columns: 1fr;
grid-row-gap: 9px;
`

const CustomControls = styled.div`
display: grid;
grid-template-columns: 50px 25px auto 50px 25px auto;
align-items: center;
column-gap: 9px;
row-gap: 9px;
margin-bottom: 9px;
`

const presetOptions = Object.keys(springConfig).map(presetId => ({
value: presetId,
label: presetId,
}))

const defaultConfig = {
mass: 1,
tension: 170,
friction: 26,
clamp: false,
precision: 0.01,
velocity: 0,
}

const MotionConfigControl = memo(({ id, property, flavors, currentFlavor, value, onChange }) => {
const type = isString(value) ? 'preset' : 'custom'
const [preset, setPreset] = useState(type === 'preset' ? value : 'default')
const [customConfig, setCustomConfig] = useState(type === 'custom' ? value : defaultConfig)

const handleTypeChange = useCallback(
event => {
const newType = event.target.value
if (newType === 'preset') {
onChange(preset)
} else {
onChange(customConfig)
}
},
[onChange]
)

const handlePresetChange = useCallback(
option => {
setPreset(option.value)
onChange(option.value)
},
[onChange]
)

const handleMassChange = event => {
const mass = Number(event.target.value)
const newCustomConfig = {
...customConfig,
mass,
}
setCustomConfig(newCustomConfig)
onChange(newCustomConfig)
}

const handleTensionChange = event => {
const tension = Number(event.target.value)
const newCustomConfig = {
...customConfig,
tension,
}
setCustomConfig(newCustomConfig)
onChange(newCustomConfig)
}

const handleFrictionChange = event => {
const friction = Number(event.target.value)
const newCustomConfig = {
...customConfig,
friction,
}
setCustomConfig(newCustomConfig)
onChange(newCustomConfig)
}

const handleClampChange = clamp => {
const newCustomConfig = {
...customConfig,
clamp,
}
setCustomConfig(newCustomConfig)
onChange(newCustomConfig)
}

return (
<Control
id={id}
description={property.description}
flavors={flavors}
currentFlavor={currentFlavor}
supportedFlavors={property.flavors}
>
<PropertyHeader id={id} {...property} />
<Row>
<Radio
options={[
{ value: 'preset', label: 'preset' },
{ value: 'custom', label: 'custom' },
]}
value={type}
onChange={handleTypeChange}
/>
{type === 'preset' && (
<Select
options={presetOptions}
value={presetOptions.find(option => option.value === value)}
onChange={handlePresetChange}
/>
)}
{type === 'custom' && (
<CustomControls>
<label>mass</label>
<code className="code-number">{value.mass}</code>
<input
type="range"
value={value.mass}
onChange={handleMassChange}
min={1}
max={500}
/>

<label>tension</label>
<code className="code-number">{value.tension}</code>
<input
type="range"
value={value.tension}
onChange={handleTensionChange}
min={1}
max={500}
/>

<label>friction</label>
<code className="code-number">{value.friction}</code>
<input
type="range"
value={value.friction}
onChange={handleFrictionChange}
min={1}
max={500}
/>

<Switch
value={value.clamp}
id={`${id}clamp`}
onChange={handleClampChange}
/>
<span />
<label>clamp</label>
</CustomControls>
)}
</Row>
<Help>{property.help}</Help>
</Control>
)
})

export default MotionConfigControl
2 changes: 1 addition & 1 deletion website/src/components/controls/Radio.js
Expand Up @@ -63,7 +63,7 @@ const Radio = memo(({ options, value, onChange }) => {
)
})

Radio.displayName = 'RadioControl'
Radio.displayName = 'Radio'
Radio.propTypes = {
value: PropTypes.string.isRequired,
options: PropTypes.arrayOf(
Expand Down
11 changes: 11 additions & 0 deletions website/src/lib/componentProperties.js
Expand Up @@ -89,6 +89,17 @@ export const motionProperties = (flavors, defaults, type = 'react-motion') => {
max: 40,
},
})
} else if (type === 'react-spring') {
props.push({
key: 'motionConfig',
flavors,
help: 'Motion config for react-spring, either a preset or a custom configuration.',
type: 'string | object',
required: false,
defaultValue: defaults.motionConfig,
controlType: 'motionConfig',
group: 'Motion',
})
}

return props
Expand Down

0 comments on commit 457ebfa

Please sign in to comment.