@nutui/nutui-react
Version:
京东风格的轻量级移动端 React 组件库,支持一套代码生成 H5 和小程序
175 lines (174 loc) • 5.78 kB
JavaScript
import React__default, { forwardRef, useRef, useState, useCallback, useImperativeHandle, useEffect } from "react";
import classNames from "classnames";
import { u as useTouch } from "./use-touch.js";
import { g as getRect } from "./use-client-rect.js";
function preventDefault(event, isStopPropagation) {
if (typeof event.cancelable !== "boolean" || event.cancelable) {
event.preventDefault();
}
{
event.stopPropagation();
}
}
const defaultProps = {
name: ""
};
const Swipe = forwardRef((props, instanceRef) => {
const classPrefix = "nut-swipe";
const touch = useTouch();
const { children, className, style } = Object.assign(Object.assign({}, defaultProps), props);
const root = useRef();
const opened = useRef(false);
const lockClick = useRef(false);
const startOffset = useRef(0);
const [state, setState] = useState({
offset: 0,
dragging: false
});
const [actionWidth, setActionWidth] = useState({
left: 0,
right: 0
});
const wrapperStyle = {
transform: `translate3d(${state.offset}px, 0, 0)`,
transitionDuration: state.dragging ? "0s" : ".6s"
};
const leftWidth = actionWidth.left;
const rightWidth = actionWidth.right;
const onTouchStart = (event) => {
var _a;
if (!props.disabled) {
startOffset.current = state.offset;
touch.start(event);
(_a = props.onTouchStart) === null || _a === void 0 ? void 0 : _a.call(props, event);
}
};
const onTouchMove = (event) => {
var _a;
if (props.disabled) {
return;
}
touch.move(event);
(_a = props.onTouchMove) === null || _a === void 0 ? void 0 : _a.call(props, event);
if (touch.isHorizontal()) {
lockClick.current = true;
const newState = Object.assign(Object.assign({}, state), { dragging: true });
const isEdge = !opened || touch.deltaX.current * startOffset.current < 0;
if (isEdge) {
preventDefault(event);
}
newState.offset = rangeCalculation(touch.deltaX.current + startOffset.current, -rightWidth, leftWidth);
setState(newState);
}
};
const onTouchEnd = (event) => {
var _a;
if (state.dragging) {
setState((v) => Object.assign(Object.assign({}, v), { dragging: false }));
toggle(state.offset > 0 ? "left" : "right");
setTimeout(() => {
lockClick.current = false;
}, 0);
(_a = props.onTouchEnd) === null || _a === void 0 ? void 0 : _a.call(props, event);
}
};
const toggle = (side) => {
const offset = Math.abs(state.offset);
const base = 0.3;
const baseNum = opened ? 1 - base : base;
const width = side === "left" ? leftWidth : rightWidth;
if (width && offset > Number(width) * baseNum) {
open(side);
} else {
close(side);
}
};
const open = (side) => {
var _a;
opened.current = true;
const offset = side === "left" ? leftWidth : -rightWidth;
const name = props.name;
(_a = props.onOpen) === null || _a === void 0 ? void 0 : _a.call(props, { name, position: side });
setState((v) => Object.assign(Object.assign({}, v), { offset: Number(offset) || 0 }));
};
const close = (position) => {
var _a;
if (opened.current) {
opened.current = false;
(_a = props.onClose) === null || _a === void 0 ? void 0 : _a.call(props, {
name: props.name,
position: position || "left"
});
}
setState((v) => Object.assign(Object.assign({}, v), { offset: 0 }));
};
const rangeCalculation = (num, min, max) => {
return Math.min(Math.max(Number(num), Number(min)), Number(max));
};
const getNodeWidth = (node) => {
if (node) {
const ele = getRect(node);
return ele.width;
}
return 0;
};
const leftRef = useCallback((node) => {
if (node !== null) {
setActionWidth((v) => Object.assign(Object.assign({}, v), { left: getNodeWidth(node) }));
}
}, [props.leftAction]);
const rightRef = useCallback((node) => {
if (node !== null) {
setActionWidth((v) => Object.assign(Object.assign({}, v), { right: getNodeWidth(node) }));
}
}, [props.rightAction]);
const renderActionContent = (side, measuredRef) => {
if (props[`${side}Action`]) {
return React__default.createElement("div", { ref: measuredRef, className: `${classPrefix}-${side}`, onClick: (e) => handleOperate(e, side) }, props[`${side}Action`]);
}
return null;
};
const handleOperate = (event, position) => {
event.stopPropagation();
if (props.beforeClose) {
props.beforeClose(position);
} else {
props.onActionClick && props.onActionClick(event, position);
}
};
useImperativeHandle(instanceRef, () => ({
open,
close: () => close()
}));
useEffect(() => {
const handler = (event) => {
const targets = [root];
if (targets.some((targetItem) => {
const targetElement = targetItem.current || targetItem;
return !targetElement || (targetElement === null || targetElement === void 0 ? void 0 : targetElement.contains(event.target));
})) {
return;
}
close();
};
document.addEventListener("touchstart", handler);
return () => {
document.removeEventListener("touchstart", handler);
};
}, []);
return React__default.createElement(
"div",
{ ref: root, className: classNames(classPrefix, className), onTouchStart: (e) => onTouchStart(e), onTouchMove: (e) => onTouchMove(e), onTouchEnd: (e) => onTouchEnd(e), style },
React__default.createElement(
"div",
{ className: `${classPrefix}-wrapper`, style: wrapperStyle },
renderActionContent("left", leftRef),
children,
renderActionContent("right", rightRef)
)
);
});
Swipe.displayName = "NutSwipe";
export {
Swipe as default
};