fix: DayTimeline DnD drop always lands at slot 0 (z-order race)#299
Merged
fix: DayTimeline DnD drop always lands at slot 0 (z-order race)#299
Conversation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
Dragging a task from the plan's anchor list and dropping it onto the DayTimeline always scheduled it at 6:00 AM (slot 0) instead of the visually targeted time slot.
Root cause — z-order race:
TaskCardblocks in DOM order; TaskCards have higher z-order by default.overSlotIndexwas computed fromclientY(viaslotIndexFromClientY), any inaccuracy ingetBoundingClientRect()at drag-start (e.g., during the first dragover into the component) could produce slot 0.z-30(above TaskCards). Subsequent drop events hit slot 0 regardless of the cursor position, callingonSlotDrop(e, 0)→ always 6:00 AM.Fix
Set
dragActive = ref(false)inDayTimeline. On@dragenterit flips true; on@dragleave(container exit),@drop(container fallback), and@drop.stop(slot-direct) it flips false.While
dragActiveis true, TaskCard blocks receivepointer-events-none. Drops over event-block areas pass through directly to the underlying slot divs, callingonSlotDrop(e, i)with the exact slot index — noslotIndexFromClientYmath involved, no z-order race.slotIndexFromClientYandonTimedAreaDropremain as a safety net for drops landing in the hour-labels gutter column (left strip with no slot divs).Tests
onTimedAreaDrop maps clientY to the correct 15-min slot using rect.top + scrollTop— mocksgetBoundingClientRectand verifiesclientY=190 / top=100maps to slot 4 (07:00). Passes before and after (math was always correct).TaskCard event blocks receive pointer-events-none class while a drag is active— fails before fix, passes after.All 539 tests pass. Build is clean (zero
vue-tscerrors).Note on approach
Team-lead's post-compact reminder suggested
await nextTick()inonTimedAreaDrop. This is incorrect —e.clientYis immutable at event-fire time andgetBoundingClientRect()is synchronous; there is nothing to settle with a tick. The structuralpointer-events-nonefix eliminates the z-order race entirely rather than patching the coordinate math.