Move board collapse/expand mode to server side to avoid board flickering

This commit is contained in:
Frederic Guillot 2015-07-07 20:19:57 -04:00
parent 4807f3e8a0
commit 2f8beda6af
8 changed files with 136 additions and 133 deletions

View File

@ -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)));
}
}

24
app/Helper/Board.php Normal file
View File

@ -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);
}
}

View File

@ -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;
}
}

View File

@ -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>
<div class="task-board-expanded">
<?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>
<?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>
<?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-closed"><i class="fa fa-ban fa-fw"></i><?= t('Closed') ?></div>
<?php endif ?>
<?php else: ?>
<div class="task-board-expanded">
<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')) ?>
<?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>
<?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>
<?= $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>

View File

@ -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>

View File

@ -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">

View File

@ -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]);

View File

@ -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();
}