Meriadec Pillet
7 years ago
committed by
GitHub
5 changed files with 166 additions and 9 deletions
@ -0,0 +1,105 @@ |
|||
// @flow
|
|||
|
|||
/* eslint-disable react/no-did-mount-set-state */ |
|||
/* eslint-disable react/no-array-index-key */ |
|||
|
|||
import React, { PureComponent, Fragment } from 'react' |
|||
import styled from 'styled-components' |
|||
import { Motion, spring } from 'react-motion' |
|||
|
|||
import Box from 'components/base/Box' |
|||
|
|||
const Container = styled(Box).attrs({ |
|||
horizontal: true, |
|||
relative: true, |
|||
})` |
|||
overflow: hidden; |
|||
white-space: pre; |
|||
` |
|||
|
|||
const TickWrapper = styled(Box).attrs({ |
|||
style: p => ({ |
|||
transform: `translate3d(0, -${p.offset}px, 0)`, |
|||
}), |
|||
})` |
|||
top: 0; |
|||
position: absolute; |
|||
` |
|||
|
|||
const RANGE_NUMBER = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9'] |
|||
const RANGE_COMPONENT = RANGE_NUMBER.map((r, i) => <Box key={i}>{r}</Box>) |
|||
|
|||
type Props = { |
|||
value: string, |
|||
} |
|||
|
|||
type State = { |
|||
height: ?number, |
|||
} |
|||
|
|||
class FlipTicker extends PureComponent<Props, State> { |
|||
state = { |
|||
height: null, |
|||
} |
|||
|
|||
componentDidMount() { |
|||
if (this._node instanceof HTMLDivElement) { |
|||
const { height } = this._node.getBoundingClientRect() |
|||
this.setState({ |
|||
height, |
|||
}) |
|||
} |
|||
} |
|||
|
|||
_node = null |
|||
|
|||
render() { |
|||
const { value, ...p } = this.props |
|||
const { height } = this.state |
|||
return ( |
|||
<Container innerRef={n => (this._node = n)} {...p}> |
|||
{value.split('').map((l, i) => ( |
|||
<Box key={i}> |
|||
{!/[0-9]/.test(l) ? ( |
|||
l === ' ' ? ( |
|||
<span> </span> |
|||
) : ( |
|||
l |
|||
) |
|||
) : ( |
|||
<Fragment> |
|||
<span |
|||
style={{ |
|||
visibility: 'hidden', |
|||
}} |
|||
> |
|||
{l} |
|||
</span> |
|||
{height && <Tick height={height} value={l} />} |
|||
</Fragment> |
|||
)} |
|||
</Box> |
|||
))} |
|||
</Container> |
|||
) |
|||
} |
|||
} |
|||
|
|||
function Tick(props: { height: number, value: string }) { |
|||
const { height, value } = props |
|||
|
|||
const index = RANGE_NUMBER.indexOf(value) |
|||
const offset = height * index |
|||
|
|||
return ( |
|||
<Motion |
|||
style={{ |
|||
offset: spring(offset), |
|||
}} |
|||
> |
|||
{m => <TickWrapper offset={m.offset}>{RANGE_COMPONENT}</TickWrapper>} |
|||
</Motion> |
|||
) |
|||
} |
|||
|
|||
export default FlipTicker |
@ -0,0 +1,59 @@ |
|||
// @flow
|
|||
|
|||
import React, { Component } from 'react' |
|||
|
|||
import { storiesOf } from '@storybook/react' |
|||
import { formatCurrencyUnit, getFiatUnit } from '@ledgerhq/currencies' |
|||
import Chance from 'chance' |
|||
|
|||
import Box from 'components/base/Box' |
|||
|
|||
import FlipTicker from 'components/base/FlipTicker' |
|||
|
|||
const stories = storiesOf('Components/base', module) |
|||
|
|||
const unit = getFiatUnit('USD') |
|||
const chance = new Chance() |
|||
|
|||
function getValue() { |
|||
return formatCurrencyUnit(unit, chance.floating({ min: 1000, max: 100000 }), { |
|||
showCode: true, |
|||
}) |
|||
} |
|||
|
|||
class Wrapper extends Component<any, any> { |
|||
state = { |
|||
value: getValue(), |
|||
} |
|||
|
|||
componentDidMount() { |
|||
this.generateValue() |
|||
} |
|||
|
|||
generateValue = () => |
|||
setTimeout(() => { |
|||
this.setState({ |
|||
value: getValue(), |
|||
}) |
|||
this.generateValue() |
|||
}, 5000) |
|||
|
|||
render() { |
|||
const { render } = this.props |
|||
const { value } = this.state |
|||
return render(value) |
|||
} |
|||
} |
|||
|
|||
stories.add('FlipTicker', () => ( |
|||
<Wrapper |
|||
render={value => ( |
|||
<Box flow={2}> |
|||
<FlipTicker value={value} fontSize={2} /> |
|||
<FlipTicker value={value} /> |
|||
<FlipTicker value={value} fontSize={8} /> |
|||
<Box>{value}</Box> |
|||
</Box> |
|||
)} |
|||
/> |
|||
)) |
Loading…
Reference in new issue