diff --git a/packages/varlet-ui/src/checkbox-group/__tests__/index.spec.js b/packages/varlet-ui/src/checkbox-group/__tests__/index.spec.js index a8983a7e57f..9e124aadd7f 100644 --- a/packages/varlet-ui/src/checkbox-group/__tests__/index.spec.js +++ b/packages/varlet-ui/src/checkbox-group/__tests__/index.spec.js @@ -72,7 +72,7 @@ test('test checkbox onClick & onChange', async () => { await wrapper.find('.var-checkbox').trigger('click') expect(onClick).toHaveBeenCalledTimes(1) - expect(onChange).lastCalledWith(true) + expect(onChange).lastCalledWith(true, false) wrapper.unmount() }) @@ -146,16 +146,20 @@ test('test checkbox readonly', async () => { wrapper.unmount() }) -test('test checkbox indeterminate', () => { +test('test checkbox indeterminate', async () => { + const onChange = vi.fn() const wrapper = mount(VarCheckbox, { props: { modelValue: false, indeterminate: true, + onChange, }, }) expect(wrapper.html()).toMatchSnapshot() + await wrapper.find('.var-checkbox').trigger('click') + expect(onChange).lastCalledWith(false, false) wrapper.unmount() }) diff --git a/packages/varlet-ui/src/checkbox/Checkbox.vue b/packages/varlet-ui/src/checkbox/Checkbox.vue index 29bee2fc2b1..c058c231de2 100644 --- a/packages/varlet-ui/src/checkbox/Checkbox.vue +++ b/packages/varlet-ui/src/checkbox/Checkbox.vue @@ -121,9 +121,7 @@ export default defineComponent({ const { checkedValue, onChange } = props value.value = changedValue - isIndeterminate.value = false - - call(onChange, value.value) + call(onChange, value.value, isIndeterminate.value) validateWithTrigger('onChange') changedValue === checkedValue ? checkboxGroup?.onChecked(checkedValue) : checkboxGroup?.onUnchecked(checkedValue) } @@ -141,6 +139,13 @@ export default defineComponent({ return } + if (isIndeterminate.value === true) { + isIndeterminate.value = false + call(props.onChange, value.value, isIndeterminate.value) + validateWithTrigger('onChange') + return + } + const maximum = checkboxGroup ? checkboxGroup.checkedCount.value >= Number(checkboxGroup.max.value) : false if (!checked.value && maximum) { diff --git a/packages/varlet-ui/src/checkbox/docs/en-US.md b/packages/varlet-ui/src/checkbox/docs/en-US.md index 650c7c961a1..8738f6a6d4b 100644 --- a/packages/varlet-ui/src/checkbox/docs/en-US.md +++ b/packages/varlet-ui/src/checkbox/docs/en-US.md @@ -21,7 +21,7 @@ | Event | Description | Arguments | | --- | --- | --- | | `click` | Triggered on Click | `e: Event` | -| `change` | Triggered on change | `value: any` | +| `change` | Triggered on change | `value: any, indeterminate: boolean` | ### Slots diff --git a/packages/varlet-ui/src/checkbox/docs/zh-CN.md b/packages/varlet-ui/src/checkbox/docs/zh-CN.md index d701e1bf0a2..327a9639754 100644 --- a/packages/varlet-ui/src/checkbox/docs/zh-CN.md +++ b/packages/varlet-ui/src/checkbox/docs/zh-CN.md @@ -21,7 +21,7 @@ | 事件名 | 说明 | 参数 | | --- | --- | --- | | `click` | 点击时触发 | `e: Event` | -| `change` | 状态变更时触发 | `value: any` | +| `change` | 状态变更时触发 | `value: any, indeterminate: boolean` | ### 插槽 diff --git a/packages/varlet-ui/src/checkbox/props.ts b/packages/varlet-ui/src/checkbox/props.ts index ec67612e018..a439ce95323 100644 --- a/packages/varlet-ui/src/checkbox/props.ts +++ b/packages/varlet-ui/src/checkbox/props.ts @@ -32,7 +32,7 @@ export const props = { }, rules: [Array, Function, Object] as PropType, onClick: defineListenerProp<(e: Event) => void>(), - onChange: defineListenerProp<(value: any) => void>(), + onChange: defineListenerProp<(value: any, indeterminate: boolean) => void>(), 'onUpdate:modelValue': defineListenerProp<(value: any) => void>(), 'onUpdate:indeterminate': defineListenerProp<(value: boolean) => void>(), } diff --git a/packages/varlet-ui/src/menu-select/MenuChildren.vue b/packages/varlet-ui/src/menu-select/MenuChildren.vue index d2daa801924..7734ee987d8 100644 --- a/packages/varlet-ui/src/menu-select/MenuChildren.vue +++ b/packages/varlet-ui/src/menu-select/MenuChildren.vue @@ -19,7 +19,7 @@ :option="option" :ripple="option.ripple" :disabled="option.disabled" - :highlight="highlightOptions.includes(option)" + :highlight="highlightOptions.some((_option) => _option.value === option.value)" @key-arrow-x="handleArrowRight" @mouseenter="handleMouseenter" /> diff --git a/packages/varlet-ui/src/menu-select/MenuSelect.vue b/packages/varlet-ui/src/menu-select/MenuSelect.vue index 4e0d76c6358..5b4e904cbe6 100644 --- a/packages/varlet-ui/src/menu-select/MenuSelect.vue +++ b/packages/varlet-ui/src/menu-select/MenuSelect.vue @@ -82,10 +82,11 @@ export default defineComponent({ components: { VarMenu, VarMenuOption, VarMenuChildren }, props, setup(props) { + const show = useVModel(props, 'show') const menu = ref>() const menuOptionsRef = ref() const menuChildren = ref[]>() - const show = useVModel(props, 'show') + const enhancedOptions = computed(() => enhance(props.options)) const { menuOptions, length, bindMenuOptions } = useMenuOptions() @@ -95,20 +96,18 @@ export default defineComponent({ optionProviders: () => menuOptions, optionProvidersLength: () => length.value, optionIsIndeterminate(optionProvider) { - const option = flattenOptions.value.find((option) => option.value === optionProvider.value.value) - if (!option) { + const enhancedOption = getEnhancedOption(optionProvider.value.value) + if (!enhancedOption) { return false } - const children = getOptionChildren(option) ?? [] + const children = (enhancedOption._children ?? []).filter((option) => !option.disabled) const selectedChildren = children.filter((option) => props.modelValue.includes(option.value)) return selectedChildren.length > 0 && selectedChildren.length < children.length }, }) - const flattenOptions = computed(() => flatten(props.options)) - const highlightOptions = computed(() => { const { multiple, modelValue } = props @@ -116,7 +115,7 @@ export default defineComponent({ return [] } - const selectedOption = flattenOptions.value.find((option) => option.value === modelValue) + const selectedOption = enhancedOptions.value.find((option) => option.value === modelValue) const highlightOptions: MenuSelectOption[] = [] let parent = selectedOption?._parent @@ -139,6 +138,14 @@ export default defineComponent({ useEventListener(() => window, 'keydown', handleKeydown) + function getEnhancedOption(value: any) { + return enhancedOptions.value.find((option) => option.value === value) + } + + function getOptionProvider(value: any) { + return menuOptions.find((optionProvider) => optionProvider.value.value === value) + } + function getOptionChildren(option: MenuSelectOption) { return option[props.childrenKey] as MenuSelectOption[] | undefined } @@ -148,51 +155,72 @@ export default defineComponent({ baseFlatten(options) - function baseFlatten(options: MenuSelectOption[], parent?: MenuSelectOption) { + function baseFlatten(options: MenuSelectOption[]) { options.forEach((option) => { + flattenOptions.push(option) + + const children = getOptionChildren(option) + if (children) { + baseFlatten(children) + } + }) + } + + return flattenOptions + } + + function enhance(options: MenuSelectOption[]) { + function baseEnhance(options: MenuSelectOption[], parent?: MenuSelectOption) { + return options.map((option) => { + option = { ...option } + if (parent) { option._parent = parent } - flattenOptions.push(option) - const children = getOptionChildren(option) + if (children) { - baseFlatten(children, option) + const enhancedChildren = baseEnhance(children, option) + option[props.childrenKey] = enhancedChildren + option._children = flatten(enhancedChildren) } + + return option }) } - return flattenOptions + return flatten(baseEnhance(options)) } function onSelect(optionProvider: MenuOptionProvider) { const { multiple, closeOnSelect } = props const { value, selected } = optionProvider - const option = flattenOptions.value.find((option) => option.value === value.value) + const enhancedOption = getEnhancedOption(value.value) - if (option) { - const children = flatten(getOptionChildren(option) ?? []) - const relationChildrenValues = children.map((option) => option.value) + if (enhancedOption) { + const childrenValues = (enhancedOption._children ?? []) + .filter((option) => !option.disabled) + .map((option) => option.value) if (multiple && selected.value) { menuOptions.forEach((optionProvider) => { - if (relationChildrenValues.includes(optionProvider.value.value)) { + if (childrenValues.includes(optionProvider.value.value)) { optionProvider.sync(true, false) } }) - broadcastParentOption(option) + broadcastParentOption(enhancedOption) } if (multiple && !selected.value) { menuOptions.forEach((optionProvider) => { - if (relationChildrenValues.includes(optionProvider.value.value)) { + if (childrenValues.includes(optionProvider.value.value)) { optionProvider.sync(false, false) } }) - broadcastParentOption(option) + broadcastParentOption(enhancedOption) } } @@ -206,23 +234,20 @@ export default defineComponent({ } } - function broadcastParentOption(option: MenuSelectOption) { - let parentOption = option._parent + function broadcastParentOption(enhancedOption: MenuSelectOption) { + let parentOption = enhancedOption._parent while (parentOption) { - const parentOptionProvider = menuOptions.find( - (optionProvider) => optionProvider.value.value === parentOption!.value - )! - - const parentOptionChildren = getOptionChildren(parentOption)! + const parentOptionProvider = getOptionProvider(parentOption.value)! + const parentOptionChildren = getOptionChildren(parentOption)!.filter((option) => !option.disabled) const isAllChildrenUnselected = parentOptionChildren.every((option) => { - const optionProvider = menuOptions.find((optionProvider) => optionProvider.value.value === option.value)! + const optionProvider = getOptionProvider(option.value)! return !optionProvider.selected.value }) const isAllChildrenSelected = parentOptionChildren.every((option) => { - const optionProvider = menuOptions.find((optionProvider) => optionProvider.value.value === option.value)! + const optionProvider = getOptionProvider(option.value)! return optionProvider.selected.value }) diff --git a/packages/varlet-ui/src/menu-select/example/index.vue b/packages/varlet-ui/src/menu-select/example/index.vue index df2d591b849..d13fef83cfd 100644 --- a/packages/varlet-ui/src/menu-select/example/index.vue +++ b/packages/varlet-ui/src/menu-select/example/index.vue @@ -80,6 +80,7 @@ const cascadeOptions = ref([ { label: '2-2-1', value: 221, + disabled: true, }, { label: '2-2-2', diff --git a/packages/varlet-ui/src/menu-select/props.ts b/packages/varlet-ui/src/menu-select/props.ts index d52be66439c..b91a8580ac0 100644 --- a/packages/varlet-ui/src/menu-select/props.ts +++ b/packages/varlet-ui/src/menu-select/props.ts @@ -15,6 +15,7 @@ export interface MenuSelectOption { disabled?: boolean ripple?: boolean _parent?: MenuSelectOption + _children?: MenuSelectOption[] [key: PropertyKey]: any } diff --git a/packages/varlet-ui/types/checkbox.d.ts b/packages/varlet-ui/types/checkbox.d.ts index dc87dfb7ba6..3842ee6be2a 100644 --- a/packages/varlet-ui/types/checkbox.d.ts +++ b/packages/varlet-ui/types/checkbox.d.ts @@ -19,7 +19,7 @@ export interface CheckboxProps extends BasicAttributes { validateTrigger?: Array rules?: CheckboxRules onClick?: ListenerProp<(e: Event) => void> - onChange?: ListenerProp<(value: any) => void> + onChange?: ListenerProp<(value: any, indeterminate: boolean) => void> 'onUpdate:modelValue'?: ListenerProp<(value: any) => void> 'onUpdate:indeterminate'?: ListenerProp<(value: boolean) => void> }