177 lines
4.5 KiB
TypeScript
177 lines
4.5 KiB
TypeScript
import type { PropType, RenderFunction } from 'vue'
|
|
import { computed, defineComponent, provide, reactive, ref, watch } from 'vue'
|
|
import { getPrefixCls } from '../_utils/getPrefixCls'
|
|
import Space from '../space/index'
|
|
import Checkbox from './checkbox'
|
|
import { checkboxGroupKey } from './context'
|
|
|
|
interface CheckboxOption {
|
|
label?: string | RenderFunction
|
|
value?: string | number
|
|
extra?: string | RenderFunction
|
|
disabled?: boolean
|
|
[key: string]: any
|
|
}
|
|
|
|
export default defineComponent({
|
|
name: 'CheckboxGroup',
|
|
props: {
|
|
modelValue: {
|
|
type: Array as PropType<Array<string | number | boolean>>,
|
|
default: () => [],
|
|
},
|
|
defaultValue: {
|
|
type: Array as PropType<Array<string | number | boolean>>,
|
|
default: () => [],
|
|
},
|
|
disabled: {
|
|
type: Boolean,
|
|
default: false,
|
|
},
|
|
labelField: {
|
|
type: String,
|
|
default: 'label',
|
|
},
|
|
valueField: {
|
|
type: String,
|
|
default: 'value',
|
|
},
|
|
extraField: {
|
|
type: String,
|
|
default: 'extra',
|
|
},
|
|
options: {
|
|
type: Array as PropType<Array<string | number | CheckboxOption>>,
|
|
default: [],
|
|
},
|
|
spaceProps: {
|
|
type: Object as PropType<Record<string, any>>,
|
|
default: () => ({}),
|
|
},
|
|
type: {
|
|
type: String as PropType<'default' | 'card' | 'pureCard'>,
|
|
default: 'default',
|
|
},
|
|
class: {
|
|
type: [String, Array, Object],
|
|
default: '',
|
|
},
|
|
style: {
|
|
type: Object,
|
|
default: () => ({}),
|
|
},
|
|
},
|
|
emits: ['update:modelValue', 'change'],
|
|
setup(props, { slots, emit }) {
|
|
const prefixCls = getPrefixCls('checkbox-group')
|
|
|
|
const _value = ref(props.modelValue ?? props.defaultValue ?? [])
|
|
|
|
watch(() => props.modelValue, (newVal) => {
|
|
_value.value = newVal ?? []
|
|
})
|
|
|
|
const groupDisabled = computed(() => props.disabled)
|
|
const groupType = computed(() => props.type)
|
|
|
|
const handleChange = (val: string | number | boolean, e: Event) => {
|
|
const current = _value.value ? [..._value.value] : []
|
|
const index = current.indexOf(val)
|
|
|
|
if (index > -1) {
|
|
// 已存在 -> 取消选中
|
|
current.splice(index, 1)
|
|
}
|
|
else {
|
|
// 不存在 -> 添加
|
|
current.push(val)
|
|
}
|
|
|
|
// 非受控模式才更新内部值
|
|
if (props.modelValue === undefined) {
|
|
_value.value = current
|
|
}
|
|
|
|
// 通知外部
|
|
emit('update:modelValue', current)
|
|
emit('change', current, e)
|
|
}
|
|
|
|
// 提供 context 给 Radio 使用
|
|
provide(checkboxGroupKey, reactive({
|
|
name: 'AoCheckboxGroup' as const,
|
|
value: _value,
|
|
disabled: groupDisabled,
|
|
type: groupType,
|
|
slots,
|
|
handleChange,
|
|
}))
|
|
|
|
const renderOptions = () => {
|
|
return props.options.map((option) => {
|
|
const opt
|
|
= typeof option === 'string' || typeof option === 'number'
|
|
? { [props.labelField]: String(option), [props.valueField]: option }
|
|
: option
|
|
|
|
const labelContent = slots.label
|
|
? slots.label({ data: opt })
|
|
: typeof opt[props.labelField] === 'function'
|
|
? opt[props.labelField]()
|
|
: opt[props.labelField]
|
|
|
|
const extraContent = slots.extra
|
|
? slots.extra({ data: opt })
|
|
: typeof opt[props.extraField] === 'function'
|
|
? opt[props.extraField]()
|
|
: opt[props.extraField]
|
|
|
|
const isChecked
|
|
= _value.value.includes(opt[props.valueField])
|
|
|
|
if (slots.radio) {
|
|
return (
|
|
<div
|
|
key={opt[props.valueField]}
|
|
onClick={e => handleChange(opt[props.valueField], e)}
|
|
>
|
|
{slots.radio({ data: opt, checked: _value.value === opt[props.valueField] })}
|
|
</div>
|
|
)
|
|
}
|
|
|
|
return (
|
|
<Checkbox
|
|
key={opt[props.valueField]}
|
|
value={opt[props.valueField]}
|
|
extra={extraContent}
|
|
disabled={opt.disabled ?? props.disabled}
|
|
checked={isChecked}
|
|
type={props.type}
|
|
>
|
|
{labelContent}
|
|
</Checkbox>
|
|
)
|
|
})
|
|
}
|
|
|
|
const spaceSize
|
|
= ['default', 'card', 'pureCard'].includes(groupType.value)
|
|
? [6, 6]
|
|
: props.spaceProps.size ?? 0
|
|
|
|
return () => (
|
|
<Space
|
|
align="start"
|
|
size={spaceSize}
|
|
{...props.spaceProps}
|
|
class={[
|
|
`${prefixCls}-${groupType.value}`,
|
|
]}
|
|
>
|
|
{props.options.length > 0 ? renderOptions() : slots.default?.()}
|
|
</Space>
|
|
)
|
|
},
|
|
})
|