preface

In practice, the modal box presents data frequently. But unfortunately sometimes when you’re done with functionality,UI comes up with a requirement for modal boxes to be able to drag. How do you implement drag and drop without modifying the original code. The final effect is as follows:

practice

1. Create higher-order component DragHoc

Create a new file ModalDrag/index.js and copy the following code into it

DragObj is the native JS code for specific drags, more on that later

  • DragHoc is a function that creates higher-order components, where the InnerComponent parameter is the modal box component that needs to be modified, and the function returns the enhanced component
  • The Render method returns the
    directly, not a new component. All the higher-order component does is add a ref attribute to the input component. With ref, the init method can get the native DOM of any component passed in via reactdom.finddomNode. Once you have the DOM, you can do the underlying DOM manipulation or event binding for drag and drop
  • The init method adds a timer with a delay of 0s, because the InnerComponent in the author’s project is encapsulated by Modal in Ant Design. During debugging, reactdom.finddomNode can only return dom elements that have been mounted to the page, otherwise null is returned. In Ant Design, Modal rendering content is asynchronous, so use a timer to wait until the next frame to retrieve the component’s DOM element using findDOMNode. If the InnerComponent does not contain code for asynchronous rendering, the following timer can be removed
  • The deStory method is called when the component is unloaded to release all bound events

Dragging an element usually requires passing in two arguments. One is the region that can be moved by push and drop, corresponding to the entire exported table control in the figure above. The control has a class named main_class. The other is the area that listens for drags, corresponding to the head in the image above, which can drag the table only if the mouse is moved over the head. The class name for the header is title_class. Both arguments are passed in externally. If neither argument is passed, the default is to listen directly to child_node and drag it

import React from 'react'; import ReactDOM from 'react-dom'; import DragObj from './drag'; Export const DragHoc = (InnerComponent,main_class,title_class) => class extends React.Component { componentDidMount() { this.init(); } init = () => { setTimeout(() => { const child_node = ReactDOM.findDOMNode(this.refs.child); If (child_node) {this.drag_obj = new DragObj(main_class? Child_node. querySelector('.${main_class} '):child_node, // just drag div title_class with class name ${main_class}? Child_node. querySelector('.${title_class} '):child_node // drags are only allowed when the mouse is over a div with the class name ${title_class}); }}, 0); }; componentWillUnmount() { if (this.drag_obj) { this.drag_obj.destory(); } } render() { return <InnerComponent {... this.props} ref="child" />; }};Copy the code

If drag and drop does not work in practice, be sure to print out child_node in the code above to see if the actual DOM is retrieved and its interior rendered completely. If the rendering is incomplete, the InnerComponent contains code for asynchronous rendering, and drag-and-drop event bindings are performed after the rendering is complete

2. Create the DragObj class

Create a new file ModalDrag/drag.js and copy the following code into it

Here is the native code that implements drag and drop. It is responsible for event binding and position changing of DOM elements

export default class DragObj { start_x0 = 0; start_y0 = 0; start_x1 = 0; start_y1 = 0; state = false; Delta_x = 0; // The horizontal offset from the original position delta_y = 0; Constructor (target, move_item) {this.target = target; // Move the dom element this.move_item = move_item; // Accept the dom element that triggers the movement behavior, typically the header of the modal box this.init(); } init() { this.move_item.style.cursor = 'move'; this.bindEvent(); } destory() { this.move_item.removeEventListener('mousedown', this.moveStartFun); document.removeEventListener('mousemove', this.movingFun); document.removeEventListener('mouseup', this.moveEndFun); } bindEvent() { this.moveStartFun = this.moveStart.bind(this); this.movingFun = this.moving.bind(this); this.moveEndFun = this.moveEnd.bind(this); this.move_item.addEventListener('mousedown', this.moveStartFun); document.addEventListener('mousemove', this.movingFun); document.addEventListener('mouseup', this.moveEndFun); } moveStart(e) { e.stopPropagation(); this.state = true; This. start_x0 = e.pagex; // Check whether the mouse is pressed. this.start_y0 = e.pageY; } moving(e) {// The default operation is e.topPropagation (); e.preventDefault(); if (! this.state) { return false; } this.start_x1 = e.pageX; this.start_y1 = e.pageY; this.render(); } moveEnd(e) { if (! this.state) { return false; } this.state = false; this.delta_x = this.start_x1 - this.start_x0 + this.delta_x; this.delta_y = this.start_y1 - this.start_y0 + this.delta_y; } render() { this.target.style.transform = `translate(${ this.start_x1 - this.start_x0 + this.delta_x }px,${this.start_y1 - this.start_y0 + this.delta_y}px)`; }}Copy the code

3. External calls

Introduce DragHoc, a higher-order function, and ToastExport, a modal box component that needs to be enhanced

Since THE author used the Modal component in Ant Design 3.0 for the Modal box in the project, only the class names “ant-modal-Content” and “ant-modal-header” need to be passed for drag and drop.

Other scenarios need to analyze which parts to move and which parts to listen to drag based on the DOM structure of the static modal box component, passing in the class names of both parts as parameters

import { DragHoc } from "./index.js"; import ToastExport from ".. /components/ToastExport/index.js"; Const ToastExportv2 = DragHoc(ToastExport," ant-modal-Content ","ant-modal-header"); // Generate a modal box component with drag-and-drop functionalityCopy the code

After the DragHoc function is called to generate ToastExportv2, it can then be used directly on the page. If the business needs to pass parameters directly to the attribute

const { visible } = this.props; //visible controls display hidden modal boxes {visible? <ToastExportv2 visible={visible}/> : null}Copy the code

If visible is false, component destruction automatically calls the DeStory method to unbind registered events. If visible is false, component destruction automatically calls the DeStory method to unbind registered events