feat: tree-select add status & placement

pull/5820/head
tangjinzhou 2022-05-22 10:10:43 +08:00
parent bf1226d3bb
commit 03f559a0dc
11 changed files with 182 additions and 7 deletions

View File

@ -11,6 +11,8 @@
<virtualScrollVue />
<customTagRenderVue />
<replaceFieldsVue />
<placementVue />
<statusVue />
</demo-sort>
</template>
<script lang="ts">
@ -25,6 +27,8 @@ import treeLineVue from './tree-line.vue';
import virtualScrollVue from './virtual-scroll.vue';
import customTagRenderVue from './custom-tag-render.vue';
import replaceFieldsVue from './replaceFields.vue';
import placementVue from './placement.vue';
import statusVue from './status.vue';
import CN from '../index.zh-CN.md';
import US from '../index.en-US.md';
import { defineComponent } from 'vue';
@ -33,6 +37,8 @@ export default defineComponent({
CN,
US,
components: {
placementVue,
statusVue,
Basic,
Multiple,
// TreeData,

View File

@ -0,0 +1,85 @@
<docs>
---
order: 28
title:
zh-CN: 弹出位置
en-US: Popup Placement
---
## zh-CN
可以通过 `placement` 手动指定弹出的位置
## en-US
You can manually specify the position of the popup via `placement`.
</docs>
<template>
<a-radio-group v-model:value="placement">
<a-radio-button value="topLeft">topLeft</a-radio-button>
<a-radio-button value="topRight">topRight</a-radio-button>
<a-radio-button value="bottomLeft">bottomLeft</a-radio-button>
<a-radio-button value="bottomRight">bottomRight</a-radio-button>
</a-radio-group>
<br />
<br />
<a-tree-select
v-model:value="value"
show-search
:dropdown-style="{ maxHeight: '400px', overflow: 'auto', minWidth: '300px' }"
placeholder="Please select"
allow-clear
tree-default-expand-all
:tree-data="treeData"
:placement="placement"
:dropdown-match-select-width="false"
>
<template #title="{ value: val, title }">
<b v-if="val === 'parent 1-1'" style="color: #08c">sss</b>
<template v-else>{{ title }}</template>
</template>
</a-tree-select>
</template>
<script lang="ts">
import { defineComponent, ref } from 'vue';
import type { TreeSelectProps } from 'ant-design-vue';
export default defineComponent({
setup() {
const placement = ref('topLeft' as const);
const value = ref<string>();
const treeData = ref<TreeSelectProps['treeData']>([
{
title: 'parent 1',
value: 'parent 1',
children: [
{
title: 'parent 1-0',
value: 'parent 1-0',
children: [
{
title: 'my leaf',
value: 'leaf1',
},
{
title: 'your leaf',
value: 'leaf2',
},
],
},
{
title: 'parent 1-1',
value: 'parent 1-1',
},
],
},
]);
return {
placement,
value,
treeData,
};
},
});
</script>

View File

@ -0,0 +1,33 @@
<docs>
---
order: 19
version: 3.3.0
title:
zh-CN: 自定义状态
en-US: Status
---
## zh-CN
使用 `status` DatePicker 添加状态可选 `error` 或者 `warning`
## en-US
Add status to DatePicker with `status`, which could be `error` or `warning`.
</docs>
<template>
<a-space direction="vertical" style="width: 100%">
<a-tree-select status="error" style="width: 100%" placeholder="Error" />
<a-tree-select status="warning" style="width: 100%" multiple placeholder="Warning multiple" />
</a-space>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
export default defineComponent({
setup() {
return {};
},
});
</script>

View File

@ -34,12 +34,14 @@ Tree selection control.
| multiple | Support multiple or not, will be `true` when enable `treeCheckable`. | boolean | false | | |
| notFoundContent | Specify content to show when no result matches | slot | `Not Found` | | |
| placeholder | Placeholder of the select input | string\|slot | - | | |
| placement | The position where the selection box pops up | `bottomLeft` `bottomRight` `topLeft` `topRight` | bottomLeft | 3.3.0 |
| replaceFields | Replace the title,value, key and children fields in treeNode with the corresponding fields in treeData | object | { children:'children', label:'title', value: 'value' } | | 1.6.1 (3.0.0 deprecated) |
| searchPlaceholder | Placeholder of the search input | string\|slot | - | | |
| searchValue(v-model) | work with `search` event to make search value controlled. | string | - | | |
| showCheckedStrategy | The way show selected item in box. **Default:** just show child nodes. **`TreeSelect.SHOW_ALL`:** show all checked treeNodes (include parent treeNode). **`TreeSelect.SHOW_PARENT`:** show checked treeNodes (just show parent treeNode). | enum { TreeSelect.SHOW_ALL, TreeSelect.SHOW_PARENT, TreeSelect.SHOW_CHILD } | TreeSelect.SHOW_CHILD | | |
| showSearch | Whether to display a search input in the dropdown menu(valid only in the single mode) | boolean | false | | |
| size | To set the size of the select input, options: `large` `small` | string | 'default' | | |
| status | Set validation status | 'error' \| 'warning' | - | 3.3.0 |
| suffixIcon | The custom suffix icon | VNode \| slot | - | | |
| tagRender | Customize tag render when `multiple` | (props) => slot | - | 3.0 | |
| title | custom title | slot | | 3.0.0 | |
@ -51,6 +53,7 @@ Tree selection control.
| treeDefaultExpandedKeys | Default expanded treeNodes | string\[] \| number\[] | - | | |
| treeExpandedKeys(v-model) | Set expanded keys | string\[] \| number\[] | - | | |
| treeIcon | Shows the icon before a TreeNode's title. There is no default style; you must set a custom style for it if set to `true` | boolean | false | | |
| treeLoadedKeys | (Controlled) Set loaded tree nodes, work with `loadData` only | string[] | [] | 3.3.0 |
| treeLine | Show the line. Ref [Tree - showLine](/components/tree/#components-tree-demo-line) | boolean \| object | false | 3.0 | |
| treeNodeFilterProp | Will be used for filtering if `filterTreeNode` returns true | string | 'value' | | |
| treeNodeLabelProp | Will render as content of select | string | 'title' | | |

View File

@ -20,10 +20,14 @@ import type { SwitcherIconProps } from '../tree/utils/iconUtil';
import renderSwitcherIcon from '../tree/utils/iconUtil';
import { warning } from '../vc-util/warning';
import { flattenChildren } from '../_util/props-util';
import { useInjectFormItemContext } from '../form/FormItemContext';
import { FormItemInputContext, useInjectFormItemContext } from '../form/FormItemContext';
import type { BaseSelectRef } from '../vc-select';
import type { BaseOptionType, DefaultOptionType } from '../vc-tree-select/TreeSelect';
import type { TreeProps } from '../tree';
import type { SelectCommonPlacement } from '../_util/transition';
import { getTransitionDirection } from '../_util/transition';
import type { InputStatus } from '../_util/statusUtils';
import { getStatusClassNames, getMergedStatus } from '../_util/statusUtils';
const getTransitionName = (rootPrefixCls: string, motion: string, transitionName?: string) => {
if (transitionName !== undefined) {
@ -62,6 +66,8 @@ export function treeSelectProps<
bordered: { type: Boolean, default: undefined },
treeLine: { type: [Boolean, Object] as PropType<TreeProps['showLine']>, default: undefined },
replaceFields: { type: Object as PropType<FieldNames> },
placement: String as PropType<SelectCommonPlacement>,
status: String as PropType<InputStatus>,
'onUpdate:value': { type: Function as PropType<(value: any) => void> },
'onUpdate:treeExpandedKeys': { type: Function as PropType<(keys: Key[]) => void> },
'onUpdate:searchValue': { type: Function as PropType<(value: string) => void> },
@ -107,6 +113,8 @@ const TreeSelect = defineComponent({
});
const formItemContext = useInjectFormItemContext();
const formItemInputContext = FormItemInputContext.useInject();
const mergedStatus = computed(() => getMergedStatus(formItemInputContext.status, props.status));
const {
prefixCls,
renderEmpty,
@ -118,8 +126,21 @@ const TreeSelect = defineComponent({
getPrefixCls,
} = useConfigInject('select', props);
const rootPrefixCls = computed(() => getPrefixCls());
// ===================== Placement =====================
const placement = computed(() => {
if (props.placement !== undefined) {
return props.placement;
}
return direction.value === 'rtl'
? ('bottomRight' as SelectCommonPlacement)
: ('bottomLeft' as SelectCommonPlacement);
});
const transitionName = computed(() =>
getTransitionName(rootPrefixCls.value, 'slide-up', props.transitionName),
getTransitionName(
rootPrefixCls.value,
getTransitionDirection(placement.value),
props.transitionName,
),
);
const choiceTransitionName = computed(() =>
getTransitionName(rootPrefixCls.value, '', props.choiceTransitionName),
@ -134,6 +155,9 @@ const TreeSelect = defineComponent({
);
const isMultiple = computed(() => !!(props.treeCheckable || props.multiple));
const mergedShowArrow = computed(() =>
props.showArrow !== undefined ? props.showArrow : props.loading || !isMultiple.value,
);
const treeSelectRef = ref();
expose({
@ -173,15 +197,20 @@ const TreeSelect = defineComponent({
multiple,
treeIcon,
treeLine,
showArrow,
switcherIcon = slots.switcherIcon?.(),
fieldNames = props.replaceFields,
id = formItemContext.id.value,
} = props;
const { isFormItemInput, hasFeedback, feedbackIcon } = formItemInputContext;
// ===================== Icons =====================
const { suffixIcon, removeIcon, clearIcon } = getIcons(
{
...props,
multiple: isMultiple.value,
showArrow: mergedShowArrow.value,
hasFeedback,
feedbackIcon,
prefixCls: prefixCls.value,
},
slots,
@ -202,6 +231,7 @@ const TreeSelect = defineComponent({
'clearIcon',
'switcherIcon',
'bordered',
'status',
'onUpdate:value',
'onUpdate:treeExpandedKeys',
'onUpdate:searchValue',
@ -214,7 +244,9 @@ const TreeSelect = defineComponent({
[`${prefixCls.value}-sm`]: size.value === 'small',
[`${prefixCls.value}-rtl`]: direction.value === 'rtl',
[`${prefixCls.value}-borderless`]: !bordered,
[`${prefixCls.value}-in-form-item`]: isFormItemInput,
},
getStatusClassNames(prefixCls.value, mergedStatus.value, hasFeedback),
attrs.class,
);
const otherProps: any = {};
@ -263,6 +295,8 @@ const TreeSelect = defineComponent({
treeCheckable: () => <span class={`${prefixCls.value}-tree-checkbox-inner`} />,
}}
maxTagPlaceholder={props.maxTagPlaceholder || slots.maxTagPlaceholder}
placement={placement.value}
showArrow={hasFeedback || showArrow}
/>
);
};

View File

@ -35,12 +35,14 @@ cover: https://gw.alipayobjects.com/zos/alicdn/Ax4DA0njr/TreeSelect.svg
| multiple | 支持多选(当设置 treeCheckable 时自动变为 true | boolean | false | | |
| notFoundContent | 当下拉列表为空时显示的内容 | slot | `Not Found` | | |
| placeholder | 选择框默认文字 | string\|slot | - | | |
| placement | 选择框弹出的位置 | `bottomLeft` `bottomRight` `topLeft` `topRight` | bottomLeft | 3.3.0 |
| replaceFields | 替换 treeNode 中 title,value,key,children 字段为 treeData 中对应的字段 | object | {children:'children', label:'title', key:'key', value: 'value' } | | 1.6.1 (3.0.0 废弃) |
| searchPlaceholder | 搜索框默认文字 | string\|slot | - | | |
| searchValue(v-model) | 搜索框的值,可以通过 `search` 事件获取用户输入 | string | - | | |
| showCheckedStrategy | 定义选中项回填的方式。`TreeSelect.SHOW_ALL`: 显示所有选中节点(包括父节点). `TreeSelect.SHOW_PARENT`: 只显示父节点(当父节点下所有子节点都选中时). 默认只显示子节点. | enum{TreeSelect.SHOW_ALL, TreeSelect.SHOW_PARENT, TreeSelect.SHOW_CHILD } | TreeSelect.SHOW_CHILD | | |
| showSearch | 在下拉中显示搜索框(仅在单选模式下生效) | boolean | false | | |
| size | 选择框大小,可选 `large` `small` | string | 'default' | | |
| status | 设置校验状态 | 'error' \| 'warning' | - | 3.3.0 |
| suffixIcon | 自定义的选择框后缀图标 | VNode \| slot | - | | |
| tagRender | 自定义 tag 内容,多选时生效 | slot | - | 3.0 | |
| title | 自定义标题 | slot | | 3.0.0 | |
@ -53,6 +55,7 @@ cover: https://gw.alipayobjects.com/zos/alicdn/Ax4DA0njr/TreeSelect.svg
| treeExpandedKeys(v-model) | 设置展开的树节点 | string\[] \| number\[] | - | | |
| treeIcon | 是否展示 TreeNode title 前的图标,没有默认样式,如设置为 true需要自行定义图标相关样式 | boolean | false | | |
| treeLine | 是否展示线条样式,请参考 [Tree - showLine](/components/tree/#components-tree-demo-line) | boolean \| object | false | 3.0 | |
| treeLoadedKeys | (受控)已经加载的节点,需要配合 `loadData` 使用 | string[] | [] | 3.3.0 |
| treeNodeFilterProp | 输入项过滤对应的 treeNode 属性 | string | 'value' | | |
| treeNodeLabelProp | 作为显示的 prop 设置 | string | 'title' | | |
| value(v-model) | 指定当前选中的条目 | string/string\[] | - | | |

View File

@ -2,6 +2,6 @@ import '../../style/index.less';
import './index.less';
// style dependencies
// deps-lint-skip: tree
// deps-lint-skip: tree, form
import '../../select/style';
import '../../empty/style';

View File

@ -181,7 +181,8 @@ export default defineComponent({
open,
notFoundContent = slots.notFoundContent?.(),
} = baseProps;
const { listHeight, listItemHeight, virtual } = context;
const { listHeight, listItemHeight, virtual, dropdownMatchSelectWidth, treeExpandAction } =
context;
const {
checkable,
treeDefaultExpandAll,
@ -228,7 +229,7 @@ export default defineComponent({
treeData={memoTreeData.value as TreeDataNode[]}
height={listHeight}
itemHeight={listItemHeight}
virtual={virtual}
virtual={virtual !== false && dropdownMatchSelectWidth !== false}
multiple={multiple}
icon={treeIcon}
showIcon={showTreeIcon}
@ -251,6 +252,7 @@ export default defineComponent({
onExpand={onInternalExpand}
onLoad={onTreeLoad}
filterTreeNode={filterTreeNode}
expandAction={treeExpandAction}
v-slots={{ ...slots, checkable: legacyContext.customSlots.treeCheckable }}
/>
</div>

View File

@ -30,6 +30,7 @@ import { conductCheck } from '../vc-tree/utils/conductUtil';
import { warning } from '../vc-util/warning';
import { toReactive } from '../_util/toReactive';
import useMaxLevel from '../vc-tree/useMaxLevel';
import type { ExpandAction } from '../tree/DirectoryTree';
export type OnInternalSelect = (value: RawValueType, info: { selected: boolean }) => void;
@ -180,6 +181,7 @@ export function treeSelectProps<
switcherIcon: PropTypes.any,
treeMotion: PropTypes.any,
children: Array as PropType<VueNode[]>,
treeExpandAction: String as PropType<ExpandAction>,
showArrow: { type: Boolean, default: undefined },
showSearch: { type: Boolean, default: undefined },
@ -621,8 +623,10 @@ export default defineComponent({
switcherIcon,
treeMotion,
customSlots,
dropdownMatchSelectWidth,
treeExpandAction,
} = toRefs(props);
toRaw;
useProvideLegacySelectContext(
toReactive({
checkable: mergedCheckable,
@ -654,6 +658,8 @@ export default defineComponent({
treeData: filteredTreeData,
fieldNames: mergedFieldNames,
onSelect: onOptionSelect,
dropdownMatchSelectWidth,
treeExpandAction,
} as unknown as TreeSelectContextProps),
);
const selectRef = ref<BaseSelectRef>();

View File

@ -1,14 +1,17 @@
import type { InjectionKey } from 'vue';
import { provide, inject } from 'vue';
import type { ExpandAction } from '../tree/DirectoryTree';
import type { DefaultOptionType, InternalFieldName, OnInternalSelect } from './TreeSelect';
export interface TreeSelectContextProps {
virtual?: boolean;
dropdownMatchSelectWidth?: boolean | number;
listHeight: number;
listItemHeight: number;
treeData: DefaultOptionType[];
fieldNames: InternalFieldName;
onSelect: OnInternalSelect;
treeExpandAction?: ExpandAction;
}
const TreeSelectContextPropsKey: InjectionKey<TreeSelectContextProps> = Symbol(

View File

@ -1,4 +1,4 @@
// base rc-tree-select@5.1.4
// base rc-tree-select@5.4.0
import type { TreeSelectProps } from './TreeSelect';
import TreeSelect, { treeSelectProps } from './TreeSelect';
import TreeNode from './TreeNode';