The pickup seeds its own slot — the lift and the shadow become one transaction
"At drag start it replaces the item's original space" is now true from the very first frame: begin takes the run's own resting slot as a seed, set in the same transaction that lifts the run out. Without it the vacated gap closed un-animated and sprang back open at the first dropUpdated — a shuffle carrying no information. The seed bypasses propose deliberately (a pickup is not a new landing spot, so no alignment tick), and the first real sample's re-propose of the same slot is the early-out's ordinary silence. A ⌥-pickup seeds nothing — a copy's resting layout keeps the originals in place, so there is no vacated space to hold. Card pickups compute the seed through the same LaneView.rendered rules the retargets count in; lane pickups seed their strip slot the same way. DRAG-REORDER.md's own-slot paragraph records the rule, and SeededPickupTests pins all four corners. Claude-Session: https://claude.ai/code/session_014PtZdPwqZuqEDLc6wZMtEy
This commit is contained in:
@@ -408,11 +408,39 @@ struct CardFaceView: View, Equatable {
|
||||
// span-cap (03-board-ui.md § Motion).
|
||||
heights: ordered.map { drops.registry.heights[$0] ?? drops.registry.nominalCardHeight },
|
||||
container: .board,
|
||||
source: store
|
||||
source: store,
|
||||
seed: ownSlotSeed(dragging: ids, laneID: lanesByCard[card.id], in: snapshot)
|
||||
)
|
||||
return payload.itemProvider()
|
||||
}
|
||||
|
||||
/// The pickup's own-slot proposal (`DragSession.begin`): the grabbed card's position among its
|
||||
/// lane's rendered cards with the dragged run lifted out — the same resting-layout index space
|
||||
/// the retargets and `moveCards` count in, filtered by the same `LaneView.rendered` rules, so
|
||||
/// the seeded shadow run stands exactly where the lifted cards stood and pickup moves nothing.
|
||||
///
|
||||
/// **A ⌥-pickup seeds nothing.** A copy's resting layout keeps the originals in place, so there
|
||||
/// is no vacated space for a shadow to hold; the first `dropUpdated` resolves the copy and opens
|
||||
/// the run beside the originals, exactly as it does today (DRAG-REORDER.md § Resting-layout
|
||||
/// zones — the modifier reflow is the feedback being asked for).
|
||||
private func ownSlotSeed(
|
||||
dragging ids: Set<ItemID>,
|
||||
laneID: ItemID?,
|
||||
in snapshot: BoardModel
|
||||
) -> DropTarget? {
|
||||
guard !NSEvent.modifierFlags.contains(.option),
|
||||
let laneID,
|
||||
let laneCards = snapshot.lanes.first(where: { $0.id == laneID })?.cards
|
||||
else { return nil }
|
||||
let index = LaneView.rendered(
|
||||
Array(laneCards.prefix { $0.id != card.id }),
|
||||
hiddenByDrag: ids,
|
||||
filter: store.searchFilter,
|
||||
renaming: store.transient.renameEditor?.targetID
|
||||
).count
|
||||
return DropTarget(boardRoot: store.rootKey, container: .lane(laneID), index: index)
|
||||
}
|
||||
|
||||
/// A trash card's drag out — **the restore**, and deliberately not special: an ordinary `.cards`
|
||||
/// session in the `.trash` container, which `BoardDropContext.commitDrop` hands to the same
|
||||
/// `moveCards`/`copyCards`/`receiveCards` every board card uses. "Restoring is an ordinary move
|
||||
|
||||
Reference in New Issue
Block a user