Skip to content

Instantly share code, notes, and snippets.

@haakenlid
Created March 18, 2016 22:19
Show Gist options
  • Save haakenlid/2913733c802429d9549a to your computer and use it in GitHub Desktop.
Save haakenlid/2913733c802429d9549a to your computer and use it in GitHub Desktop.
redux resize-box
<div id=app>
</div>
class Handle extends React.Component {
constructor(props) {
super(props)
let classNames = []
for (const prop of ['top', 'bottom', 'left', 'right']){
props[prop] && classNames.push(prop)
}
this.className = classNames.join(' ')
this.moveMask = [
props.left, props.top, props.right, props.bottom
].map((value)=>(value?1:0))
}
handleMouseDown(event) {
event.stopPropagation()
this.props.dragStart(event, this.moveMask, this.className)
}
render() {
return (
<div
className={'handle ' + this.className}
onMouseDown={this.handleMouseDown.bind(this)}
/>
)
}
}
class Canvas extends React.Component {
constructor(props) {
super(props);
this.state = {
isResizing: false,
position: props.box,
moveDelta: [0,0,0,0],
className: '',
width: 1e6,
height: 1e6,
}
}
componentDidMount(){
const r = this.refs.canvas.getBoundingClientRect()
this.setState({
height: r.height,
width: r.width,
})
}
getBox() {
const p = this.state.position
const d = this.state.moveDelta
const numsort = (a, b) => (a - b)
let horizontal = [
0, this.state.width, p[0] + d[0], p[2] + d[2]].sort(numsort)
let vertical = [
0, this.state.height, p[1] + d[1], p[3] + d[3]].sort(numsort)
const box = [
horizontal[1],
vertical[1],
horizontal[2],
vertical[2],
]
return box
}
newBox(event) {
event.stopPropagation()
const r = this.refs.canvas.getBoundingClientRect()
const x = event.pageX - r.left
const y = event.pageY - r.top
this.setState({
position: [x,y,x,y]
})
this.dragStart(event, [1,1,0,0], 'top left')
}
dragStart(event, dragMask, className) {
this.componentDidMount()
this.setState({
isResizing: true,
dragStart: [event.pageX, event.pageY],
dragMask: dragMask,
className: className,
})
}
dragMove(event) {
let dx = event.pageX - this.state.dragStart[0]
let dy = event.pageY - this.state.dragStart[1]
let dragMask = this.state.dragMask
let delta = [dx,dy,dx,dy].map(
(value, i)=>dragMask[i]*value)
this.setState({ moveDelta: delta })
}
dragEnd(event) {
this.setState({
isResizing: false,
moveDelta: [0,0,0,0],
position: this.getBox(),
className: '',
})
}
getHandles(){
const handlestrings = ['top', 'bottom', 'right', 'left',
'top left', 'top right', 'bottom right', 'bottom left']
const handles = handlestrings.map(function(propstring){
let props = {}
for (let key of propstring.split(/\s+/)){
props[key] = true
}
return props
})
return handles.map((props) => (
<Handle { ...props } dragStart={this.dragStart.bind(this)} />
))
}
render() {
if (this.state.isResizing) {
return (
<div ref="canvas"
className={"canvas " + this.state.className}
onMouseUp={this.dragEnd.bind(this)}
onMouseLeave={this.dragEnd.bind(this)}
onMouseMove={this.dragMove.bind(this)}
>
<Box ref="box" box={this.getBox()} />
</div>
)
} else {
return (
<div ref="canvas"
onMouseDown={this.newBox.bind(this)}
className="canvas">
<Box
dragStart={this.dragStart.bind(this)}
ref="box"
className="move"
box={this.getBox()}>
{ this.getHandles() }
</Box>
</div>
)
}
}
}
class Box extends React.Component {
getStyle() {
return {
left: this.props.box[0],
top: this.props.box[1],
height: this.props.box[3] - this.props.box[1],
width: this.props.box[2] - this.props.box[0],
}
}
handleMouseDown(event){
event.stopPropagation()
this.props.dragStart(event, [1,1,1,1], 'grabbing')
}
render(){
return (
<div
onMouseDown={this.handleMouseDown.bind(this)}
className={"box " + this.props.className }
style={this.getStyle()} >
<svg width="100%" height="100%">
<rect
width="100%" height="100%" />
<rect className="dashed"
width="100%" height="100%" />
</svg>
{ this.props.children }
</div>
)
}
}
class App extends React.Component {
render() {
return ( <Canvas /> )
}
}
ReactDOM.render(<App/>, document.querySelector('#app'))
<script src="https://fb.me/react-0.14.3.min.js"></script>
<script src="https://fb.me/react-dom-0.14.3.min.js"></script>
html, body{
height: 100%;
padding: 0;
}
$dash: 8px;
@keyframes ants {
0% {stroke-dashoffset: 0px;}
100% {stroke-dashoffset: 6 * $dash;}
}
$marching-ants: ants 1s infinite linear;
#app {
padding: 1em;
height: 100%;
box-sizing: border-box;
border: 1px solid #aaa;
background: #bbb;
.canvas {
overflow: hidden;
position: relative;
background: #fff;
height: 100%;
width: 100%;
}
.box {
position: absolute;
&:hover rect.dashed {
animation: $marching-ants;
}
svg{
rect{
stroke-width: 1px;
fill: none;
stroke: #fff;
&.dashed {
stroke-dasharray: $dash;
stroke: #000;
}
}
}
}
.grabbing {
cursor: move;
cursor: grabbing;
}
.move {
cursor: move;
cursor: grab;
}
.top, .bottom {
cursor: ns-resize }
.left, .right {
cursor: ew-resize }
.top.left, .bottom.right {
cursor: nwse-resize }
.top.right, .bottom.left {
cursor: nesw-resize }
}
.handle {
$handleSize: 20%;
background: #ff3;
opacity: 0.0;
position: absolute;
min-width: $handleSize;
min-height: $handleSize;
top: 0;
bottom: 0;
left: 0;
right: 0;
z-index: 1000;
&.left {
left: -$handleSize / 2;
right: unset;
}
&.right {
right: -$handleSize / 2;
left: unset;
}
&.top {
top: -$handleSize / 2;
bottom: unset;
}
&.bottom {
cursor: ns-resize;
bottom: -$handleSize / 2;
top: unset;
}
&.top, &.bottom{
&.left, &.right {
z-index: 2000;
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment