diff --git a/content/input/cascader/index.md b/content/input/cascader/index.md index cf1427af61..59ec5d2cd5 100644 --- a/content/input/cascader/index.md +++ b/content/input/cascader/index.md @@ -1666,7 +1666,7 @@ interface TriggerRenderProps { disabled: boolean; /** * 已选中的 node 在 treeData 中的层级位置,如下例子, - * 当选中浙江省-杭州市-萧山区时,此处 value 为 '0-0-0' + * 当选中浙江省-杭州市-萧山区时,此处 value 为 '0-0-1' */ value?: string | Set; /* 当前 Input 框的输入值 */ diff --git a/packages/semi-foundation/cascader/foundation.ts b/packages/semi-foundation/cascader/foundation.ts index a2018868b6..2dc24ee199 100644 --- a/packages/semi-foundation/cascader/foundation.ts +++ b/packages/semi-foundation/cascader/foundation.ts @@ -15,7 +15,8 @@ import { normalizedArr, isValid, calcMergeType, - getKeysByValuePath + getKeysByValuePath, + getKeyByPos } from './util'; import { strings } from './constants'; import isEnterPress from '../utils/isEnterPress'; @@ -56,6 +57,8 @@ export interface BasicEntity { parentKey?: string; /* key path */ path: Array; + /* pos in treeData */ + pos: string; /* value path */ valuePath: Array } @@ -1026,17 +1029,20 @@ export default class CascaderFoundation extends BaseFoundation { const { keyEntities } = this.getStates(); const { disabled } = this.getProps(); if (disabled) { /* istanbul ignore next */ return; } - const removedItem = (Object.values(keyEntities) as BasicEntity[]) - .filter(item => isEqual(item.valuePath, tagValuePath))[0]; - !isEmpty(removedItem) && - !removedItem.data.disabled && - this._handleMultipleSelect(removedItem); + const removedItem = keyEntities[key] ?? {}; + !removedItem?.data?.disable && this._handleMultipleSelect(removedItem); + } + + handleTagRemoveInTrigger = (pos: string) => { + const { treeData } = this.getStates(); + const key = getKeyByPos(pos, treeData); + this.handleTagRemoveByKey(key); } } diff --git a/packages/semi-foundation/cascader/util.ts b/packages/semi-foundation/cascader/util.ts index 8d0c719b58..a881a1be59 100644 --- a/packages/semi-foundation/cascader/util.ts +++ b/packages/semi-foundation/cascader/util.ts @@ -31,10 +31,12 @@ function traverseDataNodes(treeNodes: any, callback: any) { // Process node if is not root if (node) { const key = parent ? `${parent.key}${VALUE_SPLIT}${node.value}` : node.value; + const pos = parent ? getPosition(parent.pos, ind) : `${ind}`; item = { data: { ...node }, ind, key, + pos, level: parent ? parent.level + 1 : 0, parentKey: parent ? parent.key : null, path: parent ? [...parent.path, key] : [key], @@ -74,6 +76,17 @@ export function getValuePathByKey(key: string) { return key.split(VALUE_SPLIT); } +export function getKeyByPos(pos: string, treeData: any) { + const posArr = pos.split('-').map(item => Number(item)); + let resultData = treeData; + let valuePath = []; + posArr.forEach((item, index) => { + resultData = index === 0 ? resultData[item] : resultData?.children?.[item]; + valuePath.push(resultData?.value); + }); + return getKeyByValuePath(valuePath); +} + export function convertDataToEntities(dataNodes: any) { const keyEntities: any = {}; diff --git a/packages/semi-ui/cascader/__test__/cascader.test.js b/packages/semi-ui/cascader/__test__/cascader.test.js index f10bbddcfa..02305865a0 100644 --- a/packages/semi-ui/cascader/__test__/cascader.test.js +++ b/packages/semi-ui/cascader/__test__/cascader.test.js @@ -1257,7 +1257,7 @@ describe('Cascader', () => { const args = firstCall.args[0]; /* check arguments of triggerRender */ expect(args.value.size).toEqual(1); - expect(args.value).toEqual(new Set(['Asia'])); + expect(args.value).toEqual(new Set('0')); cascaderAutoMerge.unmount(); const spyTriggerRender2 = sinon.spy(() => 123); @@ -1272,7 +1272,7 @@ describe('Cascader', () => { const args2 = firstCall2.args[0]; /* check arguments of triggerRender */ expect(args2.value.size).toEqual(4); - expect(args2.value).toEqual(new Set(["Asia","Asia_SEMI_CASCADER_SPLIT_China","Asia_SEMI_CASCADER_SPLIT_China_SEMI_CASCADER_SPLIT_Beijing","Asia_SEMI_CASCADER_SPLIT_China_SEMI_CASCADER_SPLIT_Shanghai"])); + expect(args2.value).toEqual(new Set(['0','0-0','0-0-1','0-0-0'])); cascaderNoAutoMerge.unmount(); }); diff --git a/packages/semi-ui/cascader/index.tsx b/packages/semi-ui/cascader/index.tsx index 2b99dc8106..a91256e5fb 100644 --- a/packages/semi-ui/cascader/index.tsx +++ b/packages/semi-ui/cascader/index.tsx @@ -17,7 +17,7 @@ import { numbers as popoverNumbers } from '@douyinfe/semi-foundation/popover/con import { isSet, isEqual, isString, isEmpty, isFunction, isNumber, noop, flatten, isObject } from 'lodash'; import '@douyinfe/semi-foundation/cascader/cascader.scss'; import { IconClear, IconChevronDown } from '@douyinfe/semi-icons'; -import { convertDataToEntities, calcMergeType, getKeyByValuePath } from '@douyinfe/semi-foundation/cascader/util'; +import { convertDataToEntities, calcMergeType, getKeyByValuePath, getKeyByPos } from '@douyinfe/semi-foundation/cascader/util'; import { calcCheckedKeys, normalizeKeyList, calcDisabledKeys } from '@douyinfe/semi-foundation/tree/treeUtil'; import ConfigContext, { ContextValue } from '../configProvider/context'; import BaseComponent, { ValidateStatus } from '../_base/baseComponent'; @@ -514,13 +514,14 @@ class Cascader extends BaseComponent { this.foundation.handleInputChange(value); }; - handleTagRemove = (e: any, tagValuePath: Array) => { - this.foundation.handleTagRemove(e, tagValuePath); - }; + handleTagRemoveInTrigger = (pos: string) => { + this.foundation.handleTagRemoveInTrigger(pos); + } - handleRemoveByKey = (key) => { - const { keyEntities } = this.state; - this.handleTagRemove(null, keyEntities[key].valuePath); + handleTagClose = (tagChildren: React.ReactNode, e: React.MouseEvent, tagKey: string | number) => { + // When value has not changed, prevent clicking tag closeBtn to close tag + e.preventDefault(); + this.foundation.handleTagRemoveByKey(tagKey); } renderTagItem = (nodeKey: string, idx: number) => { @@ -542,13 +543,10 @@ class Cascader extends BaseComponent { size={size === 'default' ? 'large' : size} key={`tag-${nodeKey}-${idx}`} color="white" + tagKey={nodeKey} className={tagCls} closable - onClose={(tagChildren, e) => { - // When value has not changed, prevent clicking tag closeBtn to close tag - e.preventDefault(); - this.handleTagRemove(e, keyEntities[nodeKey].valuePath); - }} + onClose={this.handleTagClose} > {keyEntities[nodeKey].data[displayProp]} @@ -558,6 +556,10 @@ class Cascader extends BaseComponent { return null; }; + onRemoveInTagInput = (v: string) => { + this.foundation.handleTagRemoveByKey(v); + }; + renderTagInput() { const { size, disabled, placeholder, maxTagCount, showRestTagsPopover, restTagsPopoverProps } = this.props; const { inputValue, checkedKeys, keyEntities, resolvedCheckedKeys } = this.state; @@ -573,11 +575,11 @@ class Cascader extends BaseComponent { showRestTagsPopover={showRestTagsPopover} restTagsPopoverProps={restTagsPopoverProps} maxTagCount={maxTagCount} - renderTagItem={(value, index) => this.renderTagItem(value, index)} + renderTagItem={this.renderTagItem} inputValue={inputValue} onInputChange={this.handleInputChange} // TODO Modify logic, not modify type - onRemove={v => this.handleTagRemove(null, (v as unknown) as (string | number)[])} + onRemove={this.onRemoveInTagInput} placeholder={placeholder} expandRestTagsOnClick={false} /> @@ -834,16 +836,18 @@ class Cascader extends BaseComponent { renderCustomTrigger = () => { const { disabled, triggerRender, multiple } = this.props; - const { selectedKeys, inputValue, inputPlaceHolder, resolvedCheckedKeys, checkedKeys } = this.state; + const { selectedKeys, inputValue, inputPlaceHolder, resolvedCheckedKeys, checkedKeys, keyEntities } = this.state; let realValue; if (multiple) { if (this.mergeType === strings.NONE_MERGE_TYPE) { - realValue = checkedKeys; + realValue = new Set(); + checkedKeys.forEach(key => { realValue.add(keyEntities[key]?.pos); }); } else { - realValue = resolvedCheckedKeys; + realValue = new Set(); + resolvedCheckedKeys.forEach(key => { realValue.add(keyEntities[key]?.pos); }); } } else { - realValue = [...selectedKeys][0]; + realValue = keyEntities[[...selectedKeys][0]]?.pos; } return ( { componentName={'Cascader'} componentProps={{ ...this.props }} onSearch={this.handleInputChange} - onRemove={this.handleRemoveByKey} + onRemove={this.handleTagRemoveInTrigger} /> ); };