<script lang="ts">
import type { Snippet } from "svelte";
import type { HTMLInputAttributes } from "svelte/elements";
import { tv } from "tailwind-variants";
const toggle = tv({
slots: {
label: "items-center gap-xs",
input: [
"peer absolute inset-0 size-full cursor-pointer appearance-none rounded-full bg-background-muted",
"checked:bg-foreground-default",
"enabled:hover:bg-background-strong checked:enabled:hover:bg-foreground-muted",
"focus-visible:ring-2 focus-visible:ring-border-focus focus-visible:outline-none",
"disabled:cursor-not-allowed disabled:bg-background-inset checked:disabled:bg-foreground-disabled"
],
thumb: [
"pointer-events-none absolute top-[2px] left-[2px] size-sm rounded-full bg-background-default",
"transition-transform duration-150 peer-checked:translate-x-[16px]"
],
text: ""
},
variants: {
layout: {
inline: { label: "inline-flex", text: "whitespace-nowrap" },
block: { label: "flex w-full", text: "min-w-0 flex-1" }
},
size: {
small: { label: "min-h-7 typography-paragraph-small" },
regular: { label: "" }
},
disabled: {
true: { label: "cursor-not-allowed text-foreground-disabled" },
false: { label: "cursor-pointer" }
}
},
defaultVariants: {
layout: "inline",
size: "regular"
}
});
interface Props extends Omit<
HTMLInputAttributes,
"type" | "class" | "checked" | "children" | "size"
> {
checked?: boolean;
disabled?: boolean;
layout?: "inline" | "block";
size?: "small" | "regular";
class?: string;
children?: Snippet;
}
let {
checked = $bindable(false),
disabled = false,
layout = "inline",
size = "regular",
class: className,
children,
...rest
}: Props = $props();
const classes = $derived(toggle({ layout, size, disabled }));
</script>
<label class={classes.label({ class: className })}>
<span class="relative inline-flex h-md w-2xl shrink-0">
<input
type="checkbox"
role="switch"
bind:checked
{disabled}
class={classes.input()}
{...rest}
/>
<span class={classes.thumb()} aria-hidden="true"></span>
</span>
{#if children}
<span class={classes.text()}>{@render children()}</span>
{/if}
</label>