206 lines
5.6 KiB
Svelte
206 lines
5.6 KiB
Svelte
<script lang="ts">
|
|
import { locale } from "$i18n/i18n-svelte";
|
|
import { mdiPlay, mdiDotsVertical } from "@mdi/js";
|
|
import { createContextMenu, createDropdownMenu, melt } from "@melt-ui/svelte";
|
|
|
|
import {
|
|
albumLink,
|
|
formatDateStr,
|
|
formatDate,
|
|
userLink,
|
|
formatDuration,
|
|
} from "$lib/util/functions";
|
|
import { mainPhone } from "$lib/stores/layout";
|
|
import { CTXMENU_CFG, DROPDOWN_CFG } from "$lib/util/constants";
|
|
import { ListView, type FilteredItem, type Track, Direction } from "$lib/util/types";
|
|
|
|
import Icon from "$lib/components/ui/Icon.svelte";
|
|
import AvatarBadge from "$lib/components/ui/AvatarBadge.svelte";
|
|
import IconPlaying from "$lib/components/ui/IconPlaying.svelte";
|
|
import LinkText from "$lib/components/ui/LinkText.svelte";
|
|
import TrackTitle from "./TrackTitle.svelte";
|
|
import TrackMenu from "$lib/components/contextmenu/TrackMenu.svelte";
|
|
|
|
export let track: FilteredItem<Track>;
|
|
export let view: ListView;
|
|
export let showAuthors: boolean;
|
|
export let trackIndex: number;
|
|
export let selected: boolean;
|
|
export let selectedAbv: boolean;
|
|
export let selectedBel: boolean;
|
|
export let showIcon: boolean;
|
|
export let selectedTracks: Track[];
|
|
export let rowid: string;
|
|
export let moveIndicator: Direction | null;
|
|
|
|
let rowElm: HTMLElement;
|
|
|
|
$: ctxTracks = selected ? selectedTracks : [track.item];
|
|
|
|
function playLabel(track: Track): string {
|
|
return track.artists[0]
|
|
? `Play ${track.name} by ${track.artists[0].name}`
|
|
: `Play ${track.name}`;
|
|
}
|
|
|
|
function playTrack(event: Event) {
|
|
alert("playing " + track.item.name);
|
|
event.stopPropagation();
|
|
}
|
|
|
|
// Temporary variables
|
|
let playingId = "yt:abc";
|
|
|
|
// Context menu
|
|
/*const onOpenChange: ChangeFn<boolean> = ({ next }) => {
|
|
if (next) {
|
|
if ($screenPhone) {
|
|
alert("TODO: bottom sheet");
|
|
return false;
|
|
}
|
|
}
|
|
return next;
|
|
};*/
|
|
const {
|
|
elements: { trigger: ctxTrigger, menu: ctxMenu, item: ctxItem },
|
|
builders: { createSubmenu: createCtxSubmenu },
|
|
states: { open: ctxOpen },
|
|
} = createContextMenu(CTXMENU_CFG);
|
|
const {
|
|
elements: { trigger: ddnTrigger, menu: ddnMenu, item: ddnItem },
|
|
builders: { createSubmenu: createDdnSubmenu },
|
|
states: { open: ddnOpen },
|
|
} = createDropdownMenu(DROPDOWN_CFG);
|
|
</script>
|
|
|
|
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
|
<div
|
|
use:melt={$ctxTrigger}
|
|
class="row"
|
|
role="row"
|
|
aria-rowindex={trackIndex + 2}
|
|
class:selected
|
|
class:selected-abv={selectedAbv}
|
|
class:selected-bel={selectedBel}
|
|
class:show-icon={showIcon}
|
|
class:move-bef={moveIndicator === Direction.Backward}
|
|
class:move-aft={moveIndicator === Direction.Forward}
|
|
aria-selected={selected}
|
|
tabindex="-1"
|
|
draggable="true"
|
|
id={rowid}
|
|
bind:this={rowElm}
|
|
on:click
|
|
on:dragstart
|
|
on:dragover
|
|
on:dragleave
|
|
on:drop
|
|
>
|
|
<!-- Index column -->
|
|
<div class="col index" role="gridcell" aria-colindex={1}>
|
|
<button
|
|
class="index-btn"
|
|
aria-label={playLabel(track.item)}
|
|
tabindex="-1"
|
|
on:click={playTrack}
|
|
>
|
|
<Icon cls="play-icon" path={mdiPlay} size={1.4} />
|
|
<span class="index-nr">
|
|
{#if playingId === track.item.id}
|
|
<IconPlaying />
|
|
{:else}
|
|
{track.n}
|
|
{/if}
|
|
</span>
|
|
</button>
|
|
</div>
|
|
<!-- Cover/Title/Artist column -->
|
|
<div class="col first" role="gridcell" aria-colindex={2}>
|
|
<TrackTitle
|
|
track={track.item}
|
|
withCover={view !== ListView.Album}
|
|
markings={track.markings}
|
|
/>
|
|
</div>
|
|
<!-- Album column -->
|
|
{#if view > ListView.Album}
|
|
<div class="col var1" role="gridcell" aria-colindex={3}>
|
|
<LinkText
|
|
item={albumLink(track.item.album)}
|
|
marked={track.markings?.album}
|
|
tabindex={-1}
|
|
ol
|
|
/>
|
|
</div>
|
|
{/if}
|
|
<!-- Release date column -->
|
|
{#if view > ListView.Album}
|
|
<div class="col var2" role="gridcell" aria-colindex={4}>
|
|
{#if track.item.album.releaseDate}
|
|
<span class="ellipsis-ol">
|
|
{formatDateStr(track.item.album.releaseDate, $locale)}
|
|
</span>
|
|
{/if}
|
|
</div>
|
|
{/if}
|
|
<!-- Added date column -->
|
|
{#if view === ListView.Playlist}
|
|
<div class="col var3" role="gridcell" aria-colindex={5}>
|
|
{#if track.item.addedDate}
|
|
<span class="ellipsis-ol">
|
|
{formatDate(track.item.addedDate, $locale)}
|
|
</span>
|
|
{/if}
|
|
</div>
|
|
{/if}
|
|
<!-- Added by column -->
|
|
{#if showAuthors}
|
|
<div class="col var4" role="gridcell" aria-colindex={6}>
|
|
{#if track.item.addedBy}
|
|
<AvatarBadge
|
|
link={userLink(track.item.addedBy, true)}
|
|
imageUrl="https://secure.gravatar.com/avatar/7535c97c4fc2b39c8fcc3717efb96973?d=identicon&s=64"
|
|
tabindex={-1}
|
|
/>
|
|
{/if}
|
|
</div>
|
|
{/if}
|
|
<!-- Duration / context menu column -->
|
|
<div class="col last" role="gridcell" aria-colindex={7}>
|
|
<span class="ellipsis-ol text-sm text-base-content text-opacity-60">
|
|
{formatDuration(track.item.duration, $mainPhone)}
|
|
</span>
|
|
<span class="ddn-button">
|
|
<button
|
|
use:melt={$ddnTrigger}
|
|
class="btn btn-sm btn-ghost btn-circle"
|
|
aria-label="Track options"
|
|
tabindex="-1"
|
|
on:m-click={(e) => {
|
|
e.detail.originalEvent.stopPropagation();
|
|
}}
|
|
>
|
|
<Icon path={mdiDotsVertical} />
|
|
</button>
|
|
</span>
|
|
</div>
|
|
</div>
|
|
|
|
<TrackMenu
|
|
menu={$ctxMenu}
|
|
item={$ctxItem}
|
|
open={$ctxOpen}
|
|
createSubmenu={createCtxSubmenu}
|
|
tracks={ctxTracks}
|
|
playlistIndex={trackIndex}
|
|
on:moveTracks
|
|
/>
|
|
<TrackMenu
|
|
menu={$ddnMenu}
|
|
item={$ddnItem}
|
|
open={$ddnOpen}
|
|
createSubmenu={createDdnSubmenu}
|
|
tracks={ctxTracks}
|
|
playlistIndex={trackIndex}
|
|
on:moveTracks
|
|
/>
|