your personal website on atproto - mirror
blento.app
1<script lang="ts">
2 import { cn } from '@foxui/core';
3
4 let { rating, class: className }: { rating: number; class?: string } = $props();
5
6 // Convert 0-10 rating to 0-5 stars
7 const starRating = $derived(Math.max(0, Math.min(10, rating ?? 0)) / 2);
8 const fullStars = $derived(Math.floor(starRating));
9 const hasHalfStar = $derived(rating % 2 === 1);
10 const emptyStars = $derived(Math.max(0, 5 - fullStars - (hasHalfStar ? 1 : 0)));
11</script>
12
13{#snippet star(className: string)}
14 <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" class="size-5 {className}">
15 <path
16 fill="currentColor"
17 fill-rule="evenodd"
18 d="M10.788 3.21c.448-1.077 1.976-1.077 2.424 0l2.082 5.006 5.404.434c1.164.093 1.636 1.545.749 2.305l-4.117 3.527 1.257 5.273c.271 1.136-.964 2.033-1.96 1.425L12 18.354 7.373 21.18c-.996.608-2.231-.29-1.96-1.425l1.257-5.273-4.117-3.527c-.887-.76-.415-2.212.749-2.305l5.404-.434 2.082-5.005Z"
19 clip-rule="evenodd"
20 />
21 </svg>
22{/snippet}
23
24{#snippet halfStar()}
25 <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" class="fill-accent-500 size-5">
26 <defs>
27 <linearGradient id="half-filled" x1="0%" y1="0%" x2="100%" y2="0%">
28 <stop offset="50%" stop-color="currentColor" />
29 <stop offset="50%" stop-color="var(--color-base-400)" />
30 </linearGradient>
31 </defs>
32 <path
33 fill="url(#half-filled)"
34 fill-rule="evenodd"
35 d="M10.788 3.21c.448-1.077 1.976-1.077 2.424 0l2.082 5.006 5.404.434c1.164.093 1.636 1.545.749 2.305l-4.117 3.527 1.257 5.273c.271 1.136-.964 2.033-1.96 1.425L12 18.354 7.373 21.18c-.996.608-2.231-.29-1.96-1.425l1.257-5.273-4.117-3.527c-.887-.76-.415-2.212.749-2.305l5.404-.434 2.082-5.005Z"
36 clip-rule="evenodd"
37 />
38 </svg>
39{/snippet}
40
41<div class={cn('text-accent-500 flex', className)}>
42 {#each Array(fullStars)}
43 {@render star('text-accent-500')}
44 {/each}
45 {#if hasHalfStar}
46 {@render halfStar()}
47 {/if}
48 {#each Array(emptyStars)}
49 {@render star('text-base-400')}
50 {/each}
51</div>