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:
@@ -5,7 +5,29 @@
|
||||
import { el } from '../dom.js';
|
||||
import { parseYMD, daysBetweenInclusive, typeInfo, stayShortName, entrySpanDays } from '../format.js';
|
||||
|
||||
// Returns { stays: [{entry,start,end,name,days,lane}], laneCount }.
|
||||
// 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
|
||||
// tones that harmonise with the entry-type palette and keep #334155 text
|
||||
// readable on their color-mix(…20%, white) band backgrounds. Assignment is
|
||||
// deterministic — index in the chronologically-sorted stays list % 8 — so a
|
||||
// stay keeps its colour across re-renders, week rows, and the Areas list.
|
||||
export const STAY_PALETTE = [
|
||||
'#f59e0b', // amber
|
||||
'#14b8a6', // teal
|
||||
'#8b5cf6', // violet
|
||||
'#f43f5e', // rose
|
||||
'#0ea5e9', // sky
|
||||
'#65a30d', // lime
|
||||
'#f97316', // orange
|
||||
'#6366f1', // indigo
|
||||
];
|
||||
|
||||
// Deterministic colour for the Nth stay in chronological order.
|
||||
export function stayColor(index) {
|
||||
return STAY_PALETTE[((index % STAY_PALETTE.length) + STAY_PALETTE.length) % STAY_PALETTE.length];
|
||||
}
|
||||
|
||||
// Returns { stays: [{entry,start,end,name,days,lane,color}], laneCount }.
|
||||
export function computeStayLayout(entries) {
|
||||
const stays = entries
|
||||
.filter((e) => e.type === 'stay')
|
||||
@@ -16,9 +38,14 @@ export function computeStayLayout(entries) {
|
||||
name: stayShortName(e),
|
||||
days: entrySpanDays(e),
|
||||
lane: 0,
|
||||
color: null,
|
||||
}))
|
||||
.sort((a, b) => (a.start < b.start ? -1 : a.start > b.start ? 1 : a.entry.id - b.entry.id));
|
||||
|
||||
// Colour by chronological index (same ordering summary.areas uses), assigned
|
||||
// here next to lanes so every consumer of the layout shares one source.
|
||||
stays.forEach((s, i) => { s.color = stayColor(i); });
|
||||
|
||||
const laneEnds = []; // last end date (ymd) occupying each lane
|
||||
for (const s of stays) {
|
||||
// A lane is free if its last stay ended strictly before this one starts.
|
||||
@@ -58,7 +85,7 @@ export function renderWeekBands(weekYmd, layout, tctx) {
|
||||
|
||||
const band = el('div', {
|
||||
class: `cal-band${roundLeft ? ' round-l' : ''}${roundRight ? ' round-r' : ''}`,
|
||||
style: { gridColumn: `${col + 1} / span ${span}`, gridRow: String(s.lane + 1), '--chip': info.color },
|
||||
style: { gridColumn: `${col + 1} / span ${span}`, gridRow: String(s.lane + 1), '--chip': s.color },
|
||||
role: 'button',
|
||||
tabindex: '0',
|
||||
title: `${info.icon} ${s.name} · ${s.days} ${s.days === 1 ? 'day' : 'days'}`,
|
||||
|
||||
Reference in New Issue
Block a user