👩💻 Dev/💫 React
React-draggable official demo convert using hooks
진하링
2021. 5. 4. 14:39
React-draggable의 공식 Demo code는 Class Component로 작성되었습니다.
저는 Hooks Component로 code를 작성하기에, 이 code를 Hooks로 변경해보았습니다.
Demo : react-grid-layout.github.io/react-draggable/example/
SourceCode : github.com/react-grid-layout/react-draggable/blob/master/example/example.js
Index : github.com/react-grid-layout/react-draggable/blob/master/example/index.html
SourceCode 링크의 code를 hooks로 변경한 코드입니다.
Index page는 링크의 code를 그대로 사용하시면 됩니다.
RemWrapper는 제가 사용하지 않을 기능이고, 무슨 기능인지 잘 이해가 가지 않아서... 정상적으로 변환되지 않았을 수 있습니다.
import React, { useState } from 'react';
import Draggable from 'react-draggable';
import './style.css';
const App = () => {
const [state, setState] = useState({
activeDrags: 0,
deltaPosition: {
x: 0, y: 0
},
controlledPosition: {
x: -400, y: 200
}
});
const handleDrag = (e, ui) => {
const { deltaPosition } = state;
const { x, y } = deltaPosition;
const { deltaX, deltaY } = ui;
setState({
...state,
deltaPosition: {
x: x + deltaX,
y: y + deltaY
}
});
};
const onStart = () => {
const { activeDrags } = state;
setState({ ...state, activeDrags: activeDrags + 1 });
};
const onStop = () => {
const { activeDrags } = state;
setState({ ...state, activeDrags: activeDrags - 1 });
};
const onControlledDrag = (e, position) => {
const { x, y } = position;
setState({ ...state, controlledPosition: { x, y } });
};
const onControlledDragStop = (e, position) => {
onControlledDrag(e, position);
onStop();
};
const adjustXPos = e => {
e.preventDefault();
e.stopPropagation();
const { controlledPosition } = state;
const { x, y } = controlledPosition;
setState({ ...state, controlledPosition: { x: x - 10, y } });
};
const adjustYPos = e => {
e.preventDefault();
e.stopPropagation();
const { controlledPosition } = state;
const { x, y } = controlledPosition;
setState({ ...state, controlledPosition: { x, y: y - 10 } });
};
const onDrop = e => {
setState({ activeDrags: state.activeDrags - 1 });
if (e.target.classList.contains('drop-target')) {
alert('Dropped!');
e.target.classList.remove('hovered');
}
};
const onDropAreaMouseEnter = e => {
if (state.activeDrags) {
e.target.classList.add('hovered');
}
};
const onDropAreaMouseLeave = e => {
e.target.classList.remove('hovered');
};
const dragHandlers = { onStart, onStop };
const { deltaPosition, controlledPosition } = state;
return (
<div>
<h1>React Draggable</h1>
<p>Active DragHandlers: {state.activeDrags}</p>
<p>
<a href='https://github.com/STRML/react-draggable/blob/master/example/example.js'>Demo Source</a>
</p>
<Draggable {...dragHandlers}>
<div className='box'>I can be dragged anywhere</div>
</Draggable>
<Draggable axis='x' {...dragHandlers}>
<div className='box cursor-x'>I can only be dragged horizonally (x axis)</div>
</Draggable>
<Draggable axis='y' {...dragHandlers}>
<div className='box cursor-y'>I can only be dragged vertically (y axis)</div>
</Draggable>
<Draggable onStart={() => false}>
<div className='box'>{'I don\'t want to be dragged'}</div>
</Draggable>
<Draggable onDrag={handleDrag} {...dragHandlers}>
<div className='box'>
<div>I track my deltas</div>
<div>x: {deltaPosition.x.toFixed(0)}, y: {deltaPosition.y.toFixed(0)}</div>
</div>
</Draggable>
<Draggable handle='strong' {...dragHandlers}>
<div className='box no-cursor'>
<strong className='cursor'><div>Drag here</div></strong>
<div>You must click my handle to drag me</div>
</div>
</Draggable>
<Draggable handle='strong'>
<div className='box no-cursor' style={{ display: 'flex', flexDirection: 'column' }}>
<strong className='cursor'><div>Drag here</div></strong>
<div style={{ overflow: 'scroll' }}>
<div style={{ background: 'yellow', whiteSpace: 'pre-wrap' }}>
I have long scrollable content with a handle
{'\n' + Array(40).fill('x').join('\n')}
</div>
</div>
</div>
</Draggable>
<Draggable cancel='strong' {...dragHandlers}>
<div className='box'>
<strong className='no-cursor'>{'Can\'t drag here'}</strong>
<div>Dragging here works</div>
</div>
</Draggable>
<Draggable grid={[25, 25]} {...dragHandlers}>
<div className='box'>I snap to a 25 x 25 grid</div>
</Draggable>
<Draggable grid={[50, 50]} {...dragHandlers}>
<div className='box'>I snap to a 50 x 50 grid</div>
</Draggable>
<Draggable bounds={{ top: -100, left: -100, right: 100, bottom: 100 }} {...dragHandlers}>
<div className='box'>I can only be moved 100px in any direction.</div>
</Draggable>
<Draggable {...dragHandlers}>
<div className='box drop-target' onMouseEnter={onDropAreaMouseEnter} onMouseLeave={onDropAreaMouseLeave}>I can detect drops from the next box.</div>
</Draggable>
<Draggable {...dragHandlers} onStop={onDrop}>
<div className={`box ${state.activeDrags ? 'no-pointer-events' : ''}`}>I can be dropped onto another box.</div>
</Draggable>
<div className='box' style={{ height: '500px', width: '500px', position: 'relative', overflow: 'auto', padding: '0' }}>
<div style={{ height: '1000px', width: '1000px', padding: '10px' }}>
<Draggable bounds='parent' {...dragHandlers}>
<div className='box'>
I can only be moved within my offsetParent.<br /><br />
Both parent padding and child margin work properly.
</div>
</Draggable>
<Draggable bounds='parent' {...dragHandlers}>
<div className='box'>
I also can only be moved within my offsetParent.<br /><br />
Both parent padding and child margin work properly.
</div>
</Draggable>
</div>
</div>
<Draggable bounds='body' {...dragHandlers}>
<div className='box'>
I can only be moved within the confines of the body element.
</div>
</Draggable>
<Draggable {...dragHandlers}>
<div className='box' style={{ position: 'absolute', bottom: '100px', right: '100px' }}>
I already have an absolute position.
</div>
</Draggable>
<Draggable {...dragHandlers}>
<RemWrapper>
<div className='box rem-position-fix' style={{ position: 'absolute', bottom: '6.25rem', right: '18rem' }}>
I use <span style={{ fontWeight: 700 }}>rem</span>
instead of <span style={{ fontWeight: 700 }}>px</span> for my transforms.
I also have absolute positioning.
<br /><br />
I depend on a CSS hack to avoid double absolute positioning.
</div>
</RemWrapper>
</Draggable>
<Draggable defaultPosition={{ x: 25, y: 25 }} {...dragHandlers}>
<div className='box'>
{'I have a default position of {x: 25, y: 25}, so I\'m slightly offset.'}
</div>
</Draggable>
<Draggable positionOffset={{ x: '-10%', y: '-10%' }} {...dragHandlers}>
<div className='box'>
{'I have a default position based on percents {x: \'-10%\', y: \'-10%\'}, so I\'m slightly offset.'}
</div>
</Draggable>
<Draggable position={controlledPosition} {...dragHandlers} onDrag={onControlledDrag}>
<div className='box'>
My position can be changed programmatically. <br />
I have a drag handler to sync state.
<div>
<button type='button' onClick={adjustXPos}>Adjust x ({ controlledPosition.x })</button>
</div>
<div>
<button type='button' onClick={adjustYPos}>Adjust y ({ controlledPosition.y })</button>
</div>
</div>
</Draggable>
<Draggable position={controlledPosition} {...dragHandlers} onStop={onControlledDragStop}>
<div className='box'>
My position can be changed programmatically. <br />
I have a dragStop handler to sync state.
<div>
<button type='button' onClick={adjustXPos}>Adjust x ({ controlledPosition.x })</button>
</div>
<div>
<button type='button' onClick={adjustYPos}>Adjust y ({ controlledPosition.y })</button>
</div>
</div>
</Draggable>
</div>
);
};
const RemWrapper = props => {
const { children, style } = props;
const remBaseline = 16;
const translateTransformToRem = transform => {
const convertedValues = transform.replace('translate(', '').replace(')', '')
.split(',')
.map(px => px.replace('px', ''))
.map(px => parseInt(px, 10) / remBaseline)
.map(x => `${x}rem`);
const [x, y] = convertedValues;
return `translate(${x}, ${y})`;
};
const child = React.Children.only(children);
const editedStyle = {
...child.props.style,
...style,
transform: translateTransformToRem(style.transform, remBaseline),
};
return (
<div style={editedStyle}>
{children}
</div>
);
};
export default App;