UNPKG

zarm

Version:

基于 React 的移动端UI库

468 lines (367 loc) 16.5 kB
"use strict"; var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); var _typeof = require("@babel/runtime/helpers/typeof"); Object.defineProperty(exports, "__esModule", { value: true }); exports.default = void 0; var _extends2 = _interopRequireDefault(require("@babel/runtime/helpers/extends")); var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty")); var _slicedToArray2 = _interopRequireDefault(require("@babel/runtime/helpers/slicedToArray")); var _react = require("@use-gesture/react"); var _bem = require("@zarm-design/bem"); var _icons = require("@zarm-design/icons"); var _throttle = _interopRequireDefault(require("lodash/throttle")); var _react2 = _interopRequireWildcard(require("react")); var _configProvider = require("../config-provider"); var _loading = _interopRequireDefault(require("../loading")); var _dom = require("../utils/dom"); var _events = _interopRequireDefault(require("../utils/events")); var _hooks = require("../utils/hooks"); var _interface = require("./interface"); function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function _getRequireWildcardCache(e) { return e ? t : r; })(e); } function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != _typeof(e) && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) { if ("default" !== u && {}.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } } return n.default = e, t && t.set(e, n), n; } function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; } function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { (0, _defineProperty2.default)(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; } var Pull = /*#__PURE__*/_react2.default.forwardRef(function (props, ref) { var _props$load; var pullRef = ref || /*#__PURE__*/_react2.default.createRef(); var wrap = (0, _react2.useRef)(); var _useState = (0, _react2.useState)(false), _useState2 = (0, _slicedToArray2.default)(_useState, 2), isMounted = _useState2[0], setIsMounted = _useState2[1]; var _useState3 = (0, _react2.useState)(0), _useState4 = (0, _slicedToArray2.default)(_useState3, 2), offsetY = _useState4[0], setOffsetY = _useState4[1]; var _useState5 = (0, _react2.useState)(0), _useState6 = (0, _slicedToArray2.default)(_useState5, 2), animationDuration = _useState6[0], setAnimationDuration = _useState6[1]; var _useState7 = (0, _react2.useState)(props.refresh.state), _useState8 = (0, _slicedToArray2.default)(_useState7, 2), refreshState = _useState8[0], setRefreshState = _useState8[1]; var _useState9 = (0, _react2.useState)(props.load.state), _useState10 = (0, _slicedToArray2.default)(_useState9, 2), loadState = _useState10[0], setLoadState = _useState10[1]; var prevLoad = (0, _react2.useRef)({}); var prevRefresh = (0, _react2.useRef)({}); var _React$useContext = _react2.default.useContext(_configProvider.ConfigContext), prefixCls = _React$useContext.prefixCls, locale = _React$useContext.locale; var bem = (0, _bem.createBEM)('pull', { prefixCls: prefixCls }); var onScroll = (0, _hooks.useEventCallback)(function () { // window为滚动容器时,无法通过 window 直接取到 scrollHeight 和 clientHeight。 var _ref = wrap.current, _ref$scrollHeight = _ref.scrollHeight, scrollHeight = _ref$scrollHeight === void 0 ? document.body.clientHeight : _ref$scrollHeight, _ref$clientHeight = _ref.clientHeight, clientHeight = _ref$clientHeight === void 0 ? document.documentElement.clientHeight : _ref$clientHeight; var load = _objectSpread(_objectSpread({}, Pull.defaultProps.load), props.load); var handler = load.handler, distance = load.distance; if (refreshState !== _interface.REFRESH_STATE.normal || loadState !== _interface.LOAD_STATE.normal || scrollHeight <= clientHeight || // 内容高度 - 偏移值 - 修正距离 <= 容器可见高度‚ scrollHeight - (0, _dom.getScrollTop)(wrap.current) - distance > clientHeight) { return; } handler === null || handler === void 0 ? void 0 : handler(); }, [props === null || props === void 0 ? void 0 : (_props$load = props.load) === null || _props$load === void 0 ? void 0 : _props$load.handler]); var throttledScroll = (0, _throttle.default)(onScroll, 250); var setScrollParent = function setScrollParent() { var _scrollContainer = (0, _dom.getScrollParent)(pullRef.current); // scrollContainer 未变更 if (wrap.current === _scrollContainer) return; // 重新获取 scrollContainer wrap.current = _scrollContainer; }; (0, _react2.useEffect)(function () { setScrollParent(); }); (0, _react2.useEffect)(function () { _events.default.on(wrap.current, 'scroll', throttledScroll); return function () { _events.default.off(wrap.current, 'scroll', throttledScroll); }; }, [wrap.current]); /** * 执行动画 * @param {number} options.offsetY 偏移距离 * @param {number} options.animationDuration 动画执行时间 */ var doTransition = function doTransition(_ref2) { var iOffsetY = _ref2.offsetY, iAnimationDuration = _ref2.animationDuration; setOffsetY(iOffsetY); setAnimationDuration(iAnimationDuration); }; /** * 执行加载动作 * @param {LOAD_STATE} iLoadState 加载状态 */ var doLoadAction = function doLoadAction(iLoadState) { var stayTime = props.stayTime; setLoadState(iLoadState); switch (iLoadState) { case _interface.LOAD_STATE.success: doLoadAction(_interface.LOAD_STATE.normal); break; case _interface.LOAD_STATE.failure: setTimeout(function () { if (!isMounted) return; doLoadAction(_interface.LOAD_STATE.abort); }, stayTime); break; default: } }; /** * 执行刷新动作 * @param {REFRESH_STATE} iRefreshState 刷新状态 * @param {number} iOffsetY 偏移距离 */ var doRefreshAction = function doRefreshAction(iRefreshState, iOffsetY) { var stayTime = props.stayTime; setRefreshState(iRefreshState); switch (iRefreshState) { case _interface.REFRESH_STATE.pull: case _interface.REFRESH_STATE.drop: doTransition({ offsetY: iOffsetY, animationDuration: 0 }); break; case _interface.REFRESH_STATE.loading: doTransition({ offsetY: 'auto', animationDuration: animationDuration }); break; case _interface.REFRESH_STATE.success: case _interface.REFRESH_STATE.failure: doTransition({ offsetY: 'auto', animationDuration: animationDuration }); setTimeout(function () { if (!isMounted) return; doRefreshAction(_interface.REFRESH_STATE.normal); doLoadAction(_interface.LOAD_STATE.normal); }, stayTime); break; default: doTransition({ offsetY: 0, animationDuration: props.animationDuration }); } }; var onDragMove = function onDragMove(state) { var _Pull$defaultProps; var movement = state.movement, event = state.event; var _movement = (0, _slicedToArray2.default)(movement, 2), dragOffsetY = _movement[1]; var _ref3 = props.refresh, handler = _ref3.handler; if ( // 未设置刷新事件 !handler || // 上拉 dragOffsetY <= 0 || // 未滚动到顶部 dragOffsetY > 0 && (0, _dom.getScrollTop)(wrap.current) > 0 || // 已经触发过加载状态 refreshState >= _interface.REFRESH_STATE.loading) { return false; } if (event.cancelable) { event.preventDefault(); } var refresh = _objectSpread(_objectSpread({}, (_Pull$defaultProps = Pull.defaultProps) === null || _Pull$defaultProps === void 0 ? void 0 : _Pull$defaultProps.refresh), props === null || props === void 0 ? void 0 : props.refresh); var startDistance = refresh.startDistance, distance = refresh.distance; // 设置拖动距离衰减(实际下拉移动距离为拖动距离的1/3) var offset = state.movement[1] / 3; // 判断是否达到释放立即刷新的条件 var action = offset - startDistance < distance ? _interface.REFRESH_STATE.pull : _interface.REFRESH_STATE.drop; doRefreshAction(action, offset); return true; }; var onDragEnd = function onDragEnd(_ref4) { var iOffsetY = _ref4.offsetY; // 没有产生位移 if (!iOffsetY) { return; } // 当前状态为下拉状态时 if (refreshState === _interface.REFRESH_STATE.pull) { doRefreshAction(_interface.REFRESH_STATE.normal); return; } // 执行外部触发刷新的回调 var _ref5 = props.refresh, handler = _ref5.handler; handler === null || handler === void 0 ? void 0 : handler(); }; var bind = (0, _react.useDrag)(function (state) { if (state.last) { setAnimationDuration(props.animationDuration); onDragEnd({ offsetY: offsetY }); return; } onDragMove(state); }, { enabled: true, pointer: { touch: true }, axis: 'y', eventOptions: { passive: !_events.default.supportsPassiveEvents } }); var load = props.load, refresh = props.refresh; if (prevLoad.current.state !== load.state) { doLoadAction(load.state); prevLoad.current = load; } if (prevRefresh.current.state !== refresh.state) { doRefreshAction(refresh.state); prevRefresh.current = refresh; } (0, _react2.useEffect)(function () { setIsMounted(true); pullRef.current && _events.default.on(pullRef.current, 'touchmove', function () {}); return function () { pullRef.current && _events.default.off(pullRef.current, 'touchmove', function () {}); setIsMounted(false); }; }, []); /** * 渲染刷新节点 */ var renderRefresh = function renderRefresh() { var _Pull$defaultProps2; var refreshProps = _objectSpread(_objectSpread({}, (_Pull$defaultProps2 = Pull.defaultProps) === null || _Pull$defaultProps2 === void 0 ? void 0 : _Pull$defaultProps2.refresh), props === null || props === void 0 ? void 0 : props.refresh); var startDistance = refreshProps.startDistance, distance = refreshProps.distance, render = refreshProps.render; var percent = 0; if (typeof offsetY === 'number' && offsetY >= startDistance) { percent = (offsetY - startDistance < distance ? offsetY - startDistance : distance) * 100 / distance; } if (typeof render === 'function') { return render(refreshState, percent); } var refreshCls = bem('control'); switch (refreshState) { case _interface.REFRESH_STATE.pull: return /*#__PURE__*/_react2.default.createElement("div", { className: refreshCls }, /*#__PURE__*/_react2.default.createElement(_loading.default, { loading: false, percent: percent }), /*#__PURE__*/_react2.default.createElement("span", null, locale.Pull.pullText)); case _interface.REFRESH_STATE.drop: return /*#__PURE__*/_react2.default.createElement("div", { className: refreshCls }, /*#__PURE__*/_react2.default.createElement(_loading.default, { loading: false, percent: 100 }), /*#__PURE__*/_react2.default.createElement("span", null, locale.Pull.dropText)); case _interface.REFRESH_STATE.loading: return /*#__PURE__*/_react2.default.createElement("div", { className: refreshCls }, /*#__PURE__*/_react2.default.createElement(_loading.default, { type: "spinner" }), /*#__PURE__*/_react2.default.createElement("span", null, locale.Pull.loadingText)); case _interface.REFRESH_STATE.success: return /*#__PURE__*/_react2.default.createElement("div", { className: refreshCls }, /*#__PURE__*/_react2.default.createElement(_icons.SuccessCircle, { theme: "success" }), /*#__PURE__*/_react2.default.createElement("span", null, locale.Pull.successText)); case _interface.REFRESH_STATE.failure: return /*#__PURE__*/_react2.default.createElement("div", { className: refreshCls }, /*#__PURE__*/_react2.default.createElement(_icons.WarningCircle, { theme: "danger" }), /*#__PURE__*/_react2.default.createElement("span", null, locale.Pull.failureText)); default: } }; /** * 渲染加载节点 */ var renderLoad = function renderLoad() { var _Pull$defaultProps3; var loadProps = _objectSpread(_objectSpread({}, (_Pull$defaultProps3 = Pull.defaultProps) === null || _Pull$defaultProps3 === void 0 ? void 0 : _Pull$defaultProps3.load), props === null || props === void 0 ? void 0 : props.load); var render = loadProps.render; if (typeof render === 'function') { return render(loadState); } var loadCls = bem('control'); switch (loadState) { case _interface.LOAD_STATE.loading: return /*#__PURE__*/_react2.default.createElement("div", { className: loadCls }, /*#__PURE__*/_react2.default.createElement(_loading.default, { type: "spinner" }), /*#__PURE__*/_react2.default.createElement("span", null, locale.Pull.loadingText)); case _interface.LOAD_STATE.failure: return /*#__PURE__*/_react2.default.createElement("div", { className: loadCls }, /*#__PURE__*/_react2.default.createElement(_icons.WarningCircle, { theme: "danger" }), /*#__PURE__*/_react2.default.createElement("span", null, locale.Pull.failureText)); case _interface.LOAD_STATE.complete: return /*#__PURE__*/_react2.default.createElement("div", { className: loadCls }, /*#__PURE__*/_react2.default.createElement("span", null, locale.Pull.completeText)); default: } }; var className = props.className, style = props.style, children = props.children; var cls = bem([className]); var loadCls = bem('load', [{ show: loadState >= _interface.LOAD_STATE.loading }]); var contentStyle = { WebkitTransition: "all ".concat(animationDuration, "ms"), transition: "all ".concat(animationDuration, "ms") }; if (refreshState <= _interface.REFRESH_STATE.drop) { contentStyle.WebkitTransform = "translate3d(0, ".concat(offsetY, "px, 0)"); contentStyle.transform = "translate3d(0, ".concat(offsetY, "px, 0)"); } return /*#__PURE__*/_react2.default.createElement("div", (0, _extends2.default)({ className: cls, style: style }, bind(), { ref: pullRef }), /*#__PURE__*/_react2.default.createElement("div", { className: bem('content'), style: contentStyle }, /*#__PURE__*/_react2.default.createElement("div", { className: bem('refresh') }, renderRefresh()), /*#__PURE__*/_react2.default.createElement("div", { className: bem('body') }, children), /*#__PURE__*/_react2.default.createElement("div", { className: loadCls }, renderLoad()))); }); Pull.defaultProps = { refresh: { state: _interface.REFRESH_STATE.normal, startDistance: 30, distance: 30 }, load: { state: _interface.LOAD_STATE.normal, distance: 0 }, animationDuration: 400, stayTime: 1000 }; var _default = Pull; exports.default = _default;