React zoom and pan with draggable elements
Tags: react, typescript, programming, css
Published: 2022-03-21 00:04:53

I just try to build drag-able elements then move them around the screen and zoom it using mouse events, So how we can implement that using React. 

I was searching in npm.io to find a package that can provide those features. Finally, I found some packages that can implement what I want, for that, I want to share the outcome here and highlight how I make integrate these packages with each other.

![Sql builder, finally result](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/g41wu324c0mx861qvulz.gif)


The first chapter will explain how we can create draggable elements,  is a great existing package called
[**react-draggable**](https://www.npmjs.com/package/react-draggable/v/4.4.3), This package is powerful that can provide draggable elements using JavaScript.

It's very easy to use and has powerful features. 

The below code snippet explains how we can create drag-able elements. Draggable elements have powerful functions that are called when on-drag start or on end to perform some routine like update state or do something.
 
```
import * as React from 'react'
import Draggable from 'react-draggable'

export default function Table(props: ITableProps
                                    & ITableOptionsProps) {

    return (
        <Draggable onDrag={props.onDrag} onStop={props.onStop}>
            <div className='table'>

                <div className='table-name'>
                    <h3>{props.name}</h3>
                </div>

                {props.att.map((value,index)=>(

                    <div key={index} className='attribute' id={props.name+"."+value.name}>
                        <div className='element'><h3>{value.name}</h3></div>
                        <div className='element type'><h3>{value.type}</h3></div>
                        <div className='element null'><h3>{value.null}</h3></div>
                        <div className='element pk'>{value.pk?<h3>pk</h3>:null}</div>
                    </div>
                ))}
            </div>
        </Draggable>
    );}
```
CSS:

```
.table{
  position: fixed;
  display: flex;
  flex-direction: column;
  cursor: move;
  align-items: center;
  background-color: transparent;
  min-width: 380px;
  min-height: 200px;
  border: 2px solid #9BA1A6;
  border-radius: 7px;
}
```
the output of the above code block is div have style class called table `<div className='table'>` these table elements can drag around the page using mouse.

Now the second chapter explains how we can build a transform Wrapper that can provide zoom and pan, of course using the great package called [**react-zoom-pan-pinch**](https://github.com/prc5/react-zoom-pan-pinch), react-zoom-pan-pinch is a powerful package that provides transform wrapper, transform component, and zoom controlling features. 

After we create drag-able elements now we need to contain these elements in the transform wrapper to enable zoom controlling and pan the elements as a group.

```
import * as React from 'react';
import { AppContext } from '../../context';
import Table from '../table';
import { 
TransformWrapper, 
TransformComponent 
} from "react-zoom-pan-pinch";


export default function Main(props: IMainProps) {
  const context = React.useContext(AppContext)
  const [isMoveable, setIsMoveable] = React.useState<boolean>(false);

  const onDrag = () => {
    setIsMoveable(true)
    //etc
  }
  const onStop = () => {
    setIsMoveable(false)
    //etc 

  }


  return (
    <>
      <TransformWrapper
        initialScale={1}
        disabled={isMoveable}
        minScale={.5}
        maxScale={1}
        limitToBounds={false}
        onPanning={updateXarrow}
        onZoom={updateXarrow}
        pinch={{ step: 5 }}
      >

        <TransformComponent 
          contentClass='main' 
          wrapperStyle={{ height: '100vh', width: '80vw' }}>


          {context?.database.schema.map((value, index) => (
            <Table 
              key={index} 
              name={value.name} 
              att={value.att} 
              onDrag={onDrag} 
              onStop={onStop}/>
          ))}

        </TransformComponent>
      </TransformWrapper>

    </>
  );
}
```

CSS for content on transform component:

```
.main{
  display: flex;
  flex-direction: row;
  position: absolute;
}
```
we passed two functions to draggable element the first one is **onDrag** this function will perform when the user start drag element and the last one **onStop** is will perform when the user finished dragging element, The main ideas of using this function:
 
- provide enable control for transform wrapper to fix a conflict between draggable elements and transform wrapper.

- perform some updater functions like when we have an arrow between two draggable elements and we move one then we need to update arrow positions.

Now we have draggable elements and transform wrapper, you can visit [sql builder tool](https://github.com/amrshadid/sqlbuilder-tool) on Github to get a full example with source code.