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 firstPlaceHolder = $('.dashboard-placeholder:eq(0)'); var placeholderWidth = firstPlaceHolder.width(); var placeholderHeight = firstPlaceHolder.height(); 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' }); } } }