UNPKG

@kirz/react-native-toolkit

Version:

Toolkit to speed up React Native development

364 lines (363 loc) 17.2 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.FullscreenCarousel = FullscreenCarousel; exports.SelectedSlideIndexAtom = void 0; var _jotai = require("jotai"); var _react = _interopRequireWildcard(require("react")); var _reactNative = require("react-native"); var _reactNativeReanimated = _interopRequireWildcard(require("react-native-reanimated")); var _Text = require("./Text"); var _View = require("./View"); var _index = require("../index"); var _AutoplayAction = require("../utils/AutoplayAction"); function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function (nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); } function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; } function _extends() { _extends = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); } const SelectedSlideIndexAtom = (0, _jotai.atom)(0); exports.SelectedSlideIndexAtom = SelectedSlideIndexAtom; function ControlButton(_ref) { let { direction, slidesCount, ...props } = _ref; const [slideIndex] = (0, _jotai.useAtom)(SelectedSlideIndexAtom); const isDisabled = direction === 'left' && slideIndex <= 0 || direction === 'right' && slideIndex >= slidesCount - 1; if (isDisabled) { return null; } return /*#__PURE__*/_react.default.createElement(_reactNative.TouchableOpacity, _extends({}, props, { hitSlop: (0, _index.scaleX)(20), disabled: isDisabled })); } const SCREEN_WIDTH = _reactNative.Dimensions.get('screen').width; const unwrapProps = (props, item) => typeof props === 'function' ? props(item ?? {}) : props ?? {}; function FullscreenCarousel(_ref2) { let { controlRef, slides, spacing = 0, controls, progressValue, edgeOffset = 0, autoplay, flatListProps, slideLayout, staticLayout, width: widthProp = 'screen', onSlideChanged, ...props } = _ref2; const store = (0, _react.useMemo)(() => (0, _jotai.createStore)(), []); const [width, setWidth] = (0, _react.useState)(widthProp === 'screen' ? SCREEN_WIDTH : typeof widthProp === 'number' ? widthProp : 0); const flatListRef = (0, _react.useRef)(null); const activeSlideIndexRef = (0, _react.useRef)(0); const targetSlideIndexRef = (0, _react.useRef)(activeSlideIndexRef.current); const allowLastSlideAutoplay = (0, _react.useRef)(true); const slideProgress = (0, _reactNativeReanimated.useSharedValue)(0); const autoplayProgress = (0, _reactNativeReanimated.useSharedValue)(0); const autoplayActionRef = (0, _react.useRef)(); const scrollToIndexAwaiterRef = (0, _react.useRef)(null); const renderItem = (0, _react.useCallback)(_ref3 => { let { item, index } = _ref3; return /*#__PURE__*/_react.default.createElement(_View.View, { style: { flex: 1, width, overflow: 'hidden', paddingHorizontal: edgeOffset, marginBottom: -spacing } }, slideLayout.sections.map((section, sectionIdx) => { var _section$hidden; if (section !== null && section !== void 0 && (_section$hidden = section.hidden) !== null && _section$hidden !== void 0 && _section$hidden.call(section, item, index)) { return null; } const wrapperProps = unwrapProps(section.wrapperProps, item); return /*#__PURE__*/_react.default.createElement(_View.View, _extends({}, wrapperProps, { key: sectionIdx, style: [{ marginBottom: spacing }, ...(section.type === 'image' ? [{ flex: 1, justifyContent: 'center', marginHorizontal: -edgeOffset }] : section.type === 'text' ? [{ alignItems: 'center' }] : []), wrapperProps.style] }), (() => { var _section$renderItem; if (section.type === 'image') { const imageContainerProps = unwrapProps(section.imageContainerProps, item); const imageProps = unwrapProps(section.imageProps, item); return /*#__PURE__*/_react.default.createElement(_View.View, _extends({}, imageContainerProps, { style: [{ flex: 1, justifyContent: 'center', alignItems: 'center' }, imageContainerProps === null || imageContainerProps === void 0 ? void 0 : imageContainerProps.style] }), /*#__PURE__*/_react.default.createElement(_reactNative.Image, _extends({}, imageProps, { source: section.valueGetter(item, index), resizeMode: (imageProps === null || imageProps === void 0 ? void 0 : imageProps.resizeMode) ?? 'contain', style: [{ width: '100%', height: '100%' }, imageProps === null || imageProps === void 0 ? void 0 : imageProps.style] }))); } if (section.type === 'text') { const textProps = unwrapProps(section.textProps, item); return /*#__PURE__*/_react.default.createElement(_Text.Text, _extends({}, textProps, { style: [{ textAlign: 'center' }, textProps === null || textProps === void 0 ? void 0 : textProps.style] }), section.valueGetter(item, index)); } return (_section$renderItem = section.renderItem) === null || _section$renderItem === void 0 ? void 0 : _section$renderItem.call(section, item, index); })()); })); }, [slideLayout, width]); const renderStaticLayout = position => { return staticLayout.sections.filter(section => section.position === position).map((section, sectionIdx) => { var _section$wrapperProps; return /*#__PURE__*/_react.default.createElement(_View.View, _extends({}, section.wrapperProps, { key: sectionIdx, style: [position !== 'slide' ? { marginBottom: spacing } : { paddingBottom: spacing }, (_section$wrapperProps = section.wrapperProps) === null || _section$wrapperProps === void 0 ? void 0 : _section$wrapperProps.style] }), (() => { const ctx = { progress: autoplay ? autoplayProgress : slideProgress, slidesCount: slides.length, getActiveSlideIndex: () => store.get(SelectedSlideIndexAtom) }; if (section.type === 'indicator') { return /*#__PURE__*/_react.default.createElement(section.component, ctx); } return section.renderItem(ctx); })()); }); }; const scrollTo = (0, _react.useCallback)(async function (index) { var _autoplayActionRef$cu, _flatListRef$current; let animated = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true; const targetIndex = index < 0 ? 0 : index >= slides.length - 1 ? slides.length - 1 : index; if (targetIndex === targetSlideIndexRef.current) { return true; } (_autoplayActionRef$cu = autoplayActionRef.current) === null || _autoplayActionRef$cu === void 0 ? void 0 : _autoplayActionRef$cu.pause(); targetSlideIndexRef.current = targetIndex; if (scrollToIndexAwaiterRef.current) { scrollToIndexAwaiterRef.current.awaiter.resolve(false); scrollToIndexAwaiterRef.current = null; } scrollToIndexAwaiterRef.current = { awaiter: new _index.ControlledPromise(), target: targetIndex }; flatListRef === null || flatListRef === void 0 ? void 0 : (_flatListRef$current = flatListRef.current) === null || _flatListRef$current === void 0 ? void 0 : _flatListRef$current.scrollToIndex({ index: targetIndex, animated }); startAutoplay(targetIndex); return scrollToIndexAwaiterRef.current.awaiter.wait(); }, [slides.length]); const scrollToPrev = (0, _react.useCallback)(async animated => { return scrollTo(targetSlideIndexRef.current - 1, animated); }, []); const scrollToNext = (0, _react.useCallback)(async animated => { return scrollTo(targetSlideIndexRef.current + 1, animated); }, []); const startAutoplay = (0, _react.useCallback)(async function () { var _autoplayActionRef$cu2; let slideIndex = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : activeSlideIndexRef.current; const isLastSlide = slideIndex === slides.length - 1; if (isLastSlide && !allowLastSlideAutoplay.current) { return; } if (isLastSlide) { allowLastSlideAutoplay.current = false; } (_autoplayActionRef$cu2 = autoplayActionRef.current) === null || _autoplayActionRef$cu2 === void 0 ? void 0 : _autoplayActionRef$cu2.start(slideIndex); }, []); const onScroll = (0, _reactNativeReanimated.useAnimatedScrollHandler)(event => { var _flatListProps$onScro; slideProgress.value = event.contentOffset.x / width; flatListProps === null || flatListProps === void 0 ? void 0 : (_flatListProps$onScro = flatListProps.onScroll) === null || _flatListProps$onScro === void 0 ? void 0 : _flatListProps$onScro.call(flatListProps, event); }, [width]); const onViewableItemsChanged = (0, _react.useCallback)(_ref4 => { let { viewableItems } = _ref4; const visibleItem = viewableItems[0]; if (!visibleItem) { return; } activeSlideIndexRef.current = visibleItem.index; targetSlideIndexRef.current = activeSlideIndexRef.current; if (scrollToIndexAwaiterRef.current && scrollToIndexAwaiterRef.current.target === visibleItem.index) { scrollToIndexAwaiterRef.current.awaiter.resolve(true); scrollToIndexAwaiterRef.current = null; } if (visibleItem.index < slides.length - 1) { allowLastSlideAutoplay.current = true; } store.set(SelectedSlideIndexAtom, visibleItem.index); onSlideChanged === null || onSlideChanged === void 0 ? void 0 : onSlideChanged(visibleItem.index); }, []); (0, _react.useEffect)(() => { store.set(SelectedSlideIndexAtom, (flatListProps === null || flatListProps === void 0 ? void 0 : flatListProps.initialScrollIndex) ?? 0); }, [store]); (0, _react.useEffect)(() => { if (!autoplay) { return; } autoplayActionRef.current = new _AutoplayAction.AutoplayAction(autoplayProgress, autoplay.interval, { delay: autoplay.delay ?? 1000, resetDuration: autoplay.resetDuration ?? 300, async onFinish() { scrollToNext(); } }); startAutoplay(); return () => { var _autoplayActionRef$cu3; (_autoplayActionRef$cu3 = autoplayActionRef.current) === null || _autoplayActionRef$cu3 === void 0 ? void 0 : _autoplayActionRef$cu3.reset(); }; }, []); (0, _react.useImperativeHandle)(controlRef, () => ({ scrollTo, scrollToPrev, scrollToNext }), [scrollTo, scrollToPrev, scrollToNext]); return /*#__PURE__*/_react.default.createElement(_jotai.Provider, { store: store }, /*#__PURE__*/_react.default.createElement(_View.View, _extends({}, props, { style: [{ flex: 1, marginBottom: -(spacing ?? 0) }, props.style] }, widthProp === 'auto' ? { onLayout: e => { setWidth(e.nativeEvent.layout.width); } } : {}), renderStaticLayout('top'), /*#__PURE__*/_react.default.createElement(_View.View, { style: { flex: 1, marginHorizontal: -edgeOffset } }, !!width && /*#__PURE__*/_react.default.createElement(_react.default.Fragment, null, /*#__PURE__*/_react.default.createElement(_reactNativeReanimated.default.FlatList, _extends({}, flatListProps, { ref: flatListRef, data: slides, renderItem: renderItem, horizontal: true, pagingEnabled: true, showsHorizontalScrollIndicator: (flatListProps === null || flatListProps === void 0 ? void 0 : flatListProps.showsHorizontalScrollIndicator) ?? false, onScroll: onScroll, scrollEventThrottle: (flatListProps === null || flatListProps === void 0 ? void 0 : flatListProps.scrollEventThrottle) ?? 16, onViewableItemsChanged: onViewableItemsChanged, viewabilityConfig: (flatListProps === null || flatListProps === void 0 ? void 0 : flatListProps.viewabilityConfig) ?? { viewAreaCoveragePercentThreshold: 60 }, onScrollBeginDrag: e => { var _autoplayActionRef$cu4, _flatListProps$onScro2; (_autoplayActionRef$cu4 = autoplayActionRef.current) === null || _autoplayActionRef$cu4 === void 0 ? void 0 : _autoplayActionRef$cu4.pause(); flatListProps === null || flatListProps === void 0 ? void 0 : (_flatListProps$onScro2 = flatListProps.onScrollBeginDrag) === null || _flatListProps$onScro2 === void 0 ? void 0 : _flatListProps$onScro2.call(flatListProps, e); }, onScrollEndDrag: e => { var _e$nativeEvent$target, _flatListProps$onScro3; const slideWidth = e.nativeEvent.layoutMeasurement.width; const targetSlide = Math.round(((_e$nativeEvent$target = e.nativeEvent.targetContentOffset) === null || _e$nativeEvent$target === void 0 ? void 0 : _e$nativeEvent$target.x) / slideWidth); startAutoplay(targetSlide); flatListProps === null || flatListProps === void 0 ? void 0 : (_flatListProps$onScro3 = flatListProps.onScrollEndDrag) === null || _flatListProps$onScro3 === void 0 ? void 0 : _flatListProps$onScro3.call(flatListProps, e); allowLastSlideAutoplay.current = true; }, style: [{ marginBottom: spacing ?? 0 }, flatListProps === null || flatListProps === void 0 ? void 0 : flatListProps.style], contentContainerStyle: flatListProps === null || flatListProps === void 0 ? void 0 : flatListProps.contentContainerStyle })), renderStaticLayout('slide')), ((controls === null || controls === void 0 ? void 0 : controls.type) === 'buttons' || (controls === null || controls === void 0 ? void 0 : controls.type) === 'fullscreen') && /*#__PURE__*/_react.default.createElement(_View.View, { style: { position: 'absolute', top: 0, left: 0, right: 0, bottom: spacing ?? 0 }, pointerEvents: "box-none" }, controls.type === 'fullscreen' && /*#__PURE__*/_react.default.createElement(_View.View, { style: { position: 'absolute', width: '100%', height: '100%' }, pointerEvents: "box-none" }, /*#__PURE__*/_react.default.createElement(_View.View, { style: { position: 'relative', flex: 1 } }, /*#__PURE__*/_react.default.createElement(ControlButton, { onPress: () => { scrollToPrev(); }, direction: "left", style: { position: 'absolute', width: '50%', height: '100%', justifyContent: 'center', alignItems: 'center' }, slidesCount: slides.length }, controls === null || controls === void 0 ? void 0 : controls.leftIcon), /*#__PURE__*/_react.default.createElement(ControlButton, { onPress: () => { scrollToNext(); }, direction: "right", style: { position: 'absolute', width: '50%', height: '100%', right: 0, justifyContent: 'center', alignItems: 'center' }, slidesCount: slides.length }, controls === null || controls === void 0 ? void 0 : controls.rightIcon))), controls.type === 'buttons' && /*#__PURE__*/_react.default.createElement(_View.View, { style: { position: 'absolute', width: '100%', height: '100%', paddingHorizontal: (controls === null || controls === void 0 ? void 0 : controls.buttonsOffset) ?? (0, _index.scaleX)(10), flexDirection: 'row', alignItems: 'center' }, pointerEvents: "box-none" }, /*#__PURE__*/_react.default.createElement(ControlButton, { onPress: () => { scrollToPrev(); }, direction: "left", slidesCount: slides.length }, controls === null || controls === void 0 ? void 0 : controls.leftIcon), /*#__PURE__*/_react.default.createElement(_View.View, { style: { flex: 1 } }), /*#__PURE__*/_react.default.createElement(ControlButton, { onPress: () => { scrollToNext(); }, direction: "right", slidesCount: slides.length }, controls === null || controls === void 0 ? void 0 : controls.rightIcon)))), !!width && renderStaticLayout('bottom'))); } //# sourceMappingURL=FullscreenCarousel.js.map