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