Sortable and Resizable Dashboard Zones
Using DIVS to implement dashboard zones.
by humanzing
HTML
<script src="https://cdnjs.cloudflare.com/ajax/libs/jqueryui-touch-punch/0.2.3/jquery.ui.touch-punch.min.js"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.8.2/css/all.min.css">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.1.3/css/bootstrap.min.css">
<div class="row">
<div class="col-12">
<div id="zone_div">
<div class="flexgrid-container" style="width: 500px;">
<div class="flexgrid-helper">
<div class="add-row"></div>
<div class="remove-row"></div>
<button class="btn btn-sm clear-flexgrid">clear zone</button>
<button class="btn btn-sm fg-add-widget">add zone</button>
<button class="btn btn-sm save-flexgrid"><i class="fas fa-save"></i></button>
</div>
<div class="flexgrid-grid"></div>
</div>
</div>
</div>
</div>
</div>
<label class="widget-holder-label">Widgets:</label>
<div class="widget-holder">
<div class="fg-widget custom-widget"><i class="fa fa-chevron-right fg-resize-widget" aria-hidden="true"></i><i class="fa fa-times fg-remove-widget" title="remove this widget"></i><i class="fas fa-arrows-alt move-widget fg-widget-handle"></i>
<div class="fg-widget-inner"><i class="fas fa-ad inner-icon"></i></div>
</div>
<div class="fg-widget custom-widget"><i class="fa fa-chevron-right fg-resize-widget" aria-hidden="true"></i><i class="fa fa-times fg-remove-widget" title="remove this widget"></i><i class="fas fa-arrows-alt move-widget fg-widget-handle"></i>
<div class="fg-widget-inner"><i class="fas fa-ad inner-icon"></i></div>
</div>
<div class="fg-widget custom-widget"><i class="fa fa-chevron-right fg-resize-widget" aria-hidden="true"></i><i class="fa fa-times fg-remove-widget" title="remove this widget"></i><i class="fas fa-arrows-alt move-widget fg-widget-handle"></i>
<div class="fg-widget-inner"><i class="fas fa-ad inner-icon"></i></div>
</div>
<div...
CSS
@import url('https://fonts.googleapis.com/css?family=Baloo+Da&display=swap');
:root {
--widget-bg: #777;
--zone-bg: #252525;
--zone-bord: #000;
--placeholder: rgba(0, 0, 0, 0.5);
--no-drag: rgba(100, 100, 100, 0.2);
--wig-color: #222;
}
.widget-holder-label {
position: absolute;
color: white;
font-weight: bold;
top: 5px;
right: 10px;
width: 208px;
}
.widget-holder {
position: absolute;
right: 10px;
top: 30px;
width: 208px;
height: 200px;
background: #333;
border-radius: 5px;
overflow-y: scroll;
}
.widget-holder .ui-sortable-placeholder {
visibility: hidden !important;
}
.widget-holder .fg-widget {
height: 100px;
width: 100px;
position: relative;
display: inline-block;
float: left;
}
.custom-widget .fg-widget-inner {
background: #e04c8a;
}
.inner-icon {
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
font-size: 30px;
}
.fg-widget-handle {
cursor: move;
}
.fg-nested-container {
width: 100%;
height: 100%;
}
.move-widget {
position: absolute;
left: 10px;
top: 10px;
color: #222;
z-index: 9;
}
.line1, .line2 {
position: absolute;
top: 0;
bottom: 0;
width: 1px;
background: red;
}
.line2 {
background: blue;
}
.zone_div {
width: 100%;
}
.flexgrid-container {
}
.flexgrid-container * {
user-select: none;
}
.add-row, .remove-row {
position: absolute;
left: 10px;
top: 10px;
cursor: pointer;
height: 30px;
width: 30px;
border-radius: 50%;
background: #fff;
border: 2px solid #222;
}
.remove-row {
left: 50px;
}
.add-row::after, .remove-row::after {
font-family: 'Font Awesome 5 Free';
font-weight: 900;
content: "\f067";
display: inline-block;
color: #222;
z-index: 1;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%,-50%);
}
.remove-row::after {
content: "\f068";
}
body {
background: #111;
padding-bottom: 50px;
}
.btn {
background: #fff;
color: #222;
font-weight: bold;
border: 2px solid #222;
border-radius: 5px;
}
/* .absolute::after, .fake-col::after,...
JavaScript
$(function() {
// grid/zone refers to the container
// widget refers to the blocks within a grid/zone
// TODO:
// - resize axis
// - redo save widget functionality to fit plugin system
// - fix placeholder position, currently follows mouse rather than widget
// - optional size / position values passed with widgets from widget holder
// - add nested functionality
// - - optional height passed with nested widget to push other widgets down
// - widgets from html
$.fn.setFlexGrid = function(options) {
var defaults = { // default options for grid
cols: 12, // starting amount of columns across a grid
rows: 12, // starting amount of rows in a grid
fixedGrid: false, // determines whether the grid can be resized or not (adding more rows)
defaultHeight: 3, // amount of rows a widget will span upon creation
defaultWidth: 3, // amount of columns a widget will span upon creation
minHeight: 1, // minimum number of rows a widget can span ~ should be <= defaultHeight
maxHeight: null, // if set to numeric value: maximum number of rows a widget can span
minWidth: 1, // minimum number of columns a widget can span ~ should be <= defaultWidth
maxWidth: null, // if set to numeric value: maximum number of columns a widget can span
rowHeight: 1, // value times column width
nested: true, // if true, widgets will allow nested widget to be dropped into them
showGridlines: true, // if true, show gridlines on initialization
animate: true, // determines wether or not the widgets should be animated
nextAxis: 'x', // determines which axis the widget should find an open column. When X: widget will go to the first open column. When Y: widget will go to the first row that has an open column of x.
resizeHandles: 'se', // determines resize handles (n, e, s, w, ne, se, sw, nw, all) ~ default is "se"
checkRevert: false // if widget cannot be dropped in pos (going to revert) because of minWidth / minHeight, placeholder will turn...