我尝试过让它发挥作用,但这就是发生的事情。
我也在网上找到了这个 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} />"
?
如果这是一部有效的作品,谁能给我一个提示,以上两个问题如何解决?
谢谢!
对于那些可能仍在使用最新版本的
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>
);
}
}
我收到一条警告,指出 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;
我认为这个解决方案更简单,您可以使用整个模态标题来拖动模态。
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>
对于仍在为此苦苦挣扎的人,我找到了一个无需使用可顺利运行的可拖动库的解决方案。
为您的模态创建 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
};
}