finally got the playlist score bar gradient to clip at the score

This commit is contained in:
pleb 2025-11-03 11:07:31 -08:00
parent fbba09f5b6
commit 17890e42e5
2 changed files with 43 additions and 46 deletions

View File

@ -15,5 +15,5 @@
## Shell command guidance ## Shell command guidance
- Whitelisted commands: `grep` - Dont' use `cd`, prefer `pwd`
- DO NOT USE: `cd` (prefer `pwd`) - Dont' use `rg`, prefer `grep -r`

View File

@ -62,11 +62,15 @@ let playlistState: Record<number, PlaylistState> = {};
function togglePlaylist(id: number) { function togglePlaylist(id: number) {
const currentlyExpanded = Boolean(expanded[id]); const currentlyExpanded = Boolean(expanded[id]);
expanded = { ...expanded, [id]: !currentlyExpanded }; const nextExpanded: Record<number, boolean> = {};
if (!currentlyExpanded) {
nextExpanded[id] = true;
}
expanded = nextExpanded;
if (!currentlyExpanded) { if (!currentlyExpanded) {
const state = playlistState[id]; const state = playlistState[id];
const offset = state ? state.offset : 0; const currentOffset = state && typeof state.offset === 'number' ? state.offset : 0;
loadPlaylistMaps(id, offset); loadPlaylistMaps(id, currentOffset);
} }
} }
@ -222,16 +226,9 @@ function scorePercent(avgScore: number | undefined | null): number | null {
<span class="map-count">{playlist.stats?.totalMaps ?? 0} maps</span> <span class="map-count">{playlist.stats?.totalMaps ?? 0} maps</span>
</div> </div>
{#if scorePercent(playlist.stats?.avgScore) !== null} {#if scorePercent(playlist.stats?.avgScore) !== null}
<div {@const pct = scorePercent(playlist.stats?.avgScore) ?? 0}
class="row-score" <div class="row-score">
data-gradient={scorePercent(playlist.stats?.avgScore)! > 50 ? 'true' : 'false'} <div class="score-bar" style={`--score-fill:${pct}%`}></div>
>
<div class="score-bar">
<div
class="score-bar-fill"
style={`width: ${scorePercent(playlist.stats?.avgScore) ?? 0}%`}
></div>
</div>
</div> </div>
{/if} {/if}
<div class="row-sub"> <div class="row-sub">
@ -255,14 +252,15 @@ function scorePercent(avgScore: number | undefined | null): number | null {
</button> </button>
{#if expanded[playlist.playlistId]} {#if expanded[playlist.playlistId]}
{@const state = playlistState[playlist.playlistId]}
<div class="row-body"> <div class="row-body">
{#if playlistState[playlist.playlistId]?.loading} {#if state?.loading}
<div class="row-status">Loading songs…</div> <div class="row-status">Loading songs…</div>
{:else if playlistState[playlist.playlistId]?.error} {:else if state?.error}
<div class="row-status error">{playlistState[playlist.playlistId]?.error}</div> <div class="row-status error">{state.error}</div>
{:else if playlistState[playlist.playlistId]?.maps?.length} {:else if state?.maps?.length}
<div class="songs-grid"> <div class="songs-grid">
{#each playlistState[playlist.playlistId].maps as card (card.id)} {#each state.maps as card (card.id)}
<MapCard <MapCard
hash={card.hash} hash={card.hash}
coverURL={card.coverURL} coverURL={card.coverURL}
@ -278,35 +276,34 @@ function scorePercent(avgScore: number | undefined | null): number | null {
/> />
{/each} {/each}
</div> </div>
{#if playlistState[playlist.playlistId]?.total} {#if state.total && state.total > SONGS_PER_PAGE}
<div class="row-pagination"> <div class="row-pagination">
<button <button
class="pager-btn" class="pager-btn"
on:click={() => loadPlaylistMaps(playlist.playlistId, Math.max(0, playlistState[playlist.playlistId].offset - SONGS_PER_PAGE))} on:click={() => loadPlaylistMaps(playlist.playlistId, Math.max(0, (state.offset ?? 0) - SONGS_PER_PAGE))}
disabled={playlistState[playlist.playlistId].offset === 0 || playlistState[playlist.playlistId].loading} disabled={(state.offset ?? 0) === 0 || state.loading}
> >
Prev {SONGS_PER_PAGE} Prev {SONGS_PER_PAGE}
</button> </button>
<span class="row-status"> <span class="row-status">
{#if playlistState[playlist.playlistId].total === 0} {#if state.total === 0}
No songs No songs
{:else} {:else}
Showing {playlistState[playlist.playlistId].offset + 1} Showing {(state.offset ?? 0) + 1}
- -
{Math.min( {Math.min(
playlistState[playlist.playlistId].offset + playlistState[playlist.playlistId].maps.length, (state.offset ?? 0) + state.maps.length,
playlistState[playlist.playlistId].total ?? 0 state.total ?? 0
)} )}
of {playlistState[playlist.playlistId].total} of {state.total}
{/if} {/if}
</span> </span>
<button <button
class="pager-btn" class="pager-btn"
on:click={() => loadPlaylistMaps(playlist.playlistId, playlistState[playlist.playlistId].offset + SONGS_PER_PAGE)} on:click={() => loadPlaylistMaps(playlist.playlistId, (state.offset ?? 0) + SONGS_PER_PAGE)}
disabled={ disabled={
playlistState[playlist.playlistId].loading || state.loading ||
(playlistState[playlist.playlistId].offset + playlistState[playlist.playlistId].maps.length) >= ((state.offset ?? 0) + state.maps.length) >= (state.total ?? 0)
(playlistState[playlist.playlistId].total ?? 0)
} }
> >
Next {SONGS_PER_PAGE} Next {SONGS_PER_PAGE}
@ -431,25 +428,25 @@ function scorePercent(avgScore: number | undefined | null): number | null {
width: 100%; width: 100%;
height: 0.5rem; height: 0.5rem;
border-radius: 999px; border-radius: 999px;
background: rgba(34, 211, 238, 0.15); background-color: rgba(34, 211, 238, 0.15);
overflow: hidden; overflow: hidden;
} }
.score-bar-fill { .score-bar::before {
content: '';
position: absolute; position: absolute;
inset: 0; top: 0;
width: 0; right: 0;
bottom: 0;
left: 0;
border-radius: inherit; border-radius: inherit;
background: var(--score-gradient, var(--score-default)); background: linear-gradient(90deg, var(--color-neon-fuchsia), var(--color-neon));
transition: width 0.3s ease; /* Reveal only the first N% of the full-width gradient */
} -webkit-mask-image: linear-gradient(90deg, #000 0 var(--score-fill, 0%), transparent var(--score-fill, 0%) 100%);
mask-image: linear-gradient(90deg, #000 0 var(--score-fill, 0%), transparent var(--score-fill, 0%) 100%);
.row-score[data-gradient='true'] .score-bar-fill { /* Fallback for environments without mask-image support */
--score-gradient: linear-gradient(90deg, var(--color-neon-fuchsia), var(--color-neon)); clip-path: inset(0 calc(100% - var(--score-fill, 0%)) 0 0 round 999px);
} transition: -webkit-mask-image 0.3s ease, mask-image 0.3s ease, clip-path 0.3s ease;
.row-score[data-gradient='false'] .score-bar-fill {
--score-gradient: rgba(148, 163, 184, 0.35);
} }
.row-arrow { .row-arrow {