UNPKG

@progress/kendo-ui

Version:

This package is part of the [Kendo UI for jQuery](http://www.telerik.com/kendo-ui) suite.

1,384 lines (1,377 loc) 74.1 kB
//#region ../src/taskboard/column.js (function($, undefined) { var kendo = window.kendo, ui = kendo.ui, Observable = kendo.Observable, encode = kendo.htmlEncode, extend = $.extend, DOT = ".", NS = DOT + "kendoTaskBoardColumn", CLICK = "click", BLUR = "blur", MOVE = "move", MOVE_START = "moveStart", MOVE_END = "moveEnd", CHANGE = "change", ACTION = "action"; var TaskBoardColumnStyles = { element: "k-taskboard-column", header: "k-taskboard-column-header", headerText: "k-taskboard-column-header-text", ellipsis: "k-text-ellipsis", spacer: "k-spacer", actions: "k-taskboard-column-header-actions", cardsContainer: "k-taskboard-column-cards-container", columnCards: "k-taskboard-column-cards", actionButton: "k-taskboard-column-action-button", card: "k-taskboard-card", cardTitle: "k-card-title", sortableSuffix: "-kendosortable", textbox: "k-textbox k-input", input: "k-input-inner", newColumn: "k-taskboard-column-new", editColumn: "k-taskboard-column-edit", disabled: "k-disabled", dragPlaceholder: "k-taskboard-drag-placeholder", dragHint: "k-taskboard-drag-hint", ignoreDrag: "k-taskboard-drag-ignore", grabbingCursor: "k-cursor-grabbing" }; const preventTextSelectionStyles = function(prevent) { return { "-webkit-user-select": prevent ? "none" : "", "-moz-user-select": prevent ? "none" : "", "-ms-user-select": prevent ? "none" : "", "user-select": prevent ? "none" : "" }; }; function preventDefault(ev) { ev.preventDefault(); } var TaskBoardColumn = Observable.extend({ init: function(options, dataItem) { var that = this; that._dataItem = dataItem; that.options = extend(true, {}, options); that.sortableUniqueClass = that.options.sortableIdentifier + TaskBoardColumn.styles.sortableSuffix; that._render(); that._attachEvents(); if (options.states.isReorderable) { that._initSortable(); } Observable.fn.init.call(that); }, empty: function() { var that = this; that.container.empty(); }, addCard: function(cardHtml) { var that = this; that.container.append(cardHtml); }, edit: function() { var that = this, styles = TaskBoardColumn.styles; that.element.addClass(styles.editColumn); that._renderEditHeader(); }, getDataItem: function() { return this._dataItem; }, cards: function() { var that = this; return that.container.children(); }, template: (data) => `<div class="${encode(data.styles.headerText)} ${encode(data.styles.ellipsis)}">${kendo.getter(data.dataTextField)(data)}</div>` + `<span class="${encode(data.styles.spacer)}"></span>` + `${data.buttons}`, editTemplate: (data) => `<div class="${encode(data.styles.headerText)} ${encode(data.styles.ellipsis)}">` + `<span class="${encode(data.styles.textbox)}">` + `<input class="${encode(data.styles.input)}" placeholder="${encode(kendo.getter(data.dataTextField)(data) ? data.messages.editColumn : data.messages.newColumn)}" ${encode(kendo.attr("command"))}="SaveColumnCommand" value="${encode(kendo.getter(data.dataTextField)(data))}" />` + "</span>" + "</div>" + `<span class="${encode(data.styles.spacer)}"></span>` + `${data.buttons}`, actionButton: ({ styles, icon, spriteCssClass, text, command, options }) => kendo.html.renderButton(`<button class="${encode(styles.actionButton)}" title="${encode(text)}" ${encode(kendo.attr("command"))}="${encode(command)}" ${options ? encode(kendo.attr("options")) + "=" + encode(options) : ""}></button>`, { icon: encode(icon), iconClass: `k-button-icon ${encode(spriteCssClass)}`, fillMode: "flat" }), builtinButtons: { "editColumn": { name: "editColumn", icon: "pencil", text: "Edit Column", command: "EditColumnCommand", rules: "isEditable" }, "addCard": { name: "addCard", icon: "plus", text: "Add Card", command: "AddCardCommand", rules: "isEditable" }, "deleteColumn": { name: "deleteColumn", icon: "x", text: "Delete Column", command: "DeleteColumnCommand", rules: "isEditable" } }, defaultButtons: [ "editColumn", "addCard", "deleteColumn" ], _render: function() { var that = this, styles = TaskBoardColumn.styles, headerLabelId = kendo.guid(), element = $("<div class='" + styles.element + "'></div>").attr(kendo.attr("uid"), that._dataItem.uid).css({ width: that.options.width }).attr("role", "list").attr("aria-labelledby", headerLabelId), header = $("<div class='" + styles.header + "'></div>"), cardsContainer = $("<div class='" + styles.cardsContainer + "'></div>"), columnCards = $("<div class='" + styles.columnCards + "'></div>"); that.header = header.appendTo(element).attr("id", headerLabelId); that._renderHeader(); cardsContainer.appendTo(element); that.container = columnCards.appendTo(cardsContainer).addClass(that.sortableUniqueClass); that.element = element; }, _renderHeader: function() { var that = this, styles = TaskBoardColumn.styles, options = that.options, template = options.template ? options.template : that.template; that.header.append(kendo.template(template)(extend(true, {}, { styles, buttons: that._buildActionsHtml(), dataTextField: options.dataTextField }, that._dataItem))); }, _renderEditHeader: function() { var that = this, styles = TaskBoardColumn.styles, options = that.options, template = options.editTemplate ? options.editTemplate : that.editTemplate; that.header.html(""); that.header.append(kendo.template(template)(extend(true, {}, { styles, messages: options.messages, buttons: that._buildActionsHtml(), dataTextField: options.dataTextField }, that._dataItem))); setTimeout(function() { that.header.find("input").trigger("focus"); }, 0); that.header.find(DOT + styles.actions).addClass(styles.disabled); }, _buildActionsHtml: function() { var that = this, options = that.options, messages = options.messages, buttons = options.buttons || that.defaultButtons, styles = TaskBoardColumn.styles, html = "<div class='" + styles.actions + "'>"; for (var i = 0; i < buttons.length; i++) { var button = buttons[i]; button = $.isPlainObject(button) && Object.keys(button).length === 1 && button.name ? button.name : button; if (typeof button === "string") { button = extend(true, {}, that.builtinButtons[button] || { spriteCssClass: button, command: button + "Command" }, { text: messages[button] || button }); } else if ($.isPlainObject(button) && button.name) { button = extend(true, button, { text: messages[button.name] }); } if (!that._validateRules(button)) { continue; } button.spriteCssClass = button.spriteCssClass || ""; html += kendo.template(that.actionButton)(extend(true, {}, { styles, options: false }, button)); } html += "</div>"; return html; }, _validateRules: function(tool) { var that = this, states = that.options.states, rules = tool.rules ? tool.rules.split(";") : []; if (!rules.length) { return true; } for (var i = 0; i < rules.length; i++) { if (!states[rules[i]]) { return false; } } return true; }, _actionClick: function(ev) { var that = this, target = $(ev.target).closest("[" + kendo.attr("command") + "]"), command = target.attr(kendo.attr("command")), options = target.attr(kendo.attr("options")); options = typeof options === "string" ? { value: options } : options; if (!command) { return; } that.trigger(ACTION, { command, options: extend({ target }, options) }); }, _textboxBlur: function(ev) { var that = this, target = $(ev.target).closest("[" + kendo.attr("command") + "]"), command = target.attr(kendo.attr("command")), options = target.attr(kendo.attr("options")); options = typeof options === "string" ? { value: options } : options; if (!command) { return; } that.trigger(ACTION, { command, options: extend({ target }, options) }); }, _attachEvents: function() { var that = this; that.header.on(CLICK + NS, DOT + TaskBoardColumn.styles.actionButton, that._actionClick.bind(that)); that.header.on(BLUR + NS, DOT + TaskBoardColumn.styles.input, that._textboxBlur.bind(that)); that.header.on("keyup" + NS, DOT + TaskBoardColumn.styles.input, function(ev) { if (ev.keyCode === kendo.keys.ENTER) { that._textboxBlur(ev); } }); if (that.container) { that.container.on(CLICK + NS, DOT + TaskBoardColumn.styles.card + " [" + kendo.attr("command") + "]", that._actionClick.bind(that)); that.container.on(CLICK + NS, DOT + TaskBoardColumn.styles.card + " a." + TaskBoardColumn.styles.cardTitle, preventDefault); } }, _initSortable: function() { var that = this, isRtl = that.options.states.isRtl, dirClass = isRtl ? " k-rtl" : "", container = that.container; if (!that.container) { return; } that.sortable = new ui.Sortable(container, { ignore: DOT + TaskBoardColumn.styles.ignoreDrag + " *", connectWith: DOT + that.sortableUniqueClass, filter: DOT + TaskBoardColumn.styles.card, hint: function(element) { return element.clone().addClass(TaskBoardColumn.styles.dragHint + " " + TaskBoardColumn.styles.grabbingCursor + dirClass).css({ width: element[0].offsetWidth, height: element[0].offsetHeight, ...preventTextSelectionStyles(true) }); }, placeholder: function(element) { return $("<div></div>").addClass(TaskBoardColumn.styles.dragPlaceholder + " " + TaskBoardColumnStyles.card).css({ height: element[0].offsetHeight }); }, move: that._move.bind(that), start: that._start.bind(that), end: that._end.bind(that), change: that._change.bind(that) }); that.sortable.draggable.userEvents.unbind("select"); that.sortable.draggable.userEvents.bind("select", that._select.bind(that)); }, _select: function(ev) { var ignoreDragSelectors = TaskBoardColumn.ignoreDragSelectors; var target = $(ev.event.target); for (var i = 0; i < ignoreDragSelectors.length; i++) { if (target.is(ignoreDragSelectors[i])) { ev.preventDefault(); break; } } }, _move: function(ev) { extend(ev, { columnElement: ev.sender.element.parents(DOT + TaskBoardColumn.styles.element) }); this.trigger(MOVE, ev); }, _start: function(ev) { extend(ev, { columnElement: ev.sender.element.parents(DOT + TaskBoardColumn.styles.element) }); this.trigger(MOVE_START, ev); this.element.closest(".k-taskboard").css(preventTextSelectionStyles(true)); }, _end: function(ev) { const that = this; let moveArgs = { command: "MoveCardCommand", options: ev }; extend(ev, { columnElement: ev.sender.element.parents(DOT + TaskBoardColumn.styles.element) }); this.trigger(MOVE_END, ev); this.element.closest(".k-taskboard").css(preventTextSelectionStyles(false)); if (ev.action === "receive" && ev.newIndex >= 0 && that.trigger(ACTION, moveArgs)) { ev.preventDefault(); return; } else if (ev.action === "sort" && ev.newIndex !== ev.oldIndex) { moveArgs.options.newIndex += 1; that.trigger(ACTION, moveArgs); } }, _change: function(ev) { var that = this; extend(ev, { columnElement: ev.sender.element.parents(DOT + TaskBoardColumn.styles.element) }); that.trigger(CHANGE, ev); }, destroy: function() { var that = this; that.header.off(NS); that.container.off(NS); if (that.sortable) { that.sortable.destroy(); } } }); var TaskBoardNewColumn = TaskBoardColumn.extend({ _render: function() { var that = this, styles = TaskBoardColumn.styles, element = $("<div class='" + styles.element + "'></div>").addClass(styles.newColumn).attr(kendo.attr("uid"), that._dataItem.uid), header = $("<div class='" + styles.header + "'></div>"); that.header = header.appendTo(element); that._renderEditHeader(); that.element = element; } }); extend(kendo.ui, { taskboard: { Column: TaskBoardColumn, NewColumn: TaskBoardNewColumn } }); extend(true, kendo.ui.taskboard.Column, { styles: TaskBoardColumnStyles, ignoreDragSelectors: ["img"] }); })(window.kendo.jQuery); //#endregion //#region ../src/taskboard/card.js (function($, undefined) { var kendo = window.kendo, Observable = kendo.Observable, encode = kendo.htmlEncode, extend = $.extend; var TaskBoardCardStyles = { element: "k-taskboard-card", card: "k-card", header: "k-card-header", hbox: "k-hbox", title: "k-card-title", link: "k-link", spacer: "k-spacer", button: "k-button", cardMenuButton: "k-taskboard-card-menu-button", body: "k-card-body", actionsIcon: "more-vertical", moveCursor: "k-cursor-move", categoryBorder: "k-taskboard-card-category", headerActions: "k-card-header-actions", disabled: "k-disabled" }; var TaskBoardCard = Observable.extend({ init: function(options, dataItem, resources) { var that = this; that._dataItem = dataItem; that.resources = resources; that.options = extend(true, {}, options); that._render(); Observable.fn.init.call(that); }, contentTemplate: (data) => `<div class="${encode(data.styles.header)} ${encode(data.styles.hbox)}">` + `<a class="${encode(data.styles.title)} ${encode(data.styles.link)}" href="#" ${data.selectable ? kendo.attr("command") + "=SelectCardCommand" : ""}>${encode(kendo.getter(data.dataTitleField)(data))}</a>` + `<span class="${encode(data.styles.spacer)}"></span>` + `${data.cardMenuButton}` + "</div>" + `<div class="${encode(data.styles.body)}"><p>${encode(kendo.getter(data.dataDescriptionField)(data))}</p></div>`, cardMenuButtonTemplate: ({ styles }) => `<div class="${encode(styles.headerActions)}">${kendo.html.renderButton($(`<button aria-label="menu" class="${encode(styles.cardMenuButton)}"></button>`), { icon: encode(styles.actionsIcon), fillMode: "flat" })}</div>`, _render: function() { var that = this, options = that.options, styles = TaskBoardCard.styles, template = options.template || that.contentTemplate, element = $("<div class='" + styles.element + " " + styles.card + " " + styles.moveCursor + "'></div>"), cardMenuButtonTemplate = options.cardMenu ? that.cardMenuButtonTemplate : "", resources = that._resources(that._dataItem), borderDir = options.states.isRtl ? "borderRightColor" : "borderLeftColor", categoryColor; element.attr(kendo.attr("uid"), that._dataItem.uid).attr("aria-disabled", !options.states.isDisabled).attr("role", "listitem").toggleClass(styles.disabled, options.states.isDisabled); categoryColor = resources[options.dataCategoryField] && resources[options.dataCategoryField].color || that._dataItem.get(options.dataCategoryField); if (categoryColor) { element.addClass(styles.categoryBorder).css(borderDir, categoryColor); } element.append(kendo.template(template)(extend(true, {}, { styles, cardMenuButton: kendo.template(cardMenuButtonTemplate)({ styles }), selectable: options.states.isSelectable, resources, dataTitleField: options.dataTitleField, dataDescriptionField: options.dataDescriptionField }, that._dataItem))); that.element = element; }, _resources: function(card) { var that = this, resources = {}; if (!that.resources) { return resources; } for (var key in that.resources) { var resource = that.resources[key]; var field = resource.field; var cardResources = kendo.getter(field)(card); if (!cardResources) { continue; } if (!resource.multiple) { cardResources = [cardResources]; } var data = resource.dataSource.view(); for (var resourceIndex = 0; resourceIndex < cardResources.length; resourceIndex++) { var cardResource = null; var value = cardResources[resourceIndex]; if (!resource.valuePrimitive) { value = kendo.getter(resource.dataValueField)(value); } for (var dataIndex = 0; dataIndex < data.length; dataIndex++) { if (data[dataIndex].get(resource.dataValueField) == value) { cardResource = data[dataIndex]; break; } } if (cardResource !== null) { var resourceColor = kendo.getter(resource.dataColorField)(cardResource); var result = { field: resource.field, title: resource.title, name: resource.name, text: kendo.getter(resource.dataTextField)(cardResource), value, color: resourceColor }; if (resource.multiple) { if (resources[resource.field]) { resources[resource.field].push(result); } else { resources[resource.field] = [result]; } } else { resources[resource.field] = result; } } } } return resources; }, _buildTemplate: function() { var that = this, options = that.options, headerTemplate = kendo.format(that.headerTemplate, options.dataTitleField), bodyTemplate = kendo.format(that.bodyTemplate, options.dataDescriptionField); return headerTemplate + bodyTemplate; } }); extend(kendo.ui.taskboard, { Card: TaskBoardCard }); extend(true, kendo.ui.taskboard.Card, { styles: TaskBoardCardStyles }); })(window.kendo.jQuery); //#endregion //#region ../src/taskboard/cardmenu.js (function($, undefined) { var kendo = window.kendo, extend = $.extend, template = kendo.template, ContextMenu = kendo.ui.ContextMenu, encode = kendo.htmlEncode, ACTION = "action"; var TaskBoardCardMenu = ContextMenu.extend({ init: function(element, options) { var that = this; ContextMenu.fn.init.call(that, element, options); that._overrideTemplates(); that._extendItems(); that.bind("select", that._onSelect.bind(that)); that.bind("open", that._onOpen.bind(that)); that.bind("activate", that._focus.bind(that)); }, _overrideTemplates: function() { this.templates.sprite = ({ icon, spriteCssClass }) => `${icon || spriteCssClass ? kendo.ui.icon({ icon: encode(icon || ""), iconClass: encode(spriteCssClass || "") }) : ""}`; }, defaultItems: { "editCard": { name: "editCard", text: "Edit card", icon: "pencil", command: "EditCardCommand", rules: "isEditable" }, "deleteCard": { name: "deleteCard", text: "Delete card", icon: "trash", command: "DeleteCardCommand", rules: "isEditable" } }, events: ContextMenu.fn.events.concat([ACTION]), _extendItems: function() { var that = this, items = that.options.items, item, isBuiltInTool; if (items && items.length) { for (var i = 0; i < items.length; i++) { item = items[i]; isBuiltInTool = $.isPlainObject(item) && Object.keys(item).length === 1 && item.name; if (isBuiltInTool) { item = item.name; } if ($.isPlainObject(item)) { that._append(item); } else if (that.defaultItems[item]) { item = that.defaultItems[item]; that._append(item); } else if (typeof item === "string") { item = { name: item, text: item, spriteCssClass: item, command: item + "Command" }; that._append(item); } } } else { for (var key in that.defaultItems) { item = that.defaultItems[key]; that._append(item); } } }, _append: function(item) { var that = this; that._extendItem(item); if (that._validateRules(item)) { that.append(item); } }, _extendItem: function(item) { var that = this, messages = that.options.messages, attr = {}; attr[kendo.attr("command")] = item.command; if (item.options) { attr[kendo.attr("options")] = item.options; } extend(item, { text: messages[item.name], icon: item.icon || "", spriteCssClass: item.spriteCssClass || "", attr, uid: kendo.guid() }); }, _validateRules: function(tool) { var that = this, states = that.options.states, rules = tool.rules ? tool.rules.split(";") : []; if (!rules.length) { return true; } for (var i = 0; i < rules.length; i++) { if (!states[rules[i]]) { return false; } } return true; }, _onSelect: function(ev) { var command = $(ev.item).attr(kendo.attr("command")), options = $(ev.item).attr(kendo.attr("options")), target = $(ev.target); options = typeof options === "string" ? { value: options } : options; if (!command) { return; } this.action({ command, options: extend({ target }, options) }); }, _onOpen: function(ev) { var menu = ev.sender, items = menu.options.items; if (!items && $.isEmptyObject(this.defaultItems)) { ev.preventDefault(); } }, _focus: function(ev) { if (ev.sender) { ev.sender.element.trigger("focus"); } }, action: function(args) { this.trigger(ACTION, args); } }); extend(kendo.ui.taskboard, { CardMenu: TaskBoardCardMenu }); })(window.kendo.jQuery); //#endregion //#region ../src/taskboard/commands.js (function($, undefined) { var kendo = window.kendo, extend = $.extend, isPlainObject = $.isPlainObject, Class = kendo.Class; var TaskBoardCommand = Class.extend({ init: function(options) { this.options = options; this.taskboard = options.taskboard; }, _confirm: function(title, content, okText, cancel) { var that = this, taskboard = that.taskboard, taskboardOptions = taskboard.options, confirm; if (isPlainObject(taskboardOptions.editable) && taskboardOptions.editable.confirmation === false) { var fakePromise = $.Deferred(); fakePromise.resolve(); return fakePromise; } confirm = $("<div></div>").kendoConfirm(extend({}, { title, content, messages: { okText, cancel }, buttonLayout: "normal" })).data("kendoConfirm"); confirm.open(); setTimeout(function() { confirm.element.trigger("focus"); }); return confirm.result; } }); var AddColumnCommand = TaskBoardCommand.extend({ exec: function() { var that = this, taskboard = that.taskboard; if (taskboard.trigger("editColumn", { column: null })) { return; } taskboard.columns().each(function(index, column) { taskboard.enableByColumn(column, false); }); taskboard.addColumn(); } }); var EditColumnCommand = TaskBoardCommand.extend({ exec: function() { var that = this, options = that.options, taskboard = that.taskboard; if (taskboard.trigger("editColumn", { column: options.column })) { return; } taskboard.columns().each(function(index, column) { taskboard.enableByColumn(column, false); }); taskboard.editColumn(options.columnElement); } }); var DeleteColumnCommand = TaskBoardCommand.extend({ exec: function() { var that = this, options = that.options, taskboard = that.taskboard, taskboardOptions = taskboard.options, messages = taskboardOptions.messages, columnDS = taskboard.columnsDataSource; var result = that._confirm(messages.deleteColumn, messages.deleteColumnConfirm, messages["delete"], messages.cancel); result.done(function() { if (taskboard.trigger("deleteColumn", { column: options.column })) { taskboard.dataSource.cancelChanges(); return; } columnDS.remove(options.column); columnDS.sync(); }); } }); var SaveColumnCommand = TaskBoardCommand.extend({ exec: function() { var that = this, options = that.options, taskboard = that.taskboard, columnSettings = taskboard.options.columnSettings, columnDS = taskboard.columnsDataSource, column = options.column, text = options.target.val(); if (taskboard.trigger("saveColumn", { column: options.column })) { taskboard.dataSource.cancelChanges(); return; } column.set(columnSettings.dataTextField, text); columnDS.sync(); } }); var CancelEditColumnCommand = TaskBoardCommand.extend({ exec: function() { var that = this, taskboard = that.taskboard, columnDS = taskboard.columnsDataSource; columnDS.cancelChanges(); } }); var MoveFocusCommand = TaskBoardCommand.extend({ exec: function() { var that = this, taskboard = that.taskboard, options = that.options, cardElement = options.cardElement, columnElement = options.columnElement, columns = taskboard.columns(), currentColumnIndex = columnElement.index(), direction = options.value, columnIndex, column; switch (direction) { case "left": columnIndex = Math.max(0, currentColumnIndex - 1); break; case "right": columnIndex = Math.min(columns.length - 1, currentColumnIndex + 1); break; default: columnIndex = currentColumnIndex; break; } that.columns = columns; that.columnIndex = columnIndex; that.column = column = taskboard._getColumnByElement(columns.eq(columnIndex)); that.cards = column.cards(); if (cardElement) { that._moveFromCardFocus(direction, cardElement); } else if (columnElement && direction === "down") { that.cards.eq(0).trigger("focus"); } else { columns.eq(columnIndex).trigger("focus"); } }, _moveFromCardFocus: function(direction, cardElement) { var that = this, currentIndex = cardElement.index(), focusCard, index, seekFocusableCard = false; switch (direction) { case "up": index = currentIndex - 1; break; case "down": index = currentIndex + 1; break; default: seekFocusableCard = true; index = currentIndex; break; } focusCard = that.cards.eq(index); if (!focusCard.length && seekFocusableCard) { focusCard = that._getFocusableCard(index, direction); } if (index >= 0) { focusCard.trigger("focus"); } else { that.options.columnElement.focus(); } }, _getFocusableCard: function(index, direction) { var that = this, lastIndex = that.cards.length - 1, focusable = that.cards.eq(Math.min(index, lastIndex)); if (focusable.length) { return focusable; } if (that.columnIndex <= 0 || that.columnIndex >= that.columns.length - 1) { return; } switch (direction) { case "left": that.columnIndex = Math.max(0, that.columnIndex - 1); break; case "right": that.columnIndex = Math.min(that.columns.length - 1, that.columnIndex + 1); break; } that.column = that.taskboard._getColumnByElement(that.columns.eq(that.columnIndex)); that.cards = that.column.cards(); return that._getFocusableCard(index, direction); } }); var TaskBoardBaseCardCommand = TaskBoardCommand.extend({ _updateOrder: function(column, currentIndex) { var that = this, taskboard = that.taskboard, taskBoardOptions = taskboard.options, dataOrderField = taskBoardOptions.dataOrderField, options = that.options, currentCard = options.card, cardIndex = options.cardElement ? options.cardElement.index() : column.cards().length, newIndex = currentIndex, cards = column.cards().map(function(idx, card) { return taskboard.dataItem(card); }), prevCard = cards[cardIndex - 1], nextCard; currentCard.set(dataOrderField, newIndex); if (prevCard && prevCard.get(dataOrderField) >= currentCard.get(dataOrderField)) { currentCard.set(dataOrderField, prevCard.get(dataOrderField) + 1); } for (var i = newIndex + 1; i < cards.length; i++) { nextCard = cards[i]; if (nextCard.get(dataOrderField) <= currentCard.get(dataOrderField)) { nextCard.set(dataOrderField, currentCard.get(dataOrderField) + 1); currentCard = nextCard; } else { break; } } } }); var SelectCardCommand = TaskBoardCommand.extend({ exec: function() { var that = this, taskboard = that.taskboard, options = that.options, cardElement = options.cardElement; taskboard._select(cardElement, true); cardElement.focus(); } }); var SaveChangesCommand = TaskBoardBaseCardCommand.extend({ exec: function() { var that = this, taskboard = that.taskboard, taskBoardOptions = taskboard.options, options = that.options, receivedStatus = options.card.get(taskBoardOptions.dataStatusField), targetColumn = taskboard._getColumn(receivedStatus), activeElm = $(document.activeElement); if (activeElm.length) { activeElm.trigger("change"); } if (taskboard.pane && taskboard.pane.form && !taskboard.pane.form.validate()) { return; } if (taskboard.trigger("saveCard", { card: options.card })) { taskboard.dataSource.cancelChanges(); return; } if (targetColumn && taskBoardOptions.dataOrderField) { that._updateOrder(targetColumn, options.card.get(taskBoardOptions.dataOrderField)); } taskboard.dataSource.sync().then(function() { taskboard.columns().eq(0).trigger("focus"); }); } }); var DeleteCardCommand = TaskBoardCommand.extend({ exec: function() { var that = this, taskboard = that.taskboard, messages = taskboard.options.messages, options = that.options; var result = that._confirm(messages.deleteCard, messages.deleteCardConfirm, messages["delete"], messages.cancel); result.done(function() { if (taskboard.trigger("deleteCard", { card: options.card })) { taskboard.dataSource.cancelChanges(); return; } taskboard.dataSource.remove(options.card); taskboard.dataSource.sync().then(function() { taskboard.columns().eq(0).trigger("focus"); }); }).fail(function() { options.cardElement.trigger("focus"); }); } }); var MoveCardCommand = TaskBoardBaseCardCommand.extend({ exec: function() { var that = this, taskboard = that.taskboard, taskBoardOptions = taskboard.options, columnSettings = taskBoardOptions.columnSettings, options = that.options, column = options.column, card = options.card, newIndex = options.newIndex, receivedStatus = column.get(columnSettings.dataStatusField); card.set(taskBoardOptions.dataStatusField, receivedStatus); if (taskBoardOptions.dataOrderField) { that._updateOrder(taskboard._getColumn(receivedStatus), newIndex); } taskboard.dataSource.sync(); } }); var EditCardCommand = TaskBoardCommand.extend({ exec: function() { var that = this, taskboard = that.taskboard, options = that.options; if (taskboard.trigger("editCard", { card: options.card })) { return; } taskboard._openPane(extend({ pane: "Edit" }, options)); } }); var AddCardCommand = TaskBoardCommand.extend({ exec: function() { var that = this, taskboard = that.taskboard, options = that.options; if (taskboard.trigger("editCard", { card: null })) { return; } taskboard.dataSource.cancelChanges(); taskboard._openPane(extend({ pane: "Create" }, options)); } }); var OpenPaneCommand = TaskBoardCommand.extend({ exec: function() { var that = this, options = that.options, taskboard = that.taskboard; taskboard._openPane({ pane: options.value, card: options.card, cardElement: options.cardElement, column: options.column, columnElement: options.columnElement }); } }); var ClosePaneCommand = TaskBoardCommand.extend({ exec: function() { var that = this, taskboard = that.taskboard; taskboard._closePane(); taskboard.dataSource.cancelChanges(); } }); var SearchCommand = TaskBoardCommand.extend({ exec: function() { var that = this, value = that.options.value, taskboard = that.taskboard, taskboardOptions = taskboard.options, searchOptions = taskboardOptions.search, operator = searchOptions.operator, fields = searchOptions && searchOptions.fields || [taskboardOptions.dataTitleField, taskboardOptions.dataDescriptionField], filters; filters = that._buildFilters(fields, operator, value); taskboard.dataSource.filter(filters); }, _buildFilters: function(fields, operator, value) { var filters = fields.map(function(field) { return { field, operator, value }; }); return { logic: "or", filters }; } }); extend(kendo.ui.taskboard, { Command: TaskBoardCommand, commands: { AddColumnCommand, EditColumnCommand, DeleteColumnCommand, SaveColumnCommand, CancelEditColumnCommand, OpenPaneCommand, ClosePaneCommand, SelectCardCommand, MoveFocusCommand, SaveChangesCommand, DeleteCardCommand, MoveCardCommand, EditCardCommand, AddCardCommand, SearchCommand } }); })(window.kendo.jQuery); //#endregion //#region ../src/taskboard/pane.js (function($, undefined) { var kendo = window.kendo, extend = $.extend, Observable = kendo.Observable, encode = kendo.htmlEncode, Form = kendo.ui.Form, DOT = ".", NS = DOT + "kendoTaskBoardPane", ACTION = "action", CLICK = "click"; var TaskBoardPaneStyles = { element: "k-taskboard-pane", edit: "k-taskboard-edit-pane", preview: "k-taskboard-preview-pane", header: "k-taskboard-pane-header", headerText: "k-taskboard-pane-header-text", spacer: "k-spacer", headerActions: "k-taskboard-pane-header-actions", content: "k-taskboard-pane-content", footerActions: "k-taskboard-pane-actions", footerActionButtons: "k-actions k-hstack k-justify-content-start", primaryButton: "k-button-primary" }; var TaskBoardPane = Observable.extend({ init: function(taskboard, options, dataItem, resources) { var that = this; that.taskboard = taskboard; that._dataItem = dataItem; that.resources = resources; that.options = extend(true, {}, options); that._render(); that.element.on(CLICK + NS, "[" + kendo.attr("command") + "]", that._commandClick.bind(that)); Observable.fn.init.call(that); }, headerTemplate: ({ styles, messages, fieldFormat }) => `<div class="${encode(styles.headerText)}">${fieldFormat}</div>` + `<span class="${encode(styles.spacer)}"></span>` + `<div class="${encode(styles.headerActions)}">` + kendo.html.renderButton(`<button title="${encode(messages.close)}" ${encode(kendo.attr("command"))}="ClosePaneCommand"></button>`, { icon: "x", iconClass: "k-button-icon", fillMode: "flat" }) + "</div>", buttonTemplate: ({ styles, icon, spriteCssClass, text, fillMode, themeColor, command, options }) => kendo.html.renderButton(`<button title="${encode(text)}" ${encode(kendo.attr("command"))}="${encode(command)}" ${encode(kendo.attr("options"))}="${encode(options)}">` + `${encode(text)}` + "</button>", { icon: encode(icon), iconClass: `k-button-icon ${encode(spriteCssClass)}`, fillMode, themeColor }), contentTemplate: () => ``, builtinButtons: { "edit": { name: "edit", icon: "pencil", text: "Edit", themeColor: "primary", command: "EditCardCommand", rules: "isEditable" }, "delete": { name: "delete", icon: "trash", text: "Delete", command: "DeleteCardCommand", rules: "isEditable", fillMode: "flat", themeColor: "primary" }, "cancel": { name: "cancel", icon: "cancel-outline", text: "Cancel", command: "ClosePaneCommand" }, "saveChanges": { name: "saveChanges", icon: "save", text: "Save", command: "SaveChangesCommand", themeColor: "primary", rules: "isEditable" }, "create": { name: "create", icon: "save", text: "Create", command: "SaveChangesCommand", themeColor: "primary", rules: "isEditable" } }, defaultButtons: [], _render: function() { var that = this, styles = TaskBoardPane.styles, element = $("<div class='" + styles.element + "'></div>"), header = $("<div class='" + styles.header + "'></div>"), content = $("<div class='" + styles.content + "'></div>"), buttonsContainer = $("<div class='" + styles.footerActions + " " + styles.footerActionButtons + "'></div>"); that.header = header.appendTo(element); that._renderHeader(); that.content = content.appendTo(element); that._renderContent(); that.buttonsContainer = buttonsContainer.appendTo(element); that.buttonsContainer.append(that._buildButtonsHtml()); that.element = element; }, _renderHeader: function() { var that = this, styles = TaskBoardPane.styles, options = that.options, messages = options.messages, headerTemplate = options.headerTemplate ? options.headerTemplate : that.headerTemplate, resources = that._resources(that._dataItem); that.header.append(kendo.template(headerTemplate)(extend(true, {}, { styles, messages, resources, fieldFormat: that._buildHeaderTemplate(that._dataItem) }, that._dataItem))); }, _buildHeaderTemplate: function(dataItem) { var that = this; return encode(kendo.getter(that.options.dataTitleField)(dataItem)); }, _renderContent: function() { var that = this, styles = TaskBoardPane.styles, options = that.options, messages = options.messages, contentTemplate = options.template || that.contentTemplate, resources = that._resources(that._dataItem); that.content.append(kendo.template(contentTemplate)(extend(true, {}, { styles, messages, resources }, that._dataItem))); }, _resources: function(card) { var that = this, resources = {}; if (!that.resources) { return resources; } for (var key in that.resources) { var resource = that.resources[key]; var field = resource.field; var cardResources = kendo.getter(field)(card); if (!cardResources) { continue; } if (!resource.multiple) { cardResources = [cardResources]; } var data = resource.dataSource.view(); for (var resourceIndex = 0; resourceIndex < cardResources.length; resourceIndex++) { var cardResource = null; var value = cardResources[resourceIndex]; if (!resource.valuePrimitive) { value = kendo.getter(resource.dataValueField)(value); } for (var dataIndex = 0; dataIndex < data.length; dataIndex++) { if (data[dataIndex].get(resource.dataValueField) == value) { cardResource = data[dataIndex]; break; } } if (cardResource !== null) { var resourceColor = kendo.getter(resource.dataColorField)(cardResource); var result = { field: resource.field, title: resource.title, name: resource.name, text: kendo.getter(resource.dataTextField)(cardResource), value, color: resourceColor }; if (resource.multiple) { if (resources[resource.field]) { resources[resource.field].push(result); } else { resources[resource.field] = [result]; } } else { resources[resource.field] = result; } } } } return resources; }, _buildButtonsHtml: function() { var that = this, options = that.options, messages = options.messages, buttons = options.buttons || that.defaultButtons, styles = TaskBoardPane.styles, html = ""; for (var i = 0; i < buttons.length; i++) { var button = buttons[i]; button = $.isPlainObject(button) && Object.keys(button).length === 1 && button.name ? button.name : button; if (typeof button === "string") { if (button === "spacer") { html += "<span class=\"k-spacer\"></span>"; continue; } button = extend(true, {}, that.builtinButtons[button] || { spriteCssClass: button, command: button + "Command" }, { text: messages[button] || button }); } else if ($.isPlainObject(button) && button.name) { button = extend(true, button, { text: messages[button.name] }); } if (!that._validateRules(button)) { continue; } button.spriteCssClass = button.spriteCssClass || ""; html += kendo.template(that.buttonTemplate)(extend(true, {}, { styles, messages, primary: false, options: null }, button)); } return html; }, _commandClick: function(ev) { var that = this, target = $(ev.target).closest("[" + kendo.attr("command") + "]"), command = target.attr(kendo.attr("command")), options = target.attr(kendo.attr("options")), card = that._dataItem; options = typeof options === "string" ? { value: options } : options; if (!command) { return; } that.trigger(ACTION, { command, options: extend({ card }, options) }); }, _validateRules: function(tool) { var that = this, states = that.options.states, rules = tool.rules ? tool.rules.split(";") : []; if (!rules.length) { return true; } for (var i = 0; i < rules.length; i++) { if (!states[rules[i]]) { return false; } } return true; }, destroy: function() { var that = this; that.element.off(NS); that.element.remove(); } }); var TaskBoardPreviewPane = TaskBoardPane.extend({ init: function(taskboard, options, dataItem, resources) { var that = this; options = extend({}, options.previewPane, { dataTitleField: options.dataTitleField, dataDescriptionField: options.dataDescriptionField, messages: options.messages, states: options.states }); that.contentTemplate = (data) => encode(kendo.getter(options.dataDescriptionField)(data)); TaskBoardPane.fn.init.call(that, taskboard, options, dataItem, resources); that.element.addClass(TaskBoardPane.styles.preview); }, defaultButtons: [ "edit", "spacer", "delete" ] }); var TaskBoardEditPane = TaskBoardPane.extend({ init: function(taskboard, options, dataItem) { var that = this; options = extend({}, options.editable, { dataTitleField: options.dataTitleField, dataDescriptionField: options.dataDescriptionField, messages: options.messages, states: options.states }); that.formSettings = extend(that.formSettings, { items: [{ field: options.dataTitleField, label: options.messages[options.dataTitleField] }, { field: options.dataDescriptionField, label: options.messages[options.dataDescriptionField] }] }, options.form); TaskBoardPane.fn.init.call(that, taskboard, options, dataItem); that.element.addClass(TaskBoardPane.styles.edit); }, defaultButtons: ["saveChanges", "cancel"], formSettings: { buttonsTemplate: () => "" }, _buildHeaderTemplate: function(dataItem) { var that = this; return `${that.options.messages.edit} ${encode(kendo.getter(that.options.dataTitleField)(dataItem))}`; }, _renderContent: function() { var that = this, options = that.options, styles = TaskBoardPane.styles, formSettings = options.form || that.formSettings, formLabelId = kendo.guid(), element = $("<div></div>").attr("role", "form").attr("aria-labelledby", formLabelId), focusFirst = options.form && options.form.focusFirst !== undefined ? options.form.focusFirst : true; that.header.find(DOT + styles.headerText).attr("id", formLabelId); that.content.append(element); that.form = new Form(element, extend({}, formSettings, { formData: that._dataItem, focusFirst })); } }); var TaskBoardCreatePane = TaskBoardEditPane.extend({ init: function(taskboard, options, dataItem, resources, column) { var that = this, columnStatusField = options.columnSettings.dataStatusField, firstColumn = taskboard.columnsDataSource.view().at(0), status = column ? column[columnStatusField] : firstColumn[columnStatusField], clone = extend({}, dataItem); dataItem = taskboard.dataSource.add(); dataItem.set(options.dataStatusField, status); for (var key in clone) { dataItem.set(key, clone[key]); } TaskBoardEditPane.fn.init.call(that, taskboard, options, dataItem, resources); }, _buildHeaderTemplate: function(dataItem) { var that = this; return that.options.messages.createNewCard; }, defaultButtons: ["create", "cancel"] }); extend(kendo.ui.taskboard, { Pane: TaskBoardPane, panes: { Preview: TaskBoardPreviewPane, Edit: TaskBoardEditPane, Create: TaskBoardCreatePane } }); extend(true, kendo.ui.taskboard.Pane, { styles: TaskBoardPaneStyles }); })(window.kendo.jQuery); //#endregion //#region ../src/taskboard/keyboard.js (function($, undefined) { var kendo = window.kendo, Observable = kendo.Observable, extend = $.extend, DOT = ".", NS = DOT + "kendoKeyboardManager", KEYDOWN = "keydown", ACTION = "action"; var Keyboard = Observable.extend({ init: function(element) { var that = this; that.register = {}; that.element = element; that._attachEvents(); Observable.fn.init.call(that); }, registerShortcut: function(selector, shortcut, options) { var that = this; if (!that.register[selector]) { that.register[selector] = []; } if (shortcut.keyCode && isNaN(shortcut.keyCode) && shortcut.keyCode.toUpperCase) { shortcut.keyCode = shortcut.keyCode.toUpperCase().charCodeAt(0); } that.register[selector].push({ shortcut: extend({ keyCode: null, ctrlKey: false, shiftKey: false, altKey: false }, shortcut), options }); }, _attachEvents: function() { var that = this, handler = that._handler.bind(that); that.element.on(KEYDOWN + NS, handler); }, _handler: function(ev) { var that = this, target = $(ev.target), shortcuts, action; for (var selector in that.register) { if (target.is(selector)) { shortcuts = that.register[selector]; action = that._getAction(shortcuts, ev); if (action) { that._trigger(action, ev); break; } } } }, _trigger: function(action, ev) { var that = this, target = $(ev.target); if (action.command) { that.trigger(ACTION, extend({}, ev, { command: action.command, options: extend({}, { target }, action.options) })); } if (action.handler) { action.handler(ev); } }, _getAction: function(shortcuts, ev) { var that = this; for (var i = 0; i < shortcuts.length; i++) { if (that._compareShortcut(shortcuts[i].shortcut, ev)) { return shortcuts[i].options; } } }, _compareShortcut: function(shortcut, ev) { var that = this; for (var key in shortcut) { var result = false; switch (key) { case "ctrlKey": result = shortcut[key] !== that._getShortcutModifier(ev); break; default: result = shortcut[key] !== ev[key]; break; } if (result) { return false; } } return true; }, _getShortcutModifier: function(ev) { var mac = navigator.platform.toUpperCase().indexOf("MAC") >= 0; return mac ? ev.metaKey : ev.ctrlKey; }, destroy: function() { var that = this; that.element.off(NS); } }); extend(kendo.ui.taskboard, { KeyboardManager: Keyboard }); })(window.kendo.jQuery); //#endregion //#region ../src/kendo.taskboard.js const __meta__ = { id: "taskboard", name: "TaskBoard", category: "web", description: "The TaskBoard widget displays cards.", depends: [ "data", "sortable", "dialog", "form", "menu", "toolbar", "textbox" ] }; var TaskBoardStyles = { wrapper: "k-taskboard", header: "k-taskboard-header", content: "k-taskboard-content", toolbar: "k-taskboard-toolbar", columnsContainer: "k-taskboard-columns-container", card: "k-taskboard-card", column: "k-taskboard-column", selected: "k-selected", disabled: "k-disabled", ignoreDrag: "k-taskboard-drag-ignore" }; var preventDefault = function(ev) { ev.preventDefault(); }; (function($, undefined) { var kendo = window.kendo, Widget = kendo.ui.Widget, DataSource = kendo.data.DataSource, extend = $.extend, ui = kendo.ui, isArray = Array.isArray, isPlainObject = $.isPlainObject, GENERIC = "_generic", DOT = ".", MOVE = "move", MOVE_START = "moveStart", MOVE_END = "moveEnd", SELECT = "select", EXECUTE = "execute", ACTION = "action", CHANGE = "change", CLICK = "click", TOGGLE = "toggle", ERROR = "change", DATABINDING = "dataBinding", DATABOUND = "dataBound", EDIT_CARD = "editCard", SAVE_CARD = "saveCard", DELETE_CARD = "deleteCard", EDIT_COLUMN = "editColumn", SAVE_COLUMN = "saveColumn", DELETE_COLUMN = "deleteColumn", COLUMNSDATABINDING = "columnsDataBinding", COLUMNSDATABOUND = "columnsDataBound"; var TaskBoard = Widget.extend({ init: function(element, options) { var that = this; Widget.fn.init.call(that, element, options); element = that.wrapper = that.element; that._sortableIdentifier = that.element.attr("id") || kendo.guid(); that._wrapper(); that._columnsDataSource(); that._initResources(); that._dataSource(); that._initToolbar(); that._initCardMenu(); that._initKeyboard(); if (that.options.autoBind) { that.load(); } kendo.notify(that); }, events: [ MOVE, MOVE_START, MOVE_END, CHANGE, EXECUTE, SELECT, DATABINDING, DATABOUND, COLUMNSDATABINDING, COLUMNSDATABOUND, EDIT_CARD, SAVE_CARD, DELETE_CARD, EDIT_COLUMN, SAVE_COLUMN, DELETE_COLUMN ], options: { name: "TaskBoard", autoBind: true, height: 600, width: "100%", toolbar: true, dataStatusField: "status", dataTitleField: "title", dataDescriptionField: "description", dataCategoryField: "category", dataOrderField: null, cardMenu: true, editable: true, selectable: true, reorderable: true, previewPane: true, search: { operator: "contains" }, dataSource: [], columns: [], columnSettings: { dataStatusField: "status", dataTextField: "text", dataOrderField: null }, resources: [], messages: { edit: "Edit", createNewCard: "Create new card", create: "Create", search: "Search", previewCard: "Preview card", addCard: "Add card", editCard: "Edit card", deleteCard: "Delete Card", addColumn: "Add column", editColumn: "Edit column", deleteColumn: "Delete column", close: "Close", cancel: "Cancel", "delete": "Delete", saveChanges: "Save changes", title: "Title:", description: "Description:", newColumn: "New column", deleteColumnConfirm: "Are you sure you want to delete this column?", deleteCardConfirm: "Are you sure you want to delete this card?" } }, defaultTools: { addColumn: { type: "button", name: "addColumn", command: "AddColumnCommand", icon: "plus", rules: "isEditable" }, spacer: { type: "spacer" }, search: { type: "component", name: "search", command: "SearchCommand", options: "{ \"field\": \"name\", \"operator\": \"startswith\" }", rules: "isSearchable", overflow: "never", component: "TextBox", componentOptions: { placeholder: "search", icon: "search", commandOn: "input" } } }, items: function() { var that = this, result = $([]); for (var key in that._columns) { $.merge(result, that._columns[key].cards()); } return result; }, itemsByStatus: function(status) { var that = this,