diff --git a/src/Cascader.tsx b/src/Cascader.tsx index 272cd142..96575719 100644 --- a/src/Cascader.tsx +++ b/src/Cascader.tsx @@ -7,7 +7,8 @@ import type { BaseSelectRef, BaseSelectPropsWithoutPrivate, BaseSelectProps } fr import { BaseSelect } from 'rc-select'; import OptionList from './OptionList'; import CascaderContext from './context'; -import { fillFieldNames, toPathKey, toPathKeys } from './utils/commonUtil'; +import type { SHOW_CHILD } from './utils/commonUtil'; +import { fillFieldNames, toPathKey, toPathKeys, SHOW_PARENT } from './utils/commonUtil'; import useDisplayValues from './hooks/useDisplayValues'; import useRefFunc from './hooks/useRefFunc'; import useEntities from './hooks/useEntities'; @@ -43,6 +44,7 @@ export interface InternalFieldNames extends Required { export type SingleValueType = (string | number)[]; export type ValueType = SingleValueType | SingleValueType[]; +export type ShowCheckedStrategy = typeof SHOW_PARENT | typeof SHOW_CHILD; export interface BaseOptionType { disabled?: boolean; @@ -71,6 +73,7 @@ interface BaseCascaderProps React.ReactNode; checkable?: boolean | React.ReactNode; + showCheckedStrategy?: ShowCheckedStrategy; // Search showSearch?: boolean | ShowSearchType; @@ -209,6 +212,7 @@ const Cascader = React.forwardRef((props, re // Children children, dropdownMatchSelectWidth = false, + showCheckedStrategy = SHOW_PARENT, ...restProps } = props; @@ -296,10 +300,20 @@ const Cascader = React.forwardRef((props, re const deDuplicatedValues = React.useMemo(() => { const checkedKeys = toPathKeys(checkedValues); - const deduplicateKeys = formatStrategyValues(checkedKeys, getPathKeyEntities); + const deduplicateKeys = formatStrategyValues( + checkedKeys, + getPathKeyEntities, + showCheckedStrategy, + ); return [...missingCheckedValues, ...getValueByKeyPath(deduplicateKeys)]; - }, [checkedValues, getPathKeyEntities, getValueByKeyPath, missingCheckedValues]); + }, [ + checkedValues, + getPathKeyEntities, + getValueByKeyPath, + missingCheckedValues, + showCheckedStrategy, + ]); const displayValues = useDisplayValues( deDuplicatedValues, @@ -374,7 +388,11 @@ const Cascader = React.forwardRef((props, re } // Roll up to parent level keys - const deDuplicatedKeys = formatStrategyValues(checkedKeys, getPathKeyEntities); + const deDuplicatedKeys = formatStrategyValues( + checkedKeys, + getPathKeyEntities, + showCheckedStrategy, + ); nextCheckedValues = getValueByKeyPath(deDuplicatedKeys); } diff --git a/src/utils/commonUtil.ts b/src/utils/commonUtil.ts index bd1289d4..59562678 100644 --- a/src/utils/commonUtil.ts +++ b/src/utils/commonUtil.ts @@ -6,6 +6,8 @@ import type { } from '../Cascader'; export const VALUE_SPLIT = '__RC_CASCADER_SPLIT__'; +export const SHOW_PARENT = 'SHOW_PARENT'; +export const SHOW_CHILD = 'SHOW_CHILD'; export function toPathKey(value: SingleValueType) { return value.join(VALUE_SPLIT); diff --git a/src/utils/treeUtil.ts b/src/utils/treeUtil.ts index d67e7e6c..0b143e65 100644 --- a/src/utils/treeUtil.ts +++ b/src/utils/treeUtil.ts @@ -1,18 +1,28 @@ -import type { SingleValueType, DefaultOptionType, InternalFieldNames } from '../Cascader'; +import type { + SingleValueType, + DefaultOptionType, + InternalFieldNames, + ShowCheckedStrategy, +} from '../Cascader'; import type { GetEntities } from '../hooks/useEntities'; +import { SHOW_CHILD } from './commonUtil'; -export function formatStrategyValues(pathKeys: React.Key[], getKeyPathEntities: GetEntities) { +export function formatStrategyValues( + pathKeys: React.Key[], + getKeyPathEntities: GetEntities, + showCheckedStrategy: ShowCheckedStrategy, +) { const valueSet = new Set(pathKeys); const keyPathEntities = getKeyPathEntities(); return pathKeys.filter(key => { const entity = keyPathEntities[key]; const parent = entity ? entity.parent : null; + const children = entity ? entity.children : null; - if (parent && !parent.node.disabled && valueSet.has(parent.key)) { - return false; - } - return true; + return showCheckedStrategy === SHOW_CHILD + ? !(children && children.some(child => child.key && valueSet.has(child.key))) + : !(parent && !parent.node.disabled && valueSet.has(parent.key)); }); } diff --git a/tests/index.spec.tsx b/tests/index.spec.tsx index d1525ab2..904bf9fa 100644 --- a/tests/index.spec.tsx +++ b/tests/index.spec.tsx @@ -87,6 +87,56 @@ describe('Cascader.Basic', () => { expect(selectedValue.join(',')).toBe('fj,fuzhou,mawei'); }); + it('should support showCheckedStrategy parent', () => { + const wrapper = mount( + + + , + ); + wrapper.find('input').simulate('click'); + let menus = wrapper.find('.rc-cascader-menu'); + expect(menus.length).toBe(1); + wrapper.clickOption(0, 2); + menus = wrapper.find('.rc-cascader-menu'); + expect(menus.length).toBe(2); + wrapper.clickOption(1, 0); + wrapper.clickOption(1, 1); + expect(selectedValue.join(',')).toBe('bj'); + }); + + it('should support showCheckedStrategy child', () => { + const wrapper = mount( + + + , + ); + wrapper.find('input').simulate('click'); + + // Menu 1 + let menus = wrapper.find('.rc-cascader-menu'); + expect(menus.length).toBe(1); + wrapper.clickOption(0, 2); + menus = wrapper.find('.rc-cascader-menu'); + expect(menus.length).toBe(2); + wrapper.clickOption(1, 0); + wrapper.clickOption(1, 1); + expect(selectedValue[0].join(',')).toBe('bj,chaoyang'); + expect(selectedValue[1].join(',')).toBe('bj,haidian'); + expect(selectedValue.join(',')).toBe('bj,chaoyang,bj,haidian'); + }); + it('should has defaultValue', () => { const wrapper = mount(