<script lang="ts">
import type { Snippet } from "svelte";
import type { HTMLInputAttributes } from "svelte/elements";
import { tv } from "tailwind-variants";
const radio = tv({
slots: {
label: "inline-flex items-center gap-xs",
input: [
"peer size-4 shrink-0 cursor-pointer appearance-none rounded-full border border-foreground-placeholder",
"bg-background-default checked:border-foreground-default",
"hover:bg-background-subtle focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-foreground-default",
"disabled:pointer-events-none disabled:cursor-not-allowed disabled:border-border-disabled disabled:hover:bg-inherit"
],
dot: [
"absolute inset-0 m-auto hidden size-2 rounded-full",
"bg-foreground-default peer-checked:block",
"peer-disabled:bg-foreground-disabled peer-disabled:text-foreground-disabled"
]
},
variants: {
disabled: {
true: { label: "cursor-not-allowed text-foreground-disabled" },
false: { label: "cursor-pointer" }
}
}
});
interface Props extends Omit<HTMLInputAttributes, "type" | "class" | "value" | "children"> {
value: string;
group?: string;
disabled?: boolean;
class?: string;
children?: Snippet;
}
let {
value,
group = $bindable(undefined),
disabled = false,
class: className,
children,
...rest
}: Props = $props();
const classes = $derived(radio({ disabled }));
</script>
<label class={classes.label({ class: className })}>
<span class="relative inline-flex size-4 shrink-0">
<input type="radio" {value} bind:group {disabled} class={classes.input()} {...rest} />
<span class={classes.dot()} aria-hidden="true"></span>
</span>
{#if children}
<span>{@render children()}</span>
{/if}
</label>