如何使react-bootstrap模式可拖动

问题描述 投票:0回答:4

我尝试过让它发挥作用,但这就是发生的事情。

  1. 使用react-draggable npm 包,我能够使内容可拖动和可放置。但整个对话框的背面保持原位,之后看起来就破碎了。

我也在网上找到了这个 https://gist.github.com/burgalon/870a68de68c5ed15c416fab63589d503

import { Modal } from 'react-bootstrap'
import ModalDialog from 'react-bootstrap/lib/ModalDialog'
import Draggable from 'react-draggable'

class DraggableModalDialog extends React.Component {
    render() {
        return <Draggable handle=".modal-title"><ModalDialog 
{...this.props} /></Draggable>
    }
}

// enforceForce=false causes recursion exception otherwise....
export default ({titleIconClass, modalClass, children, title,...props}) =>
<Modal dialogComponent={DraggableModalDialog} show={true} enforceFocus={false} backdrop="static" {...props}>
    <Modal.Header closeButton>
        <Modal.Title>
            {title}
        </Modal.Title>
    </Modal.Header>
    <Modal.Body>
        {children}
    </Modal.Body>
</Modal>

这个代码是我从周围搜索中得到的,我实际上无法让它工作。


尤其是这个,

<ModalDialog {...this.props} />

,我不明白为什么要送道具,送什么样的道具。

还有

<Modal dialogComponent={DraggableModalDialog} show={true} enforceFocus={false} backdrop="static" {...props}>

<------ {...props} what does that do? it doesn't seem like it's giving props to Modal. what is the purpose of it? Is it relevant to

"<ModalDialog {...this.props} />"

如果这是一部有效的作品,谁能给我一个提示,以上两个问题如何解决?

谢谢!

reactjs bootstrap-modal draggable react-bootstrap
4个回答
6
投票

对于那些可能仍在使用最新版本的

react-bootstrap
的人(我在撰写本文时是
1.0.0-beta.5
)。这是(https://gist.github.com/burgalon/870a68de68c5ed15c416fab63589d503)的修改版本

import React, { Component } from "react";
import Modal from "react-bootstrap/Modal";
import Draggable from 'react-draggable';
import ModalDialog from 'react-bootstrap/ModalDialog';

class DraggableModalDialog extends React.Component {
    render() {
        return <Draggable handle=".modal-title"><ModalDialog {...this.props} /> 
   </Draggable>
    }
}

export default class BSModal extends Component {

render() {
    return (
        <Modal
                dialogAs={DraggableModalDialog} 
                show={this.props.show} 
                onHide={this.props.close}>
            <Modal.Header>
                <Modal.Title>{this.props.title}</Modal.Title>
            </Modal.Header>
            <Modal.Body>
                {this.props.children}
            </Modal.Body>
            <Modal.Footer >
            </Modal.Footer>
        </Modal>
    );
}
}

1
投票

我收到一条警告,指出 findDOMNode 在 StrictMode 中已被弃用

我可以通过使用nodeRef而不是handle来摆脱这个问题。我的功能组件/打字稿 DraggableModalDialog 看起来像这样:

/* eslint-disable react/jsx-props-no-spreading */
import React, { RefObject } from 'react';
import Draggable from 'react-draggable';
import ModalDialog, { ModalDialogProps } from 'react-bootstrap/ModalDialog';

export interface DraggableModalDialogProps extends ModalDialogProps {
  nodeRef?: RefObject<HTMLElement>;
}

function DraggableModalDialog({ nodeRef, ...props }: DraggableModalDialogProps) {
  return (
    <Draggable nodeRef={nodeRef}>
      <ModalDialog {...props} />
    </Draggable>
  );
}

export default DraggableModalDialog;

模态组件是这样的(这里稍微简化一下):

import React, { useRef } from 'react';
import { Modal, Button } from 'react-bootstrap';
import DraggableModalDialog from 'components/DraggableModalDialog';

export interface MyModalProps {
  visible: boolean;
  onClose: () => void;
}

function MyModal({ visible, onClose }: MyModalProps) {
  const nodeRef = useRef<HTMLDivElement>(null);
  return (
    <Modal
      nodeRef={nodeRef}
      dialogAs={DraggableModalDialog}
      show={visible}
      centered
      size="lg"
      backdrop="static"
      onHide={onClose}
    >
      <Modal.Header ref={nodeRef}>
        <Modal.Title>TITLE</Modal.Title>
      </Modal.Header>
      <Modal.Body>BODY</Modal.Body>
      <Modal.Footer>
        <Button variant="primary" onClick={onClose}>CLOSE</Button>
      </Modal.Footer>
    </Modal>
  );
}

export default MyModal;

0
投票

我认为这个解决方案更简单,您可以使用整个模态标题来拖动模态。

import { Modal, ModalHeader, ModalBody } from "reactstrap";
import Draggable from "react-draggable";

...

<Draggable handle=".handle">
  <Modal size="lg" toggle={function noRefCheck(){}}>
    <ModalHeader toggle={function noRefCheck(){}} className="handle">
      Modal Title
    </ModalHeader>
    <ModalBody>
      Modal Body
    </ModalBody>
  </Modal>
</Draggable>

0
投票

对于仍在为此苦苦挣扎的人,我找到了一个无需使用可顺利运行的可拖动库的解决方案。

为您的模态创建 4 个状态变量。两个用于模态标题,两个用于主组件。还为标题创建一个引用。

const [headerLeft, setHeaderLeft] = useState(0);
const [left, setLeft] = useState('0');
const [headerTop, setHeaderTop] = useState(0);
const [top, setTop] = useState('0');
const modalHeader = useRef<HTMLDivElement>(null);

使用您为标题创建的引用,在模态标题 OnMouseDown 上添加一个侦听器。然后在文档上添加一个监听器来实现您的拖动机制。

modalHeader.current?.addEventListener("mousedown", () => {
    document.addEventListener("mousemove", onDrag);
});
    

然后创建您的拖动功能。我在模态标头中添加了一个 id,以便能够使用 getElementById 获取 HTMLElement。然后我根据顶部和左侧变量计算新位置并更新我的状态。

function onDrag(e) {
    const modalHeaderElement = document.getElementById('modal-header');
    if (modalHeaderElement) {
        const getStyle = window.getComputedStyle(modalHeaderElement);
        const leftVal = parseInt(getStyle.left);
        const topVal = parseInt(getStyle.top);
    
        setHeaderLeft(e.movementX + leftVal);
        setHeaderTop(e.movementY + topVal);
        setLeft(`${leftVal + e.movementX}px`);
        setTop(`${topVal + e.movementY}px`);
    }
}

然后您需要做的就是更新模态和模态标题组件上的 style 属性

<Modal style={{ left: left, top: top }} >
     <Modal.Header id='modal-header' ref={modalHeader}  style={{ left: headerLeft, top: headerTop}}>A Header </Modal.Header>
<Modal.Body></Modal.Body>
</Modal>

最后在文档上添加另一个监听器,让 mouseUp 停止拖动

document.addEventListener("mouseup", () => {
   document.removeEventListener("mousemove", onDrag);
});

在我的用例中,我将 addEventListeners 和 OnDrag 函数放置在 useImperativeHandle 挂钩中,因为这就是我利用 Modal 的方式(我用forwardRef 包装它)。但我猜你可以在 useEffect 中做到这一点并且工作得同样好。

编辑:您还可以初始化状态变量,以免模态保持在拖动的最后位置。

编辑 2:发布下面的代码以使其成为自定义挂钩并在您的代码中重新使用它。这个想法是,自定义挂钩将返回状态变量以更新模态的坐标。自定义挂钩的输入是 modalHeader 引用、您在 Modal.Header 元素中提供的 id 以及显示或隐藏模式的显示状态变量。

export function useDraggable(modalHeader: HTMLDivElement | null, modalHeaderId: string, show: boolean) {
    const [headerLeft, setHeaderLeft] = useState(0);
    const [left, setLeft] = useState('0px');
    const [headerTop, setHeaderTop] = useState(0);
    const [top, setTop] = useState('0px');
    const [counter, setCounter] = useState(0);

    // eslint-disable-next-line @typescript-eslint/no-explicit-any
    const onDrag = useCallback((e: any) => {
        const modalHeaderElement = document.getElementById(modalHeaderId);
        if (modalHeaderElement) {
            const getStyle = window.getComputedStyle(modalHeaderElement);
            const leftVal = parseInt(getStyle.left);
            const topVal = parseInt(getStyle.top);

            setHeaderLeft(_prevState => e.movementX + leftVal);
            setHeaderTop(_prevState => e.movementY + topVal);
            setLeft(_prevState => `${leftVal + e.movementX}px`);
            setTop(_prevState => `${topVal + e.movementY}px`);
        }
    }, []);

    const onMouseUp = useCallback(() => {
        document.removeEventListener("mousemove", onDrag);
        const modalHeaderElement = document.getElementById(modalHeaderId);
        if (modalHeaderElement) {
            modalHeaderElement.classList.remove("active");
        }
    }, []);

    useEffect(() => { 
        if (modalHeader && show) {
            modalHeader.addEventListener("mousedown", () => {
                modalHeader.classList.add("active");
                document.addEventListener("mousemove", onDrag);
            });

            document.addEventListener("mouseup", onMouseUp);
        }

        if (!show) {
            if (modalHeader) {
                document.removeEventListener("mouseup", onMouseUp);
            }
            setLeft('0px');
            setTop('0px');
            setHeaderLeft(0);
            setHeaderTop(0);
        }

    }, [modalHeaderId, modalHeader, show, onMouseUp, onDrag])

    return {
        headerTop,
        headerLeft,
        top,
        left
    };
}
© www.soinside.com 2019 - 2024. All rights reserved.