UNPKG

@vnmfify/core

Version:

```shell npm i @vnmfify/core -S ```

438 lines (382 loc) 16.2 kB
import _clamp from "lodash/clamp"; var _excluded = ["className", "defaultValue", "value", "lazyRender", "loop", "touchable", "autoplay", "duration", "width", "height", "direction", "stopPropagation", "children", "onChange"]; function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { Promise.resolve(value).then(_next, _throw); } } function _asyncToGenerator(fn) { return function () { var self = this, args = arguments; return new Promise(function (resolve, reject) { var gen = fn.apply(self, args); function _next(value) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "next", value); } function _throw(err) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "throw", err); } _next(undefined); }); }; } function _objectWithoutProperties(source, excluded) { if (source == null) return {}; var target = _objectWithoutPropertiesLoose(source, excluded); var key, i; if (Object.getOwnPropertySymbols) { var sourceSymbolKeys = Object.getOwnPropertySymbols(source); for (i = 0; i < sourceSymbolKeys.length; i++) { key = sourceSymbolKeys[i]; if (excluded.indexOf(key) >= 0) continue; if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue; target[key] = source[key]; } } return target; } function _objectWithoutPropertiesLoose(source, excluded) { if (source == null) return {}; var target = {}; var sourceKeys = Object.keys(source); var key, i; for (i = 0; i < sourceKeys.length; i++) { key = sourceKeys[i]; if (excluded.indexOf(key) >= 0) continue; target[key] = source[key]; } return target; } function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (enumerableOnly) { symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; }); } keys.push.apply(keys, symbols); } return keys; } function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; if (i % 2) { ownKeys(Object(source), true).forEach(function (key) { _defineProperty(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; } function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } import { useForceUpdate, useUncontrolled } from "@vnmfify/hooks"; import { View } from "@vnxjs/components"; import { nextTick } from "@vnxjs/vnmf"; import classNames from "classnames"; import * as React from "react"; import { Children, cloneElement, isValidElement, useCallback, useEffect, useMemo, useRef, useState } from "react"; import { useMounted, useWindowResize } from "../hooks"; import { prefixClassname } from "../styles"; import { getComputedStyle } from "../utils/dom/computed-style"; import { preventDefault } from "../utils/dom/event"; import { getRect, makeRect } from "../utils/dom/rect"; import { addUnitPx, unitToPx } from "../utils/format/unit"; import { doubleRaf } from "../utils/raf"; import { useRendered, useRenderedRef, useToRef } from "../utils/state"; import { useTouch } from "../utils/touch"; import SwiperIndicator from "./swiper-indicator"; import SwiperItem from "./swiper-item"; import SwiperContext from "./swiper.context"; function useSwiperChildren(children) { var __children__ = { items: [], indicator: undefined, count: 0 }; var index = 0; Children.forEach(children, (child, i) => { if (! /*#__PURE__*/isValidElement(child)) { return; } var element = child; var elementType = element.type; if (elementType === SwiperIndicator) { __children__.indicator = element; } else if (elementType === SwiperItem) { var { key } = element; __children__.items.push( /*#__PURE__*/cloneElement(element, _objectSpread(_objectSpread({}, element.props), {}, { key: key !== null && key !== void 0 ? key : i, __dataIndex__: index++ }))); } else { __children__.items.push(element); } }); __children__.count = __children__.items.length; return __children__; } function getIndicatorValue(value, count) { return (value + count) % count; } function Swiper(props) { var { className, defaultValue, value: valueProp, lazyRender, loop = true, touchable = true, autoplay = 0, duration = 500, width, height, direction = "horizontal", stopPropagation = true, children: childrenProp, onChange: onChangeProp } = props, restProps = _objectWithoutProperties(props, _excluded); var { value = 0, setValue } = useUncontrolled({ value: valueProp, defaultValue, onChange: onChangeProp }); var valueRef = useToRef(value); var { count, indicator, items } = useSwiperChildren(childrenProp); var itemInstances = useMemo(() => [], []); var touch = useTouch(); var forceUpdate = useForceUpdate(); var vertical = direction === "vertical"; var rootRef = useRef(); var rectRef = useRef(); var [offset, setOffset] = useState(0); var swipingRef = useRef(false); var activeIndexRef = useRef(0); var touchStartTimeRef = useRef(0); var autoplayTimerRef = useRef(); var valueIndicatorRef = useRenderedRef(() => getIndicatorValue(value, count)); var activeIndicatorRef = useRenderedRef(() => getIndicatorValue(activeIndexRef.current, count)); var getDelta = useCallback(() => vertical ? touch.deltaY : touch.deltaX, []); var getCorrectDirection = useCallback(() => touch.direction === direction, []); var getCustomRect = useCallback(() => { var _rectRef$current, _rectRef$current2; return { width: width !== null && width !== void 0 ? width : (_rectRef$current = rectRef.current) === null || _rectRef$current === void 0 ? void 0 : _rectRef$current.width, height: height !== null && height !== void 0 ? height : (_rectRef$current2 = rectRef.current) === null || _rectRef$current2 === void 0 ? void 0 : _rectRef$current2.height }; }, [height, width]); var propRectRef = useRenderedRef(() => ({ width, height })); var getSize = useCallback(() => { var _ref; var { height, width } = getCustomRect(); return (_ref = vertical ? height : width) !== null && _ref !== void 0 ? _ref : 0; }, [getCustomRect, vertical]); var getTrackSize = useCallback(() => count * getSize(), [count, getSize]); var getMinOffset = useCallback(() => { if (rectRef.current) { var _ref2, _rectRef$current3, _rectRef$current4; var base = (_ref2 = vertical ? (_rectRef$current3 = rectRef.current) === null || _rectRef$current3 === void 0 ? void 0 : _rectRef$current3.height : (_rectRef$current4 = rectRef.current) === null || _rectRef$current4 === void 0 ? void 0 : _rectRef$current4.width) !== null && _ref2 !== void 0 ? _ref2 : 0; return base - getSize() * count; } return 0; }, [count, getSize, vertical]); var getMaxCount = useCallback(() => Math.ceil(Math.abs(getMinOffset()) / getSize()), [getMinOffset, getSize]); var getTargetActive = useCallback(pace => { if (pace) { if (loop) { return _clamp(activeIndexRef.current + pace, -1, count); } return _clamp(activeIndexRef.current + pace, 0, getMaxCount()); } return activeIndexRef.current; }, [loop, getMaxCount, count]); var getTargetOffset = useCallback(function (targetActive) { var offset = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0; var size = getSize(); var minOffset = getMinOffset(); var currentPosition = targetActive * size; if (!loop) { currentPosition = Math.min(currentPosition, -minOffset); } var targetOffset = offset - currentPosition; if (!loop) { targetOffset = _clamp(targetOffset, getMinOffset(), 0); } return targetOffset; }, [getMinOffset, getSize, loop]); var moveTo = useCallback(_ref3 => { var { pace = 0, offset = 0, emitChange = false } = _ref3; if (count <= 1) { return; } var minOffset = getMinOffset(); var targetActive = getTargetActive(pace); var targetOffset = getTargetOffset(targetActive, offset); if (loop) { if (itemInstances[0] && targetOffset !== minOffset) { var outRightBound = targetOffset < minOffset; itemInstances[0].setOffset(outRightBound ? getTrackSize() : 0); } if (itemInstances[count - 1] && targetOffset !== 0) { var outLeftBound = targetOffset > 0; itemInstances[count - 1].setOffset(outLeftBound ? -getTrackSize() : 0); } } var previousActiveIndex = activeIndexRef.current; activeIndexRef.current = targetActive; setOffset(targetOffset); if (emitChange && previousActiveIndex !== targetActive) { setValue(getIndicatorValue(targetActive, count)); } }, [count, getMinOffset, getTargetActive, getTargetOffset, loop, itemInstances, getSize, getTrackSize, setValue]); var correctPosition = useCallback(() => { swipingRef.current = true; if (activeIndexRef.current <= -1) { moveTo({ pace: count }); } else if (activeIndexRef.current >= count) { moveTo({ pace: -count }); } }, [count, moveTo]); var swipeTo = useCallback(index => { correctPosition(); touch.reset(); doubleRaf(() => { var targetIndex; if (loop && index === count) { targetIndex = activeIndexRef.current === 0 ? 0 : index; } else { targetIndex = index % count; } swipingRef.current = false; moveTo({ pace: targetIndex - activeIndexRef.current, emitChange: true }); }); }, [correctPosition, count, loop, moveTo, touch]); var next = useCallback(() => { correctPosition(); doubleRaf(() => { swipingRef.current = false; moveTo({ pace: 1, emitChange: true }); }); }, [correctPosition, moveTo]); var stopAutoplay = useCallback(() => { if (autoplayTimerRef.current) { clearTimeout(autoplayTimerRef.current); } }, []); var startAutoplay = useCallback(() => { stopAutoplay(); if (autoplay > 0 && count > 1) { autoplayTimerRef.current = setTimeout(() => { next(); startAutoplay(); }, +autoplay); } }, [autoplay, count, next, stopAutoplay]); var onTouchStart = useCallback(event => { if (!touchable) { return; } touch.start(event); touchStartTimeRef.current = Date.now(); stopAutoplay(); correctPosition(); }, [correctPosition, stopAutoplay, touch, touchable]); var onTouchMove = useCallback(event => { if (!touchable || !swipingRef.current) { return; } touch.move(event); var correctDirection = getCorrectDirection(); var shouldPrevent = correctDirection || touch.offsetY > touch.offsetX === vertical; if (shouldPrevent) { preventDefault(event, stopPropagation); } if (correctDirection) { moveTo({ offset: getDelta() }); } }, [getCorrectDirection, getDelta, moveTo, stopPropagation, touch, touchable, vertical]); var onTouchEnd = useCallback(() => { if (!touchable || !swipingRef.current) { return; } var duration = Date.now() - touchStartTimeRef.current; var delta = getDelta(); var speed = delta / duration; var size = getSize(); var shouldSwipe = Math.abs(speed) > 0.25 || Math.abs(delta) > size / 2; swipingRef.current = false; var correctDirection = getCorrectDirection(); if (shouldSwipe && correctDirection) { var _offset = vertical ? touch.offsetY : touch.offsetX; var pace; if (loop) { pace = _offset > 0 ? delta > 0 ? -1 : 1 : 0; } else { pace = -Math[delta > 0 ? "ceil" : "floor"](delta / size); } moveTo({ pace, emitChange: true }); } else if (delta) { moveTo({ pace: 0 }); } startAutoplay(); }, [touchable, getDelta, getSize, getCorrectDirection, startAutoplay, vertical, touch.offsetY, touch.offsetX, loop, moveTo]); var getTrackRect = useCallback(() => new Promise(resolve => { nextTick(() => resolve(Promise.all([getRect(rootRef), getComputedStyle(rootRef, ["width", "height"])]).then(_ref4 => { var [rect, style] = _ref4; return makeRect(style.width === "auto" ? rect.width : unitToPx(style.width), style.height === "auto" ? rect.height : unitToPx(style.height)); }))); }), []); var initialize = useCallback( /*#__PURE__*/_asyncToGenerator(function* () { var activeIndex = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : valueRef.current; if (!rootRef.current) { return; } rectRef.current = yield getTrackRect(); if (count) { activeIndex = Math.min(count - 1, activeIndex); } activeIndexRef.current = activeIndex; swipingRef.current = true; var targetOffset = getTargetOffset(activeIndex); setOffset(targetOffset); if (targetOffset === offset) { forceUpdate(); } itemInstances.forEach(item => item.setOffset(0)); }), [valueRef, getTrackRect, count, getTargetOffset, offset, itemInstances, forceUpdate]); var resize = useCallback(() => nextTick(() => initialize(activeIndexRef.current)), [initialize]); useMounted(initialize); useWindowResize(resize); useMounted(() => { startAutoplay(); return stopAutoplay; }); useEffect(() => { var valueIndicator = valueIndicatorRef.current; var activeIndicator = activeIndicatorRef.current; if (valueIndicator !== activeIndicator) { try { stopAutoplay(); swipeTo(valueIndicator); } finally { startAutoplay(); } } }, [valueIndicatorRef.current]); var trackStyle = useRendered(() => { var style = { transitionDuration: "".concat(swipingRef.current ? 0 : duration, "ms"), transform: "translate".concat(vertical ? "Y" : "X", "(").concat(addUnitPx(offset), ")") }; var size = getSize === null || getSize === void 0 ? void 0 : getSize(); if (size) { var mainAxis = vertical ? "height" : "width"; style[mainAxis] = "".concat(addUnitPx(size)); var crossAxis = vertical ? "width" : "height"; var crossAxisValue = propRectRef.current[crossAxis]; style[crossAxis] = crossAxisValue ? addUnitPx(crossAxisValue) : ""; } return style; }); return /*#__PURE__*/React.createElement(View, _objectSpread({ ref: rootRef, className: classNames(prefixClassname("swiper"), className) }, restProps), /*#__PURE__*/React.createElement(SwiperContext.Provider, { value: { lazyRender, loop, direction, indicator: activeIndicatorRef.current, getSize, count, itemInstances } }, /*#__PURE__*/React.createElement(View, { className: classNames(prefixClassname("swiper__track"), { [prefixClassname("swiper__track--vertical")]: vertical }), catchMove: stopPropagation, onTouchStart: onTouchStart, onTouchMove: onTouchMove, onTouchEnd: onTouchEnd, onTouchCancel: onTouchEnd, style: trackStyle, children: items }), indicator)); } export default Swiper; //# sourceMappingURL=swiper.js.map