// @flow // https://twitter.com/jevakallio/status/941258932529614848 import React, { Component, type Node } from 'react'; import styled from 'styled-components/native'; import Touchable from 'react-native-platform-touchable'; import Carousel from 'react-native-snap-carousel'; // $FlowFixMe import { LinearGradient } from 'expo'; const Container = styled.View``; // react-native-snap-carousel doesn't support setting different opacities // for inactive items based on their distance from the active item, so we'll // fake it here by fading the view in and out with gradients const StartGradient = styled(LinearGradient).attrs({ pointerEvents: 'none', colors: ['rgba(242, 242, 242, 0.6)', 'rgba(242, 242, 242, 0.6)', 'rgba(242, 242, 242, 0)'], start: [0, 0.8, 1], end: [1, 0.8, 1] })` width: ${p => p.width + 10}px; height: ${p => p.width}px; position: absolute; top: 0; left: 0; `; const EndGradient = styled(LinearGradient).attrs({ pointerEvents: 'none', colors: ['rgba(242, 242, 242, 0)', 'rgba(242, 242, 242, 0.6)', 'rgba(242, 242, 242, 0.6)'], start: [0, 0.8, 1], end: [1, 0.8, 1] })` width: ${p => p.width + 10}px; height: ${p => p.width}px; position: absolute; top: 0; right: 0; `; // show a ring around the focused item const FocusCircle = styled.View.attrs({ pointerEvents: 'none' })` position: absolute; top: 0; left: ${p => p.width * p.offset}; bottom: 0; width: ${p => p.width}; height: ${p => p.width}; border-radius: ${p => p.width}; border-color: white; border-width: 2; `; // when an item is touched, we should snap to it const ItemWrapper = styled(Touchable).attrs({ activeOpacity: 0.8, background: Touchable.SelectableBackgroundBorderless() })` background-color: transparent; width: ${p => p.width}px; height: ${p => p.width}px; align-items: center; justify-content: center; `; type Props = { onItemSelected: (item: T) => void, renderItem: (item: T) => Node, items: T[], initialItem: T, itemWidth: number, visibleItemCount: number }; export default class HorizontalPicker extends Component, *> { carouselRef = null; props: Props<*>; static defaultProps = { itemWidth: 60, visibleItemCount: 5 }; moveToItem = (index: number) => { if (this.carouselRef) { this.carouselRef.snapToItem(index); } }; onSnapToItem = (index: number) => { this.props.onItemSelected(this.props.items[index]); }; renderItem = ({ item, index }: { item: *, index: number }) => { return ( this.moveToItem(index)}> {this.props.renderItem(item)} ); }; render() { const { items, initialItem, itemWidth, visibleItemCount } = this.props; return ( { this.carouselRef = ref; }} data={items} firstItem={items.indexOf(initialItem)} renderItem={this.renderItem} onSnapToItem={this.onSnapToItem} sliderWidth={visibleItemCount * itemWidth} itemWidth={itemWidth} activeSlideOffset={10} inactiveSlideScale={0.8} inactiveSlideShift={-16} /> ); } }