expo-horizontal-picker 0.1.2 → 0.1.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,4 +1,6 @@
1
1
  # expo-horizontal-picker
2
+ [![npm version](https://badge.fury.io/js/expo-horizontal-picker.svg)](https://badge.fury.io/js/expo-horizontal-picker)
3
+ [![npm downloads](https://img.shields.io/npm/dm/expo-horizontal-picker.svg?style=flat-square)](https://www.npmjs.com/package/expo-horizontal-picker)
2
4
 
3
5
  A performant horizontal picker component for React Native and Expo apps.
4
6
  - **Smooth Horizontal Scrolling**
@@ -55,7 +57,7 @@ export default function App() {
55
57
  <SafeAreaView style={styles.container}>
56
58
  <View>
57
59
  <HorizontalPicker
58
- data={Array.from({ length: 100 }, (_, i) => ({
60
+ items={Array.from({ length: 100 }, (_, i) => ({
59
61
  label: `${i + 1}`,
60
62
  value: i + 1,
61
63
  }))}
@@ -65,7 +67,7 @@ export default function App() {
65
67
  />
66
68
 
67
69
  <HorizontalPicker
68
- data={Array.from({ length: 20 }, (_, i) => ({
70
+ items={Array.from({ length: 20 }, (_, i) => ({
69
71
  label: `${i + 1}k`,
70
72
  value: i + 1,
71
73
  }))}
@@ -76,7 +78,7 @@ export default function App() {
76
78
  />
77
79
 
78
80
  <HorizontalPicker
79
- data={Array.from({ length: 24 }, (_, i) => ({
81
+ items={Array.from({ length: 24 }, (_, i) => ({
80
82
  label: `${i + 1}h`,
81
83
  value: i + 1,
82
84
  }))}
@@ -88,7 +90,7 @@ export default function App() {
88
90
  />
89
91
 
90
92
  <HorizontalPicker
91
- data={Array.from({ length: 5 }, (_, i) => ({
93
+ items={Array.from({ length: 5 }, (_, i) => ({
92
94
  label: `${(i + 1) * 10000}`,
93
95
  value: (i + 1) * 10000,
94
96
  }))}
@@ -114,7 +116,7 @@ const styles = {
114
116
  ## 🧩 Props
115
117
  | Prop | Type | Default | Description |
116
118
  |-------------------------|--------------------------------------------------------|---------|---------------------------------------------------------------------------------|
117
- | `data` | `PickerOption[]` | – | Array of options to display. Each option is an object with `label` and `value`. |
119
+ | `items` | `PickerOption[]` | – | Array of options to display. Each option is an object with `label` and `value`. |
118
120
  | `initialIndex` | `number` | `0` | Index of the item initially selected. |
119
121
  | `visibleItemCount` | `number` | `7` | Number of items visible on screen at once. |
120
122
  | `onChange` | `(value: string \| number, index: number) => void` | – | Callback triggered when the selected item changes. |
package/build/index.d.ts CHANGED
@@ -5,7 +5,7 @@ interface PickerOption {
5
5
  value: string | number;
6
6
  }
7
7
  interface HorizontalPickerProps extends Omit<AnimatedScrollViewProps, 'style'> {
8
- data: PickerOption[];
8
+ items: PickerOption[];
9
9
  initialIndex?: number;
10
10
  visibleItemCount?: number;
11
11
  onChange?: (value: PickerOption['value'], index: number) => void;
@@ -15,6 +15,6 @@ interface HorizontalPickerProps extends Omit<AnimatedScrollViewProps, 'style'> {
15
15
  itemTextStyle?: StyleProp<TextStyle>;
16
16
  selectedItemTextStyle?: StyleProp<TextStyle>;
17
17
  }
18
- export declare function HorizontalPicker({ data, initialIndex, visibleItemCount, onChange, onHapticFeedback, containerStyle, itemContainerStyle, itemTextStyle, selectedItemTextStyle, ...props }: HorizontalPickerProps): import("react").JSX.Element;
18
+ export declare function HorizontalPicker({ items, initialIndex, visibleItemCount, onChange, onHapticFeedback, containerStyle, itemContainerStyle, itemTextStyle, selectedItemTextStyle, ...props }: HorizontalPickerProps): import("react").JSX.Element;
19
19
  export {};
20
20
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.tsx"],"names":[],"mappings":"AACA,OAAO,EAKL,KAAK,SAAS,EAGd,KAAK,SAAS,EAEd,KAAK,SAAS,EACf,MAAM,cAAc,CAAC;AACtB,OAAiB,EACf,KAAK,uBAAuB,EAM7B,MAAM,yBAAyB,CAAC;AAEjC,UAAU,YAAY;IACpB,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,GAAG,MAAM,CAAC;CACxB;AAED,UAAU,qBAAsB,SAAQ,IAAI,CAAC,uBAAuB,EAAE,OAAO,CAAC;IAC5E,IAAI,EAAE,YAAY,EAAE,CAAC;IACrB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,YAAY,CAAC,OAAO,CAAC,EAAE,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACjE,gBAAgB,CAAC,EAAE,MAAM,IAAI,CAAC;IAC9B,cAAc,CAAC,EAAE,uBAAuB,CAAC,OAAO,CAAC,CAAC;IAClD,kBAAkB,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IAC1C,aAAa,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IACrC,qBAAqB,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;CAC9C;AAED,wBAAgB,gBAAgB,CAAC,EAC/B,IAAI,EACJ,YAAgB,EAChB,gBAAoB,EACpB,QAAQ,EACR,gBAAgB,EAChB,cAAc,EACd,kBAAkB,EAClB,aAAa,EACb,qBAAqB,EACrB,GAAG,KAAK,EACT,EAAE,qBAAqB,+BAsHvB"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.tsx"],"names":[],"mappings":"AACA,OAAO,EAKL,KAAK,SAAS,EAGd,KAAK,SAAS,EAEd,KAAK,SAAS,EACf,MAAM,cAAc,CAAC;AACtB,OAAiB,EACf,KAAK,uBAAuB,EAM7B,MAAM,yBAAyB,CAAC;AAEjC,UAAU,YAAY;IACpB,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,GAAG,MAAM,CAAC;CACxB;AAED,UAAU,qBAAsB,SAAQ,IAAI,CAAC,uBAAuB,EAAE,OAAO,CAAC;IAC5E,KAAK,EAAE,YAAY,EAAE,CAAC;IACtB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,YAAY,CAAC,OAAO,CAAC,EAAE,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACjE,gBAAgB,CAAC,EAAE,MAAM,IAAI,CAAC;IAC9B,cAAc,CAAC,EAAE,uBAAuB,CAAC,OAAO,CAAC,CAAC;IAClD,kBAAkB,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IAC1C,aAAa,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IACrC,qBAAqB,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;CAC9C;AAED,wBAAgB,gBAAgB,CAAC,EAC/B,KAAK,EACL,YAAgB,EAChB,gBAAoB,EACpB,QAAQ,EACR,gBAAgB,EAChB,cAAc,EACd,kBAAkB,EAClB,aAAa,EACb,qBAAqB,EACrB,GAAG,KAAK,EACT,EAAE,qBAAqB,+BAsHvB"}
package/build/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import { useCallback, useMemo, useRef, useState } from 'react';
2
2
  import { PixelRatio, Platform, Pressable, StyleSheet, Text, View, } from 'react-native';
3
3
  import Animated, { runOnJS, useAnimatedReaction, useAnimatedScrollHandler, useSharedValue, } from 'react-native-reanimated';
4
- export function HorizontalPicker({ data, initialIndex = 0, visibleItemCount = 7, onChange, onHapticFeedback, containerStyle, itemContainerStyle, itemTextStyle, selectedItemTextStyle, ...props }) {
4
+ export function HorizontalPicker({ items, initialIndex = 0, visibleItemCount = 7, onChange, onHapticFeedback, containerStyle, itemContainerStyle, itemTextStyle, selectedItemTextStyle, ...props }) {
5
5
  const scrollViewRef = useRef(null);
6
6
  const lastHapticIndexRef = useRef(-1);
7
7
  const [scrollViewWidth, setScrollViewWidth] = useState(0);
@@ -16,46 +16,46 @@ export function HorizontalPicker({ data, initialIndex = 0, visibleItemCount = 7,
16
16
  };
17
17
  }, [scrollViewWidth, visibleItemCount]);
18
18
  const snapOffsets = useMemo(() => {
19
- return data.map((_, index) => PixelRatio.roundToNearestPixel(index * itemWidth));
20
- }, [data, itemWidth]);
19
+ return items.map((_, index) => PixelRatio.roundToNearestPixel(index * itemWidth));
20
+ }, [items, itemWidth]);
21
21
  const handleOnLayout = useCallback((e) => {
22
22
  const layoutWidth = e.nativeEvent.layout.width;
23
23
  setScrollViewWidth(layoutWidth);
24
- const safeIndex = Math.max(0, Math.min(data.length - 1, initialIndex));
24
+ const safeIndex = Math.max(0, Math.min(items.length - 1, initialIndex));
25
25
  const rawItemWidth = layoutWidth / visibleItemCount;
26
26
  const x = PixelRatio.roundToNearestPixel(safeIndex * rawItemWidth);
27
27
  requestAnimationFrame(() => {
28
28
  scrollViewRef.current?.scrollTo({ x, y: 0, animated: false });
29
29
  });
30
- }, [initialIndex, data.length, visibleItemCount]);
30
+ }, [initialIndex, items.length, visibleItemCount]);
31
31
  const handleOnScroll = useAnimatedScrollHandler({
32
32
  onScroll: (event) => {
33
33
  scrollX.value = event.contentOffset.x;
34
34
  },
35
35
  });
36
36
  const handleOnPress = useCallback((newIndex) => {
37
- const safeIndex = Math.max(0, Math.min(data.length - 1, newIndex));
37
+ const safeIndex = Math.max(0, Math.min(items.length - 1, newIndex));
38
38
  const x = PixelRatio.roundToNearestPixel(safeIndex * itemWidth);
39
39
  scrollViewRef.current?.scrollTo({ x, y: 0, animated: true });
40
- }, [data.length, itemWidth]);
40
+ }, [items.length, itemWidth]);
41
41
  const handleOnChange = useCallback((index) => {
42
- const item = data[index];
42
+ const item = items[index];
43
43
  if (item) {
44
44
  onChange?.(item.value, index);
45
45
  }
46
- }, [onChange, data]);
46
+ }, [onChange, items]);
47
47
  const handleOnHapticFeedback = useCallback((index) => {
48
- if (index !== lastHapticIndexRef.current && index >= 0 && index < data.length) {
48
+ if (index !== lastHapticIndexRef.current && index >= 0 && index < items.length) {
49
49
  lastHapticIndexRef.current = index;
50
50
  onHapticFeedback?.();
51
51
  }
52
- }, [data.length, onHapticFeedback]);
52
+ }, [items.length, onHapticFeedback]);
53
53
  useAnimatedReaction(() => {
54
54
  if (scrollViewWidth === 0) {
55
55
  return null;
56
56
  }
57
57
  const newIndex = Math.round(scrollX.value / itemWidth);
58
- const safeIndex = Math.max(0, Math.min(data.length - 1, newIndex));
58
+ const safeIndex = Math.max(0, Math.min(items.length - 1, newIndex));
59
59
  currentIndex.value = safeIndex;
60
60
  return safeIndex;
61
61
  }, (safeIndex, prevIndex) => {
@@ -63,9 +63,9 @@ export function HorizontalPicker({ data, initialIndex = 0, visibleItemCount = 7,
63
63
  runOnJS(handleOnChange)(safeIndex);
64
64
  runOnJS(handleOnHapticFeedback)(safeIndex);
65
65
  }
66
- }, [itemWidth, data.length, scrollViewWidth]);
66
+ }, [itemWidth, items.length, scrollViewWidth]);
67
67
  return (<Animated.ScrollView ref={scrollViewRef} horizontal onLayout={handleOnLayout} onScroll={handleOnScroll} showsHorizontalScrollIndicator={false} scrollEventThrottle={Platform.select({ ios: 16, android: 2 })} decelerationRate={Platform.select({ ios: undefined, android: 'fast' })} snapToOffsets={snapOffsets} contentContainerStyle={{ paddingHorizontal: paddingSide }} style={[styles.container, containerStyle]} {...props}>
68
- {data.map((item, index) => (<PickerItem key={`picker-item-${item.value}`} label={item.label} index={index} currentIndex={currentIndex} itemWidth={itemWidth} onPress={() => handleOnPress(index)} itemContainerStyle={itemContainerStyle} itemTextStyle={itemTextStyle} selectedItemTextStyle={selectedItemTextStyle}/>))}
68
+ {items.map((item, index) => (<PickerItem key={`picker-item-${item.label}-${index}`} label={item.label} index={index} currentIndex={currentIndex} itemWidth={itemWidth} onPress={() => handleOnPress(index)} itemContainerStyle={itemContainerStyle} itemTextStyle={itemTextStyle} selectedItemTextStyle={selectedItemTextStyle}/>))}
69
69
  </Animated.ScrollView>);
70
70
  }
71
71
  function PickerItem({ label, index, currentIndex, itemWidth, onPress, itemContainerStyle, itemTextStyle, selectedItemTextStyle, }) {
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.tsx"],"names":[],"mappings":"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","sourcesContent":["import { useCallback, useMemo, useRef, useState } from 'react';\nimport {\n type LayoutChangeEvent,\n PixelRatio,\n Platform,\n Pressable,\n type StyleProp,\n StyleSheet,\n Text,\n type TextStyle,\n View,\n type ViewStyle,\n} from 'react-native';\nimport Animated, {\n type AnimatedScrollViewProps,\n runOnJS,\n type SharedValue,\n useAnimatedReaction,\n useAnimatedScrollHandler,\n useSharedValue,\n} from 'react-native-reanimated';\n\ninterface PickerOption {\n label: string;\n value: string | number;\n}\n\ninterface HorizontalPickerProps extends Omit<AnimatedScrollViewProps, 'style'> {\n data: PickerOption[];\n initialIndex?: number;\n visibleItemCount?: number;\n onChange?: (value: PickerOption['value'], index: number) => void;\n onHapticFeedback?: () => void;\n containerStyle?: AnimatedScrollViewProps['style'];\n itemContainerStyle?: StyleProp<ViewStyle>;\n itemTextStyle?: StyleProp<TextStyle>;\n selectedItemTextStyle?: StyleProp<TextStyle>;\n}\n\nexport function HorizontalPicker({\n data,\n initialIndex = 0,\n visibleItemCount = 7,\n onChange,\n onHapticFeedback,\n containerStyle,\n itemContainerStyle,\n itemTextStyle,\n selectedItemTextStyle,\n ...props\n}: HorizontalPickerProps) {\n const scrollViewRef = useRef<Animated.ScrollView>(null);\n const lastHapticIndexRef = useRef<number>(-1);\n const [scrollViewWidth, setScrollViewWidth] = useState<number>(0);\n const scrollX = useSharedValue<number>(0);\n const currentIndex = useSharedValue<number>(initialIndex);\n\n const { itemWidth, paddingSide } = useMemo(() => {\n const width = PixelRatio.roundToNearestPixel(scrollViewWidth / visibleItemCount);\n const padding = PixelRatio.roundToNearestPixel(scrollViewWidth / 2 - width / 2);\n return {\n itemWidth: width,\n paddingSide: padding,\n };\n }, [scrollViewWidth, visibleItemCount]);\n\n const snapOffsets = useMemo(() => {\n return data.map((_, index) => PixelRatio.roundToNearestPixel(index * itemWidth));\n }, [data, itemWidth]);\n\n const handleOnLayout = useCallback(\n (e: LayoutChangeEvent) => {\n const layoutWidth = e.nativeEvent.layout.width;\n setScrollViewWidth(layoutWidth);\n\n const safeIndex = Math.max(0, Math.min(data.length - 1, initialIndex));\n const rawItemWidth = layoutWidth / visibleItemCount;\n const x = PixelRatio.roundToNearestPixel(safeIndex * rawItemWidth);\n requestAnimationFrame(() => {\n scrollViewRef.current?.scrollTo({ x, y: 0, animated: false });\n });\n },\n [initialIndex, data.length, visibleItemCount],\n );\n\n const handleOnScroll = useAnimatedScrollHandler({\n onScroll: (event) => {\n scrollX.value = event.contentOffset.x;\n },\n });\n\n const handleOnPress = useCallback(\n (newIndex: number) => {\n const safeIndex = Math.max(0, Math.min(data.length - 1, newIndex));\n const x = PixelRatio.roundToNearestPixel(safeIndex * itemWidth);\n scrollViewRef.current?.scrollTo({ x, y: 0, animated: true });\n },\n [data.length, itemWidth],\n );\n\n const handleOnChange = useCallback(\n (index: number) => {\n const item = data[index];\n if (item) {\n onChange?.(item.value, index);\n }\n },\n [onChange, data],\n );\n\n const handleOnHapticFeedback = useCallback(\n (index: number) => {\n if (index !== lastHapticIndexRef.current && index >= 0 && index < data.length) {\n lastHapticIndexRef.current = index;\n onHapticFeedback?.();\n }\n },\n [data.length, onHapticFeedback],\n );\n\n useAnimatedReaction(\n () => {\n if (scrollViewWidth === 0) {\n return null;\n }\n const newIndex = Math.round(scrollX.value / itemWidth);\n const safeIndex = Math.max(0, Math.min(data.length - 1, newIndex));\n currentIndex.value = safeIndex;\n return safeIndex;\n },\n (safeIndex, prevIndex) => {\n if (safeIndex !== null && safeIndex !== prevIndex) {\n runOnJS(handleOnChange)(safeIndex);\n runOnJS(handleOnHapticFeedback)(safeIndex);\n }\n },\n [itemWidth, data.length, scrollViewWidth],\n );\n\n return (\n <Animated.ScrollView\n ref={scrollViewRef}\n horizontal\n onLayout={handleOnLayout}\n onScroll={handleOnScroll}\n showsHorizontalScrollIndicator={false}\n scrollEventThrottle={Platform.select({ ios: 16, android: 2 })}\n decelerationRate={Platform.select({ ios: undefined, android: 'fast' })}\n snapToOffsets={snapOffsets}\n contentContainerStyle={{ paddingHorizontal: paddingSide }}\n style={[styles.container, containerStyle]}\n {...props}\n >\n {data.map((item, index) => (\n <PickerItem\n key={`picker-item-${item.value}`}\n label={item.label}\n index={index}\n currentIndex={currentIndex}\n itemWidth={itemWidth}\n onPress={() => handleOnPress(index)}\n itemContainerStyle={itemContainerStyle}\n itemTextStyle={itemTextStyle}\n selectedItemTextStyle={selectedItemTextStyle}\n />\n ))}\n </Animated.ScrollView>\n );\n}\n\ninterface PickerItemProps\n extends Pick<HorizontalPickerProps, 'itemContainerStyle' | 'itemTextStyle' | 'selectedItemTextStyle'> {\n label: PickerOption['label'];\n index: number;\n currentIndex: SharedValue<number>;\n itemWidth: number;\n onPress: () => void;\n}\n\nfunction PickerItem({\n label,\n index,\n currentIndex,\n itemWidth,\n onPress,\n itemContainerStyle,\n itemTextStyle,\n selectedItemTextStyle,\n}: PickerItemProps) {\n const [isFocused, setIsFocused] = useState(false);\n\n useAnimatedReaction(\n () => currentIndex.value === index,\n (shouldBeFocused, isCurrentFocused) => {\n if (shouldBeFocused !== isCurrentFocused) {\n runOnJS(setIsFocused)(shouldBeFocused);\n }\n },\n [index],\n );\n\n return (\n <Pressable onPress={onPress}>\n <View style={[{ width: itemWidth }, styles.itemContainer, itemContainerStyle]}>\n <Text style={isFocused ? [styles.itemTextSelected, selectedItemTextStyle] : [styles.itemText, itemTextStyle]}>\n {label}\n </Text>\n </View>\n </Pressable>\n );\n}\n\nconst styles = StyleSheet.create({\n container: {\n width: '100%',\n },\n itemContainer: {\n justifyContent: 'center',\n alignItems: 'center',\n height: 60,\n },\n itemText: {\n fontSize: 13,\n fontWeight: '700',\n color: '#C9CED9',\n },\n itemTextSelected: {\n fontSize: 15,\n fontWeight: '800',\n color: '#000000',\n },\n});\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.tsx"],"names":[],"mappings":"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","sourcesContent":["import { useCallback, useMemo, useRef, useState } from 'react';\nimport {\n type LayoutChangeEvent,\n PixelRatio,\n Platform,\n Pressable,\n type StyleProp,\n StyleSheet,\n Text,\n type TextStyle,\n View,\n type ViewStyle,\n} from 'react-native';\nimport Animated, {\n type AnimatedScrollViewProps,\n runOnJS,\n type SharedValue,\n useAnimatedReaction,\n useAnimatedScrollHandler,\n useSharedValue,\n} from 'react-native-reanimated';\n\ninterface PickerOption {\n label: string;\n value: string | number;\n}\n\ninterface HorizontalPickerProps extends Omit<AnimatedScrollViewProps, 'style'> {\n items: PickerOption[];\n initialIndex?: number;\n visibleItemCount?: number;\n onChange?: (value: PickerOption['value'], index: number) => void;\n onHapticFeedback?: () => void;\n containerStyle?: AnimatedScrollViewProps['style'];\n itemContainerStyle?: StyleProp<ViewStyle>;\n itemTextStyle?: StyleProp<TextStyle>;\n selectedItemTextStyle?: StyleProp<TextStyle>;\n}\n\nexport function HorizontalPicker({\n items,\n initialIndex = 0,\n visibleItemCount = 7,\n onChange,\n onHapticFeedback,\n containerStyle,\n itemContainerStyle,\n itemTextStyle,\n selectedItemTextStyle,\n ...props\n}: HorizontalPickerProps) {\n const scrollViewRef = useRef<Animated.ScrollView>(null);\n const lastHapticIndexRef = useRef<number>(-1);\n const [scrollViewWidth, setScrollViewWidth] = useState<number>(0);\n const scrollX = useSharedValue<number>(0);\n const currentIndex = useSharedValue<number>(initialIndex);\n\n const { itemWidth, paddingSide } = useMemo(() => {\n const width = PixelRatio.roundToNearestPixel(scrollViewWidth / visibleItemCount);\n const padding = PixelRatio.roundToNearestPixel(scrollViewWidth / 2 - width / 2);\n return {\n itemWidth: width,\n paddingSide: padding,\n };\n }, [scrollViewWidth, visibleItemCount]);\n\n const snapOffsets = useMemo(() => {\n return items.map((_, index) => PixelRatio.roundToNearestPixel(index * itemWidth));\n }, [items, itemWidth]);\n\n const handleOnLayout = useCallback(\n (e: LayoutChangeEvent) => {\n const layoutWidth = e.nativeEvent.layout.width;\n setScrollViewWidth(layoutWidth);\n\n const safeIndex = Math.max(0, Math.min(items.length - 1, initialIndex));\n const rawItemWidth = layoutWidth / visibleItemCount;\n const x = PixelRatio.roundToNearestPixel(safeIndex * rawItemWidth);\n requestAnimationFrame(() => {\n scrollViewRef.current?.scrollTo({ x, y: 0, animated: false });\n });\n },\n [initialIndex, items.length, visibleItemCount],\n );\n\n const handleOnScroll = useAnimatedScrollHandler({\n onScroll: (event) => {\n scrollX.value = event.contentOffset.x;\n },\n });\n\n const handleOnPress = useCallback(\n (newIndex: number) => {\n const safeIndex = Math.max(0, Math.min(items.length - 1, newIndex));\n const x = PixelRatio.roundToNearestPixel(safeIndex * itemWidth);\n scrollViewRef.current?.scrollTo({ x, y: 0, animated: true });\n },\n [items.length, itemWidth],\n );\n\n const handleOnChange = useCallback(\n (index: number) => {\n const item = items[index];\n if (item) {\n onChange?.(item.value, index);\n }\n },\n [onChange, items],\n );\n\n const handleOnHapticFeedback = useCallback(\n (index: number) => {\n if (index !== lastHapticIndexRef.current && index >= 0 && index < items.length) {\n lastHapticIndexRef.current = index;\n onHapticFeedback?.();\n }\n },\n [items.length, onHapticFeedback],\n );\n\n useAnimatedReaction(\n () => {\n if (scrollViewWidth === 0) {\n return null;\n }\n const newIndex = Math.round(scrollX.value / itemWidth);\n const safeIndex = Math.max(0, Math.min(items.length - 1, newIndex));\n currentIndex.value = safeIndex;\n return safeIndex;\n },\n (safeIndex, prevIndex) => {\n if (safeIndex !== null && safeIndex !== prevIndex) {\n runOnJS(handleOnChange)(safeIndex);\n runOnJS(handleOnHapticFeedback)(safeIndex);\n }\n },\n [itemWidth, items.length, scrollViewWidth],\n );\n\n return (\n <Animated.ScrollView\n ref={scrollViewRef}\n horizontal\n onLayout={handleOnLayout}\n onScroll={handleOnScroll}\n showsHorizontalScrollIndicator={false}\n scrollEventThrottle={Platform.select({ ios: 16, android: 2 })}\n decelerationRate={Platform.select({ ios: undefined, android: 'fast' })}\n snapToOffsets={snapOffsets}\n contentContainerStyle={{ paddingHorizontal: paddingSide }}\n style={[styles.container, containerStyle]}\n {...props}\n >\n {items.map((item, index) => (\n <PickerItem\n key={`picker-item-${item.label}-${index}`}\n label={item.label}\n index={index}\n currentIndex={currentIndex}\n itemWidth={itemWidth}\n onPress={() => handleOnPress(index)}\n itemContainerStyle={itemContainerStyle}\n itemTextStyle={itemTextStyle}\n selectedItemTextStyle={selectedItemTextStyle}\n />\n ))}\n </Animated.ScrollView>\n );\n}\n\ninterface PickerItemProps\n extends Pick<HorizontalPickerProps, 'itemContainerStyle' | 'itemTextStyle' | 'selectedItemTextStyle'> {\n label: PickerOption['label'];\n index: number;\n currentIndex: SharedValue<number>;\n itemWidth: number;\n onPress: () => void;\n}\n\nfunction PickerItem({\n label,\n index,\n currentIndex,\n itemWidth,\n onPress,\n itemContainerStyle,\n itemTextStyle,\n selectedItemTextStyle,\n}: PickerItemProps) {\n const [isFocused, setIsFocused] = useState(false);\n\n useAnimatedReaction(\n () => currentIndex.value === index,\n (shouldBeFocused, isCurrentFocused) => {\n if (shouldBeFocused !== isCurrentFocused) {\n runOnJS(setIsFocused)(shouldBeFocused);\n }\n },\n [index],\n );\n\n return (\n <Pressable onPress={onPress}>\n <View style={[{ width: itemWidth }, styles.itemContainer, itemContainerStyle]}>\n <Text style={isFocused ? [styles.itemTextSelected, selectedItemTextStyle] : [styles.itemText, itemTextStyle]}>\n {label}\n </Text>\n </View>\n </Pressable>\n );\n}\n\nconst styles = StyleSheet.create({\n container: {\n width: '100%',\n },\n itemContainer: {\n justifyContent: 'center',\n alignItems: 'center',\n height: 60,\n },\n itemText: {\n fontSize: 13,\n fontWeight: '700',\n color: '#C9CED9',\n },\n itemTextSelected: {\n fontSize: 15,\n fontWeight: '800',\n color: '#000000',\n },\n});\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "expo-horizontal-picker",
3
- "version": "0.1.2",
3
+ "version": "0.1.3",
4
4
  "description": "A performant horizontal picker component for React Native and Expo apps",
5
5
  "main": "build/index.js",
6
6
  "types": "build/index.d.ts",