UNPKG

@vidit-me/react-native-wheely

Version:

An all JavaScript, highly customizable wheel picker for react-native.

100 lines (99 loc) 6.07 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); __setModuleDefault(result, mod); return result; }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); const react_1 = __importStar(require("react")); const react_native_1 = require("react-native"); const WheelPicker_styles_1 = __importDefault(require("./WheelPicker.styles")); const WheelPickerItem_1 = __importDefault(require("./WheelPickerItem")); const WheelPicker = (0, react_1.forwardRef)(({ selectedIndex, options, onChange, selectedIndicatorStyle = {}, containerStyle = {}, itemStyle = {}, itemTextStyle = {}, itemHeight = 40, scaleFunction = (x) => Math.pow(1.0, x), rotationFunction = (x) => 1 - Math.pow(1 / 2, x), opacityFunction = (x) => Math.pow(1 / 3, x), visibleRest = 2, decelerationRate = 'fast', containerProps = {}, flatListProps = {}, }, ref) => { const flatListRef = (0, react_1.useRef)(null); const [scrollY] = (0, react_1.useState)(new react_native_1.Animated.Value(0)); const containerHeight = (1 + visibleRest * 2) * itemHeight; const paddedOptions = (0, react_1.useMemo)(() => { const array = [...options]; for (let i = 0; i < visibleRest; i++) { array.unshift(null); array.push(null); } return array; }, [options, visibleRest]); const offsets = (0, react_1.useMemo)(() => [...Array(paddedOptions.length)].map((x, i) => i * itemHeight), [paddedOptions, itemHeight]); const currentScrollIndex = (0, react_1.useMemo)(() => react_native_1.Animated.add(react_native_1.Animated.divide(scrollY, itemHeight), visibleRest), [visibleRest, scrollY, itemHeight]); const handleMomentumScrollEnd = (event) => { // Due to list bounciness when scrolling to the start or the end of the list // the offset might be negative or over the last item. // We therefore clamp the offset to the supported range. const offsetY = Math.min(itemHeight * (options.length - 1), Math.max(event.nativeEvent.contentOffset.y, 0)); let index = Math.floor(Math.floor(offsetY) / itemHeight); const last = Math.floor(offsetY % itemHeight); if (last > itemHeight / 2) index++; if (index !== selectedIndex) { onChange(index); } }; (0, react_1.useEffect)(() => { if (selectedIndex < 0 || selectedIndex >= options.length) { throw new Error(`Selected index ${selectedIndex} is out of bounds [0, ${options.length - 1}]`); } }, [selectedIndex, options]); /** * If selectedIndex is changed from outside (not via onChange) we need to scroll to the specified index. * This ensures that what the user sees as selected in the picker always corresponds to the value state. */ (0, react_1.useEffect)(() => { var _a; (_a = flatListRef.current) === null || _a === void 0 ? void 0 : _a.scrollToIndex({ index: selectedIndex, animated: false, }); }, [selectedIndex]); // Assuming you want to expose scrollToIndex method to the parent (0, react_1.useImperativeHandle)(ref, () => ({ scrollToIndex: (params) => { var _a; (_a = flatListRef.current) === null || _a === void 0 ? void 0 : _a.scrollToIndex(params); }, })); return (react_1.default.createElement(react_native_1.View, Object.assign({ style: [WheelPicker_styles_1.default.container, { height: containerHeight }, containerStyle] }, containerProps), react_1.default.createElement(react_native_1.View, { style: [ WheelPicker_styles_1.default.selectedIndicator, selectedIndicatorStyle, { transform: [{ translateY: -itemHeight / 2 }], height: itemHeight, }, ] }), react_1.default.createElement(react_native_1.Animated.FlatList, Object.assign({}, flatListProps, { ref: flatListRef, style: WheelPicker_styles_1.default.scrollView, showsVerticalScrollIndicator: false, onScroll: react_native_1.Animated.event([{ nativeEvent: { contentOffset: { y: scrollY } } }], { useNativeDriver: true }), onMomentumScrollEnd: handleMomentumScrollEnd, snapToOffsets: offsets, decelerationRate: decelerationRate, initialScrollIndex: selectedIndex, getItemLayout: (data, index) => ({ length: itemHeight, offset: itemHeight * index, index, }), data: paddedOptions, keyExtractor: (item, index) => index.toString(), renderItem: ({ item: option, index }) => (react_1.default.createElement(WheelPickerItem_1.default, { key: `option-${index}`, index: index, option: option, style: itemStyle, textStyle: itemTextStyle, height: itemHeight, currentScrollIndex: currentScrollIndex, scaleFunction: scaleFunction, rotationFunction: rotationFunction, opacityFunction: opacityFunction, visibleRest: visibleRest })) })))); }); WheelPicker.displayName = 'WheelPicker'; exports.default = WheelPicker;