This repository has no description
1<script lang="ts">
2 import type { Snippet } from "svelte";
3 import type { HTMLInputAttributes } from "svelte/elements";
4
5 interface Props extends Omit<HTMLInputAttributes, "type" | "class" | "checked" | "children"> {
6 checked?: boolean;
7 disabled?: boolean;
8 /** `inline` hugs the label, `block` stretches it to fill the row. */
9 layout?: "inline" | "block";
10 class?: string;
11 children?: Snippet;
12 }
13
14 let {
15 checked = $bindable(false),
16 disabled = false,
17 layout = "inline",
18 class: className,
19 children,
20 ...rest
21 }: Props = $props();
22
23 const labelClasses = $derived(
24 [
25 "items-center gap-2",
26 layout === "block" ? "flex w-full" : "inline-flex",
27 disabled ? "cursor-not-allowed text-foreground-disabled" : "cursor-pointer",
28 className ?? ""
29 ].join(" ")
30 );
31
32 const inputClasses = [
33 "peer absolute inset-0 size-full appearance-none rounded-full bg-background-muted",
34 "transition-colors duration-150",
35 "checked:bg-foreground-default",
36 "enabled:hover:bg-background-strong checked:enabled:hover:bg-foreground-muted",
37 "focus-visible:ring-2 focus-visible:ring-border-strong focus-visible:outline-none",
38 "disabled:cursor-not-allowed disabled:bg-background-inset checked:disabled:bg-foreground-disabled"
39 ].join(" ");
40
41 const thumbClasses = [
42 "pointer-events-none absolute top-px left-px size-4 rounded-full bg-background-default",
43 "transition-transform duration-150 peer-checked:translate-x-[15px]"
44 ].join(" ");
45
46 const textClasses = $derived(layout === "block" ? "min-w-0 flex-1" : "whitespace-nowrap");
47</script>
48
49<label class={labelClasses}>
50 <span class="relative inline-flex h-4.5 w-8.25 shrink-0">
51 <input type="checkbox" role="switch" bind:checked {disabled} class={inputClasses} {...rest} />
52 <span class={thumbClasses} aria-hidden="true"></span>
53 </span>
54 {#if children}
55 <span class={textClasses}>{@render children()}</span>
56 {/if}
57</label>