# Update — Calendar multi-select drag feedback (bug fixes) **Audience:** the junior developer who implemented the calendar multi-select feature. **Area:** `frontend/` only. No backend changes. This is a small, focused follow-up — the feature works, these are two visual/interaction bugs in the *drag-to-select* behavior. > Great work on the feature — bars, week numbers, bulk apply and the legend all look good. There are exactly **two** things to fix, described below. Please change **only** what's needed for these two; don't touch the save/bulk-apply logic, the bar rendering, or the modal behavior. --- ## The two bugs (what the user sees vs. what they want) ### Bug 1 — Days are NOT highlighted *while* you drag-select **What happens now:** When you press and drag across days, the day cells stay white during the drag. The blue "selected" highlight (light blue background + blue border) only appears **after** you release the mouse and the "Plan N days" dialog opens. **What we want:** As soon as you start dragging, every day in the current selection range should be highlighted **immediately and live**, updating as you drag over more days — and it should look just like the **hover** state of a single day. The highlight must be visible *during* the drag, before the dialog appears. ### Bug 2 — The day numbers get text-selected (blue "selecting text" boxes) **What happens now:** While dragging, the browser selects the day-number text, so the numbers (1, 2, 3, …) get the blue "highlighted text" look — exactly like dragging to select text on a web page. You can see this on the numbers in both screenshots. **What we want:** Dragging across the calendar must **never** select text. No day number (or any other calendar text) should get the text-selection highlight while dragging. --- ## Root cause (so the fix is obvious) ### Bug 1 root cause — in `src/pages/Calendar.tsx` The `.calendar-day--selected` highlight is driven by the `selectedDates` state, which is passed down to ``. But `selectedDates` is **only written in the `pointerup` handler** (the `useEffect` around lines 29–49): ```ts const handlePointerUp = () => { if (isDragging && dragAnchor && dragCurrent) { const dates = datesInRange(dragAnchor, dragCurrent) setSelectedDates(new Set(dates)) // <-- only happens on RELEASE ... setIsModalOpen(true) } setIsDragging(false) } ``` During the drag itself, only `dragAnchor` and `dragCurrent` change (via `handlePointerEnter`). Because `selectedDates` is still empty until release, nothing is highlighted while dragging. That's the whole bug — it's a *timing* problem, not a styling problem. ### Bug 2 root cause — in `src/pages/calendar.css` `.calendar-day` (and its `.calendar-day__number` child) have **no `user-select` rule**, so the browser's default text selection kicks in during a drag. Note the week-number cells (`.calendar__week-number`) already set `user-select: none` — the day cells just need the same. --- ## Exactly what to change ### Fix 1 — show the selection live during the drag (`src/pages/Calendar.tsx`) Render the highlight from a **derived "preview" set** instead of only the committed `selectedDates`. While dragging, the preview is the range between the anchor and the current day; otherwise it's the committed selection. Add this just before the `return` (near where `firstSelectedDate` is computed, ~line 184): ```ts // Dates to highlight in the grid. While dragging, show a live preview of the // range under the cursor; otherwise show the committed selection. const highlightedDates = isDragging && dragAnchor && dragCurrent ? new Set(datesInRange(dragAnchor, dragCurrent)) : selectedDates ``` Then pass `highlightedDates` (not `selectedDates`) to the week: ```tsx ``` **Important — do NOT change `selectedDates` itself or how it's used elsewhere.** The committed `selectedDates` is what `handleSavePlannedDay` / `handleDeletePlannedDay` read on save. We are only changing **what gets rendered as highlighted**, not what gets saved. Leave the `pointerup` handler and the save logic exactly as they are. **Make the highlight match hover.** The look is already close, but make sure `.calendar-day--selected` matches `.calendar-day:hover` so "selecting" and "hovering" look identical. In `src/pages/calendar.css` the hover rule is: ```css .calendar-day:hover { border-color: #0066cc; box-shadow: 0 2px 8px rgba(0, 102, 204, 0.1); background-color: #f9fbff; } ``` The existing `.calendar-day--selected` (lines ~117–121) already uses the same three values, so this should need no change — just verify they stay in sync. Do **not** invent a new selected color; the requirement is "same as hover." ### Fix 2 — stop text selection while dragging (`src/pages/calendar.css`) Add `user-select: none` to the day cell so dragging can never select the numbers. Put it on `.calendar-day` (it will also cover the `.calendar-day__number` child): ```css .calendar-day { /* ...existing rules... */ user-select: none; -webkit-user-select: none; /* Safari */ } ``` Keep both lines — Safari still needs the `-webkit-` prefix, and ESLint/CI won't complain about it. (If you prefer, you can instead add it once to the grid container `.calendar__grid` so it covers the whole calendar; either is acceptable, but `.calendar-day` is the minimal, targeted change.) --- ## What NOT to touch - The bulk-apply / save loop in `handleSavePlannedDay`, and the delete logic. - The spanning-bar rendering, lane layout, or `CalendarWeek` props (other than the one value passed to `selectedDates`). - The modal, week-number gutter, or legend. - `selectedDates` state semantics — it stays the *committed* selection used for saving. --- ## How to verify (manual) 1. Turn on week numbers in Settings (optional) and open the calendar. 2. Press on a day and drag across several days **without releasing**: - ✅ Every day under the drag is highlighted live (same look as hovering one day), updating as you move. - ✅ No day number gets the blue text-selection box; nothing on the calendar looks like selected text. 3. Release the mouse: the "Plan N days" dialog opens and the same days stay highlighted (unchanged from today). 4. Cancel/Save: highlight clears (unchanged from today). 5. A single click still opens the single-day modal (unchanged). --- ## Tests (must stay green + add coverage) CI gates from `frontend/` — all must pass: ```bash npx tsc --noEmit npm run lint # zero warnings npm run coverage # 90% gate ``` Add a test in `src/pages/Calendar.test.tsx` (or the existing multi-select test) proving the **live** highlight: - Fire `pointerDown` on one current-month day, then `pointerEnter` on a later day **without** firing `pointerup`. - Assert that the cells in that range now have the `calendar-day--selected` class **before** any `pointerup` — this is exactly the behavior that was missing. (There is no clean unit assertion for "text isn't selectable" — that's enforced by the CSS rule; cover it in the manual check above. If you want a guard, a small test asserting `.calendar-day` has the expected class is enough; the `user-select` value itself isn't worth asserting in jsdom.) Don't delete or weaken any existing calendar test to make things pass (see `AGENTS.md`). After the change, run `graphify update .`. --- ## Definition of done - [ ] Dragging highlights the day range **live**, matching the hover look, before the dialog opens. - [ ] No text (day numbers or otherwise) gets selected while dragging. - [ ] Committed selection, save/bulk-apply, delete, modal, bars, week numbers and legend all behave exactly as before. - [ ] A test covers the live (pre-`pointerup`) highlight. - [ ] `tsc --noEmit`, `npm run lint` (0 warnings), `npm run coverage` (≥90%) pass; existing tests still green. - [ ] `graphify update .` run. ```