UNPKG

@nutui/nutui-react

Version:

京东风格的轻量级移动端 React 组件库,支持一套代码生成 H5 和小程序

175 lines (174 loc) 5.78 kB
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 };