Add drag & drop for entries and per-stay band colours
Calendar chips can be dragged onto another in-range day (multi-day entries shift end_date by the same delta in one PATCH); day-editor rows get a drag handle to reorder within a day, patching sort_order only for changed positions. Continues/dropoff ghost chips and stay bands are not draggable; touch devices fall back to the existing click/edit flow. Each stay band now gets a deterministic colour from an 8-hue palette by chronological index, mirrored as dots in the summary Areas list.
This commit is contained in:
@@ -16,6 +16,7 @@ import {
|
||||
isMultiDay,
|
||||
} from '../format.js';
|
||||
import { computeStayLayout, renderWeekBands } from './stayBands.js';
|
||||
import { makeChipDraggable, makeDayDropTarget } from './dragdrop.js';
|
||||
|
||||
const WEEKDAYS = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'];
|
||||
|
||||
@@ -54,6 +55,8 @@ export function renderCalendar(tctx) {
|
||||
}
|
||||
|
||||
const stayLayout = computeStayLayout(entries);
|
||||
// id -> entry, for the drop handler to resolve the dragged chip.
|
||||
const entriesById = new Map(entries.map((e) => [e.id, e]));
|
||||
|
||||
const rangeStart = parseYMD(trip.start_date);
|
||||
const rangeEnd = parseYMD(trip.end_date);
|
||||
@@ -97,17 +100,23 @@ export function renderCalendar(tctx) {
|
||||
if (bands) weekEl.appendChild(bands);
|
||||
const daysRow = el('div', { class: 'cal-week-days' });
|
||||
for (const date of week) {
|
||||
daysRow.appendChild(dayCell(date, rangeStart, rangeEnd, byDate, contByDate, dropoffByDate, tctx, currency));
|
||||
daysRow.appendChild(dayCell(date, rangeStart, rangeEnd, byDate, contByDate, dropoffByDate, entriesById, tctx, currency));
|
||||
}
|
||||
weekEl.appendChild(daysRow);
|
||||
cal.appendChild(weekEl);
|
||||
}
|
||||
|
||||
// A cancelled drag (released outside a cell) fires dragend on the source chip,
|
||||
// which bubbles up here — sweep any lingering drop highlight.
|
||||
cal.addEventListener('dragend', () => {
|
||||
for (const n of cal.querySelectorAll('.cal-day-drop')) n.classList.remove('cal-day-drop');
|
||||
});
|
||||
|
||||
section.appendChild(cal);
|
||||
return section;
|
||||
}
|
||||
|
||||
function dayCell(date, rangeStart, rangeEnd, byDate, contByDate, dropoffByDate, tctx, currency) {
|
||||
function dayCell(date, rangeStart, rangeEnd, byDate, contByDate, dropoffByDate, entriesById, tctx, currency) {
|
||||
const key = ymd(date);
|
||||
const inRange = date >= rangeStart && date <= rangeEnd;
|
||||
// Stays render as bands, not chips, so exclude them from the day cell.
|
||||
@@ -158,6 +167,12 @@ function dayCell(date, rangeStart, rangeEnd, byDate, contByDate, dropoffByDate,
|
||||
});
|
||||
}
|
||||
|
||||
// Only in-range days accept dropped entries; new sort_order appends after the
|
||||
// day's current entries (byDate holds every entry, incl. stays, for that day).
|
||||
if (inRange) {
|
||||
makeDayDropTarget(cell, key, (byDate.get(key) || []).length, entriesById, tctx);
|
||||
}
|
||||
|
||||
return cell;
|
||||
}
|
||||
|
||||
@@ -167,7 +182,7 @@ function chip(entry, currency) {
|
||||
const chain = hasSegments(entry) ? flightChain(entry.segments) : '';
|
||||
const car = hasRental(entry) ? [entry.rental.brand, entry.rental.model].filter(Boolean).join(' ') : '';
|
||||
const label = chain || car || entry.title;
|
||||
return el(
|
||||
const node = el(
|
||||
'div',
|
||||
{
|
||||
class: 'cal-chip',
|
||||
@@ -180,6 +195,10 @@ 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.
|
||||
makeChipDraggable(node, entry);
|
||||
return node;
|
||||
}
|
||||
|
||||
// Ghost chip on the days a multi-day entry spans after its start. Clicking
|
||||
|
||||
Reference in New Issue
Block a user