import { makeComponentProps } from '@/composables/component' import { makeTagProps } from '@/composables/tag' import { genericComponent, propsFactory } from '@/utils' import { ExtractPropTypes } from 'vue' import { computed, ref } from 'vue' import { useEventListener } from '@vueuse/core' import Sizes from '@/types/sizes' import BaseCheckboxVariant from '@/types/baseCheckboxVariants' import { setColorVolume, changeColorName } from '@/utils/useColors' export const makeUCheckboxProps = propsFactory( { size: { type: String, default: Sizes.sm, required: false, }, variant: { type: String, default: BaseCheckboxVariant.default, required: false, }, disabled: { type: Boolean, default: false, required: false, }, modelValue: { type: Boolean, default: false, required: false, }, ...makeComponentProps(), ...makeTagProps(), }, 'UCheckbox' ) export type UCheckboxProps = ExtractPropTypes export type UCheckboxSlots = { default: never } export const UCheckbox = genericComponent()({ name: 'UCheckbox', props: makeUCheckboxProps(), emits: { 'update:modelValue': (value: boolean) => true, 'keydown:enter': () => true, }, setup(props, { emit, slots }) { const iconColor = computed(() => { if (!props.disabled === false && props.variant !== 'check') { return 'gray-300' } if (props.variant === 'check') { return 'white' } return 'primary-600' }) const isFocused = ref(false) const handleKeyDown = (event: KeyboardEvent) => { if (isFocused.value && event.key === 'Enter' && !props.disabled) { emit('keydown:enter') onClick(props.modelValue) } } useEventListener('keydown', handleKeyDown) const defaultCheckedClassesCheckbox = computed( () => `bg-${changeColorName( 'primary-600', -550 )} border-${'primary-600'} hover:bg-${setColorVolume( 'primary-600', 100 )} hover:border-${changeColorName( 'primary-600', 0 )} active:bg-${setColorVolume( 'primary-600', 50 )} active:shadow-md active:shadow-${changeColorName( 'primary-600', -500 )} active:border-primary-600 focus-visible:border-primary-600 focus-visible:bg-primary-100 ` ) const defaultClassesCheckbox = computed( () => `hover:bg-${setColorVolume( 'primary-600', 100 )} hover:border-${changeColorName( 'primary-600', 0 )} border-${'gray-300'} active:bg-${changeColorName('primary-600', -600)} active:shadow-md active:shadow-${changeColorName( 'primary-600', -500 )} active:border-${changeColorName('primary-600', -300)} focus-visible:border-primary-600 focus-visible:bg-primary-100 ` ) const DisabledDefaultClassesCheckbox = computed( () => `bg-gray-100 border-gray-300 cursor-not-allowed` ) const radioClassesCheckbox = computed( () => `rounded-full border-gray-300 hover:bg-${setColorVolume( 'primary-600', 100 )} hover:border-${changeColorName( 'primary-600', 0 )} active:bg-${changeColorName('primary-600', -600)} active:shadow-md active:shadow-${changeColorName( 'primary-600', -500 )} active:border-${changeColorName('primary-600', -300)} focus-visible:border-primary-600 focus-visible:bg-primary-100 ` ) const radioCheckedClassesCheckbox = computed( () => `rounded-full bg-${changeColorName( 'primary-600', -550 )} border-${'primary-600'} hover:bg-${setColorVolume( 'primary-600', 100 )} hover:border-${changeColorName( 'primary-600', 0 )} active:bg-${setColorVolume( 'primary-600', 50 )} active:shadow-md active:shadow-${changeColorName( 'primary-600', -500 )} active:border-${'primary-600'} focus-visible:border-primary-600 focus-visible:bg-primary-100 ` ) const DisabledRadioClassesCheckbox = computed( () => `rounded-full bg-gray-100 border-gray-300 cursor-not-allowed` ) const checkClassesCheckbox = computed( () => `rounded-full border-gray-300 hover:bg-${setColorVolume( 'primary-600', 50 )} hover:border-${changeColorName( 'primary-600', 0 )} active:bg-${changeColorName('primary-600', -600)} active:shadow-md active:shadow-${changeColorName( 'primary-600', -500 )} active:border-${changeColorName('primary-600', -300)} focus-visible:border-primary-600 focus-visible:bg-primary-100 ` ) const checkCheckedClassesCheckbox = computed( () => `rounded-full bg-${'primary-600'} border-${'primary-600'} hover:bg-${setColorVolume( 'primary-600', 700 )} hover:border-${setColorVolume( 'primary-600', 700 )} active:shadow-md active:shadow-${changeColorName( 'primary-600', -500 )} active:bg-${setColorVolume('primary-600', 600)} active:border-${setColorVolume('primary-600', 600)} focus-visible:border-primary-600 focus-visible:bg-primary-700 ` ) const DisabledCheckClassesCheckbox = computed( () => `rounded-full bg-gray-300 border-gray-300 cursor-not-allowed` ) const toggleBgElementSize = computed(() => { return props.size === 'sm' ? 'w-36 h-20' : 'w-44 h-24' }) const toggleBgClasses = computed( () => `bg-gray-100 hover:bg-gray-200 ${toggleBgElementSize.value} rounded-12` ) const toggleCheckedClasses = computed( () => `bg-primary-600 hover:bg-primary-700 ${toggleBgElementSize.value} rounded-12` ) const toggleDisabledPseudoClasses = computed( () => `bg-transparent ${toggleBgElementSize.value} rounded-12` ) const toggleSize = computed(() => { return props.size === 'sm' ? 'w-4 h-4.5' : 'w-5 h-5.5' }) const togglePositionClasses = computed(() => { return props.modelValue && props.size === 'sm' ? 'ml-4.6 my-0.5' : props.modelValue && props.size === 'md' ? 'ml-5.5 my-0.5' : 'my-0.5 mx-0.5' }) const toggleClasses = computed( () => `${toggleSize.value} ${togglePositionClasses.value} rounded-full bg-white shadow-sm ` ) const classes = computed(() => ({ //general styling 'box-border flex rounded border focus:outline-none': props.variant !== 'toggle', ...(props.variant === 'toggle' && !props.disabled === true && props.modelValue === false && { [toggleBgClasses.value]: true, }), ...(props.variant === 'toggle' && !props.disabled === true && props.modelValue === true && { [toggleCheckedClasses.value]: true, }), ...(props.variant === 'toggle' && props.disabled === true && { [toggleDisabledPseudoClasses.value]: true, }), ...(!props.disabled === true && { 'cursor-pointer': true, }), ...(props.variant === 'default' && !props.disabled === true && props.modelValue === false && { [defaultClassesCheckbox.value]: true, }), ...(props.variant === 'default' && !props.disabled === true && props.modelValue === true && { [defaultCheckedClassesCheckbox.value]: true, }), ...(!props.disabled === false && props.variant === 'default' && { [DisabledDefaultClassesCheckbox.value]: true, }), ...(props.variant === 'radio' && !props.disabled === true && props.modelValue === false && { [radioClassesCheckbox.value]: true, }), ...(props.variant === 'radio' && !props.disabled === true && props.modelValue === true && { [radioCheckedClassesCheckbox.value]: true, }), ...(!props.disabled === false && props.variant === 'radio' && { [DisabledRadioClassesCheckbox.value]: true, }), ...(props.variant === 'check' && !props.disabled === true && props.modelValue === false && { [checkClassesCheckbox.value]: true, }), ...(props.variant === 'check' && !props.disabled === true && props.modelValue === true && { [checkCheckedClassesCheckbox.value]: true, }), ...(!props.disabled === false && props.variant === 'check' && { [DisabledCheckClassesCheckbox.value]: true, }), 'h-[16px] w-[16px]': props.size === 'sm' && props.variant !== 'toggle', 'h-[20px] w-[20px]': props.size === 'md' && props.variant !== 'toggle', })) const onClick = (modelValue: boolean) => { if (!props.disabled) { emit('update:modelValue', !props.modelValue) } } return () => (
onClick(props.modelValue)} onFocus={() => (isFocused.value = true)} onBlur={() => (isFocused.value = false)} > {props.variant === 'toggle' ? (
) : null} {props.variant !== 'radio' && props.variant !== 'toggle' && props.modelValue && props.size === 'sm' ? ( ) : null} {props.variant !== 'radio' && props.variant !== 'toggle' && props.modelValue && props.size === 'md' ? ( ) : null} {props.variant === 'radio' && props.modelValue && props.size === 'sm' ? (
) : null} {props.variant === 'radio' && props.modelValue && props.size === 'md' ? (
) : null}
onClick(props.modelValue)}> {slots.default?.()}
) }, }) export type UCheckbox = InstanceType