"drag & drop react"
Bootstrap 3.0.0 Snippet by evarevirus

<link href="//netdna.bootstrapcdn.com/bootstrap/3.0.0/css/bootstrap.min.css" rel="stylesheet" id="bootstrap-css"> <script src="//netdna.bootstrapcdn.com/bootstrap/3.0.0/js/bootstrap.min.js"></script> <script src="//code.jquery.com/jquery-1.11.1.min.js"></script> <!------ Include the above in your HEAD tag ----------> <!DOCTYPE html><html class=''> <head><script src='//production-assets.codepen.io/assets/editor/live/console_runner-079c09a0e3b9ff743e39ee2d5637b9216b3545af0de366d4b9aad9dc87e26bfd.js'></script><script src='//production-assets.codepen.io/assets/editor/live/events_runner-73716630c22bbc8cff4bd0f07b135f00a0bdc5d14629260c3ec49e5606f98fdd.js'></script><script src='//production-assets.codepen.io/assets/editor/live/css_live_reload_init-2c0dc5167d60a5af3ee189d570b1835129687ea2a61bee3513dee3a50c115a77.js'></script><meta charset='UTF-8'><meta name="robots" content="noindex"><link rel="shortcut icon" type="image/x-icon" href="//production-assets.codepen.io/assets/favicon/favicon-8ea04875e70c4b0bb41da869e81236e54394d63638a1ef12fa558a4a835f1164.ico" /><link rel="mask-icon" type="" href="//production-assets.codepen.io/assets/favicon/logo-pin-f2d2b6d2c61838f7e76325261b7195c27224080bc099486ddd6dccb469b8e8e6.svg" color="#111" /><link rel="canonical" href="https://codepen.io/tjramage/pen/yOEbyw?q=react&order=popularity&depth=everything&show_forks=false" /> <style> *, *:before, *:after { box-sizing: border-box; } </style> <link rel='stylesheet prefetch' href='https://cdnjs.cloudflare.com/ajax/libs/normalize/5.0.0/normalize.min.css'> <style class="cp-pen-styles">html { width: 100%; height: 100%; background: -webkit-radial-gradient(50% 16% circle, #219eb0 32%, #3f679d 88%); background: radial-gradient(circle at 50% 16%, #219eb0 32%, #3f679d 88%); } .title { margin-top: 1.25em; color: #eee; text-align: center; text-shadow: 1px 1px 0 black; } .items { padding: 21px; } .item { width: calc((100% / 3) - 26px); height: 90px; padding: 2.125em 0 1.25em; -webkit-user-select: none; -moz-user-select: none; -ms-user-select: none; user-select: none; position: absolute; border-radius: 3px; box-shadow: 0 2px 2px 0 rgba(0, 0, 0, 0.2); background: rgba(255, 255, 255, 0.75); -webkit-transition: background-color 0.1s ease-in-out; transition: background-color 0.1s ease-in-out; cursor: -webkit-grab; cursor: grab; text-align: center; } .item.is-active { background: rgba(255, 255, 255, 0.9); cursor: -webkit-grabbing; cursor: grabbing; } </style></head><body> <!-- This is a proof of concept based on Cheng Lou's totally amazing React Motion library: https://github.com/chenglou/react-motion Really just a proof of concept to see if I could get it working across multiple columns (i.e. no source order reflowing) for a kanban / trello list app I'm working on. However, I couldn't elegantly solve the issue of variable item heights in calculateVisiblePositions(), so have decided to drop this as it is and move over to react-dnd instead. If anyone ever figures out how to do this with variable item heights, please let me know! --> <h1 class="title">Drag & Drop Grid Layout in React</h1> <div id="react-root"></div> <script src='//production-assets.codepen.io/assets/common/stopExecutionOnTimeout-b2a7b3fe212eaa732349046d8416e00a9dec26eb7fd347590fbced3ab38af52e.js'></script><script src='//fb.me/react-with-addons-15.0.1.min.js'></script><script src='//fb.me/react-dom-15.0.1.min.js'></script><script src='//npmcdn.com/react-motion@0.4.2/build/react-motion.js'></script><script src='//cdnjs.cloudflare.com/ajax/libs/lodash.js/4.6.1/lodash.min.js'></script> <script >'use strict'; var dataStructure = [// structure that models our initial rendered view of items [0, 1, 2], [3, 4, 5, 6, 7], [8, 9, 10, 11]]; var reinsert = function reinsert(array, colFrom, rowFrom, colTo, rowTo) { var _array = array.slice(0); var val = _array[colFrom][rowFrom]; _array[colFrom].splice(rowFrom, 1); _array[colTo].splice(rowTo, 0, val); calculateVisiblePositions(_array); return _array; }; var gutterPadding = 21; var clamp = function clamp(n, min, max) { return Math.max(Math.min(n, max), min); }; var getColumnWidth = function getColumnWidth() { return window.innerWidth / dataStructure.length - gutterPadding / dataStructure.length; }; // spread columns over available window width var height = 110; // crappy fixed item height :( var width = getColumnWidth(), layout = null; // items are ordered by their index in this visual positions array var calculateVisiblePositions = function calculateVisiblePositions(newOrder) { width = getColumnWidth(); layout = newOrder.map(function (column, col) { return _.range(column.length + 1).map(function (item, row) { return [width * col, height * row]; }); }); }; // define spring motion opts var springSetting1 = { stiffness: 180, damping: 10 }; var springSetting2 = { stiffness: 150, damping: 16 }; var List = React.createClass({ displayName: 'List', getInitialState: function getInitialState() { return { mouse: [0, 0], delta: [0, 0], // difference between mouse and item position, for dragging lastPress: null, // key of the last pressed component currentColumn: null, isPressed: false, order: dataStructure, // index: visual position. value: component key/id isResizing: false }; }, componentWillMount: function componentWillMount() { this.resizeTimeout = null; calculateVisiblePositions(dataStructure); }, componentDidMount: function componentDidMount() { window.addEventListener('touchmove', this.handleTouchMove); window.addEventListener('mousemove', this.handleMouseMove); window.addEventListener('touchend', this.handleMouseUp); window.addEventListener('mouseup', this.handleMouseUp); window.addEventListener('resize', this.handleResize); }, componentWillUnmount: function componentWillUnmount() { window.removeEventListener('resize', this.handleResize); }, handleTouchStart: function handleTouchStart(key, currentColumn, pressLocation, e) { this.handleMouseDown(key, currentColumn, pressLocation, e.touches[0]); }, handleTouchMove: function handleTouchMove(e) { e.preventDefault(); this.handleMouseMove(e.touches[0]); }, handleMouseMove: function handleMouseMove(_ref) { var pageX = _ref.pageX; var pageY = _ref.pageY; var _state = this.state; var order = _state.order; var lastPress = _state.lastPress; var colFrom = _state.currentColumn; var isPressed = _state.isPressed; var _state$delta = _state.delta; var dx = _state$delta[0]; var dy = _state$delta[1]; if (isPressed) { var mouse = [pageX - dx, pageY - dy]; var colTo = clamp(Math.floor((mouse[0] + width / 2) / width), 0, 2); var rowTo = clamp(Math.floor((mouse[1] + height / 2) / height), 0, 100); var rowFrom = order[colFrom].indexOf(lastPress); var newOrder = reinsert(order, colFrom, rowFrom, colTo, rowTo); this.setState({ mouse: mouse, order: newOrder, currentColumn: colTo }); } }, handleMouseDown: function handleMouseDown(key, currentColumn, _ref2, _ref3) { var pressX = _ref2[0]; var pressY = _ref2[1]; var pageX = _ref3.pageX; var pageY = _ref3.pageY; this.setState({ lastPress: key, currentColumn: currentColumn, isPressed: true, delta: [pageX - pressX, pageY - pressY], mouse: [pressX, pressY] }); }, handleMouseUp: function handleMouseUp() { this.setState({ isPressed: false, delta: [0, 0] }); }, handleResize: function handleResize() { var _this = this; clearTimeout(this.resizeTimeout); this.applyResizingState(true); // resize one last time after resizing stops, as sometimes this can be a little janky sometimes... this.resizeTimeout = setTimeout(function () { return _this.applyResizingState(false); }, 100); }, applyResizingState: function applyResizingState(isResizing) { this.setState({ isResizing: isResizing }); calculateVisiblePositions(dataStructure); }, render: function render() { var _this2 = this; var _state2 = this.state; var order = _state2.order; var lastPress = _state2.lastPress; var currentColumn = _state2.currentColumn; var isPressed = _state2.isPressed; var mouse = _state2.mouse; var isResizing = _state2.isResizing; return React.createElement( 'div', { className: 'items' }, order.map(function (column, colIndex) { return column.map(function (row) { var style = undefined, x = undefined, y = undefined, visualPosition = order[colIndex].indexOf(row), isActive = row === lastPress && colIndex === currentColumn && isPressed; if (isActive) { x = mouse[0]; y = mouse[1]; style = { translateX: x, translateY: y, scale: ReactMotion.spring(1.1, springSetting1) }; } else if (isResizing) { var _layout$colIndex$visu = layout[colIndex][visualPosition]; x = _layout$colIndex$visu[0]; y = _layout$colIndex$visu[1]; style = { translateX: x, translateY: y, scale: 1 }; } else { var _layout$colIndex$visu2 = layout[colIndex][visualPosition]; x = _layout$colIndex$visu2[0]; y = _layout$colIndex$visu2[1]; style = { translateX: ReactMotion.spring(x, springSetting2), translateY: ReactMotion.spring(y, springSetting2), scale: ReactMotion.spring(1, springSetting1) }; } return React.createElement( ReactMotion.Motion, { key: row, style: style }, function (_ref4) { var translateX = _ref4.translateX; var translateY = _ref4.translateY; var scale = _ref4.scale; return React.createElement( 'div', { onMouseDown: _this2.handleMouseDown.bind(null, row, colIndex, [x, y]), onTouchStart: _this2.handleTouchStart.bind(null, row, colIndex, [x, y]), className: isActive ? 'item is-active' : 'item', style: { WebkitTransform: 'translate3d(' + translateX + 'px, ' + translateY + 'px, 0) scale(' + scale + ')', transform: 'translate3d(' + translateX + 'px, ' + translateY + 'px, 0) scale(' + scale + ')', zIndex: row === lastPress && colIndex === currentColumn ? 99 : visualPosition } }, 'Item ', row + 1 ); } ); }); }) ); } }); ReactDOM.render(React.createElement(List, null), document.getElementById('react-root')); //# sourceURL=pen.js </script> </body></html>

Related: See More


Questions / Comments: