UNPKG

@chatui/core

Version:

The React library for Chatbot UI

224 lines 8.01 kB
import _slicedToArray from "@babel/runtime/helpers/esm/slicedToArray"; import React, { useState, useEffect, useRef, useCallback, useImperativeHandle } from 'react'; import clsx from 'clsx'; import { setTransform } from '../../utils/style'; import { Icon } from '../Icon'; import { Flex } from '../Flex'; import { Button } from '../Button'; import canUse from '../../utils/canUse'; import smoothScroll from '../../utils/smoothScroll'; import { useLatest } from '../../hooks/useLatest'; var canPassive = canUse('passiveListener'); var passiveOpts = canPassive ? { passive: true } : false; var nonPassiveOpts = canPassive ? { passive: false } : false; export var PullToRefresh = /*#__PURE__*/React.forwardRef(function (props, ref) { var _props$distance = props.distance, oDistance = _props$distance === void 0 ? 20 : _props$distance, _props$loadingDistanc = props.loadingDistance, loadingDistance = _props$loadingDistanc === void 0 ? 30 : _props$loadingDistanc, _props$maxDistance = props.maxDistance, maxDistance = _props$maxDistance === void 0 ? 60 : _props$maxDistance, _props$distanceRatio = props.distanceRatio, distanceRatio = _props$distanceRatio === void 0 ? 2 : _props$distanceRatio, _props$loadMoreText = props.loadMoreText, loadMoreText = _props$loadMoreText === void 0 ? '点击加载更多' : _props$loadMoreText, children = props.children, onScroll = props.onScroll, onRefresh = props.onRefresh, _props$renderIndicato = props.renderIndicator, renderIndicator = _props$renderIndicato === void 0 ? function (status) { if (status === 'active' || status === 'loading') { return /*#__PURE__*/React.createElement(Icon, { className: "PullToRefresh-spinner", type: "spinner", spin: true }); } return null; } : _props$renderIndicato; var wrapperRef = useRef(null); var contentRef = useRef(null); var onRefreshRef = useLatest(onRefresh); var _useState = useState(0), _useState2 = _slicedToArray(_useState, 2), distance = _useState2[0], setDistance = _useState2[1]; var _useState3 = useState('pending'), _useState4 = _slicedToArray(_useState3, 2), status = _useState4[0], setStatus = _useState4[1]; var _useState5 = useState(false), _useState6 = _slicedToArray(_useState5, 2), dropped = _useState6[0], setDropped = _useState6[1]; var _useState7 = useState(!props.onRefresh), _useState8 = _slicedToArray(_useState7, 2), disabled = _useState8[0], setDisabled = _useState8[1]; var sharedRef = useRef({}); var statusRef = useRef(status); var timer1 = useRef(); var timer2 = useRef(); var useFallback = !canUse('touch'); useEffect(function () { statusRef.current = status; }, [status]); var setContentStyle = function setContentStyle(y) { var content = contentRef.current; if (content) { setTransform(content, "translate3d(0px,".concat(y, "px,0)")); } }; var scrollTo = function scrollTo(_ref) { var y = _ref.y, _ref$animated = _ref.animated, animated = _ref$animated === void 0 ? true : _ref$animated; var scroller = wrapperRef.current; if (!scroller) return; var offsetTop = y === '100%' ? scroller.scrollHeight - scroller.offsetHeight : y; if (animated) { smoothScroll({ el: scroller, to: offsetTop, x: false }); } else { scroller.scrollTop = offsetTop; } }; var scrollToEnd = useCallback(function () { var _ref2 = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}, _ref2$animated = _ref2.animated, animated = _ref2$animated === void 0 ? true : _ref2$animated; scrollTo({ y: '100%', animated: animated }); }, []); var reset = useCallback(function () { setDistance(0); setStatus('pending'); setContentStyle(0); }, []); var handleLoadMore = useCallback(function () { var scroller = wrapperRef.current; if (!scroller || !onRefreshRef.current) return; setStatus('loading'); try { var sh = scroller.scrollHeight; onRefreshRef.current().then(function (res) { var handleOffset = function handleOffset() { scrollTo({ y: scroller.scrollHeight - sh - 50, animated: false }); }; clearTimeout(timer1.current); clearTimeout(timer2.current); handleOffset(); timer1.current = setTimeout(handleOffset, 150); timer2.current = setTimeout(handleOffset, 250); reset(); if (res && res.noMore) { setDisabled(true); } }); } catch (ex) { // eslint-disable-next-line no-console console.error(ex); reset(); } }, [onRefreshRef, reset]); var touchStart = function touchStart() { sharedRef.current.startY = 0; }; var touchMove = useCallback(function (e) { var _wrapperRef$current; var currentY = e.touches[0].clientY; var canPull = ((_wrapperRef$current = wrapperRef.current) === null || _wrapperRef$current === void 0 ? void 0 : _wrapperRef$current.scrollTop) <= 0; if (canPull) { if (!sharedRef.current.startY) { sharedRef.current.startY = currentY; setStatus('pull'); setDropped(false); } } else { sharedRef.current.startY = 0; } var startY = sharedRef.current.startY; if (!canPull || currentY < startY || statusRef.current === 'loading') return; var dist = (currentY - startY) / distanceRatio; if (maxDistance && dist > maxDistance) { dist = maxDistance; } if (dist > 0) { if (e.cancelable) { e.preventDefault(); } e.stopPropagation(); setContentStyle(dist); setDistance(dist); setStatus(dist >= oDistance ? 'active' : 'pull'); } }, [distanceRatio, maxDistance, oDistance]); var touchEnd = useCallback(function () { setDropped(true); if (sharedRef.current.startY && statusRef.current === 'active') { handleLoadMore(); } else { reset(); } }, [handleLoadMore, reset]); useEffect(function () { var wrapper = wrapperRef.current; if (!wrapper || useFallback) return; if (disabled) { wrapper.removeEventListener('touchstart', touchStart); wrapper.removeEventListener('touchmove', touchMove); wrapper.removeEventListener('touchend', touchEnd); wrapper.removeEventListener('touchcancel', touchEnd); } else { wrapper.addEventListener('touchstart', touchStart, passiveOpts); wrapper.addEventListener('touchmove', touchMove, nonPassiveOpts); wrapper.addEventListener('touchend', touchEnd); wrapper.addEventListener('touchcancel', touchEnd); } }, [disabled, touchEnd, touchMove, useFallback]); useEffect(function () { if (status === 'loading' && !useFallback) { setContentStyle(loadingDistance); } }, [loadingDistance, status, useFallback]); useImperativeHandle(ref, function () { return { scrollTo: scrollTo, scrollToEnd: scrollToEnd, wrapperRef: wrapperRef }; }, [scrollToEnd]); return /*#__PURE__*/React.createElement("div", { className: "PullToRefresh", ref: wrapperRef, onScroll: onScroll }, /*#__PURE__*/React.createElement("div", { className: "PullToRefresh-inner" }, /*#__PURE__*/React.createElement("div", { className: clsx('PullToRefresh-content', { 'PullToRefresh-transition': dropped }), ref: contentRef }, /*#__PURE__*/React.createElement("div", { className: "PullToRefresh-indicator" }, renderIndicator(status, distance)), !disabled && useFallback && /*#__PURE__*/React.createElement(Flex, { className: "PullToRefresh-fallback", center: true }, renderIndicator(status, oDistance), /*#__PURE__*/React.createElement(Button, { className: "PullToRefresh-loadMore", variant: "text", onClick: handleLoadMore }, loadMoreText)), React.Children.only(children)))); });