Files
workspace/code/base-project/base-vue/packages/ao-design/src/checkbox/checkbox.tsx
T
2025-09-17 14:49:15 +08:00

226 lines
6.1 KiB
TypeScript

import type { PropType, VNode } from 'vue'
import type { CheckboxGroupContext } from './context'
import { computed, defineComponent, inject, ref, watch } from 'vue'
import { getPrefixCls } from '../_utils/getPrefixCls'
import Icon from '../icon/index'
import { checkboxGroupKey } from './context'
export default defineComponent({
name: 'Checkbox',
props: {
modelValue: {
type: Boolean,
default: undefined,
},
checked: {
type: Boolean,
default: undefined,
},
value: {
type: [String, Number, Boolean] as PropType<string | number | boolean>,
default: true,
},
indeterminate: {
type: Boolean,
default: false,
},
defaultChecked: {
type: Boolean,
default: false,
},
disabled: {
type: Boolean,
default: false,
},
type: {
type: String as PropType<'default' | 'card' | 'pureCard'>,
default: 'default',
},
extra: {
type: [String, Object, Function] as unknown as () => string | VNode | (() => VNode),
default: '',
},
class: {
type: [String, Array, Object],
default: '',
},
style: {
type: Object,
default: () => ({}),
},
},
emits: ['update:modelValue', 'update:checked', 'change'],
setup(props, { slots, emit }) {
const prefixCls = getPrefixCls('checkbox')
const checkboxGroupCtx = inject<CheckboxGroupContext | undefined>(checkboxGroupKey, undefined)
const inGroup = computed(() => !!checkboxGroupCtx)
const isDisabled = computed(() => props.disabled || checkboxGroupCtx?.disabled)
const _checked = ref(props.defaultChecked)
const mergedType = computed(() =>
props.type || checkboxGroupCtx?.type || 'default',
)
const isChecked = computed(() => {
if (inGroup.value && checkboxGroupCtx) {
const groupValue = checkboxGroupCtx.value
if (Array.isArray(groupValue)) {
return groupValue.includes(props.value)
}
return groupValue === props.value
}
return props.checked ?? props.modelValue ?? _checked.value
})
const handleClick = (event: Event) => {
if (isDisabled.value) {
return
}
if (inGroup.value && checkboxGroupCtx) {
checkboxGroupCtx.handleChange(props.value, event)
}
else {
const newChecked = !isChecked.value
if (props.checked === undefined)
_checked.value = newChecked
emit('update:modelValue', newChecked)
emit('update:checked', newChecked)
}
emit('change', { value: props.value, event })
}
watch(() => props.checked, (val) => {
if (val !== undefined) {
_checked.value = val
}
})
const renderDefault = () => (
<label
class={[
`${prefixCls}-default`,
{ [`${prefixCls}-checked`]: isChecked.value },
{ [`${prefixCls}-disabled`]: isDisabled.value },
props.class,
]}
style={{ ...props.style }}
onClick={handleClick}
>
<span class={`${prefixCls}-main`}>
<input
class={`${prefixCls}-input`}
type="checkbox"
onClick={e => e.stopPropagation()}
/>
<span class={`${prefixCls}-inner`}>
{
props.indeterminate
? (
<Icon icon="garden:dash-fill-12" />
)
: isChecked.value
? (
<Icon icon="garden:check-sm-fill-12" />
)
: null
}
</span>
</span>
<div class={`${prefixCls}-content`}>
<div class={`${prefixCls}-label`}>
{slots.default?.()}
</div>
{props.extra && (
<div class={`${prefixCls}-extra`}>
{typeof props.extra === 'function' ? props.extra() : props.extra}
</div>
)}
</div>
</label>
)
const renderCard = () => (
<label
class={[
`${prefixCls}-card`,
{ [`${prefixCls}-checked`]: isChecked.value },
{ [`${prefixCls}-disabled`]: isDisabled.value },
props.class,
]}
style={{ ...props.style }}
onClick={handleClick}
>
<span class={`${prefixCls}-main`}>
<input
class={`${prefixCls}-input`}
type="checkbox"
onClick={e => e.stopPropagation()}
/>
<span class={`${prefixCls}-inner`}>
{
props.indeterminate
? (
<Icon icon="garden:dash-fill-12" />
)
: isChecked.value
? (
<Icon icon="garden:check-sm-fill-12" />
)
: null
}
</span>
</span>
<div class={`${prefixCls}-content`}>
<div class={`${prefixCls}-label`}>
{slots.default?.()}
</div>
{props.extra && (
<div class={`${prefixCls}-extra`}>
{typeof props.extra === 'function' ? props.extra() : props.extra}
</div>
)}
</div>
</label>
)
const renderPureCard = () => (
<label
class={[
`${prefixCls}-pureCard`,
{ [`${prefixCls}-checked`]: isChecked.value },
{ [`${prefixCls}-disabled`]: isDisabled.value },
props.class,
]}
style={{ ...props.style }}
onClick={handleClick}
>
<div class={`${prefixCls}-content`}>
<div class={`${prefixCls}-label`}>
{slots.default?.()}
</div>
{props.extra && (
<div class={`${prefixCls}-extra`}>
{typeof props.extra === 'function' ? props.extra() : props.extra}
</div>
)}
</div>
</label>
)
return () => {
switch (mergedType.value) {
case 'card':
return renderCard()
case 'pureCard':
return renderPureCard()
default:
return renderDefault()
}
}
},
})