books-ui: library, detail page, EPUB/PDF reader, reading status/rating/sessions migration, progress/notes APIs, owner gating and tests
This commit is contained in:
68
src/lib/books/Stars.svelte
Normal file
68
src/lib/books/Stars.svelte
Normal file
@@ -0,0 +1,68 @@
|
||||
<script lang="ts">
|
||||
// 1-5 star rating. Read-only by default; with `name` it renders radio buttons for a form.
|
||||
import Star from 'lucide-svelte/icons/star';
|
||||
|
||||
let {
|
||||
value,
|
||||
size = 14,
|
||||
name,
|
||||
onchange
|
||||
}: {
|
||||
value: number | null;
|
||||
size?: number;
|
||||
name?: string;
|
||||
onchange?: (value: number | null) => void;
|
||||
} = $props();
|
||||
|
||||
let hover = $state<number | null>(null);
|
||||
const shown = $derived(hover ?? value ?? 0);
|
||||
</script>
|
||||
|
||||
{#if name || onchange}
|
||||
<span class="stars interactive" role="radiogroup" aria-label="Rating">
|
||||
{#each [1, 2, 3, 4, 5] as n (n)}
|
||||
<button
|
||||
type={name ? 'submit' : 'button'}
|
||||
{name}
|
||||
value={value === n ? '' : String(n)}
|
||||
role="radio"
|
||||
aria-checked={value === n}
|
||||
aria-label={`${n} star${n > 1 ? 's' : ''}${value === n ? ' (click to clear)' : ''}`}
|
||||
onpointerenter={() => (hover = n)}
|
||||
onpointerleave={() => (hover = null)}
|
||||
onclick={() => onchange?.(value === n ? null : n)}
|
||||
>
|
||||
<Star {size} class={n <= shown ? 'on' : 'off'} />
|
||||
</button>
|
||||
{/each}
|
||||
</span>
|
||||
{:else if value}
|
||||
<span class="stars" aria-label={`${value} out of 5 stars`} title={`${value}/5`}>
|
||||
{#each [1, 2, 3, 4, 5] as n (n)}
|
||||
<Star {size} class={n <= value ? 'on' : 'off'} aria-hidden="true" />
|
||||
{/each}
|
||||
</span>
|
||||
{/if}
|
||||
|
||||
<style>
|
||||
.stars {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 1px;
|
||||
}
|
||||
.stars :global(.on) {
|
||||
fill: #f5b301;
|
||||
color: #f5b301;
|
||||
}
|
||||
.stars :global(.off) {
|
||||
color: color-mix(in srgb, var(--site-muted) 50%, transparent);
|
||||
}
|
||||
.interactive button {
|
||||
padding: 2px;
|
||||
border-radius: 4px;
|
||||
line-height: 0;
|
||||
}
|
||||
.interactive button:hover {
|
||||
transform: scale(1.12);
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user