55 lines
2 KiB
Vue
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>
|