UNPKG

@nutui/nutui-react

Version:

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

214 lines (213 loc) 8.92 kB
import { _ as __rest } from "./tslib.es6.js"; import React__default, { useRef, useState, useEffect, useMemo } from "react"; import classNames from "classnames"; import { createPortal } from "react-dom"; import Popup__default from "./Popup.js"; import { g as getRect } from "./use-client-rect.js"; import { C as ComponentDefaults } from "./typings.js"; import { u as useClickAway } from "./use-click-away.js"; import { c as canUseDom } from "./can-use-dom.js"; import { a as getAllScrollableParents } from "./get-scroll-parent.js"; import { useRtl } from "./ConfigProvider.js"; const defaultProps = Object.assign(Object.assign({}, ComponentDefaults), { list: [], theme: "light", location: "bottom", visible: false, offset: [0, 12], arrowOffset: 0, targetId: "", showArrow: true, closeOnOutsideClick: true, closeOnActionClick: true, overlay: false, onClick: () => { }, onOpen: () => { }, onClose: () => { } }); const classPrefix = `nut-popover`; const Popover = (props) => { const rtl = useRtl(); const _a = Object.assign(Object.assign({}, defaultProps), props), { children, list, theme, location, visible, offset, arrowOffset, targetId, overlay, closeOnOutsideClick, closeOnActionClick, className, showArrow, style, onClick, onOpen, onClose, onSelect } = _a, rest = __rest(_a, ["children", "list", "theme", "location", "visible", "offset", "arrowOffset", "targetId", "overlay", "closeOnOutsideClick", "closeOnActionClick", "className", "showArrow", "style", "onClick", "onOpen", "onClose", "onSelect"]); const popoverRef = useRef(null); const popoverContentRef = useRef(null); const [showPopup, setShowPopup] = useState(false); const [rootPosition, setRootPosition] = useState(); useEffect(() => { setShowPopup(visible); if (visible) { setTimeout(() => { getContentWidth(); }, 0); } }, [visible, location]); const update = useRef((e) => { getContentWidth(); }); useEffect(() => { if (visible) { scrollableParents.forEach((parent) => { parent.addEventListener("scroll", update.current, { passive: true }); }); } else { scrollableParents.forEach((parent) => parent.removeEventListener("scroll", update.current)); } }, [visible]); let element = null; let targetSet = []; if (canUseDom && targetId) { element = document.querySelector(`#${targetId}`); targetSet = [element, popoverContentRef.current]; } else { targetSet = [popoverRef.current, popoverContentRef.current]; } useClickAway(() => { onClick === null || onClick === void 0 ? void 0 : onClick(); onClose === null || onClose === void 0 ? void 0 : onClose(); }, targetSet, "touchstart", true, visible, closeOnOutsideClick); const scrollableParents = useMemo(() => { return getAllScrollableParents(element || popoverRef.current); }, [element, popoverRef.current]); const getContentWidth = () => { const rect = getRect(targetId ? document.querySelector(`#${targetId}`) : popoverRef.current); setRootPosition({ width: rect.width, height: rect.height, left: rtl ? rect.right : rect.left, top: rect.top + Math.max(document.documentElement.scrollTop, document.body.scrollTop), right: rtl ? rect.left : rect.right }); }; const classes = classNames({ [`${classPrefix}`]: true, [`${classPrefix}-${theme}`]: theme === "dark" }, className); const popoverArrow = () => { const prefixCls = "nut-popover-arrow"; const direction = location.split("-")[0]; return `${prefixCls} ${prefixCls}-${direction} ${prefixCls}-${location}`; }; const getRootPosition = () => { var _a2, _b; const styles = {}; if (!rootPosition) return {}; const contentWidth = (_a2 = popoverContentRef.current) === null || _a2 === void 0 ? void 0 : _a2.clientWidth; const contentHeight = (_b = popoverContentRef.current) === null || _b === void 0 ? void 0 : _b.clientHeight; const { width, height, left, top, right } = rootPosition; const direction = location.split("-")[0]; const skew = location.split("-")[1]; let cross = 0; let parallel = 0; if (Array.isArray(offset) && offset.length === 2) { const rtloffset = rtl ? -offset[0] : offset[0]; cross += +offset[1]; parallel += +rtloffset; } if (width) { const dir = rtl ? "right" : "left"; if (["bottom", "top"].includes(direction)) { const h = direction === "bottom" ? height + cross : -(contentHeight + cross); styles.top = `${top + h}px`; if (!skew) { styles[dir] = `${-(contentWidth - width) / 2 + rootPosition[dir] + parallel}px`; } if (skew === "start") { styles.left = `${left + parallel}px`; } if (skew === "end") { styles.left = `${right + parallel}px`; } } if (["left", "right"].includes(direction)) { const contentW = direction === "left" ? -(contentWidth + cross) : width + cross; styles.left = `${left + contentW}px`; if (!skew) { styles.top = `${top - contentHeight / 2 + height / 2 - 4 + parallel}px`; } if (skew === "start") { styles.top = `${top + parallel}px`; } if (skew === "end") { styles.top = `${top + height + parallel}px`; } } } return styles; }; const arrowStyle = () => { const styles = {}; const direction = location.split("-")[0]; const skew = location.split("-")[1]; const base = 16; if (arrowOffset !== 0) { const dir = rtl ? "right" : "left"; const dir2 = rtl ? "left" : "right"; if (["bottom", "top"].includes(direction)) { if (!skew) { styles[dir] = `calc(50% + ${arrowOffset}px)`; } if (skew === "start") { styles[dir] = `${base + arrowOffset}px`; } if (skew === "end") { styles[dir2] = `${base - arrowOffset}px`; } } if (["left", "right"].includes(direction)) { if (!skew) { styles.top = `calc(50% - ${arrowOffset}px)`; } if (skew === "start") { styles.top = `${base - arrowOffset}px`; } if (skew === "end") { styles.bottom = `${base + arrowOffset}px`; } } } return styles; }; const handleSelect = (item, index) => { if (!item.disabled) { onSelect === null || onSelect === void 0 ? void 0 : onSelect(item, index); } if (closeOnActionClick) { onClick === null || onClick === void 0 ? void 0 : onClick(); onClose === null || onClose === void 0 ? void 0 : onClose(); } }; return React__default.createElement( React__default.Fragment, null, !targetId && React__default.createElement("div", { className: "nut-popover-wrapper", ref: popoverRef, onClick: () => { onClick === null || onClick === void 0 ? void 0 : onClick(); if (!visible) { onOpen === null || onOpen === void 0 ? void 0 : onOpen(); } else { onClose === null || onClose === void 0 ? void 0 : onClose(); } }, style }, Array.isArray(children) ? children[0] : children), createPortal(React__default.createElement( "div", { className: classes, style: getRootPosition() }, React__default.createElement( Popup__default, Object.assign({ className: `nut-popover-content nut-popover-content-${location}`, visible: showPopup, overlay, position: "default", lockScroll: false }, rest), React__default.createElement( "div", { className: "nut-popover-content-group", ref: popoverContentRef }, showArrow && React__default.createElement("div", { className: popoverArrow(), style: arrowStyle() }), Array.isArray(children) ? children[1] : null, list.map((item, index) => { return React__default.createElement( "div", { className: classNames({ "nut-popover-menu-item": true, "nut-popover-menu-disabled": item.disabled }, item.className), key: item.key || index, onClick: () => handleSelect(item, index) }, item.icon ? React__default.createElement("div", { className: "nut-popover-menu-item-icon" }, item.icon) : null, React__default.createElement("div", { className: "nut-popover-menu-item-name" }, item.name), item.action && item.action.icon ? React__default.createElement("div", { className: "nut-popover-menu-item-action-icon", onClick: (e) => { var _a2, _b; return (_b = (_a2 = item.action) === null || _a2 === void 0 ? void 0 : _a2.onClick) === null || _b === void 0 ? void 0 : _b.call(_a2, e); } }, item.action.icon) : null ); }) ) ) ), document.body) ); }; Popover.displayName = "NutPopover"; export { Popover as default };