diff options
author | Frederic Guillot <fred@kanboard.net> | 2015-07-07 20:19:57 -0400 |
---|---|---|
committer | Frederic Guillot <fred@kanboard.net> | 2015-07-07 20:19:57 -0400 |
commit | 2f8beda6af9a3f360379ed689c2eb81833e23039 (patch) | |
tree | b7b52c1358864c6ecc89a22d01c1569c10561857 | |
parent | 4807f3e8a0ac2cca2706fb8215b762541982c682 (diff) |
Move board collapse/expand mode to server side to avoid board flickering
-rw-r--r-- | app/Controller/Board.php | 24 | ||||
-rw-r--r-- | app/Helper/Board.php | 24 | ||||
-rw-r--r-- | app/Model/UserSession.php | 24 | ||||
-rw-r--r-- | app/Template/board/task_private.php | 88 | ||||
-rw-r--r-- | app/Template/layout.php | 2 | ||||
-rw-r--r-- | app/Template/project/filters.php | 13 | ||||
-rw-r--r-- | assets/js/app.js | 15 | ||||
-rw-r--r-- | assets/js/src/board.js | 73 |
8 files changed, 133 insertions, 130 deletions
diff --git a/app/Controller/Board.php b/app/Controller/Board.php index caaa38ef..ac80a192 100644 --- a/app/Controller/Board.php +++ b/app/Controller/Board.php @@ -310,4 +310,28 @@ class Board extends Base 'recurrence_basedate_list' => $this->task->getRecurrenceBasedateList(), ))); } + + /** + * Enable collapsed mode + * + * @access public + */ + public function collapse() + { + $project_id = $this->request->getIntegerParam('project_id'); + $this->userSession->setBoardDisplayMode($project_id, true); + $this->response->redirect($this->helper->url->to('board', 'show', array('project_id' => $project_id))); + } + + /** + * Enable expanded mode + * + * @access public + */ + public function expand() + { + $project_id = $this->request->getIntegerParam('project_id'); + $this->userSession->setBoardDisplayMode($project_id, false); + $this->response->redirect($this->helper->url->to('board', 'show', array('project_id' => $project_id))); + } } diff --git a/app/Helper/Board.php b/app/Helper/Board.php new file mode 100644 index 00000000..452a3b70 --- /dev/null +++ b/app/Helper/Board.php @@ -0,0 +1,24 @@ +<?php + +namespace Helper; + +/** + * Board Helper + * + * @package helper + * @author Frederic Guillot + */ +class Board extends \Core\Base +{ + /** + * Return true if tasks are collapsed + * + * @access public + * @param integer $project_id + * @return boolean + */ + public function isCollapsed($project_id) + { + return $this->userSession->isBoardCollapsed($project_id); + } +} diff --git a/app/Model/UserSession.php b/app/Model/UserSession.php index 6de4a182..44a9c2a2 100644 --- a/app/Model/UserSession.php +++ b/app/Model/UserSession.php @@ -118,4 +118,28 @@ class UserSession extends Base { $_SESSION['filters'][$project_id] = $filters; } + + /** + * Is board collapsed or expanded + * + * @access public + * @param integer $project_id + * @return boolean + */ + public function isBoardCollapsed($project_id) + { + return ! empty($_SESSION['board_collapsed'][$project_id]) ? $_SESSION['board_collapsed'][$project_id] : false; + } + + /** + * Set board display mode + * + * @access public + * @param integer $project_id + * @param boolean $collapsed + */ + public function setBoardDisplayMode($project_id, $collapsed) + { + $_SESSION['board_collapsed'][$project_id] = $collapsed; + } } diff --git a/app/Template/board/task_private.php b/app/Template/board/task_private.php index 3f4010ea..7eaff580 100644 --- a/app/Template/board/task_private.php +++ b/app/Template/board/task_private.php @@ -10,53 +10,55 @@ <?= $this->render('board/task_menu', array('task' => $task)) ?> - <div class="task-board-collapsed" style="display: none"> - <?php if (! empty($task['assignee_username'])): ?> - <span title="<?= $this->e($task['assignee_name'] ?: $task['assignee_username']) ?>"> - <?= $this->e($this->user->getInitials($task['assignee_name'] ?: $task['assignee_username'])) ?> - </span> - - <?php endif ?> - <span class="tooltip" title="<?= $this->e($task['title']) ?>" - <?= $this->url->link($this->e($task['title']), 'task', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id']), false, 'task-board-collapsed-title') ?> - </span> - </div> + <?php if ($this->board->isCollapsed($project['id'])): ?> + <div class="task-board-collapsed"> + <?php if (! empty($task['assignee_username'])): ?> + <span title="<?= $this->e($task['assignee_name'] ?: $task['assignee_username']) ?>"> + <?= $this->e($this->user->getInitials($task['assignee_name'] ?: $task['assignee_username'])) ?> + </span> - + <?php endif ?> + <span class="tooltip" title="<?= $this->e($task['title']) ?>" + <?= $this->url->link($this->e($task['title']), 'task', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id']), false, 'task-board-collapsed-title') ?> + </span> + </div> + <?php else: ?> + <div class="task-board-expanded"> - <div class="task-board-expanded"> + <?php if ($task['reference']): ?> + <span class="task-board-reference" title="<?= t('Reference') ?>"> + (<?= $task['reference'] ?>) + </span> + <?php endif ?> - <?php if ($task['reference']): ?> - <span class="task-board-reference" title="<?= t('Reference') ?>"> - (<?= $task['reference'] ?>) - </span> - <?php endif ?> + <span class="task-board-user <?= $this->user->isCurrentUser($task['owner_id']) ? 'task-board-current-user' : '' ?>"> + <?= $this->url->link( + (! empty($task['owner_id']) ? ($task['assignee_name'] ?: $task['assignee_username']) : t('Nobody assigned')), + 'board', + 'changeAssignee', + array('task_id' => $task['id'], 'project_id' => $task['project_id']), + false, + 'task-board-popover', + t('Change assignee') + ) ?> + </span> - <span class="task-board-user <?= $this->user->isCurrentUser($task['owner_id']) ? 'task-board-current-user' : '' ?>"> - <?= $this->url->link( - (! empty($task['owner_id']) ? ($task['assignee_name'] ?: $task['assignee_username']) : t('Nobody assigned')), - 'board', - 'changeAssignee', - array('task_id' => $task['id'], 'project_id' => $task['project_id']), - false, - 'task-board-popover', - t('Change assignee') - ) ?> - </span> + <?php if ($task['is_active'] == 1): ?> + <div class="task-board-days"> + <span title="<?= t('Task age in days')?>" class="task-days-age"><?= $this->dt->age($task['date_creation']) ?></span> + <span title="<?= t('Days in this column')?>" class="task-days-incolumn"><?= $this->dt->age($task['date_moved']) ?></span> + </div> + <?php else: ?> + <div class="task-board-closed"><i class="fa fa-ban fa-fw"></i><?= t('Closed') ?></div> + <?php endif ?> - <?php if ($task['is_active'] == 1): ?> - <div class="task-board-days"> - <span title="<?= t('Task age in days')?>" class="task-days-age"><?= $this->dt->age($task['date_creation']) ?></span> - <span title="<?= t('Days in this column')?>" class="task-days-incolumn"><?= $this->dt->age($task['date_moved']) ?></span> - </div> - <?php else: ?> - <div class="task-board-closed"><i class="fa fa-ban fa-fw"></i><?= t('Closed') ?></div> - <?php endif ?> + <div class="task-board-title"> + <?= $this->url->link($this->e($task['title']), 'task', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id']), false, '', t('View this task')) ?> + </div> - <div class="task-board-title"> - <?= $this->url->link($this->e($task['title']), 'task', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id']), false, '', t('View this task')) ?> + <?= $this->render('board/task_footer', array( + 'task' => $task, + 'not_editable' => $not_editable, + )) ?> </div> - - <?= $this->render('board/task_footer', array( - 'task' => $task, - 'not_editable' => $not_editable, - )) ?> - </div> + <?php endif ?> </div> diff --git a/app/Template/layout.php b/app/Template/layout.php index d804d3d5..a9f1cbc3 100644 --- a/app/Template/layout.php +++ b/app/Template/layout.php @@ -48,7 +48,7 @@ <ul> <?php if (isset($board_selector) && ! empty($board_selector)): ?> <li> - <select id="board-selector" tabindex=="-1" data-notfound="<?= t('No results match:') ?>" data-placeholder="<?= t('Display another project') ?>" data-board-url="<?= $this->url->href('board', 'show', array('project_id' => 'PROJECT_ID')) ?>"> + <select id="board-selector" tabindex="-1" data-notfound="<?= t('No results match:') ?>" data-placeholder="<?= t('Display another project') ?>" data-board-url="<?= $this->url->href('board', 'show', array('project_id' => 'PROJECT_ID')) ?>"> <option value=""></option> <?php foreach($board_selector as $board_id => $board_name): ?> <option value="<?= $board_id ?>"><?= $this->e($board_name) ?></option> diff --git a/app/Template/project/filters.php b/app/Template/project/filters.php index 396baadf..3beb2f44 100644 --- a/app/Template/project/filters.php +++ b/app/Template/project/filters.php @@ -5,12 +5,13 @@ <ul> <?php if (isset($is_board)): ?> <li> - <span class="filter-collapse"> - <i class="fa fa-compress fa-fw"></i> <a href="#" class="filter-collapse-link" title="<?= t('Keyboard shortcut: "%s"', 's') ?>"><?= t('Collapse tasks') ?></a> - </span> - <span class="filter-expand" style="display: none"> - <i class="fa fa-expand fa-fw"></i> <a href="#" class="filter-expand-link" title="<?= t('Keyboard shortcut: "%s"', 's') ?>"><?= t('Expand tasks') ?></a> - </span> + <?php if ($this->board->isCollapsed($project['id'])): ?> + <i class="fa fa-expand fa-fw"></i> + <?= $this->url->link(t('Expand tasks'), 'board', 'expand', array('project_id' => $project['id']), false, 'board-display-mode', t('Keyboard shortcut: "%s"', 's')) ?> + <?php else: ?> + <i class="fa fa-compress fa-fw"></i> + <?= $this->url->link(t('Collapse tasks'), 'board', 'collapse', array('project_id' => $project['id']), false, 'board-display-mode', t('Keyboard shortcut: "%s"', 's')) ?> + <?php endif ?> </li> <li> <span class="filter-compact"> diff --git a/assets/js/app.js b/assets/js/app.js index d31c1737..33160e68 100644 --- a/assets/js/app.js +++ b/assets/js/app.js @@ -150,14 +150,13 @@ Mousetrap.bind("b",function(b){b.preventDefault();$("#board-selector").trigger(" ".popover",Kanboard.Popover);$("[autofocus]").each(function(b,a){$(this).focus()});$(".form-date").datepicker({showOtherMonths:!0,selectOtherMonths:!0,dateFormat:"yy-mm-dd",constrainInput:!1});$(".form-datetime").datetimepicker({controlType:"select",oneLine:!0,dateFormat:"yy-mm-dd",constrainInput:!1});$("#markdown-preview").click(Kanboard.MarkdownPreview);$("#markdown-write").click(Kanboard.MarkdownWriter);$(".auto-select").focus(function(){$(this).select()});$(".dropit-submenu").hide();$(".dropdown").not(".dropit").dropit({triggerParentEl:"span"}); $(".task-autocomplete").length&&(""==$(".opposite_task_id").val()&&$(".task-autocomplete").parent().find("input[type=submit]").attr("disabled","disabled"),$(".task-autocomplete").autocomplete({source:$(".task-autocomplete").data("search-url"),minLength:1,select:function(b,a){var c=$(".task-autocomplete").data("dst-field");$("input[name="+c+"]").val(a.item.id);$(".task-autocomplete").parent().find("input[type=submit]").removeAttr("disabled")}}));$(".tooltip").tooltip({content:function(){return'<div class="markdown">'+ $(this).attr("title")+"</div>"},position:{my:"left-20 top",at:"center bottom+9",using:function(b,a){$(this).css(b);var c=a.target.left+a.target.width/2-a.element.left-20;$("<div>").addClass("tooltip-arrow").addClass(a.vertical).addClass(1>c?"align-left":"align-right").appendTo(this)}}});Kanboard.Exists("screenshot-zone")&&Kanboard.Screenshot.Init()}}}(); -(function(){function b(a){a.preventDefault();a.stopPropagation();Kanboard.Popover(a,Kanboard.InitAfterAjax)}function a(){Mousetrap.bind("n",function(){Kanboard.OpenPopover($("#board").data("task-creation-url"),Kanboard.InitAfterAjax)});Mousetrap.bind("s",function(){"expanded"===(Kanboard.GetStorageItem(e())||"expanded")?(d(),Kanboard.SetStorageItem(e(),"collapsed")):(f(),Kanboard.SetStorageItem(e(),"expanded"))});Mousetrap.bind("c",function(){n()})}function c(){$(".filter-expand-link").click(function(a){a.preventDefault(); -f();Kanboard.SetStorageItem(e(),"expanded")});$(".filter-collapse-link").click(function(a){a.preventDefault();d();Kanboard.SetStorageItem(e(),"collapsed")});h()}function e(){return"board_stacking_"+$("#board").data("project-id")}function d(){$(".filter-collapse").hide();$(".task-board-collapsed").show();$(".filter-expand").show();$(".task-board-expanded").hide()}function f(){$(".filter-collapse").show();$(".task-board-collapsed").hide();$(".filter-expand").hide();$(".task-board-expanded").show()} -function h(){"expanded"===(Kanboard.GetStorageItem(e())||"expanded")?f():d()}function g(){$(".column").sortable({delay:300,distance:5,connectWith:".column",placeholder:"draggable-placeholder",items:".draggable-item",stop:function(a,c){k(c.item.attr("data-task-id"),c.item.parent().attr("data-column-id"),c.item.index()+1,c.item.parent().attr("data-swimlane-id"))}});$("#board").on("click",".task-board-popover",b);$("#board").on("click",".task-board",function(){window.location=$(this).data("task-url")}); -$(".task-board-tooltip").tooltip({track:!1,position:{my:"left-20 top",at:"center bottom+9",using:function(a,c){$(this).css(a);var b=c.target.left+c.target.width/2-c.element.left-20;$("<div>").addClass("tooltip-arrow").addClass(c.vertical).addClass(1>b?"align-left":"align-right").appendTo(this)}},content:function(a){if(a=$(this).attr("data-href")){var c=this;$.get(a,function p(a){$(".ui-tooltip-content:visible").html(a);a=$(".ui-tooltip:visible");a.css({top:"",left:""});a.children(".tooltip-arrow").remove(); -var b=$(c).tooltip("option","position");b.of=$(c);a.position(b);$("#tooltip-subtasks a").not(".popover").click(function(a){a.preventDefault();a.stopPropagation();$(this).hasClass("popover-subtask-restriction")?(Kanboard.OpenPopover($(this).attr("href")),$(c).tooltip("close")):$.get($(this).attr("href"),p)})});return'<i class="fa fa-refresh fa-spin fa-2x"></i>'}}}).on("mouseenter",function(){var a=this;$(this).tooltip("open");$(".ui-tooltip").on("mouseleave",function(){$(a).tooltip("close")})}).on("mouseleave focusout", -function(a){a.stopImmediatePropagation();var c=this;setTimeout(function(){$(".ui-tooltip:hover").length||$(c).tooltip("close")},100)});var a=parseInt($("#board").attr("data-check-interval"));0<a&&(m=window.setInterval(q,1E3*a))}function k(a,c,b,d){clearInterval(m);$.ajax({cache:!1,url:$("#board").attr("data-save-url"),contentType:"application/json",type:"POST",processData:!1,data:JSON.stringify({task_id:a,column_id:c,swimlane_id:d,position:b}),success:function(a){$("#board-container").remove();$("#main").append(a); -Kanboard.InitAfterAjax();g();h();l()}})}function q(){Kanboard.IsVisible()&&$.ajax({cache:!1,url:$("#board").attr("data-check-url"),statusCode:{200:function(a){$("#board-container").remove();$("#main").append(a);Kanboard.InitAfterAjax();clearInterval(m);g();h();l()}}})}function r(){jQuery(document).on("click",".filter-toggle-scrolling",function(a){a.preventDefault();n()});l()}function n(){var a=Kanboard.GetStorageItem("horizontal_scroll")||1;Kanboard.SetStorageItem("horizontal_scroll",0==a?1:0);l()} -function l(){0==Kanboard.GetStorageItem("horizontal_scroll")?($(".filter-wide").show(),$(".filter-compact").hide(),$("#board-container").addClass("board-container-compact"),$("#board th").addClass("board-column-compact")):($(".filter-wide").hide(),$(".filter-compact").show(),$("#board-container").removeClass("board-container-compact"),$("#board th").removeClass("board-column-compact"))}var m=null;jQuery(document).ready(function(){Kanboard.Exists("board")&&(g(),c(),r(),a())})})(); +(function(){function b(a){a.preventDefault();a.stopPropagation();Kanboard.Popover(a,Kanboard.InitAfterAjax)}function a(){Mousetrap.bind("n",function(){Kanboard.OpenPopover($("#board").data("task-creation-url"),Kanboard.InitAfterAjax)});Mousetrap.bind("s",function(){window.location=$(".board-display-mode").attr("href")});Mousetrap.bind("c",function(){h()})}function c(){$(".column").sortable({delay:300,distance:5,connectWith:".column",placeholder:"draggable-placeholder",items:".draggable-item",stop:function(a, +c){e(c.item.attr("data-task-id"),c.item.parent().attr("data-column-id"),c.item.index()+1,c.item.parent().attr("data-swimlane-id"))}});$("#board").on("click",".task-board-popover",b);$("#board").on("click",".task-board",function(){window.location=$(this).data("task-url")});$(".task-board-tooltip").tooltip({track:!1,position:{my:"left-20 top",at:"center bottom+9",using:function(a,c){$(this).css(a);var b=c.target.left+c.target.width/2-c.element.left-20;$("<div>").addClass("tooltip-arrow").addClass(c.vertical).addClass(1> +b?"align-left":"align-right").appendTo(this)}},content:function(a){if(a=$(this).attr("data-href")){var c=this;$.get(a,function l(a){$(".ui-tooltip-content:visible").html(a);a=$(".ui-tooltip:visible");a.css({top:"",left:""});a.children(".tooltip-arrow").remove();var b=$(c).tooltip("option","position");b.of=$(c);a.position(b);$("#tooltip-subtasks a").not(".popover").click(function(a){a.preventDefault();a.stopPropagation();$(this).hasClass("popover-subtask-restriction")?(Kanboard.OpenPopover($(this).attr("href")), +$(c).tooltip("close")):$.get($(this).attr("href"),l)})});return'<i class="fa fa-refresh fa-spin fa-2x"></i>'}}}).on("mouseenter",function(){var a=this;$(this).tooltip("open");$(".ui-tooltip").on("mouseleave",function(){$(a).tooltip("close")})}).on("mouseleave focusout",function(a){a.stopImmediatePropagation();var c=this;setTimeout(function(){$(".ui-tooltip:hover").length||$(c).tooltip("close")},100)});var a=parseInt($("#board").attr("data-check-interval"));0<a&&(k=window.setInterval(d,1E3*a))}function e(a, +b,d,e){clearInterval(k);$.ajax({cache:!1,url:$("#board").attr("data-save-url"),contentType:"application/json",type:"POST",processData:!1,data:JSON.stringify({task_id:a,column_id:b,swimlane_id:e,position:d}),success:function(a){$("#board-container").remove();$("#main").append(a);Kanboard.InitAfterAjax();c();g()}})}function d(){Kanboard.IsVisible()&&$.ajax({cache:!1,url:$("#board").attr("data-check-url"),statusCode:{200:function(a){$("#board-container").remove();$("#main").append(a);Kanboard.InitAfterAjax(); +clearInterval(k);c();g()}}})}function f(){jQuery(document).on("click",".filter-toggle-scrolling",function(a){a.preventDefault();h()});g()}function h(){var a=Kanboard.GetStorageItem("horizontal_scroll")||1;Kanboard.SetStorageItem("horizontal_scroll",0==a?1:0);g()}function g(){0==Kanboard.GetStorageItem("horizontal_scroll")?($(".filter-wide").show(),$(".filter-compact").hide(),$("#board-container").addClass("board-container-compact"),$("#board th").addClass("board-column-compact")):($(".filter-wide").hide(), +$(".filter-compact").show(),$("#board-container").removeClass("board-container-compact"),$("#board th").removeClass("board-column-compact"))}var k=null;jQuery(document).ready(function(){Kanboard.Exists("board")&&(c(),f(),a())})})(); (function(){jQuery(document).ready(function(){if(Kanboard.Exists("calendar")){var b=$("#calendar");b.fullCalendar({lang:$("body").data("js-lang"),editable:!0,eventLimit:!0,defaultView:"month",header:{left:"prev,next today",center:"title",right:"month,agendaWeek,agendaDay"},eventDrop:function(a){$.ajax({cache:!1,url:b.data("save-url"),contentType:"application/json",type:"POST",processData:!1,data:JSON.stringify({task_id:a.id,date_due:a.start.format()})})},viewRender:function(){var a=b.data("check-url"), c={start:b.fullCalendar("getView").start.format(),end:b.fullCalendar("getView").end.format()},e;for(e in c)a+="&"+e+"="+c[e];$.getJSON(a,function(a){b.fullCalendar("removeEvents");b.fullCalendar("addEventSource",a);b.fullCalendar("rerenderEvents")})}})}})})(); (function(){function b(a){return 86400<=a?Math.round(a/86400)+"d":3600<=a?Math.round(a/3600)+"h":60<=a?Math.round(a/60)+"m":a+"s"}jQuery(document).ready(function(){if(Kanboard.Exists("analytic-task-repartition")){for(var a=$("#chart").data("metrics"),c=[],e=0;e<a.length;e++)c.push([a[e].column_title,a[e].nb_tasks]);c3.generate({data:{columns:c,type:"donut"}})}else if(Kanboard.Exists("analytic-user-repartition")){a=$("#chart").data("metrics");c=[];for(e=0;e<a.length;e++)c.push([a[e].user,a[e].nb_tasks]); diff --git a/assets/js/src/board.js b/assets/js/src/board.js index 01a16d59..d86610ef 100644 --- a/assets/js/src/board.js +++ b/assets/js/src/board.js @@ -20,7 +20,7 @@ }); Mousetrap.bind("s", function() { - stack_toggle(); + window.location = $(".board-display-mode").attr("href"); }); Mousetrap.bind("c", function() { @@ -28,74 +28,6 @@ }); } - // Collapse/Expand tasks - function stack_load_events() - { - $(".filter-expand-link").click(function(e) { - e.preventDefault(); - stack_expand(); - Kanboard.SetStorageItem(stack_key(), "expanded"); - }); - - $(".filter-collapse-link").click(function(e) { - e.preventDefault(); - stack_collapse(); - Kanboard.SetStorageItem(stack_key(), "collapsed"); - }); - - stack_show(); - } - - function stack_key() - { - var projectId = $('#board').data('project-id'); - return "board_stacking_" + projectId; - } - - function stack_collapse() - { - $(".filter-collapse").hide(); - $(".task-board-collapsed").show(); - - $(".filter-expand").show(); - $(".task-board-expanded").hide(); - } - - function stack_expand() - { - $(".filter-collapse").show(); - $(".task-board-collapsed").hide(); - - $(".filter-expand").hide(); - $(".task-board-expanded").show(); - } - - function stack_toggle() - { - var state = Kanboard.GetStorageItem(stack_key()) || "expanded"; - - if (state === "expanded") { - stack_collapse(); - Kanboard.SetStorageItem(stack_key(), "collapsed"); - } - else { - stack_expand(); - Kanboard.SetStorageItem(stack_key(), "expanded"); - } - } - - function stack_show() - { - var state = Kanboard.GetStorageItem(stack_key()) || "expanded"; - - if (state === "expanded") { - stack_expand(); - } - else { - stack_collapse(); - } - } - // Setup the board function board_load_events() { @@ -243,7 +175,6 @@ $("#main").append(data); Kanboard.InitAfterAjax(); board_load_events(); - stack_show(); compactview_reload(); } }); @@ -263,7 +194,6 @@ Kanboard.InitAfterAjax(); board_unload_events(); board_load_events(); - stack_show(); compactview_reload(); } } @@ -312,7 +242,6 @@ if (Kanboard.Exists("board")) { board_load_events(); - stack_load_events(); compactview_load_events(); keyboard_shortcuts(); } |