/** * App Kanban */ 'use strict'; (async function () { let boards; const kanbanSidebar = document.querySelector('.kanban-update-item-sidebar'), kanbanWrapper = document.querySelector('.kanban-wrapper'), commentEditor = document.querySelector('.comment-editor'), kanbanAddNewBoard = document.querySelector('.kanban-add-new-board'), kanbanAddNewInput = [].slice.call(document.querySelectorAll('.kanban-add-board-input')), kanbanAddBoardBtn = document.querySelector('.kanban-add-board-btn'), datePicker = document.querySelector('#due-date'), select2 = $('.select2'), // ! Using jquery vars due to select2 jQuery dependency assetsPath = document.querySelector('html').getAttribute('data-assets-path'); // Init kanban Offcanvas const kanbanOffcanvas = new bootstrap.Offcanvas(kanbanSidebar); // Get kanban data const kanbanResponse = await fetch(assetsPath + 'json/kanban.json'); if (!kanbanResponse.ok) { console.error('error', kanbanResponse); } boards = await kanbanResponse.json(); // datepicker init if (datePicker) { datePicker.flatpickr({ monthSelectorType: 'static', static: true, altInput: true, altFormat: 'j F, Y', dateFormat: 'Y-m-d' }); } //! TODO: Update Event label and guest code to JS once select removes jQuery dependency // select2 if (select2.length) { function renderLabels(option) { if (!option.id) { return option.text; } var $badge = "
" + option.text + '
'; return $badge; } select2.each(function () { var $this = $(this); $this.wrap("
").select2({ placeholder: 'Select Label', dropdownParent: $this.parent(), templateResult: renderLabels, templateSelection: renderLabels, escapeMarkup: function (es) { return es; } }); }); } // Comment editor if (commentEditor) { new Quill(commentEditor, { modules: { toolbar: '.comment-toolbar' }, placeholder: 'Write a Comment...', theme: 'snow' }); } // Render board dropdown const renderBoardDropdown = () => ` `; // Render item dropdown const renderDropdown = () => ` `; // Render header const renderHeader = (color, text) => `
${text}
${renderDropdown()}
`; // Render avatar const renderAvatar = (images = '', pullUp = false, size = '', margin = '', members = '') => { const transitionClass = pullUp ? ' pull-up' : ''; const sizeClass = size ? `avatar-${size}` : ''; const memberList = members ? members.split(',') : []; return images ? images .split(',') .map((img, index, arr) => { const marginClass = margin && index !== arr.length - 1 ? ` me-${margin}` : ''; const memberName = memberList[index] || ''; return `
Avatar
`; }) .join('') : ''; }; // Render footer const renderFooter = (attachments, comments, assigned, members) => `
${attachments} ${comments}
${renderAvatar(assigned, true, 'xs', null, members)}
`; // Initialize kanban const kanban = new jKanban({ element: '.kanban-wrapper', gutter: '12px', widthBoard: '250px', dragItems: true, boards: boards, dragBoards: true, addItemButton: true, buttonContent: '+ Add Item', itemAddOptions: { enabled: true, content: '+ Add New Item', class: 'kanban-title-button btn btn-default border-none', footer: false }, click: el => { const element = el; const title = element.getAttribute('data-eid') ? element.querySelector('.kanban-text').textContent : element.textContent; const date = element.getAttribute('data-due-date'); const dateObj = new Date(); const year = dateObj.getFullYear(); const dateToUse = date ? `${date}, ${year}` : `${dateObj.getDate()} ${dateObj.toLocaleString('en', { month: 'long' })}, ${year}`; const label = element.getAttribute('data-badge-text'); const avatars = element.getAttribute('data-assigned'); // Show kanban offcanvas kanbanOffcanvas.show(); // Populate sidebar fields kanbanSidebar.querySelector('#title').value = title; kanbanSidebar.querySelector('#due-date').nextSibling.value = dateToUse; // Using jQuery for select2 $('.kanban-update-item-sidebar').find(select2).val(label).trigger('change'); // Remove and update assigned avatars kanbanSidebar.querySelector('.assigned').innerHTML = ''; kanbanSidebar.querySelector('.assigned').insertAdjacentHTML( 'afterbegin', `${renderAvatar(avatars, false, 'xs', '1', el.getAttribute('data-members'))}
` ); }, buttonClick: (el, boardId) => { const addNewForm = document.createElement('form'); addNewForm.setAttribute('class', 'new-item-form'); addNewForm.innerHTML = `
`; kanban.addForm(boardId, addNewForm); addNewForm.addEventListener('submit', e => { e.preventDefault(); const currentBoard = Array.from(document.querySelectorAll(`.kanban-board[data-id="${boardId}"] .kanban-item`)); kanban.addElement(boardId, { title: `${e.target[0].value}`, id: `${boardId}-${currentBoard.length + 1}` }); // Add dropdown to new tasks const kanbanTextElements = Array.from( document.querySelectorAll(`.kanban-board[data-id="${boardId}"] .kanban-text`) ); kanbanTextElements.forEach(textElem => { textElem.insertAdjacentHTML('beforebegin', renderDropdown()); }); // Prevent sidebar from opening on dropdown click const newTaskDropdowns = Array.from(document.querySelectorAll('.kanban-item .kanban-tasks-item-dropdown')); newTaskDropdowns.forEach(dropdown => { dropdown.addEventListener('click', event => event.stopPropagation()); }); // Add delete functionality for new tasks const deleteTaskButtons = Array.from( document.querySelectorAll(`.kanban-board[data-id="${boardId}"] .delete-task`) ); deleteTaskButtons.forEach(btn => { btn.addEventListener('click', () => { const taskId = btn.closest('.kanban-item').getAttribute('data-eid'); kanban.removeElement(taskId); }); }); addNewForm.remove(); }); // Remove form on clicking cancel button addNewForm.querySelector('.cancel-add-item').addEventListener('click', () => addNewForm.remove()); } }); // Kanban Wrapper scrollbar if (kanbanWrapper) { new PerfectScrollbar(kanbanWrapper); } const kanbanContainer = document.querySelector('.kanban-container'); const kanbanTitleBoard = Array.from(document.querySelectorAll('.kanban-title-board')); const kanbanItem = Array.from(document.querySelectorAll('.kanban-item')); // Render custom items if (kanbanItem.length) { kanbanItem.forEach(el => { const element = `${el.textContent}`; let img = ''; if (el.getAttribute('data-image')) { img = ` `; } el.textContent = ''; if (el.getAttribute('data-badge') && el.getAttribute('data-badge-text')) { el.insertAdjacentHTML( 'afterbegin', `${renderHeader(el.getAttribute('data-badge'), el.getAttribute('data-badge-text'))}${img}${element}` ); } if (el.getAttribute('data-comments') || el.getAttribute('data-due-date') || el.getAttribute('data-assigned')) { el.insertAdjacentHTML( 'beforeend', renderFooter( el.getAttribute('data-attachments') || 0, el.getAttribute('data-comments') || 0, el.getAttribute('data-assigned') || '', el.getAttribute('data-members') || '' ) ); } }); } // Initialize tooltips for rendered items const tooltipTriggerList = Array.from(document.querySelectorAll('[data-bs-toggle="tooltip"]')); tooltipTriggerList.forEach(tooltipTriggerEl => { new bootstrap.Tooltip(tooltipTriggerEl); }); // Prevent sidebar from opening on dropdown button click const tasksItemDropdown = Array.from(document.querySelectorAll('.kanban-tasks-item-dropdown')); if (tasksItemDropdown.length) { tasksItemDropdown.forEach(dropdown => { dropdown.addEventListener('click', event => { event.stopPropagation(); }); }); } // Toggle "add new" input and actions for add-new-btn if (kanbanAddBoardBtn) { kanbanAddBoardBtn.addEventListener('click', () => { kanbanAddNewInput.forEach(el => { el.value = ''; // Clear input value el.classList.toggle('d-none'); // Toggle visibility }); }); } // Render "add new" inline with boards if (kanbanContainer) { kanbanContainer.append(kanbanAddNewBoard); } // Makes kanban title editable for rendered boards if (kanbanTitleBoard) { kanbanTitleBoard.forEach(elem => { elem.addEventListener('mouseenter', () => { elem.contentEditable = 'true'; }); // Appends delete icon with title elem.insertAdjacentHTML('afterend', renderBoardDropdown()); }); } // Delete Board for rendered boards const deleteBoards = Array.from(document.querySelectorAll('.delete-board')); deleteBoards.forEach(elem => { elem.addEventListener('click', () => { const id = elem.closest('.kanban-board').getAttribute('data-id'); kanban.removeBoard(id); }); }); // Delete task for rendered boards const deleteTasks = Array.from(document.querySelectorAll('.delete-task')); deleteTasks.forEach(task => { task.addEventListener('click', () => { const id = task.closest('.kanban-item').getAttribute('data-eid'); kanban.removeElement(id); }); }); // Cancel "Add New Board" input const cancelAddNew = document.querySelector('.kanban-add-board-cancel-btn'); if (cancelAddNew) { cancelAddNew.addEventListener('click', () => { kanbanAddNewInput.forEach(el => { el.classList.toggle('d-none'); }); }); } // Add new board if (kanbanAddNewBoard) { kanbanAddNewBoard.addEventListener('submit', e => { e.preventDefault(); const value = e.target.querySelector('.form-control').value.trim(); const id = value.replace(/\s+/g, '-').toLowerCase(); kanban.addBoards([{ id, title: value }]); // Add delete board option to new board and make title editable const newBoard = document.querySelector('.kanban-board:last-child'); if (newBoard) { const header = newBoard.querySelector('.kanban-title-board'); header.insertAdjacentHTML('afterend', renderBoardDropdown()); // Make title editable header.addEventListener('mouseenter', () => { header.contentEditable = 'true'; }); // Add delete functionality to new board const deleteNewBoard = newBoard.querySelector('.delete-board'); if (deleteNewBoard) { deleteNewBoard.addEventListener('click', () => { const id = deleteNewBoard.closest('.kanban-board').getAttribute('data-id'); kanban.removeBoard(id); }); } } // Hide input fields kanbanAddNewInput.forEach(el => { el.classList.add('d-none'); }); // Re-append the "Add New Board" form if (kanbanContainer) { kanbanContainer.append(kanbanAddNewBoard); } }); } // Clear comment editor on Kanban sidebar close kanbanSidebar.addEventListener('hidden.bs.offcanvas', () => { const editor = kanbanSidebar.querySelector('.ql-editor').firstElementChild; if (editor) editor.innerHTML = ''; }); // Re-init tooltip when offcanvas opens(Bootstrap bug) if (kanbanSidebar) { kanbanSidebar.addEventListener('shown.bs.offcanvas', () => { const tooltipTriggerList = Array.from(kanbanSidebar.querySelectorAll('[data-bs-toggle="tooltip"]')); tooltipTriggerList.forEach(tooltipTriggerEl => { new bootstrap.Tooltip(tooltipTriggerEl); }); }); } })();