UNPKG

react-native-time-date-picker

Version:

Easy to use time and date picker with lots of options for React Native.

182 lines 7.42 kB
import React, { useEffect, useRef, useState } from "react"; import { Text, View, Easing, FlatList, Animated, I18nManager, } from "react-native"; import RNBounceable from "@freakycoder/react-native-bounceable"; import { styles } from "./SelectTime.style"; import { defaultOptions, useCalendar } from "../../TimeDatePicker"; import { Modes } from "../../../utils/types"; const AnimatedFlatList = Animated.createAnimatedComponent(FlatList); // @ts-ignore const TimeScroller = ({ title, data, onChange }) => { const { options = defaultOptions, utils } = useCalendar(); const [itemSize, setItemSize] = useState(0); const style = styles(options); const scrollAnimatedValue = useRef(new Animated.Value(0)).current; const scrollListener = useRef("0"); const active = useRef(0); data = ["", "", ...data, "", ""]; const flatListRef = useRef(); useEffect(() => { scrollListener.current && clearInterval(Number(scrollListener.current)); scrollListener.current = scrollAnimatedValue.addListener(({ value }) => (active.current = value)); return () => { clearInterval(Number(scrollListener.current)); }; }, [scrollAnimatedValue]); // @ts-ignore const changeItemWidth = ({ nativeEvent }) => { const { width } = nativeEvent.layout; !itemSize && setItemSize(width / 5); }; // @ts-ignore const renderItem = ({ item, index }) => { const makeAnimated = (a, b, c) => { return { inputRange: [...data.map((_, i) => i * itemSize)], outputRange: [ ...data.map((_, i) => { const center = i + 2; if (center === index) { return a; } else if (center + 1 === index || center - 1 === index) { return b; } else { return c; } }), ], }; }; return (<RNBounceable onPress={() => { if (title === utils.config.hour) { // @ts-ignore flatListRef.current.scrollToOffset({ animated: true, offset: (item - (options.is24Hour ? 0 : 1)) * itemSize, }); } else { // @ts-ignore flatListRef.current.scrollToOffset({ animated: true, offset: item * itemSize, }); } }}> <Animated.View style={[ { width: itemSize, opacity: scrollAnimatedValue.interpolate(makeAnimated(1, 0.6, 0.2)), transform: [ { scale: scrollAnimatedValue.interpolate(makeAnimated(1.8, 0.9, 0.8)), }, { scaleX: I18nManager.isRTL ? -1 : 1, }, ], }, style.listItem, ]}> <Text style={style.listItemText}> {utils.getConvertedNumber(String(item).length === 1 ? "0" + String(item) : String(item))} </Text> </Animated.View> </RNBounceable>); }; return (<View style={style.row} onLayout={changeItemWidth}> <Text style={style.title}>{title}</Text> <AnimatedFlatList ref={flatListRef} pagingEnabled showsHorizontalScrollIndicator={false} horizontal snapToInterval={itemSize} decelerationRate={"fast"} onScroll={Animated.event([{ nativeEvent: { contentOffset: { x: scrollAnimatedValue } } }], { useNativeDriver: true, })} data={I18nManager.isRTL ? data.reverse() : data} onMomentumScrollEnd={() => { const index = Math.round(active.current / itemSize); onChange(data[index + 2]); }} keyExtractor={(_, i) => String(i)} renderItem={renderItem} inverted={I18nManager.isRTL} contentContainerStyle={I18nManager.isRTL && { transform: [ { scaleX: -1, }, ], }}/> </View>); }; const SelectTime = () => { const { options = defaultOptions, state, utils, minuteInterval = 1, mode, disableTimeCloseButton = false, onTimeChange, onTimeCancelPress, } = useCalendar(); const [mainState, setMainState] = state; const minute = options.is24Hour ? 0 : 1; const [show, setShow] = useState(false); const [time, setTime] = useState({ minute: minute, hour: 0, }); const style = styles(options); const openAnimation = useRef(new Animated.Value(0)).current; useEffect(() => { show && setTime({ minute: minute, hour: 0, }); }, [show, minute]); useEffect(() => { mainState.timeOpen && setShow(true); Animated.timing(openAnimation, { toValue: mainState.timeOpen ? 1 : 0, duration: 350, useNativeDriver: true, easing: Easing.bezier(0.17, 0.67, 0.46, 1), }).start(() => { !mainState.timeOpen && setShow(false); }); }, [mainState.timeOpen, openAnimation]); const selectTime = () => { const newTime = utils.getDate(mainState.activeDate); newTime.hour(time.hour).minute(time.minute); setMainState({ type: "set", activeDate: newTime, selectedDate: mainState.selectedDate, }); onTimeChange(newTime.valueOf()); mode !== Modes.time && setMainState({ type: "toggleTime", }); }; const containerStyle = [ style.container, { opacity: openAnimation, transform: [ { scale: openAnimation.interpolate({ inputRange: [0, 1], outputRange: [1.1, 1], }), }, ], }, ]; return show ? (<Animated.View style={containerStyle}> <TimeScroller title={utils.config.hour} data={Array.from({ length: options.is24Hour ? 24 : 12 }, (x, i) => i + (options.is24Hour ? 0 : 1))} onChange={(hour) => { setTime({ ...time, hour }); }}/> <TimeScroller title={utils.config.minute} data={Array.from({ length: 60 / minuteInterval }, (x, i) => i * minuteInterval)} onChange={(_minute) => setTime({ ...time, minute: _minute })}/> <View style={style.footer}> <RNBounceable style={style.button} onPress={selectTime}> <Text style={style.btnText}>{utils.config.timeSelect}</Text> </RNBounceable> {!disableTimeCloseButton && mode !== Modes.time && (<RNBounceable style={[style.button, style.cancelButton]} onPress={() => { setMainState({ type: "toggleTime", }); onTimeCancelPress?.(); }}> <Text style={style.btnText}>{utils.config.timeClose}</Text> </RNBounceable>)} </View> </Animated.View>) : null; }; export { SelectTime }; //# sourceMappingURL=SelectTime.js.map