123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527 |
- /**
- * 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
- }
- 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.addItemButton = false;
- this.buttonContent = "+";
- 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.
- addItemButton: false,
- buttonContent: "+",
- 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");
- 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");
- }
- });
- }
-
- self.options.dragEl(el, source);
- 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.addItemButton;
- var buttonContent = self.options.buttonContent;
-
- //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) {
- headerBoard.classList.add(value);
- });
- headerBoard.innerHTML =
- '<div class="kanban-title-board">' + board.title + "</div>";
- // if add button is true, add button to the board
- if (addButton) {
- var btn = document.createElement("BUTTON");
- var t = document.createTextNode(buttonContent);
- btn.setAttribute(
- "class",
- "kanban-title-button btn btn-default btn-xs"
- );
- btn.appendChild(t);
- //var buttonHtml = '<button class="kanban-title-button btn btn-default btn-xs">'+buttonContent+'</button>'
- headerBoard.appendChild(btn);
- __onButtonClickHandler(btn, board.id);
- }
- //content board
- var contentBoard = document.createElement("main");
- contentBoard.classList.add("kanban-drag");
- if (board.bodyClass !== "" && board.bodyClass !== undefined)
- var bodyClasses = board.bodyClass.split(",");
- else bodyClasses = [];
- bodyClasses.map(function (value) {
- contentBoard.classList.add(value);
- });
- //add drag to array for dragula
- self.boardContainer.push(contentBoard);
- for (var itemkey in board.item) {
- //create item
- var itemKanban = board.item[itemkey];
- var nodeItem = document.createElement("div");
- nodeItem.classList.add("kanban-item");
- if (itemKanban.id) {
- nodeItem.dataset.eid = itemKanban.id;
- }
- if (itemKanban.class && Array.isArray(itemKanban.class)) {
- itemKanban.class.forEach(function (cl) {
- nodeItem.classList.add(cl);
- })
- }
- nodeItem.innerHTML = __buildItemTitle(itemKanban.title);
- //add function
- nodeItem.clickfn = itemKanban.click;
- nodeItem.dragfn = itemKanban.drag;
- nodeItem.dragendfn = itemKanban.dragend;
- nodeItem.dropfn = itemKanban.drop;
- __appendCustomProperties(nodeItem, itemKanban);
- //add click handler of item
- __onclickHandler(nodeItem);
- if (self.options.itemHandleOptions.enabled) {
- nodeItem.style.cursor = "default";
- }
- contentBoard.appendChild(nodeItem);
- }
- //footer board
- var footerBoard = document.createElement("footer");
- //board assembly
- boardNode.appendChild(headerBoard);
- boardNode.appendChild(contentBoard);
- boardNode.appendChild(footerBoard);
- //board add
- self.container.appendChild(boardNode);
- }
- return self;
- };
-
- this.findBoard = function (id) {
- var el = self.element.querySelector('[data-id="' + id + '"]');
- return el;
- };
-
- this.getParentBoardID = function (el) {
- if (typeof el === "string") {
- el = self.element.querySelector('[data-eid="' + el + '"]');
- }
- if (el === null) {
- return null;
- }
- return el.parentNode.parentNode.dataset.id;
- };
-
- this.moveElement = function (targetBoardID, elementID, element) {
- if (targetBoardID === this.getParentBoardID(elementID)) {
- return;
- }
-
- this.removeElement(elementID);
- return this.addElement(targetBoardID, element);
- };
-
- this.replaceElement = function (el, element) {
- var nodeItem = el;
- if (typeof nodeItem === "string") {
- nodeItem = self.element.querySelector('[data-eid="' + el + '"]');
- }
- nodeItem.innerHTML = element.title;
- // add function
- nodeItem.clickfn = element.click;
- nodeItem.dragfn = element.drag;
- nodeItem.dragendfn = element.dragend;
- nodeItem.dropfn = element.drop;
- __appendCustomProperties(nodeItem, element);
- return self;
- };
-
- this.findElement = function (id) {
- var el = self.element.querySelector('[data-eid="' + id + '"]');
- return el;
- };
-
- this.getBoardElements = function (id) {
- var board = self.element.querySelector(
- '[data-id="' + id + '"] .kanban-drag'
- );
- return board.childNodes;
- };
-
- this.removeElement = function (el) {
- if (typeof el === "string")
- el = self.element.querySelector('[data-eid="' + el + '"]');
- if (el !== null) {
- el.remove();
- }
- return self;
- };
-
- this.removeBoard = function (board) {
- var boardElement = null;
- if (typeof board === "string")
- boardElement = self.element.querySelector('[data-id="' + board + '"]');
- if (boardElement !== null) {
- boardElement.remove();
- }
-
- // remove thboard in options.boards
- for (var i = 0; i < self.options.boards.length; i++) {
- if (self.options.boards[i].id === board) {
- self.options.boards.splice(i, 1);
- break;
- }
- }
-
- return self;
- };
-
- // board button on click function
- this.onButtonClick = function (el) { };
- //PRIVATE FUNCTION
- function __extendDefaults(source, properties) {
- var property;
- for (property in properties) {
- if (properties.hasOwnProperty(property)) {
- source[property] = properties[property];
- }
- }
- return source;
- }
-
- function __setBoard() {
- self.element = document.querySelector(self.options.element);
- //create container
- var boardContainer = document.createElement("div");
- boardContainer.classList.add("kanban-container");
- self.container = boardContainer;
- //add boards
- self.addBoards(self.options.boards, true);
- //appends to container
- self.element.appendChild(self.container);
- }
-
- function __onclickHandler(nodeItem, clickfn) {
- nodeItem.addEventListener("click", function (e) {
- e.preventDefault();
- self.options.click(this);
- if (typeof this.clickfn === "function") this.clickfn(this);
- });
- }
-
- function __onButtonClickHandler(nodeItem, boardId) {
- nodeItem.addEventListener("click", function (e) {
- e.preventDefault();
- self.options.buttonClick(this, boardId);
- // if(typeof(this.clickfn) === 'function')
- // this.clickfn(this);
- });
- }
-
- function __findBoardJSON(id) {
- var el = [];
- self.options.boards.map(function (board) {
- if (board.id === id) {
- return el.push(board);
- }
- });
- return el[0];
- }
-
- function __appendCustomProperties(element, parentObject) {
- for (var propertyName in parentObject) {
- if (self._disallowedItemProperties.indexOf(propertyName) > -1) {
- continue;
- }
-
- element.setAttribute(
- "data-" + propertyName,
- parentObject[propertyName]
- );
- }
- }
-
- function __updateBoardsOrder() {
- var index = 1;
- for (var i = 0; i < self.container.childNodes.length; i++) {
- self.container.childNodes[i].dataset.order = index++;
- }
- }
-
- function __buildItemTitle(title) {
- var result = title;
- if (self.options.itemHandleOptions.enabled) {
- if ((self.options.itemHandleOptions.customHandler || undefined) === undefined) {
- var customCssHandler = self.options.itemHandleOptions.customCssHandler;
- var customCssIconHandler = self.options.itemHandleOptions.customCssIconHandler;
- if ((customCssHandler || undefined) === undefined) {
- customCssHandler = "drag_handler";
- }
- if ((customCssIconHandler || undefined) === undefined) {
- customCssIconHandler = customCssHandler + "_icon";
- }
-
- result = "<div class='item_handle " + customCssHandler + "'><i class='item_handle " + customCssIconHandler + "'></i></div><div>" + result + "</div>";
- } else {
- result = self.options.itemHandleOptions.customHandler.replace("%s", result);
- }
- }
- return result;
- }
-
- //init plugin
- this.init();
- };
- })();
|