Snippio

Auto Adjusted Drag and Drop Grid Stack

<!doctype html> <html> <head> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"> <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"> <title>jQuery Gridstack.js Demo</title> <link rel="stylesheet" href="https://netdna.bootstrapcdn.com/font-awesome/4.2.0/css/font-awesome.min.css"/> <link rel="stylesheet" href="https://netdna.bootstrapcdn.com/bootstrap/3.3.0/css/bootstrap.min.css"> <link href="gridstack.css" rel="stylesheet" type="text/css"> <style> body { background: #2c3e50; color: #fff; } .grid-stack-item-content { background: white; color: #2c3e50; font-family: 'Indie Flower', cursive; text-align: center; font-size: 20px; } .grid-stack-item-content .fa { font-size: 64px; display: block; margin: 20px 0 10px; } header a, header a:hover { color: #fff; } .darklue hr.star-light::after { background-color: #2c3e50; } </style> </head> <body><div id="jquery-script-menu"> <div class="jquery-script-clear"></div> </div> </div> <section id="demo" class="darklue" style="margin-top:10px;"> <div class="container"> <div class="row"> <div class="col-lg-12 text-center"> <h2>jQuery Gridstack.js Demo</h2> <hr class="star-light"> </div> </div> <div class="grid-stack" data-gs-width="12"> <div class="grid-stack-item" data-gs-x="0" data-gs-y="0" data-gs-width="4" data-gs-height="2"> <div class="grid-stack-item-content"></div> </div> <div class="grid-stack-item" data-gs-x="4" data-gs-y="0" data-gs-width="4" data-gs-height="4"> <div class="grid-stack-item-content"></div> </div> <div class="grid-stack-item" data-gs-x="8" data-gs-y="0" data-gs-width="2" data-gs-height="2" data-gs-min-width="2" data-gs-no-resize="yes"> <div class="grid-stack-item-content"> <span class="fa fa-hand-o-up"></span> Drag me! </div> </div> <div class="grid-stack-item" data-gs-x="10" data-gs-y="0" data-gs-width="2" data-gs-height="2"> <div class="grid-stack-item-content"></div> </div> <div class="grid-stack-item" data-gs-x="0" data-gs-y="4" data-gs-width="2" data-gs-height="2"> <div class="grid-stack-item-content"></div> </div> <div class="grid-stack-item" data-gs-x="2" data-gs-y="4" data-gs-width="2" data-gs-height="4"> <div class="grid-stack-item-content"></div> </div> <div class="grid-stack-item" data-gs-x="8" data-gs-y="4" data-gs-width="4" data-gs-height="2"> <div class="grid-stack-item-content"></div> </div> <div class="grid-stack-item" data-gs-x="0" data-gs-y="6" data-gs-width="2" data-gs-height="2"> <div class="grid-stack-item-content"></div> </div> <div class="grid-stack-item" data-gs-x="4" data-gs-y="6" data-gs-width="4" data-gs-height="2"> <div class="grid-stack-item-content"></div> </div> <div class="grid-stack-item" data-gs-x="8" data-gs-y="6" data-gs-width="2" data-gs-height="2"> <div class="grid-stack-item-content"></div> </div> <div class="grid-stack-item" data-gs-x="10" data-gs-y="6" data-gs-width="2" data-gs-height="2"> <div class="grid-stack-item-content"></div> </div> </div> </div> </section> <script src="https://code.jquery.com/jquery-1.11.1.min.js"></script> <script src="https://ajax.googleapis.com/ajax/libs/jqueryui/1.11.2/jquery-ui.min.js"></script> <script src="https://netdna.bootstrapcdn.com/bootstrap/3.3.0/js/bootstrap.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.7.0/underscore-min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery-easing/1.3/jquery.easing.min.js"></script> <script src="gridstack.js"></script> <script type="text/javascript"> $(function () { $('.grid-stack').gridstack({ width: 12 }); }); </script> <style> .grid-stack { position: relative; } .grid-stack-item { position: absolute; padding: 0; } .grid-stack-item .grid-stack-item-content, .grid-stack-item .placeholder-content { margin: 0; position: absolute; top: 0; left: 10px; right: 10px; bottom: 0; width: auto; z-index: 0 !important; } .grid-stack-placeholder .placeholder-content { border: 1px dashed lightgray; } .grid-stack-item.ui-draggable-dragging, .grid-stack-item.ui-resizable-resizing { z-index: 100; } .grid-stack-item.ui-draggable-dragging .box, .grid-stack-item.ui-resizable-resizing .box { box-shadow: 1px 4px 6px rgba(0, 0, 0, 0.2); opacity: 0.8; } .grid-stack-item .ui-resizable-handle { padding: 3px; margin: 3px 0; cursor: nwse-resize; color: gray; position: absolute; bottom: 0; right: 15px; font: normal normal normal 14px/1 FontAwesome; text-rendering: auto; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=1); -webkit-transform: rotate(90deg); -ms-transform: rotate(90deg); transform: rotate(90deg); font-size: 10px; } .grid-stack-item .ui-resizable-handle::before { content: "\f065"; } .grid-stack-item[data-gs-width="12"] { width: 100% } .grid-stack-item[data-gs-width="11"] { width: 91.66666667% } .grid-stack-item[data-gs-width="10"] { width: 83.33333333% } .grid-stack-item[data-gs-width="9"] { width: 75% } .grid-stack-item[data-gs-width="8"] { width: 66.66666667% } .grid-stack-item[data-gs-width="7"] { width: 58.33333333% } .grid-stack-item[data-gs-width="6"] { width: 50% } .grid-stack-item[data-gs-width="5"] { width: 41.66666667% } .grid-stack-item[data-gs-width="4"] { width: 33.33333333% } .grid-stack-item[data-gs-width="3"] { width: 25% } .grid-stack-item[data-gs-width="2"] { width: 16.66666667% } .grid-stack-item[data-gs-width="1"] { width: 8.33333333% } .grid-stack-item[data-gs-x="12"] { left: 100% } .grid-stack-item[data-gs-x="11"] { left: 91.66666667% } .grid-stack-item[data-gs-x="10"] { left: 83.33333333% } .grid-stack-item[data-gs-x="9"] { left: 75% } .grid-stack-item[data-gs-x="8"] { left: 66.66666667% } .grid-stack-item[data-gs-x="7"] { left: 58.33333333% } .grid-stack-item[data-gs-x="6"] { left: 50% } .grid-stack-item[data-gs-x="5"] { left: 41.66666667% } .grid-stack-item[data-gs-x="4"] { left: 33.33333333% } .grid-stack-item[data-gs-x="3"] { left: 25% } .grid-stack-item[data-gs-x="2"] { left: 16.66666667% } .grid-stack-item[data-gs-x="1"] { left: 8.33333333% } .grid-stack-item[data-gs-height="1"] { height: 60px; } .grid-stack-item[data-gs-height="2"] { height: 140px; } .grid-stack-item[data-gs-height="3"] { height: 220px; } .grid-stack-item[data-gs-height="4"] { height: 300px; } .grid-stack-item[data-gs-height="5"] { height: 380px; } .grid-stack-item[data-gs-height="6"] { height: 460px; } .grid-stack-item[data-gs-height="7"] { height: 540px; } .grid-stack-item[data-gs-height="8"] { height: 620px; } .grid-stack-item[data-gs-height="9"] { height: 700px; } .grid-stack-item[data-gs-height="10"] { height: 780px; } .grid-stack-item[data-gs-height="11"] { height: 860px; } .grid-stack-item[data-gs-height="12"] { height: 940px; } .grid-stack-item[data-gs-height="13"] { height: 1020px; } .grid-stack-item[data-gs-height="14"] { height: 1100px; } .grid-stack-item[data-gs-height="15"] { height: 1180px; } .grid-stack-item[data-gs-height="16"] { height: 1260px; } .grid-stack-item[data-gs-height="17"] { height: 1340px; } .grid-stack-item[data-gs-height="18"] { height: 1420px; } .grid-stack-item[data-gs-height="19"] { height: 1500px; } .grid-stack-item[data-gs-height="20"] { height: 1580px; } .grid-stack-item[data-gs-y="0"] { top: 0 } .grid-stack-item[data-gs-y="1"] { top: 80px; } .grid-stack-item[data-gs-y="2"] { top: 160px; } .grid-stack-item[data-gs-y="3"] { top: 240px; } .grid-stack-item[data-gs-y="4"] { top: 320px; } .grid-stack-item[data-gs-y="5"] { top: 400px; } .grid-stack-item[data-gs-y="6"] { top: 480px; } .grid-stack-item[data-gs-y="7"] { top: 560px; } .grid-stack-item[data-gs-y="8"] { top: 640px; } .grid-stack-item[data-gs-y="9"] { top: 720px; } .grid-stack-item[data-gs-y="10"] { top: 800px; } .grid-stack-item[data-gs-y="11"] { top: 880px; } .grid-stack-item[data-gs-y="12"] { top: 960px; } .grid-stack-item[data-gs-y="13"] { top: 1040px; } .grid-stack-item[data-gs-y="14"] { top: 1120px; } .grid-stack-item[data-gs-y="15"] { top: 1200px; } .grid-stack-item[data-gs-y="16"] { top: 1280px; } .grid-stack-item[data-gs-y="17"] { top: 1360px; } .grid-stack-item[data-gs-y="18"] { top: 1440px; } .grid-stack-item[data-gs-y="19"] { top: 1520px; } </style> <script src="https://code.jquery.com/jquery-1.11.1.min.js"></script> <script src="https://ajax.googleapis.com/ajax/libs/jqueryui/1.11.2/jquery-ui.min.js"></script> <script src="https://netdna.bootstrapcdn.com/bootstrap/3.3.0/js/bootstrap.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.7.0/underscore-min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery-easing/1.3/jquery.easing.min.js"></script> <script> (function (scope, _) { var Utils = { is_intercepted: function (a, b) { return !(a.x + a.width <= b.x || b.x + b.width <= a.x || a.y + a.height <= b.y || b.y + b.height <= a.y); } }; var id_seq = 0; var GridStackEngine = function (width, onchange) { this.width = width; this.nodes = []; this.onchange = onchange || function () {}; }; GridStackEngine.prototype._fix_collisions = function (node) { this.nodes = _.sortBy(this.nodes, function (n) { return -(n.x + n.y * this.width); }, this); while (true) { var collision_node = _.find(this.nodes, function (n) { return n != node && Utils.is_intercepted(n, node); }, this); if (typeof collision_node == 'undefined') { return; } this.move_node(collision_node, collision_node.x, node.y + node.height, collision_node.width, collision_node.height, true); } }; GridStackEngine.prototype._pack_nodes = function () { this.nodes = _.sortBy(this.nodes, function (n) { return n.x + n.y * this.width; }, this); _.each(this.nodes, function (n, i) { while (n.y > 0) { var new_y = n.y - 1; var can_be_moved = i == 0; if (i > 0) { var collision_node = _.chain(this.nodes) .first(i) .find(function (bn) { return Utils.is_intercepted({x: n.x, y: new_y, width: n.width, height: n.height}, bn); }) .value(); can_be_moved = typeof collision_node == 'undefined'; } if (!can_be_moved) { break; } n.y = new_y; } }, this); }; GridStackEngine.prototype._prepare_node = function (node, moving) { node = _.defaults(node || {}, {width: 1, height: 1, x: 0, y: 0 }); node.x = parseInt('' + node.x); node.y = parseInt('' + node.y); node.width = parseInt('' + node.width); node.height = parseInt('' + node.height); if (node.width > this.width) { node.width = this.width; } else if (node.width < 1) { node.width = 1; } if (node.height < 1) { node.height = 1; } if (node.x < 0) { node.x = 0; } if (node.x + node.width > this.width) { if (moving) { node.x = this.width - node.width; } else { node.width = this.width - node.x; } } if (node.y < 0) { node.y = 0; } return node; }; GridStackEngine.prototype._notify = function () { this.onchange(this.nodes); }; GridStackEngine.prototype.add_node = function(node) { node = this._prepare_node(node); if (typeof node.max_width != 'undefined') node.width = Math.min(node.width, node.max_width); if (typeof node.max_height != 'undefined') node.height = Math.min(node.height, node.max_height); if (typeof node.min_width != 'undefined') node.width = Math.max(node.width, node.min_width); if (typeof node.min_height != 'undefined') node.height = Math.max(node.height, node.min_height); node._id = ++id_seq; this.nodes.push(node); this._fix_collisions(node); this._pack_nodes(); this._notify(); return node; }; GridStackEngine.prototype.remove_node = function (node) { node._id = null; this.nodes = _.without(this.nodes, node); this._pack_nodes(); this._notify(node); }; GridStackEngine.prototype.move_node = function (node, x, y, width, height, no_pack) { if (typeof x == 'undefined') x = node.x; if (typeof y == 'undefined') y = node.y; if (typeof width == 'undefined') width = node.width; if (typeof height == 'undefined') height = node.height; if (typeof node.max_width != 'undefined') width = Math.min(width, node.max_width); if (typeof node.max_height != 'undefined') height = Math.min(height, node.max_height); if (typeof node.min_width != 'undefined') width = Math.max(width, node.min_width); if (typeof node.min_height != 'undefined') height = Math.max(height, node.min_height); if (node.x == x && node.y == y && node.width == width && node.height == height) { return node; } var moving = node.x != x; node.x = x; node.y = y; node.width = width; node.height = height; node = this._prepare_node(node, moving); this._fix_collisions(node); if (!no_pack) { this._pack_nodes(); this._notify(); } return node; }; GridStackEngine.prototype.get_grid_height = function () { return _.reduce(this.nodes, function (memo, n) { return Math.max(memo, n.y + n.height); }, 0); }; var GridStack = function (el, opts) { var self = this; this.container = $(el); this.opts = _.defaults(opts || {}, { width: 12, item_class: 'grid-stack-item', placeholder_class: 'grid-stack-placeholder', handle: '.grid-stack-item-content', cell_height: 60, vertical_margin: 20 }); this.grid = new GridStackEngine(this.opts.width, function (nodes) { _.each(nodes, function (n) { n.el .attr('data-gs-x', n.x) .attr('data-gs-y', n.y) .attr('data-gs-width', n.width) .attr('data-gs-height', n.height); }); }); this.container.find('.' + this.opts.item_class).each(function (index, el) { self._prepare_element(el); }); this.placeholder = $('<div class="' + this.opts.placeholder_class + ' ' + this.opts.item_class + '"><div class="placeholder-content" /></div>').hide(); this.container.append(this.placeholder); this.container.height((this.grid.get_grid_height()) * (this.opts.cell_height + this.opts.vertical_margin) - this.opts.vertical_margin); }; GridStack.prototype._update_container_height = function () { this.container.height(this.grid.get_grid_height() * (this.opts.cell_height + this.opts.vertical_margin) - this.opts.vertical_margin); }; GridStack.prototype._prepare_element = function (el) { var self = this; el = $(el); var node = self.grid.add_node({ x: el.attr('data-gs-x'), y: el.attr('data-gs-y'), width: el.attr('data-gs-width'), height: el.attr('data-gs-height'), max_width: el.attr('data-gs-max-width'), min_width: el.attr('data-gs-min-width'), max_height: el.attr('data-gs-max-height'), min_height: el.attr('data-gs-min-height'), el: el }); el.data('_gridstack_node', node); var cell_width, cell_height = this.opts.cell_height + this.opts.vertical_margin / 2; var on_start_moving = function (event, ui) { var o = $(this); cell_width = Math.ceil(o.outerWidth() / o.attr('data-gs-width')); self.placeholder .attr('data-gs-x', o.attr('data-gs-x')) .attr('data-gs-y', o.attr('data-gs-y')) .attr('data-gs-width', o.attr('data-gs-width')) .attr('data-gs-height', o.attr('data-gs-height')) .show(); node.el = self.placeholder; }; var on_end_moving = function (event, ui) { var o = $(this); node.el = o; self.placeholder.hide(); o .attr('data-gs-x', node.x) .attr('data-gs-y', node.y) .attr('data-gs-width', node.width) .attr('data-gs-height', node.height) .removeAttr('style'); self._update_container_height(); }; el.draggable({ handle: this.opts.handle, scroll: true, appendTo: 'body', start: on_start_moving, stop: on_end_moving, drag: function (event, ui) { var x = Math.round(ui.position.left / cell_width), y = Math.floor(ui.position.top / cell_height); self.grid.move_node(node, x, y); self._update_container_height(); } }); if (!el.attr('data-gs-no-resize')) { el.resizable({ autoHide: true, handles: 'se', minHeight: this.opts.cell_height - 10, minWidth: 70, start: on_start_moving, stop: on_end_moving, resize: function (event, ui) { var width = Math.round(ui.size.width / cell_width), height = Math.round(ui.size.height / cell_height); self.grid.move_node(node, node.x, node.y, width, height); self._update_container_height(); } }); } }; GridStack.prototype.add_widget = function (el, x, y, width, height) { el = $(el); if (typeof x != 'undefined') el.attr('data-gs-x', x); if (typeof y != 'undefined') el.attr('data-gs-y', y); if (typeof width != 'undefined') el.attr('data-gs-width', width); if (typeof height != 'undefined') el.attr('data-gs-height', height); this.container.append(el); this._prepare_element(el); }; GridStack.prototype.remove_widget = function (el) { var node = $(el).data('_gridstack_node'); this.grid.remove_node(node); el.remove(); }; scope.GridStackUI = GridStack; $.fn.gridstack = function (opts) { return this.each(function () { if (!$(this).data('_gridstack')) { $(this).data('_gridstack', new GridStack(this, opts)); } }); }; })(window, _); </script> </body> </html>

Preview