Something went wrong. Try again.
a simple but full-featured web player for OpenSubsonic music servers (Navidrome, Gonic, Ampache, etc) devins.page/tinysub
svelte typescript javascript subsonic gonic navidrome
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147<script lang="ts">import type { Snippet } from "svelte";import Icon from "../icons/Icon.svelte";import { t } from "../languages/translate.svelte.js";
let { open: isOpen = $bindable(false), title, children, closable = true, onclose, class: className = "", style = "",} = $props<{ open?: boolean; title?: string; children?: Snippet; closable?: boolean; onclose?: () => void; class?: string; style?: string;}>();let dialog: HTMLDialogElement;
$effect(() => { if (isOpen && !dialog.open) { dialog.showModal(); } else if (!isOpen && dialog.open) { dialog.close(); }});</script>
<!-- biome-ignore lint/a11y/useKeyWithClickEvents: Pointer backdrop dismissal with keyboard alternatives --><dialog bind:this={dialog} class={className} {style} onclose={() => { if (closable) { isOpen = false; onclose?.(); } else if (isOpen) { dialog.showModal(); } }} oncancel={(e) => !closable && e.preventDefault()} onclick={(e) => { if (closable && e.target === dialog) isOpen = false; }}> <header> <span>{title}</span> {#if closable} <button type="button" onclick={() => (isOpen = false)} title={t("common.close")} > <Icon name="close" /> </button> {/if} </header> <div>{@render children?.()}</div></dialog>
<style>dialog { position: fixed; inset: var(--viewport-inset-top) var(--viewport-inset-right) var(--viewport-inset-bottom) var(--viewport-inset-left); margin: auto; max-inline-size: var(--viewport-inline-size); max-block-size: var(--viewport-block-size); border: var(--border); padding: 0; box-shadow: var(--shadow-md); border-radius: var(--radius-md); corner-shape: squircle;
> header { display: flex; justify-content: space-between; align-items: center; padding: 0.25rem 0.5rem; background-color: var(--layer); border-block-end: var(--border);
> span { flex: 1 1 auto; contain: inline-size; min-inline-size: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-weight: bold; }
> button { display: flex; } }
> div { display: flex; flex-direction: column; flex: 1; overflow: hidden;
:global(.field) { display: flex; flex-direction: column; gap: 0.5rem; margin-block-end: 0.5rem; }
:global(form) { display: flex; flex-direction: column; gap: 0.5rem; }
:global(.modal-section) { padding: 0.5rem 1rem; }
:global(.warn) { color: red; font-weight: bold; }
:global(.actions) { display: flex; justify-content: flex-end; flex-wrap: wrap; gap: 0.5rem; } }}:global(html.motion) dialog[open] { animation: pop-in 75ms ease-out;}:global(html.motion) dialog[open]::backdrop { animation: fade-in 75ms ease-out;}</style>