UNPKG

antd-mobile

Version:
352 lines (351 loc) 13.1 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.Swiper = void 0; var _react = _interopRequireWildcard(require("react")); var _nativeProps = require("../../utils/native-props"); var _withDefaultProps = require("../../utils/with-default-props"); var _classnames = _interopRequireDefault(require("classnames")); var _swiperItem = require("./swiper-item"); var _devLog = require("../../utils/dev-log"); var _web = require("@react-spring/web"); var _react2 = require("@use-gesture/react"); var _pageIndicator = _interopRequireDefault(require("../page-indicator")); var _stagedComponents = require("staged-components"); var _useRefState = require("../../utils/use-ref-state"); var _bound = require("../../utils/bound"); var _ahooks = require("ahooks"); var _withFuncProps = require("../../utils/with-func-props"); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } 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; } const classPrefix = `adm-swiper`; const eventToPropRecord = { 'mousedown': 'onMouseDown', 'mousemove': 'onMouseMove', 'mouseup': 'onMouseUp' }; const defaultProps = { defaultIndex: 0, allowTouchMove: true, autoplay: false, autoplayInterval: 3000, loop: false, direction: 'horizontal', slideSize: 100, trackOffset: 0, stuckAtBoundary: true, rubberband: true, stopPropagation: [] }; let currentUid; const Swiper = (0, _react.forwardRef)((0, _stagedComponents.staged)((p, ref) => { const props = (0, _withDefaultProps.mergeProps)(defaultProps, p); const { direction, total, children, indicator } = props; const [uid] = (0, _react.useState)({}); const timeoutRef = (0, _react.useRef)(null); const isVertical = direction === 'vertical'; const slideRatio = props.slideSize / 100; const offsetRatio = props.trackOffset / 100; const { validChildren, count, renderChildren } = (0, _react.useMemo)(() => { let count = 0; let renderChildren = undefined; let validChildren = undefined; if (typeof children === 'function') { renderChildren = children; } else { validChildren = _react.default.Children.map(children, child => { if (!_react.default.isValidElement(child)) return null; if (child.type !== _swiperItem.SwiperItem) { (0, _devLog.devWarning)('Swiper', 'The children of `Swiper` must be `Swiper.Item` components.'); return null; } count++; return child; }); } return { renderChildren, validChildren, count }; }, [children]); const mergedTotal = total !== null && total !== void 0 ? total : count; if (mergedTotal === 0 || !validChildren && !renderChildren) { (0, _devLog.devWarning)('Swiper', '`Swiper` needs at least one child.'); return null; } return () => { let loop = props.loop; if (slideRatio * (mergedTotal - 1) < 1) { loop = false; } const trackRef = (0, _react.useRef)(null); function getSlidePixels() { const track = trackRef.current; if (!track) return 0; const trackPixels = isVertical ? track.offsetHeight : track.offsetWidth; return trackPixels * props.slideSize / 100; } const [current, setCurrent, getCurrent] = (0, _ahooks.useGetState)(props.defaultIndex); const [dragging, setDragging, draggingRef] = (0, _useRefState.useRefState)(false); function boundIndex(current) { let min = 0; let max = mergedTotal - 1; if (props.stuckAtBoundary) { min += offsetRatio / slideRatio; max -= (1 - slideRatio - offsetRatio) / slideRatio; } return (0, _bound.bound)(current, min, max); } const [{ position }, api] = (0, _web.useSpring)(() => ({ position: boundIndex(current) * 100, config: { tension: 200, friction: 30 }, onRest: () => { if (draggingRef.current) return; if (!loop) return; const rawX = position.get(); const totalWidth = 100 * mergedTotal; const standardPosition = modulus(rawX, totalWidth); if (standardPosition === rawX) return; api.start({ position: standardPosition, immediate: true }); } }), [mergedTotal]); const dragCancelRef = (0, _react.useRef)(null); function forceCancelDrag() { var _a; (_a = dragCancelRef.current) === null || _a === void 0 ? void 0 : _a.call(dragCancelRef); draggingRef.current = false; } const bind = (0, _react2.useDrag)(state => { dragCancelRef.current = state.cancel; if (!state.intentional) return; if (state.first && !currentUid) { currentUid = uid; } if (currentUid !== uid) return; currentUid = state.last ? undefined : uid; const slidePixels = getSlidePixels(); if (!slidePixels) return; const paramIndex = isVertical ? 1 : 0; const offset = state.offset[paramIndex]; const direction = state.direction[paramIndex]; const velocity = state.velocity[paramIndex]; setDragging(true); if (!state.last) { api.start({ position: offset * 100 / slidePixels, immediate: true }); } else { const minIndex = Math.floor(offset / slidePixels); const maxIndex = minIndex + 1; const index = Math.round((offset + velocity * 2000 * direction) / slidePixels); swipeTo((0, _bound.bound)(index, minIndex, maxIndex)); window.setTimeout(() => { setDragging(false); }); } }, { transform: ([x, y]) => [-x, -y], from: () => { const slidePixels = getSlidePixels(); return [position.get() / 100 * slidePixels, position.get() / 100 * slidePixels]; }, triggerAllEvents: true, bounds: () => { if (loop) return {}; const slidePixels = getSlidePixels(); const lowerBound = boundIndex(0) * slidePixels; const upperBound = boundIndex(mergedTotal - 1) * slidePixels; return isVertical ? { top: lowerBound, bottom: upperBound } : { left: lowerBound, right: upperBound }; }, rubberband: props.rubberband, axis: isVertical ? 'y' : 'x', preventScroll: !isVertical, pointer: { touch: true } }); function swipeTo(index, immediate = false) { var _a; const roundedIndex = Math.round(index); const targetIndex = loop ? modulus(roundedIndex, mergedTotal) : (0, _bound.bound)(roundedIndex, 0, mergedTotal - 1); if (targetIndex !== getCurrent()) { (_a = props.onIndexChange) === null || _a === void 0 ? void 0 : _a.call(props, targetIndex); } setCurrent(targetIndex); api.start({ position: (loop ? roundedIndex : boundIndex(roundedIndex)) * 100, immediate }); } function swipeNext() { swipeTo(Math.round(position.get() / 100) + 1); } function swipePrev() { swipeTo(Math.round(position.get() / 100) - 1); } (0, _react.useImperativeHandle)(ref, () => ({ swipeTo, swipeNext, swipePrev })); (0, _ahooks.useIsomorphicLayoutEffect)(() => { const maxIndex = mergedTotal - 1; if (current > maxIndex) { swipeTo(maxIndex, true); } }); const { autoplay, autoplayInterval } = props; const runTimeSwiper = () => { timeoutRef.current = window.setTimeout(() => { if (autoplay === 'reverse') { swipePrev(); } else { swipeNext(); } runTimeSwiper(); }, autoplayInterval); }; (0, _react.useEffect)(() => { if (!autoplay || dragging) return; runTimeSwiper(); return () => { if (timeoutRef.current) window.clearTimeout(timeoutRef.current); }; }, [autoplay, autoplayInterval, dragging, mergedTotal]); // ============================== Render ============================== // Render Item function renderItem(index, child) { let itemStyle = {}; if (loop) { itemStyle = { [isVertical ? 'y' : 'x']: position.to(position => { let finalPosition = -position + index * 100; const totalWidth = mergedTotal * 100; const flagWidth = totalWidth / 2; finalPosition = modulus(finalPosition + flagWidth, totalWidth) - flagWidth; return `${finalPosition}%`; }), [isVertical ? 'top' : 'left']: `-${index * 100}%` }; } return _react.default.createElement(_web.animated.div, { className: (0, _classnames.default)(`${classPrefix}-slide`, { [`${classPrefix}-slide-active`]: current === index }), style: itemStyle, key: index }, child); } function renderItems() { if (renderChildren && total) { const offsetCount = 2; const startIndex = Math.max(current - offsetCount, 0); const endIndex = Math.min(current + offsetCount, total - 1); const items = []; for (let index = startIndex; index <= endIndex; index += 1) { items.push(renderItem(index, renderChildren(index))); } return _react.default.createElement(_react.default.Fragment, null, _react.default.createElement("div", { className: `${classPrefix}-slide-placeholder`, style: { width: `${startIndex * 100}%` } }), items); } return _react.default.Children.map(validChildren, (child, index) => { return renderItem(index, child); }); } // Render Track Inner function renderTrackInner() { if (loop) { return _react.default.createElement("div", { className: `${classPrefix}-track-inner` }, renderItems()); } else { return _react.default.createElement(_web.animated.div, { className: `${classPrefix}-track-inner`, style: { [isVertical ? 'y' : 'x']: position.to(position => `${-position}%`) } }, renderItems()); } } // Render const style = { '--slide-size': `${props.slideSize}%`, '--track-offset': `${props.trackOffset}%` }; const dragProps = Object.assign({}, props.allowTouchMove ? bind() : {}); const stopPropagationProps = {}; for (const key of props.stopPropagation) { const prop = eventToPropRecord[key]; stopPropagationProps[prop] = function (e) { e.stopPropagation(); }; } const mergedProps = (0, _withFuncProps.mergeFuncProps)(dragProps, stopPropagationProps); let indicatorNode = null; if (typeof indicator === 'function') { indicatorNode = indicator(mergedTotal, current); } else if (indicator !== false) { indicatorNode = _react.default.createElement("div", { className: `${classPrefix}-indicator` }, _react.default.createElement(_pageIndicator.default, Object.assign({}, props.indicatorProps, { total: mergedTotal, current: current, direction: direction }))); } return (0, _nativeProps.withNativeProps)(props, _react.default.createElement("div", { className: (0, _classnames.default)(classPrefix, `${classPrefix}-${direction}`), style: style }, _react.default.createElement("div", Object.assign({ ref: trackRef, className: (0, _classnames.default)(`${classPrefix}-track`, { [`${classPrefix}-track-allow-touch-move`]: props.allowTouchMove }), onClickCapture: e => { if (draggingRef.current) { e.stopPropagation(); } forceCancelDrag(); } }, mergedProps), renderTrackInner()), indicatorNode)); }; })); exports.Swiper = Swiper; function modulus(value, division) { const remainder = value % division; return remainder < 0 ? remainder + division : remainder; }