Loëck Vézien
7 years ago
2 changed files with 195 additions and 0 deletions
@ -0,0 +1,150 @@ |
|||
// @flow
|
|||
|
|||
import React, { PureComponent } from 'react' |
|||
import styled from 'styled-components' |
|||
|
|||
import debounce from 'lodash/debounce' |
|||
import noop from 'lodash/noop' |
|||
|
|||
import Box from 'components/base/Box' |
|||
|
|||
const Container = styled(Box).attrs({ |
|||
horizontal: true, |
|||
flow: 1, |
|||
alignItems: 'center', |
|||
justifyContent: 'center', |
|||
fontSize: 4, |
|||
ff: 'Rubik', |
|||
color: 'smoke', |
|||
})` |
|||
background-color: rgba(100, 144, 241, 0.1); |
|||
border-radius: 12px; |
|||
display: inline-flex; |
|||
height: 24px; |
|||
padding: 0 3px; |
|||
` |
|||
|
|||
const Btn = styled(Box).attrs({ |
|||
bg: p => (p.disabled ? 'rgba(100, 144, 241, 0.5)' : 'wallet'), |
|||
color: 'white', |
|||
alignItems: 'center', |
|||
justifyContent: 'center', |
|||
})` |
|||
border-radius: 50%; |
|||
cursor: ${p => (p.disabled ? 'default' : 'pointer')}; |
|||
height: 18px; |
|||
width: 18px; |
|||
` |
|||
|
|||
const Num = styled(Box).attrs({ |
|||
alignItems: 'center', |
|||
justifyContent: 'center', |
|||
})` |
|||
min-width: 20px; |
|||
` |
|||
|
|||
const DELAY_CLICK = 150 |
|||
const DEBOUNCE_ON_CHANGE = 250 |
|||
|
|||
type Props = { |
|||
max: number, |
|||
min: number, |
|||
onChange: Function, |
|||
step: number, |
|||
value: number, |
|||
} |
|||
|
|||
type State = { |
|||
value: number, |
|||
} |
|||
|
|||
class StepperNumber extends PureComponent<Props, State> { |
|||
static defaultProps = { |
|||
max: 10, |
|||
min: 0, |
|||
onChange: noop, |
|||
step: 1, |
|||
value: 0, |
|||
} |
|||
|
|||
state = { |
|||
value: this.props.value, |
|||
} |
|||
|
|||
_timeout = undefined |
|||
|
|||
isMax = (v: number) => v >= this.props.max |
|||
isMin = (v: number) => v <= this.props.min |
|||
|
|||
emitChange = (v: number) => { |
|||
this.setState({ |
|||
value: v, |
|||
}) |
|||
this.debounceOnChange(v) |
|||
} |
|||
|
|||
debounceOnChange = debounce(v => this.props.onChange(v), DEBOUNCE_ON_CHANGE) |
|||
|
|||
decrement = () => { |
|||
const { step, min } = this.props |
|||
const { value } = this.state |
|||
|
|||
const newValue = value - step |
|||
|
|||
if (newValue !== value) { |
|||
const isMin = this.isMin(newValue) |
|||
const v = isMin ? min : newValue |
|||
this.emitChange(v) |
|||
if (!isMin) { |
|||
this._timeout = setTimeout(this.decrement, DELAY_CLICK) |
|||
} |
|||
} |
|||
} |
|||
|
|||
increment = () => { |
|||
const { step, max } = this.props |
|||
const { value } = this.state |
|||
|
|||
const newValue = value + step |
|||
|
|||
if (newValue !== value) { |
|||
const isMax = this.isMax(newValue) |
|||
const v = isMax ? max : newValue |
|||
this.emitChange(v) |
|||
if (!isMax) { |
|||
this._timeout = setTimeout(this.increment, DELAY_CLICK) |
|||
} |
|||
} |
|||
} |
|||
|
|||
handleMouseUp = () => clearTimeout(this._timeout) |
|||
|
|||
render() { |
|||
const { value } = this.state |
|||
|
|||
const isMin = this.isMin(value) |
|||
const isMax = this.isMax(value) |
|||
|
|||
return ( |
|||
<Container> |
|||
<Btn |
|||
onMouseDown={!isMin ? this.decrement : undefined} |
|||
onMouseUp={this.handleMouseUp} |
|||
disabled={isMin} |
|||
> |
|||
- |
|||
</Btn> |
|||
<Num>{value}</Num> |
|||
<Btn |
|||
onMouseDown={!isMax ? this.increment : undefined} |
|||
onMouseUp={this.handleMouseUp} |
|||
disabled={isMax} |
|||
> |
|||
+ |
|||
</Btn> |
|||
</Container> |
|||
) |
|||
} |
|||
} |
|||
|
|||
export default StepperNumber |
@ -0,0 +1,45 @@ |
|||
// @flow
|
|||
|
|||
import React, { Component } from 'react' |
|||
import { storiesOf } from '@storybook/react' |
|||
import { action } from '@storybook/addon-actions' |
|||
import { number } from '@storybook/addon-knobs' |
|||
|
|||
import StepperNumber from 'components/base/StepperNumber' |
|||
|
|||
const stories = storiesOf('Components/base', module) |
|||
|
|||
class Wrapper extends Component<any, any> { |
|||
state = { |
|||
value: 0, |
|||
} |
|||
|
|||
handleChange = value => { |
|||
action('onChange')(value) |
|||
this.setState({ value }) |
|||
} |
|||
|
|||
render() { |
|||
const { render } = this.props |
|||
const { value } = this.state |
|||
|
|||
return render({ |
|||
onChange: this.handleChange, |
|||
value, |
|||
}) |
|||
} |
|||
} |
|||
|
|||
stories.add('StepperNumber', () => ( |
|||
<Wrapper |
|||
render={({ value, onChange }) => ( |
|||
<StepperNumber |
|||
min={number('min', 0)} |
|||
max={number('max', 10)} |
|||
step={number('step', 1)} |
|||
onChange={onChange} |
|||
value={value} |
|||
/> |
|||
)} |
|||
/> |
|||
)) |
Loading…
Reference in new issue