@nutui/nutui-react
Version:
京东风格的轻量级移动端 React 组件库,支持一套代码生成 H5 和小程序
214 lines (213 loc) • 8.92 kB
JavaScript
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
};