1
This commit is contained in:
+9
@@ -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
@@ -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
@@ -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
@@ -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)
|
||||
}));
|
||||
}
|
||||
+7
@@ -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;
|
||||
+8
@@ -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
@@ -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
@@ -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;
|
||||
Reference in New Issue
Block a user