2232 lines
43 KiB
JavaScript
2232 lines
43 KiB
JavaScript
;(function() {
|
|
|
|
"use strict";
|
|
|
|
BX.namespace("BX.Kanban");
|
|
|
|
/**
|
|
*
|
|
* @param {object} options
|
|
* @param {string|number} options.id
|
|
* @param {string} [options.name]
|
|
* @param {string} [options.color]
|
|
* @param {object} [options.data]
|
|
* @param {number} [options.total]
|
|
* @constructor
|
|
*/
|
|
BX.Kanban.Column = function(options)
|
|
{
|
|
options = options || {};
|
|
if (!BX.Kanban.Utils.isValidId(options.id))
|
|
{
|
|
throw new Error("BX.Kanban.Column: 'id' parameter is not valid.")
|
|
}
|
|
|
|
this.id = options.id;
|
|
this.name = null;
|
|
this.color = null;
|
|
this.data = Object.create(null);
|
|
this.total = null;
|
|
this.isTotalFrozen = false;
|
|
this.animate = options.animate || null;
|
|
|
|
this.canEdit = null;
|
|
this.canSort = null;
|
|
this.canSortItems = null;
|
|
this.canRemove = null;
|
|
this.canAddItem = null;
|
|
|
|
this.droppable = true;
|
|
|
|
this.setOptions(options);
|
|
|
|
/**@var {BX.Kanban.Item[]} **/
|
|
this.items = [];
|
|
/**@var {BX.Kanban.DraftItem} **/
|
|
this.draftItem = null;
|
|
|
|
/** @var {BX.Kanban.Grid} **/
|
|
this.grid = null;
|
|
|
|
this.selectedItems = [];
|
|
|
|
this.page = 1;
|
|
|
|
this.layout = {
|
|
container: null,
|
|
items: null,
|
|
dragTarget: null,
|
|
title: null,
|
|
subTitle: null,
|
|
subTitleAddButton: null,
|
|
subTitleAddButtonText: null,
|
|
subTitleAddButtonTextWrapper: null,
|
|
total: null,
|
|
name: null,
|
|
titleArrow: null,
|
|
color: null,
|
|
editForm: null,
|
|
fillColorButton: null,
|
|
titleTextBox: null,
|
|
addColumnButton: null,
|
|
addColumnButtonAfter: null,
|
|
addColumnButtonBefore: null,
|
|
editButton: null,
|
|
removeButton: null,
|
|
ahaItem: null
|
|
};
|
|
|
|
this.rectArea = null;
|
|
|
|
this.dragColumnOffset = null;
|
|
this.dragColumnIndex = null;
|
|
this.dragTargetColumn = null;
|
|
|
|
this.confirmDialog = null;
|
|
this.textBoxTimeout = null;
|
|
this.colorChanged = false;
|
|
this.hasBeenEdt = null;
|
|
this.addItemTitleText = null;
|
|
|
|
this.pagination = new BX.Kanban.Pagination(this);
|
|
|
|
this.handleScrollWithThrottle = BX.Runtime.throttle(this.handleScroll, 100, this);
|
|
};
|
|
|
|
BX.Kanban.Column.DEFAULT_COLOR = "ace9fb";
|
|
|
|
BX.Kanban.Column.prototype =
|
|
{
|
|
/**
|
|
*
|
|
* @returns {number|string}
|
|
*/
|
|
getId: function()
|
|
{
|
|
return this.id;
|
|
},
|
|
|
|
/**
|
|
*
|
|
* @param {object} options
|
|
* @param {string} [options.name]
|
|
* @param {string} [options.color]
|
|
* @param {object} [options.data]
|
|
* @param {number} [options.total]
|
|
*/
|
|
setOptions: function(options)
|
|
{
|
|
if (!options)
|
|
{
|
|
return;
|
|
}
|
|
|
|
this.setName(options.name);
|
|
this.setTotal(options.total);
|
|
this.setColor(options.color);
|
|
this.setData(options.data);
|
|
|
|
var boolOptions = ["canEdit", "canSort", "canRemove", "canAddItem", "droppable", "canSortItems",];
|
|
boolOptions.forEach(function(boolOption) {
|
|
if (BX.type.isBoolean(options[boolOption]))
|
|
{
|
|
this[boolOption] = options[boolOption];
|
|
}
|
|
}, this);
|
|
},
|
|
|
|
setColor: function(color)
|
|
{
|
|
if (BX.Kanban.Utils.isValidColor(color))
|
|
{
|
|
this.color = color.toLowerCase();
|
|
}
|
|
},
|
|
|
|
getColor: function()
|
|
{
|
|
return this.color !== null ? this.color : BX.Kanban.Column.DEFAULT_COLOR;
|
|
},
|
|
|
|
/**
|
|
* @param {BX.Kanban.Grid} grid
|
|
* @internal
|
|
*/
|
|
setGrid: function(grid)
|
|
{
|
|
this.grid = grid;
|
|
BX.onCustomEvent(this, "Kanban.Column:onAddedToGrid", [this]);
|
|
},
|
|
|
|
/**
|
|
* @returns {BX.Kanban.Grid}
|
|
*/
|
|
getGrid: function()
|
|
{
|
|
return this.grid;
|
|
},
|
|
|
|
/**
|
|
*
|
|
* @returns {BX.Kanban.Pagination}
|
|
*/
|
|
getPagination: function()
|
|
{
|
|
return this.pagination;
|
|
},
|
|
|
|
addSelectedItems: function()
|
|
{
|
|
|
|
},
|
|
|
|
/**
|
|
*
|
|
* @param {BX.Kanban.Item} item
|
|
* @param {BX.Kanban.Item} [beforeItem]
|
|
* @internal
|
|
*/
|
|
addItem: function(item, beforeItem)
|
|
{
|
|
if (!(item instanceof BX.Kanban.Item))
|
|
{
|
|
throw new Error("item must be an instance of BX.Kanban.Item");
|
|
}
|
|
|
|
item.setColumnId(this.getId());
|
|
//? setGrid
|
|
var index = BX.util.array_search(beforeItem, this.items);
|
|
if (index >= 0)
|
|
{
|
|
this.items.splice(index, 0, item);
|
|
}
|
|
else
|
|
{
|
|
this.items.push(item);
|
|
}
|
|
|
|
if (item.isCountable())
|
|
{
|
|
this.incrementTotal();
|
|
}
|
|
|
|
if (this.getGrid().isRendered())
|
|
{
|
|
this.render();
|
|
}
|
|
},
|
|
|
|
addItems: function(items, startBeforeItem)
|
|
{
|
|
this.selectedItems = items;
|
|
|
|
for (var itemId in this.selectedItems)
|
|
{
|
|
var item = this.selectedItems[itemId];
|
|
item.setColumnId(this.getId());
|
|
|
|
var index = BX.util.array_search(startBeforeItem, this.items);
|
|
|
|
if (index >= 0)
|
|
{
|
|
this.items.splice(index, 0, item);
|
|
}
|
|
else
|
|
{
|
|
this.items.push(item);
|
|
}
|
|
|
|
if (item.isCountable())
|
|
{
|
|
this.incrementTotal();
|
|
}
|
|
}
|
|
|
|
if (this.getGrid().isRendered())
|
|
{
|
|
this.render();
|
|
this.getGrid().cleanSelectedItems();
|
|
this.getGrid().adjustMultiSelectMode();
|
|
}
|
|
},
|
|
|
|
/**
|
|
*
|
|
* @returns {BX.Kanban.Item[]}
|
|
*/
|
|
getItems: function()
|
|
{
|
|
return this.items;
|
|
},
|
|
|
|
getItemsCount: function()
|
|
{
|
|
return this.items.reduce(function(count, /*BX.Kanban.Item*/ item) {
|
|
return item.isCountable() && item.isVisible() ? count + 1 : count;
|
|
}, 0);
|
|
},
|
|
|
|
/**
|
|
*
|
|
* @param {boolean} [onlyVisible=true]
|
|
* @returns {BX.Kanban.Item|null}
|
|
*/
|
|
getFirstItem: function(onlyVisible)
|
|
{
|
|
var items = this.getItems();
|
|
|
|
for (var i = 0; i < items.length; i++)
|
|
{
|
|
var item = items[i];
|
|
|
|
if (item.isVisible() || onlyVisible === false)
|
|
{
|
|
return item;
|
|
}
|
|
}
|
|
|
|
return null;
|
|
},
|
|
|
|
/**
|
|
*
|
|
* @param {boolean} [onlyVisible=true]
|
|
* @returns {BX.Kanban.Item|null}
|
|
*/
|
|
getLastItem: function(onlyVisible)
|
|
{
|
|
var items = this.getItems();
|
|
|
|
for (var i = items.length - 1; i >= 0; i--)
|
|
{
|
|
var item = items[i];
|
|
if (item.isVisible() || onlyVisible === false)
|
|
{
|
|
return item;
|
|
}
|
|
}
|
|
|
|
return null;
|
|
},
|
|
|
|
/**
|
|
*
|
|
* @param {BX.Kanban.Item|string|number} currentItem
|
|
* @param {boolean} [onlyVisible=true]
|
|
* @returns {BX.Kanban.Item|null}
|
|
*/
|
|
getNextItemSibling: function(currentItem, onlyVisible)
|
|
{
|
|
currentItem = this.getGrid().getItem(currentItem);
|
|
|
|
var items = this.getItems();
|
|
var itemIndex = BX.util.array_search(currentItem, items);
|
|
|
|
if (itemIndex === -1 || !items[itemIndex + 1])
|
|
{
|
|
return null;
|
|
}
|
|
|
|
for (var i = itemIndex + 1; i < items.length; i++)
|
|
{
|
|
var item = items[i];
|
|
|
|
if (item.isVisible() || onlyVisible === false)
|
|
{
|
|
return item;
|
|
}
|
|
}
|
|
|
|
return null;
|
|
},
|
|
|
|
/**
|
|
*
|
|
* @param {BX.Kanban.Item|string|number} currentItem
|
|
* @param {boolean} [onlyVisible=true]
|
|
* @returns {BX.Kanban.Item|null}
|
|
*/
|
|
getPreviousItemSibling: function(currentItem, onlyVisible)
|
|
{
|
|
currentItem = this.getGrid().getItem(currentItem);
|
|
|
|
var items = this.getItems();
|
|
var itemIndex = BX.util.array_search(currentItem, items);
|
|
|
|
if (itemIndex === -1 || !items[itemIndex - 1])
|
|
{
|
|
return null;
|
|
}
|
|
|
|
for (var i = itemIndex - 1; i >= 0; i--)
|
|
{
|
|
var item = items[i];
|
|
if (item.isVisible() || onlyVisible === false)
|
|
{
|
|
return item;
|
|
}
|
|
}
|
|
|
|
return null;
|
|
},
|
|
|
|
/**
|
|
*
|
|
* @param {BX.Kanban.Item} itemToRemove
|
|
*/
|
|
removeItem: function(itemToRemove)
|
|
{
|
|
var found = false;
|
|
this.items = this.items.filter(function(item) {
|
|
|
|
if (item === itemToRemove)
|
|
{
|
|
found = true;
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
});
|
|
|
|
if (found)
|
|
{
|
|
if (itemToRemove.isCountable() && itemToRemove.isVisible())
|
|
{
|
|
this.decrementTotal();
|
|
}
|
|
|
|
if (this.getGrid().isRendered())
|
|
{
|
|
this.render();
|
|
}
|
|
}
|
|
},
|
|
|
|
removeSelectedItems: function(itemsToRemove)
|
|
{
|
|
var found = false;
|
|
for(var itemId in itemsToRemove)
|
|
{
|
|
var itemToRemove = itemsToRemove[itemId];
|
|
|
|
this.items = this.items.filter(function(item) {
|
|
|
|
if (item === itemToRemove)
|
|
{
|
|
found = true;
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
});
|
|
|
|
if (found)
|
|
{
|
|
if (itemToRemove.isCountable() && itemToRemove.isVisible())
|
|
{
|
|
this.decrementTotal();
|
|
}
|
|
}
|
|
}
|
|
|
|
if (this.getGrid().isRendered())
|
|
{
|
|
this.render();
|
|
}
|
|
},
|
|
|
|
removeItems: function()
|
|
{
|
|
this.items = [];
|
|
this.total = null;
|
|
BX.cleanNode(this.layout.items);
|
|
this.render();
|
|
},
|
|
|
|
setName: function(name)
|
|
{
|
|
if (BX.type.isNotEmptyString(name))
|
|
{
|
|
this.name = name;
|
|
}
|
|
},
|
|
|
|
getName: function()
|
|
{
|
|
return this.name;
|
|
},
|
|
|
|
getData: function()
|
|
{
|
|
return this.data;
|
|
},
|
|
|
|
setData: function(data)
|
|
{
|
|
if (BX.type.isPlainObject(data))
|
|
{
|
|
this.data = data;
|
|
}
|
|
},
|
|
|
|
/**
|
|
*
|
|
* @returns {object}
|
|
*/
|
|
getGridData: function()
|
|
{
|
|
return this.getGrid().getData();
|
|
},
|
|
|
|
isEditable: function()
|
|
{
|
|
return this.canEdit !== null ? this.canEdit : this.getGrid().canEditColumns();
|
|
},
|
|
|
|
isSortable: function()
|
|
{
|
|
return this.canSort !== null ? this.canSort : this.getGrid().canSortColumns();
|
|
},
|
|
|
|
isRemovable: function()
|
|
{
|
|
return this.canRemove !== null ? this.canRemove : this.getGrid().canRemoveColumns();
|
|
},
|
|
|
|
canAddItems: function()
|
|
{
|
|
return this.canAddItem !== null ? this.canAddItem : this.getGrid().canAddItems();
|
|
},
|
|
|
|
canSortColumnItems: function()
|
|
{
|
|
return this.canSortItems ?? true;
|
|
},
|
|
|
|
/**
|
|
*
|
|
* @returns {number}
|
|
*/
|
|
getTotal: function()
|
|
{
|
|
return this.total !== null ? this.total : this.getItemsCount();
|
|
},
|
|
|
|
/**
|
|
*
|
|
* @param {number} [value=1]
|
|
*/
|
|
incrementTotal: function(value)
|
|
{
|
|
if (this.total !== null && this.getGrid().isRendered() && !this.isTotalFrozen)
|
|
{
|
|
value = BX.type.isNumber(value) ? value : 1;
|
|
this.total = Math.max(this.total + value, this.getItemsCount());
|
|
}
|
|
},
|
|
|
|
/**
|
|
*
|
|
* @param {number} [value=1]
|
|
*/
|
|
decrementTotal: function(value)
|
|
{
|
|
if (this.total !== null && this.getGrid().isRendered() && !this.isTotalFrozen)
|
|
{
|
|
value = BX.type.isNumber(value) ? value : 1;
|
|
this.total = Math.max(this.total - value, this.getItemsCount());
|
|
}
|
|
},
|
|
|
|
freezeTotal: function()
|
|
{
|
|
this.isTotalFrozen = true;
|
|
},
|
|
|
|
unfreezeTotal: function()
|
|
{
|
|
this.isTotalFrozen = false;
|
|
},
|
|
|
|
setTotal: function(total)
|
|
{
|
|
if (BX.type.isNumber(total) && total >= 0)
|
|
{
|
|
this.total = total;
|
|
}
|
|
},
|
|
|
|
refreshTotal: function()
|
|
{
|
|
if (this.total !== null && this.total < this.getItemsCount())
|
|
{
|
|
this.total = this.getItemsCount();
|
|
this.renderTitle();
|
|
}
|
|
},
|
|
|
|
hasLoading: function()
|
|
{
|
|
return this.total !== null && this.total > this.getItemsCount();
|
|
},
|
|
|
|
getIndex: function()
|
|
{
|
|
return this.getGrid().getColumnIndex(this);
|
|
},
|
|
|
|
/**
|
|
*
|
|
* @returns {Element}
|
|
*/
|
|
render: function()
|
|
{
|
|
var title = this.getTitleContainer();
|
|
|
|
BX.cleanNode(title);
|
|
title.appendChild(this.renderTitle());
|
|
|
|
if (this.getGrid().canAddColumns())
|
|
{
|
|
title.appendChild(this.getAddColumnButton());
|
|
}
|
|
|
|
var subTitle = this.getSubTitle();
|
|
|
|
BX.cleanNode(subTitle);
|
|
var subTitleContent = this.renderSubTitle();
|
|
if (subTitleContent !== null)
|
|
{
|
|
subTitle.appendChild(subTitleContent);
|
|
}
|
|
|
|
this.cleanLayoutItems();
|
|
|
|
var isEmptyColumn = true;
|
|
var items = document.createDocumentFragment();
|
|
for (var i = 0; i < this.items.length; i++)
|
|
{
|
|
var item = this.items[i];
|
|
if (item.isVisible())
|
|
{
|
|
isEmptyColumn = false;
|
|
items.appendChild(item.renderLayout());
|
|
}
|
|
}
|
|
|
|
if (!isEmptyColumn)
|
|
{
|
|
this.layout.items.appendChild(items);
|
|
}
|
|
|
|
var columnContainer = this.getContainer();
|
|
columnContainer.classList[isEmptyColumn ? "add" : "remove"]("main-kanban-column-empty");
|
|
columnContainer.classList[this.isDroppable() ? "add" : "remove"]("main-kanban-column-droppable");
|
|
|
|
if(!this.getGrid().firstRenderComplete)
|
|
{
|
|
this.hasBeenEdt = true;
|
|
}
|
|
|
|
if( (this.getContainer().classList.contains("main-kanban-column-droppable")
|
|
|| this.getContainer().classList.contains("main-kanban-column-draggable"))
|
|
&& this.getGrid().firstRenderComplete && !this.hasBeenEdt)
|
|
{
|
|
title.classList.add("--animate-complete");
|
|
var cleanAnimate = function() {
|
|
title.classList.remove("--animate-complete");
|
|
title.removeEventListener("animationend", cleanAnimate);
|
|
}.bind(this);
|
|
title.addEventListener("animationend", cleanAnimate);
|
|
this.hasBeenEdt = true;
|
|
}
|
|
|
|
if (this.getGrid().isRendered())
|
|
{
|
|
this.getPagination().adjust();
|
|
this.getGrid().adjustEmptyStub();
|
|
}
|
|
|
|
BX.onCustomEvent(this.getGrid(), "Kanban.Column:render", [this]);
|
|
|
|
return columnContainer;
|
|
},
|
|
|
|
/**
|
|
* Renders title content. It can be overridden.
|
|
* @returns {Element}
|
|
*/
|
|
renderTitle: function()
|
|
{
|
|
var titleBody = this.getDefaultTitleLayout();
|
|
|
|
var isDark = BX.Kanban.Utils.isDarkColor(this.getColor());
|
|
titleBody.classList[isDark ? "add" : "remove"]("main-kanban-column-title-dark");
|
|
|
|
this.layout.nameInner.textContent = this.getName();
|
|
this.layout.total.textContent = this.getTotal();
|
|
|
|
this.layout.color.style.backgroundColor = "#" + this.getColor();
|
|
this.layout.titleArrow.style.background =
|
|
"transparent url(data:image/svg+xml;charset=US-ASCII,%3Csvg%20xmlns%3D%22http%3A//www.w3.org/2000/svg%22%" +
|
|
"20width%3D%2213%22%20height%3D%2232%22%20viewBox%3D%220%200%2013%2032%22%3E%3Cpath%20fill%3D%22%23" +
|
|
this.getColor() +
|
|
"%22%20fill-opacity%3D%221%22%20d%3D%22M0%200h3c2.8%200%204%203%204%203l6%2013-6%2013s-1.06%203-" +
|
|
"4%203H0V0z%22/%3E%3C/svg%3E) no-repeat"
|
|
;
|
|
|
|
return titleBody;
|
|
},
|
|
|
|
/**
|
|
*
|
|
* @returns {Element}
|
|
*/
|
|
getDefaultTitleLayout: function()
|
|
{
|
|
if (this.layout.titleBody)
|
|
{
|
|
return this.layout.titleBody;
|
|
}
|
|
|
|
var customButtons = this.getCustomTitleButtons();
|
|
if (BX.type.isDomNode(customButtons))
|
|
{
|
|
customButtons = [customButtons];
|
|
}
|
|
else if (!BX.type.isArray(customButtons))
|
|
{
|
|
customButtons = [];
|
|
}
|
|
|
|
this.layout.titleBody = BX.create("div", {
|
|
attrs: {
|
|
className: "main-kanban-column-title-wrapper"
|
|
},
|
|
children: [
|
|
this.layout.color = BX.create("div", {
|
|
attrs: {
|
|
className: "main-kanban-column-title-bg",
|
|
style: "background: #" + this.getColor()
|
|
}
|
|
}),
|
|
this.layout.info = BX.create("div", {
|
|
attrs: {
|
|
className: "main-kanban-column-title-info"
|
|
},
|
|
children: [
|
|
|
|
this.layout.name = BX.create("div", {
|
|
attrs: {
|
|
className: "main-kanban-column-title-text"
|
|
},
|
|
children: [
|
|
this.getColumnTitle(),
|
|
this.getTotalItem()
|
|
]
|
|
}),
|
|
|
|
this.isEditable() ? this.getEditButton() : null
|
|
].concat(customButtons)
|
|
}),
|
|
|
|
this.isEditable() ? this.getEditForm() : null,
|
|
|
|
this.layout.titleArrow = BX.create("span", {
|
|
attrs: {
|
|
className: "main-kanban-column-title-right"
|
|
|
|
}
|
|
})
|
|
|
|
]});
|
|
|
|
return this.layout.titleBody;
|
|
},
|
|
|
|
getColumnTitle: function ()
|
|
{
|
|
return this.layout.nameInner = BX.create("div", {
|
|
attrs: {
|
|
className: "main-kanban-column-title-text-inner"
|
|
}
|
|
})
|
|
},
|
|
|
|
getTotalItem: function ()
|
|
{
|
|
return this.layout.total = BX.create("div", {
|
|
attrs: {
|
|
className: "main-kanban-column-total-item"
|
|
}
|
|
})
|
|
},
|
|
|
|
/**
|
|
*
|
|
* @returns {Element}
|
|
*/
|
|
getEditButton: function()
|
|
{
|
|
if (this.layout.editButton)
|
|
{
|
|
return this.layout.editButton;
|
|
}
|
|
|
|
this.layout.editButton = BX.create("div", {
|
|
attrs: {
|
|
className: "main-kanban-column-edit"
|
|
},
|
|
events: {
|
|
click: this.switchToEditMode.bind(this)
|
|
}
|
|
});
|
|
|
|
return this.layout.editButton;
|
|
},
|
|
|
|
getCustomTitleButtons: function()
|
|
{
|
|
return null;
|
|
},
|
|
|
|
getRemoveButton: function()
|
|
{
|
|
if (this.layout.removeButton)
|
|
{
|
|
return this.layout.removeButton;
|
|
}
|
|
|
|
this.layout.removeButton = BX.create("div", {
|
|
attrs: {
|
|
className: "main-kanban-column-remove-button"
|
|
},
|
|
events: {
|
|
click: this.handleRemoveButtonClick.bind(this)
|
|
}
|
|
});
|
|
|
|
return this.layout.removeButton;
|
|
},
|
|
|
|
getEditForm: function()
|
|
{
|
|
if (this.layout.editForm)
|
|
{
|
|
return this.layout.editForm;
|
|
}
|
|
|
|
this.layout.editForm = BX.create("div", {
|
|
attrs: {
|
|
className: "main-kanban-column-title-block-edit"
|
|
},
|
|
children: [
|
|
this.getTitleTextBox(),
|
|
this.getFillColorButton(),
|
|
this.isRemovable() ? this.getRemoveButton() : null
|
|
]
|
|
});
|
|
|
|
return this.layout.editForm;
|
|
},
|
|
|
|
getTitleTextBox: function()
|
|
{
|
|
if (this.layout.titleTextBox)
|
|
{
|
|
return this.layout.titleTextBox;
|
|
}
|
|
|
|
this.layout.titleTextBox = BX.create("input", {
|
|
attrs: {
|
|
className: "main-kanban-column-title-input-edit",
|
|
type: "text",
|
|
placeholder: this.getGrid().getMessage("COLUMN_TITLE_PLACEHOLDER2"),
|
|
autocomplete: "off",
|
|
disabled: true
|
|
},
|
|
events: {
|
|
blur: this.handleTextBoxBlur.bind(this),
|
|
keydown: this.handleTextBoxKeyDown.bind(this)
|
|
}
|
|
});
|
|
|
|
return this.layout.titleTextBox;
|
|
},
|
|
|
|
getFillColorButton: function()
|
|
{
|
|
if (this.layout.fillColorButton)
|
|
{
|
|
return this.layout.fillColorButton;
|
|
}
|
|
|
|
this.layout.fillColorButton = BX.create("div", {
|
|
attrs: {
|
|
className: "main-kanban-column-color"
|
|
},
|
|
events: {
|
|
click: this.showColorPicker.bind(this)
|
|
}
|
|
});
|
|
|
|
return this.layout.fillColorButton;
|
|
},
|
|
|
|
switchToEditMode: function()
|
|
{
|
|
this.disableDragging();
|
|
this.getContainer().classList.add("main-kanban-column-edit-mode");
|
|
this.getTitleTextBox().disabled = false;
|
|
this.getTitleTextBox().value = this.getName();
|
|
|
|
this.focusTextBox();
|
|
},
|
|
|
|
isEditModeEnabled: function()
|
|
{
|
|
return this.getContainer().classList.contains("main-kanban-column-edit-mode");
|
|
},
|
|
|
|
applyEditMode: function()
|
|
{
|
|
if (!this.isEditModeEnabled())
|
|
{
|
|
return;
|
|
}
|
|
|
|
var title = BX.util.trim(this.getTitleTextBox().value);
|
|
var titleChanged = false;
|
|
if (title.length > 0 && this.getName() !== title)
|
|
{
|
|
titleChanged = true;
|
|
}
|
|
|
|
if (titleChanged || this.colorChanged)
|
|
{
|
|
if (titleChanged)
|
|
{
|
|
this.setName(title);
|
|
}
|
|
|
|
BX.onCustomEvent(this.getGrid(), "Kanban.Grid:onColumnUpdated", [this]);
|
|
this.render();
|
|
}
|
|
|
|
this.colorChanged = false;
|
|
this.enableDragging();
|
|
|
|
this.getTitleTextBox().disabled = true;
|
|
this.getContainer().classList.remove("main-kanban-column-edit-mode");
|
|
},
|
|
|
|
cleanAnimate: function()
|
|
{
|
|
if(!this.animate)
|
|
{
|
|
return;
|
|
}
|
|
|
|
this.animate = null;
|
|
this.getContainer().classList.remove("--animate-" + this.animate);
|
|
this.getContainer().removeEventListener('animationend');
|
|
},
|
|
|
|
handleTextBoxBlur: function(event)
|
|
{
|
|
this.textBoxTimeout = setTimeout(function() {
|
|
|
|
this.applyEditMode();
|
|
this.textBoxTimeout = null;
|
|
|
|
}.bind(this), 250);
|
|
|
|
},
|
|
|
|
stopTextBoxBlur: function()
|
|
{
|
|
if (this.textBoxTimeout)
|
|
{
|
|
clearTimeout(this.textBoxTimeout);
|
|
}
|
|
},
|
|
|
|
focusTextBox: function()
|
|
{
|
|
this.getTitleTextBox().focus();
|
|
},
|
|
|
|
handleTextBoxKeyDown: function(event)
|
|
{
|
|
if (event.keyCode === 13)
|
|
{
|
|
this.applyEditMode();
|
|
}
|
|
},
|
|
|
|
handleRemoveButtonClick: function(event)
|
|
{
|
|
this.showRemoveConfirmDialog();
|
|
},
|
|
|
|
handleScroll(event) {
|
|
BX.Event.EventEmitter.emit(this.getGrid(), 'Kanban.Column:onScroll', { event });
|
|
},
|
|
showColorPicker: function()
|
|
{
|
|
this.stopTextBoxBlur();
|
|
this.getColorPicker().open();
|
|
},
|
|
|
|
/**
|
|
*
|
|
* @returns {BX.ColorPicker}
|
|
*/
|
|
getColorPicker: function()
|
|
{
|
|
if (this.colorPicker)
|
|
{
|
|
return this.colorPicker;
|
|
}
|
|
|
|
this.colorPicker = new BX.ColorPicker({
|
|
bindElement: this.getFillColorButton(),
|
|
onColorSelected: this.onColorSelected.bind(this),
|
|
popupOptions: {
|
|
events: {
|
|
onPopupClose: this.focusTextBox.bind(this)
|
|
}
|
|
}
|
|
});
|
|
|
|
return this.colorPicker;
|
|
},
|
|
|
|
/**
|
|
*
|
|
* @param {string} color
|
|
*/
|
|
onColorSelected: function(color)
|
|
{
|
|
this.setColor(color.substr(1));
|
|
this.colorChanged = true;
|
|
this.render();
|
|
},
|
|
|
|
/**
|
|
*
|
|
* @returns {BX.PopupWindow}
|
|
*/
|
|
getConfirmDialog: function()
|
|
{
|
|
if (this.confirmDialog)
|
|
{
|
|
return this.confirmDialog;
|
|
}
|
|
|
|
this.confirmDialog = new BX.PopupWindow(
|
|
"main-kanban-confirm-" + BX.util.getRandomString(5),
|
|
null,
|
|
{
|
|
titleBar: this.getGrid().getMessage("REMOVE_COLUMN_CONFIRM_TITLE"),
|
|
content: this.getGrid().getMessage("REMOVE_COLUMN_CONFIRM_DESC"),
|
|
width: 400,
|
|
autoHide: false,
|
|
overlay: true,
|
|
closeByEsc : true,
|
|
closeIcon : true,
|
|
draggable : { restrict : true},
|
|
buttons: [
|
|
new BX.PopupWindowButton({
|
|
text: this.getGrid().getMessage("REMOVE_BUTTON"),
|
|
id: "main-kanban-confirm-remove-button",
|
|
className: "popup-window-button-create",
|
|
events: {
|
|
click: this.handleConfirmButtonClick.bind(this)
|
|
}
|
|
}),
|
|
new BX.PopupWindowButtonLink({
|
|
text: this.getGrid().getMessage("CANCEL_BUTTON"),
|
|
className: "popup-window-button-link-cancel",
|
|
events: {
|
|
click: function() {
|
|
this.popupWindow.close();
|
|
}
|
|
}
|
|
})
|
|
],
|
|
events: {
|
|
onPopupClose: function() {
|
|
this.focusTextBox();
|
|
this.confirmDialog.destroy();
|
|
this.confirmDialog = null;
|
|
}.bind(this)
|
|
}
|
|
}
|
|
);
|
|
|
|
return this.confirmDialog;
|
|
},
|
|
|
|
handleConfirmButtonClick: function()
|
|
{
|
|
var confirmDialog = this.getConfirmDialog();
|
|
var removeButton = confirmDialog.getButton("main-kanban-confirm-remove-button");
|
|
if (removeButton.getContainer().classList.contains("popup-window-button-wait"))
|
|
{
|
|
//double click protection
|
|
return;
|
|
}
|
|
|
|
removeButton.addClassName("popup-window-button-wait");
|
|
|
|
var promise = this.getGrid().getEventPromise(
|
|
"Kanban.Grid:onColumnRemovedAsync",
|
|
null,
|
|
function(result) {
|
|
|
|
this.getGrid().removeColumn(this);
|
|
removeButton.removeClassName("popup-window-button-wait");
|
|
confirmDialog.close();
|
|
|
|
}.bind(this),
|
|
function(error) {
|
|
confirmDialog.setContent(error);
|
|
removeButton.getContainer().style.display = "none";
|
|
}.bind(this)
|
|
);
|
|
|
|
promise.fulfill(this);
|
|
},
|
|
|
|
showRemoveConfirmDialog: function()
|
|
{
|
|
this.stopTextBoxBlur();
|
|
var confirmDialog = this.getConfirmDialog();
|
|
confirmDialog.show();
|
|
},
|
|
|
|
handleAddColumnButtonClick: function(event)
|
|
{
|
|
var newColumn = this.getGrid().addColumn({
|
|
id: "kanban-new-column-" + BX.util.getRandomString(5),
|
|
type: "BX.Kanban.DraftColumn",
|
|
canSort: false,
|
|
canAddItem: false,
|
|
droppable: false,
|
|
targetId: this.getGrid().getNextColumnSibling(this)
|
|
});
|
|
|
|
newColumn.switchToEditMode();
|
|
},
|
|
|
|
/**
|
|
* Renders subtitle content. It can be overridden.
|
|
* @returns {Element}
|
|
*/
|
|
renderSubTitle: function()
|
|
{
|
|
if (this.layout.subTitleAddButton)
|
|
{
|
|
return this.layout.subTitleAddButton;
|
|
}
|
|
|
|
var button;
|
|
this.layout.subTitleAddButton = BX.create("div", {
|
|
attrs: {
|
|
className: "main-kanban-column-subtitle-box"
|
|
},
|
|
children: [
|
|
this.canAddItems()
|
|
? button = BX.create("div", {
|
|
attrs: {
|
|
className: "main-kanban-column-add-item-button"
|
|
},
|
|
events: {
|
|
click: this.handleAddItemButtonClick.bind(this)
|
|
},
|
|
children: [
|
|
this.getGrid().getAddItemTitleText()
|
|
? this.subTitleAddButtonTextWrapper = BX.create("div", {
|
|
props: {
|
|
className: "main-kanban-column-add-item-button-text"
|
|
},
|
|
children: [
|
|
this.subTitleAddButtonText = BX.create("span", {
|
|
text: this.getGrid().getAddItemTitleText()
|
|
})
|
|
],
|
|
})
|
|
: null
|
|
]
|
|
})
|
|
: null
|
|
]
|
|
});
|
|
|
|
return this.layout.subTitleAddButton;
|
|
},
|
|
|
|
handleAddItemButtonClick: function(event)
|
|
{
|
|
var firstItem = this.getFirstItem(false);
|
|
if (firstItem)
|
|
{
|
|
var existsDraftItem = firstItem.getId().indexOf('kanban-new-item-') === 0;
|
|
if (existsDraftItem)
|
|
{
|
|
firstItem.applyDraftEditMode();
|
|
|
|
return;
|
|
}
|
|
}
|
|
|
|
this.addDraftItem(firstItem);
|
|
},
|
|
|
|
cleanLayoutItems: function()
|
|
{
|
|
BX.cleanNode(this.layout.items);
|
|
},
|
|
|
|
getDraftItem: function()
|
|
{
|
|
return this.draftItem;
|
|
},
|
|
|
|
/**
|
|
*
|
|
* @returns {BX.Kanban.DraftItem|null}
|
|
*/
|
|
addDraftItem: function(targetItem)
|
|
{
|
|
var id = "kanban-new-item-" + this.getId();
|
|
if (this.getGrid().getItem(id))
|
|
{
|
|
return null;
|
|
}
|
|
|
|
if(!targetItem)
|
|
{
|
|
targetItem = this.getFirstItem(false);
|
|
}
|
|
|
|
var targetId = null;
|
|
if (targetItem instanceof BX.Kanban.Item && targetItem.getColumn() === this)
|
|
{
|
|
targetId = targetItem;
|
|
}
|
|
|
|
this.draftItem = this.getGrid().addItem({
|
|
id: id,
|
|
type: "BX.Kanban.DraftItem",
|
|
columnId: this.getId(),
|
|
draggable: false,
|
|
droppable: false,
|
|
countable: false,
|
|
targetId: targetId
|
|
});
|
|
|
|
if (this.draftItem)
|
|
{
|
|
this.draftItem.focusDraftTextArea();
|
|
}
|
|
|
|
return this.draftItem;
|
|
},
|
|
|
|
removeDraftItem: function()
|
|
{
|
|
if (this.draftItem !== null)
|
|
{
|
|
this.getGrid().removeItem(this.draftItem);
|
|
this.draftItem = null;
|
|
}
|
|
},
|
|
|
|
/**
|
|
*
|
|
* @returns {Element}
|
|
*/
|
|
getContainer: function()
|
|
{
|
|
if (this.layout.container !== null)
|
|
{
|
|
return this.layout.container;
|
|
}
|
|
|
|
this.layout.container = BX.create("div", {
|
|
attrs: {
|
|
className: this.animate ? "main-kanban-column" + " --animate-" + this.animate : "main-kanban-column"
|
|
},
|
|
children: [
|
|
this.getHeader(),
|
|
this.getBody()
|
|
],
|
|
events: {
|
|
mouseenter: function() {
|
|
if (this.subTitleAddButtonText && this.subTitleAddButtonTextWrapper)
|
|
{
|
|
this.subTitleAddButtonTextWrapper.style.width = this.subTitleAddButtonText.offsetWidth + 'px';
|
|
}
|
|
}.bind(this),
|
|
mouseleave: function() {
|
|
if (this.subTitleAddButtonText && this.subTitleAddButtonTextWrapper)
|
|
{
|
|
this.subTitleAddButtonTextWrapper.style.width = null;
|
|
}
|
|
}.bind(this)
|
|
|
|
}
|
|
});
|
|
|
|
this.makeDraggable();
|
|
this.makeDroppable();
|
|
|
|
return this.layout.container;
|
|
},
|
|
|
|
/**
|
|
*
|
|
* @returns {Element}
|
|
*/
|
|
getHeader: function()
|
|
{
|
|
if (this.layout.header)
|
|
{
|
|
return this.layout.header;
|
|
}
|
|
|
|
this.layout.header = BX.create("div", {
|
|
attrs: {
|
|
className: "main-kanban-column-header"
|
|
},
|
|
children: [
|
|
this.getTitleContainer(),
|
|
this.getSubTitle()
|
|
]
|
|
});
|
|
|
|
return this.layout.header;
|
|
},
|
|
|
|
/**
|
|
*
|
|
* @returns {Element}
|
|
*/
|
|
getBody: function()
|
|
{
|
|
if (this.layout.body)
|
|
{
|
|
return this.layout.body;
|
|
}
|
|
|
|
this.layout.body = BX.create("div", {
|
|
attrs: {
|
|
className: "main-kanban-column-body",
|
|
"data-id": this.getId(),
|
|
"data-type": "column"
|
|
},
|
|
events: {
|
|
wheel: BX.delegate(this.blockPageScroll, this),
|
|
scroll: this.handleScrollWithThrottle.bind(this),
|
|
},
|
|
children: [
|
|
this.getItemsContainer(),
|
|
this.getDragTarget()
|
|
]
|
|
});
|
|
|
|
return this.layout.body;
|
|
},
|
|
|
|
/**
|
|
*
|
|
* @returns {Element}
|
|
*/
|
|
getTitleContainer: function(event)
|
|
{
|
|
if (this.layout.title)
|
|
{
|
|
return this.layout.title;
|
|
}
|
|
|
|
this.layout.title = BX.create("div", {
|
|
attrs: {
|
|
className: "main-kanban-column-title"
|
|
}
|
|
});
|
|
|
|
return this.layout.title;
|
|
},
|
|
|
|
/**
|
|
*
|
|
* @returns {Element}
|
|
*/
|
|
getSubTitle: function()
|
|
{
|
|
if (!this.layout.subTitle)
|
|
{
|
|
this.layout.subTitle = BX.create("div", {
|
|
attrs: {
|
|
className: "main-kanban-column-subtitle"
|
|
}
|
|
})
|
|
}
|
|
|
|
return this.layout.subTitle;
|
|
},
|
|
|
|
getAddColumnButton: function ()
|
|
{
|
|
if (this.layout.addColumnButton)
|
|
{
|
|
return this.layout.addColumnButton;
|
|
}
|
|
this.layout.addColumnButton = BX.create("div", {
|
|
attrs: {
|
|
className: "main-kanban-column-title-add-column"
|
|
},
|
|
children: [
|
|
this.layout.addColumnButtonBefore = BX.create("div", {
|
|
props: {
|
|
className: 'main-kanban-column-title-add-column-before'
|
|
}
|
|
}),
|
|
this.layout.addColumnButtonAfter = BX.create("div", {
|
|
props: {
|
|
className: 'main-kanban-column-title-add-column-after'
|
|
}
|
|
})
|
|
],
|
|
events: {
|
|
click: this.handleAddColumnButtonClick.bind(this)
|
|
}
|
|
});
|
|
|
|
return this.layout.addColumnButton;
|
|
},
|
|
|
|
handlerHoverClass: function(node)
|
|
{
|
|
if(!node)
|
|
{
|
|
return;
|
|
}
|
|
|
|
node.addEventListener("mouseenter", function()
|
|
{
|
|
node.classList.add("--hover");
|
|
});
|
|
|
|
node.addEventListener("mouseleave", function()
|
|
{
|
|
node.classList.remove("--hover");
|
|
});
|
|
},
|
|
|
|
/**
|
|
*
|
|
* @returns {Element}
|
|
*/
|
|
getItemsContainer: function()
|
|
{
|
|
if (!this.layout.items)
|
|
{
|
|
this.layout.items = BX.create("div", {
|
|
attrs: {
|
|
className: "main-kanban-column-items"
|
|
}
|
|
})
|
|
}
|
|
|
|
return this.layout.items;
|
|
},
|
|
|
|
onAhaMode: function()
|
|
{
|
|
this.getBody().appendChild(this.getAhaItem());
|
|
},
|
|
|
|
offAhaMode: function()
|
|
{
|
|
this.getBody().removeChild(this.getAhaItem());
|
|
},
|
|
|
|
getAhaItem: function()
|
|
{
|
|
if (!this.layout.ahaItem)
|
|
{
|
|
this.layout.ahaItem = BX.create("div", {
|
|
props: {
|
|
className: "main-kanban-item-aha"
|
|
}
|
|
})
|
|
}
|
|
|
|
return this.layout.ahaItem;
|
|
},
|
|
|
|
/**
|
|
*
|
|
* @returns {Element}
|
|
*/
|
|
getDragTarget: function()
|
|
{
|
|
if (!this.layout.dragTarget)
|
|
{
|
|
this.layout.dragTarget = BX.create("div", {
|
|
attrs: {
|
|
className: "main-kanban-column-drag-target"
|
|
}
|
|
});
|
|
}
|
|
|
|
return this.layout.dragTarget;
|
|
},
|
|
|
|
/**
|
|
*
|
|
* @param {WheelEvent} event
|
|
*/
|
|
blockPageScroll: function(event)
|
|
{
|
|
var bodyContainer = this.getBody();
|
|
if (bodyContainer.scrollHeight > bodyContainer.offsetHeight)
|
|
{
|
|
var mouseScroll = event.deltaY || event.detail || event.wheelDelta;
|
|
|
|
if (mouseScroll < 0 && bodyContainer.scrollTop === 0)
|
|
{
|
|
event.preventDefault();
|
|
}
|
|
|
|
if (mouseScroll > 0 && bodyContainer.scrollHeight - bodyContainer.clientHeight - bodyContainer.scrollTop <= 1)
|
|
{
|
|
event.preventDefault();
|
|
}
|
|
}
|
|
},
|
|
|
|
makeDraggable: function()
|
|
{
|
|
if (!this.isSortable())
|
|
{
|
|
return;
|
|
}
|
|
|
|
var title = this.getTitleContainer();
|
|
|
|
//main events
|
|
title.onbxdragstart = BX.delegate(this.onColumnDragStart, this);
|
|
title.onbxdrag = BX.delegate(this.onColumnDrag, this);
|
|
title.onbxdragstop = BX.delegate(this.onColumnDragStop, this);
|
|
|
|
this.enableDragging();
|
|
},
|
|
|
|
makeDroppable: function()
|
|
{
|
|
if (!this.isDroppable())
|
|
{
|
|
return;
|
|
}
|
|
|
|
var columnBody = this.getBody();
|
|
|
|
columnBody.onbxdestdraghover = BX.delegate(this.onDragEnter, this);
|
|
columnBody.onbxdestdraghout = BX.delegate(this.onDragLeave, this);
|
|
columnBody.onbxdestdragfinish = BX.delegate(this.onDragDrop, this);
|
|
|
|
columnBody.onbxdestdragstop = BX.delegate(this.onItemDragEnd, this);
|
|
|
|
jsDD.registerDest(columnBody, 40);
|
|
|
|
this.disableDropping();
|
|
},
|
|
|
|
disableDragging: function()
|
|
{
|
|
if (this.isSortable())
|
|
{
|
|
jsDD.unregisterObject(this.getTitleContainer());
|
|
}
|
|
},
|
|
|
|
enableDragging: function()
|
|
{
|
|
if (this.isSortable())
|
|
{
|
|
jsDD.registerObject(this.getTitleContainer());
|
|
}
|
|
},
|
|
|
|
disableDropping: function()
|
|
{
|
|
if (this.isDroppable())
|
|
{
|
|
jsDD.disableDest(this.getBody());
|
|
}
|
|
},
|
|
|
|
enableDropping: function()
|
|
{
|
|
if (this.isDroppable())
|
|
{
|
|
jsDD.enableDest(this.getBody());
|
|
}
|
|
},
|
|
|
|
/**
|
|
*
|
|
* @returns {boolean}
|
|
*/
|
|
isDraggable: function()
|
|
{
|
|
return this.isSortable();
|
|
},
|
|
|
|
/**
|
|
*
|
|
* @returns {boolean}
|
|
*/
|
|
isDroppable: function()
|
|
{
|
|
return this.droppable;
|
|
},
|
|
|
|
/**
|
|
*
|
|
* @param {Element} itemNode
|
|
* @param {number} x
|
|
* @param {number} y
|
|
*/
|
|
onDragEnter: function(itemNode, x, y)
|
|
{
|
|
var draggableItem = this.getGrid().getItemByElement(itemNode);
|
|
this.showDragTarget(draggableItem.getBodyContainer().offsetHeight);
|
|
},
|
|
|
|
/**
|
|
*
|
|
* @param {Element} itemNode
|
|
* @param {number} x
|
|
* @param {number} y
|
|
*/
|
|
onDragLeave: function(itemNode, x, y)
|
|
{
|
|
this.hideDragTarget();
|
|
},
|
|
|
|
/**
|
|
*
|
|
* @param {Element} itemNode
|
|
* @param {number} x
|
|
* @param {number} y
|
|
*/
|
|
onDragDrop: function(itemNode, x, y)
|
|
{
|
|
if(this.getGrid().getSelectedItems().length > 0)
|
|
{
|
|
this.onDragDropMulti(this.getGrid().getSelectedItems());
|
|
return;
|
|
}
|
|
|
|
this.hideDragTarget();
|
|
var draggableItem = this.getGrid().getItemByElement(itemNode);
|
|
|
|
var event = new BX.Kanban.DragEvent();
|
|
event.setItem(draggableItem);
|
|
event.setTargetColumn(this);
|
|
|
|
BX.onCustomEvent(this.getGrid(), "Kanban.Grid:onBeforeItemMoved", [event]);
|
|
if (!event.isActionAllowed())
|
|
{
|
|
return;
|
|
}
|
|
|
|
var success = this.getGrid().moveItem(draggableItem, this);
|
|
if (success)
|
|
{
|
|
BX.onCustomEvent(this.getGrid(), "Kanban.Grid:onItemMoved", [draggableItem, this, null]);
|
|
}
|
|
},
|
|
|
|
/**
|
|
*
|
|
* @param {Object} items
|
|
* @param {number} x
|
|
* @param {number} y
|
|
*/
|
|
onDragDropMulti: function(items, x, y)
|
|
{
|
|
this.hideDragTarget();
|
|
|
|
var draggableItems = items;
|
|
|
|
var event = new BX.Kanban.DragEvent();
|
|
event.setItems(draggableItems);
|
|
event.setTargetColumn(this);
|
|
|
|
BX.onCustomEvent(this.getGrid(), "Kanban.Grid:onBeforeItemMoved", [event]);
|
|
if (!event.isActionAllowed())
|
|
{
|
|
return;
|
|
}
|
|
|
|
var success = this.getGrid().moveItems(draggableItems, this);
|
|
if (success)
|
|
{
|
|
BX.onCustomEvent(this.getGrid(), "Kanban.Grid:onItemsMoved", [draggableItems, this, null]);
|
|
}
|
|
},
|
|
|
|
/**
|
|
*
|
|
* @param {Element} itemNode
|
|
* @param {number} x
|
|
* @param {number} y
|
|
*/
|
|
onItemDragEnd: function(itemNode, x, y)
|
|
{
|
|
this.disableDropping();
|
|
},
|
|
|
|
onColumnDragStart: function()
|
|
{
|
|
BX.onCustomEvent(this.getGrid(), "Kanban.Grid:onColumnDragStart", [this]);
|
|
|
|
this.getContainer().classList.add("main-kanban-column-draggable");
|
|
|
|
this.dragColumnOffset = jsDD.start_x - this.getRectArea().left;
|
|
this.dragColumnIndex = this.getIndex();
|
|
this.dragTargetColumn = this.dragTargetColumn || this;
|
|
},
|
|
|
|
/**
|
|
*
|
|
* @param {number} x
|
|
* @param {number} y
|
|
*/
|
|
onColumnDrag: function(x, y)
|
|
{
|
|
this.getContainer().style.transform = "translateX(" + (x - this.dragColumnOffset - this.getRectArea().left) + "px)";
|
|
|
|
var columns = this.getGrid().getColumns();
|
|
var columnWidth = this.getRectArea().width;
|
|
|
|
for (var columnIndex in columns)
|
|
{
|
|
var column = columns[columnIndex];
|
|
if (column === this || !column.isSortable())
|
|
{
|
|
continue;
|
|
}
|
|
|
|
var columnContainer = column.getContainer();
|
|
var columnRectArea = column.getRectArea();
|
|
var columnMiddle = columnRectArea.middle;
|
|
|
|
if (
|
|
x > columnMiddle &&
|
|
columnIndex > this.dragColumnIndex &&
|
|
columnContainer.style.transform !== "translateX(" + (-columnWidth) + "px)"
|
|
)
|
|
{
|
|
//move left
|
|
columnContainer.style.transition = "300ms";
|
|
columnContainer.style.transform = "translateX(" + (-columnWidth) + "px)";
|
|
this.dragTargetColumn = this.getGrid().getNextColumnSibling(column);
|
|
|
|
column.resetRectArea();
|
|
}
|
|
|
|
if (
|
|
x < columnMiddle &&
|
|
columnIndex < this.dragColumnIndex &&
|
|
columnContainer.style.transform !== "translateX("+(columnWidth)+"px)"
|
|
)
|
|
{
|
|
//move right
|
|
columnContainer.style.transition = "300ms";
|
|
columnContainer.style.transform = "translateX(" + columnWidth + "px)";
|
|
this.dragTargetColumn = column;
|
|
|
|
column.resetRectArea();
|
|
}
|
|
|
|
var moveBackRight =
|
|
x < columnMiddle &&
|
|
columnIndex > this.dragColumnIndex &&
|
|
columnContainer.style.transform !== "" &&
|
|
columnContainer.style.transform !== "translateX(0px)"
|
|
;
|
|
|
|
var moveBackLeft =
|
|
x > columnMiddle &&
|
|
columnIndex < this.dragColumnIndex &&
|
|
columnContainer.style.transform !== "" &&
|
|
columnContainer.style.transform !== "translateX(0px)"
|
|
;
|
|
|
|
if (moveBackLeft || moveBackRight)
|
|
{
|
|
//move to the start position
|
|
columnContainer.style.transition = "300ms";
|
|
columnContainer.style.transform = "translateX(0px)";
|
|
this.dragTargetColumn = moveBackRight ? column : this.getGrid().getNextColumnSibling(column);
|
|
|
|
column.resetRectArea();
|
|
}
|
|
|
|
}
|
|
},
|
|
|
|
/**
|
|
*
|
|
* @param {number} x
|
|
* @param {number} y
|
|
*/
|
|
onColumnDragStop: function(x, y)
|
|
{
|
|
BX.onCustomEvent(this.getGrid(), "Kanban.Grid:onColumnDragStop", [this]);
|
|
|
|
var success = this.getGrid().moveColumn(this, this.dragTargetColumn);
|
|
if (success)
|
|
{
|
|
BX.onCustomEvent(this.getGrid(), "Kanban.Grid:onColumnMoved", [this, this.getGrid().getNextColumnSibling(this)]);
|
|
}
|
|
|
|
this.getContainer().classList.remove("main-kanban-column-draggable");
|
|
|
|
var columns = this.getGrid().getColumns();
|
|
for (var columnIndex in columns)
|
|
{
|
|
var column = columns[columnIndex];
|
|
var columnContainer = column.getContainer();
|
|
|
|
column.resetRectArea();
|
|
columnContainer.style.removeProperty("transition");
|
|
columnContainer.style.removeProperty("transform");
|
|
}
|
|
|
|
this.getGrid().adjustEars();
|
|
},
|
|
|
|
/**
|
|
*
|
|
* @returns {ClientRect}
|
|
*/
|
|
getRectArea: function()
|
|
{
|
|
if (!this.rectArea)
|
|
{
|
|
this.rectArea = BX.pos(this.getContainer());
|
|
this.rectArea.middle = this.rectArea.left + this.rectArea.width / 2;
|
|
}
|
|
|
|
return this.rectArea;
|
|
},
|
|
|
|
resetRectArea: function()
|
|
{
|
|
this.rectArea = null;
|
|
},
|
|
|
|
/**
|
|
*
|
|
* @param {number} height
|
|
*/
|
|
showDragTarget: function(height)
|
|
{
|
|
this.getContainer().classList.add("main-kanban-column-target-shown");
|
|
this.getDragTarget().style.height = height + "px";
|
|
},
|
|
|
|
hideDragTarget: function()
|
|
{
|
|
this.getContainer().classList.remove("main-kanban-column-target-shown");
|
|
this.getDragTarget().style.removeProperty("height");
|
|
}
|
|
};
|
|
|
|
|
|
/**
|
|
*
|
|
* @param options
|
|
* @extends {BX.Kanban.Column}
|
|
* @constructor
|
|
*/
|
|
BX.Kanban.DraftColumn = function(options)
|
|
{
|
|
BX.Kanban.Column.apply(this, arguments);
|
|
this.asyncEventStarted = false;
|
|
|
|
BX.addCustomEvent(this, "Kanban.Column:onAddedToGrid", this.onAddedToGrid.bind(this));
|
|
};
|
|
|
|
BX.Kanban.DraftColumn.lastColorIndex = -1;
|
|
|
|
BX.Kanban.DraftColumn.prototype = {
|
|
__proto__: BX.Kanban.Column.prototype,
|
|
constructor: BX.Kanban.DraftColumn,
|
|
|
|
applyEditMode: function()
|
|
{
|
|
if (this.asyncEventStarted)
|
|
{
|
|
return;
|
|
}
|
|
|
|
var title = BX.util.trim(this.getTitleTextBox().value);
|
|
if (!title.length)
|
|
{
|
|
title = this.getGrid().getMessage("COLUMN_TITLE_PLACEHOLDER");
|
|
}
|
|
|
|
this.setName(title);
|
|
this.getContainer().classList.add("main-kanban-column-disabled");
|
|
this.getTitleTextBox().disabled = true;
|
|
|
|
this.asyncEventStarted = true;
|
|
var promise = this.getGrid().getEventPromise(
|
|
"Kanban.Grid:onColumnAddedAsync",
|
|
null,
|
|
function(result) {
|
|
|
|
if (!BX.Kanban.Utils.isValidId(result.targetId))
|
|
{
|
|
var targetColumn = this.getGrid().getNextColumnSibling(this);
|
|
if (targetColumn)
|
|
{
|
|
result.targetId = targetColumn.getId();
|
|
}
|
|
}
|
|
|
|
this.getGrid().removeColumn(this);
|
|
this.getGrid().addColumn(result);
|
|
|
|
}.bind(this),
|
|
function(error) {
|
|
|
|
this.getGrid().removeColumn(this);
|
|
|
|
}.bind(this)
|
|
);
|
|
|
|
promise.fulfill(this);
|
|
},
|
|
|
|
handleRemoveButtonClick: function(event)
|
|
{
|
|
this.stopTextBoxBlur();
|
|
this.getGrid().removeColumn(this);
|
|
},
|
|
|
|
onAddedToGrid: function()
|
|
{
|
|
this.setColor(this.getNextColor());
|
|
},
|
|
|
|
getNextColor: function()
|
|
{
|
|
var defaultColors = BX.Kanban.Utils.getDefaultColors();
|
|
if (!defaultColors.length)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
if (BX.Kanban.DraftColumn.lastColorIndex === -1)
|
|
{
|
|
var columns = this.getGrid().getColumns();
|
|
for (var i = columns.length - 1; i >= 0; i--)
|
|
{
|
|
var column = columns[i];
|
|
var index = BX.util.array_search(column.getColor(), defaultColors);
|
|
if (index !== -1)
|
|
{
|
|
BX.Kanban.DraftColumn.lastColorIndex = index;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
BX.Kanban.DraftColumn.lastColorIndex =
|
|
defaultColors[BX.Kanban.DraftColumn.lastColorIndex + 1] ? BX.Kanban.DraftColumn.lastColorIndex + 1 : 0;
|
|
|
|
return defaultColors[BX.Kanban.DraftColumn.lastColorIndex];
|
|
}
|
|
};
|
|
|
|
/**
|
|
*
|
|
* @param {BX.Kanban.Column} column
|
|
* @constructor
|
|
*/
|
|
BX.Kanban.Pagination = function(column)
|
|
{
|
|
/** @var {BX.Kanban.Column} **/
|
|
this.column = column;
|
|
this.timer = null;
|
|
this.page = 1;
|
|
this.loadingInProgress = false;
|
|
|
|
this.layout = {
|
|
topButton: null,
|
|
bottomButton: null,
|
|
loader: null
|
|
};
|
|
|
|
BX.addCustomEvent(column, "Kanban.Column:onAddedToGrid", this.init.bind(this));
|
|
};
|
|
|
|
BX.Kanban.Pagination.prototype = {
|
|
|
|
init: function()
|
|
{
|
|
var column = this.getColumn();
|
|
var columnContainer = column.getContainer();
|
|
var bodyContainer = column.getBody();
|
|
columnContainer.appendChild(this.getTopButton());
|
|
columnContainer.appendChild(this.getBottomButton());
|
|
bodyContainer.appendChild(this.getLoader());
|
|
|
|
var adjust = BX.delegate(this.adjust, this);
|
|
|
|
BX.bind(bodyContainer, "scroll", BX.throttle(adjust, 150));
|
|
BX.bind(window, "scroll", BX.throttle(adjust, 150));
|
|
BX.addCustomEvent("Kanban.Grid:onFirstRender", adjust);
|
|
},
|
|
|
|
adjust: function()
|
|
{
|
|
var column = this.getColumn();
|
|
var columnContainer = column.getContainer();
|
|
var bodyContainer = column.getBody();
|
|
|
|
var scrollHeight = bodyContainer.scrollHeight;
|
|
var offsetHeight = bodyContainer.offsetHeight;
|
|
var scrollTop = bodyContainer.scrollTop;
|
|
|
|
var isTopVisible = bodyContainer.scrollTop > 0;
|
|
var isBottomVisible = scrollHeight > offsetHeight + scrollTop;
|
|
|
|
columnContainer.classList[isTopVisible ? "add" : "remove"]("main-kanban-column-top-button-shown");
|
|
columnContainer.classList[isBottomVisible ? "add" : "remove"]("main-kanban-column-bottom-button-shown");
|
|
|
|
if (columnContainer.classList.contains("main-kanban-column-top-button-shown"))
|
|
{
|
|
this.getTopButton().style.top = this.getColumn().getBody().offsetTop + "px";
|
|
}
|
|
|
|
jsDD.refreshDestArea();
|
|
|
|
var loader = this.getLoader();
|
|
if (!this.loadingInProgress && column.hasLoading() && loader.offsetTop < scrollTop + offsetHeight)
|
|
{
|
|
this.showLoader();
|
|
this.loadItems();
|
|
}
|
|
},
|
|
|
|
loadItems: function()
|
|
{
|
|
this.loadingInProgress = true;
|
|
|
|
var promise = this.getColumn().getGrid().getEventPromise(
|
|
"Kanban.Grid:onColumnLoadAsync",
|
|
null,
|
|
this.onPromiseFulfilled.bind(this),
|
|
this.onPromiseRejected.bind(this)
|
|
);
|
|
|
|
promise.fulfill(this.getColumn());
|
|
},
|
|
|
|
onPromiseFulfilled: function(result)
|
|
{
|
|
this.hideLoader();
|
|
this.processPromiseResult(result);
|
|
},
|
|
|
|
onPromiseRejected: function(reason)
|
|
{
|
|
this.hideLoader();
|
|
//this.loadingInProgress = false;
|
|
},
|
|
|
|
processPromiseResult: function(result)
|
|
{
|
|
if (!BX.type.isArray(result) || !result.length)
|
|
{
|
|
return;
|
|
}
|
|
|
|
var column = this.getColumn();
|
|
column.freezeTotal();
|
|
|
|
column.getGrid().setRenderStatus(false);
|
|
var scrollTop = column.getBody().scrollTop;
|
|
|
|
for (var i = 0; i < result.length; i++)
|
|
{
|
|
var item = result[i];
|
|
column.getGrid().addItem(item);
|
|
}
|
|
|
|
column.render();
|
|
column.getBody().scrollTop = scrollTop;
|
|
|
|
column.getGrid().setRenderStatus(true);
|
|
|
|
column.unfreezeTotal();
|
|
column.refreshTotal();
|
|
|
|
this.page++;
|
|
this.loadingInProgress = false;
|
|
|
|
this.adjust();
|
|
},
|
|
|
|
/**
|
|
*
|
|
* @returns {BX.Kanban.Column}
|
|
*/
|
|
getColumn: function()
|
|
{
|
|
return this.column;
|
|
},
|
|
|
|
/**
|
|
*
|
|
* @returns {number}
|
|
*/
|
|
getPage: function()
|
|
{
|
|
return this.page;
|
|
},
|
|
|
|
/**
|
|
*
|
|
* @returns {Element}
|
|
*/
|
|
getTopButton: function()
|
|
{
|
|
if (this.layout.topButton)
|
|
{
|
|
return this.layout.topButton;
|
|
}
|
|
|
|
this.layout.topButton = BX.create("div", {
|
|
attrs: {
|
|
className: "main-kanban-column-top-button"
|
|
},
|
|
events: {
|
|
mouseenter: BX.delegate(this.scrollUp, this),
|
|
mouseleave: BX.delegate(this.stopScroll, this)
|
|
}
|
|
});
|
|
|
|
return this.layout.topButton;
|
|
},
|
|
|
|
/**
|
|
*
|
|
* @returns {Element}
|
|
*/
|
|
getBottomButton: function()
|
|
{
|
|
if (this.layout.bottomButton)
|
|
{
|
|
return this.layout.bottomButton;
|
|
}
|
|
|
|
this.layout.bottomButton = BX.create("div", {
|
|
attrs: {
|
|
className: "main-kanban-column-bottom-button"
|
|
},
|
|
events: {
|
|
mouseenter: BX.delegate(this.scrollDown, this),
|
|
mouseleave: BX.delegate(this.stopScroll, this)
|
|
}
|
|
});
|
|
|
|
return this.layout.bottomButton;
|
|
},
|
|
|
|
/**
|
|
*
|
|
* @returns {Element}
|
|
*/
|
|
getLoader: function()
|
|
{
|
|
if (this.layout.loader)
|
|
{
|
|
return this.layout.loader;
|
|
}
|
|
|
|
this.layout.loader = BX.create("div", {
|
|
attrs: {
|
|
className: "main-kanban-loader"
|
|
},
|
|
html:
|
|
'<div class="main-kanban-loader-outer">' +
|
|
'<div class="main-kanban-loader-inner">' +
|
|
'<svg class="main-kanban-loader-circle" viewBox="25 25 50 50">' +
|
|
'<circle ' +
|
|
'class="main-kanban-loader-path" cx="50" cy="50" r="20" fill="none" ' +
|
|
'stroke-width="1" stroke-miterlimit="10"' +
|
|
'/>' +
|
|
'</svg>' +
|
|
'</div>' +
|
|
'</div>'
|
|
});
|
|
|
|
return this.layout.loader;
|
|
},
|
|
|
|
showLoader: function()
|
|
{
|
|
this.getLoader().classList.add("main-kanban-loader-shown");
|
|
},
|
|
|
|
hideLoader: function()
|
|
{
|
|
this.getLoader().classList.remove("main-kanban-loader-shown");
|
|
},
|
|
|
|
scrollUp: function()
|
|
{
|
|
if (this.getColumn().getGrid().getDragMode() !== BX.Kanban.DragMode.ITEM)
|
|
{
|
|
return;
|
|
}
|
|
|
|
this.timer = setInterval(BX.delegate(function()
|
|
{
|
|
this.getColumn().getBody().scrollTop -= 10;
|
|
}, this), 20);
|
|
},
|
|
|
|
scrollDown: function()
|
|
{
|
|
if (this.getColumn().getGrid().getDragMode() !== BX.Kanban.DragMode.ITEM)
|
|
{
|
|
return;
|
|
}
|
|
|
|
this.timer = setInterval(BX.delegate(function()
|
|
{
|
|
this.getColumn().getBody().scrollTop += 10;
|
|
}, this), 20);
|
|
},
|
|
|
|
stopScroll: function()
|
|
{
|
|
clearInterval(this.timer);
|
|
}
|
|
};
|
|
|
|
})();
|