From 408e5909225f09a72ef0aeda24a2d854268d08f0 Mon Sep 17 00:00:00 2001 From: "Janus C. H. Knudsen" Date: Thu, 15 Jan 2026 20:49:56 +0100 Subject: [PATCH] Improves scroll and interaction behavior in schedule view Enhances dragging and clicking interactions for schedule containers - Adds drag threshold to prevent unintended scrolling - Prevents click events during dragging - Updates time badge selectors for consistent element handling Fixes potential usability issues with schedule cell interactions --- .../wwwroot/ts/modules/employees.ts | 44 ++++++++++++++----- 1 file changed, 34 insertions(+), 10 deletions(-) diff --git a/PlanTempus.Application/wwwroot/ts/modules/employees.ts b/PlanTempus.Application/wwwroot/ts/modules/employees.ts index d130680..4fa8007 100644 --- a/PlanTempus.Application/wwwroot/ts/modules/employees.ts +++ b/PlanTempus.Application/wwwroot/ts/modules/employees.ts @@ -448,6 +448,7 @@ class ScheduleController { scrollContainers.forEach(container => { const el = container as HTMLElement; let isDown = false; + let hasDragged = false; let startX = 0; let scrollLeft = 0; @@ -456,7 +457,7 @@ class ScheduleController { if ((e.target as HTMLElement).closest('swp-btn, input, select')) return; isDown = true; - el.classList.add('dragging'); + hasDragged = false; startX = e.clientX; scrollLeft = el.scrollLeft; }; @@ -464,20 +465,43 @@ class ScheduleController { const onMouseUp = () => { isDown = false; el.classList.remove('dragging'); + + // Reset hasDragged after a short delay to allow click events to check it + setTimeout(() => { hasDragged = false; }, 0); }; const onMouseMove = (e: MouseEvent) => { if (!isDown) return; - e.preventDefault(); + const x = e.clientX; + const diff = Math.abs(startX - x); + + // Only start dragging after moving 5px (allows clicks/double-clicks) + if (!hasDragged && diff > 5) { + hasDragged = true; + el.classList.add('dragging'); + } + + if (!hasDragged) return; + + e.preventDefault(); const walk = (startX - x) * 1.5; el.scrollLeft = scrollLeft + walk; }; + // Prevent click events if we just dragged + const onClick = (e: MouseEvent) => { + if (hasDragged) { + e.preventDefault(); + e.stopPropagation(); + } + }; + el.addEventListener('mousedown', onMouseDown); el.addEventListener('mouseup', onMouseUp); el.addEventListener('mouseleave', onMouseUp); el.addEventListener('mousemove', onMouseMove); + el.addEventListener('click', onClick, true); // capture phase }); } @@ -741,13 +765,13 @@ class ScheduleController { * Prefill form from cell data */ private prefillFormFromCell(cell: HTMLElement): void { - const timeDisplay = cell.querySelector('swp-time-display'); - if (!timeDisplay) return; + const timeBadge = cell.querySelector('swp-time-badge'); + if (!timeBadge) return; let status = 'work'; - if (timeDisplay.classList.contains('off')) status = 'off'; - else if (timeDisplay.classList.contains('vacation')) status = 'vacation'; - else if (timeDisplay.classList.contains('sick')) status = 'sick'; + if (timeBadge.classList.contains('off')) status = 'off'; + else if (timeBadge.classList.contains('vacation')) status = 'vacation'; + else if (timeBadge.classList.contains('sick')) status = 'sick'; // Update status options document.querySelectorAll('#scheduleStatusOptions swp-status-option').forEach(opt => { @@ -760,7 +784,7 @@ class ScheduleController { // Parse time if work status if (status === 'work') { - const timeText = timeDisplay.textContent?.trim() || ''; + const timeText = timeBadge.textContent?.trim() || ''; const timeMatch = timeText.match(/(\d{2}:\d{2})\s*-\s*(\d{2}:\d{2})/); if (timeMatch) { this.setTimeRange(timeMatch[1], timeMatch[2]); @@ -1008,7 +1032,7 @@ class ScheduleController { const formattedTime = `${startTime} - ${endTime}`; this.selectedCells.forEach(cell => { - const timeDisplay = cell.querySelector('swp-time-display'); + const timeDisplay = cell.querySelector('swp-time-badge'); if (timeDisplay && !timeDisplay.classList.contains('off') && !timeDisplay.classList.contains('vacation') && !timeDisplay.classList.contains('sick')) { @@ -1032,7 +1056,7 @@ class ScheduleController { const endTime = endInput ? this.valueToTime(parseInt(endInput.value)) : '17:00'; this.selectedCells.forEach(cell => { - const timeDisplay = cell.querySelector('swp-time-display'); + const timeDisplay = cell.querySelector('swp-time-badge'); if (!timeDisplay) return; timeDisplay.classList.remove('off', 'off-override', 'vacation', 'sick');