react-circular-input
A declarative and composable approach means we have a lot of flexibility, here are a few examples that showcase it.
Or play around with the examples at CodeSandbox
Default
An example of using the 3 components included with their default styling.
const [value, setValue] = useState(0.25) return ( <CircularInput value={value} onChange={setValue}> <CircularTrack /> <CircularProgress /> <CircularThumb /> </CircularInput> )
Animated
You can use libraries like react-spring to add animation.
const [value, setValue] = useState(0.25) return ( <Spring to={{ value }}> {props => ( <CircularInput value={props.value} onChange={setValue}> <CircularTrack /> <CircularProgress /> <CircularThumb /> </CircularInput> )} </Spring> )
Styled
The recommended way to style the components is to use CSS classes or CSS-in-JS as you normally do for other components 🙂
CircularTrack
, CircularProgress
and CircularThumb
are just SVG <circle />
so you can also just tweak most (see Component docs) attributes 💅
const [value, setValue] = useState(0.25) return ( <StyledCircularInput value={value} onChange={setValue}> {/* CSS-in-JS */} <StyledCircularTrack /> {/* CSS class */} <CircularProgress className="custom-progress" /> {/* SVG style prop */} <CircularThumb r={10} fill="rgba(255,255,255,0.5)" /> </StyledCircularInput> )
Custom Component
Using the provided Hooks you can create your own components! 🤩
const CustomComponent = () => { const { getPointFromValue, value } = useCircularInputContext() const { x, y } = getPointFromValue() return ( <text x={x} y={y} style={{ pointerEvents: 'none' }}> {Math.round(value * 100)} </text> ) } const CustomComponentExample = () => { const [value, setValue] = useState(0.25) return ( <CircularInput value={value} onChange={setValue}> <CircularProgress /> <CircularThumb /> {/* Add any component and use the provided hooks! */} <CustomComponent /> </CircularInput> ) }
Progress
Can be used to simply display progress/gauge.
<CircularInput value={Math.random()}> <CircularTrack strokeWidth={5} stroke="#eee" /> <CircularProgress stroke={`hsl(${props.value * 100}, 100%, 50%)`} /> </CircularInput>
Min, Max & Scale
There are no props for min/max/etc as you can easily achieve these and much more with simple code.
const [value, setValue] = useState(0.25) // custom limits const min = 0.25 const max = 0.75 // get value within limits const valueWithinLimits = v => Math.min(Math.max(v, min), max) // custom range const range = [0, 100] // scaled range value const rangeValue = value * (range[1] - range[0]) + range[0] return ( <CircularInput value={valueWithinLimits(value)} onChange={v => setValue(valueWithinLimits(v))} > <CircularTrack /> <CircularProgress /> {/* limit lines */} <line x1={-10} x2={10} y1={100} y2={100} stroke="black" /> <line x1={190} x2={210} y1={100} y2={100} stroke="black" /> <CircularThumb /> {/* range value in center */} <text x={100} y={100} textAnchor="middle" dy="0.3em" fontWeight="bold"> {Math.round(rangeValue)}% </text> </CircularInput> )
Steps
You're in full control of the value so it's easy to introduce the stepped interaction.
const [value, setValue] = useState(0.25) const stepValue = v => Math.round(v * 10) / 10 return ( <CircularInput value={stepValue(value)} onChange={v => setValue(stepValue(v))} > <CircularTrack /> <CircularProgress /> <CircularThumb /> <text x={100} y={100} textAnchor="middle" dy="0.3em" fontWeight="bold"> {Math.round(stepValue(value) * 100)}% </text> </CircularInput> )
Readonly
Omitting the onChange
prop makes it readonly.
<CircularInput value={0.25}> <CircularTrack /> <CircularProgress /> </CircularInput>
Render Prop
Use children
prop as a function to receive the current context.
const [value, setValue] = useState(0.25) return ( <CircularInput value={value} onChange={setValue}> {({ getPointFromValue }) => ( <> <CircularTrack /> <CircularProgress /> <text {...getPointFromValue()} dx="30" dy="0.3em"> wee! </text> <CircularThumb /> </> )} </CircularInput> )
It might be a good time to dive deep into customisation with components and hooks: