likes
comments
collection
share

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

作者站长头像
站长
· 阅读数 16

写在前面:

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

效果如下:

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

实现如下:

安装依赖库

首先引入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的状态。

代码如下:

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的解构

   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} />
        ...}
        
    );