zarm
Version:
基于 React 的移动端UI库
468 lines (367 loc) • 16.5 kB
JavaScript
"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;