Facebook React experiment. Outline for a simple dashboard with draggable panels
A Pen by Andreas Bielk on CodePen.
<div id="content"></div> |
Facebook React experiment. Outline for a simple dashboard with draggable panels
A Pen by Andreas Bielk on CodePen.
DOM = React.DOM | |
PANEL_WIDTH = 250 | |
PANEL_HEIGHT = 150 | |
PANEL_MARGIN = 20 | |
Panel = React.createClass | |
displayName: 'Panel' | |
render: -> | |
style = | |
position: 'absolute' | |
width: PANEL_WIDTH | |
height: PANEL_HEIGHT | |
left: @props.x | |
top: @props.y | |
className = 'panel' | |
if @props.dragging | |
className = className + ' dragging' | |
DOM.div | |
key: @props.id | |
style: style | |
className: className | |
onMouseDown: @props.onMouseDown | |
, @props.id | |
Panels = React.createClass | |
displayName: 'Panels' | |
getInitialState: -> | |
data = [] | |
data.push | |
id: 'panel1' | |
x: 0 | |
y: 0 | |
data.push | |
id: 'panel2' | |
x: PANEL_WIDTH + PANEL_MARGIN | |
y: PANEL_HEIGHT + PANEL_MARGIN | |
data.push | |
id: 'panel3' | |
x: 2* (PANEL_WIDTH + PANEL_MARGIN) | |
y: 0 | |
return data: data, dragging: false | |
snap: (x,y) -> | |
col = Math.round (x / (PANEL_WIDTH + PANEL_MARGIN)) | |
row = Math.round (y / (PANEL_HEIGHT + PANEL_MARGIN)) | |
sx = col * (PANEL_WIDTH + PANEL_MARGIN) | |
sy = row * (PANEL_HEIGHT + PANEL_MARGIN) | |
return [sx,sy] | |
snapPanels: (panels) -> | |
that = @ | |
panels.map (p) -> | |
[p.x,p.y] = that.snap p.x,p.y | |
return p | |
shufflePanels: (panels,bully) -> | |
occupied = (x,y) -> | |
for p in panels | |
if p.x is x and p.y is y and (p.id isnt bully.id) then return true | |
return false | |
return panels unless occupied(bully.x,bully.y) | |
result = panels.slice(0) | |
for p in result | |
continue if p.id is bully.id | |
continue if p.y < bully.y | |
continue if p.x isnt bully.x | |
p.y += PANEL_HEIGHT + PANEL_MARGIN | |
return result | |
getPanel: (id,[email protected]) -> | |
for p in ps | |
return p if p.id is id | |
return null | |
updatePanelPosition: (id,x,y) -> | |
ds = @state.data.slice(0) | |
for p in ds | |
if p.id is id | |
p.x = x | |
p.y = y | |
@setState | |
data: ds | |
dragStart: (panel,e) -> | |
e.preventDefault() | |
@setState dragging: panel, dragStartX: e.pageX, dragStartY: e.pageY | |
return | |
dragEnd: (e) -> | |
e.preventDefault() | |
return unless @state.dragging | |
ps = @state.data | |
ps = @snapPanels ps | |
ps = @shufflePanels ps, @getPanel(@state.dragging.id,ps) | |
@setState dragging: false, data: ps | |
return | |
onDrag: (e) -> | |
e.preventDefault() | |
dx = e.pageX - @state.dragStartX | |
dy = e.pageY - @state.dragStartY | |
@updatePanelPosition @state.dragging.id, @state.dragging.x + dx, @state.dragging.y + dy | |
render: -> | |
dragStart = @dragStart | |
dragEnd = @dragEnd | |
onMouseMove = if @state.dragging then @onDrag else null | |
dragging = @state.dragging | |
panels = [] | |
for p in @state.data | |
do (p) -> | |
_p = id:p.id,x:p.x,y:p.y | |
panels.push Panel | |
id: p.id | |
x: p.x | |
y: p.y | |
dragging: dragging and (dragging.id is p.id) | |
onMouseDown: (e) -> dragStart _p,e | |
# markers | |
if @state.dragging | |
style = | |
position: 'absolute' | |
width: PANEL_WIDTH | |
height: PANEL_HEIGHT | |
left: @state.dragging.x | |
top: @state.dragging.y | |
panels.push DOM.div {className: 'drag-from-marker', style: style}, [] | |
cp = @getPanel @state.dragging.id | |
[x,y] = @snap cp.x,cp.y | |
unless x is @state.dragging.x and y is @state.dragging.y | |
style = | |
position: 'absolute' | |
width: PANEL_WIDTH | |
height: PANEL_HEIGHT | |
left: x | |
top: y | |
panels.push DOM.div {className: 'drag-to-marker', style: style}, [] | |
DOM.div {ref: 'panels',className: 'panels',onMouseMove: onMouseMove, onMouseUp: dragEnd},panels | |
React.renderComponent( | |
Panels(null), | |
document.getElementById('content') | |
); |
.panels { | |
margin-top: 30px; | |
margin-left: 0px; | |
padding: 0; | |
} | |
.panel { | |
background: #eef; | |
z-index: 1000; | |
transition: left 0.5s, top 0.5s; | |
} | |
.panel.dragging { | |
z-index: 1002; | |
opacity: 0.5; | |
transition: left 0, top 0, opacity 0.2s; | |
} | |
.drag-from-marker { | |
background: #fee; | |
z-index: 500; | |
} | |
.drag-to-marker { | |
background: #efe; | |
z-index: 1001; | |
opacity: 0.5; | |
} |