react-native-dates-picker 0.0.9 → 0.1.1

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.
Files changed (156) hide show
  1. package/README.md +27 -28
  2. package/lib/commonjs/CalendarContext.js.map +1 -1
  3. package/lib/commonjs/DateTimePicker.js +15 -8
  4. package/lib/commonjs/DateTimePicker.js.map +1 -1
  5. package/lib/commonjs/components/Calendar.js +14 -12
  6. package/lib/commonjs/components/Calendar.js.map +1 -1
  7. package/lib/commonjs/components/DatePicker.js +46 -58
  8. package/lib/commonjs/components/DatePicker.js.map +1 -1
  9. package/lib/commonjs/components/Day.js +18 -16
  10. package/lib/commonjs/components/Day.js.map +1 -1
  11. package/lib/commonjs/components/DaySelector.js +12 -12
  12. package/lib/commonjs/components/DaySelector.js.map +1 -1
  13. package/lib/commonjs/components/Header.js +27 -25
  14. package/lib/commonjs/components/Header.js.map +1 -1
  15. package/lib/commonjs/components/MonthSelector.js +9 -7
  16. package/lib/commonjs/components/MonthSelector.js.map +1 -1
  17. package/lib/commonjs/components/TimeSelector.js +22 -17
  18. package/lib/commonjs/components/TimeSelector.js.map +1 -1
  19. package/lib/commonjs/components/WheelPicker/Wheel.js +9 -4
  20. package/lib/commonjs/components/WheelPicker/Wheel.js.map +1 -1
  21. package/lib/commonjs/components/WheelPicker/WheelNative.js +14 -47
  22. package/lib/commonjs/components/WheelPicker/WheelNative.js.map +1 -1
  23. package/lib/commonjs/components/WheelPicker/WheelNativePicker/index.js +10 -0
  24. package/lib/commonjs/components/WheelPicker/WheelNativePicker/index.js.map +1 -0
  25. package/lib/commonjs/components/WheelPicker/WheelNativePicker/wheel-picker-item.js +130 -0
  26. package/lib/commonjs/components/WheelPicker/WheelNativePicker/wheel-picker-item.js.map +1 -0
  27. package/lib/commonjs/components/WheelPicker/WheelNativePicker/wheel-picker.js +174 -0
  28. package/lib/commonjs/components/WheelPicker/WheelNativePicker/wheel-picker.js.map +1 -0
  29. package/lib/commonjs/components/WheelPicker/WheelWeb.js +51 -38
  30. package/lib/commonjs/components/WheelPicker/WheelWeb.js.map +1 -1
  31. package/lib/commonjs/components/WheelPicker/animated-math.js +26 -0
  32. package/lib/commonjs/components/WheelPicker/animated-math.js.map +1 -0
  33. package/lib/commonjs/components/WheelPicker/period-native.js +36 -0
  34. package/lib/commonjs/components/WheelPicker/period-native.js.map +1 -0
  35. package/lib/commonjs/components/WheelPicker/period-picker.js +19 -0
  36. package/lib/commonjs/components/WheelPicker/period-picker.js.map +1 -0
  37. package/lib/commonjs/components/WheelPicker/period-web.js +34 -0
  38. package/lib/commonjs/components/WheelPicker/period-web.js.map +1 -0
  39. package/lib/commonjs/components/YearSelector.js +10 -8
  40. package/lib/commonjs/components/YearSelector.js.map +1 -1
  41. package/lib/commonjs/enums.js +5 -4
  42. package/lib/commonjs/enums.js.map +1 -1
  43. package/lib/commonjs/utils.js +4 -1
  44. package/lib/commonjs/utils.js.map +1 -1
  45. package/lib/module/CalendarContext.js.map +1 -1
  46. package/lib/module/DateTimePicker.js +11 -6
  47. package/lib/module/DateTimePicker.js.map +1 -1
  48. package/lib/module/components/Calendar.js +4 -4
  49. package/lib/module/components/Calendar.js.map +1 -1
  50. package/lib/module/components/DatePicker.js +36 -50
  51. package/lib/module/components/DatePicker.js.map +1 -1
  52. package/lib/module/components/Day.js +8 -8
  53. package/lib/module/components/Day.js.map +1 -1
  54. package/lib/module/components/DaySelector.js +4 -4
  55. package/lib/module/components/DaySelector.js.map +1 -1
  56. package/lib/module/components/Header.js +3 -3
  57. package/lib/module/components/Header.js.map +1 -1
  58. package/lib/module/components/MonthSelector.js +3 -2
  59. package/lib/module/components/MonthSelector.js.map +1 -1
  60. package/lib/module/components/TimeSelector.js +10 -7
  61. package/lib/module/components/TimeSelector.js.map +1 -1
  62. package/lib/module/components/WheelPicker/Wheel.js +6 -3
  63. package/lib/module/components/WheelPicker/Wheel.js.map +1 -1
  64. package/lib/module/components/WheelPicker/WheelNative.js +10 -45
  65. package/lib/module/components/WheelPicker/WheelNative.js.map +1 -1
  66. package/lib/module/components/WheelPicker/WheelNativePicker/index.js +3 -0
  67. package/lib/module/components/WheelPicker/WheelNativePicker/index.js.map +1 -0
  68. package/lib/module/components/WheelPicker/WheelNativePicker/wheel-picker-item.js +122 -0
  69. package/lib/module/components/WheelPicker/WheelNativePicker/wheel-picker-item.js.map +1 -0
  70. package/lib/module/components/WheelPicker/WheelNativePicker/wheel-picker.js +165 -0
  71. package/lib/module/components/WheelPicker/WheelNativePicker/wheel-picker.js.map +1 -0
  72. package/lib/module/components/WheelPicker/WheelWeb.js +48 -37
  73. package/lib/module/components/WheelPicker/WheelWeb.js.map +1 -1
  74. package/lib/module/components/WheelPicker/animated-math.js +20 -0
  75. package/lib/module/components/WheelPicker/animated-math.js.map +1 -0
  76. package/lib/module/components/WheelPicker/period-native.js +27 -0
  77. package/lib/module/components/WheelPicker/period-native.js.map +1 -0
  78. package/lib/module/components/WheelPicker/period-picker.js +10 -0
  79. package/lib/module/components/WheelPicker/period-picker.js.map +1 -0
  80. package/lib/module/components/WheelPicker/period-web.js +26 -0
  81. package/lib/module/components/WheelPicker/period-web.js.map +1 -0
  82. package/lib/module/components/YearSelector.js +3 -3
  83. package/lib/module/components/YearSelector.js.map +1 -1
  84. package/lib/module/enums.js +4 -3
  85. package/lib/module/enums.js.map +1 -1
  86. package/lib/module/utils.js +4 -1
  87. package/lib/module/utils.js.map +1 -1
  88. package/lib/typescript/CalendarContext.d.ts.map +1 -1
  89. package/lib/typescript/DateTimePicker.d.ts +3 -3
  90. package/lib/typescript/DateTimePicker.d.ts.map +1 -1
  91. package/lib/typescript/components/Calendar.d.ts +3 -3
  92. package/lib/typescript/components/Calendar.d.ts.map +1 -1
  93. package/lib/typescript/components/DatePicker.d.ts +2 -2
  94. package/lib/typescript/components/DatePicker.d.ts.map +1 -1
  95. package/lib/typescript/components/Day.d.ts +4 -5
  96. package/lib/typescript/components/Day.d.ts.map +1 -1
  97. package/lib/typescript/components/DaySelector.d.ts +2 -2
  98. package/lib/typescript/components/DaySelector.d.ts.map +1 -1
  99. package/lib/typescript/components/Header.d.ts +3 -3
  100. package/lib/typescript/components/Header.d.ts.map +1 -1
  101. package/lib/typescript/components/MonthSelector.d.ts +2 -2
  102. package/lib/typescript/components/MonthSelector.d.ts.map +1 -1
  103. package/lib/typescript/components/TimeSelector.d.ts +2 -2
  104. package/lib/typescript/components/TimeSelector.d.ts.map +1 -1
  105. package/lib/typescript/components/WheelPicker/Wheel.d.ts +5 -6
  106. package/lib/typescript/components/WheelPicker/Wheel.d.ts.map +1 -1
  107. package/lib/typescript/components/WheelPicker/WheelNative.d.ts +7 -8
  108. package/lib/typescript/components/WheelPicker/WheelNative.d.ts.map +1 -1
  109. package/lib/typescript/components/WheelPicker/WheelNativePicker/index.d.ts +3 -0
  110. package/lib/typescript/components/WheelPicker/WheelNativePicker/index.d.ts.map +1 -0
  111. package/lib/typescript/components/WheelPicker/WheelNativePicker/wheel-picker-item.d.ts +16 -0
  112. package/lib/typescript/components/WheelPicker/WheelNativePicker/wheel-picker-item.d.ts.map +1 -0
  113. package/lib/typescript/components/WheelPicker/WheelNativePicker/wheel-picker.d.ts +19 -0
  114. package/lib/typescript/components/WheelPicker/WheelNativePicker/wheel-picker.d.ts.map +1 -0
  115. package/lib/typescript/components/WheelPicker/WheelWeb.d.ts +6 -5
  116. package/lib/typescript/components/WheelPicker/WheelWeb.d.ts.map +1 -1
  117. package/lib/typescript/components/WheelPicker/animated-math.d.ts +5 -0
  118. package/lib/typescript/components/WheelPicker/animated-math.d.ts.map +1 -0
  119. package/lib/typescript/components/WheelPicker/period-native.d.ts +8 -0
  120. package/lib/typescript/components/WheelPicker/period-native.d.ts.map +1 -0
  121. package/lib/typescript/components/WheelPicker/period-picker.d.ts +8 -0
  122. package/lib/typescript/components/WheelPicker/period-picker.d.ts.map +1 -0
  123. package/lib/typescript/components/WheelPicker/period-web.d.ts +8 -0
  124. package/lib/typescript/components/WheelPicker/period-web.d.ts.map +1 -0
  125. package/lib/typescript/components/YearSelector.d.ts +2 -2
  126. package/lib/typescript/components/YearSelector.d.ts.map +1 -1
  127. package/lib/typescript/enums.d.ts +2 -1
  128. package/lib/typescript/enums.d.ts.map +1 -1
  129. package/lib/typescript/types.d.ts +5 -2
  130. package/lib/typescript/types.d.ts.map +1 -1
  131. package/lib/typescript/utils.d.ts +2 -2
  132. package/lib/typescript/utils.d.ts.map +1 -1
  133. package/package.json +7 -6
  134. package/src/DateTimePicker.tsx +20 -15
  135. package/src/components/Calendar.tsx +8 -8
  136. package/src/components/DatePicker.tsx +38 -66
  137. package/src/components/Day.tsx +9 -9
  138. package/src/components/DaySelector.tsx +6 -6
  139. package/src/components/Header.tsx +5 -5
  140. package/src/components/MonthSelector.tsx +4 -3
  141. package/src/components/TimeSelector.tsx +11 -12
  142. package/src/components/WheelPicker/Wheel.tsx +10 -10
  143. package/src/components/WheelPicker/WheelNative.tsx +13 -47
  144. package/src/components/WheelPicker/WheelNativePicker/index.ts +3 -0
  145. package/src/components/WheelPicker/WheelNativePicker/wheel-picker-item.tsx +140 -0
  146. package/src/components/WheelPicker/WheelNativePicker/wheel-picker.tsx +220 -0
  147. package/src/components/WheelPicker/WheelWeb.tsx +92 -65
  148. package/src/components/WheelPicker/animated-math.ts +33 -0
  149. package/src/components/WheelPicker/period-native.tsx +39 -0
  150. package/src/components/WheelPicker/period-picker.tsx +16 -0
  151. package/src/components/WheelPicker/period-web.tsx +37 -0
  152. package/src/components/YearSelector.tsx +4 -4
  153. package/src/enums.ts +4 -3
  154. package/src/types.ts +6 -2
  155. package/src/utils.ts +3 -3
  156. /package/src/{CalendarContext.tsx → CalendarContext.ts} +0 -0
@@ -0,0 +1,140 @@
1
+ import React, { memo } from 'react';
2
+ import { Animated, Text, StyleSheet } from 'react-native';
3
+ import { useCalendarContext } from 'src/CalendarContext';
4
+ import { PickerOption } from 'src/types';
5
+
6
+ interface ItemProps {
7
+ option: PickerOption | null;
8
+ height: number;
9
+ index: number;
10
+ currentScrollIndex: Animated.AnimatedAddition<number>;
11
+ visibleRest: number;
12
+ rotationFunction: (x: number) => number;
13
+ opacityFunction: (x: number) => number;
14
+ scaleFunction: (x: number) => number;
15
+ }
16
+
17
+ const WheelPickerItem: React.FC<ItemProps> = ({
18
+ height,
19
+ option,
20
+ index,
21
+ visibleRest,
22
+ currentScrollIndex,
23
+ opacityFunction,
24
+ rotationFunction,
25
+ scaleFunction,
26
+ }) => {
27
+ const { theme } = useCalendarContext();
28
+ const relativeScrollIndex = Animated.subtract(index, currentScrollIndex);
29
+
30
+ const translateY = relativeScrollIndex.interpolate({
31
+ inputRange: (() => {
32
+ const range = [0];
33
+ for (let i = 1; i <= visibleRest + 1; i++) {
34
+ range.unshift(-i);
35
+ range.push(i);
36
+ }
37
+ return range;
38
+ })(),
39
+ outputRange: (() => {
40
+ const range = [0];
41
+ for (let i = 1; i <= visibleRest + 1; i++) {
42
+ let y =
43
+ (height / 2) * (1 - Math.sin(Math.PI / 2 - rotationFunction(i)));
44
+ for (let j = 1; j < i; j++) {
45
+ y += height * (1 - Math.sin(Math.PI / 2 - rotationFunction(j)));
46
+ }
47
+ range.unshift(y);
48
+ range.push(-y);
49
+ }
50
+ return range;
51
+ })(),
52
+ });
53
+
54
+ const opacity = relativeScrollIndex.interpolate({
55
+ inputRange: (() => {
56
+ const range = [0];
57
+ for (let i = 1; i <= visibleRest + 1; i++) {
58
+ range.unshift(-i);
59
+ range.push(i);
60
+ }
61
+ return range;
62
+ })(),
63
+ outputRange: (() => {
64
+ const range = [1];
65
+ for (let x = 1; x <= visibleRest + 1; x++) {
66
+ const y = opacityFunction(x);
67
+ range.unshift(y);
68
+ range.push(y);
69
+ }
70
+ return range;
71
+ })(),
72
+ });
73
+
74
+ const scale = relativeScrollIndex.interpolate({
75
+ inputRange: (() => {
76
+ const range = [0];
77
+ for (let i = 1; i <= visibleRest + 1; i++) {
78
+ range.unshift(-i);
79
+ range.push(i);
80
+ }
81
+ return range;
82
+ })(),
83
+ outputRange: (() => {
84
+ const range = [1.0];
85
+ for (let x = 1; x <= visibleRest + 1; x++) {
86
+ const y = scaleFunction(x);
87
+ range.unshift(y);
88
+ range.push(y);
89
+ }
90
+ return range;
91
+ })(),
92
+ });
93
+
94
+ const rotateX = relativeScrollIndex.interpolate({
95
+ inputRange: (() => {
96
+ const range = [0];
97
+ for (let i = 1; i <= visibleRest + 1; i++) {
98
+ range.unshift(-i);
99
+ range.push(i);
100
+ }
101
+ return range;
102
+ })(),
103
+ outputRange: (() => {
104
+ const range = ['0deg'];
105
+ for (let x = 1; x <= visibleRest + 1; x++) {
106
+ const y = rotationFunction(x);
107
+ range.unshift(`${y}deg`);
108
+ range.push(`${y}deg`);
109
+ }
110
+ return range;
111
+ })(),
112
+ });
113
+
114
+ return (
115
+ <Animated.View
116
+ style={[
117
+ styles.option,
118
+ {
119
+ height,
120
+ opacity,
121
+ transform: [{ translateY }, { rotateX }, { scale }],
122
+ },
123
+ theme.wheelPickerItemStyle,
124
+ ]}
125
+ >
126
+ <Text style={theme.wheelPickerTextStyle}>{option?.text}</Text>
127
+ </Animated.View>
128
+ );
129
+ };
130
+
131
+ const styles = StyleSheet.create({
132
+ option: {
133
+ alignItems: 'center',
134
+ justifyContent: 'center',
135
+ paddingHorizontal: 10,
136
+ zIndex: 100,
137
+ },
138
+ });
139
+
140
+ export default memo(WheelPickerItem);
@@ -0,0 +1,220 @@
1
+ import React, { useEffect, useMemo, useRef, useState, memo } from 'react';
2
+ import {
3
+ NativeSyntheticEvent,
4
+ NativeScrollEvent,
5
+ Animated,
6
+ View,
7
+ ViewProps,
8
+ FlatListProps,
9
+ FlatList,
10
+ Platform,
11
+ StyleSheet,
12
+ } from 'react-native';
13
+ import WheelPickerItem from './wheel-picker-item';
14
+ import { PickerOption } from 'src/types';
15
+ import { useCalendarContext } from 'src/CalendarContext';
16
+
17
+ interface Props {
18
+ value: number | string;
19
+ options: PickerOption[];
20
+ onChange: (index: number | string) => void;
21
+ itemHeight?: number;
22
+ containerProps?: Omit<ViewProps, 'style'>;
23
+ scaleFunction?: (x: number) => number;
24
+ rotationFunction?: (x: number) => number;
25
+ opacityFunction?: (x: number) => number;
26
+ visibleRest?: number;
27
+ decelerationRate?: 'normal' | 'fast' | number;
28
+ flatListProps?: Omit<FlatListProps<string | null>, 'data' | 'renderItem'>;
29
+ }
30
+
31
+ const WheelPicker: React.FC<Props> = ({
32
+ value,
33
+ options,
34
+ onChange,
35
+ itemHeight = 40,
36
+ scaleFunction = (x: number) => 1.0 ** x,
37
+ rotationFunction = (x: number) => 1 - Math.pow(1 / 2, x),
38
+ opacityFunction = (x: number) => Math.pow(1 / 3, x),
39
+ visibleRest = 2,
40
+ decelerationRate = 'normal',
41
+ containerProps = {},
42
+ flatListProps = {},
43
+ }) => {
44
+ const { theme } = useCalendarContext();
45
+ const momentumStarted = useRef(false);
46
+ const selectedIndex = options.findIndex((item) => item.value === value);
47
+
48
+ const flatListRef = useRef<FlatList>(null);
49
+ const [scrollY] = useState(new Animated.Value(selectedIndex * itemHeight));
50
+
51
+ const containerHeight = (1 + visibleRest * 2) * itemHeight;
52
+ const paddedOptions = useMemo(() => {
53
+ const array: (PickerOption | null)[] = [...options];
54
+ for (let i = 0; i < visibleRest; i++) {
55
+ array.unshift(null);
56
+ array.push(null);
57
+ }
58
+ return array;
59
+ }, [options, visibleRest]);
60
+
61
+ const offsets = useMemo(
62
+ () => [...Array(paddedOptions.length)].map((_, i) => i * itemHeight),
63
+ [paddedOptions, itemHeight]
64
+ );
65
+
66
+ const currentScrollIndex = useMemo(
67
+ () => Animated.add(Animated.divide(scrollY, itemHeight), visibleRest),
68
+ [visibleRest, scrollY, itemHeight]
69
+ );
70
+
71
+ const handleScrollEnd = (event: NativeSyntheticEvent<NativeScrollEvent>) => {
72
+ const offsetY = Math.min(
73
+ itemHeight * (options.length - 1),
74
+ Math.max(event.nativeEvent.contentOffset.y, 0)
75
+ );
76
+
77
+ let index = Math.floor(offsetY / itemHeight);
78
+ const remainder = offsetY % itemHeight;
79
+ if (remainder > itemHeight / 2) {
80
+ index++;
81
+ }
82
+
83
+ if (index !== selectedIndex) {
84
+ onChange(options[index]?.value || 0);
85
+ }
86
+ };
87
+
88
+ const handleMomentumScrollBegin = () => {
89
+ momentumStarted.current = true;
90
+ };
91
+
92
+ const handleMomentumScrollEnd = (
93
+ event: NativeSyntheticEvent<NativeScrollEvent>
94
+ ) => {
95
+ momentumStarted.current = false;
96
+ handleScrollEnd(event);
97
+ };
98
+
99
+ const handleScrollEndDrag = (
100
+ event: NativeSyntheticEvent<NativeScrollEvent>
101
+ ) => {
102
+ // Capture the offset value immediately
103
+ const offsetY = event.nativeEvent.contentOffset?.y;
104
+
105
+ // We'll start a short timer to see if momentum scroll begins
106
+ setTimeout(() => {
107
+ // If momentum scroll hasn't started within the timeout,
108
+ // then it was a slow scroll that won't trigger momentum
109
+ if (!momentumStarted.current && offsetY !== undefined) {
110
+ // Create a synthetic event with just the data we need
111
+ const syntheticEvent = {
112
+ nativeEvent: {
113
+ contentOffset: { y: offsetY },
114
+ },
115
+ };
116
+ handleScrollEnd(syntheticEvent as any);
117
+ }
118
+ }, 50);
119
+ };
120
+
121
+ useEffect(() => {
122
+ if (selectedIndex < 0 || selectedIndex >= options.length) {
123
+ throw new Error(
124
+ `Selected index ${selectedIndex} is out of bounds [0, ${
125
+ options.length - 1
126
+ }]`
127
+ );
128
+ }
129
+ }, [selectedIndex, options]);
130
+
131
+ /**
132
+ * If selectedIndex is changed from outside (not via onChange) we need to scroll to the specified index.
133
+ * This ensures that what the user sees as selected in the picker always corresponds to the value state.
134
+ */
135
+ useEffect(() => {
136
+ flatListRef.current?.scrollToIndex({
137
+ index: selectedIndex,
138
+ animated: Platform.OS === 'ios',
139
+ });
140
+ }, [selectedIndex, itemHeight]);
141
+
142
+ return (
143
+ <View
144
+ style={[
145
+ styles.container,
146
+ { height: containerHeight },
147
+ theme.wheelPickerContainerStyle,
148
+ ]}
149
+ {...containerProps}
150
+ >
151
+ <View
152
+ style={[
153
+ styles.selectedIndicator,
154
+ {
155
+ transform: [{ translateY: -itemHeight / 2 }],
156
+ height: itemHeight,
157
+ },
158
+ theme.wheelPickerSelectedIndicatorStyle,
159
+ ]}
160
+ />
161
+ <Animated.FlatList
162
+ {...flatListProps}
163
+ ref={flatListRef}
164
+ nestedScrollEnabled
165
+ style={styles.scrollView}
166
+ showsVerticalScrollIndicator={false}
167
+ onScroll={Animated.event(
168
+ [{ nativeEvent: { contentOffset: { y: scrollY } } }],
169
+ { useNativeDriver: true }
170
+ )}
171
+ onScrollEndDrag={handleScrollEndDrag}
172
+ onMomentumScrollBegin={handleMomentumScrollBegin}
173
+ onMomentumScrollEnd={handleMomentumScrollEnd}
174
+ snapToOffsets={offsets}
175
+ decelerationRate={decelerationRate}
176
+ initialScrollIndex={selectedIndex}
177
+ getItemLayout={(_, index) => ({
178
+ length: itemHeight,
179
+ offset: itemHeight * index,
180
+ index,
181
+ })}
182
+ data={paddedOptions}
183
+ keyExtractor={(item, index) =>
184
+ item ? `${item.value}-${item.text}-${index}` : `null-${index}`
185
+ }
186
+ renderItem={({ item: option, index }) => (
187
+ <WheelPickerItem
188
+ key={`option-${index}`}
189
+ index={index}
190
+ option={option}
191
+ height={itemHeight}
192
+ currentScrollIndex={currentScrollIndex}
193
+ scaleFunction={scaleFunction}
194
+ rotationFunction={rotationFunction}
195
+ opacityFunction={opacityFunction}
196
+ visibleRest={visibleRest}
197
+ />
198
+ )}
199
+ />
200
+ </View>
201
+ );
202
+ };
203
+
204
+ const styles = StyleSheet.create({
205
+ container: {
206
+ display: 'flex',
207
+ position: 'relative',
208
+ },
209
+ selectedIndicator: {
210
+ position: 'absolute',
211
+ width: '100%',
212
+ top: '50%',
213
+ },
214
+ scrollView: {
215
+ overflow: 'hidden',
216
+ flex: 1,
217
+ },
218
+ });
219
+
220
+ export default memo(WheelPicker);
@@ -1,36 +1,43 @@
1
- import { memo, useMemo, useRef } from 'react';
1
+ import React, { memo, useMemo, useRef } from 'react';
2
2
  import {
3
3
  Animated,
4
4
  PanResponder,
5
5
  StyleSheet,
6
6
  View,
7
7
  Platform,
8
+ Text,
8
9
  } from 'react-native';
9
- import { useCalendarContext } from '../../CalendarContext';
10
- import { sin } from './AnimatedMath';
11
- import { CALENDAR_HEIGHT } from '../../enums';
10
+ import { isEqual } from 'lodash';
11
+ import { CONTAINER_HEIGHT } from 'src/enums';
12
+ import { PickerOption } from 'src/types';
13
+ import { useCalendarContext } from 'src/CalendarContext';
14
+ import { sin } from './animated-math';
12
15
 
13
16
  interface WheelProps {
14
- value: number;
15
- setValue?: (value: number) => void;
16
- items: string[];
17
+ value: number | string;
18
+ setValue?: (value: any) => void;
19
+ items: PickerOption[];
17
20
  }
18
21
 
19
- const WheelWeb = ({ value, setValue = () => {}, items }: WheelProps) => {
20
- const { theme } = useCalendarContext();
22
+ const ITEM_HEIGHT = 44;
21
23
 
24
+ const WheelWeb: React.FC<WheelProps> = ({
25
+ value,
26
+ setValue = () => {},
27
+ items,
28
+ }) => {
29
+ const { theme } = useCalendarContext();
22
30
  const displayCount = 5;
23
31
  const translateY = useRef(new Animated.Value(0)).current;
24
32
  const renderCount =
25
33
  displayCount * 2 < items.length ? displayCount * 8 : displayCount * 2 - 1;
26
34
  const circular = items.length >= displayCount;
27
- const height = 130;
35
+ const height = 140;
28
36
  const radius = height / 2;
29
37
 
30
- const valueIndex = useMemo(
31
- () => items.indexOf(('0' + value).slice(-2)),
32
- [items, value]
33
- );
38
+ const valueIndex = useMemo(() => {
39
+ return items.findIndex((item) => item.value === value) || 0;
40
+ }, [items, value]);
34
41
 
35
42
  const panResponder = useMemo(() => {
36
43
  return PanResponder.create({
@@ -54,11 +61,12 @@ const WheelWeb = ({ value, setValue = () => {}, items }: WheelProps) => {
54
61
  else if (newValueIndex >= items.length)
55
62
  newValueIndex = items.length - 1;
56
63
 
57
- const newValue = items[newValueIndex] || '00';
58
- if (newValue === ('0' + value).slice(-2)) {
64
+ const newValue = items[newValueIndex];
65
+ if (newValue?.value === value) {
59
66
  translateY.setOffset(0);
60
67
  translateY.setValue(0);
61
- } else setValue(parseInt(newValue, 10));
68
+ } else if (newValue?.value) setValue(newValue.value);
69
+ else if (items[0]?.value) setValue(items[0].value);
62
70
  },
63
71
  });
64
72
  }, [
@@ -77,19 +85,21 @@ const WheelWeb = ({ value, setValue = () => {}, items }: WheelProps) => {
77
85
 
78
86
  return Array.from({ length: renderCount }, (_, index) => {
79
87
  let targetIndex = valueIndex + index - centerIndex;
80
- if (targetIndex < 0 || targetIndex >= items.length) {
81
- if (!circular) return 0;
88
+ if (circular)
89
+ targetIndex =
90
+ ((targetIndex % items.length) + items.length) % items.length;
91
+ else targetIndex = Math.max(0, Math.min(targetIndex, items.length - 1));
82
92
 
83
- targetIndex = (targetIndex + items.length) % items.length;
84
- }
85
- return items[targetIndex] || 0;
93
+ return items[targetIndex] || items[0];
86
94
  });
87
95
  }, [renderCount, valueIndex, items, circular]);
88
96
 
89
97
  const animatedAngles = useMemo(() => {
90
98
  //translateY.setValue(0);
91
99
  translateY.setOffset(0);
92
- const currentIndex = displayValues.indexOf(('0' + value).slice(-2));
100
+ const currentIndex = displayValues.findIndex(
101
+ (item) => item?.value === value
102
+ );
93
103
  return displayValues && displayValues.length > 0
94
104
  ? displayValues.map((_, index) =>
95
105
  translateY
@@ -112,54 +122,63 @@ const WheelWeb = ({ value, setValue = () => {}, items }: WheelProps) => {
112
122
  }, [displayValues, radius, value, displayCount, translateY]);
113
123
 
114
124
  return (
115
- <View style={[styles.container]} {...panResponder.panHandlers}>
125
+ <View
126
+ style={[defaultStyles.container, theme.wheelPickerContainerStyle]}
127
+ {...panResponder.panHandlers}
128
+ >
129
+ <View
130
+ style={[
131
+ defaultStyles.selectedIndicator,
132
+ {
133
+ transform: [{ translateY: -ITEM_HEIGHT / 2 }],
134
+ height: ITEM_HEIGHT,
135
+ },
136
+ theme.wheelPickerSelectedIndicatorStyle,
137
+ ]}
138
+ />
116
139
  {displayValues?.map((displayValue, index) => {
117
140
  const animatedAngle = animatedAngles[index];
118
141
  return (
119
- <Animated.Text
120
- key={`${displayValue}-${index}`}
121
- style={[
122
- { ...styles.wheelPickerText, ...theme?.wheelPickerTextStyle },
123
- // eslint-disable-next-line react-native/no-inline-styles
124
- {
125
- position: 'absolute',
126
- height: 25,
127
- transform: animatedAngle
128
- ? [
129
- {
130
- translateY: Animated.multiply(
131
- radius,
132
- sin(animatedAngle)
133
- ),
134
- },
135
- {
136
- rotateX: animatedAngle.interpolate({
137
- inputRange: [-Math.PI / 2, Math.PI / 2],
138
- outputRange: ['-89deg', '89deg'],
139
- extrapolate: 'clamp',
140
- }),
141
- },
142
- ]
143
- : [],
144
- opacity: displayValue !== ('0' + value).slice(-2) ? 0.3 : 1,
145
- },
146
- ]}
142
+ <Animated.View
143
+ key={`${displayValue?.text}-${index}`}
144
+ // eslint-disable-next-line react-native/no-inline-styles
145
+ style={{
146
+ position: 'absolute',
147
+ height: ITEM_HEIGHT - 10,
148
+ transform: animatedAngle
149
+ ? [
150
+ {
151
+ translateY: Animated.multiply(radius, sin(animatedAngle)),
152
+ },
153
+ {
154
+ rotateX: animatedAngle.interpolate({
155
+ inputRange: [-Math.PI / 2, Math.PI / 2],
156
+ outputRange: ['-89deg', '89deg'],
157
+ extrapolate: 'clamp',
158
+ }),
159
+ },
160
+ ]
161
+ : [],
162
+ opacity: displayValue?.value !== value ? 0.3 : 1,
163
+ }}
147
164
  >
148
- {displayValue}
149
- </Animated.Text>
165
+ <Text style={theme?.wheelPickerTextStyle}>
166
+ {displayValue?.text}
167
+ </Text>
168
+ </Animated.View>
150
169
  );
151
170
  })}
152
171
  </View>
153
172
  );
154
173
  };
155
174
 
156
- const styles = StyleSheet.create({
175
+ const defaultStyles = StyleSheet.create({
157
176
  container: {
158
177
  minWidth: 30,
159
178
  overflow: 'hidden',
160
179
  alignItems: 'center',
161
180
  justifyContent: 'center',
162
- height: CALENDAR_HEIGHT / 2,
181
+ height: CONTAINER_HEIGHT / 2,
163
182
  ...Platform.select({
164
183
  web: {
165
184
  cursor: 'pointer',
@@ -171,15 +190,23 @@ const styles = StyleSheet.create({
171
190
  justifyContent: 'space-between',
172
191
  alignItems: 'center',
173
192
  },
174
- wheelPickerText: {
175
- fontSize: 20,
176
- fontWeight: '500',
193
+ selectedIndicator: {
194
+ position: 'absolute',
195
+ width: '100%',
196
+ top: '50%',
177
197
  },
178
198
  });
179
199
 
180
- export default memo(WheelWeb, (prevProps, nextProps) => {
181
- return (
182
- prevProps.value === nextProps.value &&
183
- prevProps.setValue === nextProps.setValue
184
- );
185
- });
200
+ const customComparator = (
201
+ prev: Readonly<WheelProps>,
202
+ next: Readonly<WheelProps>
203
+ ) => {
204
+ const areEqual =
205
+ prev.value === next.value &&
206
+ prev.setValue === next.setValue &&
207
+ isEqual(prev.items, next.items);
208
+
209
+ return areEqual;
210
+ };
211
+
212
+ export default memo(WheelWeb, customComparator);
@@ -0,0 +1,33 @@
1
+ import { Animated } from 'react-native';
2
+
3
+ const FACTORIAL_3 = 3 * 2;
4
+ const FACTORIAL_5 = 5 * 4 * FACTORIAL_3;
5
+ const FACTORIAL_7 = 7 * 6 * FACTORIAL_5;
6
+
7
+ function sin(animated: Animated.Animated) {
8
+ const normalized = normalize(animated);
9
+ const square = Animated.multiply(normalized, normalized);
10
+ const pow3 = Animated.multiply(normalized, square);
11
+ const pow5 = Animated.multiply(pow3, square);
12
+ const pow7 = Animated.multiply(pow5, square);
13
+
14
+ return Animated.add(
15
+ Animated.add(normalized, Animated.multiply(pow3, -1 / FACTORIAL_3)),
16
+ Animated.add(
17
+ Animated.multiply(pow5, 1 / FACTORIAL_5),
18
+ Animated.multiply(pow7, -1 / FACTORIAL_7)
19
+ )
20
+ );
21
+ }
22
+
23
+ function normalize(animated: Animated.Animated): Animated.Animated {
24
+ return Animated.add(
25
+ Animated.modulo(Animated.add(animated, Math.PI), Math.PI * 2),
26
+ -Math.PI
27
+ ).interpolate({
28
+ inputRange: [-Math.PI, -Math.PI / 2, Math.PI / 2, Math.PI],
29
+ outputRange: [0, -Math.PI / 2, Math.PI / 2, 0],
30
+ });
31
+ }
32
+
33
+ export { sin, normalize };
@@ -0,0 +1,39 @@
1
+ import React, { memo } from 'react';
2
+ import WheelPicker from './WheelNativePicker';
3
+ import { PickerOption } from 'src/types';
4
+
5
+ interface PeriodProps {
6
+ value: string;
7
+ setValue?: (value: any) => void;
8
+ }
9
+
10
+ const options: PickerOption[] = [
11
+ { value: 'AM', text: 'AM' },
12
+ { value: 'PM', text: 'PM' },
13
+ ];
14
+
15
+ const PeriodNative: React.FC<PeriodProps> = ({
16
+ value,
17
+ setValue = () => {},
18
+ }) => {
19
+ return (
20
+ <WheelPicker
21
+ value={value}
22
+ options={options}
23
+ onChange={setValue}
24
+ itemHeight={44}
25
+ decelerationRate="fast"
26
+ />
27
+ );
28
+ };
29
+
30
+ const customComparator = (
31
+ prev: Readonly<PeriodProps>,
32
+ next: Readonly<PeriodProps>
33
+ ) => {
34
+ const areEqual = prev.value === next.value && prev.setValue === next.setValue;
35
+
36
+ return areEqual;
37
+ };
38
+
39
+ export default memo(PeriodNative, customComparator);
@@ -0,0 +1,16 @@
1
+ import React, { memo } from 'react';
2
+ import { Platform } from 'react-native';
3
+ import PeriodNative from './period-native';
4
+ import PeriodWeb from './period-web';
5
+
6
+ type PeriodProps = {
7
+ value: string;
8
+ setValue?: (value: any) => void;
9
+ };
10
+
11
+ const PeriodPicker: React.FC<PeriodProps> = (props) => {
12
+ const Component = Platform.OS === 'web' ? PeriodWeb : PeriodNative;
13
+ return <Component {...props} />;
14
+ };
15
+
16
+ export default memo(PeriodPicker);