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
This commit is contained in:
Janus C. H. Knudsen 2026-01-15 20:49:56 +01:00
parent 531c681b7d
commit 408e590922

View file

@ -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');