Skip to content

Commit eaac6cb

Browse files
dylanjeffersclaude
andcommitted
fix(web): polish playlist edit-mode shift+click range selection
Two small correctness fixes on top of the existing row-click selection in playlist edit mode. - `EditAwareTracksTable` now resets its shift-key ref on `window` blur. Previously, holding Shift while Cmd/Alt-Tabbing away would leave the ref stuck true (the keyup fires in the other window), so the next click on return was interpreted as a shift-extend. - `TrackSelectionContext.toggle` now keeps the anchor row stable across a sequence of shift-clicks. The anchor only moves on a plain click. This matches Finder / Gmail / Drive: click A, shift-click C selects A..C; a follow-up shift-click E then selects A..E (not C..E). Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
1 parent 865bd00 commit eaac6cb

2 files changed

Lines changed: 17 additions & 3 deletions

File tree

packages/web/src/components/collection/desktop/edit-mode/tracks/EditAwareTracksTable.tsx

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,11 +42,18 @@ export const EditAwareTracksTable = (props: EditAwareTracksTableProps) => {
4242
const up = (e: KeyboardEvent) => {
4343
if (e.key === 'Shift') shiftRef.current = false
4444
}
45+
// Without this, Cmd/Alt-Tabbing away while holding Shift leaves shiftRef
46+
// stuck true because the keyup fires in the other window.
47+
const reset = () => {
48+
shiftRef.current = false
49+
}
4550
window.addEventListener('keydown', down)
4651
window.addEventListener('keyup', up)
52+
window.addEventListener('blur', reset)
4753
return () => {
4854
window.removeEventListener('keydown', down)
4955
window.removeEventListener('keyup', up)
56+
window.removeEventListener('blur', reset)
5057
}
5158
}, [isEditingThis])
5259

packages/web/src/components/collection/desktop/edit-mode/tracks/TrackSelectionContext.tsx

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -42,10 +42,13 @@ export const TrackSelectionProvider = ({
4242

4343
const toggle = useCallback<SelectionContextValue['toggle']>(
4444
(id, index, opts) => {
45+
const isShiftRange = !!opts?.shift && lastIndexRef.current !== null
4546
setSelectedState((prev) => {
4647
const next = new Set(prev)
47-
if (opts?.shift && lastIndexRef.current !== null) {
48-
const [a, b] = [lastIndexRef.current, index].sort((x, y) => x - y)
48+
if (isShiftRange) {
49+
const [a, b] = [lastIndexRef.current as number, index].sort(
50+
(x, y) => x - y
51+
)
4952
for (let i = a; i <= b; i += 1) {
5053
const sliceId = orderedIds[i]
5154
if (sliceId !== undefined) next.add(sliceId)
@@ -55,9 +58,13 @@ export const TrackSelectionProvider = ({
5558
} else {
5659
next.add(id)
5760
}
58-
lastIndexRef.current = index
5961
return next
6062
})
63+
// Keep the anchor stable across a shift-click sequence (matches
64+
// Finder / Gmail / Drive). A plain click resets the anchor.
65+
if (!isShiftRange) {
66+
lastIndexRef.current = index
67+
}
6168
},
6269
[orderedIds]
6370
)

0 commit comments

Comments
 (0)