Skip to content

Commit ebb690c

Browse files
authored
Feat/SectionsWheelPicker new component (#1260)
* Feat/SectionsWheelPicker new component * update prop name * fix some props passings * pass testID to the incubator wheelPicker * add testID to the incubator wheelPicker item * fix wheelPicker width * fix style * reorder imports
1 parent 84e0d0c commit ebb690c

File tree

16 files changed

+364
-134
lines changed

16 files changed

+364
-134
lines changed

demo/src/index.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,9 @@ module.exports = {
9494
get RadioButtonScreen() {
9595
return require('./screens/componentScreens/RadioButtonScreen').default;
9696
},
97+
get SectionsWheelPickerScreen() {
98+
return require('./screens/componentScreens/SectionsWheelPickerScreen').default;
99+
},
97100
get SegmentedControlScreen() {
98101
return require('./screens/componentScreens/SegmentedControlScreen').default;
99102
},

demo/src/screens/MenuStructure.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ export const navigationData = {
5656
{title: 'Picker', tags: 'picker form', screen: 'unicorn.components.PickerScreen'},
5757
{title: 'DateTimePicker', tags: 'date time picker form', screen: 'unicorn.components.DateTimePickerScreen'},
5858
{title: 'RadioButton', tags: 'radio button group controls', screen: 'unicorn.components.RadioButtonScreen'},
59+
{title: 'SectionsWheelPicker', tags: 'sections wheel picker form', screen: 'unicorn.components.SectionsWheelPickerScreen'},
5960
{title: 'SegmentedControl', tags: 'segmented control switch toggle', screen: 'unicorn.components.SegmentedControlScreen'},
6061
{title: 'Stepper', tags: 'stepper form', screen: 'unicorn.components.StepperScreen'},
6162
{title: 'Slider', tags: 'slider', screen: 'unicorn.components.SliderScreen'},
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
import _ from 'lodash';
2+
import React, {useState, useCallback} from 'react';
3+
import {Alert} from 'react-native';
4+
import {Text, View, SectionsWheelPicker, SegmentedControl, Button, Incubator} from 'react-native-ui-lib';
5+
6+
const SectionsWheelPickerScreen = () => {
7+
const [showMinutes, setShowMinutes] = useState(false);
8+
9+
const [selectedDays, setSelectedDays] = useState(0);
10+
const [selectedHours, setSelectedHours] = useState(0);
11+
const [selectedMinutes, setSelectedMinutes] = useState(0);
12+
13+
const days = _.times(10, i => i);
14+
const hours = _.times(24, i => i);
15+
const minutes = _.times(60, i => i);
16+
17+
const getItems = useCallback(values => {
18+
return _.map(values, item => ({label: '' + item, value: item}));
19+
}, []);
20+
21+
const onDaysChange = (item: number | string) => {
22+
setSelectedDays(item as number);
23+
};
24+
25+
const onHoursChange = (item: number | string) => {
26+
setSelectedHours(item as number);
27+
};
28+
29+
const onMinutesChange = (item: number | string) => {
30+
setSelectedMinutes(item as number);
31+
};
32+
33+
const onSavePress = () => {
34+
const days = selectedDays === 1 ? 'day' : 'days';
35+
const hours = selectedHours === 1 ? 'hour' : 'hours';
36+
const minutes = selectedMinutes === 1 ? 'minute' : 'minutes';
37+
38+
showMinutes
39+
? Alert.alert('Your chosen duration is:\n' +
40+
selectedDays +
41+
' ' +
42+
days +
43+
', ' +
44+
selectedHours +
45+
' ' +
46+
hours +
47+
' and ' +
48+
selectedMinutes +
49+
' ' +
50+
minutes)
51+
: Alert.alert('Your chosen duration is:\n' + selectedDays + ' ' + days + ' and ' + selectedHours + ' ' + hours);
52+
};
53+
54+
const onResetPress = () => {
55+
setSelectedDays(0);
56+
setSelectedHours(0);
57+
setSelectedMinutes(0);
58+
};
59+
60+
const sections: Incubator.WheelPickerProps[] = [
61+
{items: getItems(days), onChange: onDaysChange, selectedValue: selectedDays, label: 'Days'},
62+
{items: getItems(hours), onChange: onHoursChange, selectedValue: selectedHours, label: 'Hrs'},
63+
{
64+
items: getItems(minutes),
65+
onChange: onMinutesChange,
66+
selectedValue: selectedMinutes,
67+
label: 'Mins'
68+
}
69+
];
70+
71+
const sectionsToPresent = showMinutes ? sections : _.slice(sections, 0, 2);
72+
73+
const onChangeIndex = (index: number) => {
74+
return index === 0 ? setShowMinutes(false) : setShowMinutes(true);
75+
};
76+
77+
return (
78+
<View>
79+
<Text text40 marginL-10 marginT-20>
80+
Sections Wheel Picker
81+
</Text>
82+
<View centerH marginT-40>
83+
<SegmentedControl segments={[{label: '2 sections'}, {label: '3 sections'}]} onChangeIndex={onChangeIndex}/>
84+
<Text text50 marginV-20>
85+
Pick a duration
86+
</Text>
87+
</View>
88+
<SectionsWheelPicker sections={sectionsToPresent}/>
89+
<Button marginH-150 marginT-300 label={'Save'} onPress={onSavePress}/>
90+
<Button marginH-150 marginT-15 label={'Reset'} onPress={onResetPress}/>
91+
</View>
92+
);
93+
};
94+
95+
export default SectionsWheelPickerScreen;

demo/src/screens/componentScreens/index.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ export function registerScreens(registrar) {
3333
registrar('unicorn.animations.ProgressBarScreen', () => require('../componentScreens/ProgressBarScreen').default);
3434
registrar('unicorn.components.RadioButtonScreen', () => require('./RadioButtonScreen').default);
3535
registrar('unicorn.components.ScrollBarScreen', () => require('./ScrollBarScreen').default);
36+
registrar('unicorn.components.SectionsWheelPickerScreen', () => require('./SectionsWheelPickerScreen').default);
3637
registrar('unicorn.components.SegmentedControlScreen', () => require('./SegmentedControlScreen').default);
3738
registrar('unicorn.components.SharedTransitionScreen', () => require('./SharedTransitionScreen').default);
3839
registrar('unicorn.components.StepperScreen', () => require('./StepperScreen').default);

demo/src/screens/incubatorScreens/WheelPickerScreen.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,7 @@ export default () => {
124124
<Incubator.WheelPicker
125125
onChange={onDaysChange}
126126
selectedValue={selectedDays}
127-
label={selectedDays === 1 ? 'Day' : 'Days'}
127+
label={'Days'}
128128
items={getDays()}
129129
/>
130130
</Dialog>
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
import React from 'react';
2+
import { TextStyle } from 'react-native';
3+
import { WheelPickerProps } from '../../incubator';
4+
export declare type SectionsWheelPickerProps = {
5+
/**
6+
* Array of sections.
7+
*/
8+
sections?: WheelPickerProps[];
9+
/**
10+
* Describe the height of each item in the WheelPicker
11+
* default value: 44
12+
*/
13+
itemHeight?: number;
14+
/**
15+
* Describe the number of rows visible
16+
* default value: 5
17+
*/
18+
numberOfVisibleRows?: number;
19+
/**
20+
* Text color for the focused row
21+
*/
22+
activeTextColor?: string;
23+
/**
24+
* Text color for other, non-focused rows
25+
*/
26+
inactiveTextColor?: string;
27+
/**
28+
* Row text style
29+
*/
30+
textStyle?: TextStyle;
31+
testID?: string;
32+
};
33+
declare const _default: React.ComponentClass<SectionsWheelPickerProps & {
34+
useCustomTheme?: boolean | undefined;
35+
}, any>;
36+
export default _default;

generatedTypes/incubator/WheelPicker/Item.d.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ interface InternalProps extends ItemProps {
1212
inactiveColor?: string;
1313
style?: TextStyle;
1414
onSelect: (index: number) => void;
15+
testID?: string;
16+
centerH?: boolean;
1517
}
16-
declare const _default: ({ index, label, itemHeight, onSelect, offset, activeColor, inactiveColor, style }: InternalProps) => JSX.Element;
18+
declare const _default: ({ index, label, itemHeight, onSelect, offset, activeColor, inactiveColor, style, testID, centerH }: InternalProps) => JSX.Element;
1719
export default _default;

generatedTypes/incubator/WheelPicker/index.d.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ export interface WheelPickerProps {
5757
* WheelPicker initial value, can be ItemProps.value, number as index
5858
*/
5959
selectedValue: ItemProps | number | string;
60+
testID?: string;
6061
}
61-
declare const WheelPicker: React.MemoExoticComponent<({ items: propItems, itemHeight, numberOfVisibleRows, activeTextColor, inactiveTextColor, textStyle, label, labelStyle, labelProps, onChange, style, children, selectedValue }: WheelPickerProps) => JSX.Element>;
62+
declare const WheelPicker: React.MemoExoticComponent<({ items: propItems, itemHeight, numberOfVisibleRows, activeTextColor, inactiveTextColor, textStyle, label, labelStyle, labelProps, onChange, style, children, selectedValue, testID }: WheelPickerProps) => JSX.Element>;
6263
export default WheelPicker;

generatedTypes/incubator/index.d.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
export { default as TabController } from './TabController';
22
export { default as TextField } from './TextField';
33
export { default as TouchableOpacity, TouchableOpacityProps } from './TouchableOpacity';
4-
export { default as WheelPicker } from './WheelPicker';
4+
export { default as WheelPicker, WheelPickerProps } from './WheelPicker';

generatedTypes/index.d.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ export {default as Image, ImageProps} from './components/image';
4040
export {default as Overlay, OverlayTypes} from './components/overlay';
4141
export {default as RadioButton, RadioButtonPropTypes, RadioButtonProps} from './components/radioButton/RadioButton';
4242
export {default as RadioGroup, RadioGroupPropTypes, RadioGroupProps} from './components/radioButton/RadioGroup';
43+
export {default as SectionsWheelPicker, SectionsWheelPickerProps} from './components/sectionsWheelPicker';
4344
export {default as HapticService, HapticType} from './services/HapticService';
4445
export {default as SegmentedControl, SegmentedControlProps, SegmentedControlItemProps} from './components/segmentedControl';
4546
export {default as Switch, SwitchProps} from './components/switch';
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
import _ from 'lodash';
2+
import React from 'react';
3+
import {TextStyle} from 'react-native';
4+
import {asBaseComponent} from '../../commons/new';
5+
import View from '../view';
6+
import {WheelPicker, WheelPickerProps} from '../../incubator';
7+
8+
export type SectionsWheelPickerProps = {
9+
/**
10+
* Array of sections.
11+
*/
12+
sections?: WheelPickerProps[];
13+
/**
14+
* Describe the height of each item in the WheelPicker
15+
* default value: 44
16+
*/
17+
itemHeight?: number;
18+
/**
19+
* Describe the number of rows visible
20+
* default value: 5
21+
*/
22+
numberOfVisibleRows?: number;
23+
/**
24+
* Text color for the focused row
25+
*/
26+
activeTextColor?: string;
27+
/**
28+
* Text color for other, non-focused rows
29+
*/
30+
inactiveTextColor?: string;
31+
/**
32+
* Row text style
33+
*/
34+
textStyle?: TextStyle;
35+
testID?: string;
36+
};
37+
38+
/**
39+
* @description: SectionsWheelPicker component for presenting set of wheelPickers
40+
* @example: https://github.com/wix/react-native-ui-lib/blob/master/demo/src/screens/componentScreens/SectionsWheelPickerScreen.tsx
41+
*/
42+
43+
const SectionsWheelPicker = (props: SectionsWheelPickerProps) => {
44+
const {sections, itemHeight, numberOfVisibleRows, activeTextColor, inactiveTextColor, textStyle, testID} = props;
45+
46+
const wheelPickerProps = {
47+
itemHeight,
48+
numberOfVisibleRows,
49+
activeTextColor,
50+
inactiveTextColor,
51+
textStyle
52+
};
53+
54+
const renderSections = () =>
55+
_.map(sections, (section, index) => {
56+
return (
57+
<View height={1} key={index} testID={testID}>
58+
<WheelPicker testID={`${testID}.${index}`} {...wheelPickerProps} {...section}/>
59+
</View>
60+
);
61+
});
62+
63+
return (
64+
<View flex row centerH>
65+
{renderSections()}
66+
</View>
67+
);
68+
};
69+
70+
SectionsWheelPicker.displayName = 'SectionsWheelPicker';
71+
72+
export default asBaseComponent<SectionsWheelPickerProps>(SectionsWheelPicker);

src/incubator/WheelPicker/Item.tsx

Lines changed: 25 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,11 @@
11
import React, {useCallback, useMemo} from 'react';
2+
import {TextStyle, StyleSheet} from 'react-native';
23
import Animated, {interpolateColors} from 'react-native-reanimated';
34
import Text from '../../components/text';
45
import TouchableOpacity from '../../components/touchableOpacity';
5-
import {TextStyle} from 'react-native';
6-
import {Colors} from '../../../src/style';
6+
import {Colors, Spacings} from '../../../src/style';
77

8-
const AnimatedTouchableOpacity = Animated.createAnimatedComponent(
9-
TouchableOpacity
10-
);
8+
const AnimatedTouchableOpacity = Animated.createAnimatedComponent(TouchableOpacity);
119
const AnimatedText = Animated.createAnimatedComponent(Text);
1210

1311
export interface ItemProps {
@@ -23,6 +21,8 @@ interface InternalProps extends ItemProps {
2321
inactiveColor?: string;
2422
style?: TextStyle;
2523
onSelect: (index: number) => void;
24+
testID?: string;
25+
centerH?: boolean;
2626
}
2727

2828
export default ({
@@ -33,34 +33,36 @@ export default ({
3333
offset,
3434
activeColor = Colors.primary,
3535
inactiveColor = Colors.grey20,
36-
style
36+
style,
37+
testID,
38+
centerH = true
3739
}: InternalProps) => {
38-
3940
const selectItem = useCallback(() => onSelect(index), [index]);
4041
const itemOffset = index * itemHeight;
4142

4243
const color = useMemo(() => {
4344
return interpolateColors(offset, {
44-
inputRange: [
45-
itemOffset - itemHeight,
46-
itemOffset,
47-
itemOffset + itemHeight
48-
],
45+
inputRange: [itemOffset - itemHeight, itemOffset, itemOffset + itemHeight],
4946
outputColorRange: [inactiveColor, activeColor, inactiveColor]
5047
});
5148
}, [itemHeight]);
52-
49+
50+
const containerStyle = useMemo(() => {
51+
return [{height: itemHeight}, styles.container];
52+
}, [itemHeight]);
53+
5354
return (
5455
<AnimatedTouchableOpacity
5556
activeOpacity={1}
56-
style={{
57-
height: itemHeight
58-
}}
57+
style={containerStyle}
5958
key={index}
60-
center
59+
centerV
60+
centerH={centerH}
61+
right={!centerH}
6162
onPress={selectItem}
6263
// @ts-ignore reanimated2
6364
index={index}
65+
testID={testID}
6466
>
6567
{/* @ts-ignore reanimated2*/}
6668
<AnimatedText text60R style={{color, ...style}}>
@@ -69,3 +71,9 @@ export default ({
6971
</AnimatedTouchableOpacity>
7072
);
7173
};
74+
75+
const styles = StyleSheet.create({
76+
container: {
77+
minWidth: Spacings.s10
78+
}
79+
});

0 commit comments

Comments
 (0)