Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 24 additions & 1 deletion src/cascader/_example-composition/custom-options.vue
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<template>
<t-space>
<t-space direction="vertical">
<!-- 方式一:使用 options 自定义下拉选项内容 -->
<t-cascader
v-model="value1"
Expand Down Expand Up @@ -33,6 +33,22 @@
style="width: 300px"
>
</t-cascader>
<t-cascader
v-model="value4"
:popup-props="{ overlayClassName: 'tdesign-demo-select__overlay-option' }"
:options="options"
multiple
>
<template #option="{ item, onChange }">
<div class="tdesign-demo__user-option" @click="(e) => handleClick(item, onChange)">
<img src="https://tdesign.gtimg.com/site/avatar.jpg" />
<div class="tdesign-demo__user-option-info">
<div>{{ item.label }}</div>
<div>{{ item.value }}</div>
</div>
</div>
</template>
</t-cascader>
</t-space>
</template>

Expand All @@ -42,6 +58,7 @@ import { ref, computed } from 'vue';
const value1 = ref('');
const value2 = ref('');
const value3 = ref('');
const value4 = ref([]);
const options = ref([
{
label: '选项一',
Expand Down Expand Up @@ -97,6 +114,12 @@ const getDeepOptions = (options) => {
}),
}));
};

const handleClick = (item, changeCallback) => {
if (!Array.isArray(item.children)) {
changeCallback();
}
};
</script>

<style>
Expand Down
1 change: 1 addition & 0 deletions src/cascader/_example-composition/filterable.vue
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
<t-cascader v-model="value" :options="options" filterable clearable />
<t-cascader v-model="value2" :options="options" filterable clearable multiple :min-collapsed-num="2" />
<t-cascader v-model="value3" :filter="filterMethod" :options="options" clearable :min-collapsed-num="2" />
<t-cascader v-model="value4" filterable :options="options" clearable multiple check-strictly />
</t-space>
</template>
<script setup>
Expand Down
26 changes: 24 additions & 2 deletions src/cascader/_example/custom-options.vue
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<template>
<t-space>
<t-space direction="vertical">
<!-- 方式一:使用 options 自定义下拉选项内容 -->
<t-cascader
v-model="value1"
Expand All @@ -9,10 +9,10 @@
/>
<!-- 方式二:使用插槽自定义下拉选项内容 -->
<t-cascader
v-model="value2"
:popupProps="{ overlayClassName: 'tdesign-demo-select__overlay-option' }"
:options="options"
style="width: 300px"
multiple
>
<template v-slot:option="{ item }">
<div class="tdesign-demo__user-option">
Expand All @@ -33,6 +33,22 @@
style="width: 300px"
>
</t-cascader>
<t-cascader
v-model="value4"
:popup-props="{ overlayClassName: 'tdesign-demo-select__overlay-option' }"
:options="options"
multiple
>
<template #option="{ item, onChange }">
<div class="tdesign-demo__user-option" @click="(e) => handleClick(item, onChange)">
<img src="https://tdesign.gtimg.com/site/avatar.jpg" />
<div class="tdesign-demo__user-option-info">
<div>{{ item.label }}</div>
<div>{{ item.value }}</div>
</div>
</div>
</template>
</t-cascader>
</t-space>
</template>

Expand All @@ -43,6 +59,7 @@ export default {
value1: '',
value2: '',
value3: '',
value4: [],
options: [
{
label: '选项一',
Expand Down Expand Up @@ -105,6 +122,11 @@ export default {
</div>
);
},
handleClick(item, changeCallback) {
if (!Array.isArray(item.children)) {
changeCallback();
}
},
},
};
</script>
Expand Down
2 changes: 2 additions & 0 deletions src/cascader/_example/filterable.vue
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
<t-cascader v-model="value" :options="options" filterable clearable />
<t-cascader v-model="value2" :options="options" filterable clearable multiple :min-collapsed-num="2" />
<t-cascader v-model="value3" :filter="filterMethod" :options="options" clearable :min-collapsed-num="2" />
<t-cascader v-model="value4" filterable :options="options" clearable multiple check-strictly />
</t-space>
</template>
<script>
Expand Down Expand Up @@ -46,6 +47,7 @@ export default {
value: '',
value2: ['1.1'],
value3: '',
value4: [],
};
},
methods: {
Expand Down
4 changes: 2 additions & 2 deletions src/cascader/cascader.en-US.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,14 +26,14 @@ loadingText | String / Slot / Function | - | Typescript:`string \| TNode`。[s
max | Number | 0 | \- | N
minCollapsedNum | Number | 0 | \- | N
multiple | Boolean | false | \- | N
option | Slot / Function | - | customize one option。Typescript:`TNode<{ item: CascaderOption; index: number }>`。[see more ts definition](https://github.com/Tencent/tdesign-vue/blob/develop/src/common.ts) | N
option | Slot / Function | - | customize one option。Typescript:`TNode<{ item: CascaderOption; index: number; onChange: ()=> void; onExpand: ()=> void }>`。[see more ts definition](https://github.com/Tencent/tdesign-vue/blob/develop/packages/components/common.ts) | N
options | Array | [] | Typescript:`Array<CascaderOption>` | N
placeholder | String | undefined | \- | N
popupProps | Object | - | Typescript:`PopupProps`,[Popup API Documents](./popup?tab=api)。[see more ts definition](https://github.com/Tencent/tdesign-vue/tree/develop/src/cascader/type.ts) | N
popupVisible | Boolean | - | \- | N
defaultPopupVisible | Boolean | - | uncontrolled property | N
readonly | Boolean | false | \- | N
reserveKeyword | Boolean | false | \- | N
reserveKeyword | Boolean | true | \- | N
selectInputProps | Object | - | Typescript:`SelectInputProps`,[SelectInput API Documents](./select-input?tab=api)。[see more ts definition](https://github.com/Tencent/tdesign-vue/tree/develop/src/cascader/type.ts) | N
showAllLevels | Boolean | true | \- | N
size | String | medium | options: large/medium/small。Typescript:`SizeEnum`。[see more ts definition](https://github.com/Tencent/tdesign-vue/blob/develop/src/common.ts) | N
Expand Down
4 changes: 2 additions & 2 deletions src/cascader/cascader.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,13 @@ loadingText | String / Slot / Function | - | 远程加载时显示的文字,
max | Number | 0 | 用于控制多选数量,值为 0 则不限制 | N
minCollapsedNum | Number | 0 | 最小折叠数量,用于多选情况下折叠选中项,超出该数值的选中项折叠。值为 0 则表示不折叠 | N
multiple | Boolean | false | 是否允许多选 | N
option | Slot / Function | - | 自定义单个级联选项。TS 类型:`TNode<{ item: CascaderOption; index: number }>`。[通用类型定义](https://github.com/Tencent/tdesign-vue/blob/develop/src/common.ts) | N
option | Slot / Function | - | 自定义单个级联选项, item 是选项本身的值,index 是下标,onChange 用于触发当前节点选中,onExpand 用于触发当前节点展开。TS 类型:`TNode<{ item: CascaderOption; index: number; onChange: ()=> void; onExpand: ()=> void }>`。[通用类型定义](https://github.com/Tencent/tdesign-vue/blob/develop/packages/components/common.ts) | N
options | Array | [] | 可选项数据源。TS 类型:`Array<CascaderOption>` | N
placeholder | String | undefined | 占位符 | N
popupProps | Object | - | 参考 popup 组件 API。TS 类型:`PopupProps`,[Popup API Documents](./popup?tab=api)。[详细类型定义](https://github.com/Tencent/tdesign-vue/tree/develop/src/cascader/type.ts) | N
popupVisible | Boolean | - | 是否显示下拉框 | N
readonly | Boolean | false | 只读状态,值为真会隐藏输入框,且无法打开下拉框 | N
reserveKeyword | Boolean | false | 多选且可搜索时,是否在选中一个选项后保留当前的搜索关键词 | N
reserveKeyword | Boolean | true | 多选且可搜索时,是否在选中一个选项后保留当前的搜索关键词 | N
selectInputProps | Object | - | 透传 SelectInput 筛选器输入框组件的全部属性。TS 类型:`SelectInputProps`,[SelectInput API Documents](./select-input?tab=api)。[详细类型定义](https://github.com/Tencent/tdesign-vue/tree/develop/src/cascader/type.ts) | N
showAllLevels | Boolean | true | 选中值使用完整路径,输入框在单选时也显示完整路径 | N
size | String | medium | 组件尺寸。可选项:large/medium/small。TS 类型:`SizeEnum`。[通用类型定义](https://github.com/Tencent/tdesign-vue/blob/develop/src/common.ts) | N
Expand Down
5 changes: 3 additions & 2 deletions src/cascader/components/Item.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ export default defineComponent({

function RenderCheckBox(node: TreeNode, cascaderContext: CascaderContextType) {
const {
checkProps, value, max, inputVal,
checkProps, value, max, inputVal, isParentFilterable,
} = cascaderContext;
const label = RenderLabelInner(node, cascaderContext);
return (
Expand All @@ -109,7 +109,7 @@ export default defineComponent({
disabled={node.isDisabled() || ((value as TreeNodeValue[]).length >= max && max !== 0)}
name={String(node.value)}
title={inputVal ? getFullPathLabel(node) : node.label}
stopLabelTrigger={!!node.children}
stopLabelTrigger={!!node.children && !isParentFilterable}
onChange={(vale: boolean, { e }: { e: MouseEvent }) => {
e.stopPropagation();
onChange();
Expand Down Expand Up @@ -138,6 +138,7 @@ export default defineComponent({
? RenderCheckBox(node, cascaderContext)
: RenderLabelContent(node, cascaderContext))}
{node.children
&& !this.cascaderContext.isParentFilterable
&& (node.loading ? <TLoading class={iconClass} size="small" /> : <ChevronRightIcon class={iconClass} />)}
</li>
);
Expand Down
9 changes: 8 additions & 1 deletion src/cascader/components/Panel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,14 @@ export default defineComponent({
const optionChild = node.data.content
? getDefaultNode(node.data.content(this.$createElement))
: renderTNodeJSXDefault('option', {
params: { item: node.data, index },
params: {
item: node.data,
index,
onExpand: () => handleExpand(node, 'click'),
onChange: () => {
valueChangeEffect(node, cascaderContext);
},
},
});
return (
<Item
Expand Down
9 changes: 5 additions & 4 deletions src/cascader/core/className.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,10 @@ export function getNodeStatusClass(
cascaderContext: CascaderContextType,
) {
const {
checkStrictly, multiple, value, max,
checkStrictly, multiple, value, max, isParentFilterable,
} = cascaderContext;
const expandedActive = (!checkStrictly && node.expanded && (multiple ? !node.isLeaf() : true)) || (checkStrictly && node.expanded);
const expandedActive = (!checkStrictly && node.expanded && (multiple ? !node.isLeaf() : true))
|| (checkStrictly && node.expanded && !isParentFilterable);

const isLeaf = node.isLeaf();

Expand Down Expand Up @@ -69,14 +70,14 @@ export function getCascaderItemClass(
STATUS: Record<string, string>,
cascaderContext: CascaderContextType,
) {
const { size } = cascaderContext;
const { size, isParentFilterable } = cascaderContext;
return [
`${prefix}-cascader__item`,
...getNodeStatusClass(node, STATUS, cascaderContext),
SIZE[size],
{
[`${prefix}-cascader__item--with-icon`]: !!node.children,
[`${prefix}-cascader__item--leaf`]: node.isLeaf(),
[`${prefix}-cascader__item--leaf`]: node.isLeaf() || isParentFilterable,
},
];
}
Expand Down
19 changes: 16 additions & 3 deletions src/cascader/core/effect.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ export function expendClickEffect(
valueType,
filterable,
inputVal,
isParentFilterable,
} = cascaderContext;

const isDisabled = node.disabled || (multiple && (value as TreeNodeValue[]).length >= max && max !== 0);
Expand All @@ -46,7 +47,7 @@ export function expendClickEffect(
setTreeNodes(nodes);

// 多选条件下手动维护expend
if (multiple) {
if (multiple && !isParentFilterable) {
setExpend(expanded);
}
}
Expand Down Expand Up @@ -74,7 +75,17 @@ export function expendClickEffect(
*/
export function valueChangeEffect(node: TreeNode, cascaderContext: CascaderContextType) {
const {
disabled, max, inputVal, multiple, setVisible, setValue, treeNodes, treeStore, valueType,
disabled,
max,
inputVal,
multiple,
setVisible,
setValue,
treeNodes,
treeStore,
valueType,
setInputVal,
reserveKeyword,
} = cascaderContext;

if (!node || disabled || node.disabled) {
Expand Down Expand Up @@ -117,6 +128,7 @@ export function valueChangeEffect(node: TreeNode, cascaderContext: CascaderConte
.map((item) => item.value));

setValue(resValue, node.checked ? 'uncheck' : 'check', node.getModel());
if (!reserveKeyword) setInputVal('');
}

/**
Expand Down Expand Up @@ -188,12 +200,13 @@ export const treeNodesEffect = (
treeStore: CascaderContextType['treeStore'],
setTreeNodes: CascaderContextType['setTreeNodes'],
filter: CascaderContextType['filter'],
isParentFilterable: boolean,
) => {
if (!treeStore) return;
let nodes = [];
if (inputVal) {
const filterMethods = (node: TreeNode) => {
if (!node.isLeaf()) return;
if (!node.isLeaf() && !isParentFilterable) return;
if (isFunction(filter)) {
return filter(`${inputVal}`, node as TreeNodeModel & TreeNode);
}
Expand Down
16 changes: 14 additions & 2 deletions src/cascader/hooks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,11 @@ export const useContext = (
expend: [],
});

// 部分模式下需要允许父节点被搜索选择 valueMode = 'parentFirst' 和 checkStrictly
const isParentFilterable = computed(
() => (props.valueMode === 'parentFirst' || props.checkStrictly) && statusContext.inputVal,
);

return {
statusContext,
cascaderContext: computed(() => {
Expand All @@ -53,6 +58,8 @@ export const useContext = (
minCollapsedNum,
valueType,
value,
valueMode,
reserveKeyword,
} = props;
return {
value: statusContext.scopeVal,
Expand All @@ -68,7 +75,10 @@ export const useContext = (
showAllLevels,
minCollapsedNum,
valueType,
valueMode,
reserveKeyword,
visible: innerPopupVisible.value,
isParentFilterable: isParentFilterable.value,
cascaderValue: value,
...statusContext,
setTreeNodes: (nodes: TreeNode[]) => {
Expand Down Expand Up @@ -108,8 +118,10 @@ export const useCascaderContext = (props: TdCascaderProps) => {

// 更新treeNodes
const updatedTreeNodes = () => {
const { inputVal, treeStore, setTreeNodes } = cascaderContext.value;
treeNodesEffect(inputVal, treeStore, setTreeNodes, props.filter);
const {
inputVal, treeStore, setTreeNodes, isParentFilterable,
} = cascaderContext.value;
treeNodesEffect(inputVal, treeStore, setTreeNodes, props.filter, isParentFilterable);
};

// 更新节点展开状态
Expand Down
3 changes: 3 additions & 0 deletions src/cascader/interface.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ export interface CascaderContextType
| 'value'
| 'minCollapsedNum'
| 'valueType'
| 'valueMode'
| 'reserveKeyword'
> {
treeStore: TreeStore;
setValue: (val: CascaderValue, source: CascaderChangeSource, node?: TreeNodeModel) => void;
Expand All @@ -33,6 +35,7 @@ export interface CascaderContextType
setInputVal: (val: TdSelectInputProps['inputValue']) => void;
setExpend: (val: TreeNodeValue[]) => void;
cascaderValue: CascaderValue;
isParentFilterable: boolean;
}

export { TreeNode } from '../_common/js/tree/tree-node';
Expand Down
5 changes: 4 additions & 1 deletion src/cascader/props.ts
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,10 @@ export default {
/** 只读状态,值为真会隐藏输入框,且无法打开下拉框 */
readonly: Boolean,
/** 多选且可搜索时,是否在选中一个选项后保留当前的搜索关键词 */
reserveKeyword: Boolean,
reserveKeyword: {
type: Boolean,
default: true,
},
/** 透传 SelectInput 筛选器输入框组件的全部属性 */
selectInputProps: {
type: Object as PropType<TdCascaderProps['selectInputProps']>,
Expand Down
6 changes: 3 additions & 3 deletions src/cascader/type.ts
Original file line number Diff line number Diff line change
Expand Up @@ -110,9 +110,9 @@ export interface TdCascaderProps<CascaderOption extends TreeOptionData = TreeOpt
*/
multiple?: boolean;
/**
* 自定义单个级联选项
* 自定义单个级联选项, item 是选项本身的值,index 是下标,onChange 用于触发当前节点选中,onExpand 用于触发当前节点展开
*/
option?: TNode<{ item: CascaderOption; index: number }>;
option?: TNode<{ item: CascaderOption; index: number; onChange: () => void; onExpand: () => void }>;
/**
* 可选项数据源
* @default []
Expand All @@ -137,7 +137,7 @@ export interface TdCascaderProps<CascaderOption extends TreeOptionData = TreeOpt
readonly?: boolean;
/**
* 多选且可搜索时,是否在选中一个选项后保留当前的搜索关键词
* @default false
* @default true
*/
reserveKeyword?: boolean;
/**
Expand Down
Loading
Loading