event_platform-1.0.x-dev/event_platform_scheduler/js/scheduler.js
event_platform_scheduler/js/scheduler.js
(function (Drupal, once) { 'use strict'; Drupal.behaviors.eventPlatformScheduler = { attach: function (context) { const unassign = document.querySelector('.scheduler--unassign'); const filters = document.querySelectorAll('#scheduler_filters select'); const filter_values = []; // Activate empty slots as drag targets. once('eventPlatformScheduler', '#scheduler-table .empty', context).forEach(function (element) { makDragTarget(element); }); // Activate filters. once('eventPlatformScheduler', '#scheduler_filters select', context).forEach(function (element) { element.addEventListener('change', applyFilters); }); // Activate unassign slots as a drag target. once('eventPlatformScheduler', '#scheduler--sessions .scheduler--unassign', context).forEach(function (element) { element.addEventListener('dragover', dragOver); element.addEventListener('dragenter', dragEnter); element.addEventListener('dragleave', dragLeave); element.addEventListener('drop', dragDropUnassign); }); // Activate sessions as draggable. once('eventPlatformScheduler', '.session', context).forEach(function (element) { element.addEventListener('dragstart', dragStart); element.addEventListener('dragend', dragEnd); if (element.dataset.room && element.dataset.timeslot) { // If a cell matches the sessions data, move it there. const cell = document.querySelector('td[data-room="' + element.dataset.room + '"][data-timeslot="' + element.dataset.timeslot + '"]'); if (cell) { cell.appendChild(element); cell.classList.remove("empty"); cell.classList.add("filled"); removeDragTarget(cell); } else { // Invalid room or timeslot, so reset values. element.dataset.room = ''; element.dataset.timeslot = ''; } } }); function makDragTarget(element) { element.addEventListener('dragover', dragOver); element.addEventListener('dragenter', dragEnter); element.addEventListener('dragleave', dragLeave); element.addEventListener('drop', dragDrop); } function removeDragTarget(element) { element.removeEventListener('dragover', dragOver); element.removeEventListener('dragenter', dragEnter); element.removeEventListener('dragleave', dragLeave); element.removeEventListener('drop', dragDrop); } function checkForConflicts(element) { const user = element.dataset.user; const timeslot = element.dataset.timeslot; const conflicts = document.querySelectorAll('.session[data-user="' + user + '"]'); if (conflicts) { conflicts.forEach((conflict) => { if (!conflict.dataset.timeslot || conflict.dataset.timeslot == timeslot) { return; } createConflict(conflict.dataset.timeslot, user); }); } } function createConflict(timeslot, user) { const cells = document.querySelectorAll('td[data-timeslot="' + timeslot + '"]'); var session; cells.forEach((cell) => { cell.classList.add('conflict'); removeDragTarget(cell); if (session = cell.querySelector('.session[data-user="' + user + '"]')) { session.classList.add('theconflict'); } }); } function removeConflicts() { const cells = document.querySelectorAll('td.conflict'); cells.forEach((cell) => { cell.classList.remove('conflict'); if (!cell.classList.contains('filled')) { makDragTarget(cell); } }); const sessions = document.querySelectorAll('.session.theconflict'); sessions.forEach((session) => { session.classList.remove('theconflict'); }); } // Drag Functions function dragStart(e) { this.classList.add("dragging"); e.dataTransfer.setData("text/plain", e.target.id); const slot = this.closest(".filled"); if (slot) { makDragTarget(slot); slot.classList.remove('filled'); } if (this.dataset.room || this.dataset.timeslot) { unassign.classList.remove('hidden'); } checkForConflicts(this); } function dragEnd() { this.classList.remove("dragging"); unassign.classList.add('hidden'); removeConflicts(); } function dragOver(e) { e.preventDefault(); e.dataTransfer.dropEffect = "move"; } function dragEnter(e) { e.preventDefault(); this.classList.add("dragover"); } function dragLeave() { this.classList.remove("dragover"); } function dragDrop(e) { // Get the id of the target and add the moved element to the target's DOM const data = e.dataTransfer.getData("text/plain"); const node = document.getElementById(data) // Replace classes. this.classList.remove("dragover"); this.classList.remove("empty"); this.classList.add("filled"); removeDragTarget(this); // Assemble the data necessary to update the node. const nid = node.dataset.nid; const room = this.dataset.room; const timeslot = this.dataset.timeslot; // Only update if the new data is different. if (nid && room && timeslot && (room != node.dataset.room || timeslot != node.dataset.timeslot)) { e.target.appendChild(node); // console.log("Target: " + nid + "-" + room + "-" + timeslot); const path = '/admin/event-details/scheduler/assign/' + nid + '/' + room + '/' + timeslot; fetch(path) .then(response => response.text()); // .then(text => console.log(text)); // @todo show feedback to user. // Add data attributes to the session. node.dataset.room = room; node.dataset.timeslot = timeslot; // Check if the session should be highlighted as a filters match. if (!arrayEmpty(filter_values) && checkFiltersMatch(node)) { node.classList.add('scheduler--filters-match'); } } } function dragDropUnassign(e) { // Get the id of the target and return the element to the list. const data = e.dataTransfer.getData("text/plain"); const node = document.getElementById(data) e.target.parentElement.appendChild(node); const nid = node.dataset.nid; if (nid) { const path = '/admin/event-details/scheduler/unassign/' + nid; fetch(path) .then(response => response.text()); // .then(text => console.log(text)); // @todo show feedback to user. } // Reset data attributes on the session. node.dataset.room = ''; node.dataset.timeslot = ''; node.classList.remove('scheduler--filters-match'); } function applyFilters() { // Get all filter values. filters.forEach((element) => { filter_values[element.name] = element.value; }); // Get all unassigned sessions. const sessions = document.querySelectorAll('#scheduler--sessions .session'); // Make all sessions visible. sessions.forEach((session) => { session.style.display = "block"; // Hide any sessions that don't match filter criteria. if (!checkFiltersMatch(session)) { session.style.display = "none"; } }); // Selectively add a class to highlight filter matches. let filters_empty = arrayEmpty(filter_values); const scheduledMatches = document.querySelectorAll('#scheduler-table .session'); scheduledMatches.forEach((session) => { // Reset all existing classes to start. session.classList.remove('scheduler--filters-match'); if (!filters_empty && checkFiltersMatch(session)) { session.classList.add('scheduler--filters-match'); } }); } function checkFiltersMatch(session) { let is_match = true; Object.entries(filter_values).forEach(([index, value]) => { if (!value) { return; } if (session.dataset[index] != value) { is_match = false; return; } }); return is_match; } function arrayEmpty(array) { let is_empty = true; // We don't use the index value, but wanted approach to be consistent. Object.entries(array).forEach(([index, value]) => { if (value) { is_empty = false; } }); return is_empty; } } }; }(Drupal, once));