/** * jKanban * Vanilla Javascript plugin for manage kanban boards * * @site: http://www.riccardotartaglia.it/jkanban/ * @author: Riccardo Tartaglia */ //Require dragula var dragula = require('dragula'); (function () { this.jKanban = function () { var self = this var __DEFAULT_ITEM_HANDLE_OPTIONS = { enabled: false } var __DEFAULT_ITEM_ADD_OPTIONS = { enabled: false } this._disallowedItemProperties = [ 'id', 'title', 'click', 'drag', 'dragend', 'drop', 'order' ] this.element = '' this.container = '' this.boardContainer = [] this.handlers = [] this.dragula = dragula this.drake = '' this.drakeBoard = '' this.itemAddOptions = __DEFAULT_ITEM_ADD_OPTIONS this.itemHandleOptions = __DEFAULT_ITEM_HANDLE_OPTIONS var defaults = { element: '', gutter: '15px', widthBoard: '250px', responsive: '700', responsivePercentage: false, boards: [], dragBoards: true, dragItems: true, //whether can drag cards or not, useful when set permissions on it. itemAddOptions: __DEFAULT_ITEM_ADD_OPTIONS, itemHandleOptions: __DEFAULT_ITEM_HANDLE_OPTIONS, dragEl: function (el, source) {}, dragendEl: function (el) {}, dropEl: function (el, target, source, sibling) {}, dragBoard: function (el, source) {}, dragendBoard: function (el) {}, dropBoard: function (el, target, source, sibling) {}, click: function (el) {}, buttonClick: function (el, boardId) {} } if (arguments[0] && typeof arguments[0] === 'object') { this.options = __extendDefaults(defaults, arguments[0]) } this.__getCanMove = function (handle) { if (!self.options.itemHandleOptions.enabled) { return !!self.options.dragItems } if (self.options.itemHandleOptions.handleClass) { return handle.classList.contains(self.options.itemHandleOptions.handleClass) } return handle.classList.contains('item_handle') } this.init = function () { //set initial boards __setBoard() //set drag with dragula if (window.innerWidth > self.options.responsive) { //Init Drag Board self.drakeBoard = self .dragula([self.container], { moves: function (el, source, handle, sibling) { if (!self.options.dragBoards) return false return ( handle.classList.contains('kanban-board-header') || handle.classList.contains('kanban-title-board') ) }, accepts: function (el, target, source, sibling) { return target.classList.contains('kanban-container') }, revertOnSpill: true, direction: 'horizontal' }) .on('drag', function (el, source) { el.classList.add('is-moving') self.options.dragBoard(el, source) if (typeof el.dragfn === 'function') el.dragfn(el, source) }) .on('dragend', function (el) { __updateBoardsOrder() el.classList.remove('is-moving') self.options.dragendBoard(el) if (typeof el.dragendfn === 'function') el.dragendfn(el) }) .on('drop', function (el, target, source, sibling) { el.classList.remove('is-moving') self.options.dropBoard(el, target, source, sibling) if (typeof el.dropfn === 'function') el.dropfn(el, target, source, sibling) }) //Init Drag Item self.drake = self .dragula(self.boardContainer, { moves: function (el, source, handle, sibling) { return self.__getCanMove(handle) }, revertOnSpill: true }) .on('cancel', function (el, container, source) { self.enableAllBoards() }) .on('drag', function (el, source) { var elClass = el.getAttribute('class') if (elClass !== '' && elClass.indexOf('not-draggable') > -1) { self.drake.cancel(true) return } el.classList.add('is-moving') self.options.dragEl(el, source) var boardJSON = __findBoardJSON(source.parentNode.dataset.id) if (boardJSON.dragTo !== undefined) { self.options.boards.map(function (board) { if ( boardJSON.dragTo.indexOf(board.id) === -1 && board.id !== source.parentNode.dataset.id ) { self.findBoard(board.id).classList.add('disabled-board') } }) } if (el !== null && typeof el.dragfn === 'function') el.dragfn(el, source) }) .on('dragend', function (el) { self.options.dragendEl(el) if (el !== null && typeof el.dragendfn === 'function') el.dragendfn(el) }) .on('drop', function (el, target, source, sibling) { self.enableAllBoards() var boardJSON = __findBoardJSON(source.parentNode.dataset.id) if (boardJSON.dragTo !== undefined) { if ( boardJSON.dragTo.indexOf(target.parentNode.dataset.id) === -1 && target.parentNode.dataset.id !== source.parentNode.dataset.id ) { self.drake.cancel(true) } } if (el !== null) { var result = self.options.dropEl(el, target, source, sibling) if (result === false) { self.drake.cancel(true) } el.classList.remove('is-moving') if (typeof el.dropfn === 'function') el.dropfn(el, target, source, sibling) } }) } } this.enableAllBoards = function () { var allB = document.querySelectorAll('.kanban-board') if (allB.length > 0 && allB !== undefined) { for (var i = 0; i < allB.length; i++) { allB[i].classList.remove('disabled-board') } } } this.addElement = function (boardID, element) { var board = self.element.querySelector( '[data-id="' + boardID + '"] .kanban-drag' ) var nodeItem = document.createElement('div') nodeItem.classList.add('kanban-item') if (typeof element.id !== 'undefined' && element.id !== '') { nodeItem.setAttribute('data-eid', element.id) } if (element.class && Array.isArray(element.class)) { element.class.forEach(function (cl) { nodeItem.classList.add(cl) }) } nodeItem.innerHTML = __buildItemTitle(element.title) //add function nodeItem.clickfn = element.click nodeItem.dragfn = element.drag nodeItem.dragendfn = element.dragend nodeItem.dropfn = element.drop __appendCustomProperties(nodeItem, element) __onclickHandler(nodeItem) if (self.options.itemHandleOptions.enabled) { nodeItem.style.cursor = 'default' } board.appendChild(nodeItem) return self } this.addForm = function (boardID, formItem) { var board = self.element.querySelector( '[data-id="' + boardID + '"] .kanban-drag' ) var _attribute = formItem.getAttribute('class') formItem.setAttribute('class', _attribute + ' not-draggable') board.appendChild(formItem) return self } this.addBoards = function (boards, isInit) { if (self.options.responsivePercentage) { self.container.style.width = '100%' self.options.gutter = '1%' if (window.innerWidth > self.options.responsive) { var boardWidth = (100 - boards.length * 2) / boards.length } else { var boardWidth = 100 - boards.length * 2 } } else { var boardWidth = self.options.widthBoard } var addButton = self.options.itemAddOptions.enabled var buttonContent = self.options.itemAddOptions.content var buttonClass = self.options.itemAddOptions.class var buttonFooter = self.options.itemAddOptions.footer //for on all the boards for (var boardkey in boards) { // single board var board = boards[boardkey] if (!isInit) { self.options.boards.push(board) } if (!self.options.responsivePercentage) { //add width to container if (self.container.style.width === '') { self.container.style.width = parseInt(boardWidth) + parseInt(self.options.gutter) * 2 + 'px' } else { self.container.style.width = parseInt(self.container.style.width) + parseInt(boardWidth) + parseInt(self.options.gutter) * 2 + 'px' } } //create node var boardNode = document.createElement('div') boardNode.dataset.id = board.id boardNode.dataset.order = self.container.childNodes.length + 1 boardNode.classList.add('kanban-board') //set style if (self.options.responsivePercentage) { boardNode.style.width = boardWidth + '%' } else { boardNode.style.width = boardWidth } boardNode.style.marginLeft = self.options.gutter boardNode.style.marginRight = self.options.gutter // header board var headerBoard = document.createElement('header') if (board.class !== '' && board.class !== undefined) var allClasses = board.class.split(',') else allClasses = [] headerBoard.classList.add('kanban-board-header') allClasses.map(function (value) { // Remove empty spaces value = value.replace(/^[ ]+/g, '') headerBoard.classList.add(value) }) headerBoard.innerHTML = '