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

简介: Concis组件库封装——Popover气泡卡片组件记录

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

气泡卡片...顾名思义,移动到或点击某块区域,出现一个气泡卡片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);

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

目录
相关文章
|
jenkins Java 持续交付
运用Jenkins实现Java项目的持续集成与自动化部署
在新建的Jenkins Job中,我们需要配置源码管理,通常选择Git、SVN等版本控制系统,并填入仓库地址和凭据。接着,设置构建触发器,如定时构建、轮询SCM变更、GitHub Webhook等方式,以便在代码提交后自动触发构建过程。
537 2
|
前端开发
前端 CSS 经典:旋转边框效果
前端 CSS 经典:旋转边框效果
231 0
Vue3时间轴(Timeline)
这是一个基于 Vue2 的时间轴(Timeline)组件,支持多种自定义属性,包括时间轴内容数组 `timelineData`、总宽度 `width`、线条样式 `lineStyle`、模式 `mode` 和位置 `position`。时间轴内容数组包含描述 `desc` 和圆圈颜色 `color`。组件提供了丰富的样式选项,如虚线、居中显示等,并支持内容交替展现。适用于多种场景下的时间轴展示需求。
1368 1
Vue3时间轴(Timeline)
|
设计模式 安全 算法
【Java面试题汇总】设计模式篇(2023版)
谈谈你对设计模式的理解、七大原则、单例模式、工厂模式、代理模式、模板模式、观察者模式、JDK中用到的设计模式、Spring中用到的设计模式
【Java面试题汇总】设计模式篇(2023版)
|
JavaScript
JS 数组去重(含简单数组去重【5种方法】、对象数组去重【2种方法】)
JS 数组去重(含简单数组去重【5种方法】、对象数组去重【2种方法】)
1203 0
|
SQL JavaScript 小程序
来了,MyBatisPlus的join联表查询!
来了,MyBatisPlus的join联表查询!
来了,MyBatisPlus的join联表查询!
|
弹性计算
阿里云服务器带宽计费模式怎么选划算?按使用流量还是固定带宽?
阿里云服务器带宽计费模式按固定带宽和按使用流量如何选择更划算?按固定带宽计费1M带宽一个月23元,按使用流量计费1GB流量0.8元,如何选择带宽计费模式?当带宽利用率高于10%选择按固定带宽计费,带宽利用率较低选择按使用流量计费更划算。
2448 0
阿里云服务器带宽计费模式怎么选划算?按使用流量还是固定带宽?
stata对包含协变量的模型进行缺失值多重插补分析
stata对包含协变量的模型进行缺失值多重插补分析
|
SQL 网络协议 关系型数据库
|
前端开发
iconfont字体图标的使用方法
iconfont字体图标的使用方法