Skip to content

Commit afd7b82

Browse files
committed
Keep working on WheelPicker (incubator)
1 parent 05057ee commit afd7b82

File tree

6 files changed

+212
-97
lines changed

6 files changed

+212
-97
lines changed

demo/src/screens/MenuStructure.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,8 @@ export const navigationData = {
137137
Incubator: {
138138
title: 'Incubator (Experimental)',
139139
screens: [
140-
{title: 'Native TouchableOpacity', tags: 'touchable native', screen: 'unicorn.incubator.TouchableOpacityScreen'}
140+
{title: 'Native TouchableOpacity', tags: 'touchable native', screen: 'unicorn.incubator.TouchableOpacityScreen'},
141+
{title: 'WheelPicker (Incubator)', tags: 'wheel picker spinner experimental', screen: 'unicorn.incubator.WheelPickerScreen'}
141142
]
142143
},
143144
Inspirations: {
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import React from 'react';
2+
import {View, Text, Incubator} from 'react-native-ui-lib';
3+
import _ from 'lodash';
4+
5+
const items = [
6+
'January',
7+
'February',
8+
'March',
9+
'April',
10+
'May',
11+
'June',
12+
'July',
13+
'August',
14+
'September',
15+
'October',
16+
'November',
17+
'December'
18+
];
19+
20+
export default () => {
21+
return (
22+
<View flex padding-page>
23+
<Text h1>Wheel Picker</Text>
24+
<View flex centerH paddingT-page>
25+
<Incubator.WheelPicker
26+
items={_.map(items, (i) => ({text: i, value: i}))}
27+
/>
28+
</View>
29+
</View>
30+
);
31+
};

demo/src/screens/incubatorScreens/index.js

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,9 @@ import {gestureHandlerRootHOC} from 'react-native-gesture-handler';
22

33
export function registerScreens(registrar) {
44
registrar('unicorn.incubator.TouchableOpacityScreen', () =>
5-
gestureHandlerRootHOC(require('./TouchableOpacityScreen').default));
5+
gestureHandlerRootHOC(require('./TouchableOpacityScreen').default)
6+
);
7+
registrar('unicorn.incubator.WheelPickerScreen', () =>
8+
gestureHandlerRootHOC(require('./WheelPickerScreen').default)
9+
);
610
}

src/incubator/WheelPicker/Item.tsx

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
import React, {useCallback, useMemo} from 'react';
2+
import Animated, {concat} from 'react-native-reanimated';
3+
import {transformOrigin} from 'react-native-redash';
4+
import Text from '../../components/text';
5+
import TouchableOpacity from '../../components/touchableOpacity';
6+
import {TextStyle} from 'react-native';
7+
8+
const AnimatedTouchableOpacity = Animated.createAnimatedComponent(
9+
TouchableOpacity
10+
);
11+
const AnimatedText = Animated.createAnimatedComponent(Text);
12+
13+
export interface ItemProps {
14+
text: string;
15+
value: string | number;
16+
onSelect: (index: number) => void;
17+
}
18+
19+
interface InternalProps extends ItemProps {
20+
index: number;
21+
offset: any;
22+
itemHeight: number;
23+
textStyle?: TextStyle;
24+
}
25+
26+
export default ({
27+
index,
28+
text,
29+
textStyle,
30+
value,
31+
itemHeight,
32+
onSelect,
33+
offset
34+
}: InternalProps) => {
35+
const itemOffset = index * itemHeight;
36+
const opacity = Animated.interpolate(offset, {
37+
inputRange: [itemOffset - itemHeight, itemOffset, itemOffset + itemHeight],
38+
outputRange: [0.5, 1, 0.5]
39+
});
40+
41+
const rotateXValue = useMemo(() => {
42+
return Animated.interpolate(offset, {
43+
inputRange: [
44+
itemOffset - 2.5 * itemHeight,
45+
itemOffset,
46+
itemOffset + 2.5 * itemHeight
47+
],
48+
outputRange: [-85, 0, -85]
49+
});
50+
}, [itemHeight]);
51+
52+
const rotateX = concat(rotateXValue, 'deg');
53+
54+
const scale = useMemo(() => {
55+
return Animated.interpolate(offset, {
56+
inputRange: [
57+
itemOffset - 2 * itemHeight,
58+
itemOffset,
59+
itemOffset + 2 * itemHeight
60+
],
61+
outputRange: [0.8, 1, 0.8]
62+
});
63+
}, [itemHeight]);
64+
65+
const selectItem = useCallback(() => onSelect(index), [index]);
66+
67+
return (
68+
<AnimatedTouchableOpacity
69+
activeOpacity={1}
70+
style={{
71+
height: itemHeight,
72+
opacity,
73+
transform: transformOrigin({x: 125, y: 24}, {rotateX})
74+
}}
75+
key={index}
76+
center
77+
onPress={selectItem}
78+
index={index}
79+
>
80+
<AnimatedText text60 style={[textStyle, {transform: [{scale}]}]}>
81+
{text}
82+
</AnimatedText>
83+
</AnimatedTouchableOpacity>
84+
);
85+
};

src/incubator/WheelPicker/index.js

Lines changed: 0 additions & 95 deletions
This file was deleted.

src/incubator/WheelPicker/index.tsx

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
// TODO: Support onChange callback
2+
// TODO: Support style customization
3+
// TODO: Support control of visible items
4+
import _ from 'lodash';
5+
import React, {useCallback, useRef} from 'react';
6+
import {TextStyle} from 'react-native';
7+
import Animated from 'react-native-reanimated';
8+
import {onScrollEvent, useValues} from 'react-native-redash';
9+
10+
import View from '../../components/view';
11+
import {Constants} from '../../helpers';
12+
13+
import Item, {ItemProps} from './Item';
14+
15+
export interface WheelPickerProps {
16+
items?: ItemProps[];
17+
itemHeight?: number;
18+
itemTextStyle?: TextStyle;
19+
onChange: (item: ItemProps, index: number) => void;
20+
}
21+
22+
const WheelPicker = ({
23+
items,
24+
itemHeight = 48,
25+
itemTextStyle
26+
}: WheelPickerProps) => {
27+
const height = itemHeight * 4;
28+
const scrollview = useRef();
29+
const [offset] = useValues([0], []);
30+
const onScroll = onScrollEvent({y: offset});
31+
32+
const selectItem = useCallback(
33+
(index) => {
34+
scrollview.current
35+
.getNode()
36+
.scrollTo({y: index * itemHeight, animated: true});
37+
},
38+
[itemHeight]
39+
);
40+
41+
const onChange = useCallback(() => {
42+
// TODO: need to implement on change event that calc the current selected index
43+
44+
}, [itemHeight]);
45+
46+
return (
47+
<View>
48+
<View width={250} height={height} br20>
49+
<Animated.ScrollView
50+
scrollEventThrottle={16}
51+
onScroll={onScroll}
52+
onMomentumScrollEnd={onChange}
53+
showsVerticalScrollIndicator={false}
54+
ref={scrollview}
55+
contentContainerStyle={{
56+
paddingVertical: height / 2 - itemHeight / 2
57+
}}
58+
snapToInterval={itemHeight}
59+
decelerationRate={Constants.isAndroid ? 0.98 : 'normal'}
60+
>
61+
{_.map(items, (item, index) => {
62+
return (
63+
<Item
64+
key={item.value}
65+
index={index}
66+
itemHeight={itemHeight}
67+
onSelect={selectItem}
68+
offset={offset}
69+
textStyle={itemTextStyle}
70+
{...item}
71+
/>
72+
);
73+
})}
74+
</Animated.ScrollView>
75+
<View absF centerV pointerEvents="none">
76+
<View
77+
style={{
78+
borderTopWidth: 1,
79+
borderBottomWidth: 1,
80+
height: itemHeight
81+
}}
82+
/>
83+
</View>
84+
</View>
85+
</View>
86+
);
87+
};
88+
89+
export default WheelPicker;

0 commit comments

Comments
 (0)