From 71a158aa5194a993acbb71a1ec0aa2c7be6d1635 Mon Sep 17 00:00:00 2001 From: grabowski Date: Mon, 20 Jul 2026 09:50:01 +0700 Subject: [PATCH] Make stay bands draggable to move stays between days Dragging any week-segment of a stay band onto an in-range day moves the whole stay there, preserving its length (same one-PATCH date+end_date shift the multi-day chips use). Clicking a band still opens the editor. --- public/css/styles.css | 3 +++ public/js/views/calendar.js | 5 +++-- public/js/views/dragdrop.js | 20 ++++++++++++++++++-- public/js/views/stayBands.js | 4 ++++ 4 files changed, 28 insertions(+), 4 deletions(-) diff --git a/public/css/styles.css b/public/css/styles.css index 25baca5..771ed03 100644 --- a/public/css/styles.css +++ b/public/css/styles.css @@ -268,6 +268,9 @@ textarea.input { resize: vertical; } .cal-chip-draggable { cursor: grab; } .cal-chip-draggable:active { cursor: grabbing; } .cal-chip.dragging { opacity: 0.4; } +.cal-band-draggable { cursor: grab; } +.cal-band-draggable:active { cursor: grabbing; } +.cal-band.dragging { opacity: 0.4; } .cal-day-drop { border-color: var(--brand); box-shadow: 0 0 0 2px var(--brand); background: var(--brand-soft); } /* ---------- Detail grid (map + summary) ---------- */ diff --git a/public/js/views/calendar.js b/public/js/views/calendar.js index fff95aa..63cddca 100644 --- a/public/js/views/calendar.js +++ b/public/js/views/calendar.js @@ -196,8 +196,9 @@ function chip(entry, currency) { ? el('span', { class: 'chip-price' }, formatMoney(entry.price, currency, { compact: true })) : null, ); - // Regular chips are drag sources (move the entry to another day). The - // "…continues" and "dropoff" ghost chips and stay bands stay non-draggable. + // Regular chips are drag sources (move the entry to another day), same as + // stay area bands (see stayBands.js). The "…continues" and "dropoff" ghost + // chips stay non-draggable — they're derived markers, not the entry itself. makeChipDraggable(node, entry); return node; } diff --git a/public/js/views/dragdrop.js b/public/js/views/dragdrop.js index 439ee52..709e681 100644 --- a/public/js/views/dragdrop.js +++ b/public/js/views/dragdrop.js @@ -1,6 +1,7 @@ // HTML5 drag-and-drop helpers, no libraries. Three flows: -// • Calendar — drag a day chip onto another in-range day to move that entry -// (shifts end_date by the same delta so multi-day spans stay intact). +// • Calendar — drag a day chip (or a stay area band) onto another in-range +// day to move that entry (shifts end_date by the same delta so multi-day +// spans / stay lengths stay intact). // • Day editor — drag a row's handle to reorder entries within one day. // • Trip dashboard — drag a card's handle to reorder trips (shares the day // editor's row-reorder machinery, see enableReorder below). @@ -30,6 +31,21 @@ export function makeChipDraggable(node, entry) { node.addEventListener('dragend', () => node.classList.remove('dragging')); } +// Mark a stay area band segment as a drag source carrying its entry id. Same +// mechanics as makeChipDraggable — dropping on an in-range day moves the +// whole stay to start there, preserving its length (end_date shifts by the +// same delta in makeDayDropTarget below). +export function makeBandDraggable(node, entry) { + node.setAttribute('draggable', 'true'); + node.classList.add('cal-band-draggable'); + node.addEventListener('dragstart', (e) => { + e.dataTransfer.setData(DND_MIME, String(entry.id)); + e.dataTransfer.effectAllowed = 'move'; + node.classList.add('dragging'); + }); + node.addEventListener('dragend', () => node.classList.remove('dragging')); +} + // Wire an in-range day cell as a drop target. On drop the dragged entry moves to // `targetDate` with sort_order = `targetCount` (appended after that day's // entries); an entry with an end_date has it shifted by the same day-delta in diff --git a/public/js/views/stayBands.js b/public/js/views/stayBands.js index cde1c6a..e0d178e 100644 --- a/public/js/views/stayBands.js +++ b/public/js/views/stayBands.js @@ -4,6 +4,7 @@ // partitioning) so a stay keeps the same vertical position across weeks. import { el } from '../dom.js'; import { parseYMD, daysBetweenInclusive, typeInfo, stayShortName, entrySpanDays } from '../format.js'; +import { makeBandDraggable } from './dragdrop.js'; // Per-instance band palette: each stay gets its own hue (not the generic stay // type colour) so overlapping/adjacent areas read apart. Muted-but-distinct @@ -97,6 +98,9 @@ export function renderWeekBands(weekYmd, layout, tctx) { band.addEventListener('keydown', (e) => { if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); open(e); } }); + // Standard HTML5 DnD doesn't fire click after a real drag, so a plain + // click still opens the day editor with no extra guard needed. + makeBandDraggable(band, s.entry); strip.appendChild(band); } return strip;