class Dashboard {
//@TODO:
// - Pouvoir indiquer que la dashboard à une taille dynamique :
// - On laisse toujours 2-3 lignes de placeholder sous le dernier widget
// - On recalcule le nb de lignes de placeholder en plus à afficher sous le dernier widget à chaque add / resize / move / delete d'un widget
constructor(options) {
this.options = $.extend({
columnNumber : 1,
lineNumber : 1,
placeHolderPadding : 10
},options);
this.widgets = {};
var grid = '
';
for(var u=0 ; u
';
}
}
grid += '';
this.grid = $(grid);
this.options.element.addClass('dashboard-container');
this.options.element.css({width:'100%',height:'100%'});
this.options.element.append(this.grid);
this.widgetTemplate = $('#dashboard-widget-template').html();
this.placeHolders = $('.dashboard-placeholder');
this.placeHolders.initialWidth = this.placeHolders.eq(0).outerWidth();
this.placeHolders.initialHeight = this.placeHolders.eq(0).outerHeight();
this.placeHolders.render = function(placeholder, offsets, delay){
var delay = delay!=null ? delay : 200;
var offsets = $.extend({
rowStart: 0,
rowEnd: 0,
columnStart: 0,
columnEnd: 0
}, offsets);
var rowStart = parseInt(placeholder.attr('data-row'))+1+parseInt(offsets.rowStart);
var rowEnd = parseInt(placeholder.attr('data-row'))+1+parseInt(offsets.rowEnd);
var columnStart = parseInt(placeholder.attr('data-column'))+1+parseInt(offsets.columnStart);
var columnEnd = parseInt(placeholder.attr('data-column'))+1+parseInt(offsets.columnEnd);
setTimeout(function(){
placeholder.css({
'grid-row': rowStart+'/'+rowEnd,
'grid-column': columnStart+'/'+columnEnd,
});
}, delay);
}
this.triggers = [];
var object = this;
this.placeHolders.droppable({
tolerance: 'pointer',
classes: { "ui-droppable-hover": 'drag-over' },
over: function(event, ui){
//@TODO:
// - Gérer les overflow de la grid => ex: lacher un widget en bas de gris doit recalculer le placeholder le plus bas possible sans sortir du cadre
// - Ne pas pouvoir créer un widget dessous un widget existant
// - Ne pas pouvoir superposer des widgets (ou en faire une option)
var currentWidget = ui.draggable;
var offsets = {
rowEnd: currentWidget.attr('data-height'),
columnEnd: currentWidget.attr('data-width')
}
//On custom la grid du placeholder avec la taille du widget
object.placeHolders.render($(this), offsets, 0);
},
out: function(event, ui){
//On reset la grid du placeholder
object.placeHolders.render($(this));
},
drop: function(event, ui) {
var placeholder = $(this);
//On reset la grid du placeholder
object.placeHolders.render(placeholder);
var data = placeholder.data();
var widgetId = ui.draggable.attr('data-id');
object.widgets[widgetId].column = data.column;
object.widgets[widgetId].row = data.row;
object.renderPositions();
object.trigger('move', object.widgetToArray(object.widgets[widgetId]));
}
});
$(window).resize(function() {
object.renderPositions();
});
$(window).mousemove(function(e){
if(!object.resizing) return;
$('.dashboard-widget').css('z-index', '');
object.resizing.element.css('z-index', 1000);
var objectPosition = object.resizing.element.position();
object.resizing.element.css('width',(e.clientX - objectPosition.left)+'px');
object.resizing.element.css('height',(e.clientY - objectPosition.top - 50)+'px');
//On affiche la taille finale du widget avec de la couleur sur les placeholders pour guider le resize
object.placeHolders.removeClass('drag-over');
var width = Math.round(object.resizing.element.outerWidth() / object.placeHolders.initialWidth);
var height = Math.round(object.resizing.element.outerHeight() / object.placeHolders.initialHeight);
var placeholder = $('.dashboard-placeholder[data-row="'+parseInt(object.resizing.row)+'"][data-column="'+parseInt(object.resizing.column)+'"]');
var offsets = {
rowEnd: height,
columnEnd: width
}
object.placeHolders.render(placeholder, offsets, 0);
$(placeholder).addClass('drag-over');
});
$(window).mouseup(function(){
if(!object.resizing) return;
object.placeHolders.removeClass('drag-over');
object.grid.removeClass('bordered');
var width = Math.round(object.resizing.element.outerWidth() / object.placeHolders.initialWidth);
var height = Math.round(object.resizing.element.outerHeight() / object.placeHolders.initialHeight);
object.widgets[object.resizing.id].width = width;
object.widgets[object.resizing.id].height = height;
object.widgets[object.resizing.id].element.attr({
'data-width': width,
'data-height': height,
});
//On reset la grid du placeholder
var placeholder = $('.dashboard-placeholder[data-row="'+parseInt(object.resizing.row)+'"][data-column="'+parseInt(object.resizing.column)+'"]');
object.placeHolders.render(placeholder);
object.renderPositions();
object.trigger('resize', object.widgetToArray(object.widgets[object.resizing.id]));
object.resizing = null;
});
$('.dashboard-placeholder').click(function(){
var placeholder = $(this);
object.trigger('placeholder-click',{
element : placeholder,
row: placeholder.attr('data-row'),
column: placeholder.attr('data-column')
});
});
}
on(event,callback){
if(!this.triggers[event]) this.triggers[event] = [];
this.triggers[event].push(callback);
return this;
}
trigger(event,data){
if(!this.triggers[event]) return;
if(!data) data = {};
var abort = false;
for(var k in this.triggers[event]){
if(this.triggers[event][k](data) === true) abort = true;
}
return abort;
}
//Ajoute un ou plusieurs widgets
addWidgets(widgets){
var object = this;
for(var k in widgets){
var widgetOptions = $.extend({
id: Math.floor(Math.random() * 100) + Date.now(),
draggable: true,
resizeable: true,
removable: true,
width: 1,
height: 1,
row: 0,
column:0,
options: [],
label: 'Sans titre',
content: "Aucun contenu"
}, widgets[k]);
widgetOptions.options.push({
icon: 'fas fa-ellipsis-v',
class: 'widget-option-configure',
label: 'Configurer',
click: function(element){
object.configureWidget($(element).closest('.dashboard-widget').attr('data-id'));
}
});
widgetOptions.options.push({
icon: 'fa fa-times',
class: 'widget-option-delete',
label: 'Supprimer',
click: function(element){
object.deleteWidget($(element).closest('.dashboard-widget').attr('data-id'));
}
});
object.addWidget(widgetOptions)
}
//Gestion de la disposition des widgets sur la dashboard
this.renderPositions();
}
loadJs(files,iteration,callback){
var object = this;
var js = files[iteration];
if($('script[src="'+js+'"]').length!=0 || js==null) {
if(files.length > iteration) object.loadJs(files,iteration+1,callback);
if((files.length-1) == iteration) if(callback) callback();
return;
}
//on supprime tout autre js ayant la même base mais des versions plus vielles
var baseJs = js.replace(/\?v=.*/gm,'');
$('script[src^="'+baseJs+'?"]').remove();
var jsFile = document.createElement('script');
jsFile.setAttribute("type","text/javascript");
document.getElementsByTagName("body")[0].appendChild(jsFile);
jsFile.onload = function() {
if(files.length > iteration) object.loadJs(files,iteration+1,callback);
if((files.length-1) == iteration) if(callback) callback();
};
jsFile.src = js;
}
//Ajout d'un widget en fonction de paramètre
addWidget(widgetOptions,callback){
var object = this;
//Ajout du widget au tableau de widgets interne
object.widgets[widgetOptions.id] = widgetOptions;
//Ajout de css tiers si définits
if(widgetOptions.cssUrl && widgetOptions.cssUrl.length>0){
for (var k in widgetOptions.cssUrl) {
var url = widgetOptions.cssUrl[k];
if($('link[href="'+url+'"]').length!=0) continue;
//on supprime tout autre css ayant la même base mais des versions plus vielles
var baseCss = url.replace(/\?v=.*/gm,'');
$('link[href^="'+baseCss+'"]').remove();
var cssFile = document.createElement('link');
cssFile.setAttribute("rel","stylesheet");
cssFile.setAttribute("type","text/css");
cssFile.setAttribute("href", url);
document.getElementsByTagName("body")[0].appendChild(cssFile);
}
}
//Ajout de js tiers si définits
if(widgetOptions.jsUrl && widgetOptions.jsUrl.length>0){
object.loadJs(widgetOptions.jsUrl,0,function(){
if(callback) callback(data);
});
}
//Render de la template pour les propriétés du widget courant
object.renderContent(widgetOptions.id);
var widgetElement = $(object.widgets[widgetOptions.id].element);
object.grid.append(widgetElement);
widgetElement.draggable({
handle: '.widget-header-icon, .widget-header-title',
containment: object.grid,
"ui-draggable-dragging" : "widget-dragging",
cursorAt: {
left: 7,
top: 10
},
start: function(event, ui){
var element = $(event.currentTarget);
if(!element.hasClass('widget-draggable')) return false;
$('.dashboard-widget').css('z-index','');
element.css('z-index',1000);
},
});
widgetElement.find('.dashboard-widget-resize').mousedown(function(){
var element = $(this).closest('.dashboard-widget');
if(!element.hasClass('widget-resizeable')) return;
object.resizing = object.widgets[element.attr('data-id')];
//On affiche la taille finale du widget avec de la couleur sur les placeholders pour guider le resize
object.placeHolders.removeClass('drag-over');
var width = Math.round(object.resizing.element.outerWidth() / object.placeHolders.initialWidth);
var height = Math.round(object.resizing.element.outerHeight() / object.placeHolders.initialHeight);
var placeHolder = $('.dashboard-placeholder[data-row="'+parseInt(object.resizing.row)+'"][data-column="'+parseInt(object.resizing.column)+'"]');
var offsets = {
rowEnd: height,
columnEnd: width
}
object.placeHolders.render(placeHolder, offsets, 0);
$(placeHolder).addClass('drag-over');
object.grid.addClass('bordered');
});
object.trigger('add', object.widgetToArray(widgetOptions));
}
//Supprime un widget en fonction de son id
deleteWidget(id){
var widget = this.widgets[id];
if(this.trigger('delete',this.widgetToArray(widget))) return;
this.widgets[id].element.remove();
}
configureWidget(id){
var object = this;
$('#dashboard-modal').off('shown.bs.modal').on('shown.bs.modal', function () {
object.trigger('configure', id);
}).modal('show');
}
widgetToArray(widget){
if(!widget) return {};
return {
id: widget.id,
draggable: widget.draggable,
resizeable: widget.resizeable,
removable: widget.removable,
width: widget.width,
height: widget.height,
row: widget.row,
column: widget.column,
label: widget.label,
content: widget.content,
icon: widget.icon,
headerBackground: widget.headerBackground
};
}
//Render d'un widget en fonction de ses paramètres
//Gère le refresh du widget si l'élément est déjà existant
renderContent(id) {
var widget = this.widgets[id];
var widgetElement = widget.element!=null ? widget.element : $(Mustache.render(this.widgetTemplate, widget));
if(widget.label !== null) widgetElement.find('.widget-header-title').html(widget.label);
if(widget.icon !== null) widgetElement.find('.widget-header-icon i').attr('class', widget.icon);
if(widget.headerBackground !== null) widgetElement.find('.dashboard-widget-header').css('backgroundColor',widget.headerBackground);
if(widget.bodyBackground !== null) widgetElement.find('.dashboard-widget-content').css('backgroundColor',widget.bodyBackground);
if(widget.titleColor !== null) widgetElement.find('.widget-header-title,.widget-header-options').css('color',widget.titleColor);
if(widget.iconColor !== null) widgetElement.find('.widget-header-icon i').css('color',widget.iconColor);
if(widget.options !== null){
var tpl = '';
widgetElement.find('.widget-header-options').html('');
for(var u in widget.options){
var option = $(Mustache.render(tpl, widget.options[u]));
if(widget.options[u].click){
option.data('click',widget.options[u].click);
option.click(function(){
$(this).data('click')(this);
});
}
widgetElement.find('.widget-header-options').append(option);
}
}
widgetElement
.toggleClass('widget-draggable', widget.draggable)
.toggleClass('widget-resizeable', widget.resizeable)
.toggleClass('widget-removable', widget.removable);
if(widget.content !== null) widgetElement.find('.dashboard-widget-content').html(widget.content);
widget.element = widgetElement;
}
//Rafraichit le contenu des widgets en fonction du tableau de widgets interne
renderContents() {
for(var k in this.widgets){
var widget = this.widgets[k];
this.renderContent(widget.id);
}
}
//Resize / Replace les widgets en fonction de la taille de la fenetre et des cellules de grilles
renderPositions(){
var placeholderWidth = this.placeHolders.initialWidth;
var placeholderHeight = this.placeHolders.initialHeight;
for(var k in this.widgets){
var widget = this.widgets[k];
var cell = $('[data-row="'+widget.row+'"][data-column="'+widget.column+'"]').position();
if(!cell) continue;
widget.element.css({
width: ((widget.width * placeholderWidth)-(this.options.placeHolderPadding*2))+'px',
height: ((widget.height * placeholderHeight)-(this.options.placeHolderPadding*2))+'px',
top: (cell.top+this.options.placeHolderPadding)+'px',
left: (cell.left+this.options.placeHolderPadding)+'px'
});
}
}
}