Skip to content

Instantly share code, notes, and snippets.

@idahopotato1
Created April 3, 2021 09:49
Show Gist options
  • Save idahopotato1/a00d08925ce1b051952e5396b0a95a67 to your computer and use it in GitHub Desktop.
Save idahopotato1/a00d08925ce1b051952e5396b0a95a67 to your computer and use it in GitHub Desktop.
Nested Grid System
<div class="col-sm-12 form-group"></div>
<div class="col-sm-12">
<div class="row">
<div class="col-sm-3 offset-sm-9">
<button id="save_coord" class="btn button form-group">save</button>
</div>
</div>
<div class="row">
<div class="col-sm-12">
<div id="zone_div"></div>
</div>
</div>
<div class="row">
<div class="col-sm-12">
<div id="zone_output"></div>
</div>
</div>
</div>
<div class="nest-contain">
<div class="nested-widget cloner">
<div class="nested-widget-inner">
<div class="remove-nested-widget"><i class="fas fa-times"></i></div>
<div class="nested-widget-txt">cloner</div>
</div>
</div>
<div class="nested-widget" data-cs-height="300">
<div class="nested-widget-inner">
<div class="remove-nested-widget"><i class="fas fa-times"></i></div>
<div class="nested-widget-txt">1</div>
</div>
</div>
<div class="nested-widget" data-cs-height="400">
<div class="nested-widget-inner">
<div class="remove-nested-widget"><i class="fas fa-times"></i></div>
<div class="nested-widget-txt">2</div>
</div>
</div>
<div class="nested-widget">
<div class="nested-widget-inner">
<div class="remove-nested-widget"><i class="fas fa-times"></i></div>
<div class="nested-widget-txt">3</div>
</div>
</div>
<div class="nested-widget">
<div class="nested-widget-inner">
<div class="remove-nested-widget"><i class="fas fa-times"></i></div>
<div class="nested-widget-txt">4</div>
</div>
</div>
</div>
$(function() {
// maximum number of columns that can be within a row,
// for instance: 3 would be 3 col-sm-4 columns across a row.
// var dyCol = 6;
var colors = [
{col: '#50cc79'},
{col: '#406fff'},
{col: '#4c4fe0'},
{col: '#e04c8a'},
{col: '#fae34b'},
{col: '#fa9d4b'}
];
var zone_array = [
{
id: 'zone_1',
innerId: 'inner_1',
col: '',
offset: '',
height: '540px',
width: '500px',
css: 'position: absolute; top: 50%; left: 50%; transform: translate(-50%,0%);',
rowCount: '3',
}
];
$.fn.stack = function() {
var o = $(this).closest('.absolute').siblings('.absolute').find('.widget');
var group = $(o).sort(function(a,b) {
return (parseInt($(a).css("z-index"),10) || 1) - (parseInt($(b).css("z-index"),10) || 1);
});
if (!group.length) { return; }
var min = parseInt($(group[0]).css('z-index')) || 1;
$(group).each(function(i) {
console.log(i);
$(this).css('z-index', min + i);
});
$(this[0]).css('z-index', min + group.length);
}
// function loadDoc() {
// $.getScript( "https://codepen.io/Souleste/pen/poogjWr.js", function( data, textStatus, jqxhr ) {
var zoneCount = 0;
$(document).on('click', '.add-row', function() {
var zoneInner = $(this).closest('.zone').find('.zone-inner');
addCols();
enableSortable();
});
$(document).on('click', '.remove-row', function() {
var zone = $(this).closest('.zone');
var zoneInner = zone.find('.zone-inner');
var zH = zoneInner.height();
var zW = zoneInner.width();
var zoneCol = zoneInner.find('.absolute');
var dyCol = zoneInner.attr('data-cols');
var c = Math.floor( zW / 12 );
var colsPerWig = 12 / dyCol; // gives us the 'col-sm' in bootstrap terms for each widget
var colsNum = Math.floor( c * colsPerWig );
var c3 = Math.floor( c * colsPerWig );
var rowCount = zoneInner.find('.absolute').last().attr('data-row');
if (zoneCol.length > (12 - colsPerWig) + 1) {
var el = zoneCol.slice( ((12 - colsPerWig) + 1) * -1 );
var nested = el.find('.nested-widget').not('.cloner');
if (nested.length > 0) {
nested.css({'height':'100px', 'width':'100px', 'min-height': '100px'});
$('.nest-contain').append(nested);
}
el.remove();
zoneInner.height(zH - c3);
zone.height(zoneInner.height() + 50);
zoneInner.find('.fake-col[data-row="'+rowCount+'"]').remove();
}
for (i = 0; i < zoneCol.length; i++) {
var self = zoneCol.eq(i);
var el = self;
var widget = self.find('.widget');
var wH = widget.height();
var r = Math.floor( zH / c3 );
var zH = zoneInner[0].offsetHeight;
var zB = zoneInner[0].offsetTop + zH;
var cH = el[0].offsetHeight;
var cB = el[0].offsetTop + cH;
if ( cB > zH ) {
widget.css('height', wH - c3 );
widget.height( wH - c3 );
// console.log(wH);
// console.log(wH - c3);
}
}
});
$(document).on('click', '#add_zone', function() {
var addZone = $(this);
var rando = Math.floor(Math.random() * colors.length);
var zoneInner = $(this).closest('.zone').find('.zone-inner');
var cols = zoneInner.find('.absolute');
var zoneHeight = zoneInner.height();
var zoneWidth = zoneInner.width();
var cols = Math.floor(zoneWidth / 12);
var dyCol = zoneInner.attr('data-cols');
var colsm = 12 / dyCol;
var colsNum = Math.floor( cols * colsm );
zoneCount = 0;
zoneInner.find('.widget').each(function() {
var self = $(this);
var txt = self.find('.zone-txt');
self.attr('id','wig_' + (zoneCount + 1) );
txt.html(zoneCount + 1);
zoneCount = zoneCount + 1;
});
zoneCount = zoneCount;
if ( $('.zone-col').length < 1 ) {
addCols();
enableSortable();
}
$('.absolute').each(function() {
if ( !$(this).hasClass('disabled') && $(this).children().length == 0 ) {
zoneCount++;
var widget = $('<div class="widget" id="wig_'+zoneCount+'" style="width: '+colsNum+'px; height: '+colsNum+'px;"><i class="fa fa-chevron-right resize-icon" aria-hidden="true"></i><i class="fa fa-times remove-widget" title="remove this widget"></i><div class="widget-inner" style="background: '+colors[rando]["col"]+';"><div class="nested-col nested-absolute"></div><div class="zone-txt text-center">'+zoneCount+'</div><div class="widget-title"></div></div></div>');
$(this).append(widget);
widget.css({'min-width':colsNum, 'min-height':colsNum });
setData(widget, $(this));
disableCol($(this));
var sibs = $(this).siblings('.absolute');
// overlap(sibs, $(this));
var ani = $(this).find('.widget-inner');
animateWidget(ani);
var uiItem = $('.widget');
var containTo = $(this).closest('.zone-inner');
resize(uiItem, containTo, cols, colsNum, colsm);
enableSortable();
// var howMany = parseInt($(this).find('.widget').attr('data-height'));
// // for (n = 0; n < howMany; n++) {
// createNestedCols($(this).find('.widget').find('.widget-inner'));
// // }
return false;
}
});
});
// CREATE ZONES FROM ARRAY
function createZones() {
// count the number of zones in zone_array
var count = zone_array.length;
for (var i = 0; i < count; i++) {
$('#zone_div').append('<div id="'+zone_array[i]["id"]+'" class="zone" style="height:'+zone_array[i]["height"]+'; width:'+zone_array[i]["width"]+'; '+zone_array[i]["css"]+'" data-rows="'+zone_array[i]["rowCount"]+'"><div class="zone-helper"><div class="add-row" title="add a row."></div><div class="remove-row" title="remove a row."></div><button class="btn btn-sm clear-zone" title="remove all widgets from this zone.">clear zone</button><button id="add_zone" class="btn btn-sm">add zone</button></div><div id="'+zone_array[i]["innerId"]+'" class="zone-inner"></div></div>');
}
// console.log( JSON.stringify(zone_array, null, "\t") );
} createZones();
// CREATE ZONE-COLS
function createZoneCols() {
$('.zone-inner').each(function(e) {
var self = $(this);
var win = $(this).width();
switch(true) {
case ( 0 < win && win <= 400 ): self.attr('data-cols', '3'); break;
case ( 400 < win && win <= 500 ): self.attr('data-cols', '4'); break;
case ( 500 < win ): self.attr('data-cols', '6'); break;
}
// get the zones width, divide by 12 to make 12 'columns'
var zoneHeight = self.height();
var zoneWidth = parseFloat( self.width() );
var cols = Math.floor( zoneWidth / 12 ); // make 12 columns
var perc = (cols / zoneWidth) * 100; // one column percent of zone-inner
var dyCol = self.attr('data-cols'); // how many widgets are allowed to fit in a row
var colsPerWig = 12 / dyCol; // gives us the 'col-sm' in bootstrap terms for each widget
var colsNum = Math.floor( cols * colsPerWig );
// var rows = Math.floor( zoneHeight / colsNum );
var rows = parseInt(self.closest('.zone').attr('data-rows'));
var rows = 3; // make a square
var colAmount = 13 - colsPerWig;
for (var x = 0; x < rows; x++) {
for (var y = 0; y < colAmount; y++) {
// self.append('<div class="zone-col absolute" data-row="'+x+'" style="min-width: '+ (cols) +'px; min-height: '+ colsNum +'px; top:'+ (colsNum * x) +'px; left: '+ (cols * y) +'px; "></div>');
self.append('<div class="zone-col absolute" data-eq="'+y+'" data-row="'+x+'" style="min-width: '+perc+'%; min-height: '+ colsNum +'px; top:'+(colsNum * x)+'px; left: '+(cols * y)+'px; "></div>');
var appended = self.find('.absolute[data-row="'+x+'"][data-eq="'+y+'"]');
var i = self.find('.absolute').index(appended);
appended.attr('data-index', i);
}
for (var f = 0; f < (12 - colAmount); f++) {
var lastFake = self.find('.fake-col[data-row="'+x+'"]').last();
var left = lastFake.length > 0 ? lastFake.position().left + cols : colAmount * cols;
self.append('<div class="fake-col" data-row="'+x+'" style="min-width: '+perc+'%; min-height: '+ colsNum +'px; top:'+(colsNum * x)+'px; left: '+ left +'px; "></div>');
}
}
var lastCol = self.find('.absolute').last();
var rowCount = parseInt(lastCol.attr('data-row'));
self.css({
'height': (rowCount + 1) * colsNum,
'width': (cols * 12)
});
self.closest('.zone').css({
'height': (rowCount + 1) * colsNum + 50,
'width': (cols * 12) + 15
});
enableSortable();
});
} createZoneCols();
function buildZones() {
for (z = 0; z < zone_array.length; z++) {
// console.log('zone: ' + JSON.stringify(zone_array[z], null, "\t"));
var zone = zone_array[z];
// get the zones width, divide by 12 to make 12 'columns'
var zone_inner = $('.zone-inner');
var zoneWidth = parseFloat(zone_inner.width());
var cols = Math.floor( (zoneWidth / 12) ); // make 12 columns
var colP = ( (cols / zoneWidth) * 100 ); // one column percent of zone-inner
var dyCol = zone_inner.attr('data-cols'); // how many widgets are allowed to fit in a row
var colsPerWig = 12 / dyCol; // gives us the 'col-sm' in bootstrap terms for each widget
var colsNum = Math.floor( cols * colsPerWig );
// go through each widget and append to corresponding zone.
var wig_array = zone['children'];
for (w = 0; w < wig_array.length; w++) {
var current = wig_array[w];
var data_width = parseInt(current["data_width"]) * cols;
var data_height = parseInt(current["data_height"]) * colsNum;
var wig = $('<div class="widget" id="'+current["id"]+'" style="width: '+data_width+'px; height: '+data_height+'px; min-width: '+colsNum+'px; min-height: '+colsNum+'px;" data-x="'+current["data_x"]+'" data-y="'+current["data_y"]+'"><i class="fa fa-chevron-right resize-icon" aria-hidden="true"></i><i class="fa fa-times remove-widget" title="remove this widget"></i><div class="widget-inner" style="background:'+current["bg"]+';"><div class="nested-col nested-absolute"></div><div class="zone-txt text-center">blah</div><div class="widget-title"></div></div></div>');
var goHere;
var og = zone_inner.find('.absolute[data-row="'+current['data_y']+'"]').eq(current['data_x']);
// if the position from the array is not open, find the next open '.zone-col', and add more rows when needed.
// this could happen when the width of the zone changes.
if (og.length == 0) {
// need to find the last .absolute that has .widget child
goHere = zone_inner.find('.absolute:has(".widget")').last().nextAll('.zone-col').first();
if (goHere.length == 0) {
addCols();
}
goHere = zone_inner.find('.absolute:has(".widget")').last().nextAll('.zone-col').first();
} else if (og.hasClass('disabled')) {
goHere = zone_inner.find('.absolute:has(".widget")').last().nextAll('.zone-col').first();
if (goHere.length == 0) {
addCols();
}
goHere = zone_inner.find('.absolute:has(".widget")').last().nextAll('.zone-col').first();
} else {
goHere = og;
}
function moreHeight() {
// when there are not enough rows for a widget's height...
var row_and_height = parseInt(goHere.attr('data-row')) + parseInt(current["data_height"]);
var last_row_num = parseInt(zone_inner.find('.absolute').last().attr('data-row'));
if (row_and_height != last_row_num) {
var amountNeeded = (row_and_height - last_row_num) - 1;
for (n = 0; n < amountNeeded; n++) {
addCols();
}
}
} moreHeight();
goHere.append(wig);
// check if widget is overflowing the zone, if so, find the next open zone-col
var zH = zone_inner[0].offsetHeight;
var zW = zone_inner[0].offsetWidth;
var zR = zone_inner[0].offsetLeft + zW;
var zB = zone_inner[0].offsetTop + zH;
var cH = goHere[0].offsetHeight;
var cW = goHere[0].offsetWidth;
var cR = goHere[0].offsetLeft + cW;
var cB = goHere[0].offsetTop + cH;
while (cR > zR) {
var de = goHere.find('.widget').detach();
goHere = goHere.nextAll('.zone-col').first();
goHere.append(de);
cW = goHere[0].offsetWidth;
cR = goHere[0].offsetLeft + cW;
if (cR < zR) {
break;
}
}
// if there are no open zone-cols, append a new row until the widget fits.
moreHeight();
// enable all the sortables
enableSortable();
// zoneOverflow(zone_inner, goHere);
for (i = 0; i < goHere.siblings('.absolute').length; i++) {
var colSib = goHere.siblings('.absolute').eq(i);
if (colSib.children('.widget').length > 0) {
dropCollision(colSib, goHere);
}
overlap(colSib, goHere);
}
disableCol(goHere);
setData(wig, goHere);
// disableCol(goHere);
// var howMany = parseInt(goHere.find('.widget').attr('data-height'));
// // for (n = 0; n < howMany; n++) {
// createNestedCols(goHere.find('.widget').find('.widget-inner'));
// // }
var containTo = zone_inner;
resize(wig, containTo, cols, colsNum, colsPerWig);
}
}
}
// buildZones();
// function createNestedCols(el) {
// var zoneInner = el.closest('.zone-inner');
// var dyCol = zoneInner.attr('data-cols');
// var zoneWidth = parseFloat( zoneInner.width() );
// var cols = Math.floor( zoneWidth / 12 ); // make 12 columns
// var perc = (cols / zoneWidth) * 100; // one column percent of zone-inner
// var colsPerWig = 12 / dyCol; // gives us the 'col-sm' in bootstrap terms for each widget
// var colsNum = Math.floor( cols * colsPerWig );
// var newCol = $('<div class="nested-col nested-absolute"></div>');
// el.append(newCol);
// }
// function removeWhitespace() {
// var zoneInner = $('.zone-inner');
// var zH = $('.zone-inner').height();
// var zW = $('.zone-inner').width();
// var dyCol = $('.zone-inner').attr('data-cols');
// var c = Math.floor( zW / 12 );
// var colsm = 12 / dyCol;
// var c3 = Math.floor( c * colsm );
// var last = $('.absolute').last();
// var x = $('.absolute').index(last);
// if ($('.absolute').length > ((dyCol * 2) - 1) * 3) {
// for (i = x; i > x - ((dyCol * 2) - 1); i--) {
// console.log(i);
// var change = $('.absolute').eq(i);
// console.log(change);
// change.css('background','blue');
// change.addClass('remove-this');
// }
// if ($('.remove-this').children().length < 1) {
// $('.remove-this').remove();
// $('.zone-inner').height(zH - c3);
// $('.zone').height($('.zone').height() - c3);
// }
// }
// }
function enableSortable() {
var thisWig;
$('.zone-col').sortable({
items: '.widget',
connectWith: '.zone-col',
handle: ".widget-inner",
placeholder: "widget-placeholder ui-corner-all",
// tolerance: 'pointer',
// cursorAt: { left: 20, top: 20 },
start: function(event, ui) {
console.log($(event.target));
var thisCol = $(this);
thisCol.find('.widget').css('position', 'absolute !important');
var zone = $(this).closest('.zone-inner');
var zoneWidth = thisCol.parent('.zone-inner').width();
var cols = parseFloat( zoneWidth / 12 );
var dyCol = thisCol.parent('.zone-inner').attr('data-cols');
var colsm = 12 / dyCol;
var colsNum = Math.floor( cols * colsm );
var allCols = zone.find('.absolute');
var sibs = $(this).siblings('.absolute');
// enable all cols so that we can drop to any cols that we were previously overlapping.
enableAll(allCols);
// loop through the siblings of the currently dragged widget,
// if that sibling has a child, call disableCol() on it.
// then loop through the siblings of that sibling to call overlap() in case it is overlapping other cols.
for (i = 0; i < sibs.length; i++) {
var el = sibs.eq(i);
if ( el.find('.widget').length > 0) {
var elSibs = el.siblings('.absolute');
for (a = 0; a < elSibs.length; a++) {
var colSib = elSibs.eq(a);
overlap(colSib, el);
}
disableCol(el);
}
}
var wig = thisCol.find('.widget-inner');
$('.widget-inner').not(wig).toggleClass('not-dragging');
// set the placeholder's height and width equal to this widget.
var wigW = wig[0].offsetWidth;
var wigH = wig[0].offsetHeight;
zone.find('.widget-placeholder').css({
'width': wigW,
'height': wigH
});
// set the 'ogIndex' so that we can see if it changes on stop.
ui.item.data({
'ogIndex': ui.item.closest('.absolute').index(thisCol)
});
},
over: function(event, ui) {
// refresh sortable columns only when we are dragging over them
// previously called in sortable start and that caused a lot of lag with multiple widgets...
$(this).sortable('refresh');
},
sort: function(event, ui) {
var x = event.pageX;
var y = event.pageY;
var parentOffset = ui.item.offset();
var relX = x - parentOffset.left;
var relY = y - parentOffset.top;
ui.item.offset({
left: Math.floor(x - relX),
top: Math.floor(y - relY)
});
},
receive: function(event, ui) {
var zoneInner = $(this).closest('.zone-inner');
var zH = zoneInner.height();
var zW = zoneInner.width();
var cols = Math.floor( zW / 12 );
var dyCol = zoneInner.attr('data-cols');
var colsm = 12 / dyCol;
var colsNum = Math.floor( cols * colsm );
var thisCol = $(this);
// DETECT ZONE OVERFLOW
// zoneOverflow(zoneInner, thisCol);
function moreHeight() {
// when there are not enough rows for a widget's height...
var row_and_height = parseInt(thisCol.attr('data-row')) + parseInt(ui.item.attr('data-height'));
var last_row_num = parseInt(zoneInner.find('.absolute').last().attr('data-row'));
if (row_and_height != last_row_num) {
var amountNeeded = (row_and_height - last_row_num) - 1;
for (n = 0; n < amountNeeded; n++) {
addCols();
}
}
} moreHeight();
var el = $(this);
var elSibs = $(this).siblings('.absolute');
for (i = 0; i < elSibs.length; i++) {
var colSib = elSibs.eq(i);
if (colSib.find('.widget').length > 0) {
dropCollision(colSib, el);
}
overlap(colSib, el);
}
disableCol(thisCol);
var ani = el.find('.widget-inner');
animateWidget(ani);
if ( ui.item.find(".remove-widget").length == 0 ) {
ui.item.append('<i class="fa fa-chevron-right resize-icon" aria-hidden="true"></i><i class="fa fa-times remove-widget" title="remove this widget"></i>');
ui.item.outerWidth(colsNum);
ui.item.outerHeight(colsNum);
ui.item.css({'min-width':colsNum, 'min-height':colsNum });
var el;
var elSibs;
var uiItem;
}
var uiItem = ui.item;
var containTo = $(this).closest('.zone-inner');
resize(uiItem, containTo, cols, colsNum, colsm);
var widget = $(this).find('.widget');
var zoneCol = $(this);
setData(widget, zoneCol);
var wig = ui.item.find('.widget-inner');
var func = 'zonesortreceive';
var nested = ui.item.find('.nested-widget');
for(b = 0; b < nested.length; b++) {
var self = nested.eq(b);
outOfNest(func, wig, self);
}
},
stop: function(event, ui) {
$('.widget-inner').not(thisWig).toggleClass('not-dragging');
var thisCol = $(this);
var zoneInner = thisCol.closest('.zone-inner');
var el = $(this);
var elSibs = $(this).siblings('.absolute');
// DETECT ZONE OVERFLOW
zoneOverflow(zoneInner, thisCol);
for (i = 0; i < elSibs.length; i++) {
var colSib = elSibs.eq(i);
if (colSib.find('.widget').length > 0) {
dropCollision(colSib, el);
}
overlap(colSib, el);
}
if ($(this).children().length > 0) {
disableCol(thisCol);
}
var ani = el.find('.widget-inner');
animateWidget(ani);
var widget = $(this).find('.widget');
var zoneCol = $(this);
setData(widget, zoneCol);
var wig = ui.item.find('.widget-inner');
var func = 'zonesortstop';
var nested = ui.item.find('.nested-widget');
for(b = 0; b < nested.length; b++) {
var self = nested.eq(b);
outOfNest(func, wig, self);
}
// detect if the item position has changed so that -
// we can remind the user to save...
if (ui.item.closest('.absolute').index(thisCol) != ui.item.data('ogIndex')) {
console.log('position has changed');
}
}
});
$('.nest-contain').sortable({
connectWith: '.nested-col',
items: '.nested-widget',
placeholder: 'nested-placeholder',
handle: '.nested-widget-inner',
zIndex: 9999,
start: function(event, ui) {
if ( ui.item.hasClass('cloner') ) {
clone = ui.item.clone();
return clone;
}
},
stop: function(event,ui) {
if ( ui.item.parent().hasClass('nest-contain') ) {
ui.item.css({'width':'100px', 'height':'100px', 'min-width':'100px', 'min-height':'100px', 'position':'relative !important'});
}
if (ui.item.hasClass('cloner') && ! ui.item.parent('.nest-contain').length ) {
clone.appendTo('.nest-contain');
}
}
});
$('.nested-col').sortable({
items: '.nested-widget',
placeholder: 'nested-placeholder',
forcePlaceholderSize: true,
handle: '.nested-widget-inner',
connectWith:['.nested-col', '.nest-contain'],
zIndex: 9999,
start: function(event, ui) {
var wig = ui.item.closest('.widget');
wig.stack();
var ab = wig.closest('.absolute');
ui.placeholder.css({
'height': ui.placeholder.height() - 30,
'margin-top': '12px'
});
ui.item.data({
'ogParent': ui.item.closest('.widget').attr('id'),
'ogHeight': ui.item.height()
});
$('.nested-col').sortable("option", "stack", ui.item.closest('.widget'));
},
receive: function(event, ui) {
console.log('received');
var zoneInner = $(this).closest('.zone-inner');
var zoneWidth = parseFloat(zoneInner.width());
var cols = Math.floor( (zoneWidth / 12) ); // make 12 columns
var dyCol = zoneInner.attr('data-cols'); // how many widgets are allowed to fit in a row
var colsPerWig = 12 / dyCol; // gives us the 'col-sm' in bootstrap terms for each widget
var colsNum = Math.floor( cols * colsPerWig );
console.log(ui.item.closest('.widget-inner').height());
var minHeight = $(this).height() / ui.item.closest('.widget').attr('data-height');
ui.item.css({
'width': '100%',
'min-height': colsNum - 10,
'height': colsNum - 10
});
var widget = ui.item.closest('.widget');
var zoneCol = widget.closest('.absolute');
var sibs = zoneCol.siblings('.absolute');
var widgetOff = parseInt(widget.attr('data-y')) + parseInt(widget.attr('data-height'));
var csHeight = ui.item.attr('data-cs-height');
if (csHeight != undefined) {
if (csHeight.length) {
while (ui.item.height() < csHeight) {
ui.item.height(ui.item.height() + colsNum);
console.log('something happened');
if (ui.item.height() >= csHeight) {
break;
}
}
}
}
var y1 = widget.offset().top;
var b1 = y1 + widget.height();
var y2 = ui.item.offset().top;
var b2 = y2 + ui.item.height();
// var firstNested = widget.find('.nested-widget').first();
var newHeight = 0;
var nested = widget.find('.nested-widget');
for(var n = 0; n < nested.length; n++) {
var self = nested.eq(n);
console.log('height: ' + self.outerHeight());
newHeight += self.outerHeight();
}
console.log(newHeight);
// if (newHeight > (widget.height() + widget.offset().top)) {
widget.height( Math.round( (newHeight + 10) / colsNum) * colsNum );
// }
setData(widget, zoneCol); // set data attributes
var group = [];
var group2 = [];
var isPushy = '';
for (a = 0; a < sibs.length; a++) {
var sib = sibs.eq(a);
if (sib.find('.widget').length > 0) {
var sibWig = sib.find('.widget');
var pushem = pushy(sib, zoneCol);
if (pushem == true) {
group.push(sibWig[0]);
sibWig[0].remove();
isPushy = true;
} else if (pushem == false) {
console.log('blah');
} else {
console.log(pushem);
var sibWig = pushem.obj.find('.widget');
group2.push(sibWig[0]);
sibWig[0].remove();
// isPushy = true;
}
}
}
if (isPushy == true) {
var groupLength = group.length - 1;
var firstWig = group[0];
var lastWig = group[groupLength];
var wigEnd = (parseInt(widget.attr('data-y')) + parseInt(widget.attr('data-height')));
var goHere = zoneInner.find('.absolute[data-row="'+ (wigEnd - 1) +'"]').last();
if (goHere.length == 0) {
addCols();
}
goHere = zoneInner.find('.absolute[data-row="'+ (wigEnd - 1) +'"]').last();
var rowsNeeded = (wigEnd - widgetOff);
addColsAfter(goHere, rowsNeeded);
for (b = 0; b < sibs.length; b++) {
var sib = sibs.eq(b);
var colY = zoneCol.offset().top;
var colB = zoneCol.height() + colY;
var sibY = sib.offset().top;
var sibB = sib.height() + sibY;
var row = parseInt(sib.attr('data-row'));
if ( sibB > colB && sibY >= colB) {
var de = sib.detach();
zoneInner.append(de);
de.css('top', sib.position().top + (colsNum * rowsNeeded));
de.attr('data-row', row + rowsNeeded);
}
}
var lastCol = zoneInner.find('.absolute').last();
var rowCount = parseInt(lastCol.attr('data-row'));
zoneInner.height((rowCount + 1) * colsNum);
zoneInner.closest('.zone').height( $('.zone-inner').height() + 50 );
// for each element that is being overlapped, find its new column after we have appended rows.
for (var g = 0; g < group.length; g++) {
var self = group[g];
var x = $(self).attr('data-x');
var y = parseInt($(self).attr('data-y')) + rowsNeeded; // our new y position is the original y + the amount of rows we appended.
var putWidgetHere = zoneInner.find('.absolute[data-eq="'+x+'"][data-row="'+y+'"]');
putWidgetHere.append($(self));
setData($(self), putWidgetHere); // set data attributes
}
}
// for each element that is not under the overlapped elements, append to its original position.
for (var g2 = 0; g2 < group2.length; g2++) {
var self = group2[g2];
var x = $(self).attr('data-x');
var y = $(self).attr('data-y');
var putWidgetHere = zoneInner.find('.absolute[data-eq="'+x+'"][data-row="'+y+'"]');
putWidgetHere.append($(self));
setData($(self), putWidgetHere); // set data attributes
}
// var wig = ui.item.closest('.widget-inner');
// var func = event.type;
// outOfNest(func, wig, ui.item);
// var nestSibs = ui.item.siblings('.nested-widget');
// for (i = 0; i < nestSibs.length; i++) {
// var self = nestSibs.eq(i);
// outOfNest(func, wig, self);
// }
function moreHeight() {
// when there are not enough rows for a widget's height...
var row_and_height = parseInt(zoneCol.attr('data-row')) + parseInt(widget.attr('data-height'));
var last_row_num = parseInt(zoneInner.find('.absolute').last().attr('data-row'));
if (row_and_height != last_row_num) {
var amountNeeded = (row_and_height - last_row_num) - 1;
for (n = 0; n < amountNeeded; n++) {
addCols();
}
}
} moreHeight();
enableAll(zoneInner.find('.absolute'));
for (bl = 0; bl < sibs.length; bl++) {
var el = sibs.eq(bl);
if ( el.find('.widget').length > 0) {
var elSibs = el.siblings('.absolute');
for (a = 0; a < elSibs.length; a++) {
var colSib = elSibs.eq(a);
// if (colSib.find('.widget').length > 0) {
// disableCol(colSib);
// }
var sibSibs = colSib.siblings('.absolute');
for (ss = 0; ss < sibSibs.length; ss++) {
var sibSib = sibSibs.eq(ss);
overlap(sibSib, colSib);
}
overlap(colSib, el);
}
disableCol(el);
}
overlap(el, zoneCol);
setData(el.find('.widget'), el); // set data attributes
}
disableCol(zoneCol);
setData(widget, zoneCol); // set data attributes
nestedResize(ui.item, colsNum, ui.item.closest('.nested-col'));
},
stop: function(event, ui) {
var zoneInner = $(this).closest('.zone-inner');
var zoneWidth = parseFloat(zoneInner.width());
var cols = Math.floor( (zoneWidth / 12) ); // make 12 columns
var dyCol = zoneInner.attr('data-cols'); // how many widgets are allowed to fit in a row
var colsPerWig = 12 / dyCol; // gives us the 'col-sm' in bootstrap terms for each widget
var colsNum = Math.floor( cols * colsPerWig );
var wig = ui.item.closest('.widget-inner');
var func = event.type;
outOfNest(func, wig, ui.item);
var sibs = ui.item.siblings('.nested-widget');
for (i = 0; i < sibs.length; i++) {
var self = sibs.eq(i);
outOfNest(func, wig, self);
}
nestedResize(ui.item, colsNum, ui.item.closest('.nested-col'));
},
over: function(event, ui) {
var zoneInner = $(this).closest('.zone-inner');
var zoneWidth = parseFloat(zoneInner.width());
var cols = Math.floor( (zoneWidth / 12) ); // make 12 columns
var dyCol = zoneInner.attr('data-cols'); // how many widgets are allowed to fit in a row
var colsPerWig = 12 / dyCol; // gives us the 'col-sm' in bootstrap terms for each widget
var colsNum = Math.floor( cols * colsPerWig );
var minHeight = colsNum - 10;
console.log('yeeeee');
ui.item.css({
'width': $(this).width()
});
if(ui.item.data('ogParent') != ui.placeholder.closest('.widget').attr('id')) {
ui.placeholder.height(minHeight - 30);
ui.item.height(minHeight);
console.log(ui.placeholder.closest('.widget').attr('id'));
} else {
ui.item.height(ui.item.data('ogHeight'));
ui.placeholder.height(ui.item.data('ogHeight') - 30);
}
}
});
} enableSortable();
function nestedResize(uiItem, colsNum, containTo) {
console.log(containTo);
uiItem.resizable({
containment: containTo,
handles: 's',
grid: [null, colsNum],
start: function(event, ui) {
},
resize: function(event, ui) {
var wig = $(this).closest('.widget-inner');
var func = event.type;
var sibs = $(this).siblings('.nested-widget');
for (i = 0; i < sibs.length; i++) {
var self = sibs.eq(i);
outOfNest(func, wig, self);
}
},
stop: function(event, ui) {
var wig = $(this).closest('.widget-inner');
var func = event.type;
var sibs = $(this).siblings('.nested-widget');
for (i = 0; i < sibs.length; i++) {
var self = sibs.eq(i);
outOfNest(func, wig, self);
}
}
});
}
$(document).on('mousemove', function(e) {
var el = $('.nested-widget.ui-resizable-resizing');
if (el.length > 0) {
var wig = el.closest('.widget-inner');
var i = wig.find('.nested-widget').index(el);
if (i > 0) {
var y1 = el.offset().top;
var b1 = y1 + el.height();
var y2 = wig.offset().top;
var b2 = y2 + wig.height();
if (b1 > b2) {
el.css({ 'max-height': el.height() });
} else {
el.css({ 'max-height': 'unset' });
}
}
}
});
function outOfNest(func, wig, item) {
var zoneInner = item.closest('.zone-inner');
var zoneWidth = parseFloat(zoneInner.width());
var cols = Math.floor( (zoneWidth / 12) ); // make 12 columns
var dyCol = zoneInner.attr('data-cols'); // how many widgets are allowed to fit in a row
var colsPerWig = 12 / dyCol; // gives us the 'col-sm' in bootstrap terms for each widget
var colsNum = Math.floor( cols * colsPerWig );
var x1 = item[0].offsetLeft;
var y1 = item[0].offsetTop;
var r1 = item[0].offsetWidth + x1;
var b1 = item[0].offsetHeight + y1;
var x2 = wig[0].offsetLeft;
var y2 = wig[0].offsetTop;
var r2 = wig[0].offsetWidth + x2;
var b2 = wig[0].offsetHeight + y2;
console.log(b1, b2);
switch(func) {
// case 'resizestop':
// if (b1 > b2) {
// var inner = item.find('.nested-widget-inner').html();
// var re = $('<div class="nested-widget"><div class="nested-widget-inner">'+inner+'</div></div>');
// item.remove();
// if (!item.hasClass('cloner')) {
// $('.nest-contain').append(re);
// }
// }
// break;
case 'resize':
if (b1 > b2) {
item.find('.nested-widget-inner').css({
'background' : 'rgba(245, 75, 66, 0.5)'
});
} else {
item.find('.nested-widget-inner').css({
'background' : '#333'
});
}
break;
case 'sortstop': case 'sortreceive':
if (b1 > b2) {
var inner = item.find('.nested-widget-inner').html();
var re = $('<div class="nested-widget"><div class="nested-widget-inner">'+inner+'</div></div>');
item.remove();
if (!item.hasClass('cloner')) {
$('.nest-contain').append(re);
}
}
break;
case 'zonesortstop': case 'zonesortreceive': case 'resizestop':
if (b1 > b2) {
if (wig.find('.nested-widget').index(item) + 1 == wig.find('.nested-widget').length && y1 < b2) {
console.log('yeeessSDsdee');
item.css({'height': (b1 - b2) - 10});
item.find('.nested-widget-inner').css({
'background' : '#333'
});
} else {
var inner = item.find('.nested-widget-inner').html();
var re = $('<div class="nested-widget"><div class="nested-widget-inner">'+inner+'</div></div>');
item.remove();
if (!item.hasClass('cloner')) {
$('.nest-contain').append(re);
}
}
}
break;
}
}
function addCols() {
var zoneHeight = $('.zone-inner').height();
var zoneWidth = $('.zone-inner').width();
var cols = Math.floor( zoneWidth / 12 ); // make 12 columns
var perc = (cols / zoneWidth) * 100;
var dyCol = $('.zone-inner').attr('data-cols');
var colsPerWig = 12 / dyCol; // gives us the 'col-sm' in bootstrap terms for each widget
var colsNum = Math.floor( cols * colsPerWig );
$('.zone-inner').closest('.zone').height( (zoneHeight + colsNum));
var lastCol = $('.zone-inner').find('.absolute').last();
var appendHere = lastCol[0].offsetTop + colsNum;
var rowCount = parseInt(lastCol.attr('data-row'));
var colAmount = (12 - colsPerWig) + 1;
for (var i = 0; i < colAmount; i++) {
$('.zone-inner').append('<div class="zone-col absolute ui-sortable" data-eq="'+i+'" data-row="'+(rowCount + 1)+'" style="min-width: '+perc+'%; min-height: '+colsNum+'px; top:'+appendHere+'px; left: '+(cols * i)+'px; "></div>');
var blah = $('.zone-inner').find('.absolute[data-row="'+(rowCount + 1)+'"][data-eq="'+i+'"]');
var n = $('.zone-inner').find('.absolute').index(blah);
blah.attr('data-index', n);
}
// for (var f = 0; f < (12 - colAmount); f++) {
// var lastFake = $('.zone-inner').find('.fake-col[data-row="'+(rowCount + 1)+'"]').last();
// var left = lastFake.length > 0 ? lastFake.position().left + cols : colAmount * cols;
// $('.zone-inner').append('<div class="fake-col" data-row="'+(rowCount + 1)+'" style="min-width: '+perc+'%; min-height: '+ colsNum +'px; top:'+(colsNum * (rowCount + 1))+'px; left: '+ left +'px; "></div>');
// }
lastCol = $('.zone-inner').find('.absolute').last();
rowCount = parseInt(lastCol.attr('data-row'));
$('.zone-inner').height((rowCount + 1) * colsNum);
$('.zone-inner').closest('.zone').height( $('.zone-inner').height() + 50 );
enableSortable();
}
function addColsAfter(el, rowsNeeded) {
var zoneInner = el.closest('.zone-inner');
var zoneHeight = zoneInner.height();
var zoneWidth = zoneInner.width();
var cols = Math.floor( zoneWidth / 12 ); // make 12 columns
var perc = (cols / zoneWidth) * 100;
var dyCol = zoneInner.attr('data-cols');
var colsPerWig = 12 / dyCol; // gives us the 'col-sm' in bootstrap terms for each widget
var colsNum = Math.floor( cols * colsPerWig );
zoneInner.closest('.zone').height( (zoneHeight + colsNum));
var appendHere = el[0].offsetTop + colsNum;
var rowCount = parseInt(el.attr('data-row')) + 1;
var colAmount = (12 - colsPerWig) + 1;
for (la = 0; la < rowsNeeded; la++) {
for (var i = 0; i < colAmount; i++) {
zoneInner.append('<div class="zone-col absolute ui-sortable" data-eq="'+i+'" data-row="'+rowCount+'" style="min-width: '+perc+'%; min-height: '+colsNum+'px; top:'+appendHere+'px; left: '+(cols * i)+'px;"></div>');
var blah = $('.zone-inner').find('.absolute[data-row="'+(rowCount + 1)+'"][data-eq="'+i+'"]');
var n = $('.zone-inner').find('.absolute').index(blah);
blah.attr('data-index', n);
}
console.log(la);
appendHere = el[0].offsetTop + (colsNum * (la + 2));
rowCount = rowCount + 1;
}
enableSortable();
}
// REMOVE WIDGET
$(document).on('click', '.remove-widget', function() {
var widget = $(this).closest('.widget');
var zone = widget.closest('.zone-inner');
var zoneCol = widget.closest('.absolute');
var zoneWidth = zone.width();
var widgetID = widget.attr('id');
var widgetTxt = widget.find('.zone-txt').html();
var allCols = zone.find('.absolute');
var sibs = $(this).closest('.absolute').siblings('.absolute');
enableAll(allCols);
widget.remove();
// again looping through all sortable columns to find overlap.
for (i = 0; i < sibs.length; i++) {
var el = sibs.eq(i);
if ( el.find('.widget').length > 0 ) {
disableCol(el);
var elSibs = el.siblings('.absolute');
for (a = 0; a < elSibs.length; a++) {
var colSib = elSibs.eq(a);
overlap(colSib, el);
}
}
}
var nested = widget.find('.nested-widget').not('.cloner');
nested.css({'height':'100px', 'width':'100px', 'min-height':'100px'});
$('.nest-contain').append(nested);
// refresh sortable columns, causes lag but is needed.
allCols.sortable('refresh');
zoneCount = 0;
for (x = 0; x < zone.find('.widget').length; x++) {
var wig = zone.find('.widget').eq(x);
var txt = wig.find('.zone-txt');
var reCount = zoneCount + 1;
wig.attr('id','wig_' + reCount);
txt.html(zoneCount = reCount);
}
});
// CLEAR CHOSEN ZONE
$(document).on('click', '.clear-zone', function() {
var zoneInner = $(this).closest('.zone').find('.zone-inner');
var nested = zoneInner.find('.nested-widget').not('.cloner');
zoneInner.empty();
nested.css({'height':'100px', 'width':'100px', 'min-height': '100px'});
$('.nest-contain').append(nested);
createZoneCols();
zoneCount = 0;
});
$(document).on('click', '.remove-nested-widget', function() {
var self = $(this).closest('.nested-widget');
var inner = self.find('.nested-widget-inner').html();
var re = $('<div class="nested-widget"><div class="nested-widget-inner">'+inner+'</div></div>');
self.remove();
$('.nest-contain').append(re);
});
// cursor grabbing
// will not work properly when developer tools are open
// function mouseDrag() {
// var ismousedown = false;
// $(document).on({
// mouseover: function() { ismousedown = false; $(this).addClass('grab'); },
// mousedown: function() { ismousedown = true; $(this).addClass('grabbing').removeClass('grab').css('cursor','grabbing !important'); },
// mouseup: function() { ismousedown = false; $(this).removeClass('grabbing').addClass('grab').css('cursor','grab !important');},
// mousemove: function() {
// if (ismousedown == true) {
// $(this).addClass('grabbing').removeClass('grab').css('cursor','grabbing !important');
// }
// },
// mouseout: function() { $(this).removeClass('grab grabbing'); }
// }, '.widget-inner');
// } mouseDrag();
// SERIALIZATION
$('#save_coord').on('click', function(e) {
e.preventDefault();
var coords = [];
$.each($('.zone-inner'), function() {
var self = this;
var widgets = $(self).find('.widget');
var coord = $(self).position();
var coord_width = $(self).outerWidth();
var coord_height = $(self).outerHeight();
var children = [];
var item = { id: $(self).parent().attr('id'), x: coord.left, y: coord.top, width: coord_width, height: coord_height, data_cols: $(self).attr('data-cols'), children: children};
coords.push(item);
var count = coords.length - 1;
$(widgets).each(function() {
var widgetSelf = $(this);
var el = widgetSelf.parent('.absolute');
var widgetWidth = $(el).outerWidth(true);
var zoneWidth = $(el).parent('.zone-inner').width();
var data_sm = $(widgetSelf).attr('data-width');
var sibs = $(el).siblings('.absolute');
var x1 = $(el).position().left;
var y1 = $(el).position().top;
var r1 = x1 + $(el).width();
var b1 = y1 + $(el).height();
var offPerc = parseFloat( (x1 / zoneWidth) * 100 );
// var offset = $(widgetSelf).attr('data-x');
// sibs.each(function() {
// var self = this;
// var x2 = $(self).position().left;
// var y2 = $(self).position().top;
// var r2 = x2 + $(self).width();
// var b2 = y2 + $(self).height();
// if ( $(self).children('.widget').length > 0 ) {
// var widget = $(self).find('.widget');
// if ( y1==y2 && x1>x2) {
// offset = offset - widget.attr('data-width');
// }
// if ( x1==r2 && y1==y2 && b1==b2 || x1==r2 && y1>=y2 && b1<=b2 || x1==r2 && y1<=y2 && b1>=b2 ) {
// offset = 0;
// }
// }
// });
var elCoord = el.position();
var elCoord_width = el.outerWidth();
var elCoord_height = el.outerHeight();
var elItem = { id: el.children().attr('id'), x: elCoord.left, y: elCoord.top, data_x: widgetSelf.attr('data-x'), data_y: widgetSelf.attr('data-y'), data: elCoord_width, height: elCoord_height, data_width: widgetSelf.attr('data-width'), data_height: widgetSelf.attr('data-height'), col: data_sm, bg: widgetSelf.find('.widget-inner').css('background') };
coords[count]['children'].push(elItem);
});
});
console.log( JSON.stringify(coords, null, "\t") );
console.log(coords);
});
// Resize function
function resize(uiItem, containTo, cols, colsNum, colsm) {
uiItem.resizable({
//(Math.floor( cols * colsm ) / 2)
grid: [cols, colsNum],
handles: 'se',
containment: containTo,
start: function(event, ui) {
// DETECT COLLISION
var el = ui.element.closest('.absolute');
var elSibs = ui.element.closest('.absolute').siblings('.absolute');
// so that we can continue resizine once there is no collision
ui.element.resizable( "option", "maxHeight", null );
ui.element.resizable( "option", "maxWidth", null );
for (i = 0; i < elSibs.length; i++) {
var colSib = elSibs.eq(i);
var child = colSib.children().length;
if (child > 0) {
for (x = 0; x < colSib.length; x++) {
var self = colSib.eq(x);
resizeCollision(self, el);
}
}
}
var thisWig = ui.element.find('.widget-inner');
$('.widget-inner').not(thisWig).toggleClass('not-dragging');
// don't let the widget resize up past the last nested-widget
var nested = $(this).find('.nested-widget').last();
if (nested.length > 0) {
var nestY = nested.offset().top;
var nestB = nestY + nested.height();
var elY = $(this).offset().top;
var elB = elY + $(this).height();
if ( (nestB - 8) == elB ) {
$(this, '.ui-resizable-resizing').resizable( "option", "minHeight", $(this).outerHeight());
}
}
// find the original size of item so we can later detect if it has changed.
ui.element.data('ogSize', { width: ui.element[0].offsetWidth, height: ui.element[0].offsetHeight });
},
resize: function(event, ui) {
// DETECT COLLISION
var el = ui.element.parent();
var elSibs = ui.element.parent().siblings('.absolute');
// so that we can continue to resize once there is no collision
ui.element.resizable( "option", "maxHeight", null );
ui.element.resizable( "option", "maxWidth", null );
// don't let the widget resize up past the last nested-widget
var nested = $(this).find('.nested-widget').last();
if (nested.length > 0) {
var nestY = nested.offset().top;
var nestB = nestY + nested.height();
var elY = $(this).offset().top;
var elB = elY + $(this).height();
if ( (nestB-8) == elB ) {
$(this, '.ui-resizable-resizing').resizable( "option", "minHeight", $(this).outerHeight());
}
}
for (i = 0; i < elSibs.length; i++) {
var colSib = elSibs.eq(i);
var child = colSib.children('.widget').length;
if (child > 0) {
for (x = 0; x < colSib.length; x++) {
var self = colSib.eq(x);
resizeCollision(self, el);
}
}
colSib.addClass('zone-col').removeClass('disabled');
overlap(colSib, el);
disableCol(el);
}
},
stop: function(event, ui) {
var wig = ui.element;
var thisWig = wig.find('.widget-inner');
var el = wig.parent();
var elSibs = el.siblings('.absolute');
for (i = 0; i < elSibs.length; i++) {
var colSib = elSibs.eq(i);
var sibs = colSib.siblings('.absolute');
if (colSib.children('.widget').length > 0) {
// overlap(colSib, el);
resizeCollision(colSib, el);
for (x = 0; x < sibs.length; x++) {
var thisSib = sibs.eq(x);
overlap(thisSib, colSib);
}
dropCollision(colSib, el);
disableCol(colSib);
}
}
// drop/stop animation
var ani = thisWig;
animateWidget(ani);
// not working in "else" conditions, making other "if" conditions obsolete
wig.resizable("option", "maxHeight", null);
wig.resizable("option", "maxWidth", null);
$('.widget-inner').not(thisWig).toggleClass('not-dragging');
// set data attributes
setData(wig, el);
// detect if the item size has changed so that
// we can remind the user to save...
if (ui.element[0].offsetWidth != ui.element.data('ogSize').width || ui.element[0].offsetHeight != ui.element.data('ogSize').height) {
console.log('Size has changed.');
}
var zoneInner = ui.element.closest('.zone-inner');
var zoneWidth = zoneInner.width();
var cols = Math.floor( zoneWidth / 12 ); // make 12 columns
var perc = (cols / zoneWidth) * 100;
var dyCol = zoneInner.attr('data-cols');
var colsPerWig = 12 / dyCol; // gives us the 'col-sm' in bootstrap terms for each widget
var colsNum = Math.floor( cols * colsPerWig );
$(this, '.ui-resizable-resizing').resizable( "option", "minHeight", null);
}
});
}
// COLLISION DETECTION FUNCTION
function resizeCollision(colSib, el) {
var zone = el.closest('.zone-inner');
var zoneHeight = zone[0].offsetHeight;
var zoneWidth = zone[0].offsetWidth;
var x1 = el[0].offsetLeft; // ui.element left position
var y1 = el[0].offsetTop; // ui.element top position
var b1 = y1 + el[0].offsetHeight; // ui.element "bottom" position
var r1 = x1 + el[0].offsetWidth; // ui.element "right" position
var x2 = colSib[0].offsetLeft; // collided sibling left position
var y2 = colSib[0].offsetTop; // collided sibling top position
var b2 = y2 + colSib[0].offsetHeight; // collided sibling "bottom" position
var r2 = x2 + colSib[0].offsetWidth; // collided sibling "right" position
// X-AXIS
if ( (r1 == x2 && y1 == y2 || r1 == x2 && b1 == b2)
|| (y2 < y1 && b2 > b1 && r2 > r1)
|| (y1 < y2 && b1 > b2 && r2 > r1)
|| (y2 < b1 && b2 > b1 && r1 == x2) )
{
$('.ui-resizable-resizing').resizable( "option", "maxWidth", (x2 - x1) );
}
// Y-AXIS
if ( (b1 == y2 && x1 == x2 || b1 == y2 && r1 == r2)
|| (x1 < x2 && r1 > r2 && b1 == y2)
|| (x2 < x1 && r2 > r1 && b1 == y2)
|| (r2 > r1 && x2 < r1 && b1 == y2) )
{
$('.ui-resizable-resizing').resizable( "option", "maxHeight", (y2 - y1) );
}
}
function disableCol(thisCol) {
var zone = thisCol.closest('.zone-inner');
var zoneWidth = zone ? zone[0].offsetWidth : '';
var cols = Math.floor( zoneWidth / 12 );
var dyCol = thisCol.closest('.zone-inner').attr('data-cols');
var colsPerWig = 12 / dyCol;
var colAmount = (12 - colsPerWig) + 1;
var disable = 12 - colAmount;
var cols8 = cols * (colAmount - 1);
var i = zone.find('.absolute').index(thisCol);
// if (dyCol == 7 || dyCol == 9 || dyCol == 10) {
// var cols8 = (dyCol - colsPerWig) * (cols * colsPerWig);
// } else {
// var cols8 = (dyCol - 1) * (cols * colsPerWig);
// }
// if (thisCol[0].offsetLeft <= 0) {
// thisCol.removeClass('zone-col').addClass('disabled');
// for (a = i; a <= i + disable; a++) {
// var toDisable = zone.find('.absolute').eq(a);
// if (toDisable[0].offsetTop == thisCol[0].offsetTop) {
// zone.find('.absolute').eq(a).removeClass('zone-col').addClass('disabled');
// }
// }
// } else if (thisCol[0].offsetLeft >= cols8 ) {
// thisCol.removeClass('zone-col').addClass('disabled');
// for (b = i; b >= i - disable; b--) {
// var toDisable = zone.find('.absolute').eq(b);
// if (toDisable[0].offsetTop == thisCol[0].offsetTop) {
// toDisable.removeClass('zone-col').addClass('disabled');
// }
// }
// } else {
thisCol.removeClass('zone-col').addClass('disabled');
for (a = i; a <= i + disable; a++) {
var toDisable = zone.find('.absolute').eq(a);
if (toDisable.length > 0) {
if (toDisable[0].offsetTop == thisCol[0].offsetTop) {
zone.find('.absolute').eq(a).removeClass('zone-col').addClass('disabled');
}
}
}
for (b = i; b >= i - disable; b--) {
var toDisable = zone.find('.absolute').eq(b);
if (toDisable[0].offsetTop == thisCol[0].offsetTop) {
toDisable.removeClass('zone-col').addClass('disabled');
}
}
// }
}
function enableCol(thisCol) {
thisCol.addClass('zone-col').removeClass('disabled');
thisCol.prev().addClass('zone-col').removeClass('disabled');
thisCol.next().addClass('zone-col').removeClass('disabled');
}
function enableAll(allCols) {
allCols.addClass('zone-col').removeClass('disabled');
}
// check if a zone-col is overlapping any other zone-cols,
// if so, disabled those zone-cols.
function overlap(colSib, el) {
var zone = el.closest('.zone-inner');
var zoneHeight = zone[0].offsetHeight;
var zoneWidth = zone[0].offsetWidth;
var x1 = el[0].offsetLeft; // ui.element left position
var y1 = el[0].offsetTop; // ui.element top position
var b1 = y1 + el[0].offsetHeight; // ui.element "bottom" position
var r1 = x1 + el[0].offsetWidth; // ui.element "right" position
var x2 = colSib[0].offsetLeft; // collided sibling left position
var y2 = colSib[0].offsetTop; // collided sibling top position
var b2 = y2 + colSib[0].offsetHeight; // collided sibling "bottom" position
var r2 = x2 + colSib[0].offsetWidth; // collided sibling "right" position
// causing slow down?
// var i = zone.find('.absolute').index(colSib); // at 64 widgets: ~604ms
var i = colSib.attr('data-index'); // at 64 widgets: ~168ms
var cols = Math.floor( zoneWidth / 12 );
var dyCol = zone.attr('data-cols');
var colsPerWig = 12 / dyCol;
var colAmount = (12 - colsPerWig) + 1;
if ( r1 > x2 && x1 < r2 && y1 <= y2 && b1 >= b2 ) {
colSib.addClass('disabled').removeClass('zone-col');
for (b = i; b >= i - (12 - colAmount); b--) {
var toDisable = zone.find('.absolute').eq(b);
if (toDisable[0].offsetTop == y2) {
toDisable.removeClass('zone-col').addClass('disabled');
}
}
}
}
function pushy(sib, el) { // when a nested widget overflows the parent widget, we need to push down any widgets below said widget.
var zone = el.closest('.zone-inner');
var zoneHeight = zone[0].offsetHeight;
var zoneWidth = zone[0].offsetWidth;
var x1 = el[0].offsetLeft;
var y1 = el[0].offsetTop;
var b1 = y1 + el[0].offsetHeight;
var r1 = x1 + el[0].offsetWidth;
var x2 = sib[0].offsetLeft;
var y2 = sib[0].offsetTop;
var b2 = y2 + sib[0].offsetHeight;
var r2 = x2 + sib[0].offsetWidth;
var theSib = sib;
var res = false;
if ( (b1>y2 && y1<y2 && (x1==x2 || r1==r2))
|| (r1<r2 && x1>x2 && (y1==y2 || (b1==b2 || (b1>b2 && (y1<y2 || y1==y2)))) )
|| (x1<x2 && r1>r2 && (y1==y2 || (b1==b2 || (b1>b2 && (y1<y2 || y1==y2)))) )
|| (x1>x2 && r1<r2 && (y1==y2 || (b1==b2 || (b1>b2 && (y1<y2 || y1==y2)))) )
|| (r1>x2 && r1<r2 && y2<b1 && y2>y1)
|| (x1>x2 && x1<r2 && y2<b1 && y2>y1) )
{
sib.find('.widget').css('background', 'green');
res = true;
} else if (x2 >= r1 || r2 <= x1) {
res = {push: 'no', obj: theSib};
// res = 'nopush';
}
return res;
}
// check for collision when dropping a widget, if it overlaps another widget.
function dropCollision(colSib, el) {
console.log('yooooooohoooooooooo');
var zone = el.closest('.zone-inner');
var zoneHeight = zone[0].offsetHeight;
var zoneWidth = zone[0].offsetWidth;
var x1 = el[0].offsetLeft; // ui.element left position
var y1 = el[0].offsetTop; // ui.element top position
var b1 = y1 + el[0].offsetHeight; // ui.element "bottom" position
var r1 = x1 + el[0].offsetWidth; // ui.element "right" position
var x2 = colSib[0].offsetLeft; // collided sibling left position
var y2 = colSib[0].offsetTop; // collided sibling top position
var b2 = y2 + colSib[0].offsetHeight; // collided sibling "bottom" position
var r2 = x2 + colSib[0].offsetWidth; // collided sibling "right" position
//X-AXIS
if ( (r1 > x2 && x1 < x2 && (y1 == y2 || b1 == b2 )) || (y1 > y2 && b1 < b2 && (x1 == x2 || r1 == r2)) || (y1 < b2 && b1 >= b2 && y1 >= y2 && r1 >= x2 && x1 < x2) )
{
el.find('.widget').css('width', (x2 - x1) );
}
//Y-AXIS
if ( (b1>y2 && y1<y2 && (x1==x2 || r1==r2))
|| (r1<r2 && x1>x2 && (y1==y2 || (b1==b2 || (b1>b2 && (y1<y2 || y1==y2)))) )
|| (x1<x2 && r1>r2 && (y1==y2 || (b1==b2 || (b1>b2 && (y1<y2 || y1==y2)))) )
|| (x1>x2 && r1<r2 && (y1==y2 || (b1==b2 || (b1>b2 && (y1<y2 || y1==y2)))) )
|| (r1>x2 && r1<r2 && y2<b1 && y2>y1)
|| (x1>x2 && x1<r2 && y2<b1 && y2>y1) )
{
el.find('.widget').css('height', (y2 - y1) );
}
}
function zoneOverflow(zoneInner, el) {
var col = parseFloat(zoneInner.width() / 12);
var dyCol = zoneInner.attr('data-cols');
var colsPerWig = 12 / dyCol; // gives us the 'col-sm' in bootstrap terms for each widget
var colsNum = Math.floor( col * colsPerWig );
var zH = zoneInner[0].offsetHeight;
var zW = zoneInner[0].offsetWidth;
var cols = Math.floor(zW / colsNum);
var rows = Math.floor(zH / colsNum);
var zR = zoneInner[0].offsetLeft + zW;
var zB = zoneInner[0].offsetTop + zH;
var cH = el[0].offsetHeight;
var cW = el[0].offsetWidth;
var cR = el[0].offsetLeft + cW;
var cB = el[0].offsetTop + cH;
if ( cR > zR ) {
el.find('.widget').css('width', ((cols * colsNum) - el[0].offsetLeft));
}
if ( cB > zB ) {
el.find('.widget').css('height', ((rows * colsNum) - el[0].offsetTop));
}
}
// set data attributes like: 'data_width', 'data_height', 'data_x', and 'data_y'
// these attributes will be used when loading in widgets.
function setData(widget, zoneCol) {
if (widget[0] != undefined && zoneCol[0] != undefined) {
var widgetWidth = Math.floor(widget[0].offsetWidth);
var widgetHeight = Math.floor(widget[0].offsetHeight);
var zoneWidth = widget.closest('.zone-inner')[0].offsetWidth;
var zoneHeight = widget.closest('.zone-inner')[0].offsetHeight;
var cols = Math.floor( (zoneWidth / 12) ); // make 12 columns
var dyCol = widget.closest('.zone-inner').attr('data-cols');
var colsm = 12 / dyCol;
var colsNum = Math.floor( cols * colsm );
var rows = Math.floor(zoneHeight / colsNum);
var colPerc = parseFloat( (widgetWidth / zoneWidth) * 100 );
var data_width;
switch(true) {
case ( 92 < colPerc && colPerc <= 100 ): data_width = 12; break;
case ( 84 < colPerc && colPerc < 92 ): data_width = 11; break;
case ( 76 < colPerc && colPerc < 84 ): data_width = 10; break;
case ( 67 < colPerc && colPerc < 76 ): data_width = 9; break;
case ( 59 < colPerc && colPerc < 67 ): data_width = 8; break;
case ( 51 < colPerc && colPerc < 59 ): data_width = 7; break;
case ( 42 < colPerc && colPerc < 51 ): data_width = 6; break;
case ( 34 < colPerc && colPerc < 42 ): data_width = 5; break;
case ( 26 < colPerc && colPerc < 34 ): data_width = 4; break;
case ( 17 < colPerc && colPerc < 26 ): data_width = 3; break;
case ( 9 < colPerc && colPerc < 17 ): data_width = 2; break;
case ( 0 < colPerc && colPerc < 9 ): data_width = 1; break;
}
var data_height = parseFloat(widgetHeight / colsNum);
var data_y = zoneCol.attr('data-row');
var data_x = $('.absolute[data-row="'+data_y+'"]').index(widget.closest('.absolute[data-row="'+data_y+'"]'));
widget.attr({
'data-width': data_width,
'data-height': data_height,
'data-x': data_x,
'data-y': data_y
});
}
}
function animateWidget(el) {
el.queue('fx', function(next) {
$(this).addClass('animate');
next();
});
el.delay(400).queue('fx', function(next) {
$(this).removeClass('animate');
next();
});
}
// });
// } loadDoc();
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jqueryui/1.11.4/jquery-ui.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jqueryui-touch-punch/0.2.3/jquery.ui.touch-punch.min.js"></script>
@import url('https://fonts.googleapis.com/css?family=Baloo+Da&display=swap');
.flat {
--border-rad: 0;
}
.round {
--border-rad: 5px;
}
:root {
--widget-bg: #111;
--zone-bg: #333;
--zone-bord: #000;
--placeholder: rgba(0, 0, 0, 0.5);
--no-drag: rgba(100, 100, 100, 0.2);
/* --wig-color: #4457ad; */
}
.zone_div {
width: 100%;
}
.zone * {
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";
}
.widget-title {
background: transparent;
border: none;
position: absolute;
top: 30px;
right: 5px;
bottom: 20px;
left: 5px;
}
.txt-area {
display: none;
width: 100%;
height: 100%;
resize: none;
color: #4457ad;
background: #222;
border-radius: 5px;
border: 1px solid #222;
padding: 10px;
outline: none;
}
.title-txt {
display: inline-block;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%,-50%);
font-family: 'Baloo Da', cursive;
color: var(--wig-color);
font-size: 18px;
word-break: break-word;
}
body {
background: #111;
/* overflow-x: hidden; */
padding-bottom: 200px;
position: relative;
}
.btn {
background: #fff;
color: #222;
font-weight: bold;
border: 2px solid #222;
border-radius: 5px;
}
.absolute::after, .fake-col::after, .absolute::before, .fake-col::before {
content: '';
height: 1px;
background: rgba(0,0,0,0.1);
position: absolute;
top: 50%;
left: 0;
right: 0;
}
.zone-inner[data-cols="3"] .absolute::after, .zone-inner[data-cols="3"] .fake-col::after, .zone-inner[data-cols="4"] .absolute::after, .zone-inner[data-cols="4"] .fake-col::after {
top: 33%;
}
.zone-inner[data-cols="3"] .absolute::before, .zone-inner[data-cols="3"] .fake-col::before, .zone-inner[data-cols="4"] .absolute::before, .zone-inner[data-cols="4"] .fake-col::before {
top: unset;
bottom: 33%;
}
.absolute {
position: absolute !important;
outline: 1px solid rgba(0,0,0,0.1);
}
.fake-col {
outline: 1px solid rgba(0,0,0,0.1);
position: absolute;
}
.relative {
position: relative !important;
}
.zone, .zone-out {
position: relative;
display: inline-block;
float: left;
background: var(--zone-bg);
border: 2px solid var(--zone-bord);
border-radius: 5px;
}
.zone-helper {
display: block;
position: sticky;
top: 0px;
height: 40px;
padding: 5px;
background: transparent;
z-index: 1048;
}
.clear-zone, #add_zone {
float: right;
margin: 5px;
transition: 0.3s;
}
.btn:active {
transform-origin: 50% 50%;
transform: scale(0.9);
}
.zone-inner, .zone-out-inner {
position: absolute;
top: 45px;
right: 5px;
bottom: 5px;
left: 5px;
height: 100%;
}
.zone-col {
height: auto;
width: auto;
}
.zone .widget {
position: relative;
float: left;
}
.widget-inner {
position: absolute;
background: var(--widget-bg);
z-index: 8;
border-radius: 5px;
cursor: move;
}
.ui-helper {
position: absolute;
background: var(--widget-bg);
/* z-index: 8; */
box-shadow: 2px 2px 5px rgba(0,0,0,0.5);
border-radius: 5px;
}
.zone-txt {
font-family: 'Baloo Da', cursive;
color: var(--wig-color);
font-size: 18px;
}
#holder .widget-inner {
top: 5px;
right: 5px;
bottom: 5px;
left: 5px;
}
.zone-inner .widget-inner {
top: 5px;
right: 5px;
bottom: 5px;
left: 5px;
box-shadow: 1px 1px 5px rgba(0,0,0,0.8);
}
.widget {
border-radius: 5px;
}
.widget:hover .resize-icon, .widget:hover .remove-widget, .widget:hover .edit-icon, .widget:hover .add-icon {
display: block;
}
.widget-placeholder {
border: 1px dashed var(--widget-bg) !important;
border-radius: 5px;
background: var(--placeholder);
top: 5px;
left: 5px;
bottom: 5px;
right: -100%;
/* z-index: 3; */
position: absolute;
}
.hold-col .widget-placeholder {
opacity: 0;
}
.ui-resizable-se {
height: 20px;
width: 20px;
position: absolute;
bottom: 5px;
right: 5px;
opacity: 0;
cursor: se-resize;
}
.ui-resizable-sw {
display: none !important;
}
.resize-icon {
color: var(--wig-color);
font-size: 15px;
transform: rotate(45deg);
position: absolute;
bottom: 6px;
right: 9px;
display: none;
z-index: 9;
}
.remove-widget {
color: var(--wig-color);
position: absolute;
top: 10px;
right: 11px;
font-size: 15px;
cursor: pointer;
display: none;
z-index: 9;
}
.widget.ui-resizable-resizing {
border: 1px dashed var(--widget-bg) !important;
/* background: var(--placeholder); */
/* border-radius: 5px; */
}
.grab {
cursor: grab !important;
}
.grabbing {
cursor: grabbing !important;
}
.dragging {
/* z-index: 9999; */
}
.not-dragging {
background: var(--no-drag);
}
.disabled {
/* background: red !important; */
}
@media screen and (max-width: 700px) {
.widget .remove-widget {
display: block;
}
.widget .resize-icon {
display: block;
}
}
.animate {
animation-name: pop;
animation-iteration-count: 1;
animation-duration: 0.3s;
animation-timing-function: ease-in-out;
/* z-index: 9999 !important; */
}
@keyframes pop {
0% {
top: 5px;
right: 5px;
bottom: 5px;
left: 5px;
}
50% {
top: 0px;
right: 0px;
bottom: 0px;
left: 0px;
}
100% {
top: 5px;
right: 5px;
bottom: 5px;
left: 5px;
}
}
.nest-contain {
height: 200px;
width: 220px;
background: #ccc;
position: sticky;
top: 20px;
right: 20px;
}
.nested-col {
width: 100%;
height: calc(100% - 10px);
top: 10px;
position: absolute;
}
.nest-contain .nested-widget {
position: relative !important;
float: left;
height: 100px !important;
width: 100px !important;
}
.nested-widget {
/* position: relative !important; */
/* float: left !important; */
width: 100%;
}
.nested-col .nested-widget:not(:first-child) {
margin-top: 10px !important;
}
.nest-contain .nested-widget-inner {
top: 10px;
right: 10px;
bottom: 10px;
left: 10px;
}
.nested-col .nested-widget-inner {
top: 15px;
right: 20px;
bottom: 15px;
left: 20px;
}
.nested-widget-inner {
position: absolute;
background: #333;
border-radius: 5px;
z-index: 9000 !important;
}
.remove-nested-widget {
position: absolute;
right: 5px;
top: 0px;
font-size: 13px;
color: white;
cursor: pointer;
}
.nested-col .remove-nested-widget {
display: none;
}
.nested-col .nested-widget-inner:hover .remove-nested-widget {
display: block;
}
.nest-contain .remove-nested-widget {
display: none;
}
.nested-widget-txt {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%,-50%);
color: white;
}
.nested-placeholder {
margin-left: 20px;
width: calc(100% - 40px);
border: 1px dashed #111;
background: rgba(0,0,0,0.2);
border-radius: 5px;
}
.nested-widget.ui-resizable-resizing .ui-resizable-s {
background: rgba(255,255,255, 0.1);
display: block !important;
}
.nested-widget:hover .ui-resizable-s {
display: block !important;
}
.ui-resizable-s {
z-index: 9999 !important;
height: 17px;
position: absolute;
bottom: 15px;
left: 20px;
right: 20px;
cursor: s-resize;
background: rgba(0,0,0,0.2);
border-radius: 0 0 5px 5px;
display: none !important;
}
.ui-resizable-s::after, .ui-resizable-s::before {
color: white;
font-size: 12px;
content: '\f078';
font-family: 'Font Awesome 5 Free';
font-weight: 600;
position: absolute;
top: 0px;
}
.ui-resizable-s::after {
right: 5px;
}
.ui-resizable-s::before {
left: 5px;
}
/* width */
#holder::-webkit-scrollbar {
width: 10px;
}
/* Track */
#holder::-webkit-scrollbar-track {
background: var(--zone-bg);
}
/* Handle */
#holder::-webkit-scrollbar-thumb {
background: var(--zone-bord);
}
/* Handle on hover */
#holder::-webkit-scrollbar-thumb:hover {
background: #111;
}
.something-changed {
color: #b81825;
font-weight: bold;
display: none;
}
<link href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.1.3/css/bootstrap.min.css" rel="stylesheet" />
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.8.2/css/all.min.css" rel="stylesheet" />
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment