Polish playlist layout
This commit is contained in:
parent
5caf656baf
commit
2cae4ad3f6
@ -206,26 +206,26 @@ function togglePlaylist(id: number) {
|
|||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="mt-6 space-y-3">
|
<div class="mt-6 playlists-grid">
|
||||||
{#each playlists as playlist (playlist.playlistId)}
|
{#each playlists as playlist (playlist.playlistId)}
|
||||||
<article class="playlist-row card-surface">
|
<article class:expanded={Boolean(expanded[playlist.playlistId])} class="playlist-tile card-surface">
|
||||||
<button class="row-header" type="button" on:click={() => togglePlaylist(playlist.playlistId)}>
|
<button class="tile-header" type="button" on:click={() => togglePlaylist(playlist.playlistId)}>
|
||||||
<div class="row-cover">
|
<div class="tile-cover">
|
||||||
<img src={playlist.playlistImage} alt={`Playlist cover for ${playlist.name}`} loading="lazy" />
|
<img src={playlist.playlistImage} alt={`Playlist cover for ${playlist.name}`} loading="lazy" />
|
||||||
</div>
|
</div>
|
||||||
<div class="row-main">
|
<div class="tile-main">
|
||||||
<div class="row-title">
|
<div class="tile-title">
|
||||||
<a href={playlistLink(playlist.playlistId)} target="_blank" rel="noopener noreferrer" on:click|stopPropagation>
|
<a href={playlistLink(playlist.playlistId)} target="_blank" rel="noopener noreferrer" on:click|stopPropagation>
|
||||||
{playlist.name}
|
{playlist.name}
|
||||||
</a>
|
</a>
|
||||||
<span class="map-count">{playlist.stats?.totalMaps ?? 0} maps</span>
|
<span class="map-count">{playlist.stats?.totalMaps ?? 0} maps</span>
|
||||||
</div>
|
</div>
|
||||||
{#if isFiniteScore(playlist.stats?.avgScore)}
|
{#if isFiniteScore(playlist.stats?.avgScore)}
|
||||||
<div class="row-score">
|
<div class="tile-score">
|
||||||
<ScoreBar value={playlist.stats?.avgScore ?? null} size="sm" />
|
<ScoreBar value={playlist.stats?.avgScore ?? null} size="sm" />
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
<div class="row-sub">
|
<div class="tile-sub">
|
||||||
<span>
|
<span>
|
||||||
by
|
by
|
||||||
{#if playlist.owner?.name}
|
{#if playlist.owner?.name}
|
||||||
@ -238,7 +238,7 @@ function togglePlaylist(id: number) {
|
|||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class:arrow-expanded={Boolean(expanded[playlist.playlistId])} class="row-arrow" aria-hidden="true">
|
<div class:arrow-expanded={Boolean(expanded[playlist.playlistId])} class="tile-arrow" aria-hidden="true">
|
||||||
<svg viewBox="0 0 24 24" width="20" height="20" role="presentation">
|
<svg viewBox="0 0 24 24" width="20" height="20" role="presentation">
|
||||||
<path d="M6 9l6 6 6-6" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" />
|
<path d="M6 9l6 6 6-6" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" />
|
||||||
</svg>
|
</svg>
|
||||||
@ -247,11 +247,11 @@ function togglePlaylist(id: number) {
|
|||||||
|
|
||||||
{#if expanded[playlist.playlistId]}
|
{#if expanded[playlist.playlistId]}
|
||||||
{@const state = playlistState[playlist.playlistId]}
|
{@const state = playlistState[playlist.playlistId]}
|
||||||
<div class="row-body">
|
<div class="tile-body">
|
||||||
{#if state?.loading}
|
{#if state?.loading}
|
||||||
<div class="row-status">Loading songs…</div>
|
<div class="tile-status">Loading songs…</div>
|
||||||
{:else if state?.error}
|
{:else if state?.error}
|
||||||
<div class="row-status error">{state.error}</div>
|
<div class="tile-status error">{state.error}</div>
|
||||||
{:else if state?.maps?.length}
|
{:else if state?.maps?.length}
|
||||||
<div class="songs-grid">
|
<div class="songs-grid">
|
||||||
{#each state.maps as card (card.id)}
|
{#each state.maps as card (card.id)}
|
||||||
@ -280,7 +280,7 @@ function togglePlaylist(id: number) {
|
|||||||
{/each}
|
{/each}
|
||||||
</div>
|
</div>
|
||||||
{#if state.total && state.total > SONGS_PER_PAGE}
|
{#if state.total && state.total > SONGS_PER_PAGE}
|
||||||
<div class="row-pagination">
|
<div class="tile-pagination">
|
||||||
<button
|
<button
|
||||||
class="pager-btn"
|
class="pager-btn"
|
||||||
on:click={() => loadPlaylistMaps(playlist.playlistId, Math.max(0, (state.offset ?? 0) - SONGS_PER_PAGE))}
|
on:click={() => loadPlaylistMaps(playlist.playlistId, Math.max(0, (state.offset ?? 0) - SONGS_PER_PAGE))}
|
||||||
@ -288,7 +288,7 @@ function togglePlaylist(id: number) {
|
|||||||
>
|
>
|
||||||
Prev {SONGS_PER_PAGE}
|
Prev {SONGS_PER_PAGE}
|
||||||
</button>
|
</button>
|
||||||
<span class="row-status">
|
<span class="tile-status">
|
||||||
{#if state.total === 0}
|
{#if state.total === 0}
|
||||||
No songs
|
No songs
|
||||||
{:else}
|
{:else}
|
||||||
@ -314,7 +314,7 @@ function togglePlaylist(id: number) {
|
|||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
{:else}
|
{:else}
|
||||||
<div class="row-status">No songs found.</div>
|
<div class="tile-status">No songs found.</div>
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
@ -336,18 +336,30 @@ function togglePlaylist(id: number) {
|
|||||||
</section>
|
</section>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
.playlist-row {
|
.playlists-grid {
|
||||||
padding: 0;
|
display: grid;
|
||||||
overflow: hidden;
|
gap: 1.25rem;
|
||||||
|
grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
|
||||||
}
|
}
|
||||||
|
|
||||||
.row-header {
|
.playlist-tile {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
overflow: hidden;
|
||||||
|
transition: transform 0.2s ease, box-shadow 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.playlist-tile.expanded {
|
||||||
|
grid-column: 1 / -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tile-header {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: auto 1fr auto;
|
grid-template-columns: auto 1fr auto;
|
||||||
gap: 1rem;
|
|
||||||
align-items: center;
|
align-items: center;
|
||||||
padding: 1rem 1.25rem;
|
gap: 1rem;
|
||||||
|
padding: 1rem 1.25rem 0.85rem;
|
||||||
background: transparent;
|
background: transparent;
|
||||||
color: inherit;
|
color: inherit;
|
||||||
border: none;
|
border: none;
|
||||||
@ -355,43 +367,42 @@ function togglePlaylist(id: number) {
|
|||||||
text-align: left;
|
text-align: left;
|
||||||
}
|
}
|
||||||
|
|
||||||
.row-cover {
|
.tile-cover {
|
||||||
height: 3em;
|
position: relative;
|
||||||
width: auto;
|
width: 88px;
|
||||||
display: flex;
|
height: 88px;
|
||||||
align-items: center;
|
border-radius: 0.6rem;
|
||||||
justify-content: center;
|
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
border-radius: 0.5rem;
|
|
||||||
background: rgba(15, 23, 42, 0.6);
|
background: rgba(15, 23, 42, 0.6);
|
||||||
}
|
}
|
||||||
|
|
||||||
.row-cover img {
|
.tile-cover img {
|
||||||
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
width: auto;
|
object-fit: cover;
|
||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
|
|
||||||
.row-main {
|
.tile-main {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: 0.4rem;
|
gap: 0.5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.row-title {
|
.tile-title {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
flex-direction: column;
|
||||||
gap: 0.75rem;
|
gap: 0.45rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tile-title a {
|
||||||
|
color: white;
|
||||||
font-size: 1.05rem;
|
font-size: 1.05rem;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
}
|
|
||||||
|
|
||||||
.row-title a {
|
|
||||||
color: white;
|
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.row-title a:hover {
|
.tile-title a:hover {
|
||||||
text-decoration: underline;
|
text-decoration: underline;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -400,7 +411,18 @@ function togglePlaylist(id: number) {
|
|||||||
color: rgba(148, 163, 184, 0.85);
|
color: rgba(148, 163, 184, 0.85);
|
||||||
}
|
}
|
||||||
|
|
||||||
.row-sub {
|
.tile-score {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 0.4rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tile-score :global(.score-meter) {
|
||||||
|
width: 100%;
|
||||||
|
max-width: 280px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tile-sub {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
gap: 0.5rem;
|
gap: 0.5rem;
|
||||||
@ -408,52 +430,61 @@ function togglePlaylist(id: number) {
|
|||||||
color: rgba(148, 163, 184, 0.95);
|
color: rgba(148, 163, 184, 0.95);
|
||||||
}
|
}
|
||||||
|
|
||||||
.row-sub a {
|
.tile-sub a {
|
||||||
color: var(--color-neon);
|
color: var(--color-neon);
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.row-sub a:hover {
|
.tile-sub a:hover {
|
||||||
text-decoration: underline;
|
text-decoration: underline;
|
||||||
color: color-mix(in srgb, var(--color-neon) 80%, white 20%);
|
color: color-mix(in srgb, var(--color-neon) 80%, white 20%);
|
||||||
}
|
}
|
||||||
|
|
||||||
.row-score {
|
.tile-arrow {
|
||||||
display: flex;
|
align-self: center;
|
||||||
flex-direction: column;
|
justify-self: center;
|
||||||
gap: 0.4rem;
|
color: rgba(148, 163, 184, 0.75);
|
||||||
max-width: 220px;
|
transition: transform 0.2s ease, color 0.2s ease;
|
||||||
}
|
}
|
||||||
|
|
||||||
.row-score :global(.score-meter) {
|
.tile-arrow.arrow-expanded {
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.row-arrow {
|
|
||||||
transition: transform 0.2s ease;
|
|
||||||
color: rgba(148, 163, 184, 0.8);
|
|
||||||
}
|
|
||||||
|
|
||||||
.row-arrow.arrow-expanded {
|
|
||||||
transform: rotate(180deg);
|
transform: rotate(180deg);
|
||||||
color: var(--color-neon);
|
color: var(--color-neon);
|
||||||
}
|
}
|
||||||
|
|
||||||
.row-body {
|
.tile-body {
|
||||||
border-top: 1px solid rgba(148, 163, 184, 0.08);
|
border-top: 1px solid rgba(148, 163, 184, 0.08);
|
||||||
padding: 1rem 1.25rem 1.25rem;
|
padding: 1rem 1.25rem 1.25rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.row-status {
|
.tile-status {
|
||||||
color: rgba(148, 163, 184, 0.95);
|
color: rgba(148, 163, 184, 0.95);
|
||||||
font-size: 0.9rem;
|
font-size: 0.9rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.row-status.error {
|
@media (max-width: 640px) {
|
||||||
|
.tile-header {
|
||||||
|
grid-template-columns: minmax(64px, 1fr);
|
||||||
|
grid-template-rows: auto auto;
|
||||||
|
justify-items: stretch;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tile-cover {
|
||||||
|
width: 100%;
|
||||||
|
height: auto;
|
||||||
|
aspect-ratio: 16 / 9;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tile-arrow {
|
||||||
|
justify-self: end;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.tile-status.error {
|
||||||
color: #f87171;
|
color: #f87171;
|
||||||
}
|
}
|
||||||
|
|
||||||
.row-pagination {
|
.tile-pagination {
|
||||||
margin-top: 1rem;
|
margin-top: 1rem;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user