Skip to content

Commit 1e1309a

Browse files
authored
Add support to initialValue in WheelPicker (#1436)
* Add support to initialValue in WheelPicker * Fix typings stuff in WheelPicker screen * Remove reset button in example scren * Remove redundant type conversion
1 parent 42b1d11 commit 1e1309a

File tree

6 files changed

+70
-71
lines changed

6 files changed

+70
-71
lines changed

demo/src/configurations.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,8 @@ Typography.loadTypographies({
2222
h1: {...Typography.text40},
2323
h2: {...Typography.text50},
2424
h3: {...Typography.text60},
25-
body: Typography.text70
25+
body: Typography.text70,
26+
bodySmall: Typography.text80
2627
});
2728

2829
Spacings.loadSpacings({

demo/src/screens/incubatorScreens/WheelPickerScreen.tsx

Lines changed: 24 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
1-
import React, {useCallback, useMemo, useState} from 'react';
1+
import React, {useCallback, useState} from 'react';
22
import {View, Text, Incubator, Colors, Typography, Button, Dialog} from 'react-native-ui-lib';
33
import _ from 'lodash';
44

5-
// Months
6-
const months = [
5+
type WheelPickerValue = Incubator.WheelPickerProps['initialValue'];
6+
7+
const monthItems = _.map([
78
'January',
89
'February',
910
'March',
@@ -16,17 +17,18 @@ const months = [
1617
'October',
1718
'November',
1819
'December'
19-
];
20-
21-
// Years
22-
const years = _.times(2020, i => i);
20+
],
21+
item => ({label: item, value: item}));
2322

24-
const days = _.times(365, i => i + 1);
23+
const yearItems = _.times(2030, i => i)
24+
.reverse()
25+
.map(item => ({label: `${item}`, value: item}));
26+
const dayItems = _.times(31, i => i + 1).map(day => ({label: `${day}`, value: day}));
2527

2628
const useData = (initialMonth?: string, initialYear?: string, initialDays?: number) => {
27-
const [selectedMonth, setMonth] = useState<string | undefined>(initialMonth);
28-
const [selectedYear, setYear] = useState<string | undefined>(initialYear);
29-
const [selectedDays, setDays] = useState<number | undefined>(initialDays);
29+
const [selectedMonth, setMonth] = useState<WheelPickerValue>(initialMonth);
30+
const [, setYear] = useState<WheelPickerValue>(initialYear);
31+
const [selectedDays, setDays] = useState<WheelPickerValue>(initialDays);
3032
const [showDialog, setShowDialog] = useState(false);
3133

3234
const onPickDaysPress = useCallback(() => {
@@ -37,39 +39,23 @@ const useData = (initialMonth?: string, initialYear?: string, initialDays?: numb
3739
setShowDialog(false);
3840
}, []);
3941

40-
const onMonthChange = useCallback((item: string | undefined, _: number) => {
42+
const onMonthChange = useCallback((item: WheelPickerValue, _: number) => {
4143
setMonth(item);
4244
}, []);
4345

44-
const onYearChange = useCallback((item: string | undefined, _: number) => {
46+
const onYearChange = useCallback((item: WheelPickerValue, _: number) => {
4547
setYear(item);
4648
}, []);
4749

48-
const onDaysChange = useCallback((item: number | undefined, _: number) => {
50+
const onDaysChange = useCallback((item: WheelPickerValue, _: number) => {
4951
setDays(item);
5052
}, []);
5153

52-
const monthItems = useMemo(() => {
53-
return _.map(months, item => ({label: item, value: item}));
54-
}, []);
55-
56-
const yearItems = useMemo(() => {
57-
return _.map(years, item => ({label: '' + item, value: item}));
58-
}, []);
59-
60-
const dayItems = useMemo(() => {
61-
return _.map(days, item => ({label: '' + item, value: item}));
62-
}, []);
63-
6454
return {
65-
monthItems,
66-
yearItems,
67-
dayItems,
6855
onMonthChange,
6956
onYearChange,
7057
onDaysChange,
7158
selectedMonth,
72-
selectedYear,
7359
selectedDays,
7460
onPickDaysPress,
7561
onDialogDismissed,
@@ -81,13 +67,9 @@ export default () => {
8167
const {
8268
selectedMonth,
8369
onMonthChange,
84-
monthItems,
85-
selectedYear,
8670
onYearChange,
87-
yearItems,
8871
selectedDays,
8972
onDaysChange,
90-
dayItems,
9173
onPickDaysPress,
9274
onDialogDismissed,
9375
showDialog
@@ -97,10 +79,9 @@ export default () => {
9779
<View flex padding-page>
9880
<Text h1>Wheel Picker</Text>
9981

100-
<View flex marginT-20 centerH>
82+
<View marginT-s5 centerH>
10183
<Text h3>Months</Text>
10284
<Incubator.WheelPicker
103-
style={{width: '100%'}}
10485
onChange={onMonthChange}
10586
activeTextColor={Colors.primary}
10687
inactiveTextColor={Colors.grey20}
@@ -110,15 +91,20 @@ export default () => {
11091
/>
11192

11293
<Text h3>Years</Text>
113-
<View width={'100%'}>
94+
<Text bodySmall grey30>
95+
(Uncontrolled, initialValue passed)
96+
</Text>
97+
<View width={'100%'} marginT-s3>
11498
<Incubator.WheelPicker
11599
onChange={onYearChange}
116100
numberOfVisibleRows={3}
117-
selectedValue={selectedYear}
101+
initialValue={2021}
118102
items={yearItems}
119103
/>
120104
</View>
105+
</View>
121106

107+
<View marginB-s10>
122108
<Button marginT-40 label={'Pick Days'} marginH-100 onPress={onPickDaysPress}/>
123109
<Dialog width={'90%'} height={260} bottom visible={showDialog} onDismiss={onDialogDismissed}>
124110
<Incubator.WheelPicker onChange={onDaysChange} selectedValue={selectedDays} label={'Days'} items={dayItems}/>

generatedTypes/incubator/WheelPicker/index.d.ts

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,14 @@ import { TextStyle, ViewStyle } from 'react-native';
33
import { ItemProps } from './Item';
44
import { TextProps } from '../../components/text';
55
export interface WheelPickerProps {
6+
/**
7+
* Initial value (doesn't work with selectedValue)
8+
*/
9+
initialValue?: ItemProps | number | string;
10+
/**
11+
* The current selected value
12+
*/
13+
selectedValue?: ItemProps | number | string;
614
/**
715
* Data source for WheelPicker
816
*/
@@ -53,11 +61,7 @@ export interface WheelPickerProps {
5361
* Support passing items as children props
5462
*/
5563
children?: JSX.Element | JSX.Element[];
56-
/**
57-
* WheelPicker initial value, can be ItemProps.value, number as index
58-
*/
59-
selectedValue: ItemProps | number | string;
6064
testID?: string;
6165
}
62-
declare const WheelPicker: React.MemoExoticComponent<({ items: propItems, itemHeight, numberOfVisibleRows, activeTextColor, inactiveTextColor, textStyle, label, labelStyle, labelProps, onChange, style, children, selectedValue, testID }: WheelPickerProps) => JSX.Element>;
66+
declare const WheelPicker: React.MemoExoticComponent<({ items: propItems, itemHeight, numberOfVisibleRows, activeTextColor, inactiveTextColor, textStyle, label, labelStyle, labelProps, onChange, style, children, initialValue, selectedValue, testID }: WheelPickerProps) => JSX.Element>;
6367
export default WheelPicker;

generatedTypes/incubator/WheelPicker/usePresenter.d.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,8 @@
22
import { ItemProps } from './Item';
33
export declare type ItemValueTypes = ItemProps | number | string;
44
declare type PropTypes = {
5-
selectedValue: ItemValueTypes;
5+
initialValue?: ItemValueTypes;
6+
selectedValue?: ItemValueTypes;
67
children?: JSX.Element | JSX.Element[];
78
items?: ItemProps[];
89
itemHeight: number;
@@ -19,5 +20,5 @@ interface Presenter {
1920
height: number;
2021
getRowItemAtOffset: (offset: number) => RowItem;
2122
}
22-
declare const usePresenter: ({ selectedValue, children, items: propItems, itemHeight, preferredNumVisibleRows }: PropTypes) => Presenter;
23+
declare const usePresenter: ({ initialValue, selectedValue, children, items: propItems, itemHeight, preferredNumVisibleRows }: PropTypes) => Presenter;
2324
export default usePresenter;

src/incubator/WheelPicker/index.tsx

Lines changed: 20 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,14 @@ import Text, {TextProps} from '../../components/text';
1414
const AnimatedFlatList = Animated.createAnimatedComponent(FlatList);
1515

1616
export interface WheelPickerProps {
17+
/**
18+
* Initial value (doesn't work with selectedValue)
19+
*/
20+
initialValue?: ItemProps | number | string;
21+
/**
22+
* The current selected value
23+
*/
24+
selectedValue?: ItemProps | number | string;
1725
/**
1826
* Data source for WheelPicker
1927
*/
@@ -64,10 +72,6 @@ export interface WheelPickerProps {
6472
* Support passing items as children props
6573
*/
6674
children?: JSX.Element | JSX.Element[];
67-
/**
68-
* WheelPicker initial value, can be ItemProps.value, number as index
69-
*/
70-
selectedValue: ItemProps | number | string;
7175
testID?: string;
7276
}
7377

@@ -84,6 +88,7 @@ const WheelPicker = React.memo(({
8488
onChange,
8589
style,
8690
children,
91+
initialValue,
8792
selectedValue,
8893
testID
8994
}: WheelPickerProps) => {
@@ -100,6 +105,7 @@ const WheelPicker = React.memo(({
100105
index: currentIndex,
101106
getRowItemAtOffset
102107
} = usePresenter({
108+
initialValue,
103109
selectedValue,
104110
items: propItems,
105111
children,
@@ -109,24 +115,19 @@ const WheelPicker = React.memo(({
109115

110116
const prevIndex = useRef(currentIndex);
111117
const [scrollOffset, setScrollOffset] = useState(currentIndex * itemHeight);
112-
113-
useEffect(() => {
114-
controlComponent();
115-
});
116-
117118
const keyExtractor = useCallback((item: ItemProps, index: number) => `${item}.${index}`, []);
118119

119-
/**
120-
* The picker is a controlled component. This means we expect the
121-
* to relay on `selectedValue` prop to be our
122-
* source of truth - not the picker current value.
123-
* This way, you can control disallow or mutate selection of some values.
124-
*/
125-
const controlComponent = () => {
120+
/* This effect enforce the index to be controlled by selectedValue passed by the user */
121+
useEffect(() => {
126122
if (shouldControlComponent(scrollOffset)) {
127123
scrollToIndex(currentIndex, true);
128124
}
129-
};
125+
});
126+
127+
/* This effect making sure to reset index if initialValue has changed */
128+
useEffect(() => {
129+
scrollToIndex(currentIndex, true);
130+
}, [currentIndex]);
130131

131132
const scrollToPassedIndex = useCallback(() => {
132133
scrollToIndex(currentIndex, false);
@@ -164,7 +165,8 @@ const WheelPicker = React.memo(({
164165

165166
const {index, value} = getRowItemAtOffset(event.nativeEvent.contentOffset.y);
166167
onChange?.(value, index);
167-
}, [onChange]);
168+
},
169+
[onChange]);
168170

169171
const renderItem = useCallback(({item, index}) => {
170172
return (

src/incubator/WheelPicker/usePresenter.ts

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,8 @@ import useMiddleIndex from './helpers/useListMiddleIndex';
66
export type ItemValueTypes = ItemProps | number | string;
77

88
type PropTypes = {
9-
selectedValue: ItemValueTypes;
9+
initialValue?: ItemValueTypes;
10+
selectedValue?: ItemValueTypes;
1011
children?: JSX.Element | JSX.Element[];
1112
items?: ItemProps[];
1213
itemHeight: number;
@@ -27,13 +28,14 @@ interface Presenter {
2728
}
2829

2930
const usePresenter = ({
31+
initialValue,
3032
selectedValue,
3133
children,
3234
items: propItems,
3335
itemHeight,
3436
preferredNumVisibleRows
3537
}: PropTypes): Presenter => {
36-
38+
const value = !_.isUndefined(selectedValue) ? selectedValue : initialValue;
3739
const extractItemsFromChildren = (): ItemProps[] => {
3840
const items = React.Children.map(children, child => {
3941
const childAsType: ItemProps = {value: child?.props.value, label: child?.props.label};
@@ -45,15 +47,18 @@ const usePresenter = ({
4547
const items = useRef<ItemProps[]>(children ? extractItemsFromChildren() : propItems!).current;
4648
const middleIndex = useMiddleIndex({itemHeight, listSize: items.length});
4749

48-
const getSelectedValueIndex = (): number => {
49-
if (_.isString(selectedValue) || _.isNumber(selectedValue)) {
50-
return _.findIndex(items, {value: selectedValue});
50+
const getSelectedValueIndex = () => {
51+
if (_.isString(value) || _.isNumber(value)) {
52+
return _.findIndex(items, {value});
5153
}
52-
return _.findIndex(items, {value: selectedValue?.value});
54+
return _.findIndex(items, {value: (value)?.value});
5355
};
5456

5557
const shouldControlComponent = (offset: number): boolean => {
56-
return offset >= 0 && selectedValue !== getRowItemAtOffset(offset).value;
58+
if (!_.isUndefined(selectedValue)) {
59+
return offset >= 0 && selectedValue !== getRowItemAtOffset(offset).value;
60+
}
61+
return false;
5762
};
5863

5964
const getRowItemAtOffset = (offset: number): RowItem => {

0 commit comments

Comments
 (0)