dashboard.js 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324
  1. class Dashboard {
  2. //@TODO:
  3. // - Pouvoir indiquer que la dashboard à une taille dynamique :
  4. // - On laisse toujours 2-3 lignes de placeholder sous le dernier widget
  5. // - On recalcule le nb de lignes de placeholder en plus à afficher sous le dernier widget à chaque add / resize / move / delete d'un widget
  6. constructor(options) {
  7. this.options = $.extend({
  8. columnNumber : 1,
  9. lineNumber : 1,
  10. placeHolderPadding : 10
  11. },options);
  12. this.widgets = {};
  13. var grid = '<div class="dashboard-grid">';
  14. for(var u=0 ; u<options.lineNumber;u++){
  15. for(var i=0 ; i<options.columnNumber;i++){
  16. grid += '<div data-row="'+u+'" data-column="'+i+'" style="grid-area: '+(u+1)+" / "+(i+1)+" / "+(u+1)+" / "+(i+1)+';" class="dashboard-placeholder"><i class="fa fa-plus placeholder-add"></i></div>';
  17. }
  18. }
  19. grid += '</div>';
  20. this.grid = $(grid);
  21. this.options.element.addClass('dashboard-container');
  22. this.options.element.css({width:'100%',height:'100%'});
  23. this.options.element.append(this.grid);
  24. this.widgetTemplate = $('#dashboard-widget-template').html();
  25. this.placeHolders = $('.dashboard-placeholder');
  26. this.placeHolders.initialWidth = this.placeHolders.eq(0).outerWidth();
  27. this.placeHolders.initialHeight = this.placeHolders.eq(0).outerHeight();
  28. this.placeHolders.render = function(placeholder, offsets, delay){
  29. var delay = delay!=null ? delay : 200;
  30. var offsets = $.extend({
  31. rowStart: 0,
  32. rowEnd: 0,
  33. columnStart: 0,
  34. columnEnd: 0
  35. }, offsets);
  36. var rowStart = parseInt(placeholder.attr('data-row'))+1+parseInt(offsets.rowStart);
  37. var rowEnd = parseInt(placeholder.attr('data-row'))+1+parseInt(offsets.rowEnd);
  38. var columnStart = parseInt(placeholder.attr('data-column'))+1+parseInt(offsets.columnStart);
  39. var columnEnd = parseInt(placeholder.attr('data-column'))+1+parseInt(offsets.columnEnd);
  40. setTimeout(function(){
  41. placeholder.css({
  42. 'grid-row': rowStart+'/'+rowEnd,
  43. 'grid-column': columnStart+'/'+columnEnd,
  44. });
  45. }, delay);
  46. }
  47. this.triggers = [];
  48. var object = this;
  49. this.placeHolders.droppable({
  50. tolerance: 'pointer',
  51. classes: { "ui-droppable-hover": 'drag-over' },
  52. over: function(event, ui){
  53. //@TODO:
  54. // - 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
  55. // - Ne pas pouvoir superposer des widgets
  56. var currentWidget = ui.draggable;
  57. var offsets = {
  58. rowEnd: currentWidget.attr('data-height'),
  59. columnEnd: currentWidget.attr('data-width')
  60. }
  61. //On custom la grid du placeholder avec la taille du widget
  62. object.placeHolders.render($(this), offsets, 0);
  63. },
  64. out: function(event, ui){
  65. //On reset la grid du placeholder
  66. object.placeHolders.render($(this));
  67. },
  68. drop: function(event, ui) {
  69. var placeholder = $(this);
  70. //On reset la grid du placeholder
  71. object.placeHolders.render(placeholder);
  72. var data = placeholder.data();
  73. var widgetId = ui.draggable.attr('data-id');
  74. object.widgets[widgetId].column = data.column;
  75. object.widgets[widgetId].row = data.row;
  76. object.renderPositions();
  77. object.trigger('move', object.widgetToArray(object.widgets[widgetId]));
  78. }
  79. });
  80. $(window).resize(function() {
  81. object.renderPositions();
  82. });
  83. $(window).mousemove(function(e){
  84. if(!object.resizing) return;
  85. $('.dashboard-widget').css('z-index', '');
  86. object.resizing.element.css('z-index', 1000);
  87. var objectPosition = object.resizing.element.position();
  88. object.resizing.element.css('width',(e.clientX - objectPosition.left)+'px');
  89. object.resizing.element.css('height',(e.clientY - objectPosition.top - 50)+'px');
  90. //On affiche la taille finale du widget avec de la couleur sur les placeholders pour guider le resize
  91. object.placeHolders.removeClass('drag-over');
  92. var width = Math.round(object.resizing.element.outerWidth() / object.placeHolders.initialWidth);
  93. var height = Math.round(object.resizing.element.outerHeight() / object.placeHolders.initialHeight);
  94. var resizePlaceholder = [];
  95. for(var i=parseInt(object.resizing.column); i<parseInt(parseInt(object.resizing.column)+width); i++){
  96. for(var j=parseInt(object.resizing.row); j<parseInt(parseInt(object.resizing.row)+height); j++){
  97. resizePlaceholder.push($('.dashboard-placeholder[data-row="'+j+'"][data-column="'+i+'"]').get(0));
  98. }
  99. }
  100. $(resizePlaceholder).addClass('drag-over')
  101. });
  102. $(window).mouseup(function(){
  103. if(!object.resizing) return;
  104. object.placeHolders.removeClass('drag-over bordered');
  105. var width = Math.round(object.resizing.element.outerWidth() / object.placeHolders.initialWidth);
  106. var height = Math.round(object.resizing.element.outerHeight() / object.placeHolders.initialHeight);
  107. object.widgets[object.resizing.id].width = width;
  108. object.widgets[object.resizing.id].height = height;
  109. object.widgets[object.resizing.id].element.attr({
  110. 'data-width': width,
  111. 'data-height': height,
  112. })
  113. object.renderPositions();
  114. object.trigger('resize', object.widgetToArray(object.widgets[object.resizing.id]));
  115. object.resizing = null;
  116. });
  117. $('.dashboard-placeholder').click(function(){
  118. var placeholder = $(this);
  119. object.trigger('placeholder-click',{
  120. element : placeholder,
  121. row: placeholder.attr('data-row'),
  122. column: placeholder.attr('data-column')
  123. });
  124. });
  125. }
  126. on(event,callback){
  127. if(!this.triggers[event]) this.triggers[event] = [];
  128. this.triggers[event].push(callback);
  129. return this;
  130. }
  131. trigger(event,data){
  132. if(!this.triggers[event]) return;
  133. if(!data) data = {};
  134. var abort = false;
  135. for(var k in this.triggers[event]){
  136. if(this.triggers[event][k](data) === true) abort = true;
  137. }
  138. return abort;
  139. }
  140. //Ajoute un ou plusieurs widgets
  141. addWidgets(widgets){
  142. var object = this;
  143. for(var k in widgets){
  144. var widgetOptions = $.extend({
  145. id: Math.floor(Math.random() * 100) + Date.now(),
  146. draggable: true,
  147. resizeable: true,
  148. removable: true,
  149. width: 1,
  150. height: 1,
  151. row: 0,
  152. column:0,
  153. options: [],
  154. label: 'Sans titre',
  155. content: "Aucun contenu"
  156. }, widgets[k]);
  157. widgetOptions.options.push({
  158. icon: 'fas fa-ellipsis-v',
  159. class: 'widget-option-configure',
  160. label: 'Configurer',
  161. click: function(element){
  162. object.configureWidget($(element).closest('.dashboard-widget').attr('data-id'));
  163. }
  164. });
  165. widgetOptions.options.push({
  166. icon: 'fa fa-times',
  167. class: 'widget-option-delete',
  168. label: 'Supprimer',
  169. click: function(element){
  170. object.deleteWidget($(element).closest('.dashboard-widget').attr('data-id'));
  171. }
  172. });
  173. object.addWidget(widgetOptions)
  174. }
  175. //Gestion de la disposition des widgets sur la dashboard
  176. this.renderPositions();
  177. }
  178. //Ajout d'un widget en fonction de paramètre
  179. addWidget(widgetOptions){
  180. var object = this;
  181. //Ajout du widget au tableau de widgets interne
  182. object.widgets[widgetOptions.id] = widgetOptions;
  183. //Render de la template pour les propriétés du widget courant
  184. object.renderContent(widgetOptions.id);
  185. var widgetElement = $(object.widgets[widgetOptions.id].element);
  186. object.grid.append(widgetElement);
  187. widgetElement.draggable({
  188. handle: '.widget-header-title',
  189. containment: object.grid,
  190. "ui-draggable-dragging" : "widget-dragging",
  191. start: function(event){
  192. var element = $(event.currentTarget);
  193. if(!element.hasClass('widget-draggable')) return false;
  194. $('.dashboard-widget').css('z-index','');
  195. element.css('z-index',1000);
  196. }
  197. });
  198. widgetElement.find('.dashboard-widget-resize').mousedown(function(){
  199. var element = $(this).closest('.dashboard-widget');
  200. if(!element.hasClass('widget-resizeable'))return;
  201. object.resizing = object.widgets[element.attr('data-id')];
  202. $('.dashboard-placeholder').addClass('bordered');
  203. });
  204. object.trigger('add', object.widgetToArray(widgetOptions));
  205. }
  206. //Supprime un widget en fonction de son id
  207. deleteWidget(id){
  208. var widget = this.widgets[id];
  209. if(this.trigger('delete',this.widgetToArray(widget))) return;
  210. this.widgets[id].element.remove();
  211. }
  212. configureWidget(id){
  213. $('#dashboard-modal').modal('show');
  214. this.trigger('configure', id);
  215. }
  216. widgetToArray(widget){
  217. if(!widget) return {};
  218. return {
  219. id: widget.id,
  220. draggable: widget.draggable,
  221. resizeable: widget.resizeable,
  222. removable: widget.removable,
  223. width: widget.width,
  224. height: widget.height,
  225. row: widget.row,
  226. column: widget.column,
  227. label: widget.label,
  228. content: widget.content,
  229. icon: widget.icon,
  230. headerBackground: widget.headerBackground
  231. };
  232. }
  233. //Render d'un widget en fonction de ses paramètres
  234. //Gère le refresh du widget si l'élément est déjà existant
  235. renderContent(id) {
  236. var widget = this.widgets[id];
  237. var widgetElement = widget.element!=null ? widget.element : $(Mustache.render(this.widgetTemplate, widget));
  238. if(widget.label !== null) widgetElement.find('.widget-header-title').html(widget.label);
  239. if(widget.icon !== null) widgetElement.find('.widget-header-icon i').attr('class', widget.icon);
  240. if(widget.headerBackground !== null) widgetElement.find('.dashboard-widget-header').css('backgroundColor',widget.headerBackground);
  241. if(widget.options !== null){
  242. var tpl = '<li class="{{class}}" title="{{label}}"><i class="{{icon}}"></i></li>';
  243. widgetElement.find('.widget-header-options').html('');
  244. for(var u in widget.options){
  245. var option = $(Mustache.render(tpl, widget.options[u]));
  246. if(widget.options[u].click){
  247. option.data('click',widget.options[u].click);
  248. option.click(function(){
  249. $(this).data('click')(this);
  250. });
  251. }
  252. widgetElement.find('.widget-header-options').append(option);
  253. }
  254. }
  255. widgetElement
  256. .toggleClass('widget-draggable', widget.draggable)
  257. .toggleClass('widget-resizeable', widget.resizeable)
  258. .toggleClass('widget-removable', widget.removable);
  259. if(widget.content !== null) widgetElement.find('.dashboard-widget-content').html(widget.content);
  260. widget.element = widgetElement;
  261. }
  262. //Rafraichit le contenu des widgets en fonction du tableau de widgets interne
  263. renderContents() {
  264. for(var k in this.widgets){
  265. var widget = this.widgets[k];
  266. this.renderContent(widget.id);
  267. }
  268. }
  269. //Resize / Replace les widgets en fonction de la taille de la fenetre et des cellules de grilles
  270. renderPositions(){
  271. var placeholderWidth = this.placeHolders.initialWidth;
  272. var placeholderHeight = this.placeHolders.initialHeight;
  273. for(var k in this.widgets){
  274. var widget = this.widgets[k];
  275. var cell = $('[data-row="'+widget.row+'"][data-column="'+widget.column+'"]').position();
  276. if(!cell) continue;
  277. widget.element.css({
  278. width: ((widget.width * placeholderWidth)-(this.options.placeHolderPadding*2))+'px',
  279. height: ((widget.height * placeholderHeight)-(this.options.placeHolderPadding*2))+'px',
  280. top: (cell.top+this.options.placeHolderPadding)+'px',
  281. left: (cell.left+this.options.placeHolderPadding)+'px'
  282. });
  283. }
  284. }
  285. }