multi-select on track-list
This commit is contained in:
@@ -23,10 +23,10 @@ export class TrackList extends LitElement {
|
|||||||
private tracks: library.Track[] = [];
|
private tracks: library.Track[] = [];
|
||||||
|
|
||||||
@state()
|
@state()
|
||||||
private contextMenuOpen = false;
|
private selectedTracks: Set<string> = new Set();
|
||||||
|
|
||||||
@state()
|
@state()
|
||||||
private contextMenuTrack: library.Track | null = null;
|
private contextMenuOpen = false;
|
||||||
|
|
||||||
@state()
|
@state()
|
||||||
private playlistSubmenuOpen = false;
|
private playlistSubmenuOpen = false;
|
||||||
@@ -68,6 +68,8 @@ export class TrackList extends LitElement {
|
|||||||
border-bottom: 1px solid #333;
|
border-bottom: 1px solid #333;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
cursor: default;
|
||||||
|
user-select: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.track-row > * {
|
.track-row > * {
|
||||||
@@ -78,30 +80,28 @@ export class TrackList extends LitElement {
|
|||||||
background-color: rgba(255, 255, 255, 0.05);
|
background-color: rgba(255, 255, 255, 0.05);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.track-row.selected {
|
||||||
|
background-color: rgba(100, 160, 255, 0.15);
|
||||||
|
}
|
||||||
|
|
||||||
.track-row.active {
|
.track-row.active {
|
||||||
background-color: rgba(255, 212, 59, 0.1);
|
background-color: rgba(255, 212, 59, 0.1);
|
||||||
}
|
}
|
||||||
|
|
||||||
.track-row.active .track-name-button {
|
.track-row.active .track-name {
|
||||||
color: #ffd43b;
|
color: #ffd43b;
|
||||||
}
|
}
|
||||||
|
|
||||||
.track-name-button {
|
.track-row.selected.active {
|
||||||
background: none;
|
background-color: rgba(100, 160, 255, 0.15);
|
||||||
border: none;
|
}
|
||||||
color: inherit;
|
|
||||||
text-align: left;
|
.track-name {
|
||||||
padding: 0;
|
|
||||||
cursor: pointer;
|
|
||||||
width: 100%;
|
|
||||||
font: inherit;
|
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
}
|
cursor: default;
|
||||||
|
user-select: none;
|
||||||
.track-name-button:hover {
|
|
||||||
text-decoration: underline;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.artist-name,
|
.artist-name,
|
||||||
@@ -169,6 +169,7 @@ export class TrackList extends LitElement {
|
|||||||
try {
|
try {
|
||||||
const tracks = await GetAllTracks();
|
const tracks = await GetAllTracks();
|
||||||
this.tracks = tracks;
|
this.tracks = tracks;
|
||||||
|
this.selectedTracks = new Set();
|
||||||
|
|
||||||
if (tracks[0]) {
|
if (tracks[0]) {
|
||||||
LogPrint(tracks[0].TrackName);
|
LogPrint(tracks[0].TrackName);
|
||||||
@@ -178,7 +179,32 @@ export class TrackList extends LitElement {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private onTrackClick(track: library.Track) {
|
private getSelectedFilePaths(): string[] {
|
||||||
|
return this.tracks
|
||||||
|
.filter((t) => this.selectedTracks.has(t.FilePath))
|
||||||
|
.map((t) => t.FilePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
private onTrackRowClick(e: MouseEvent, track: library.Track) {
|
||||||
|
const isCtrl = e.ctrlKey || e.metaKey;
|
||||||
|
|
||||||
|
if (isCtrl) {
|
||||||
|
const next = new Set(this.selectedTracks);
|
||||||
|
|
||||||
|
if (next.has(track.FilePath)) {
|
||||||
|
next.delete(track.FilePath);
|
||||||
|
} else {
|
||||||
|
next.add(track.FilePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.selectedTracks = next;
|
||||||
|
} else {
|
||||||
|
this.selectedTracks = new Set([track.FilePath]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private onTrackRowDblClick(track: library.Track) {
|
||||||
|
this.selectedTracks = new Set();
|
||||||
this.queue.setQueue([track.FilePath], 0);
|
this.queue.setQueue([track.FilePath], 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -186,7 +212,10 @@ export class TrackList extends LitElement {
|
|||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
|
|
||||||
this.contextMenuTrack = track;
|
if (!this.selectedTracks.has(track.FilePath)) {
|
||||||
|
this.selectedTracks = new Set([track.FilePath]);
|
||||||
|
}
|
||||||
|
|
||||||
this.contextMenuOpen = true;
|
this.contextMenuOpen = true;
|
||||||
|
|
||||||
// Position the popup at the mouse cursor using a virtual anchor.
|
// Position the popup at the mouse cursor using a virtual anchor.
|
||||||
@@ -214,31 +243,34 @@ export class TrackList extends LitElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private onContextMenuAction(action: string) {
|
private onContextMenuAction(action: string) {
|
||||||
if (!this.contextMenuTrack) return;
|
const filePaths = this.getSelectedFilePaths();
|
||||||
|
|
||||||
const filePath = this.contextMenuTrack.FilePath;
|
if (filePaths.length === 0) return;
|
||||||
|
|
||||||
switch (action) {
|
switch (action) {
|
||||||
case 'play':
|
case 'play':
|
||||||
this.queue.setQueue([filePath], 0);
|
this.queue.setQueue(filePaths, 0);
|
||||||
break;
|
break;
|
||||||
case 'add-to-queue':
|
case 'add-to-queue':
|
||||||
this.queue.addToQueue(filePath);
|
this.queue.addTracksToQueue(filePaths);
|
||||||
break;
|
break;
|
||||||
case 'play-next':
|
case 'play-next':
|
||||||
this.queue.playNext(filePath);
|
this.queue.playTracksNext(filePaths);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.closeContextMenu();
|
this.closeContextMenu(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
private closeContextMenu() {
|
private closeContextMenu(clearSelection = false) {
|
||||||
if (!this.contextMenuOpen) return;
|
if (!this.contextMenuOpen) return;
|
||||||
|
|
||||||
this.closePlaylistSubmenu();
|
this.closePlaylistSubmenu();
|
||||||
this.contextMenuOpen = false;
|
this.contextMenuOpen = false;
|
||||||
this.contextMenuTrack = null;
|
|
||||||
|
if (clearSelection) {
|
||||||
|
this.selectedTracks = new Set();
|
||||||
|
}
|
||||||
|
|
||||||
const popup = this.contextMenuPopup;
|
const popup = this.contextMenuPopup;
|
||||||
|
|
||||||
@@ -282,7 +314,7 @@ export class TrackList extends LitElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private onPlaylistActionComplete = () => {
|
private onPlaylistActionComplete = () => {
|
||||||
this.closeContextMenu();
|
this.closeContextMenu(true);
|
||||||
};
|
};
|
||||||
|
|
||||||
private isActiveTrack(track: library.Track): boolean {
|
private isActiveTrack(track: library.Track): boolean {
|
||||||
@@ -295,22 +327,29 @@ export class TrackList extends LitElement {
|
|||||||
|
|
||||||
private renderTrackRow = (track: library.Track): unknown => {
|
private renderTrackRow = (track: library.Track): unknown => {
|
||||||
const active = this.isActiveTrack(track);
|
const active = this.isActiveTrack(track);
|
||||||
|
const selected = this.selectedTracks.has(track.FilePath);
|
||||||
|
|
||||||
|
const classes = [
|
||||||
|
'track-row',
|
||||||
|
active ? 'active' : '',
|
||||||
|
selected ? 'selected' : '',
|
||||||
|
]
|
||||||
|
.filter(Boolean)
|
||||||
|
.join(' ');
|
||||||
|
|
||||||
return html`
|
return html`
|
||||||
<div
|
<div
|
||||||
class="track-row ${active ? 'active' : ''}"
|
class=${classes}
|
||||||
@contextmenu=${(e: MouseEvent) => this.onTrackContextMenu(e, track)}
|
@click=${(e: MouseEvent) => this.onTrackRowClick(e, track)}
|
||||||
|
@dblclick=${() => this.onTrackRowDblClick(track)}
|
||||||
|
@contextmenu=${(e: MouseEvent) =>
|
||||||
|
this.onTrackContextMenu(e, track)}
|
||||||
>
|
>
|
||||||
<div>
|
<div class="track-name">${track.TrackName}</div>
|
||||||
<button
|
|
||||||
class="track-name-button"
|
|
||||||
@click=${() => this.onTrackClick(track)}
|
|
||||||
>
|
|
||||||
${track.TrackName}
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<div class="artist-name">${track.ArtistName}</div>
|
<div class="artist-name">${track.ArtistName}</div>
|
||||||
<div class="track-length">${formatMilliseconds(track.TrackLength)}</div>
|
<div class="track-length">
|
||||||
|
${formatMilliseconds(track.TrackLength)}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
};
|
};
|
||||||
@@ -381,10 +420,10 @@ export class TrackList extends LitElement {
|
|||||||
placement="right-start"
|
placement="right-start"
|
||||||
.active=${this.playlistSubmenuOpen}
|
.active=${this.playlistSubmenuOpen}
|
||||||
>
|
>
|
||||||
${this.playlistSubmenuOpen && this.contextMenuTrack
|
${this.playlistSubmenuOpen && this.selectedTracks.size > 0
|
||||||
? html`
|
? html`
|
||||||
<playlist-picker
|
<playlist-picker
|
||||||
.filePaths=${[this.contextMenuTrack.FilePath]}
|
.filePaths=${this.getSelectedFilePaths()}
|
||||||
@playlist-action-complete=${this.onPlaylistActionComplete}
|
@playlist-action-complete=${this.onPlaylistActionComplete}
|
||||||
@click=${(e: Event) => e.stopPropagation()}
|
@click=${(e: Event) => e.stopPropagation()}
|
||||||
></playlist-picker>
|
></playlist-picker>
|
||||||
|
|||||||
Executable → Regular
Executable → Regular
Reference in New Issue
Block a user