From c05b2eca8df4af430a8f47bbd753f56bc6500e06 Mon Sep 17 00:00:00 2001 From: Alice Gaudon Date: Tue, 21 Jan 2020 13:03:32 +0100 Subject: [PATCH] Make services reorderable (drag and drop) --- resources/index.html | 1 + resources/js/index.js | 95 ++++++++++++++++++++++++++++++++++++++- resources/style/index.css | 45 +++++++++++++++++++ src/main.js | 21 +++++++++ 4 files changed, 160 insertions(+), 2 deletions(-) diff --git a/resources/index.html b/resources/index.html index 4e74d31..01e5523 100644 --- a/resources/index.html +++ b/resources/index.html @@ -21,6 +21,7 @@ + diff --git a/resources/js/index.js b/resources/js/index.js index eac4dca..b59ec95 100644 --- a/resources/js/index.js +++ b/resources/js/index.js @@ -96,6 +96,19 @@ ipcRenderer.on('data', (event, appData, brandIcons, solidIcons, actualServices, createService(i); } + // Init drag last position + const lastDragPosition = document.getElementById('service-last-drag-position'); + lastDragPosition.addEventListener('dragover', () => { + const index = services.length; + if (draggedId !== index && draggedId !== index - 1) { + resetDrag(); + lastDragTarget = dragTargetId = index; + lastDragPosition.classList.remove('hidden'); + lastDragPosition.classList.add('drag-target'); + } + }); + + // Set active service if (actualSelectedService < 0 || actualSelectedService >= services.length) { actualSelectedService = 0; } @@ -129,6 +142,31 @@ ipcRenderer.on('updateService', (e, id, data) => { } }); +ipcRenderer.on('reorderService', (e, serviceId, targetId) => { + const oldServices = services; + services = []; + + for (let i = 0; i < targetId; i++) { + if (i !== serviceId) { + services.push(oldServices[i]); + } + } + services.push(oldServices[serviceId]); + const newId = services.length - 1; + for (let i = targetId; i < oldServices.length; i++) { + if (i !== serviceId) { + services.push(oldServices[i]); + } + } + + document.getElementById('service-selector').innerHTML = ''; + for (let i = 0; i < services.length; i++) { + services[i].li = undefined; + createService(i); + } + setActiveService(newId); +}); + ipcRenderer.on('deleteService', (e, id) => { const nav = document.querySelector('#service-selector'); @@ -199,6 +237,55 @@ function createService(index, nextNavButton) { if (service.autoLoad) { loadService(index, service); } + + initDrag(index, li); +} + +let draggedId; +let lastDragTarget = -1; +let dragTargetId = -1; +let dragTargetCount = 0; + +function initDrag(index, li) { + li.serviceId = index; + li.draggable = true; + li.addEventListener('dragstart', (event) => { + draggedId = index; + event.dataTransfer.dropEffect = 'move'; + document.getElementById('service-last-drag-position').classList.remove('hidden'); + }); + li.addEventListener('dragover', () => { + if (draggedId !== index && draggedId !== index - 1) { + resetDrag(); + lastDragTarget = dragTargetId = index; + document.getElementById('service-last-drag-position').classList.remove('hidden'); + li.classList.add('drag-target'); + } + }); + li.addEventListener('dragend', () => { + reorderService(draggedId, lastDragTarget); + resetDrag(); + }); +} + +function resetDrag() { + lastDragTarget = -1; + dragTargetId = -1; + dragTargetCount = 0; + document.getElementById('service-selector').querySelectorAll('li').forEach(li => { + li.classList.remove('drag-target'); + }); + const lastDragPosition = document.getElementById('service-last-drag-position'); + lastDragPosition.classList.remove('drag-target'); + lastDragPosition.classList.add('hidden'); +} + +function reorderService(serviceId, targetId) { + console.log('Reordering service', serviceId, targetId); + if (targetId >= 0) { + setActiveService(null); + ipcRenderer.send('reorderService', serviceId, targetId); + } } document.addEventListener('DOMContentLoaded', () => { @@ -215,7 +302,9 @@ document.addEventListener('DOMContentLoaded', () => { function setActiveService(serviceId) { const currentService = services[serviceId]; process.nextTick(() => { - loadService(serviceId, currentService); + if (currentService) { + loadService(serviceId, currentService); + } // Hide previous service if (services[selectedService] && services[selectedService].view) { @@ -223,7 +312,9 @@ function setActiveService(serviceId) { } // Show service - currentService.view.classList.add('active'); + if (currentService) { + currentService.view.classList.add('active'); + } // Save active service ID selectedService = serviceId; diff --git a/resources/style/index.css b/resources/style/index.css index dd364aa..726e0bc 100644 --- a/resources/style/index.css +++ b/resources/style/index.css @@ -10,6 +10,51 @@ body { padding: 0; } +#service-selector [draggable] { + user-select: none; + background-color: rgb(43, 43, 43); +} + +#service-selector [draggable] img { + -webkit-user-drag: none; + user-drag: none; +} + +#service-selector .drag-target button { + height: 144px; + padding-top: 88px; +} + +#service-selector .drag-target button::after, +#service-last-drag-position.drag-target { + content: ""; + height: 72px; + border: 1px dashed #fff; + box-sizing: border-box; + + background-color: rgb(43, 43, 43); +} +#service-selector .drag-target button::after { + position: absolute; + left: 0; + top: 0; + width: 100%; +} +#service-last-drag-position:not(.hidden):not(.drag-target) { + display: block; + padding: 16px 4px; + background-color: #fff5; +} +#service-last-drag-position:not(.drag-target)::after { + content: ""; + display: block; + border-bottom: 1px solid #fff; +} + +#service-selector .drag-target::after { + top: 75% !important; +} + *:focus { outline-color: rgb(118, 93, 176); } diff --git a/src/main.js b/src/main.js index 6d86ed6..20e39c8 100644 --- a/src/main.js +++ b/src/main.js @@ -158,6 +158,27 @@ function createWindow() { window.webContents.send('deleteService', id); }); + ipcMain.on('reorderService', (e, serviceId, targetId) => { + console.log('Reordering services', serviceId, targetId); + + const oldServices = config.services; + config.services = []; + + for (let i = 0; i < targetId; i++) { + if (i !== serviceId) { + config.services.push(oldServices[i]); + } + } + config.services.push(oldServices[serviceId]); + for (let i = targetId; i < oldServices.length; i++) { + if (i !== serviceId) { + config.services.push(oldServices[i]); + } + } + + e.reply('reorderService', serviceId, targetId); + }); + ipcMain.on('updateWindowTitle', (event, serviceId, viewTitle) => { if (serviceId === null) { window.setTitle(Meta.title);