diff --git a/module/common/view/kanban.html.php b/module/common/view/kanban.html.php
index 3e96f73506..5d01a0e574 100644
--- a/module/common/view/kanban.html.php
+++ b/module/common/view/kanban.html.php
@@ -5,66 +5,57 @@
#kanbanList .panel-heading {padding: 10px;}
#kanbanList .panel-body {padding: 0 10px 10px;}
#kanbanList .kanban {min-height: 120px; overflow: visible;}
-#kanbanList .kanban-item {margin-top: 0; border-radius: 2px;}
-#kanbanList .kanban-item:hover {border: 1px solid #ccc;}
-#kanbanList .kanban-item + .kanban-item {margin-top: 10px;}
-#kanbanList .kanban-lane-items {padding: 10px; min-height: 38px;}
+#kanbanList .kanban-card {border-radius: 2px; padding: 10px;}
+#kanbanList .kanban-card:hover {border: 1px solid #ccc;}
#kanbanList .kanban-header,
-#kanbanList .kanban-lane {border-bottom: none; margin-bottom: 0; min-height: 60px;}
+#kanbanList .kanban-lane {border-bottom: none; margin-bottom: 0;}
#kanbanList .kanban-sub-lane {border-bottom: 0;}
#kanbanList .kanban-lane {margin-top: 2px; min-height: 100px;}
#kanbanList .kanban-lane.has-sub-lane {background-color: transparent;}
-#kanbanList .kanban-lane + .kanban-lane {border-top: 10px solid #fff;}
+#kanbanList .kanban-lane + .kanban-lane {margin-top: 10px;}
#kanbanList .kanban-sub-lane + .kanban-sub-lane {margin-top: 2px;}
-#kanbanList .kanban-col + .kanban-col {border-left: 2px solid #fff;}
-#kanbanList .kanban-header-col {height: 72px; padding: 20px 5px;}
-#kanbanList .kanban-header-col > .title {margin: 0; line-height: 32px; height: 32px}
-#kanbanList .kanban-header-col > .title > .text {font-weight: bold; max-width: 200px; max-width: calc(100% - 50px);}
-#kanbanList .kanban-header-col > .title > .icon, #kanbanList .kanban-header-col > .title > .count {top: -11px}
+#kanbanList .kanban-header-col > .title > .text {max-width: 200px; max-width: calc(100% - 50px);}
#kanbanList .kanban-header + .kanban-lane > .kanban-lane-name {margin-top: 0;}
#kanbanList .kanban-header {position: relative;}
#kanbanList .kanban-item.link-block {padding: 0;}
-#kanbanList .kanban-item.link-block > a {padding: 10px; display: block;}
-#kanbanList .kanban-item > .title {white-space: nowrap; overflow: hidden; text-overflow: clip;}
-#kanbanList .kanban-item.link-block > a {padding: 10px; display: block;}
-#kanbanList .kanban-item.has-progress {padding-right: 40px; position: relative;}
-#kanbanList .kanban-item.has-progress > .progress-pie {position: absolute; right: 7px; top: 7px}
-#kanbanList .kanban-item.has-left-border {border-left: 2px solid #838a9d;}
-#kanbanList .kanban-item.has-left-border.border-left-green {border-left-color: #0bd986;}
-#kanbanList .kanban-item.has-left-border.border-left-red {border-left-color: #ff5d5d;}
-#kanbanList .kanban-item.has-left-border.border-left-blue {border-left-color: #0991ff;}
+#kanbanList .kanban-item.link-block a {padding: 10px; display: block;}
+#kanbanList .kanban-card > .title {white-space: nowrap; overflow: hidden; text-overflow: clip;}
+#kanbanList .kanban-card.has-progress {padding-right: 40px; position: relative;}
+#kanbanList .kanban-card.has-progress > .progress-pie {position: absolute; right: 7px; top: 7px}
+#kanbanList .kanban-card.has-left-border {border-left: 2px solid #838a9d;}
+#kanbanList .kanban-card.has-left-border.border-left-green {border-left-color: #0bd986;}
+#kanbanList .kanban-card.has-left-border.border-left-red {border-left-color: #ff5d5d;}
+#kanbanList .kanban-card.has-left-border.border-left-blue {border-left-color: #0991ff;}
#kanbanList .no-flex .kanban-lane > .kanban-sub-lanes[data-sub-lanes-count="1"] > .kanban-sub-lane {min-height: 90px;}
#kanbanList .no-flex .kanban-lane > .kanban-sub-lanes[data-sub-lanes-count="2"] > .kanban-sub-lane {min-height: 45px;}
.kanban-affixed {padding-top: 72px;}
.kanban-affixed > .kanban-header {position: fixed!important; top: 0; background: rgba(80,80,80,.9); color: #fff; z-index: 100;}
-#kanbanList .kanban-header-col[data-type="doingProject"],
-#kanbanList .kanban-header-col[data-type="doingProject"] + .kanban-header-col[data-type="doingExecution"] {padding: 38px 10px 0;}
-#kanbanList .kanban-header-col[data-type="doingProject"]:after {content: attr(data-span-text); display: block; position: absolute; z-index: 10; left: 0; right: -100%; right: calc(-100% - 2px); top: 0; line-height: 36px; text-align: center; font-weight: bold; border-bottom: 2px solid #fff; background-color: #ededed;}
-#kanbanList .kanban-col[data-type="unclosedProduct"] .kanban-lane-items {height: 100%; display: flex; flex-direction: column; justify-content: center;}
-#kanbanList .kanban-item.kanban-item-span,
-#kanbanList .kanban-col[data-type="unclosedProduct"] .kanban-item {background-color: transparent; border: none; padding: 0; text-align: center; box-shadow: none!important;}
-#kanbanList .kanban-item.kanban-item-span:hover,
-#kanbanList .kanban-col[data-type="unclosedProduct"] .kanban-item:hover {box-shadow: none;}
-#kanbanList .kanban-item.kanban-item-span > .title,
-#kanbanList .kanban-col[data-type="unclosedProduct"] .kanban-item > .title {white-space: normal;}
+#kanbanList .kanban-col[data-type="unclosedProduct"] .kanban-item {padding: 0;}
+#kanbanList .kanban-col[data-type="unclosedProduct"] .kanban-lane-items {height: 100%; display: flex; flex-direction: column; justify-content: center; padding: 0; overflow: hidden;}
+#kanbanList .kanban-card.kanban-card-span,
+#kanbanList .kanban-col[data-type="unclosedProduct"] .kanban-card {background-color: transparent; border: none; padding: 0; text-align: center; box-shadow: none!important; margin: 0; height: auto!important;}
+#kanbanList .kanban-card.kanban-card-span:hover,
+#kanbanList .kanban-col[data-type="unclosedProduct"] .kanban-card:hover {box-shadow: none;}
+#kanbanList .kanban-card.kanban-card-span > .title,
+#kanbanList .kanban-col[data-type="unclosedProduct"] .kanban-card > .title {white-space: normal; line-height: 1;}
-#kanbanList .kanban-col[data-type="normalRelease"] .kanban-item > .title {display: flex; flex-direction: row; flex-wrap: nowrap; align-items: center; height: 38px;}
-#kanbanList .kanban-col[data-type="normalRelease"] .kanban-item > .title > .text {display: block; white-space: nowrap; text-overflow: clip; overflow: hidden;}
-#kanbanList .kanban-col[data-type="normalRelease"] .kanban-item > .title.has-icon > .text {margin-right: 5px; max-width: calc(100% - 20px);}
-#kanbanList .no-flex .kanban-col[data-type="normalRelease"] .kanban-item > .title {display: block; height: 38px;}
-#kanbanList .no-flex .kanban-col[data-type="normalRelease"] .kanban-item > .title > .text {display: inline-block;}
-#kanbanList .no-flex .kanban-col[data-type="normalRelease"] .kanban-item > .title > .icon {position: relative; top: -5px}
+#kanbanList .kanban-col[data-type="normalRelease"] .kanban-card > .title {display: flex; flex-direction: row; flex-wrap: nowrap; align-items: center;}
+#kanbanList .kanban-col[data-type="normalRelease"] .kanban-card > .title > .text {display: block; white-space: nowrap; text-overflow: clip; overflow: hidden;}
+#kanbanList .kanban-col[data-type="normalRelease"] .kanban-card > .title.has-icon > .text {margin-right: 5px; max-width: calc(100% - 20px);}
+#kanbanList .no-flex .kanban-col[data-type="normalRelease"] .kanban-card > .title {display: block; height: 38px;}
+#kanbanList .no-flex .kanban-col[data-type="normalRelease"] .kanban-card > .title > .text {display: inline-block;}
+#kanbanList .no-flex .kanban-col[data-type="normalRelease"] .kanban-card > .title > .icon {position: relative; top: -5px}
#kanbanList .kanban-affixed .kanban-header-col[data-type="doingProject"]:after {background-color: #606060;}
/* Show project and execution in one row */
#kanbanList .kanban-lane-col[data-type="doingProject"] + .kanban-lane-col {border-left: none; box-shadow: inset 2px 0 0 #fff;}
-#kanbanList .kanban-lane-col[data-type="doingProject"] > .kanban-lane-items {padding: 0; overflow: visible; max-height: none!important;}
-#kanbanList .project-row {position: relative; width: 200%; width: calc(200% + 2px);}
-#kanbanList .project-row + .project-row {border-top: 2px solid #fff;}
+#kanbanList .kanban-lane-col[data-type="doingProject"] > .kanban-lane-items {padding: 0!important; overflow: visible; max-height: none!important;}
+#kanbanList .kanban-item-span {padding: 0!important;}
+#kanbanList .project-row {position: relative; width: 200%; width: calc(200% + 2px); height: 62px!important;}
+#kanbanList .kanban-item-span + .kanban-item-span > .project-row {border-top: 2px solid #fff;}
#kanbanList .project-row > .project-col {float: left; width: 50%; padding: 10px;}
-#kanbanList .project-row > .project-col + .project-col {padding: 10px 9px 10px 11px;}
#kanbanList .project-row > .execution-item {position: absolute!important; left: 100%; top: 0}
diff --git a/module/execution/css/executionkanban.css b/module/execution/css/executionkanban.css
index 9fa5db2f43..a89240d4b9 100644
--- a/module/execution/css/executionkanban.css
+++ b/module/execution/css/executionkanban.css
@@ -3,6 +3,5 @@
#kanbanList .kanban-header-col {height: 32px!important; padding: 0!important;}
#kanbanList .kanban-header-col > .title {padding: 0 5px!important;}
#kanbanList .kanban-col[data-type="project"] .kanban-lane-items {height: 100%; display: flex; flex-direction: column; justify-content: center;}
-#kanbanList .kanban-lane-name {width: 5px; margin-right: 15px;}
#kanbanList .kanban-lane-name > .text {display: none;}
#kanbanList .kanban-item.link-block > a {cursor: move;}
diff --git a/module/execution/css/kanban.css b/module/execution/css/kanban.css
index daa4d7b0d7..cb6f061391 100644
--- a/module/execution/css/kanban.css
+++ b/module/execution/css/kanban.css
@@ -2,20 +2,19 @@
#main > .container {max-width: 1960px!important; padding: 0 10px}
.kanban + .kanban {margin-top: 15px}
-.kanban-item {position: relative;}
-.kanban-item > .title { display: block;white-space: nowrap; overflow: hidden; text-overflow: ellipsis}
-.kanban-item > .infos {position: relative; margin-top: 5px}
-.kanban-item > .infos > .info + .info {margin-left: 8px}
-.kanban-item > .infos > .info-id,
-.kanban-item > .infos > .info-deadline,
-.kanban-item > .infos > .info-estimate {font-size: 12px; position: relative; top: 2px}
-.kanban-item > .infos > .label-pri {min-width: 16px; line-height: 14px; height: 16px; padding: 0}
-.kanban-item > .infos > .label-severity {transform: scale(.75)}
-.kanban-item > .infos > .avatar {position: absolute; right: 0; top: 0}
-.kanban-item > .actions {position: absolute; top: 4px; right: 4px; opacity: 0;}
-.kanban-item:hover > .actions {opacity: 1;}
-.kanban-item > .actions > a {display: block; float: left; width: 20px; height: 20px; line-height: 20px; text-align: center; border-radius: 4px; opacity: .7;}
-.kanban-item > .actions > a:hover {background-color: rgba(0,0,0,.075); opacity: 1;}
+.kanban-card > .title {display: block; white-space: nowrap; overflow: hidden; text-overflow: ellipsis}
+.kanban-card > .infos {position: relative; margin-top: 5px}
+.kanban-card > .infos > .info + .info {margin-left: 8px}
+.kanban-card > .infos > .info-id,
+.kanban-card > .infos > .info-deadline,
+.kanban-card > .infos > .info-estimate {font-size: 12px; position: relative; top: 2px}
+.kanban-card > .infos > .label-pri {min-width: 16px; line-height: 14px; height: 16px; padding: 0}
+.kanban-card > .infos > .label-severity {transform: scale(.75)}
+.kanban-card > .infos > .avatar {position: absolute; right: 0; top: 0}
+.kanban-card > .actions {position: absolute; top: 4px; right: 4px; opacity: 0;}
+.kanban-card:hover > .actions {opacity: 1;}
+.kanban-card > .actions > a {display: block; float: left; width: 20px; height: 20px; line-height: 20px; text-align: center; border-radius: 4px; opacity: .7;}
+.kanban-card > .actions > a:hover {background-color: rgba(0,0,0,.075); opacity: 1;}
.kanban-header-col > .actions {padding-top: 22px}
.kanban-header-parent-col .kanban-header-col > .actions {padding-top: 6px; padding-right: 2px;}
@@ -35,6 +34,20 @@
#kanbans .kanban {min-height: initial;}
#kanbans .kanban-lane {min-height: 150px;}
#kanbans .kanban-affixed > .kanban-header {top: 100px;}
+#kanbans .kanban-card[data-scale-size="4"] {padding: 8px;}
+#kanbans .kanban-card[data-scale-size="3"] {padding: 3px 4px;}
+#kanbans .kanban-card[data-scale-size="3"] > .title {white-space: normal; max-height: 100%;}
+#kanbans .kanban-card[data-scale-size="2"] {padding: 3px 4px;}
+#kanbans .kanban-card[data-scale-size="2"] > .title {line-height: 18px; white-space: normal; max-height: 100%;}
+#kanbans .kanban-card[data-scale-size="2"] > .title > .infos {display: inline-block; height: 18px; vertical-align: top;}
+#kanbans .kanban-card[data-scale-size="2"] > .title > .infos > .info-pri {transform: scale(.8); vertical-align: top;}
+#kanbans .kanban-card[data-scale-size="2"] > .title > .infos > .avatar {display: inline-block; transform: scale(.8); position: relative; top: -1px; vertical-align: top; margin-right: -1px;}
+
+#kanbanScaleControl {width: 100px;}
+#kanbanScaleControl .input-group-addon {background: #fff; padding: 5px;}
+#kanbanScaleControl > .input-group-btn:first-child > .btn {border-radius: 16px 0 0 16px; border-right-color: transparent;}
+#kanbanScaleControl > .input-group-btn:last-child > .btn {border-radius: 0 16px 16px 0; border-left-color: transparent;}
+#kanbanScaleControl > .input-group-btn > .btn:hover {border-color: #b8bfce;}
#type_chosen .icon-kanban {padding-right: 10px; font-size: 15px;}
diff --git a/module/execution/js/executionkanban.js b/module/execution/js/executionkanban.js
index 373b92e045..5d75bf3c95 100644
--- a/module/execution/js/executionkanban.js
+++ b/module/execution/js/executionkanban.js
@@ -157,8 +157,8 @@ $(function()
$('#kanban').kanban(
{
- data: processKanbanData(),
- // noLaneName: true,
+ data: processKanbanData(),
+ laneNameWidth: 5,
droppable:
{
selector: '.kanban-item:not(.kanban-item-span)',
@@ -166,5 +166,6 @@ $(function()
finish: handleFinishDrop,
mouseButton: 'left'
},
+ virtualize: true
});
});
diff --git a/module/execution/js/kanban.js b/module/execution/js/kanban.js
index 8ce3c9e1f3..0f6fc866d6 100644
--- a/module/execution/js/kanban.js
+++ b/module/execution/js/kanban.js
@@ -9,9 +9,10 @@ function changeView(view)
* @param {String|{account: string, avatar: string}} user User account or user object
* @returns {string}
*/
-function renderUserAvatar(user, objectType, objectID)
+function renderUserAvatar(user, objectType, objectID, size)
{
- var $noPrivAndNoAssigned = $('
');
+ var avatarSizeClass = 'avatar-' + (size || 'sm');
+ var $noPrivAndNoAssigned = $('
');
if(objectType == 'task')
{
if(!priv.canAssignTask && !user) return $noPrivAndNoAssigned;
@@ -28,17 +29,17 @@ function renderUserAvatar(user, objectType, objectID)
var link = createLink('bug', 'assignto', 'id=' + objectID, '', true);
}
- if(!user) return $('');
+ if(!user) return $('');
if(typeof user === 'string') user = {account: user};
if(!user.avatar && window.userList && window.userList[user.account]) user = window.userList[user.account];
- var $noPrivAvatar = $('').avatar({user: user});
+ var $noPrivAvatar = $('').avatar({user: user});
if(objectType == 'task' && !priv.canAssignTask) return $noPrivAvatar;
if(objectType == 'story' && !priv.canAssignStory) return $noPrivAvatar;
if(objectType == 'bug' && !priv.canAssignBug) return $noPrivAvatar;
- return $('').avatar({user: user});
+ return $('').avatar({user: user});
}
/**
@@ -71,43 +72,56 @@ function renderDeadline(deadline)
*/
function renderStoryItem(item, $item, col)
{
- var $title = $item.find('.title');
- if(!$title.length)
- {
- $title = $(' ')
- .attr('href', $.createLink('story', 'view', 'storyID=' + item.id, '', true));
- $title.appendTo($item);
- }
- $title.attr('title', item.title).find('.text').text(item.title);
+ var scaleSize = window.kanbanScaleSize;
+ if(+$item.attr('data-scale-size') !== scaleSize) $item.empty().attr('data-scale-size', scaleSize);
- var $infos = $item.find('.infos');
- if(!$infos.length)
+ if(scaleSize <= 3)
{
- $infos = $('').appendTo($item);
- }
- $infos.html(
- [
- '#' + item.id + '',
- '' + item.pri + '',
- item.estimate ? '' + item.estimate + 'h' : '',
- ].join(''));
- $infos.append(renderUserAvatar(item.assignedTo, 'story', item.id));
-
- var $actions = $item.find('.actions');
- if(!$actions.length && item.menus.length)
- {
- $actions = $([
- ''
- ].join('')).appendTo($item);
+ var $title = $item.find('.title');
+ if(!$title.length)
+ {
+ $title = $('' + (scaleSize <= 1 ? ' ' : '') + '')
+ .attr('href', $.createLink('story', 'view', 'storyID=' + item.id, '', true));
+ $title.appendTo($item);
+ }
+ $title.attr('title', item.title).find('.text').text(item.title);
}
- $item.attr('data-type', 'story').addClass('kanban-item-story');
+ if(scaleSize <= 2)
+ {
+ var idHtml = scaleSize <= 1 ? ('#' + item.id + '') : '';
+ var priHtml = '' + item.pri + '';
+ var hoursHtml = (item.estimate && scaleSize <= 1) ? ('' + item.estimate + 'h') : '';
+ var avatarHtml = renderUserAvatar(item.assignedTo, 'story', item.id);
+ var $infos = $item.find('.infos');
+ if(!$infos.length) $infos = $('');
+ $infos.html([idHtml, priHtml, hoursHtml].join(''));
- return $item;
+ $infos[scaleSize <= 1 ? 'append' : 'prepend'](avatarHtml);
+ if(scaleSize <= 1) $infos.appendTo($item);
+ else $infos.prependTo($item.find('.title'));
+ }
+ else if(scaleSize === 4)
+ {
+ $item.html(renderUserAvatar(item.assignedTo, 'story', item.id, 'md'));
+ }
+
+ if(scaleSize <= 1)
+ {
+ var $actions = $item.find('.actions');
+ if(!$actions.length && item.menus.length)
+ {
+ $actions = $([
+ ''
+ ].join('')).appendTo($item);
+ }
+ }
+
+ return $item.attr('data-type', 'story').addClass('kanban-item-story');
}
/**
@@ -119,44 +133,58 @@ function renderStoryItem(item, $item, col)
*/
function renderBugItem(item, $item, col)
{
- var $title = $item.find('.title');
- if(!$title.length)
- {
- $title = $(' ')
- .attr('href', $.createLink('bug', 'view', 'bugID=' + item.id, '', true));
- $title.appendTo($item);
- }
- $title.attr('title', item.title).find('.text').text(item.title);
+ var scaleSize = window.kanbanScaleSize;
+ if(+$item.attr('data-scale-size') !== scaleSize) $item.empty().attr('data-scale-size', scaleSize);
- var $infos = $item.find('.infos');
- if(!$infos.length)
+ if(scaleSize <= 3)
{
- $infos = $('').appendTo($item);
- }
- $infos.html(
- [
- '#' + item.id + '',
- '',
- '' + item.pri + '',
- ].join(''));
- if(item.deadline) $infos.append(renderDeadline(item.deadline));
- $infos.append(renderUserAvatar(item.assignedTo, 'bug', item.id));
-
- var $actions = $item.find('.actions');
- if(!$actions.length && item.menus.length)
- {
- $actions = $([
- ''
- ].join('')).appendTo($item);
+ var $title = $item.find('.title');
+ if(!$title.length)
+ {
+ $title = $('' + (scaleSize <= 1 ? ' ' : '') + '')
+ .attr('href', $.createLink('bug', 'view', 'bugID=' + item.id, '', true));
+ $title.appendTo($item);
+ }
+ $title.attr('title', item.title).find('.text').text(item.title);
}
- $item.attr('data-type', 'bug').addClass('kanban-item-bug');
+ if(scaleSize <= 2)
+ {
+ var idHtml = scaleSize <= 1 ? ('#' + item.id + '') : '';
+ var severityHtml = scaleSize <= 1 ? ('') : '';
+ var priHtml = '' + item.pri + '';
+ var avatarHtml = renderUserAvatar(item.assignedTo, 'bug', item.id);
- return $item;
+ var $infos = $item.find('.infos');
+ if(!$infos.length) $infos = $('');
+ $infos.html([idHtml, severityHtml, priHtml].join(''));
+ if(item.deadline && scaleSize <= 1) $infos.append(renderDeadline(item.deadline));
+ $infos[scaleSize <= 1 ? 'append' : 'prepend'](avatarHtml);
+
+ if(scaleSize <= 1) $infos.appendTo($item);
+ else $infos.prependTo($item.find('.title'));
+ }
+ else if(scaleSize === 4)
+ {
+ $item.html(renderUserAvatar(item.assignedTo, 'bug', item.id, 'md'));
+ }
+
+ if(scaleSize <= 1)
+ {
+ var $actions = $item.find('.actions');
+ if(!$actions.length && item.menus.length)
+ {
+ $actions = $([
+ ''
+ ].join('')).appendTo($item);
+ }
+ }
+
+ return $item.attr('data-type', 'bug').addClass('kanban-item-bug');
}
/**
@@ -168,39 +196,55 @@ function renderBugItem(item, $item, col)
*/
function renderTaskItem(item, $item, col)
{
- var $title = $item.find('.title');
- if(!$title.length)
- {
- $title = $(' ')
- .attr('href', $.createLink('task', 'view', 'taskID=' + item.id, '', true));
- $title.appendTo($item);
- }
- $title.attr('title', item.name).find('.text').text(item.name);
+ var scaleSize = window.kanbanScaleSize;
+ if(+$item.attr('data-scale-size') !== scaleSize) $item.empty().attr('data-scale-size', scaleSize);
- var $infos = $item.find('.infos');
- if(!$infos.length)
+ if(scaleSize <= 3)
{
- $infos = $('').appendTo($item);
+ var $title = $item.find('.title');
+ if(!$title.length)
+ {
+ $title = $('' + (scaleSize <= 1 ? ' ' : '') + '')
+ .attr('href', $.createLink('task', 'view', 'taskID=' + item.id, '', true));
+ $title.appendTo($item);
+ }
+ $title.attr('title', item.name).find('.text').text(item.name);
}
- $infos.html(
- [
- '#' + item.id + '',
- '' + item.pri + '',
- item.estimate ? '' + item.estimate + 'h' : '',
- ].join(''));
- if(item.deadline) $infos.append(renderDeadline(item.deadline));
- $infos.append(renderUserAvatar(item.assignedTo, 'task', item.id));
- var $actions = $item.find('.actions');
- if(!$actions.length && item.menus.length)
+ if(scaleSize <= 2)
{
- $actions = $([
- ''
- ].join('')).appendTo($item);
+ var idHtml = scaleSize <= 1 ? ('#' + item.id + '') : '';
+ var priHtml = '' + item.pri + '';
+ var hoursHtml = (item.estimate && scaleSize <= 1) ? ('' + item.estimate + 'h') : '';
+ var avatarHtml = renderUserAvatar(item.assignedTo, 'task', item.id);
+
+ var $infos = $item.find('.infos');
+ if(!$infos.length) $infos = $('');
+ $infos.html([idHtml, priHtml, hoursHtml].join(''));
+ if(item.deadline && scaleSize <= 1) $infos.append(renderDeadline(item.deadline));
+ $infos[scaleSize <= 1 ? 'append' : 'prepend'](avatarHtml);
+
+ if(scaleSize <= 1) $infos.appendTo($item);
+ else $infos.prependTo($item.find('.title'));
+ }
+ else if(scaleSize === 4)
+ {
+ $item.html(renderUserAvatar(item.assignedTo, 'task', item.id, 'md'));
+ }
+
+ if(scaleSize <= 1)
+ {
+ var $actions = $item.find('.actions');
+ if(!$actions.length && item.menus.length)
+ {
+ $actions = $([
+ ''
+ ].join('')).appendTo($item);
+ }
}
$item.attr('data-type', 'task').addClass('kanban-item-task');
@@ -737,20 +781,57 @@ $.extend($.fn.kanban.Constructor.DEFAULTS,
}
});
+/** Get card height */
+function getCardHeight()
+{
+ return [59, 59, 62, 62, 47][window.kanbanScaleSize];
+}
+
+/** Change kanban scale size */
+function changeKanbanScaleSize(newScaleSize)
+{
+ var newScaleSize = Math.max(1, Math.min(4, newScaleSize));
+ if(newScaleSize === window.kanbanScaleSize) return;
+
+ window.kanbanScaleSize = newScaleSize;
+ $.zui.store.set('executionKanbanScaleSize', newScaleSize);
+ $('#kanbanScaleSize').text(newScaleSize);
+ $('#kanbanScaleControl .btn[data-type="+"]').attr('disabled', newScaleSize >= 4 ? 'disabled' : null);
+ $('#kanbanScaleControl .btn[data-type="-"]').attr('disabled', newScaleSize <= 1 ? 'disabled' : null);
+
+ $('#kanbans').children('.kanban').each(function()
+ {
+ var kanban = $(this).data('zui.kanban');
+ if(!kanban) return;
+ kanban.setOptions({cardsPerRow: newScaleSize, cardHeight: getCardHeight()});
+ });
+
+ return newScaleSize;
+}
+
/* Example code: */
$(function()
{
$.cookie('isFullScreen', 0);
+ window.kanbanScaleSize = +$.zui.store.get('executionKanbanScaleSize', 1);
+ $('#kanbanScaleSize').text(window.kanbanScaleSize);
+ $('#kanbanScaleControl .btn[data-type="+"]').attr('disabled', window.kanbanScaleSize >= 4 ? 'disabled' : null);
+ $('#kanbanScaleControl .btn[data-type="-"]').attr('disabled', window.kanbanScaleSize <= 1 ? 'disabled' : null);
+
/* Common options 用于初始化看板的通用选项 */
var commonOptions =
{
- maxColHeight: 'auto',
- minColWidth: 240,
- maxColWidth: 240,
- showCount: true,
- showZeroCount: true,
- fluidBoardWidth: true,
+ maxColHeight: 'auto',
+ minColWidth: 240,
+ maxColWidth: 240,
+ cardHeight: getCardHeight(),
+ showCount: true,
+ showZeroCount: true,
+ fluidBoardWidth: false,
+ cardsPerRow: window.kanbanScaleSize,
+ virtualize: true,
+ virtualRenderOptions: {container: '#kanbanContainer>.panel-body'},
droppable:
{
target: findDropColumns,
@@ -806,6 +887,12 @@ $(function()
$.zui.ContextMenu.show(items, items.$options || {event: event});
});
+ /* Make kanbanScaleControl works */
+ $('#kanbanScaleControl').on('click', '.btn', function()
+ {
+ changeKanbanScaleSize(window.kanbanScaleSize + ($(this).data('type') === '+' ? 1 : -1));
+ });
+
/* Resize kanban container on window resize */
resizeKanbanContainer();
$(window).on('resize', resizeKanbanContainer);
@@ -823,18 +910,20 @@ $(function()
{
location.href = createLink('execution', 'importPlanStories', 'executionID=' + executionID + '&planID=' + planID + '&productID=0&fromMethod=kanban');
}
- })
+ });
$('#type_chosen .chosen-single span').prepend('');
$('#group_chosen .chosen-single span').prepend(kanbanLang.laneGroup + ': ');
/* Ajax update kanban. */
+ var lastUpdateData;
setInterval(function()
{
$.get(createLink('execution', 'ajaxUpdateKanban', "executionID=" + executionID + "&entertime=" + entertime + "&browseType=" + browseType + "&groupBy=" + groupBy), function(data)
{
- if(data)
+ if(data && lastUpdateData !== lastUpdateData)
{
+ lastUpdateData = data;
kanbanGroup = $.parseJSON(data);
if(groupBy == 'default')
{
diff --git a/module/execution/lang/en.php b/module/execution/lang/en.php
index 73c5a6ce9d..11a7270660 100644
--- a/module/execution/lang/en.php
+++ b/module/execution/lang/en.php
@@ -395,6 +395,7 @@ $lang->execution->bugList = "Bugs";
$lang->execution->kanbanHideCols = 'Closed & Cancelled Columns';
$lang->execution->kanbanShowOption = 'Unfold';
$lang->execution->kanbanColsColor = 'Customize Column Color';
+$lang->execution->kanbanCardsUnit = 'X';
$lang->execution->kanbanViewList['all'] = 'All';
$lang->execution->kanbanViewList['story'] = "{$lang->SRCommon}";
diff --git a/module/execution/lang/zh-cn.php b/module/execution/lang/zh-cn.php
index 880679eb6d..347c474da1 100644
--- a/module/execution/lang/zh-cn.php
+++ b/module/execution/lang/zh-cn.php
@@ -396,6 +396,7 @@ $lang->execution->bugList = "Bug列表";
$lang->execution->kanbanHideCols = '看板隐藏已关闭、已取消列';
$lang->execution->kanbanShowOption = '显示折叠信息';
$lang->execution->kanbanColsColor = '看板列自定义颜色';
+$lang->execution->kanbanCardsUnit = '个';
$lang->execution->kanbanViewList['all'] = '综合看板';
$lang->execution->kanbanViewList['story'] = "{$lang->SRCommon}看板";
diff --git a/module/execution/view/kanban.html.php b/module/execution/view/kanban.html.php
index 534de79bca..13c0e6b116 100644
--- a/module/execution/view/kanban.html.php
+++ b/module/execution/view/kanban.html.php
@@ -21,6 +21,17 @@
+
+
+
+
+
+ 1execution->kanbanCardsUnit; ?>
+
+
+
+
+
createLink('task', 'export', "execution=$executionID&orderBy=$orderBy&type=kanban");
@@ -52,7 +63,7 @@
if(common::hasPriv('execution', 'printKanban')) echo '
' .html::a($this->createLink('execution', 'printKanban', "executionID=$executionID"), " " . $lang->execution->printKanban, '', "class='iframe btn btn-link' id='printKanban' title='{$lang->execution->printKanban}' data-width='500'") . '';
echo '' .html::a('javascript:fullScreen()', " " . $lang->execution->fullScreen, '', "class='btn btn-link' title='{$lang->execution->fullScreen}' data-width='500'") . '';
echo '';
-?>
+ ?>
execution = $executionID;
diff --git a/module/product/js/kanban.js b/module/product/js/kanban.js
index 60c0eff6cf..30d2879cc2 100644
--- a/module/product/js/kanban.js
+++ b/module/product/js/kanban.js
@@ -10,12 +10,28 @@ function processKanbanData(key, programsData)
/* Generate columns */
var columns = [];
+ var hasDoingProject = false;
$.each(kanbanColumns, function(_, column)
{
+ var colType = column.type;
+ if(colType === 'doingProject')
+ {
+ hasDoingProject = true;
+ columns.push(
+ {
+ kanban: kanbanId,
+ id: kanbanId + '-doing',
+ type: 'doing',
+ asParent: true,
+ name: doingText,
+ count: ''
+ });
+ }
columns.push($.extend({}, column,
{
- kanban: kanbanId,
- id: kanbanId + '-' + column.type,
+ kanban: kanbanId,
+ id: kanbanId + '-' + colType,
+ parentType: (hasDoingProject && (colType === 'doingProject' || colType === 'doingExecution')) ? 'doing' : false,
}));
});
@@ -45,7 +61,7 @@ function processKanbanData(key, programsData)
}
/* doing projects */
- if(kanbanColumns.doingProject)
+ if(hasDoingProject)
{
items.doingProject = [];
var productProjects = projectProduct[productID];
@@ -69,6 +85,7 @@ function processKanbanData(key, programsData)
/* doing execution */
items.doingExecution = [];
var productExecutions = classicExecution[productID];
+ console.log('productExecutions', productID, productExecutions);
if(productExecutions)
{
$.each(productExecutions, function(_, execution)
@@ -106,6 +123,12 @@ function processKanbanData(key, programsData)
return {id: kanbanId, columns: columns, lanes: lanes};
}
+/** Calculate column height */
+function calcColHeight(col, lane, colCards, colHeight)
+{
+ if (col.type !== 'doingProject') return colHeight;
+ return colCards.length * 62;
+}
$(function()
{
@@ -115,6 +138,6 @@ $(function()
var $kanban = $('#kanban-' + key);
if(!$kanban.length) return;
var data = processKanbanData(key, programsData);
- $kanban.kanban({data: data, noLaneName: isClassicMode});
+ $kanban.kanban({data: data, noLaneName: isClassicMode, calcColHeight: calcColHeight});
});
});
diff --git a/module/product/view/kanban.html.php b/module/product/view/kanban.html.php
index 7fc219f1b2..6e9228e03e 100644
--- a/module/product/view/kanban.html.php
+++ b/module/product/view/kanban.html.php
@@ -50,7 +50,7 @@ $userPrivs['project'] = common::hasPriv('project', 'index');
$userPrivs['execution'] = common::hasPriv('execution', 'task');
$userPrivs['release'] = common::hasPriv('release', 'view');
js::set('isClassicMode', $config->systemMode != 'new');
-js::set('kanbanColumns', $kanbanColumns);
+js::set('kanbanColumns', array_values($kanbanColumns));
js::set('userPrivs', $userPrivs);
js::set('kanbanList', $kanbanList);
js::set('programList', $programList);
diff --git a/module/program/js/kanban.js b/module/program/js/kanban.js
index c1574a2fb1..5824925373 100644
--- a/module/program/js/kanban.js
+++ b/module/program/js/kanban.js
@@ -12,10 +12,25 @@ function processKanbanData(key, programsData)
var columns = [];
$.each(kanbanColumns, function(_, column)
{
+ var colType = column.type;
+ if(colType === 'doingProject')
+ {
+ columns.push(
+ {
+ kanban: kanbanId,
+ id: kanbanId + '-doing',
+ type: 'doing',
+ asParent: true,
+ name: doingText,
+ count: ''
+ });
+ }
+
columns.push($.extend({}, column,
{
- kanban: kanbanId,
- id: kanbanId + '-' + column.type,
+ kanban: kanbanId,
+ id: kanbanId + '-' + column.type,
+ parentType: (colType === 'doingProject' || colType === 'doingExecution') ? 'doing' : false,
}));
});
@@ -107,6 +122,6 @@ $(function()
{
var $kanban = $('#kanban-' + key);
if(!$kanban.length) return;
- $kanban.kanban({data: processKanbanData(key, programsData)});
+ $kanban.kanban({data: processKanbanData(key, programsData), virtualize: true});
});
});
diff --git a/module/program/view/kanban.html.php b/module/program/view/kanban.html.php
index d055074eab..2ade64655c 100644
--- a/module/program/view/kanban.html.php
+++ b/module/program/view/kanban.html.php
@@ -43,7 +43,7 @@ $userPrivs['productplan'] = common::hasPriv('productplan', 'view');
$userPrivs['project'] = common::hasPriv('project', 'index');
$userPrivs['execution'] = common::hasPriv('execution', 'task');
$userPrivs['release'] = common::hasPriv('release', 'view');
-js::set('kanbanColumns', $kanbanColumns);
+js::set('kanbanColumns', array_values($kanbanColumns));
js::set('userPrivs', $userPrivs);
js::set('kanbanGroup', $kanbanGroup);
js::set('doingText', $lang->program->statusList['doing']);
diff --git a/module/project/js/kanban.js b/module/project/js/kanban.js
index 147c67aad2..506d68b027 100644
--- a/module/project/js/kanban.js
+++ b/module/project/js/kanban.js
@@ -12,10 +12,25 @@ function processKanbanData(key, programGroup)
var columns = [];
$.each(kanbanColumns, function(_, column)
{
+ var colType = column.type;
+ if(colType === 'doingProject')
+ {
+ columns.push(
+ {
+ kanban: kanbanId,
+ id: kanbanId + '-doing',
+ type: 'doing',
+ asParent: true,
+ name: doingText,
+ count: ''
+ });
+ }
+
columns.push($.extend({}, column,
{
- kanban: kanbanId,
- id: kanbanId + '-' + column.type,
+ kanban: kanbanId,
+ id: kanbanId + '-' + column.type,
+ parentType: (colType === 'doingProject' || colType === 'doingExecution') ? 'doing' : false,
}));
});
/* Format lanes data */
@@ -68,7 +83,6 @@ function findDropColumns($element, $root)
var col = $col.data();
var lane = $col.closest('.kanban-lane').data();
var kanbanID = $root.data('id');
- console.log('findDropColumns', {$element, $root, kanbanID, col, lane});
var kanbanRules = window.kanbanDropRules ? window.kanbanDropRules[kanbanID] : null;
if(!kanbanRules) return $root.find('.kanban-lane[data-id="' + lane.id + '"] .kanban-lane-col:not([data-type="doingExecution"],[data-type="' + col.type + '"])');
@@ -162,6 +176,13 @@ function handleFinishDrop(event)
changeCardColType(card, fromColType, toColType, kanbanID);
}
+/** Calculate column height */
+function calcColHeight(col, lane, colCards, colHeight)
+{
+ if (col.type !== 'doingProject') return colHeight;
+ return colCards.length * 62;
+}
+
$(function()
{
/* Init all kanbans */
@@ -171,8 +192,9 @@ $(function()
if(!$kanban.length) return;
$kanban.kanban(
{
- data: processKanbanData(key, programGroup),
- maxColHeight: 'auto',
+ data: processKanbanData(key, programGroup),
+ calcColHeight: calcColHeight,
+ virtualize: true,
droppable:
{
selector: '.kanban-item:not(.execution-item)',
diff --git a/module/project/view/kanban.html.php b/module/project/view/kanban.html.php
index a96ed7ee8d..f29a074a49 100644
--- a/module/project/view/kanban.html.php
+++ b/module/project/view/kanban.html.php
@@ -38,7 +38,7 @@ $kanbanColumns['closedProject'] = array('name' => $lang->project->closedProject
$userPrivs = array();
$userPrivs['project'] = common::hasPriv('project', 'index');
$userPrivs['execution'] = common::hasPriv('execution', 'task');
-js::set('kanbanColumns', $kanbanColumns);
+js::set('kanbanColumns', array_values($kanbanColumns));
js::set('userPrivs', $userPrivs);
js::set('kanbanGroup', $kanbanGroup);
js::set('latestExecutions', $latestExecutions);
diff --git a/www/js/zui/kanban/min.css b/www/js/zui/kanban/min.css
index 12ec49d6ab..c0410ace0d 100644
--- a/www/js/zui/kanban/min.css
+++ b/www/js/zui/kanban/min.css
@@ -1,6 +1,6 @@
/*!
- * ZUI: ZUI Kanban View - v1.10.0 - 2021-11-22
+ * ZUI: ZUI Kanban View - v1.10.0 - 2021-11-30
* http://openzui.com
* GitHub: https://github.com/easysoft/zui.git
* Copyright (c) 2021 cnezsoft.com; Licensed MIT
- */.kanban{min-height:300px}.kanban-header,.kanban-lane,.kanban-sub-lane{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;width:100%;flex-direction:row;background-color:#f1f3f5;border-bottom:2px solid #fff;-webkit-box-orient:horizontal;-webkit-box-direction:normal;-webkit-flex-direction:row;-ms-flex-direction:row;-webkit-flex-wrap:nowrap;-ms-flex-wrap:nowrap;flex-wrap:nowrap}.kanban-header{padding-left:20px;background-color:rgba(0,0,0,.07)}.kanban-no-lane-name .kanban-header{padding-left:0}.kanban-col{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;-webkit-box-flex:1;-webkit-flex:1;-ms-flex:1;flex:1}.not-use-flex .kanban-col{-webkit-box-flex:0;-webkit-flex:none;-ms-flex:none;flex:none}.no-flex .kanban-col{float:left}.kanban-col+.kanban-col{border-left:2px solid #fff}.kanban-header-col{position:relative;height:32px}.kanban-header-col>.title{display:block;height:32px;margin:0 32px;line-height:32px;text-align:center}.kanban-header-col>.title>.text{display:inline-block;margin:0 5px;overflow:hidden;text-overflow:'';white-space:nowrap}.kanban-header-col>.title>.count,.kanban-header-col>.title>.icon{position:relative;top:-12px}.kanban-header-col>.title>.count{color:#8b91a2}.kanban-header-col[data-id=ADD]>.title{margin:6px;opacity:.7}.kanban-header-col[data-id=ADD]>.actions{display:none}.kanban-header-col>.actions{position:absolute;top:0;right:0}.kanban-header-col>.actions>.btn{min-width:32px}.kanban-header-col>.actions>.btn>.icon{opacity:.5}.kanban-header-has-parent>.kanban-header-col{height:64px;padding:16px 0}.kanban-header-parent-col{padding:0!important}.kanban-header-parent-col>.kanban-header-col{height:32px;padding:0}.kanban-header-sub-cols{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;margin-top:-1px;margin-left:-2px;flex-direction:row;border-top:2px solid #fff;-webkit-box-orient:horizontal;-webkit-box-direction:normal;-webkit-flex-direction:row;-ms-flex-direction:row}.kanban-header-sub-cols>.kanban-col{-webkit-box-flex:1;-webkit-flex:1;-ms-flex:1;flex:1}.no-flex .kanban-header-sub-cols{display:table;table-layout:fixed}.no-flex .kanban-header-sub-cols>.kanban-col{width:50%}.kanban-col:first-child>.kanban-header-sub-cols{margin-left:0}.kanban-lane{min-height:100px}.kanban-lane+.kanban-lane{margin-top:3px}.no-flex .kanban-lane{position:relative;padding-left:20px}.kanban-lane-name{position:relative;display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;width:20px;overflow:hidden;color:#fff;text-align:center;background-color:#3dc6fd;-webkit-box-align:center;-webkit-align-items:center;-ms-flex-align:center;align-items:center;-webkit-box-pack:center;-webkit-justify-content:center;-ms-flex-pack:center;justify-content:center;-webkit-box-flex:0;-webkit-flex:none;-ms-flex:none;flex:none}.kanban-lane-name>.text{position:absolute;top:5px;right:1px;bottom:5px;display:block;overflow:hidden;line-height:20px;text-align:center;white-space:normal;-webkit-writing-mode:tb-rl;-ms-writing-mode:tb-rl;writing-mode:tb-rl;-webkit-writing-mode:vertical-rl;writing-mode:vertical-rl}.no-flex .kanban-lane-name{position:absolute;top:0;bottom:0;left:0}.kanban-sub-lanes{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;flex-direction:column;-webkit-box-flex:1;-webkit-flex:auto;-ms-flex:auto;flex:auto;-webkit-box-align:stretch;-webkit-align-items:stretch;-ms-flex-align:stretch;align-items:stretch;-webkit-box-pack:stretch;-webkit-justify-content:stretch;-ms-flex-pack:stretch;justify-content:stretch;-webkit-box-orient:vertical;-webkit-box-direction:normal;-webkit-flex-direction:column;-ms-flex-direction:column}.kanban-sub-lanes.no-sub-lane{background-color:#f1f3f5}.kanban-sub-lane{-webkit-box-flex:1;-webkit-flex:auto;-ms-flex:auto;flex:auto}.kanban-dragging .kanban-lane-col{-webkit-transition:-webkit-box-shadow .1s;-o-transition:box-shadow .1s;transition:-webkit-box-shadow .1s;transition:box-shadow .1s;transition:box-shadow .1s,-webkit-box-shadow .1s}.kanban-lane-col.drop-target{-webkit-box-shadow:inset 0 0 0 3px rgba(255,152,0,.25);box-shadow:inset 0 0 0 3px rgba(255,152,0,.25)}.kanban-lane-col.drop-to{-webkit-box-shadow:inset 0 0 1px 4px rgba(255,152,0,.75);box-shadow:inset 0 0 1px 4px rgba(255,152,0,.75)}.kanban-lane-col.drop-to .kanban-lane-actions>.btn{background-color:rgba(255,152,0,.25);border:1px dotted #ff9800}.kanban-lane-col.drop-to .kanban-lane-actions>.btn>span{opacity:0}.kanban-lane-col[data-type=EMPTY]{background-color:#fff}.kanban-lane-items{max-height:300px;padding:0 10px 10px;overflow:auto}.kanban-size-inited .kanban-lane-items{max-height:100%!important}.kanban-lane-actions{padding:10px 15px}.kanban-item{padding:8px 10px;margin-top:10px;background:#fff;border:1px solid #fff;border-radius:4px;-webkit-box-shadow:0 1px 3px 0 rgba(0,0,0,.1),0 1px 2px 0 rgba(0,0,0,.06);box-shadow:0 1px 3px 0 rgba(0,0,0,.1),0 1px 2px 0 rgba(0,0,0,.06);-webkit-transition:-webkit-box-shadow .2s,-webkit-transform .2s;-o-transition:box-shadow .2s,-o-transform .2s;transition:-webkit-box-shadow .2s,-webkit-transform .2s;transition:box-shadow .2s,transform .2s;transition:box-shadow .2s,transform .2s,-webkit-box-shadow .2s,-webkit-transform .2s,-o-transform .2s}.kanban-item.drag-from{background-color:#aaa;opacity:.2}.kanban-item:hover{border-color:rgba(0,0,0,.1);-webkit-box-shadow:0 4px 10px 0 rgba(0,0,0,.09);box-shadow:0 4px 10px 0 rgba(0,0,0,.09)}.kanban-item.drag-shadow{z-index:10;border-color:rgba(0,0,0,.2);-webkit-box-shadow:0 4px 10px 0 rgba(0,0,0,.05),0 4px 20px 0 rgba(0,0,0,.3);box-shadow:0 4px 10px 0 rgba(0,0,0,.05),0 4px 20px 0 rgba(0,0,0,.3);-webkit-transition:-webkit-box-shadow .2s,-webkit-transform .4s!important;-o-transition:box-shadow .2s,-o-transform .4s!important;transition:-webkit-box-shadow .2s,-webkit-transform .4s!important;transition:box-shadow .2s,transform .4s!important;transition:box-shadow .2s,transform .4s,-webkit-box-shadow .2s,-webkit-transform .4s,-o-transform .4s!important}.kanban-item.drag-shadow.in{-webkit-transform:scale(1.1) rotate(5deg);-ms-transform:scale(1.1) rotate(5deg);-o-transform:scale(1.1) rotate(5deg);transform:scale(1.1) rotate(5deg)}.kanban-dragging{cursor:move}
\ No newline at end of file
+ */.kanban{min-height:300px}.kanban-header{background-color:rgba(0,0,0,.07)}.use-flex .kanban-header{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;flex-direction:row;-webkit-box-orient:horizontal;-webkit-box-direction:normal;-webkit-flex-direction:row;-ms-flex-direction:row;-webkit-flex-wrap:nowrap;-ms-flex-wrap:nowrap;flex-wrap:nowrap}.kanban-col{min-width:0;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;-webkit-box-flex:1;-webkit-flex:1;-ms-flex:1;flex:1}.no-flex .kanban-col{float:left}.kanban-col+.kanban-col{border-left:2px solid #fff}.kanban-header-col{position:relative}.kanban-header-col>.title>.icon{display:inline-block}.kanban-header-col>.title>.text{display:inline-block;margin:0 5px;overflow:hidden;font-weight:700;text-overflow:clip;white-space:nowrap}.kanban-header-col>.title>.count{display:inline-block;color:#8b91a2}.kanban-affixed .kanban-header-col>.title>.count{color:#ededed}.use-flex .kanban-header-col{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;min-width:0;min-height:0;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;padding:0 30px;flex-direction:row;-webkit-box-align:center;-webkit-align-items:center;-ms-flex-align:center;align-items:center;-webkit-box-pack:center;-webkit-justify-content:center;-ms-flex-pack:center;justify-content:center;-webkit-box-orient:horizontal;-webkit-box-direction:normal;-webkit-flex-direction:row;-ms-flex-direction:row}.use-flex .kanban-header-col>.title{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;max-width:100%;flex-direction:row;-webkit-box-align:center;-webkit-align-items:center;-ms-flex-align:center;align-items:center;-webkit-box-orient:horizontal;-webkit-box-direction:normal;-webkit-flex-direction:row;-ms-flex-direction:row;-webkit-box-flex:1;-webkit-flex:auto;-ms-flex:auto;flex:auto;-webkit-box-pack:center;-webkit-justify-content:center;-ms-flex-pack:center;justify-content:center}.no-flex .kanban-header-col>.title{position:absolute;top:50%;right:30px;left:30px;margin-top:-10px;line-height:20px;text-align:center;white-space:nowrap}.kanban-header-col>.actions{position:absolute;top:0;right:0}.kanban-header-col>.actions>.btn,.kanban-header-col>.actions>a{min-width:20px}.kanban-header-col>.actions>.btn>.icon,.kanban-header-col>.actions>a>.icon{opacity:.5}.kanban-affixed .kanban-header-col>.actions>.btn,.kanban-affixed .kanban-header-col>.actions>a{color:#fff}.use-flex .kanban-header-parent-col{padding:0;flex-direction:column;-webkit-box-orient:vertical;-webkit-box-direction:normal;-webkit-flex-direction:column;-ms-flex-direction:column;-webkit-box-align:stretch;-webkit-align-items:stretch;-ms-flex-align:stretch;align-items:stretch}.use-flex .kanban-header-parent-col>.kanban-header-col,.use-flex .kanban-header-parent-col>.kanban-header-sub-cols{-webkit-box-flex:1;-webkit-flex:1;-ms-flex:1;flex:1}.kanban-header-sub-cols{margin-top:-1px;margin-left:-2px;border-top:2px solid #fff}.use-flex .kanban-header-sub-cols{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;flex-direction:row;-webkit-box-orient:horizontal;-webkit-box-direction:normal;-webkit-flex-direction:row;-ms-flex-direction:row}.use-flex .kanban-header-sub-cols>.kanban-col{-webkit-box-flex:1;-webkit-flex:1;-ms-flex:1;flex:1}.no-flex .kanban-header-sub-cols{display:table;table-layout:fixed}.no-flex .kanban-header-sub-cols>.kanban-col{width:50%}.kanban-col:first-child>.kanban-header-sub-cols{margin-left:0}.kanban-lane,.kanban-sub-lane{position:relative;background-color:#f1f3f5}.use-flex .kanban-lane,.use-flex .kanban-sub-lane{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;min-height:0;flex-direction:row;-webkit-box-orient:horizontal;-webkit-box-direction:normal;-webkit-flex-direction:row;-ms-flex-direction:row;-webkit-flex-wrap:nowrap;-ms-flex-wrap:nowrap;flex-wrap:nowrap}.kanban-lane+.kanban-lane{margin-top:2px}.kanban-lane>*{opacity:1;-webkit-transition:opacity .1s;-o-transition:opacity .1s;transition:opacity .1s}.kanban-lane.virtual-pending>*{opacity:0}.kanban-lane .has-sub-lane{background:0 0}.kanban-sub-lane{-webkit-box-flex:1;-webkit-flex:auto;-ms-flex:auto;flex:auto}.kanban-lane-name{position:relative;display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;width:20px;overflow:hidden;color:#fff;text-align:center;background-color:#3dc6fd;-webkit-box-align:center;-webkit-align-items:center;-ms-flex-align:center;align-items:center;-webkit-box-pack:center;-webkit-justify-content:center;-ms-flex-pack:center;justify-content:center;-webkit-box-flex:0;-webkit-flex:none;-ms-flex:none;flex:none}.kanban-lane-name>.text{position:absolute;top:5px;bottom:5px;display:block;overflow:hidden;line-height:20px;text-align:center;white-space:normal;-webkit-writing-mode:tb-rl;-ms-writing-mode:tb-rl;writing-mode:tb-rl;-webkit-writing-mode:vertical-rl;writing-mode:vertical-rl}.no-flex .kanban-lane-name{position:absolute;top:0;bottom:0;left:0}.use-flex .kanban-sub-lanes{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;flex-direction:column;-webkit-box-flex:1;-webkit-flex:auto;-ms-flex:auto;flex:auto;-webkit-box-align:stretch;-webkit-align-items:stretch;-ms-flex-align:stretch;align-items:stretch;-webkit-box-pack:stretch;-webkit-justify-content:stretch;-ms-flex-pack:stretch;justify-content:stretch;-webkit-box-orient:vertical;-webkit-box-direction:normal;-webkit-flex-direction:column;-ms-flex-direction:column}.kanban-sub-lanes.no-sub-lane{background-color:#f1f3f5}.kanban-dragging .kanban-lane-col{-webkit-transition:-webkit-box-shadow .1s;-o-transition:box-shadow .1s;transition:-webkit-box-shadow .1s;transition:box-shadow .1s;transition:box-shadow .1s,-webkit-box-shadow .1s}.kanban-lane-col.drop-target{-webkit-box-shadow:inset 0 0 0 3px rgba(255,152,0,.25);box-shadow:inset 0 0 0 3px rgba(255,152,0,.25)}.kanban-lane-col.drop-to{-webkit-box-shadow:inset 0 0 1px 4px rgba(255,152,0,.75);box-shadow:inset 0 0 1px 4px rgba(255,152,0,.75)}.kanban-lane-col.drop-to .kanban-lane-actions>.btn{background-color:rgba(255,152,0,.25);border:1px dotted #ff9800}.kanban-lane-col.drop-to .kanban-lane-actions>.btn>span{opacity:0}.kanban-lane-col[data-type=EMPTY]{background-color:#fff}.kanban-lane-items{height:100%;overflow:auto}.kanban-lane-actions{padding:10px 15px}.use-flex .kanban-items-grid{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-box-orient:horizontal;-webkit-box-direction:normal;-webkit-flex-flow:row wrap;-ms-flex-flow:row wrap;flex-flow:row wrap;-webkit-align-content:flex-start;-ms-flex-line-pack:start;align-content:flex-start}.kanban-card{position:relative;padding:8px 10px;background:#fff;border:1px solid #fff;border-radius:4px;-webkit-box-shadow:0 1px 3px 0 rgba(0,0,0,.1),0 1px 2px 0 rgba(0,0,0,.06);box-shadow:0 1px 3px 0 rgba(0,0,0,.1),0 1px 2px 0 rgba(0,0,0,.06);-webkit-transition:-webkit-box-shadow .2s,-webkit-transform .2s;-o-transition:box-shadow .2s,-o-transform .2s;transition:-webkit-box-shadow .2s,-webkit-transform .2s;transition:box-shadow .2s,transform .2s;transition:box-shadow .2s,transform .2s,-webkit-box-shadow .2s,-webkit-transform .2s,-o-transform .2s}.drag-from .kanban-card{background-color:#aaa;opacity:.2}.kanban-card:hover{border-color:rgba(0,0,0,.1);-webkit-box-shadow:0 4px 10px 0 rgba(0,0,0,.09);box-shadow:0 4px 10px 0 rgba(0,0,0,.09)}.drag-shadow .kanban-card{z-index:10;border-color:rgba(0,0,0,.2);-webkit-box-shadow:0 4px 10px 0 rgba(0,0,0,.05),0 4px 20px 0 rgba(0,0,0,.3);box-shadow:0 4px 10px 0 rgba(0,0,0,.05),0 4px 20px 0 rgba(0,0,0,.3);-webkit-transition:-webkit-box-shadow .2s,-webkit-transform .4s!important;-o-transition:box-shadow .2s,-o-transform .4s!important;transition:-webkit-box-shadow .2s,-webkit-transform .4s!important;transition:box-shadow .2s,transform .4s!important;transition:box-shadow .2s,transform .4s,-webkit-box-shadow .2s,-webkit-transform .4s,-o-transform .4s!important}.drag-shadow .kanban-card.in{-webkit-transform:scale(1.1) rotate(5deg);-ms-transform:scale(1.1) rotate(5deg);-o-transform:scale(1.1) rotate(5deg);transform:scale(1.1) rotate(5deg)}.kanban-dragging{cursor:move}
\ No newline at end of file
diff --git a/www/js/zui/kanban/min.js b/www/js/zui/kanban/min.js
index 3acc4ee3d8..63abd2d137 100644
--- a/www/js/zui/kanban/min.js
+++ b/www/js/zui/kanban/min.js
@@ -1,7 +1,7 @@
/*!
- * ZUI: ZUI Kanban View - v1.10.0 - 2021-11-22
+ * ZUI: ZUI Kanban View - v1.10.0 - 2021-11-30
* http://openzui.com
* GitHub: https://github.com/easysoft/zui.git
* Copyright (c) 2021 cnezsoft.com; Licensed MIT
*/
-!function(a){"use strict";var n="zui.kanban",e="object"==typeof CSS&&CSS.supports("display","flex"),t=function(d,i){var o=this;o.name=n,o.$=a(d).addClass("kanban"),o.options=i=a.extend({},t.DEFAULTS,this.$.data(),i),o.data=i.data||[],o.render(o.data);var s=function(n){if(i.onAction){var e=a(this);i.onAction(e.data("action"),e,n,o)}};if(o.$.on("click",".action",s).on("dblclick",".action-dbc",s),"auto"===i.droppable&&(i.droppable=!i.readonly),i.droppable){var r=0,l={dropOnMouseleave:!0,selector:".kanban-item",target:'.kanban-lane-col:not([data-type="EMPTY"])',drop:function(a){"function"==typeof i.droppable?i.droppable(a):i.onAction&&i.onAction("dropItem",a.element,a,o)},start:function(n){o.$.addClass("kanban-dragging"),r&&clearTimeout(r),r=setTimeout(function(){a(n.shadowElement).addClass("in"),r=0},50)},always:function(){o.$.removeClass("kanban-dragging"),r&&(clearTimeout(r),r=0)}};"object"==typeof i.droppable&&a.extend(l,i.droppable),o.$.droppable(l)}e&&i.useFlex||a(window).on("resize",function(){o.adjustSize()}),i.useFlex&&e||o.$.addClass("not-use-flex"),i.onCreate&&i.onCreate(o)};t.prototype.render=function(a){var n=this;a&&(n.data=a),n.data&&!Array.isArray(n.data)&&(n.data=[n.data]);var t=n.data||[];n.options.beforeRender&&n.options.beforeRender(n,t),n.$.toggleClass("kanban-readonly",!!n.options.readonly).toggleClass("kanban-no-lane-name",!!n.options.noLaneName),n.$.children(".kanban-board").addClass("kanban-expired");for(var d=0;d-1){var d=this.data[t];n=a.extend(d,n),this.data[t]=n}else this.data.push(n)}n.id||(n.id=a.zui.uuid());var i=this,o=i.$,s=o.children('.kanban-board[data-id="'+n.id+'"]');s.length?s.removeClass("kanban-expired"):(s=a('').appendTo(o),e||s.addClass("no-flex")),i.renderKanbanHeader(n,s),s.children(".kanban-lane").addClass("kanban-expired");for(var r=n.lanes||[],l=0;l.kanban-lane-items .kanban-item").length,b=s.find('.kanban-header-col[data-id="'+p.id+'"]'+(p.asParent?">.kanban-header-col":"")+">.title>.count");b.text(h||(i.options.showZeroCount?0:"")),i.options.onRenderCount&&i.options.onRenderCount(b,h,p,i)}i.adjustKanbanSize(n,s),i.options.onRenderKanban&&i.options.onRenderKanban(s,n,i)},t.prototype.renderKanbanHeader=function(n,t){var d=this;t=t||d.$.children('.kanban-board[data-id="'+n.id+'"]');var i=t.children(".kanban-header");i.length||(i=a('').prependTo(t),e||i.addClass("clearfix")),i.children(".kanban-col").addClass("kanban-expired");for(var o=n.columns,s={},r=!1,l=0;l.kanban-col").addClass("kanban-expired"):o=a(['"].join("")),s&&s.length?s.after(o):e.prepend(o),o.data("col",n),o.attr("data-type",n.type).attr("data-subs-count",n.subs.length);var r=o.children(".kanban-header-col");r.find(".title>.icon").attr("class","icon icon-"+(n.icon||""));var l=r.find(".title>.text").text(n.name);n.color&&l.css("color",n.color),i.options.showCount&&r.find(".title>.count").text(n.count||(i.options.showZeroCount?0:"")),i.options.onRenderHeaderCol&&i.options.onRenderHeaderCol(o,n,e,d)},t.prototype.renderHeaderCol=function(n,e,t,d,i){var o=this;if(n.parentType&&t){var s=e.children('.kanban-header-parent-col[data-id="'+t.id+'"]');s.attr("data-subs-count",t.subs.length),o.options.useFlex&&s.css("flex",t.subs.length+" 1 0%"),e=s.children(".kanban-header-sub-cols")}var r=e.children('.kanban-header-col[data-id="'+n.id+'"]'),l=d?e.children('.kanban-header-col[data-id="'+d.id+'"]'):null,c="ADD"===n.id;if(r.length)r.removeClass("kanban-expired");else{r=a(['"].join("")),c?r.children(".title").addClass("action").attr("data-action","addCol"):(r.children(".title").addClass("action-dbc").attr("data-action","editCol"),o.options.readonly||r.append(''));var p=o.options.minColWidth;p&&r.css("min-width",p);var h=o.options.maxColWidth;h&&r.css("max-width",o.options.maxColWidth)}l&&l.length?l.after(r):e.prepend(r),r.data("col",n),r.attr("data-type",n.type),r.find(".title>.icon").attr("class","icon icon-"+(n.icon||""));var b=r.find(".title>.text").text(n.name);n.color&&b.css("color",n.color),o.options.showCount&&r.find(".title>.count").text(n.count||(o.options.showZeroCount?0:"")),o.options.onRenderHeaderCol&&o.options.onRenderHeaderCol(r,n,e,i)},t.prototype.renderLane=function(n,t,d,i,o){var s=this;d=d||s.$.children('.kanban-board[data-id="'+n.kanban+'"]');var r=d.children('.kanban-lane[data-id="'+n.id+'"]');if(r.length?r.removeClass("kanban-expired"):(r=a('').appendTo(d),e||r.addClass("clearfix")),r.attr("data-index",o).data("lane",n),!s.options.noLaneName){var l=r.children('.kanban-lane-name[data-id="'+n.id+'"]');l.length||(l=a('').appendTo(r)),l.empty().attr("title",n.name).append(a('').text(n.name)),n.color&&l.css("background-color",n.color),s.options.onRenderLaneName&&s.options.onRenderLaneName(l,n,d,t,i)}if(r.children(".kanban-col,.kanban-sub-lanes").addClass("kanban-expired"),r.toggleClass("has-sub-lane",!!n.subLanes),n.subLanes)s.renderSubLanes(n,t,r,i);else{var c=n.items||n.cards;c&&s.renderLaneItems(t,c,r,n,i)}s.options.showAddCol&&s.renderLaneCol({id:"EMPTY",type:"EMPTY"},r),r.children(".kanban-expired").remove()},t.prototype.renderSubLanes=function(n,e,t,d){var i=this,o=t.children('.kanban-sub-lanes[data-id="'+n.id+'"]');o.length?o.removeClass("kanban-expired"):o=a('').appendTo(t),o.children(".kanban-sub-lane").addClass("kanban-expired");for(var s=0;s').appendTo(d),e||s.addClass("clearfix")),s.attr("data-index",o).data("lane",n),s.children(".kanban-col").addClass("kanban-expired");var r=n.items||n.cards;r&&this.renderLaneItems(t,r,s,n,i),s.children(".kanban-expired").remove()},t.prototype.renderLaneItems=function(a,n,e,t,d){for(var i=this,o=null,s=0;s'),"EMPTY"!==n.id&&(i.append(''),d.options.readonly||i.append(['','","
"].join("")));var s=d.options;s.minColWidth&&i.css("min-width",s.minColWidth),s.maxColWidth&&i.css("max-width",s.maxColWidth),s.maxColHeight&&i.find(".kanban-lane-items").css("max-height",s.maxColHeight),s.laneItemsClass&&i.find(".kanban-lane-items").addClass(s.laneItemsClass),s.laneColClass&&i.addClass(s.laneColClass)}if(o&&o.length)o.after(i);else{var r=e.children(".kanban-lane-name").first();r.length?r.after(i):e.prepend(i)}return i.attr({"data-parent":n.parentType?n.parentType:null,"data-type":n.type}),i},t.prototype.renderLaneItem=function(n,e,t,d,i){var o=e.children('.kanban-item[data-id="'+n.id+'"]');if(o.length?o.removeClass("kanban-expired"):o=a('').appendTo(e),o.data("item",n),this.options.itemRender)this.options.itemRender(n,o,t,d,i);else{var s=o.find(".title");s.length||(s=a('').appendTo(o)),s.text(n.name||n.title)}return o},t.prototype.adjustKanbanSize=function(n,t){for(var d=this,i=n.columns,o=d.options.noLaneName?0:d.options.laneNameWidth,s=d.options.showAddCol?1:0,r=0;r0&&(t.children(".kanban-lane").each(function(){var n=a(this),e=n.height();n.children(".kanban-col,.kanban-sub-lanes").css("min-height",e)}),t.find(".kanban-lane:not(.has-sub-lane),.kanban-sub-lane").each(function(){var n=a(this),e=n.height(),t=n.children(".kanban-lane-col").css("height","auto");t.each(function(){e=Math.max(e,a(this).outerHeight())}),t.css("height",e)})),t.addClass("kanban-size-inited")}},t.prototype.adjustSize=function(){for(var a=0;a=0&&t.top>=0&&t.left+t.width<=d&&t.top+t.height<=r;var i=t.top<=r&&t.top+t.height>=0,o=t.left<=d&&t.left+t.width>=0;return i&&o}var n="zui.virtualRender",t=function(e,r){"function"==typeof r&&(r={render:r});var d=this;d.name=n,d.$=a(e),d.options=r=a.extend({},t.DEFAULTS,this.$.data(),r),d.rendered=!1;var i=r.container;"function"==typeof i&&(i=i(d));var o=a(i?i:window);d.tryRender()||(d.$container=o,d.scrollListener=d.tryRender.bind(d),r.pendingClass&&d.$.addClass(r.pendingClass),o.on("scroll",d.scrollListener))};t.prototype.tryRender=function(){var n=this;return!(n.rendered||!e(n.$))&&(n.renderTaskID&&a.zui.clearAsap(n.renderTaskID),n.renderTaskID=a.zui.asap(function(){n.renderTaskID=null;var a=n.options.render(n.$);a!==!1&&(n.rendered=!0,n.destroy())},n.options.delay),!0)},t.prototype.destroy=function(){var e=this;e.renderTaskID&&a.zui.clearAsap(e.renderTaskID),e.scrollListener&&(e.$container.off("scroll",e.scrollListener),e.scrollListener=null);var t=e.options.pendingClass;t&&e.$.removeClass(t),e.$.removeData(n)},t.DEFAULTS={pendingClass:"virtual-pending"},a.fn.virtualRender=function(e){return this.each(function(){var r=a(this),d=r.data(n);if(d){if("string"==typeof e)return d[e]();d.destroy()}r.data(n,d=new t(this,e))})},a.zui.isElementInViewport=e}(jQuery),function(a){"use strict";var e="zui.kanban",n="object"==typeof CSS&&CSS.supports("display","flex"),t=function(n,r){var d=(a.zui.uuid(),this);if(d.name=e,d.$=a(n).addClass("kanban"),r=d.setOptions(a.extend({},t.DEFAULTS,this.$.data(),r)),r.onAction){var i=function(e){var n=a(this);r.onAction(n.data("action"),n,e,d)};d.$.on("click",".action",i).on("dblclick",".action-dbc",i)}if("auto"===r.droppable&&(r.droppable=!r.readonly),r.droppable){var o=0,s={dropOnMouseleave:!0,selector:".kanban-item",target:'.kanban-lane-col:not([data-type="EMPTY"])',drop:function(a){"function"==typeof r.droppable?r.droppable(a):r.onAction&&r.onAction("dropItem",a.element,a,d)},start:function(e){d.$.addClass("kanban-dragging"),o&&clearTimeout(o),o=setTimeout(function(){a(e.shadowElement).addClass("in"),o=0},50)},always:function(){d.$.removeClass("kanban-dragging"),o&&(clearTimeout(o),o=0)}};"object"==typeof r.droppable&&a.extend(s,r.droppable),d.$.droppable(s)}if(!r.useFlex){var l=0;a(window).on("resize",function(){l&&a.zui.clearAsap(l),l=a.zui.asap(function(){l=0,d.adjustSize()})})}r.onCreate&&r.onCreate(d)};t.prototype.setOptions=function(e){var t=this,r=a.extend({},t.options,e);t.options=r,r.useFlex&&!n&&(r.useFlex=!1),t.$.toggleClass("no-flex",!r.useFlex).toggleClass("use-flex",!!r.useFlex);var d=!!a.fn.virtualRender&&r.virtualize;return d&&("object"!=typeof d&&(d={lane:!0}),t.virtualize=a.extend({},d)),t.data=r.data||[],t.render(t.data),r},t.prototype.render=function(a){var e=this;a&&(e.data=a),e.data&&!Array.isArray(e.data)&&(e.data=[e.data]);var t=e.data||[];e.options.beforeRender&&e.options.beforeRender(e,t),e.$.toggleClass("kanban-readonly",!!e.options.readonly).toggleClass("kanban-no-lane-name",!!e.options.noLaneName),e.$.children(".kanban-board").addClass("kanban-expired");for(var r=0;r-1){var r=n.data[t];e=a.extend(r,e),n.data[t]=e}else n.data.push(e)}e.id||(e.id=a.zui.uuid());var d=e.id,i=n.options,o=n.$,s=o.children('.kanban-board[data-id="'+d+'"]');s.length?s.removeClass("kanban-expired"):s=a('').appendTo(o);for(var l=e.columns,c=i.noLaneName?0:i.laneNameWidth,p=0,u={},h=!1,b=0;b0&&i.subLaneSpace&&($.$height+=i.subLaneSpace)}}else for(var T=$.items||$.cards||{},R=0;R