react-native-dates-picker 0.1.2 → 0.1.4
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/lib/commonjs/DateTimePicker.js +144 -84
- package/lib/commonjs/DateTimePicker.js.map +1 -1
- package/lib/commonjs/components/Calendar.js +3 -5
- package/lib/commonjs/components/Calendar.js.map +1 -1
- package/lib/commonjs/components/DatePicker.js +16 -83
- package/lib/commonjs/components/DatePicker.js.map +1 -1
- package/lib/commonjs/components/Day.js +3 -8
- package/lib/commonjs/components/Day.js.map +1 -1
- package/lib/commonjs/components/TimeSelector.js +3 -3
- package/lib/commonjs/components/TimeSelector.js.map +1 -1
- package/lib/commonjs/components/WheelPicker/WheelNative.js +2 -2
- package/lib/commonjs/components/WheelPicker/WheelNative.js.map +1 -1
- package/lib/commonjs/components/WheelPicker/WheelNativePicker/wheel-picker-item.js +56 -72
- package/lib/commonjs/components/WheelPicker/WheelNativePicker/wheel-picker-item.js.map +1 -1
- package/lib/commonjs/components/WheelPicker/WheelNativePicker/wheel-picker.js +26 -48
- package/lib/commonjs/components/WheelPicker/WheelNativePicker/wheel-picker.js.map +1 -1
- package/lib/commonjs/components/WheelPicker/WheelWeb.js +5 -7
- package/lib/commonjs/components/WheelPicker/WheelWeb.js.map +1 -1
- package/lib/commonjs/components/WheelSelector.js +129 -0
- package/lib/commonjs/components/WheelSelector.js.map +1 -0
- package/lib/commonjs/utils.js +68 -0
- package/lib/commonjs/utils.js.map +1 -1
- package/lib/module/DateTimePicker.js +146 -86
- package/lib/module/DateTimePicker.js.map +1 -1
- package/lib/module/components/Calendar.js +3 -5
- package/lib/module/components/Calendar.js.map +1 -1
- package/lib/module/components/DatePicker.js +16 -81
- package/lib/module/components/DatePicker.js.map +1 -1
- package/lib/module/components/Day.js +3 -8
- package/lib/module/components/Day.js.map +1 -1
- package/lib/module/components/TimeSelector.js +3 -3
- package/lib/module/components/TimeSelector.js.map +1 -1
- package/lib/module/components/WheelPicker/WheelNative.js +2 -2
- package/lib/module/components/WheelPicker/WheelNative.js.map +1 -1
- package/lib/module/components/WheelPicker/WheelNativePicker/wheel-picker-item.js +57 -73
- package/lib/module/components/WheelPicker/WheelNativePicker/wheel-picker-item.js.map +1 -1
- package/lib/module/components/WheelPicker/WheelNativePicker/wheel-picker.js +26 -48
- package/lib/module/components/WheelPicker/WheelNativePicker/wheel-picker.js.map +1 -1
- package/lib/module/components/WheelPicker/WheelWeb.js +5 -7
- package/lib/module/components/WheelPicker/WheelWeb.js.map +1 -1
- package/lib/module/components/WheelSelector.js +120 -0
- package/lib/module/components/WheelSelector.js.map +1 -0
- package/lib/module/utils.js +65 -0
- package/lib/module/utils.js.map +1 -1
- package/lib/typescript/DateTimePicker.d.ts +2 -2
- package/lib/typescript/DateTimePicker.d.ts.map +1 -1
- package/lib/typescript/components/Calendar.d.ts.map +1 -1
- package/lib/typescript/components/DatePicker.d.ts +6 -1
- package/lib/typescript/components/DatePicker.d.ts.map +1 -1
- package/lib/typescript/components/Day.d.ts.map +1 -1
- package/lib/typescript/components/WheelPicker/Wheel.d.ts +1 -1
- package/lib/typescript/components/WheelPicker/WheelNative.d.ts +1 -1
- package/lib/typescript/components/WheelPicker/WheelNativePicker/wheel-picker-item.d.ts.map +1 -1
- package/lib/typescript/components/WheelPicker/WheelNativePicker/wheel-picker.d.ts +1 -2
- package/lib/typescript/components/WheelPicker/WheelNativePicker/wheel-picker.d.ts.map +1 -1
- package/lib/typescript/components/WheelPicker/WheelWeb.d.ts +1 -1
- package/lib/typescript/components/WheelPicker/WheelWeb.d.ts.map +1 -1
- package/lib/typescript/components/WheelSelector.d.ts +8 -0
- package/lib/typescript/components/WheelSelector.d.ts.map +1 -0
- package/lib/typescript/types.d.ts +1 -1
- package/lib/typescript/types.d.ts.map +1 -1
- package/lib/typescript/utils.d.ts +9 -0
- package/lib/typescript/utils.d.ts.map +1 -1
- package/package.json +2 -3
- package/src/DateTimePicker.tsx +226 -107
- package/src/components/Calendar.tsx +3 -11
- package/src/components/DatePicker.tsx +17 -138
- package/src/components/Day.tsx +1 -4
- package/src/components/TimeSelector.tsx +3 -3
- package/src/components/WheelPicker/Wheel.tsx +1 -1
- package/src/components/WheelPicker/WheelNative.tsx +3 -3
- package/src/components/WheelPicker/WheelNativePicker/wheel-picker-item.tsx +65 -74
- package/src/components/WheelPicker/WheelNativePicker/wheel-picker.tsx +20 -48
- package/src/components/WheelPicker/WheelWeb.tsx +6 -7
- package/src/components/WheelSelector.tsx +170 -0
- package/src/types.ts +1 -1
- package/src/utils.ts +77 -0
- package/lib/commonjs/components/WheelPicker/AnimatedMath.js +0 -26
- package/lib/commonjs/components/WheelPicker/AnimatedMath.js.map +0 -1
- package/lib/module/components/WheelPicker/AnimatedMath.js +0 -20
- package/lib/module/components/WheelPicker/AnimatedMath.js.map +0 -1
- package/lib/typescript/components/WheelPicker/AnimatedMath.d.ts +0 -5
- package/lib/typescript/components/WheelPicker/AnimatedMath.d.ts.map +0 -1
- package/src/components/WheelPicker/AnimatedMath.tsx +0 -33
package/src/components/Day.tsx
CHANGED
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
import React, { memo, useCallback } from 'react';
|
|
2
2
|
import { View, Text, Pressable, StyleSheet } from 'react-native';
|
|
3
|
-
import { isEqual } from 'lodash';
|
|
4
3
|
import { CalendarThemeProps, IDayObject } from '../types';
|
|
5
4
|
import { CONTAINER_HEIGHT } from '../enums';
|
|
6
|
-
import { addColorAlpha } from '../utils';
|
|
5
|
+
import { addColorAlpha, isEqual } from '../utils';
|
|
7
6
|
|
|
8
7
|
export const daySize = 46;
|
|
9
8
|
|
|
@@ -97,7 +96,6 @@ const Day: React.FC<Props> = ({
|
|
|
97
96
|
<View
|
|
98
97
|
style={[
|
|
99
98
|
style.rangeRoot,
|
|
100
|
-
// eslint-disable-next-line react-native/no-inline-styles
|
|
101
99
|
{
|
|
102
100
|
left: '50%',
|
|
103
101
|
backgroundColor: rangeRootBackground,
|
|
@@ -110,7 +108,6 @@ const Day: React.FC<Props> = ({
|
|
|
110
108
|
<View
|
|
111
109
|
style={[
|
|
112
110
|
style.rangeRoot,
|
|
113
|
-
// eslint-disable-next-line react-native/no-inline-styles
|
|
114
111
|
{
|
|
115
112
|
right: '50%',
|
|
116
113
|
backgroundColor: rangeRootBackground,
|
|
@@ -35,7 +35,7 @@ const TimeSelector: React.FC = () => {
|
|
|
35
35
|
<Wheel
|
|
36
36
|
value={hour}
|
|
37
37
|
items={hours}
|
|
38
|
-
|
|
38
|
+
onChange={(value) => handleChange(value, 'hour')}
|
|
39
39
|
/>
|
|
40
40
|
</View>
|
|
41
41
|
<Text
|
|
@@ -50,7 +50,7 @@ const TimeSelector: React.FC = () => {
|
|
|
50
50
|
<Wheel
|
|
51
51
|
value={minute}
|
|
52
52
|
items={minutes}
|
|
53
|
-
|
|
53
|
+
onChange={(value) => handleChange(value, 'minute')}
|
|
54
54
|
/>
|
|
55
55
|
</View>
|
|
56
56
|
<Text
|
|
@@ -65,7 +65,7 @@ const TimeSelector: React.FC = () => {
|
|
|
65
65
|
<Wheel
|
|
66
66
|
value={second}
|
|
67
67
|
items={seconds}
|
|
68
|
-
|
|
68
|
+
onChange={(value) => handleChange(value, 'second')}
|
|
69
69
|
/>
|
|
70
70
|
</View>
|
|
71
71
|
</View>
|
|
@@ -4,20 +4,20 @@ import { PickerOption } from '../../types';
|
|
|
4
4
|
|
|
5
5
|
interface WheelProps {
|
|
6
6
|
value: number | string;
|
|
7
|
-
|
|
7
|
+
onChange?: (value: any) => void;
|
|
8
8
|
items: PickerOption[];
|
|
9
9
|
}
|
|
10
10
|
|
|
11
11
|
const WheelNative: React.FC<WheelProps> = ({
|
|
12
12
|
value,
|
|
13
|
-
|
|
13
|
+
onChange = () => {},
|
|
14
14
|
items,
|
|
15
15
|
}) => {
|
|
16
16
|
return (
|
|
17
17
|
<WheelPicker
|
|
18
18
|
value={value}
|
|
19
19
|
options={items}
|
|
20
|
-
onChange={
|
|
20
|
+
onChange={onChange}
|
|
21
21
|
itemHeight={44}
|
|
22
22
|
decelerationRate="fast"
|
|
23
23
|
/>
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import React, { memo } from 'react';
|
|
1
|
+
import React, { memo, useMemo } from 'react';
|
|
2
2
|
import { Animated, Text, StyleSheet } from 'react-native';
|
|
3
3
|
import { useCalendarContext } from '../../../CalendarContext';
|
|
4
4
|
import { PickerOption } from '../../../types';
|
|
@@ -25,90 +25,81 @@ const WheelPickerItem: React.FC<ItemProps> = ({
|
|
|
25
25
|
scaleFunction,
|
|
26
26
|
}) => {
|
|
27
27
|
const { theme } = useCalendarContext();
|
|
28
|
-
const relativeScrollIndex =
|
|
28
|
+
const relativeScrollIndex = useMemo(
|
|
29
|
+
() => Animated.subtract(index, currentScrollIndex),
|
|
30
|
+
[index, currentScrollIndex]
|
|
31
|
+
);
|
|
29
32
|
|
|
30
|
-
const
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
y += height * (1 - Math.sin(Math.PI / 2 - rotationFunction(j)));
|
|
46
|
-
}
|
|
47
|
-
range.unshift(y);
|
|
48
|
-
range.push(-y);
|
|
33
|
+
const interpolInputRange = useMemo(() => {
|
|
34
|
+
const range = [0];
|
|
35
|
+
for (let i = 1; i <= visibleRest + 1; i++) {
|
|
36
|
+
range.unshift(-i);
|
|
37
|
+
range.push(i);
|
|
38
|
+
}
|
|
39
|
+
return range;
|
|
40
|
+
}, [visibleRest]);
|
|
41
|
+
|
|
42
|
+
const translateYOutputRange = useMemo(() => {
|
|
43
|
+
const range = [0];
|
|
44
|
+
for (let i = 1; i <= visibleRest + 1; i++) {
|
|
45
|
+
let y = (height / 2) * (1 - Math.sin(Math.PI / 2 - rotationFunction(i)));
|
|
46
|
+
for (let j = 1; j < i; j++) {
|
|
47
|
+
y += height * (1 - Math.sin(Math.PI / 2 - rotationFunction(j)));
|
|
49
48
|
}
|
|
50
|
-
|
|
51
|
-
|
|
49
|
+
range.unshift(y);
|
|
50
|
+
range.push(-y);
|
|
51
|
+
}
|
|
52
|
+
return range;
|
|
53
|
+
}, [height, visibleRest, rotationFunction]);
|
|
54
|
+
|
|
55
|
+
const opacityOutputRange = useMemo(() => {
|
|
56
|
+
const range = [1];
|
|
57
|
+
for (let x = 1; x <= visibleRest + 1; x++) {
|
|
58
|
+
const val = opacityFunction(x);
|
|
59
|
+
range.unshift(val);
|
|
60
|
+
range.push(val);
|
|
61
|
+
}
|
|
62
|
+
return range;
|
|
63
|
+
}, [visibleRest, opacityFunction]);
|
|
64
|
+
|
|
65
|
+
const scaleOutputRange = useMemo(() => {
|
|
66
|
+
const range = [1];
|
|
67
|
+
for (let x = 1; x <= visibleRest + 1; x++) {
|
|
68
|
+
const val = scaleFunction(x);
|
|
69
|
+
range.unshift(val);
|
|
70
|
+
range.push(val);
|
|
71
|
+
}
|
|
72
|
+
return range;
|
|
73
|
+
}, [visibleRest, scaleFunction]);
|
|
74
|
+
|
|
75
|
+
const rotateXOutputRange = useMemo(() => {
|
|
76
|
+
const range = ['0deg'];
|
|
77
|
+
for (let x = 1; x <= visibleRest + 1; x++) {
|
|
78
|
+
const deg = `${rotationFunction(x)}deg`;
|
|
79
|
+
range.unshift(deg);
|
|
80
|
+
range.push(deg);
|
|
81
|
+
}
|
|
82
|
+
return range;
|
|
83
|
+
}, [visibleRest, rotationFunction]);
|
|
84
|
+
|
|
85
|
+
const translateY = relativeScrollIndex.interpolate({
|
|
86
|
+
inputRange: interpolInputRange,
|
|
87
|
+
outputRange: translateYOutputRange,
|
|
52
88
|
});
|
|
53
89
|
|
|
54
90
|
const opacity = relativeScrollIndex.interpolate({
|
|
55
|
-
inputRange:
|
|
56
|
-
|
|
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
|
-
})(),
|
|
91
|
+
inputRange: interpolInputRange,
|
|
92
|
+
outputRange: opacityOutputRange,
|
|
72
93
|
});
|
|
73
94
|
|
|
74
95
|
const scale = relativeScrollIndex.interpolate({
|
|
75
|
-
inputRange:
|
|
76
|
-
|
|
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
|
-
})(),
|
|
96
|
+
inputRange: interpolInputRange,
|
|
97
|
+
outputRange: scaleOutputRange,
|
|
92
98
|
});
|
|
93
99
|
|
|
94
100
|
const rotateX = relativeScrollIndex.interpolate({
|
|
95
|
-
inputRange:
|
|
96
|
-
|
|
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
|
-
})(),
|
|
101
|
+
inputRange: interpolInputRange,
|
|
102
|
+
outputRange: rotateXOutputRange,
|
|
112
103
|
});
|
|
113
104
|
|
|
114
105
|
return (
|
|
@@ -5,7 +5,6 @@ import {
|
|
|
5
5
|
Animated,
|
|
6
6
|
View,
|
|
7
7
|
ViewProps,
|
|
8
|
-
FlatListProps,
|
|
9
8
|
FlatList,
|
|
10
9
|
Platform,
|
|
11
10
|
StyleSheet,
|
|
@@ -25,7 +24,6 @@ interface Props {
|
|
|
25
24
|
opacityFunction?: (x: number) => number;
|
|
26
25
|
visibleRest?: number;
|
|
27
26
|
decelerationRate?: 'normal' | 'fast' | number;
|
|
28
|
-
flatListProps?: Omit<FlatListProps<string | null>, 'data' | 'renderItem'>;
|
|
29
27
|
}
|
|
30
28
|
|
|
31
29
|
const WheelPicker: React.FC<Props> = ({
|
|
@@ -39,11 +37,10 @@ const WheelPicker: React.FC<Props> = ({
|
|
|
39
37
|
visibleRest = 2,
|
|
40
38
|
decelerationRate = 'normal',
|
|
41
39
|
containerProps = {},
|
|
42
|
-
flatListProps = {},
|
|
43
40
|
}) => {
|
|
44
41
|
const { theme } = useCalendarContext();
|
|
45
|
-
const momentumStarted = useRef(false);
|
|
46
42
|
const selectedIndex = options.findIndex((item) => item.value === value);
|
|
43
|
+
const timerRef = useRef<NodeJS.Timeout>();
|
|
47
44
|
|
|
48
45
|
const flatListRef = useRef<FlatList>(null);
|
|
49
46
|
const [scrollY] = useState(new Animated.Value(selectedIndex * itemHeight));
|
|
@@ -69,54 +66,35 @@ const WheelPicker: React.FC<Props> = ({
|
|
|
69
66
|
);
|
|
70
67
|
|
|
71
68
|
const handleScrollEnd = (event: NativeSyntheticEvent<NativeScrollEvent>) => {
|
|
69
|
+
if (timerRef.current) clearTimeout(timerRef.current);
|
|
72
70
|
const offsetY = Math.min(
|
|
73
71
|
itemHeight * (options.length - 1),
|
|
74
72
|
Math.max(event.nativeEvent.contentOffset.y, 0)
|
|
75
73
|
);
|
|
76
|
-
|
|
77
74
|
let index = Math.floor(offsetY / itemHeight);
|
|
78
75
|
const remainder = offsetY % itemHeight;
|
|
79
76
|
if (remainder > itemHeight / 2) {
|
|
80
77
|
index++;
|
|
81
78
|
}
|
|
82
|
-
|
|
79
|
+
const value = options[index]?.value || 0;
|
|
83
80
|
if (index !== selectedIndex) {
|
|
84
|
-
|
|
81
|
+
timerRef.current = setTimeout(() => {
|
|
82
|
+
onChange(value);
|
|
83
|
+
clearTimeout(timerRef.current!);
|
|
84
|
+
timerRef.current = undefined;
|
|
85
|
+
}, 100);
|
|
85
86
|
}
|
|
86
87
|
};
|
|
87
88
|
|
|
88
|
-
const
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
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
|
-
};
|
|
89
|
+
const scrollEvent = useMemo(
|
|
90
|
+
() =>
|
|
91
|
+
Animated.event([{ nativeEvent: { contentOffset: { y: scrollY } } }], {
|
|
92
|
+
useNativeDriver: true,
|
|
93
|
+
listener: handleScrollEnd,
|
|
94
|
+
}),
|
|
95
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
96
|
+
[]
|
|
97
|
+
);
|
|
120
98
|
|
|
121
99
|
useEffect(() => {
|
|
122
100
|
if (selectedIndex < 0 || selectedIndex >= options.length) {
|
|
@@ -159,18 +137,12 @@ const WheelPicker: React.FC<Props> = ({
|
|
|
159
137
|
]}
|
|
160
138
|
/>
|
|
161
139
|
<Animated.FlatList
|
|
162
|
-
{...flatListProps}
|
|
163
140
|
ref={flatListRef}
|
|
164
141
|
nestedScrollEnabled
|
|
165
142
|
style={styles.scrollView}
|
|
166
143
|
showsVerticalScrollIndicator={false}
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
{ useNativeDriver: true }
|
|
170
|
-
)}
|
|
171
|
-
onScrollEndDrag={handleScrollEndDrag}
|
|
172
|
-
onMomentumScrollBegin={handleMomentumScrollBegin}
|
|
173
|
-
onMomentumScrollEnd={handleMomentumScrollEnd}
|
|
144
|
+
scrollEventThrottle={16}
|
|
145
|
+
onScroll={scrollEvent}
|
|
174
146
|
snapToOffsets={offsets}
|
|
175
147
|
decelerationRate={decelerationRate}
|
|
176
148
|
initialScrollIndex={selectedIndex}
|
|
@@ -181,7 +153,7 @@ const WheelPicker: React.FC<Props> = ({
|
|
|
181
153
|
})}
|
|
182
154
|
data={paddedOptions}
|
|
183
155
|
keyExtractor={(item, index) =>
|
|
184
|
-
item ? `${item.value}-${item.text}-${index}` :
|
|
156
|
+
item ? `${item.value}-${item.text}-${index}` : `-${index}`
|
|
185
157
|
}
|
|
186
158
|
renderItem={({ item: option, index }) => (
|
|
187
159
|
<WheelPickerItem
|
|
@@ -15,7 +15,7 @@ import { sin } from './animated-math';
|
|
|
15
15
|
|
|
16
16
|
interface WheelProps {
|
|
17
17
|
value: number | string;
|
|
18
|
-
|
|
18
|
+
onChange?: (value: any) => void;
|
|
19
19
|
items: PickerOption[];
|
|
20
20
|
}
|
|
21
21
|
|
|
@@ -23,7 +23,7 @@ const ITEM_HEIGHT = 44;
|
|
|
23
23
|
|
|
24
24
|
const WheelWeb: React.FC<WheelProps> = ({
|
|
25
25
|
value,
|
|
26
|
-
|
|
26
|
+
onChange = () => {},
|
|
27
27
|
items,
|
|
28
28
|
}) => {
|
|
29
29
|
const { theme } = useCalendarContext();
|
|
@@ -65,15 +65,15 @@ const WheelWeb: React.FC<WheelProps> = ({
|
|
|
65
65
|
if (newValue?.value === value) {
|
|
66
66
|
translateY.setOffset(0);
|
|
67
67
|
translateY.setValue(0);
|
|
68
|
-
} else if (newValue?.value)
|
|
69
|
-
else if (items[0]?.value)
|
|
68
|
+
} else if (newValue?.value) onChange(newValue.value);
|
|
69
|
+
else if (items[0]?.value) onChange(items[0].value);
|
|
70
70
|
},
|
|
71
71
|
});
|
|
72
72
|
}, [
|
|
73
73
|
circular,
|
|
74
74
|
displayCount,
|
|
75
75
|
radius,
|
|
76
|
-
|
|
76
|
+
onChange,
|
|
77
77
|
value,
|
|
78
78
|
valueIndex,
|
|
79
79
|
items,
|
|
@@ -141,7 +141,6 @@ const WheelWeb: React.FC<WheelProps> = ({
|
|
|
141
141
|
return (
|
|
142
142
|
<Animated.View
|
|
143
143
|
key={`${displayValue?.text}-${index}`}
|
|
144
|
-
// eslint-disable-next-line react-native/no-inline-styles
|
|
145
144
|
style={{
|
|
146
145
|
position: 'absolute',
|
|
147
146
|
height: ITEM_HEIGHT - 10,
|
|
@@ -203,7 +202,7 @@ const customComparator = (
|
|
|
203
202
|
) => {
|
|
204
203
|
const areEqual =
|
|
205
204
|
prev.value === next.value &&
|
|
206
|
-
prev.
|
|
205
|
+
prev.onChange === next.onChange &&
|
|
207
206
|
isEqual(prev.items, next.items);
|
|
208
207
|
|
|
209
208
|
return areEqual;
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
import React, { useCallback, useEffect, useMemo, useRef } from 'react';
|
|
2
|
+
import { View, StyleSheet } from 'react-native';
|
|
3
|
+
import { useCalendarContext } from '../CalendarContext';
|
|
4
|
+
import {
|
|
5
|
+
getFormatted,
|
|
6
|
+
getParsedDate,
|
|
7
|
+
getDaysNumInMonth,
|
|
8
|
+
getTimeRange,
|
|
9
|
+
getDate,
|
|
10
|
+
} from '../utils';
|
|
11
|
+
import Wheel from './WheelPicker/Wheel';
|
|
12
|
+
import { OrderedDateParts } from '../types';
|
|
13
|
+
|
|
14
|
+
function createNumberList(start: number, end: number, first: number = 1) {
|
|
15
|
+
return new Array(end - start).fill(0).map((_, index) => ({
|
|
16
|
+
value: index + first + start,
|
|
17
|
+
text: String(index + first + start).padStart(2, '0'),
|
|
18
|
+
}));
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
interface WheelSelectorProps {
|
|
22
|
+
type: OrderedDateParts;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const WheelSelector: React.FC<WheelSelectorProps> = ({ type }) => {
|
|
26
|
+
const {
|
|
27
|
+
date,
|
|
28
|
+
currentDate,
|
|
29
|
+
onSelectYear,
|
|
30
|
+
onSelectMonth,
|
|
31
|
+
onSelectDate,
|
|
32
|
+
minDate = '1900-01-01',
|
|
33
|
+
maxDate = '2099-12-31',
|
|
34
|
+
} = useCalendarContext();
|
|
35
|
+
const latestValues = useRef({ date, currentDate });
|
|
36
|
+
const { year, month } = useMemo(() => {
|
|
37
|
+
const cd = getDate(currentDate);
|
|
38
|
+
if (cd.isAfter(getDate(maxDate))) return getParsedDate(maxDate);
|
|
39
|
+
if (cd.isBefore(getDate(minDate))) return getParsedDate(minDate);
|
|
40
|
+
return getParsedDate(cd);
|
|
41
|
+
}, [currentDate, maxDate, minDate]);
|
|
42
|
+
|
|
43
|
+
const { year: startYear, month: startMonth } = getParsedDate(minDate);
|
|
44
|
+
const { year: endYear, month: endMonth } = getParsedDate(maxDate);
|
|
45
|
+
const { hour, minute, second } = getParsedDate(currentDate);
|
|
46
|
+
const years = createNumberList(startYear - 1, endYear);
|
|
47
|
+
const months = useMemo(
|
|
48
|
+
() =>
|
|
49
|
+
createNumberList(
|
|
50
|
+
startYear === year ? startMonth : 0,
|
|
51
|
+
endYear === year ? endMonth + 1 : 12
|
|
52
|
+
),
|
|
53
|
+
[endMonth, endYear, startMonth, startYear, year]
|
|
54
|
+
);
|
|
55
|
+
|
|
56
|
+
const days = useMemo(
|
|
57
|
+
() => getDaysNumInMonth(year, month + 1, minDate, maxDate),
|
|
58
|
+
[year, month, minDate, maxDate]
|
|
59
|
+
);
|
|
60
|
+
const hours = useMemo(
|
|
61
|
+
() =>
|
|
62
|
+
createNumberList(
|
|
63
|
+
...getTimeRange(currentDate, minDate, maxDate, 'hour'),
|
|
64
|
+
0
|
|
65
|
+
),
|
|
66
|
+
[currentDate, maxDate, minDate]
|
|
67
|
+
);
|
|
68
|
+
const minutes = useMemo(
|
|
69
|
+
() =>
|
|
70
|
+
createNumberList(
|
|
71
|
+
...getTimeRange(currentDate, minDate, maxDate, 'minute'),
|
|
72
|
+
0
|
|
73
|
+
),
|
|
74
|
+
[currentDate, maxDate, minDate]
|
|
75
|
+
);
|
|
76
|
+
const seconds = useMemo(
|
|
77
|
+
() =>
|
|
78
|
+
createNumberList(
|
|
79
|
+
...getTimeRange(currentDate, minDate, maxDate, 'second'),
|
|
80
|
+
0
|
|
81
|
+
),
|
|
82
|
+
[currentDate, maxDate, minDate]
|
|
83
|
+
);
|
|
84
|
+
|
|
85
|
+
const handleChangeDate = useCallback(
|
|
86
|
+
(value: number, type: 'date' | 'hour' | 'minute' | 'second') => {
|
|
87
|
+
const { date: latestDate, currentDate: latestCurrentDate } =
|
|
88
|
+
latestValues.current;
|
|
89
|
+
const newDate = getDate(latestDate || latestCurrentDate)[type](value);
|
|
90
|
+
onSelectDate(getFormatted(newDate));
|
|
91
|
+
},
|
|
92
|
+
[onSelectDate]
|
|
93
|
+
);
|
|
94
|
+
|
|
95
|
+
const renderItem = () => {
|
|
96
|
+
if (type === 'year')
|
|
97
|
+
return (
|
|
98
|
+
<Wheel
|
|
99
|
+
value={year}
|
|
100
|
+
items={years}
|
|
101
|
+
key="year"
|
|
102
|
+
onChange={(value) => onSelectYear(value)}
|
|
103
|
+
/>
|
|
104
|
+
);
|
|
105
|
+
if (type === 'month')
|
|
106
|
+
return (
|
|
107
|
+
<Wheel
|
|
108
|
+
key="month"
|
|
109
|
+
value={month + 1}
|
|
110
|
+
items={months}
|
|
111
|
+
onChange={(value) => onSelectMonth(value - 1)}
|
|
112
|
+
/>
|
|
113
|
+
);
|
|
114
|
+
if (type === 'day')
|
|
115
|
+
return (
|
|
116
|
+
<Wheel
|
|
117
|
+
key="day"
|
|
118
|
+
value={getDate(currentDate).date()}
|
|
119
|
+
items={days}
|
|
120
|
+
onChange={(value) => handleChangeDate(value, 'date')}
|
|
121
|
+
/>
|
|
122
|
+
);
|
|
123
|
+
if (type === 'hour')
|
|
124
|
+
return (
|
|
125
|
+
<Wheel
|
|
126
|
+
key="hour"
|
|
127
|
+
value={hour}
|
|
128
|
+
items={hours}
|
|
129
|
+
onChange={(value) => handleChangeDate(value, 'hour')}
|
|
130
|
+
/>
|
|
131
|
+
);
|
|
132
|
+
if (type === 'minute')
|
|
133
|
+
return (
|
|
134
|
+
<Wheel
|
|
135
|
+
key="minute"
|
|
136
|
+
value={minute}
|
|
137
|
+
items={minutes}
|
|
138
|
+
onChange={(value) => handleChangeDate(value, 'minute')}
|
|
139
|
+
/>
|
|
140
|
+
);
|
|
141
|
+
if (type === 'second')
|
|
142
|
+
return (
|
|
143
|
+
<Wheel
|
|
144
|
+
key="second"
|
|
145
|
+
value={second}
|
|
146
|
+
items={seconds}
|
|
147
|
+
onChange={(value) => handleChangeDate(value, 'second')}
|
|
148
|
+
/>
|
|
149
|
+
);
|
|
150
|
+
return null;
|
|
151
|
+
};
|
|
152
|
+
|
|
153
|
+
useEffect(() => {
|
|
154
|
+
latestValues.current = { date, currentDate };
|
|
155
|
+
}, [date, currentDate]);
|
|
156
|
+
|
|
157
|
+
return (
|
|
158
|
+
<View style={styles.wheelContainer} key={'date-' + type}>
|
|
159
|
+
{renderItem()}
|
|
160
|
+
</View>
|
|
161
|
+
);
|
|
162
|
+
};
|
|
163
|
+
|
|
164
|
+
const styles = StyleSheet.create({
|
|
165
|
+
wheelContainer: {
|
|
166
|
+
flex: 1,
|
|
167
|
+
},
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
export default WheelSelector;
|
package/src/types.ts
CHANGED
|
@@ -13,7 +13,7 @@ export type LocalState = {
|
|
|
13
13
|
date: DateType;
|
|
14
14
|
startDate: DateType;
|
|
15
15
|
endDate: DateType;
|
|
16
|
-
dates
|
|
16
|
+
dates?: DateType[];
|
|
17
17
|
calendarView: CalendarViews;
|
|
18
18
|
currentDate: DateType; // used for latest state of calendar based on Month and Year
|
|
19
19
|
currentYear: number;
|
package/src/utils.ts
CHANGED
|
@@ -300,3 +300,80 @@ export function addColorAlpha(color: string | undefined, opacity: number) {
|
|
|
300
300
|
|
|
301
301
|
return color + opacityHex;
|
|
302
302
|
}
|
|
303
|
+
|
|
304
|
+
/**
|
|
305
|
+
|
|
306
|
+
深度比较两个值是否相等
|
|
307
|
+
@param {any} value
|
|
308
|
+
@param {any} other
|
|
309
|
+
@returns {boolean}
|
|
310
|
+
*/
|
|
311
|
+
export const isEqual = (value: any, other: any) => {
|
|
312
|
+
// 如果是同一个引用
|
|
313
|
+
if (value === other) {
|
|
314
|
+
// 排除 0 和 -0 的特殊情况
|
|
315
|
+
return value !== 0 || 1 / value === 1 / other;
|
|
316
|
+
}
|
|
317
|
+
// 如果有一个是 null/undefined,或者类型不同
|
|
318
|
+
if (value == null || other == null || typeof value !== typeof other) {
|
|
319
|
+
// 同时要特殊处理 NaN
|
|
320
|
+
return Number.isNaN(value) && Number.isNaN(other);
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
// 如果是对象或函数,则继续
|
|
324
|
+
if (typeof value === 'object' || typeof value === 'function') {
|
|
325
|
+
// Date
|
|
326
|
+
if (value instanceof Date && other instanceof Date) {
|
|
327
|
+
return value.getTime() === other.getTime();
|
|
328
|
+
}
|
|
329
|
+
// RegExp
|
|
330
|
+
if (value instanceof RegExp && other instanceof RegExp) {
|
|
331
|
+
return value.source === other.source && value.flags === other.flags;
|
|
332
|
+
}
|
|
333
|
+
// Array 或者普通对象
|
|
334
|
+
if (
|
|
335
|
+
(Array.isArray(value) && Array.isArray(other)) ||
|
|
336
|
+
(Object.prototype.toString.call(value) === '[object Object]' &&
|
|
337
|
+
Object.prototype.toString.call(other) === '[object Object]')
|
|
338
|
+
) {
|
|
339
|
+
// 先比较键的数量是否一致
|
|
340
|
+
const valueKeys = Object.keys(value);
|
|
341
|
+
const otherKeys = Object.keys(other);
|
|
342
|
+
if (valueKeys.length !== otherKeys.length) {
|
|
343
|
+
return false;
|
|
344
|
+
}
|
|
345
|
+
// 递归比较每个键对应的值
|
|
346
|
+
for (const key of valueKeys) {
|
|
347
|
+
if (
|
|
348
|
+
!Object.prototype.hasOwnProperty.call(other, key) ||
|
|
349
|
+
!isEqual(value[key], other[key])
|
|
350
|
+
) {
|
|
351
|
+
return false;
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
return true;
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
// 如果以上情况都不满足,则直接比较
|
|
359
|
+
return false;
|
|
360
|
+
};
|
|
361
|
+
|
|
362
|
+
export function throttle<T extends (...args: any[]) => any>(
|
|
363
|
+
func: T,
|
|
364
|
+
limit: number = 200
|
|
365
|
+
): T {
|
|
366
|
+
let inThrottle = false;
|
|
367
|
+
let lastResult: any;
|
|
368
|
+
|
|
369
|
+
return ((...args: Parameters<T>) => {
|
|
370
|
+
if (!inThrottle) {
|
|
371
|
+
lastResult = func(...args);
|
|
372
|
+
inThrottle = true;
|
|
373
|
+
setTimeout(() => {
|
|
374
|
+
inThrottle = false;
|
|
375
|
+
}, limit);
|
|
376
|
+
}
|
|
377
|
+
return lastResult;
|
|
378
|
+
}) as T;
|
|
379
|
+
}
|