Skip to content

Commit 3dafa66

Browse files
authored
Fix/ WheelPicker alignment and touch area (#1464)
* fix alignment and touch area * PR review fixes
1 parent ada2c50 commit 3dafa66

File tree

5 files changed

+109
-20
lines changed

5 files changed

+109
-20
lines changed

demo/src/screens/componentScreens/SectionsWheelPickerScreen.tsx

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import React, {useState, useCallback} from 'react';
33
import {Alert} from 'react-native';
44
import {Text, View, SectionsWheelPicker, SegmentedControl, Button, Incubator} from 'react-native-ui-lib';
55

6+
const {WheelPicker} = Incubator;
67
const SectionsWheelPickerScreen = () => {
78
const [numOfSections, setNumOfSections] = useState(1);
89

@@ -65,21 +66,24 @@ const SectionsWheelPickerScreen = () => {
6566
onChange: onDaysChange,
6667
selectedValue: selectedDays,
6768
label: 'Days',
68-
style: numOfSections === 1 ? {flex: 1} : {flex: 1, alignItems: 'flex-end'}
69+
align: numOfSections === 1 ? WheelPicker.alignments.CENTER : WheelPicker.alignments.RIGHT,
70+
style: {flex: 1}
6971
},
7072
{
7173
items: getItems(hours),
7274
onChange: onHoursChange,
7375
selectedValue: selectedHours,
7476
label: 'Hrs',
75-
style: numOfSections === 2 ? {flex: 1, alignItems: 'flex-start'} : undefined
77+
align: numOfSections === 2 ? WheelPicker.alignments.LEFT : WheelPicker.alignments.CENTER,
78+
style: numOfSections === 2 ? {flex: 1} : undefined
7679
},
7780
{
7881
items: getItems(minutes),
7982
onChange: onMinutesChange,
8083
selectedValue: selectedMinutes,
8184
label: 'Mins',
82-
style: {flex: 1, alignItems: 'flex-start'}
85+
align: WheelPicker.alignments.LEFT,
86+
style: {flex: 1}
8387
}
8488
];
8589

generatedTypes/incubator/WheelPicker/Item.d.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,12 @@
11
import React from 'react';
22
import { TextStyle } from 'react-native';
33
import Animated from 'react-native-reanimated';
4+
import { TextProps } from '../../components/text';
45
export interface ItemProps {
56
label: string;
7+
fakeLabel?: string;
8+
fakeLabelStyle?: TextStyle;
9+
fakeLabelProps?: TextProps;
610
value: string | number;
711
}
812
interface InternalProps extends ItemProps {
@@ -16,5 +20,5 @@ interface InternalProps extends ItemProps {
1620
testID?: string;
1721
centerH?: boolean;
1822
}
19-
declare const _default: React.MemoExoticComponent<({ index, label, itemHeight, onSelect, offset, activeColor, inactiveColor, style, testID, centerH }: InternalProps) => JSX.Element>;
23+
declare const _default: React.MemoExoticComponent<({ index, label, fakeLabel, fakeLabelStyle, fakeLabelProps, itemHeight, onSelect, offset, activeColor, inactiveColor, style, testID, centerH }: InternalProps) => JSX.Element>;
2024
export default _default;

generatedTypes/incubator/WheelPicker/index.d.ts

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,12 @@
1-
import React from 'react';
1+
/// <reference types="react" />
22
import { TextStyle, ViewStyle } from 'react-native';
33
import { ItemProps } from './Item';
44
import { TextProps } from '../../components/text';
5+
declare enum WheelPickerAlign {
6+
CENTER = "center",
7+
RIGHT = "right",
8+
LEFT = "left"
9+
}
510
export interface WheelPickerProps {
611
/**
712
* Initial value (doesn't work with selectedValue)
@@ -61,7 +66,14 @@ export interface WheelPickerProps {
6166
* Support passing items as children props
6267
*/
6368
children?: JSX.Element | JSX.Element[];
69+
/**
70+
* Align the content to center, right ot left (default: center)
71+
*/
72+
align?: WheelPickerAlign;
6473
testID?: string;
6574
}
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>;
75+
declare const WheelPicker: {
76+
({ items: propItems, itemHeight, numberOfVisibleRows, activeTextColor, inactiveTextColor, textStyle, label, labelStyle, labelProps, onChange, align, style, children, initialValue, selectedValue, testID }: WheelPickerProps): JSX.Element;
77+
alignments: typeof WheelPickerAlign;
78+
};
6779
export default WheelPicker;

src/incubator/WheelPicker/Item.tsx

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import React, {useCallback, useMemo, memo} from 'react';
22
import {TextStyle, StyleSheet} from 'react-native';
33
import Animated, {interpolateColor, useAnimatedStyle} from 'react-native-reanimated';
4-
import Text from '../../components/text';
4+
import Text, {TextProps} from '../../components/text';
55
import TouchableOpacity from '../../components/touchableOpacity';
66
import {Colors, Spacings} from '../../../src/style';
77

@@ -10,6 +10,9 @@ const AnimatedText = Animated.createAnimatedComponent(Text);
1010

1111
export interface ItemProps {
1212
label: string;
13+
fakeLabel?: string;
14+
fakeLabelStyle?: TextStyle;
15+
fakeLabelProps?: TextProps;
1316
value: string | number;
1417
}
1518

@@ -28,6 +31,9 @@ interface InternalProps extends ItemProps {
2831
export default memo(({
2932
index,
3033
label,
34+
fakeLabel,
35+
fakeLabelStyle,
36+
fakeLabelProps,
3137
itemHeight,
3238
onSelect,
3339
offset,
@@ -63,16 +69,31 @@ export default memo(({
6369
// @ts-ignore reanimated2
6470
index={index}
6571
testID={testID}
72+
row
6673
>
67-
<AnimatedText text60R style={[animatedColorStyle, style]}>
74+
<AnimatedText
75+
text60R
76+
style={[animatedColorStyle, style, fakeLabel ? styles.textWithLabelPadding : styles.textPadding]}
77+
>
6878
{label}
6979
</AnimatedText>
80+
{fakeLabel && (
81+
<Text marginL-s2 marginR-s5 text80M color={'white'} {...fakeLabelProps} style={fakeLabelStyle}>
82+
{fakeLabel}
83+
</Text>
84+
)}
7085
</AnimatedTouchableOpacity>
7186
);
7287
});
7388

7489
const styles = StyleSheet.create({
7590
container: {
7691
minWidth: Spacings.s10
92+
},
93+
textPadding: {
94+
paddingHorizontal: Spacings.s5
95+
},
96+
textWithLabelPadding: {
97+
paddingLeft: Spacings.s5
7798
}
7899
});

src/incubator/WheelPicker/index.tsx

Lines changed: 60 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,12 @@ import Text, {TextProps} from '../../components/text';
1313

1414
const AnimatedFlatList = Animated.createAnimatedComponent(FlatList);
1515

16+
enum WheelPickerAlign {
17+
CENTER = 'center',
18+
RIGHT = 'right',
19+
LEFT = 'left'
20+
}
21+
1622
export interface WheelPickerProps {
1723
/**
1824
* Initial value (doesn't work with selectedValue)
@@ -72,10 +78,14 @@ export interface WheelPickerProps {
7278
* Support passing items as children props
7379
*/
7480
children?: JSX.Element | JSX.Element[];
81+
/**
82+
* Align the content to center, right ot left (default: center)
83+
*/
84+
align?: WheelPickerAlign;
7585
testID?: string;
7686
}
7787

78-
const WheelPicker = React.memo(({
88+
const WheelPicker = ({
7989
items: propItems,
8090
itemHeight = 44,
8191
numberOfVisibleRows = 5,
@@ -86,6 +96,7 @@ const WheelPicker = React.memo(({
8696
labelStyle,
8797
labelProps,
8898
onChange,
99+
align,
89100
style,
90101
children,
91102
initialValue,
@@ -115,6 +126,7 @@ const WheelPicker = React.memo(({
115126

116127
const prevIndex = useRef(currentIndex);
117128
const [scrollOffset, setScrollOffset] = useState(currentIndex * itemHeight);
129+
const [flatListWidth, setFlatListWidth] = useState(0);
118130
const keyExtractor = useCallback((item: ItemProps, index: number) => `${item}.${index}`, []);
119131

120132
/* This effect enforce the index to be controlled by selectedValue passed by the user */
@@ -168,6 +180,14 @@ const WheelPicker = React.memo(({
168180
},
169181
[onChange]);
170182

183+
const alignmentStyle = useMemo(() =>
184+
align === WheelPickerAlign.RIGHT
185+
? {alignSelf: 'flex-end'}
186+
: align === WheelPickerAlign.LEFT
187+
? {alignSelf: 'flex-start'}
188+
: {alignSelf: 'center'},
189+
[align]);
190+
171191
const renderItem = useCallback(({item, index}) => {
172192
return (
173193
<Item
@@ -178,6 +198,9 @@ const WheelPicker = React.memo(({
178198
inactiveColor={inactiveTextColor}
179199
style={textStyle}
180200
{...item}
201+
fakeLabel={label}
202+
fakeLabelStyle={labelStyle}
203+
fakeLabelProps={labelProps}
181204
centerH={!label}
182205
onSelect={selectItem}
183206
testID={`${testID}.item_${index}`}
@@ -194,15 +217,22 @@ const WheelPicker = React.memo(({
194217
);
195218
}, []);
196219

220+
const labelContainerStyle = useMemo(() => {
221+
return [{position: 'absolute', top: 0, bottom: 0}, alignmentStyle];
222+
}, [alignmentStyle]);
223+
197224
const labelContainer = useMemo(() => {
198225
return (
199-
<View centerV>
200-
<Text marginL-s2 text80M {...labelProps} color={activeTextColor} style={labelStyle}>
201-
{label}
202-
</Text>
226+
// @ts-expect-error
227+
<View style={labelContainerStyle} width={flatListWidth} pointerEvents="none">
228+
<View style={styles.label} centerV pointerEvents="none">
229+
<Text marginL-s2 marginR-s5 text80M {...labelProps} color={activeTextColor} style={labelStyle}>
230+
{label}
231+
</Text>
232+
</View>
203233
</View>
204234
);
205-
}, []);
235+
}, [flatListWidth, labelContainerStyle, label, labelProps, activeTextColor, labelStyle]);
206236

207237
const fader = useMemo(() => (position: FaderPosition) => {
208238
return <Fader visible position={position} size={60}/>;
@@ -214,14 +244,23 @@ const WheelPicker = React.memo(({
214244
},
215245
[itemHeight]);
216246

247+
const updateFlatListWidth = useCallback((width: number) => {
248+
setFlatListWidth(width);
249+
}, []);
250+
217251
const contentContainerStyle = useMemo(() => {
218-
return {paddingVertical: height / 2 - itemHeight / 2};
219-
}, [height, itemHeight]);
252+
return [
253+
{
254+
paddingVertical: height / 2 - itemHeight / 2
255+
},
256+
alignmentStyle
257+
];
258+
}, [height, itemHeight, alignmentStyle]);
220259

221260
return (
222261
<View testID={testID} bg-white style={style}>
223-
<View row marginH-s5 centerH>
224-
<View>
262+
<View row centerH>
263+
<View flexG>
225264
<AnimatedFlatList
226265
testID={`${testID}.list`}
227266
height={height}
@@ -235,23 +274,26 @@ const WheelPicker = React.memo(({
235274
onLayout={scrollToPassedIndex}
236275
// @ts-ignore
237276
ref={scrollView}
277+
// @ts-expect-error
238278
contentContainerStyle={contentContainerStyle}
239279
snapToInterval={itemHeight}
240280
decelerationRate={Constants.isAndroid ? 0.98 : 'normal'}
241281
renderItem={renderItem}
242282
getItemLayout={getItemLayout}
243283
initialScrollIndex={currentIndex}
284+
onContentSizeChange={updateFlatListWidth}
244285
/>
245286
</View>
246-
{label && labelContainer}
247287
</View>
288+
{label && labelContainer}
248289
{fader(FaderPosition.BOTTOM)}
249290
{fader(FaderPosition.TOP)}
250291
{separators}
251292
</View>
252293
);
253-
});
294+
};
254295

296+
WheelPicker.alignments = WheelPickerAlign;
255297
export default WheelPicker;
256298

257299
const styles = StyleSheet.create({
@@ -260,5 +302,11 @@ const styles = StyleSheet.create({
260302
borderBottomWidth: 1,
261303
height: Spacings.s9,
262304
borderColor: Colors.grey60
305+
},
306+
label: {
307+
position: 'absolute',
308+
right: 0,
309+
top: 0,
310+
bottom: 0
263311
}
264312
});

0 commit comments

Comments
 (0)