Concis组件库封装——Popover气泡卡片

1,333 阅读2分钟

您好,如果喜欢我的文章,可以关注我的公众号「量子前端」,将不定期关注推送前端好文~

气泡卡片...顾名思义,移动到或点击某块区域,出现一个气泡卡片dialog,组件库文档如下:

在这里插入图片描述 在这里插入图片描述 主要支持的有移动或点击触发,以及不同方向的气泡卡片,并提供了气泡卡片宽度,内容为调用端的DOM,样式也在调用端所控制。 API能力如下: 在这里插入图片描述 源码如下:

import React, {
  FC,
  memo,
  ReactNode,
  useState,
  useEffect,
  useMemo,
  useRef,
} from 'react';
import lodash from 'loadsh';
import './index.module.less';

interface popoverProps {
  children?: ReactNode;
  /**
   * @description 触发形式 hover/click
   * @default hover
   */
  type?: string;
  /**
   * @description 对齐方式 left/right/top/bottom
   * @default bottom
   */
  align?: string;
  /**
   * @description 卡片内容
   * @default <></>
   */
  content: ReactNode;
  /**
   * @description 卡片宽度
   * @default 200px
   */
  dialogWidth?: number;
  /**
   * @description 提供给调用层的卡片显示隐藏状态
   * @default false
   */
  propsVisiable?: boolean;
  /**
   * @description 卡片显示隐藏回调
   */
  onVisableChange?: Function;
}
type alignStyle = {
  left?: string;
  right?: string;
  top?: string;
  bottom?: string;
};
const Popover: FC<popoverProps> = (props) => {
  const {
    children,
    type = 'hover',
    align = 'bottom',
    content,
    dialogWidth = 200,
    propsVisiable,
    onVisableChange,
  } = props;

  const showBtnRef = useRef();
  const [showDialog, setShowDialog] = useState<boolean>(propsVisiable || false); //是否显示
  const [showBtnSize, setShowBtnSize] = useState({
    width: '',
    height: '',
  });

  useEffect(() => {
    setShowBtnSize({
      width: (showBtnRef.current as any).offsetWidth,
      height: (showBtnRef.current as any).offsetHeight,
    });
    if (type == 'click') {
      window.addEventListener('click', () => {
        setShowDialog(false);
        if (propsVisiable) {
          setShowDialog(false);
        }
      });
    }
  }, []);

  useEffect(() => {
    if (propsVisiable != undefined) {
      console.log('执行');
      setShowDialog(propsVisiable as boolean);
    }
  }, [propsVisiable]);
  const clickToggleDialog = (e: any) => {
    //点击打开dialog
    e.stopPropagation();
    if (type == 'click') {
      setShowDialog(!showDialog);
      onVisableChange && onVisableChange(!showDialog);
    }
  };
  const hoverOpenDialog = lodash.debounce(() => {
    //移入打开dialog
    if (type == 'hover' && showDialog == false) {
      setShowDialog(true);

      onVisableChange && onVisableChange(true);
    }
  }, 200);
  const hoverCloseDialog = lodash.debounce(() => {
    //移开关闭dialog
    if (type == 'hover' && showDialog == true) {
      setShowDialog(false);
      onVisableChange && onVisableChange(false);
    }
  }, 200);
  const dialogStyle = useMemo(() => {
    let alignStyle: alignStyle = {};
    if (align == 'bottom') {
    } else if (align == 'top') {
      alignStyle.bottom = showBtnSize.height + 'px';
    } else if (align == 'right') {
      alignStyle.left = showBtnSize.width + 'px';
      alignStyle.bottom = Number(showBtnSize.height) / 2 + 'px';
    } else if (align == 'left') {
      alignStyle.right = showBtnSize.width + 'px';
      alignStyle.bottom = Number(showBtnSize.height) / 2 + 'px';
    }
    return {
      width: showDialog ? `${dialogWidth}px` : '0px',
      height: showDialog ? '' : '0px',
      opacity: showDialog ? 1 : 0,
      ...alignStyle,
    };
  }, [content, showDialog, propsVisiable, showBtnSize]);
  return (
    <div className="popover-card">
      <div
        className="open-container"
        onMouseEnter={() => hoverOpenDialog()}
        onMouseLeave={() => hoverCloseDialog()}
      >
        <div className="show-btn" onClick={(e) => clickToggleDialog(e)} ref={showBtnRef as any}>
          {children}
        </div>
        <div
          className="pop-dialog"
          style={dialogStyle}
          onClick={(e) => e.stopPropagation()}
          onMouseEnter={() => hoverOpenDialog()}
          onMouseLeave={() => hoverCloseDialog()}
        >
          {content}
        </div>
      </div>
    </div>
  );
};

export default memo(Popover);

有任何问题或建议欢迎留言,此文为展示记录性博客。