element-plus
Version:
A Component Library for Vue 3
236 lines (214 loc) • 6.09 kB
text/typescript
import { ref, computed, inject, getCurrentInstance, watch } from 'vue'
import { toTypeString } from '@vue/shared'
import { UPDATE_MODEL_EVENT } from '@element-plus/utils/constants'
import { useGlobalConfig } from '@element-plus/utils/util'
import { elFormKey, elFormItemKey } from '@element-plus/tokens'
import type { ExtractPropTypes } from 'vue'
import type { ElFormContext, ElFormItemContext } from '@element-plus/tokens'
import type { PartialReturnType } from '@element-plus/utils/types'
import type { ICheckboxGroupInstance } from './checkbox.type'
export const useCheckboxProps = {
modelValue: {
type: [Boolean, Number, String],
default: () => undefined,
},
label: {
type: [String, Boolean, Number, Object],
},
indeterminate: Boolean,
disabled: Boolean,
checked: Boolean,
name: {
type: String,
default: undefined,
},
trueLabel: {
type: [String, Number],
default: undefined,
},
falseLabel: {
type: [String, Number],
default: undefined,
},
size: String,
}
export type IUseCheckboxProps = ExtractPropTypes<typeof useCheckboxProps>
export const useCheckboxGroup = () => {
const ELEMENT = useGlobalConfig()
const elForm = inject(elFormKey, {} as ElFormContext)
const elFormItem = inject(elFormItemKey, {} as ElFormItemContext)
const checkboxGroup = inject<ICheckboxGroupInstance>('CheckboxGroup', {})
const isGroup = computed(
() => checkboxGroup && checkboxGroup?.name === 'ElCheckboxGroup'
)
const elFormItemSize = computed(() => {
return elFormItem.size
})
return {
isGroup,
checkboxGroup,
elForm,
ELEMENT,
elFormItemSize,
elFormItem,
}
}
const useModel = (props: IUseCheckboxProps) => {
const selfModel = ref(false)
const { emit } = getCurrentInstance()
const { isGroup, checkboxGroup } = useCheckboxGroup()
const isLimitExceeded = ref(false)
const store = computed(() =>
checkboxGroup ? checkboxGroup.modelValue?.value : props.modelValue
)
const model = computed({
get() {
return isGroup.value ? store.value : props.modelValue ?? selfModel.value
},
set(val: unknown) {
if (isGroup.value && Array.isArray(val)) {
isLimitExceeded.value = false
if (
checkboxGroup.min !== undefined &&
val.length < checkboxGroup.min.value
) {
isLimitExceeded.value = true
}
if (
checkboxGroup.max !== undefined &&
val.length > checkboxGroup.max.value
) {
isLimitExceeded.value = true
}
isLimitExceeded.value === false && checkboxGroup?.changeEvent?.(val)
} else {
emit(UPDATE_MODEL_EVENT, val)
selfModel.value = val as boolean
}
},
})
return {
model,
isLimitExceeded,
}
}
const useCheckboxStatus = (
props: IUseCheckboxProps,
{ model }: PartialReturnType<typeof useModel>
) => {
const { isGroup, checkboxGroup, elFormItemSize, ELEMENT } = useCheckboxGroup()
const focus = ref(false)
const size = computed<string | undefined>(
() =>
checkboxGroup?.checkboxGroupSize?.value ||
elFormItemSize.value ||
ELEMENT.size
)
const isChecked = computed<boolean>(() => {
const value = model.value
if (toTypeString(value) === '[object Boolean]') {
return value
} else if (Array.isArray(value)) {
return value.includes(props.label)
} else if (value !== null && value !== undefined) {
return value === props.trueLabel
} else {
return !!value
}
})
const checkboxSize = computed(() => {
const temCheckboxSize = props.size || elFormItemSize.value || ELEMENT.size
return isGroup.value
? checkboxGroup?.checkboxGroupSize?.value || temCheckboxSize
: temCheckboxSize
})
return {
isChecked,
focus,
size,
checkboxSize,
}
}
const useDisabled = (
props: IUseCheckboxProps,
{
model,
isChecked,
}: PartialReturnType<typeof useModel> &
PartialReturnType<typeof useCheckboxStatus>
) => {
const { elForm, isGroup, checkboxGroup } = useCheckboxGroup()
const isLimitDisabled = computed(() => {
const max = checkboxGroup.max?.value
const min = checkboxGroup.min?.value
return (
(!!(max || min) && model.value.length >= max && !isChecked.value) ||
(model.value.length <= min && isChecked.value)
)
})
const isDisabled = computed(() => {
const disabled = props.disabled || elForm.disabled
return isGroup.value
? checkboxGroup.disabled?.value || disabled || isLimitDisabled.value
: props.disabled || elForm.disabled
})
return {
isDisabled,
isLimitDisabled,
}
}
const setStoreValue = (
props: IUseCheckboxProps,
{ model }: PartialReturnType<typeof useModel>
) => {
function addToStore() {
if (Array.isArray(model.value) && !model.value.includes(props.label)) {
model.value.push(props.label)
} else {
model.value = props.trueLabel || true
}
}
props.checked && addToStore()
}
const useEvent = (
props: IUseCheckboxProps,
{ isLimitExceeded }: PartialReturnType<typeof useModel>
) => {
const { elFormItem } = useCheckboxGroup()
const { emit } = getCurrentInstance()
function handleChange(e: InputEvent) {
if (isLimitExceeded.value) return
const target = e.target as HTMLInputElement
const value = target.checked
? props.trueLabel ?? true
: props.falseLabel ?? false
emit('change', value, e)
}
watch(
() => props.modelValue,
() => {
elFormItem.validate?.('change')
}
)
return {
handleChange,
}
}
export const useCheckbox = (props: IUseCheckboxProps) => {
const { model, isLimitExceeded } = useModel(props)
const { focus, size, isChecked, checkboxSize } = useCheckboxStatus(props, {
model,
})
const { isDisabled } = useDisabled(props, { model, isChecked })
const { handleChange } = useEvent(props, { isLimitExceeded })
setStoreValue(props, { model })
return {
isChecked,
isDisabled,
checkboxSize,
model,
handleChange,
focus,
size,
}
}