Nychthemeron/packages/library/src/components/ui/checkbox/Checkbox.vue
Matthew L McPeak 38674be217
All checks were successful
ci / build (push) Successful in 35s
ci / publish (push) Successful in 11s
ci / publish-docs (push) Successful in 1m4s
Inital Commit
2026-07-15 20:47:53 -04:00

55 lines
2 KiB
Vue

<script setup lang="ts">
import { CheckIcon } from '@lucide/vue'
import type { HTMLAttributes } from 'vue'
import { useVModel } from '@vueuse/core'
import { cn } from '@/lib/utils'
const props = defineProps<{
modelValue?: boolean
defaultValue?: boolean
disabled?: boolean
required?: boolean
class?: HTMLAttributes['class']
}>()
const emit = defineEmits<{ 'update:modelValue': [value: boolean] }>()
const checked = useVModel(props, 'modelValue', emit, {
passive: true,
defaultValue: props.defaultValue ?? false,
})
function toggle() {
if (props.disabled) return
checked.value = !checked.value
}
</script>
<template>
<button
type="button"
role="checkbox"
data-slot="checkbox"
:aria-checked="checked"
:aria-required="required"
:data-state="checked ? 'checked' : 'unchecked'"
:disabled="disabled"
:class="cn('border-input dark:bg-input/30 data-checked:bg-primary data-checked:text-primary-foreground dark:data-checked:bg-primary data-checked:border-primary aria-invalid:aria-checked:border-primary aria-invalid:border-destructive dark:aria-invalid:border-destructive/50 focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 flex size-4 items-center justify-center rounded-[4px] border transition-colors group-has-disabled/field:opacity-50 focus-visible:ring-3 aria-invalid:ring-3 peer relative shrink-0 outline-none after:absolute after:-inset-x-3 after:-inset-y-2 disabled:cursor-not-allowed disabled:opacity-50 hover:border-ring', props.class)"
@click="toggle"
>
<Transition
enter-active-class="animate-in fade-in-0 zoom-in-50 animation-duration-100"
leave-active-class="animate-out fade-out-0 zoom-out-50 animation-duration-100"
>
<span
v-if="checked"
data-slot="checkbox-indicator"
class="[&>svg]:size-3.5 grid place-content-center text-current"
>
<slot>
<CheckIcon />
</slot>
</span>
</Transition>
</button>
</template>