Spaces:
Running
Running
File size: 2,152 Bytes
c58d011 a1a6daf c58d011 a1a6daf c58d011 a1a6daf c58d011 a1a6daf c58d011 a1a6daf c58d011 c976b5f c58d011 c976b5f a1a6daf c976b5f a1a6daf c976b5f a1a6daf c976b5f c58d011 c976b5f c58d011 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 |
<script lang="ts">
import CarbonPause from "~icons/carbon/pause";
import CarbonPlay from "~icons/carbon/play";
interface Props {
src: string;
name: string;
}
let { src, name }: Props = $props();
let time = $state(0);
let duration = $state(0);
let paused = $state(true);
function format(time: number) {
if (isNaN(time)) return "...";
const minutes = Math.floor(time / 60);
const seconds = Math.floor(time % 60);
return `${minutes}:${seconds < 10 ? `0${seconds}` : seconds}`;
}
function seek(e: PointerEvent) {
if (!e.currentTarget) return;
const { left, width } = (e.currentTarget as HTMLElement).getBoundingClientRect();
let p = (e.clientX - left) / width;
if (p < 0) p = 0;
if (p > 1) p = 1;
time = p * duration;
}
</script>
<div
class="flex h-14 w-72 items-center gap-4 rounded-2xl border border-gray-200 bg-white p-2.5 text-gray-600 shadow-sm transition-all dark:border-gray-800 dark:bg-gray-900 dark:text-gray-300"
>
<audio
{src}
bind:currentTime={time}
bind:duration
bind:paused
preload="metadata"
onended={() => {
time = 0;
}}
></audio>
<button
class="mx-auto my-auto aspect-square size-8 rounded-full border border-gray-400 bg-gray-100 dark:border-gray-800 dark:bg-gray-700"
aria-label={paused ? "play" : "pause"}
onclick={() => (paused = !paused)}
>
{#if paused}
<CarbonPlay class="mx-auto my-auto text-gray-600 dark:text-gray-300" />
{:else}
<CarbonPause class="mx-auto my-auto text-gray-600 dark:text-gray-300" />
{/if}
</button>
<div class="overflow-hidden">
<div class="truncate font-medium">{name}</div>
{#if duration !== Infinity}
<div class="flex items-center gap-2">
<span class="text-xs">{format(time)}</span>
<div
class="relative h-2 flex-1 rounded-full bg-gray-200 dark:bg-gray-700"
onpointerdown={() => {
paused = true;
}}
onpointerup={seek}
>
<div
class="absolute inset-0 h-full bg-gray-400 dark:bg-gray-600"
style="width: {(time / duration) * 100}%"
></div>
</div>
<span class="text-xs">{duration ? format(duration) : "--:--"}</span>
</div>
{/if}
</div>
</div>
|