Skip to content

Commit 503bdd1

Browse files
dylanjeffersclaude
andcommitted
feat(web): row-click selection on playlist tracks in edit mode
While the playlist detail page is in edit mode, clicking a track row toggles its selection in the bulk-actions context instead of playing the track. Holding shift while clicking extends the selection over the range between the previous click and the new one. - New `EditAwareTracksTable` wrapper around the standard `TracksTable`. It captures the global shift-key state with a window listener (TracksTable's onClickRow does not pass a MouseEvent) and rewrites `onClickRow` to call `selection.toggle(id, index, { shift })` when edit mode is active. - Outside of edit mode the wrapper is a transparent pass-through and the existing play-on-click behavior is preserved. - Desktop `CollectionPage` swaps its `TracksTable` usage for the new edit-aware wrapper. Combined with the bulk-actions bar from the previous PR, the user can now: shift-click a range, Cmd/Ctrl+A to select all, Escape to clear, Delete to remove, Cmd/Ctrl+Z/Y for undo/redo, and the bar's Copy URLs / Remove buttons for bulk operations. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 46c2b73 commit 503bdd1

2 files changed

Lines changed: 69 additions & 2 deletions

File tree

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
import { ComponentProps, useCallback, useEffect, useRef } from 'react'
2+
3+
import { ID } from '@audius/common/models'
4+
5+
import { TracksTable } from 'components/tracks-table'
6+
7+
import { usePlaylistEditMode } from '../PlaylistEditModeContext'
8+
9+
import { useTrackSelection } from './TrackSelectionContext'
10+
11+
type TrackLike = { track_id?: number | null } & Record<string, unknown>
12+
13+
type EditAwareTracksTableProps = ComponentProps<typeof TracksTable> & {
14+
collectionId: ID
15+
}
16+
17+
/**
18+
* Wraps the standard TracksTable for the playlist detail page so that, while
19+
* the page is in edit mode, clicking a row toggles selection (shift to extend
20+
* the range) instead of activating playback. Outside of edit mode the
21+
* behavior is identical to the underlying TracksTable.
22+
*/
23+
export const EditAwareTracksTable = (props: EditAwareTracksTableProps) => {
24+
const { collectionId, onClickRow, ...rest } = props
25+
const editMode = usePlaylistEditMode()
26+
const selection = useTrackSelection()
27+
const isEditingThis =
28+
editMode.isEditMode && editMode.collectionId === collectionId
29+
30+
// Capture shift modifier state from keyboard so we can extend the selection
31+
// even though TracksTable's onClickRow does not pass the MouseEvent.
32+
const shiftRef = useRef(false)
33+
useEffect(() => {
34+
if (!isEditingThis) {
35+
shiftRef.current = false
36+
return
37+
}
38+
const down = (e: KeyboardEvent) => {
39+
if (e.key === 'Shift') shiftRef.current = true
40+
}
41+
const up = (e: KeyboardEvent) => {
42+
if (e.key === 'Shift') shiftRef.current = false
43+
}
44+
window.addEventListener('keydown', down)
45+
window.addEventListener('keyup', up)
46+
return () => {
47+
window.removeEventListener('keydown', down)
48+
window.removeEventListener('keyup', up)
49+
}
50+
}, [isEditingThis])
51+
52+
const handleClickRow = useCallback(
53+
(track: TrackLike, index: number) => {
54+
if (!isEditingThis) {
55+
onClickRow?.(track, index)
56+
return
57+
}
58+
const id = track.track_id
59+
if (typeof id !== 'number') return
60+
selection.toggle(id, index, { shift: shiftRef.current })
61+
},
62+
[isEditingThis, onClickRow, selection]
63+
)
64+
65+
return <TracksTable {...rest} onClickRow={handleClickRow} />
66+
}

packages/web/src/pages/collection-page/components/desktop/CollectionPage.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,14 +24,14 @@ import { CollectionDogEar } from 'components/collection'
2424
import { CollectionHeader } from 'components/collection/desktop/CollectionHeader'
2525
import { PlaylistEditModeBar } from 'components/collection/desktop/edit-mode/PlaylistEditModeBar'
2626
import { PlaylistEditModeProvider } from 'components/collection/desktop/edit-mode/PlaylistEditModeContext'
27+
import { EditAwareTracksTable } from 'components/collection/desktop/edit-mode/tracks/EditAwareTracksTable'
2728
import { TrackBulkActionsBar } from 'components/collection/desktop/edit-mode/tracks/TrackBulkActionsBar'
2829
import { TrackHistoryProvider } from 'components/collection/desktop/edit-mode/tracks/TrackHistoryContext'
2930
import { TrackSelectionProvider } from 'components/collection/desktop/edit-mode/tracks/TrackSelectionContext'
3031
import FilterInput from 'components/filter-input/FilterInput'
3132
import Page from 'components/page/Page'
3233
import { SuggestedTracks } from 'components/suggested-tracks'
3334
import { RESPONSIVE_TABLE_POLICIES } from 'components/table/responsivePolicies'
34-
import { TracksTable } from 'components/tracks-table'
3535
import { useRequiresAccountCallback } from 'hooks/useRequiresAccount'
3636
import { useMainContentRef } from 'pages/MainContentContext'
3737
import { computeCollectionMetadataProps } from 'pages/collection-page/store/utils'
@@ -349,7 +349,8 @@ const CollectionPage = ({ type }: CollectionPageProps) => {
349349
<NoSearchResultsContent />
350350
) : (
351351
<div className={styles.tableWrapper}>
352-
<TracksTable
352+
<EditAwareTracksTable
353+
collectionId={playlistId!}
353354
// @ts-ignore
354355
columns={tracksTableColumns}
355356
wrapperClassName={styles.tracksTableWrapper}

0 commit comments

Comments
 (0)