This commit is contained in:
2026-05-25 17:08:18 +08:00
parent df501f6151
commit f1259b71b5
9178 changed files with 1626125 additions and 0 deletions
@@ -0,0 +1,9 @@
import * as React from 'react';
import type { AlignType, ArrowPos, ArrowTypeOuter } from '../interface';
export interface ArrowProps {
prefixCls: string;
align: AlignType;
arrow: ArrowTypeOuter;
arrowPos: ArrowPos;
}
export default function Arrow(props: ArrowProps): React.JSX.Element;
+64
View File
@@ -0,0 +1,64 @@
import { clsx } from 'clsx';
import * as React from 'react';
export default function Arrow(props) {
const {
prefixCls,
align,
arrow,
arrowPos
} = props;
const {
className,
content,
style
} = arrow || {};
const {
x = 0,
y = 0
} = arrowPos;
const arrowRef = React.useRef(null);
// Skip if no align
if (!align || !align.points) {
return null;
}
const alignStyle = {
position: 'absolute'
};
// Skip if no need to align
if (align.autoArrow !== false) {
const popupPoints = align.points[0];
const targetPoints = align.points[1];
const popupTB = popupPoints[0];
const popupLR = popupPoints[1];
const targetTB = targetPoints[0];
const targetLR = targetPoints[1];
// Top & Bottom
if (popupTB === targetTB || !['t', 'b'].includes(popupTB)) {
alignStyle.top = y;
} else if (popupTB === 't') {
alignStyle.top = 0;
} else {
alignStyle.bottom = 0;
}
// Left & Right
if (popupLR === targetLR || !['l', 'r'].includes(popupLR)) {
alignStyle.left = x;
} else if (popupLR === 'l') {
alignStyle.left = 0;
} else {
alignStyle.right = 0;
}
}
return /*#__PURE__*/React.createElement("div", {
ref: arrowRef,
className: clsx(`${prefixCls}-arrow`, className),
style: {
...alignStyle,
...style
}
}, content);
}
+11
View File
@@ -0,0 +1,11 @@
import type { CSSMotionProps } from '@rc-component/motion';
import * as React from 'react';
export interface MaskProps {
prefixCls: string;
open?: boolean;
zIndex?: number;
mask?: boolean;
motion?: CSSMotionProps;
mobile?: boolean;
}
export default function Mask(props: MaskProps): React.JSX.Element;
+29
View File
@@ -0,0 +1,29 @@
function _extends() { _extends = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }
import { clsx } from 'clsx';
import CSSMotion from '@rc-component/motion';
import * as React from 'react';
export default function Mask(props) {
const {
prefixCls,
open,
zIndex,
mask,
motion,
mobile
} = props;
if (!mask) {
return null;
}
return /*#__PURE__*/React.createElement(CSSMotion, _extends({}, motion, {
motionAppear: true,
visible: open,
removeOnLeave: true
}), ({
className
}) => /*#__PURE__*/React.createElement("div", {
style: {
zIndex
},
className: clsx(`${prefixCls}-mask`, mobile && `${prefixCls}-mobile-mask`, className)
}));
}
@@ -0,0 +1,7 @@
import * as React from 'react';
export interface PopupContentProps {
children?: React.ReactNode;
cache?: boolean;
}
declare const PopupContent: React.MemoExoticComponent<({ children }: PopupContentProps) => React.ReactElement<any, string | React.JSXElementConstructor<any>>>;
export default PopupContent;
@@ -0,0 +1,8 @@
import * as React from 'react';
const PopupContent = /*#__PURE__*/React.memo(({
children
}) => children, (_, next) => next.cache);
if (process.env.NODE_ENV !== 'production') {
PopupContent.displayName = 'PopupContent';
}
export default PopupContent;
+57
View File
@@ -0,0 +1,57 @@
import type { CSSMotionProps } from '@rc-component/motion';
import { type ResizeObserverProps } from '@rc-component/resize-observer';
import * as React from 'react';
import type { TriggerProps } from '../';
import type { AlignType, ArrowPos, ArrowTypeOuter } from '../interface';
import type { PortalProps } from '@rc-component/portal';
export interface MobileConfig {
mask?: boolean;
/** Set popup motion. You can ref `rc-motion` for more info. */
motion?: CSSMotionProps;
/** Set mask motion. You can ref `rc-motion` for more info. */
maskMotion?: CSSMotionProps;
}
export interface PopupProps {
onEsc?: PortalProps['onEsc'];
prefixCls: string;
className?: string;
style?: React.CSSProperties;
popup?: TriggerProps['popup'];
target: HTMLElement;
onMouseEnter?: React.MouseEventHandler<HTMLDivElement>;
onMouseLeave?: React.MouseEventHandler<HTMLDivElement>;
onPointerEnter?: React.MouseEventHandler<HTMLDivElement>;
onPointerDownCapture?: React.MouseEventHandler<HTMLDivElement>;
zIndex?: number;
mask?: boolean;
onVisibleChanged: (visible: boolean) => void;
align?: AlignType;
arrow?: ArrowTypeOuter;
arrowPos: ArrowPos;
open: boolean;
/** Tell Portal that should keep in screen. e.g. should wait all motion end */
keepDom: boolean;
fresh?: boolean;
onClick?: React.MouseEventHandler<HTMLDivElement>;
motion?: CSSMotionProps;
maskMotion?: CSSMotionProps;
forceRender?: boolean;
getPopupContainer?: TriggerProps['getPopupContainer'];
autoDestroy?: boolean;
portal: React.ComponentType<any>;
children?: React.ReactElement;
ready: boolean;
offsetX: number;
offsetY: number;
offsetR: number;
offsetB: number;
onAlign: VoidFunction;
onPrepare: () => Promise<void>;
stretch?: string;
targetWidth?: number;
targetHeight?: number;
onResize?: ResizeObserverProps['onResize'];
mobile?: MobileConfig;
}
declare const Popup: React.ForwardRefExoticComponent<PopupProps & React.RefAttributes<HTMLDivElement>>;
export default Popup;
+191
View File
@@ -0,0 +1,191 @@
function _extends() { _extends = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }
import { clsx } from 'clsx';
import CSSMotion from '@rc-component/motion';
import ResizeObserver from '@rc-component/resize-observer';
import useLayoutEffect from "@rc-component/util/es/hooks/useLayoutEffect";
import { composeRef } from "@rc-component/util/es/ref";
import * as React from 'react';
import Arrow from "./Arrow";
import Mask from "./Mask";
import PopupContent from "./PopupContent";
import useOffsetStyle from "../hooks/useOffsetStyle";
import { useEvent } from '@rc-component/util';
const Popup = /*#__PURE__*/React.forwardRef((props, ref) => {
const {
onEsc,
popup,
className,
prefixCls,
style,
target,
onVisibleChanged,
// Open
open,
keepDom,
fresh,
// Click
onClick,
// Mask
mask,
// Arrow
arrow,
arrowPos,
align,
// Motion
motion,
maskMotion,
// Mobile
mobile,
// Portal
forceRender,
getPopupContainer,
autoDestroy,
portal: Portal,
children,
zIndex,
onMouseEnter,
onMouseLeave,
onPointerEnter,
onPointerDownCapture,
ready,
offsetX,
offsetY,
offsetR,
offsetB,
onAlign,
onPrepare,
// Resize
onResize,
stretch,
targetWidth,
targetHeight
} = props;
const popupContent = typeof popup === 'function' ? popup() : popup;
// We can not remove holder only when motion finished.
const isNodeVisible = open || keepDom;
// ========================= Mobile =========================
const isMobile = !!mobile;
// ========================== Mask ==========================
const [mergedMask, mergedMaskMotion, mergedPopupMotion] = React.useMemo(() => {
if (mobile) {
return [mobile.mask, mobile.maskMotion, mobile.motion];
}
return [mask, maskMotion, motion];
}, [mobile, mask, maskMotion, motion]);
// ======================= Container ========================
const getPopupContainerNeedParams = getPopupContainer?.length > 0;
const [show, setShow] = React.useState(!getPopupContainer || !getPopupContainerNeedParams);
// Delay to show since `getPopupContainer` need target element
useLayoutEffect(() => {
if (!show && getPopupContainerNeedParams && target) {
setShow(true);
}
}, [show, getPopupContainerNeedParams, target]);
// ========================= Resize =========================
const onInternalResize = useEvent((size, ele) => {
onResize?.(size, ele);
onAlign();
});
// ========================= Styles =========================
const offsetStyle = useOffsetStyle(isMobile, ready, open, align, offsetR, offsetB, offsetX, offsetY);
// ========================= Render =========================
if (!show) {
return null;
}
// >>>>> Misc
const miscStyle = {};
if (stretch) {
if (stretch.includes('height') && targetHeight) {
miscStyle.height = targetHeight;
} else if (stretch.includes('minHeight') && targetHeight) {
miscStyle.minHeight = targetHeight;
}
if (stretch.includes('width') && targetWidth) {
miscStyle.width = targetWidth;
} else if (stretch.includes('minWidth') && targetWidth) {
miscStyle.minWidth = targetWidth;
}
}
if (!open) {
miscStyle.pointerEvents = 'none';
}
return /*#__PURE__*/React.createElement(Portal, {
open: forceRender || isNodeVisible,
getContainer: getPopupContainer && (() => getPopupContainer(target)),
autoDestroy: autoDestroy,
onEsc: onEsc
}, /*#__PURE__*/React.createElement(Mask, {
prefixCls: prefixCls,
open: open,
zIndex: zIndex,
mask: mergedMask,
motion: mergedMaskMotion,
mobile: isMobile
}), /*#__PURE__*/React.createElement(ResizeObserver, {
onResize: onInternalResize,
disabled: !open
}, resizeObserverRef => {
return /*#__PURE__*/React.createElement(CSSMotion, _extends({
motionAppear: true,
motionEnter: true,
motionLeave: true,
removeOnLeave: false,
forceRender: forceRender,
leavedClassName: `${prefixCls}-hidden`
}, mergedPopupMotion, {
onAppearPrepare: onPrepare,
onEnterPrepare: onPrepare,
visible: open,
onVisibleChanged: nextVisible => {
motion?.onVisibleChanged?.(nextVisible);
onVisibleChanged(nextVisible);
}
}), ({
className: motionClassName,
style: motionStyle
}, motionRef) => {
const cls = clsx(prefixCls, motionClassName, className, {
[`${prefixCls}-mobile`]: isMobile
});
return /*#__PURE__*/React.createElement("div", {
ref: composeRef(resizeObserverRef, ref, motionRef),
className: cls,
style: {
'--arrow-x': `${arrowPos.x || 0}px`,
'--arrow-y': `${arrowPos.y || 0}px`,
...offsetStyle,
...miscStyle,
...motionStyle,
boxSizing: 'border-box',
zIndex,
...style
},
onMouseEnter: onMouseEnter,
onMouseLeave: onMouseLeave,
onPointerEnter: onPointerEnter,
onClick: onClick,
onPointerDownCapture: onPointerDownCapture
}, arrow && /*#__PURE__*/React.createElement(Arrow, {
prefixCls: prefixCls,
arrow: arrow,
arrowPos: arrowPos,
align: align
}), /*#__PURE__*/React.createElement(PopupContent, {
cache: !open && !fresh
}, popupContent));
});
}), children);
});
if (process.env.NODE_ENV !== 'production') {
Popup.displayName = 'Popup';
}
export default Popup;