【React】如何实现并封装一个Popup组件(tsx)

写在前面:

在移动端展示数据时,常常会用到popup组件展示,本文实现一个可以上下拖动并且超出视口会自动回弹的popup组件

效果如下:

实现如下:

安装依赖库

首先引入antdreact-draggable来。

封装步骤:

  1. 定义Popup组件。这是一个函数式组件,接受onClosewidget作为参数。在组件内部,通过调用useState钩子函数创建了一个名为position的状态变量,并初始化{ x: 0, y: 0 }position用于跟踪弹出窗口的位置信息。

  2. 定义一个handleDrag函数,用于处理拖拽事件。当拖拽事件发生时,该函数会根据拖拽的位置更新position的状态。同时,通过一些条件判断限制了弹出窗口的垂直拖拽范围。

  3. 使用StyledPopup组件作为根容器,并嵌套了其他子元素。其中,使用了Draggable组件实现了弹出窗口的可拖拽功能,handle=".handle-area"表示只能通过拖拽.handle-area类的元素来进行拖拽。在拖拽过程中,会触发handleDrag函数来更新position的状态。

代码如下:

tsx 复制代码
import { Table } from 'antd';
import React, { useState } from 'react';
import Draggable from 'react-draggable';
import styled from 'styled-components';
import closeSvg from '../../../../assets/images/close.svg';
import { Widget } from '../../types/widgetTypes';

interface PopupProps {
  onClose: () => void;
  widget: Widget | undefined;
}

const Popup: React.FC<PopupProps> = ({ onClose, widget }) => {
  const closePopup = () => {
    onClose();
  };
  const tableData = widget?.config?.content?.dataChart?.config?.sampleData;

  const [position, setPosition] = useState({ x: 0, y: 0 });
  const handleDrag = (e, ui) => {
    if (ui.y < 0 && ui.y < -window.innerHeight * 0.48) {
      ui.y = -window.innerHeight * 0.48;
    } else if (ui.y > 0 && ui.y > window.innerHeight * 0.45) {
      ui.y = window.innerHeight * 0.45;
    }
    setPosition({ x: 0, y: ui.y });
  };
  return (
    <StyledPopup>
      <Draggable
        handle=".handle-area"
        axis="y"
        onDrag={handleDrag}
        position={position}
      >
        <div className="popup">
          <div className="handle-area">
            <div className="popup-handle"></div>
          </div>
          <div className="popup-header">
            <span>查看数据</span>
            <img onClick={closePopup} src={closeSvg} alt="Close" />
          </div>
          <div className="popup-content">
            {
              <TableWrapper>
                <Table
                  size="small"
                  dataSource={tableData?.rows}
                  columns={tableData?.columns?.map((col, index) => ({
                    key: col.name,
                    title: col.name,
                    dataIndex: index,
                  }))}
                  bordered
                />
              </TableWrapper>
            }
          </div>
        </div>
      </Draggable>
    </StyledPopup>
  );
};
const TableWrapper = styled.div`
  padding: 10px;
`;
const StyledPopup = styled.div`
  position: fixed;
  top: 50%;
  width: 100vw;
  & .popup {
    display: flex;
    flex-direction: column;
    border-top-left-radius: 20px;
    border-top-right-radius: 20px;
    height: 150vh;
    background-color: #fff;

    .popup-handle {
      height: 5px;
      width: 10%;
      background-color: rgb(217, 217, 217);
      cursor: row-resize;
      border-radius: 5px;
      margin: 8px auto;
      margin-bottom: 16px;
    }

    .popup-header {
      position: relative;
      display: flex;
      flex-direction: row;
      justify-content: center;
      font-size: 20px;
      font-weight: 700;
      color: rgb(19, 23, 55);

      img {
        position: absolute;
        right: 5px;
        top: 50%;
        transform: translate(-50%, -50%);
        width: 36px;
        height: 36px;
      }
    }
  }

  .popup-content {
    display: flex;
    flex-direction: column;
    margin: 20px 15px;
    max-height: 80vh;
    overflow: auto;
  }
`;

export default Popup;

3.使用参考:

比如可以在打开组件的函数、tsx内,传入数据以及打开的boolen值,并暴露一个onclose函数实现关闭popup的解构

tsx 复制代码
   const [popupOpen, setPopupOpen] = useState(false);
    const [popupWidget, setPopupWidget] = useState<Widget>();
    const onClosePopup = () => {
      setPopupOpen(false);
    };
onWidgetOpenPopup: (widget: Widget) => {
          setPopupOpen(true);
          setPopupWidget(widget);
        },
return (
        ...
        {popupOpen && <Popup onClose={onClosePopup} widget={popupWidget} />
        ...}
        
    );
相关推荐
程序员黑豆12 小时前
Java类型推断完全指南:从var到菱形运算符,掌握使用限制与最佳实践
java·前端·ai编程
To_OC13 小时前
踩了个 TS 的坑之后,我终于把 type 和 interface 掰明白了
前端·react.js·typescript
GreenTea13 小时前
深度解读 Anthropic 多智能体报告:更强的模型 ≠ 更好的协调
前端·后端·算法
浮生望13 小时前
前端API工程化:用 Mock 数据与 Axios 配置实现独立于后端的并行开发
前端
万少14 小时前
给 DeepSeek Harness 装个"应用商店":一条命令,595 个插件随你逛
前端·javascript·后端
波波00714 小时前
C# 15 重磅新特性: 带标签 break 与 continue:重新定义嵌套循环控制流
服务器·前端·c#
Brown.alexis15 小时前
es6知识点1-自备使用
前端·ecmascript·es6
小爬的老粉丝16 小时前
JavaScript 纯前端预览 WPS:先识别容器,再路由解析器
前端·javascript·wps
计算机魔术师16 小时前
新兴多智能体系统的模式与问题
前端
用户0595401744617 小时前
AI Agent 上下文污染踩坑实录:用 pytest + Redis 揪出 3 类记忆串号 bug,排查 6 小时
前端·css