import React from "react"; import { BubbleContent, BubbleContentProps } from "./BubbleContent"; import { isChildOfType } from "../_util/is-child-of-type"; import { Button } from "../button"; import { useScheduleUpdate } from "../_util/use-schedule-update"; import { Input } from "../input"; import { Select } from "../select"; import { Popover, PopoverProps } from "../popover"; import { TextArea } from "../input/TextArea"; export interface BubbleProps extends Pick< PopoverProps, | "trigger" | "visible" | "onVisibleChange" | "placementOffset" | "escapeWithReference" | "flipped" | "closeOnScroll" | "destroyOnClose" | "openDelay" | "closeDelay" | "onExited" | "overlayClassName" | "overlayStyle" | "animationScaleFrom" | "transitionTimeout" | "updateOnDimensionChange" | "referenceElement" | "popupContainer" | "positionFixed" >, BubbleContentProps { /** * 气泡内容 * * 如果气泡内容传入 `null` 或者 `undefined`,则不会出现气泡 */ content?: React.ReactNode; /** * 气泡箭头是否指向目标元素中心 * * @default false * @since 2.2.0 */ arrowPointAtCenter?: boolean; /** * 触发更新位置的变量依赖列表,可用于性能优化 * * **更新位置可防止气泡溢出屏幕或产生偏移** * * @default [content] */ updateDeps?: any[]; } export const Bubble = React.forwardRef(function Bubble( { visible, onVisibleChange, content, arrowPointAtCenter, trigger = "hover", overlayClassName, overlayStyle, openDelay, closeDelay, closeOnScroll, updateOnDimensionChange, transitionTimeout, escapeWithReference, referenceElement, popupContainer, destroyOnClose, placement = "top", placementOffset, animationScaleFrom, onExited, children, updateDeps = [content], flipped, positionFixed, ...bubbleProps }: BubbleProps, ref: React.Ref ) { // 内容变化时更新位置 const scheduleUpdateRef = useScheduleUpdate( Array.isArray(updateDeps) ? updateDeps : [content] ); // issue: https://github.com/facebook/react/issues/4251 if ( trigger !== "click" && React.Children.count(children) === 1 && (isChildOfType(children, "button") || isChildOfType(children, "input") || isChildOfType(children, "textarea") || isChildOfType(children, "select") || isChildOfType(children, Button) || isChildOfType(children, Input) || isChildOfType(children, TextArea) || (isChildOfType(children, Select) && children.props.type !== "simulate")) && children.props.disabled ) { // eslint-disable-next-line no-param-reassign children = ( {React.cloneElement(children as React.ReactElement, { style: { ...(children.props.style || {}), pointerEvents: "none", }, })} ); } let offset = placementOffset ?? 10; if (arrowPointAtCenter) { offset = Array.isArray(offset) ? offset[1] : offset; const [, placementModifier] = placement.split("-"); if (placementModifier === "start") { offset = ["50%-26", offset]; } else if (placementModifier === "end") { offset = ["26-50%", offset]; } } return ( { scheduleUpdateRef.current = scheduleUpdate; if (!content && content !== 0) { return null; } return ( {content} ); }} overlayClassName={overlayClassName} overlayStyle={overlayStyle} openDelay={openDelay} closeDelay={closeDelay} updateOnDimensionChange={updateOnDimensionChange} transitionTimeout={transitionTimeout} escapeWithReference={escapeWithReference} popupContainer={popupContainer} destroyOnClose={destroyOnClose} onExited={onExited} > {children} ); }); Bubble.displayName = "Bubble";