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; | |
| } |