Compare commits

...

17 Commits

Author SHA1 Message Date
tangjinzhou 3e39a9ff79 fix: maxTagmaxTagPlaceholder not work 2022-01-21 21:28:42 +08:00
tangjinzhou c304ebb54f refactor: cascader 2022-01-21 17:40:30 +08:00
tangjinzhou 3a6cc6aa09 refactor: cascader 2022-01-21 15:01:11 +08:00
tangjinzhou 616478be1f refactor: cascader 2022-01-20 21:29:50 +08:00
tangjinzhou e08c6da9b5 refactor: cascader 2022-01-20 10:56:17 +08:00
tangjinzhou d46762c1d6 feat: tree-select add tag-render 2022-01-16 11:13:39 +08:00
tangjinzhou 646fad2eda refactor: remove lod vc-tree-select 2022-01-16 11:10:45 +08:00
tangjinzhou 6980c8e9f2 feat: add showLeafIcon 2022-01-16 10:06:35 +08:00
tangjinzhou 4e8a16f2bb refactor: tree-select 2022-01-15 22:44:47 +08:00
tangjinzhou 314461848c refactor: tree-select 2022-01-13 21:15:28 +08:00
tangjinzhou 6982052e74 Merge remote-tracking branch 'origin/next' into refactor-cascader 2022-01-13 10:13:43 +08:00
tangjinzhou 63c5df9e73 refactor: vc-tree-select 2022-01-12 21:08:12 +08:00
tangjinzhou bb91ce7592 refactor: select 2022-01-12 10:49:30 +08:00
tangjinzhou ca17b5a928 Merge remote-tracking branch 'origin/next' into refactor-cascader
# Conflicts:
#	components/select/demo/size.vue
2022-01-12 10:35:21 +08:00
tangjinzhou d027f286e3 refactor: select 2022-01-11 17:59:38 +08:00
tangjinzhou 1c508d61fb refactor: select 2022-01-09 22:29:13 +08:00
tangjinzhou 54cdc3ff40 feat: tree 2022-01-06 22:10:23 +08:00
151 changed files with 6901 additions and 6847 deletions

View File

@ -1,7 +1,7 @@
import type { FunctionalComponent } from 'vue';
import type { OptionGroupData } from '../vc-select/interface';
import type { DefaultOptionType } from '../select';
export type OptGroupProps = Omit<OptionGroupData, 'options'>;
export type OptGroupProps = Omit<DefaultOptionType, 'options'>;
export interface OptionGroupFC extends FunctionalComponent<OptGroupProps> {
/** Legacy for check if is a Option Group */

View File

@ -1,7 +1,7 @@
import type { FunctionalComponent } from 'vue';
import type { OptionCoreData } from '../vc-select/interface';
import type { DefaultOptionType } from '../vc-select/Select';
export interface OptionProps extends Omit<OptionCoreData, 'label'> {
export interface OptionProps extends Omit<DefaultOptionType, 'label'> {
/** Save for customize data */
[prop: string]: any; // eslint-disable-line @typescript-eslint/no-explicit-any
}

View File

@ -45,7 +45,7 @@ function YearSelect<DateType>(props: SharedProps<DateType>) {
options={options}
value={year}
class={`${prefixCls}-year-select`}
onChange={numYear => {
onChange={(numYear: number) => {
let newDate = generateConfig.setYear(value, numYear);
if (validRange) {
@ -108,7 +108,7 @@ function MonthSelect<DateType>(props: SharedProps<DateType>) {
class={`${prefixCls}-month-select`}
value={month}
options={options}
onChange={newMonth => {
onChange={(newMonth: number) => {
onChange(generateConfig.setMonth(value, newMonth));
}}
getPopupContainer={() => divRef!.value!}

View File

@ -1,62 +1,159 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`renders ./components/cascader/demo/basic.vue correctly 1`] = `
<span class="ant-cascader-picker" tabindex="0"><span class="ant-cascader-picker-label"></span><input placeholder="Please select" autocomplete="off" type="text" readonly="" class="ant-input ant-cascader-input">
<!----><span role="img" aria-label="down" class="anticon anticon-down ant-cascader-picker-arrow"><svg focusable="false" class="" data-icon="down" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M884 256h-75c-5.1 0-9.9 2.5-12.9 6.6L512 654.2 227.9 262.6c-3-4.1-7.8-6.6-12.9-6.6h-75c-6.5 0-10.3 7.4-6.5 12.7l352.6 486.1c12.8 17.6 39 17.6 51.7 0l352.6-486.1c3.9-5.3.1-12.7-6.4-12.7z"></path></svg></span></span>
<div class="ant-select ant-cascader ant-select-single ant-select-allow-clear ant-select-show-arrow">
<!---->
<!---->
<div class="ant-select-selector"><span class="ant-select-selection-search"><input id="rc_select_TEST_OR_SSR" autocomplete="off" class="ant-select-selection-search-input" style="opacity: 0;" role="combobox" aria-haspopup="listbox" aria-owns="rc_select_TEST_OR_SSR_list" aria-autocomplete="list" aria-controls="rc_select_TEST_OR_SSR_list" readonly="" unselectable="on" type="search"></span>
<!----><span class="ant-select-selection-placeholder">Please select</span>
</div><span class="ant-select-arrow" style="user-select: none;" unselectable="on" aria-hidden="true"><span role="img" aria-label="down" class="anticon anticon-down ant-select-suffix"><svg focusable="false" class="" data-icon="down" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M884 256h-75c-5.1 0-9.9 2.5-12.9 6.6L512 654.2 227.9 262.6c-3-4.1-7.8-6.6-12.9-6.6h-75c-6.5 0-10.3 7.4-6.5 12.7l352.6 486.1c12.8 17.6 39 17.6 51.7 0l352.6-486.1c3.9-5.3.1-12.7-6.4-12.7z"></path></svg></span></span>
<!---->
</div>
`;
exports[`renders ./components/cascader/demo/change-on-select.vue correctly 1`] = `
<span class="ant-cascader-picker" tabindex="0"><span class="ant-cascader-picker-label"></span><input placeholder="Please select" autocomplete="off" type="text" readonly="" class="ant-input ant-cascader-input">
<!----><span role="img" aria-label="down" class="anticon anticon-down ant-cascader-picker-arrow"><svg focusable="false" class="" data-icon="down" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M884 256h-75c-5.1 0-9.9 2.5-12.9 6.6L512 654.2 227.9 262.6c-3-4.1-7.8-6.6-12.9-6.6h-75c-6.5 0-10.3 7.4-6.5 12.7l352.6 486.1c12.8 17.6 39 17.6 51.7 0l352.6-486.1c3.9-5.3.1-12.7-6.4-12.7z"></path></svg></span></span>
<div class="ant-select ant-cascader ant-select-single ant-select-allow-clear ant-select-show-arrow">
<!---->
<!---->
<div class="ant-select-selector"><span class="ant-select-selection-search"><input id="rc_select_TEST_OR_SSR" autocomplete="off" class="ant-select-selection-search-input" style="opacity: 0;" role="combobox" aria-haspopup="listbox" aria-owns="rc_select_TEST_OR_SSR_list" aria-autocomplete="list" aria-controls="rc_select_TEST_OR_SSR_list" readonly="" unselectable="on" type="search"></span>
<!----><span class="ant-select-selection-placeholder">Please select</span>
</div><span class="ant-select-arrow" style="user-select: none;" unselectable="on" aria-hidden="true"><span role="img" aria-label="down" class="anticon anticon-down ant-select-suffix"><svg focusable="false" class="" data-icon="down" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M884 256h-75c-5.1 0-9.9 2.5-12.9 6.6L512 654.2 227.9 262.6c-3-4.1-7.8-6.6-12.9-6.6h-75c-6.5 0-10.3 7.4-6.5 12.7l352.6 486.1c12.8 17.6 39 17.6 51.7 0l352.6-486.1c3.9-5.3.1-12.7-6.4-12.7z"></path></svg></span></span>
<!---->
</div>
`;
exports[`renders ./components/cascader/demo/custom-render.vue correctly 1`] = `<span class="ant-cascader-picker" style="width: 100%;" tabindex="0"><span class="ant-cascader-picker-label"><span><span>Zhejiang /</span></span><span><span>Hangzhou /</span></span><span><span>West Lake ( <a>752100</a> ) </span></span></span><input autocomplete="off" type="text" readonly="" class="ant-input ant-cascader-input"><span tabindex="-1" role="img" aria-label="close-circle" class="anticon anticon-close-circle ant-cascader-picker-clear"><svg focusable="false" class="" data-icon="close-circle" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm165.4 618.2l-66-.3L512 563.4l-99.3 118.4-66.1.3c-4.4 0-8-3.5-8-8 0-1.9.7-3.7 1.9-5.2l130.1-155L340.5 359a8.32 8.32 0 01-1.9-5.2c0-4.4 3.6-8 8-8l66.1.3L512 464.6l99.3-118.4 66-.3c4.4 0 8 3.5 8 8 0 1.9-.7 3.7-1.9 5.2L553.5 514l130 155c1.2 1.5 1.9 3.3 1.9 5.2 0 4.4-3.6 8-8 8z"></path></svg></span><span role="img" aria-label="down" class="anticon anticon-down ant-cascader-picker-arrow"><svg focusable="false" class="" data-icon="down" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M884 256h-75c-5.1 0-9.9 2.5-12.9 6.6L512 654.2 227.9 262.6c-3-4.1-7.8-6.6-12.9-6.6h-75c-6.5 0-10.3 7.4-6.5 12.7l352.6 486.1c12.8 17.6 39 17.6 51.7 0l352.6-486.1c3.9-5.3.1-12.7-6.4-12.7z"></path></svg></span></span>`;
exports[`renders ./components/cascader/demo/custom-render.vue correctly 1`] = `
<div style="width: 100%;" class="ant-select ant-cascader ant-select-single ant-select-allow-clear ant-select-show-arrow">
<!---->
<!---->
<div class="ant-select-selector"><span class="ant-select-selection-search"><input id="rc_select_TEST_OR_SSR" autocomplete="off" class="ant-select-selection-search-input" style="opacity: 0;" role="combobox" aria-haspopup="listbox" aria-owns="rc_select_TEST_OR_SSR_list" aria-autocomplete="list" aria-controls="rc_select_TEST_OR_SSR_list" readonly="" unselectable="on" type="search"></span><span class="ant-select-selection-item"><span><span>Zhejiang /</span></span><span><span>Hangzhou /</span></span><span><span>West Lake ( <a>752100</a> ) </span></span></span>
<!---->
</div><span class="ant-select-arrow" style="user-select: none;" unselectable="on" aria-hidden="true"><span role="img" aria-label="down" class="anticon anticon-down ant-select-suffix"><svg focusable="false" class="" data-icon="down" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M884 256h-75c-5.1 0-9.9 2.5-12.9 6.6L512 654.2 227.9 262.6c-3-4.1-7.8-6.6-12.9-6.6h-75c-6.5 0-10.3 7.4-6.5 12.7l352.6 486.1c12.8 17.6 39 17.6 51.7 0l352.6-486.1c3.9-5.3.1-12.7-6.4-12.7z"></path></svg></span></span><span class="ant-select-clear" style="user-select: none;" unselectable="on" aria-hidden="true"><span role="img" aria-label="close-circle" class="anticon anticon-close-circle"><svg focusable="false" class="" data-icon="close-circle" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm165.4 618.2l-66-.3L512 563.4l-99.3 118.4-66.1.3c-4.4 0-8-3.5-8-8 0-1.9.7-3.7 1.9-5.2l130.1-155L340.5 359a8.32 8.32 0 01-1.9-5.2c0-4.4 3.6-8 8-8l66.1.3L512 464.6l99.3-118.4 66-.3c4.4 0 8 3.5 8 8 0 1.9-.7 3.7-1.9 5.2L553.5 514l130 155c1.2 1.5 1.9 3.3 1.9 5.2 0 4.4-3.6 8-8 8z"></path></svg></span></span>
</div>
`;
exports[`renders ./components/cascader/demo/custom-trigger.vue correctly 1`] = `<span>Unselect &nbsp; <!----><a href="#" tabindex="0">Change city</a></span>`;
exports[`renders ./components/cascader/demo/custom-trigger.vue correctly 1`] = `<span>Unselect &nbsp; <!----><a href="#">Change city</a></span>`;
exports[`renders ./components/cascader/demo/disabled-option.vue correctly 1`] = `
<span class="ant-cascader-picker" tabindex="0"><span class="ant-cascader-picker-label"></span><input placeholder="Please select" autocomplete="off" type="text" readonly="" class="ant-input ant-cascader-input">
<!----><span role="img" aria-label="down" class="anticon anticon-down ant-cascader-picker-arrow"><svg focusable="false" class="" data-icon="down" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M884 256h-75c-5.1 0-9.9 2.5-12.9 6.6L512 654.2 227.9 262.6c-3-4.1-7.8-6.6-12.9-6.6h-75c-6.5 0-10.3 7.4-6.5 12.7l352.6 486.1c12.8 17.6 39 17.6 51.7 0l352.6-486.1c3.9-5.3.1-12.7-6.4-12.7z"></path></svg></span></span>
<div class="ant-select ant-cascader ant-select-single ant-select-allow-clear ant-select-show-arrow">
<!---->
<!---->
<div class="ant-select-selector"><span class="ant-select-selection-search"><input id="rc_select_TEST_OR_SSR" autocomplete="off" class="ant-select-selection-search-input" style="opacity: 0;" role="combobox" aria-haspopup="listbox" aria-owns="rc_select_TEST_OR_SSR_list" aria-autocomplete="list" aria-controls="rc_select_TEST_OR_SSR_list" readonly="" unselectable="on" type="search"></span>
<!----><span class="ant-select-selection-placeholder">Please select</span>
</div><span class="ant-select-arrow" style="user-select: none;" unselectable="on" aria-hidden="true"><span role="img" aria-label="down" class="anticon anticon-down ant-select-suffix"><svg focusable="false" class="" data-icon="down" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M884 256h-75c-5.1 0-9.9 2.5-12.9 6.6L512 654.2 227.9 262.6c-3-4.1-7.8-6.6-12.9-6.6h-75c-6.5 0-10.3 7.4-6.5 12.7l352.6 486.1c12.8 17.6 39 17.6 51.7 0l352.6-486.1c3.9-5.3.1-12.7-6.4-12.7z"></path></svg></span></span>
<!---->
</div>
`;
exports[`renders ./components/cascader/demo/fields-name.vue correctly 1`] = `
<span class="ant-cascader-picker" tabindex="0"><span class="ant-cascader-picker-label"></span><input placeholder="Please select" autocomplete="off" type="text" readonly="" class="ant-input ant-cascader-input">
<!----><span role="img" aria-label="down" class="anticon anticon-down ant-cascader-picker-arrow"><svg focusable="false" class="" data-icon="down" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M884 256h-75c-5.1 0-9.9 2.5-12.9 6.6L512 654.2 227.9 262.6c-3-4.1-7.8-6.6-12.9-6.6h-75c-6.5 0-10.3 7.4-6.5 12.7l352.6 486.1c12.8 17.6 39 17.6 51.7 0l352.6-486.1c3.9-5.3.1-12.7-6.4-12.7z"></path></svg></span></span>
<div class="ant-select ant-cascader ant-select-single ant-select-allow-clear ant-select-show-arrow">
<!---->
<!---->
<div class="ant-select-selector"><span class="ant-select-selection-search"><input id="rc_select_TEST_OR_SSR" autocomplete="off" class="ant-select-selection-search-input" style="opacity: 0;" role="combobox" aria-haspopup="listbox" aria-owns="rc_select_TEST_OR_SSR_list" aria-autocomplete="list" aria-controls="rc_select_TEST_OR_SSR_list" readonly="" unselectable="on" type="search"></span>
<!----><span class="ant-select-selection-placeholder">Please select</span>
</div><span class="ant-select-arrow" style="user-select: none;" unselectable="on" aria-hidden="true"><span role="img" aria-label="down" class="anticon anticon-down ant-select-suffix"><svg focusable="false" class="" data-icon="down" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M884 256h-75c-5.1 0-9.9 2.5-12.9 6.6L512 654.2 227.9 262.6c-3-4.1-7.8-6.6-12.9-6.6h-75c-6.5 0-10.3 7.4-6.5 12.7l352.6 486.1c12.8 17.6 39 17.6 51.7 0l352.6-486.1c3.9-5.3.1-12.7-6.4-12.7z"></path></svg></span></span>
<!---->
</div>
`;
exports[`renders ./components/cascader/demo/hover.vue correctly 1`] = `
<span class="ant-cascader-picker" tabindex="0"><span class="ant-cascader-picker-label"><!----></span><input placeholder="Please select" autocomplete="off" type="text" readonly="" class="ant-input ant-cascader-input">
<!----><span role="img" aria-label="down" class="anticon anticon-down ant-cascader-picker-arrow"><svg focusable="false" class="" data-icon="down" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M884 256h-75c-5.1 0-9.9 2.5-12.9 6.6L512 654.2 227.9 262.6c-3-4.1-7.8-6.6-12.9-6.6h-75c-6.5 0-10.3 7.4-6.5 12.7l352.6 486.1c12.8 17.6 39 17.6 51.7 0l352.6-486.1c3.9-5.3.1-12.7-6.4-12.7z"></path></svg></span></span>
<div class="ant-select ant-cascader ant-select-single ant-select-allow-clear ant-select-show-arrow">
<!---->
<!---->
<div class="ant-select-selector"><span class="ant-select-selection-search"><input id="rc_select_TEST_OR_SSR" autocomplete="off" class="ant-select-selection-search-input" style="opacity: 0;" role="combobox" aria-haspopup="listbox" aria-owns="rc_select_TEST_OR_SSR_list" aria-autocomplete="list" aria-controls="rc_select_TEST_OR_SSR_list" readonly="" unselectable="on" type="search"></span>
<!----><span class="ant-select-selection-placeholder">Please select</span>
</div><span class="ant-select-arrow" style="user-select: none;" unselectable="on" aria-hidden="true"><span role="img" aria-label="down" class="anticon anticon-down ant-select-suffix"><svg focusable="false" class="" data-icon="down" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M884 256h-75c-5.1 0-9.9 2.5-12.9 6.6L512 654.2 227.9 262.6c-3-4.1-7.8-6.6-12.9-6.6h-75c-6.5 0-10.3 7.4-6.5 12.7l352.6 486.1c12.8 17.6 39 17.6 51.7 0l352.6-486.1c3.9-5.3.1-12.7-6.4-12.7z"></path></svg></span></span>
<!---->
</div>
`;
exports[`renders ./components/cascader/demo/lazy.vue correctly 1`] = `
<span class="ant-cascader-picker" tabindex="0"><span class="ant-cascader-picker-label"></span><input placeholder="Please select" autocomplete="off" type="text" readonly="" class="ant-input ant-cascader-input">
<!----><span role="img" aria-label="down" class="anticon anticon-down ant-cascader-picker-arrow"><svg focusable="false" class="" data-icon="down" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M884 256h-75c-5.1 0-9.9 2.5-12.9 6.6L512 654.2 227.9 262.6c-3-4.1-7.8-6.6-12.9-6.6h-75c-6.5 0-10.3 7.4-6.5 12.7l352.6 486.1c12.8 17.6 39 17.6 51.7 0l352.6-486.1c3.9-5.3.1-12.7-6.4-12.7z"></path></svg></span></span>
<div class="ant-select ant-cascader ant-select-single ant-select-allow-clear ant-select-show-arrow">
<!---->
<!---->
<div class="ant-select-selector"><span class="ant-select-selection-search"><input id="rc_select_TEST_OR_SSR" autocomplete="off" class="ant-select-selection-search-input" style="opacity: 0;" role="combobox" aria-haspopup="listbox" aria-owns="rc_select_TEST_OR_SSR_list" aria-autocomplete="list" aria-controls="rc_select_TEST_OR_SSR_list" readonly="" unselectable="on" type="search"></span>
<!----><span class="ant-select-selection-placeholder">Please select</span>
</div><span class="ant-select-arrow" style="user-select: none;" unselectable="on" aria-hidden="true"><span role="img" aria-label="down" class="anticon anticon-down ant-select-suffix"><svg focusable="false" class="" data-icon="down" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M884 256h-75c-5.1 0-9.9 2.5-12.9 6.6L512 654.2 227.9 262.6c-3-4.1-7.8-6.6-12.9-6.6h-75c-6.5 0-10.3 7.4-6.5 12.7l352.6 486.1c12.8 17.6 39 17.6 51.7 0l352.6-486.1c3.9-5.3.1-12.7-6.4-12.7z"></path></svg></span></span>
<!---->
</div>
`;
exports[`renders ./components/cascader/demo/multiple.vue correctly 1`] = `
<div style="width: 233px;" class="ant-select ant-cascader ant-select-multiple ant-select-allow-clear">
<!---->
<!---->
<div class="ant-select-selector">
<div class="ant-select-selection-overflow">
<!---->
<div class="ant-select-selection-overflow-item ant-select-selection-overflow-item-suffix" style="opacity: 1;">
<div class="ant-select-selection-search" style="width: 0px;"><input id="rc_select_TEST_OR_SSR" autocomplete="off" class="ant-select-selection-search-input" style="opacity: 0;" role="combobox" aria-haspopup="listbox" aria-owns="rc_select_TEST_OR_SSR_list" aria-autocomplete="list" aria-controls="rc_select_TEST_OR_SSR_list" readonly="" unselectable="on" type="search"><span class="ant-select-selection-search-mirror" aria-hidden="true">&nbsp;</span></div>
</div>
</div><span class="ant-select-selection-placeholder">Please select</span>
</div>
<!---->
<!---->
</div>
`;
exports[`renders ./components/cascader/demo/search.vue correctly 1`] = `
<span class="ant-cascader-picker ant-cascader-picker-show-search" tabindex="0"><span class="ant-cascader-picker-label"></span><input placeholder="Please select" autocomplete="off" type="text" class="ant-input ant-cascader-input">
<!----><span role="img" aria-label="down" class="anticon anticon-down ant-cascader-picker-arrow"><svg focusable="false" class="" data-icon="down" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M884 256h-75c-5.1 0-9.9 2.5-12.9 6.6L512 654.2 227.9 262.6c-3-4.1-7.8-6.6-12.9-6.6h-75c-6.5 0-10.3 7.4-6.5 12.7l352.6 486.1c12.8 17.6 39 17.6 51.7 0l352.6-486.1c3.9-5.3.1-12.7-6.4-12.7z"></path></svg></span></span>
<div class="ant-select ant-cascader ant-select-single ant-select-allow-clear ant-select-show-arrow ant-select-show-search">
<!---->
<!---->
<div class="ant-select-selector"><span class="ant-select-selection-search"><input id="rc_select_TEST_OR_SSR" autocomplete="off" class="ant-select-selection-search-input" role="combobox" aria-haspopup="listbox" aria-owns="rc_select_TEST_OR_SSR_list" aria-autocomplete="list" aria-controls="rc_select_TEST_OR_SSR_list" type="search"></span>
<!----><span class="ant-select-selection-placeholder">Please select</span>
</div><span class="ant-select-arrow" style="user-select: none;" unselectable="on" aria-hidden="true"><span role="img" aria-label="down" class="anticon anticon-down ant-select-suffix"><svg focusable="false" class="" data-icon="down" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M884 256h-75c-5.1 0-9.9 2.5-12.9 6.6L512 654.2 227.9 262.6c-3-4.1-7.8-6.6-12.9-6.6h-75c-6.5 0-10.3 7.4-6.5 12.7l352.6 486.1c12.8 17.6 39 17.6 51.7 0l352.6-486.1c3.9-5.3.1-12.7-6.4-12.7z"></path></svg></span></span>
<!---->
</div>
`;
exports[`renders ./components/cascader/demo/size.vue correctly 1`] = `
<span class="ant-cascader-picker ant-cascader-picker-large" tabindex="0"><span class="ant-cascader-picker-label"></span><input placeholder="Please select" autocomplete="off" type="text" readonly="" class="ant-input ant-cascader-input ant-input-lg">
<!----><span role="img" aria-label="down" class="anticon anticon-down ant-cascader-picker-arrow"><svg focusable="false" class="" data-icon="down" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M884 256h-75c-5.1 0-9.9 2.5-12.9 6.6L512 654.2 227.9 262.6c-3-4.1-7.8-6.6-12.9-6.6h-75c-6.5 0-10.3 7.4-6.5 12.7l352.6 486.1c12.8 17.6 39 17.6 51.7 0l352.6-486.1c3.9-5.3.1-12.7-6.4-12.7z"></path></svg></span></span>
<div size="large" class="ant-select ant-cascader ant-select-lg ant-select-single ant-select-allow-clear ant-select-show-arrow">
<!---->
<!---->
<div class="ant-select-selector"><span class="ant-select-selection-search"><input id="rc_select_TEST_OR_SSR" autocomplete="off" class="ant-select-selection-search-input" style="opacity: 0;" role="combobox" aria-haspopup="listbox" aria-owns="rc_select_TEST_OR_SSR_list" aria-autocomplete="list" aria-controls="rc_select_TEST_OR_SSR_list" readonly="" unselectable="on" type="search"></span>
<!----><span class="ant-select-selection-placeholder">Please select</span>
</div><span class="ant-select-arrow" style="user-select: none;" unselectable="on" aria-hidden="true"><span role="img" aria-label="down" class="anticon anticon-down ant-select-suffix"><svg focusable="false" class="" data-icon="down" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M884 256h-75c-5.1 0-9.9 2.5-12.9 6.6L512 654.2 227.9 262.6c-3-4.1-7.8-6.6-12.9-6.6h-75c-6.5 0-10.3 7.4-6.5 12.7l352.6 486.1c12.8 17.6 39 17.6 51.7 0l352.6-486.1c3.9-5.3.1-12.7-6.4-12.7z"></path></svg></span></span>
<!---->
</div>
<br>
<br>
<span class="ant-cascader-picker" tabindex="0"><span class="ant-cascader-picker-label"></span><input placeholder="Please select" autocomplete="off" type="text" readonly="" class="ant-input ant-cascader-input">
<!----><span role="img" aria-label="down" class="anticon anticon-down ant-cascader-picker-arrow"><svg focusable="false" class="" data-icon="down" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M884 256h-75c-5.1 0-9.9 2.5-12.9 6.6L512 654.2 227.9 262.6c-3-4.1-7.8-6.6-12.9-6.6h-75c-6.5 0-10.3 7.4-6.5 12.7l352.6 486.1c12.8 17.6 39 17.6 51.7 0l352.6-486.1c3.9-5.3.1-12.7-6.4-12.7z"></path></svg></span></span>
<div class="ant-select ant-cascader ant-select-single ant-select-allow-clear ant-select-show-arrow">
<!---->
<!---->
<div class="ant-select-selector"><span class="ant-select-selection-search"><input id="rc_select_TEST_OR_SSR" autocomplete="off" class="ant-select-selection-search-input" style="opacity: 0;" role="combobox" aria-haspopup="listbox" aria-owns="rc_select_TEST_OR_SSR_list" aria-autocomplete="list" aria-controls="rc_select_TEST_OR_SSR_list" readonly="" unselectable="on" type="search"></span>
<!----><span class="ant-select-selection-placeholder">Please select</span>
</div><span class="ant-select-arrow" style="user-select: none;" unselectable="on" aria-hidden="true"><span role="img" aria-label="down" class="anticon anticon-down ant-select-suffix"><svg focusable="false" class="" data-icon="down" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M884 256h-75c-5.1 0-9.9 2.5-12.9 6.6L512 654.2 227.9 262.6c-3-4.1-7.8-6.6-12.9-6.6h-75c-6.5 0-10.3 7.4-6.5 12.7l352.6 486.1c12.8 17.6 39 17.6 51.7 0l352.6-486.1c3.9-5.3.1-12.7-6.4-12.7z"></path></svg></span></span>
<!---->
</div>
<br>
<br>
<span class="ant-cascader-picker ant-cascader-picker-small" tabindex="0"><span class="ant-cascader-picker-label"></span><input placeholder="Please select" autocomplete="off" type="text" readonly="" class="ant-input ant-cascader-input ant-input-sm">
<!----><span role="img" aria-label="down" class="anticon anticon-down ant-cascader-picker-arrow"><svg focusable="false" class="" data-icon="down" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M884 256h-75c-5.1 0-9.9 2.5-12.9 6.6L512 654.2 227.9 262.6c-3-4.1-7.8-6.6-12.9-6.6h-75c-6.5 0-10.3 7.4-6.5 12.7l352.6 486.1c12.8 17.6 39 17.6 51.7 0l352.6-486.1c3.9-5.3.1-12.7-6.4-12.7z"></path></svg></span></span>
<div size="small" class="ant-select ant-cascader ant-select-sm ant-select-single ant-select-allow-clear ant-select-show-arrow">
<!---->
<!---->
<div class="ant-select-selector"><span class="ant-select-selection-search"><input id="rc_select_TEST_OR_SSR" autocomplete="off" class="ant-select-selection-search-input" style="opacity: 0;" role="combobox" aria-haspopup="listbox" aria-owns="rc_select_TEST_OR_SSR_list" aria-autocomplete="list" aria-controls="rc_select_TEST_OR_SSR_list" readonly="" unselectable="on" type="search"></span>
<!----><span class="ant-select-selection-placeholder">Please select</span>
</div><span class="ant-select-arrow" style="user-select: none;" unselectable="on" aria-hidden="true"><span role="img" aria-label="down" class="anticon anticon-down ant-select-suffix"><svg focusable="false" class="" data-icon="down" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M884 256h-75c-5.1 0-9.9 2.5-12.9 6.6L512 654.2 227.9 262.6c-3-4.1-7.8-6.6-12.9-6.6h-75c-6.5 0-10.3 7.4-6.5 12.7l352.6 486.1c12.8 17.6 39 17.6 51.7 0l352.6-486.1c3.9-5.3.1-12.7-6.4-12.7z"></path></svg></span></span>
<!---->
</div>
<br>
<br>
`;
exports[`renders ./components/cascader/demo/suffix.vue correctly 1`] = `
<span class="ant-cascader-picker" style="margin-top: 1rem;" tabindex="0"><span class="ant-cascader-picker-label"></span><input placeholder="Please select" autocomplete="off" type="text" readonly="" class="ant-input ant-cascader-input">
<!----><span role="img" aria-label="smile" class="anticon anticon-smile ant-cascader-picker-arrow"><svg focusable="false" class="" data-icon="smile" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M288 421a48 48 0 1096 0 48 48 0 10-96 0zm352 0a48 48 0 1096 0 48 48 0 10-96 0zM512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm263 711c-34.2 34.2-74 61-118.3 79.8C611 874.2 562.3 884 512 884c-50.3 0-99-9.8-144.8-29.2A370.4 370.4 0 01248.9 775c-34.2-34.2-61-74-79.8-118.3C149.8 611 140 562.3 140 512s9.8-99 29.2-144.8A370.4 370.4 0 01249 248.9c34.2-34.2 74-61 118.3-79.8C413 149.8 461.7 140 512 140c50.3 0 99 9.8 144.8 29.2A370.4 370.4 0 01775.1 249c34.2 34.2 61 74 79.8 118.3C874.2 413 884 461.7 884 512s-9.8 99-29.2 144.8A368.89 368.89 0 01775 775zM664 533h-48.1c-4.2 0-7.8 3.2-8.1 7.4C604 589.9 562.5 629 512 629s-92.1-39.1-95.8-88.6c-.3-4.2-3.9-7.4-8.1-7.4H360a8 8 0 00-8 8.4c4.4 84.3 74.5 151.6 160 151.6s155.6-67.3 160-151.6a8 8 0 00-8-8.4z"></path></svg></span></span>
<span class="ant-cascader-picker" style="margin-top: 1rem;" tabindex="0"><span class="ant-cascader-picker-label"></span><input placeholder="Please select" autocomplete="off" type="text" readonly="" suffixicon="ab" class="ant-input ant-cascader-input">
<!----><span class="ant-cascader-picker-arrow">ab</span></span>
<div style="margin-top: 1rem;" class="ant-select ant-cascader ant-select-single ant-select-allow-clear ant-select-show-arrow">
<!---->
<!---->
<div class="ant-select-selector"><span class="ant-select-selection-search"><input id="rc_select_TEST_OR_SSR" autocomplete="off" class="ant-select-selection-search-input" style="opacity: 0;" role="combobox" aria-haspopup="listbox" aria-owns="rc_select_TEST_OR_SSR_list" aria-autocomplete="list" aria-controls="rc_select_TEST_OR_SSR_list" readonly="" unselectable="on" type="search"></span>
<!----><span class="ant-select-selection-placeholder">Please select</span>
</div><span class="ant-select-arrow" style="user-select: none;" unselectable="on" aria-hidden="true"><span role="img" aria-label="smile" class="anticon anticon-smile test"><svg focusable="false" class="" data-icon="smile" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M288 421a48 48 0 1096 0 48 48 0 10-96 0zm352 0a48 48 0 1096 0 48 48 0 10-96 0zM512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm263 711c-34.2 34.2-74 61-118.3 79.8C611 874.2 562.3 884 512 884c-50.3 0-99-9.8-144.8-29.2A370.4 370.4 0 01248.9 775c-34.2-34.2-61-74-79.8-118.3C149.8 611 140 562.3 140 512s9.8-99 29.2-144.8A370.4 370.4 0 01249 248.9c34.2-34.2 74-61 118.3-79.8C413 149.8 461.7 140 512 140c50.3 0 99 9.8 144.8 29.2A370.4 370.4 0 01775.1 249c34.2 34.2 61 74 79.8 118.3C874.2 413 884 461.7 884 512s-9.8 99-29.2 144.8A368.89 368.89 0 01775 775zM664 533h-48.1c-4.2 0-7.8 3.2-8.1 7.4C604 589.9 562.5 629 512 629s-92.1-39.1-95.8-88.6c-.3-4.2-3.9-7.4-8.1-7.4H360a8 8 0 00-8 8.4c4.4 84.3 74.5 151.6 160 151.6s155.6-67.3 160-151.6a8 8 0 00-8-8.4z"></path></svg></span></span>
<!---->
</div>
<div suffixicon="ab" style="margin-top: 1rem;" class="ant-select ant-cascader ant-select-single ant-select-allow-clear ant-select-show-arrow">
<!---->
<!---->
<div class="ant-select-selector"><span class="ant-select-selection-search"><input id="rc_select_TEST_OR_SSR" autocomplete="off" class="ant-select-selection-search-input" style="opacity: 0;" role="combobox" aria-haspopup="listbox" aria-owns="rc_select_TEST_OR_SSR_list" aria-autocomplete="list" aria-controls="rc_select_TEST_OR_SSR_list" readonly="" unselectable="on" type="search"></span>
<!----><span class="ant-select-selection-placeholder">Please select</span>
</div><span class="ant-select-arrow" style="user-select: none;" unselectable="on" aria-hidden="true">ab</span>
<!---->
</div>
`;

View File

@ -1,116 +1,160 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`Cascader can be selected 1`] = `
<div>
<ul class="ant-cascader-menu">
<li class="ant-cascader-menu-item ant-cascader-menu-item-expand ant-cascader-menu-item-active" title="Zhejiang" role="menuitem">Zhejiang<span class="ant-cascader-menu-item-expand-icon"><span role="img" aria-label="right" class="anticon anticon-right"><svg focusable="false" class="" data-icon="right" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M765.7 486.8L314.9 134.7A7.97 7.97 0 00302 141v77.3c0 4.9 2.3 9.6 6.1 12.6l360 281.1-360 281.1c-3.9 3-6.1 7.7-6.1 12.6V883c0 6.7 7.7 10.4 12.9 6.3l450.8-352.1a31.96 31.96 0 000-50.4z"></path></svg></span></span>
<!---->
</li>
<li class="ant-cascader-menu-item ant-cascader-menu-item-expand" title="Jiangsu" role="menuitem">Jiangsu<span class="ant-cascader-menu-item-expand-icon"><span role="img" aria-label="right" class="anticon anticon-right"><svg focusable="false" class="" data-icon="right" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M765.7 486.8L314.9 134.7A7.97 7.97 0 00302 141v77.3c0 4.9 2.3 9.6 6.1 12.6l360 281.1-360 281.1c-3.9 3-6.1 7.7-6.1 12.6V883c0 6.7 7.7 10.4 12.9 6.3l450.8-352.1a31.96 31.96 0 000-50.4z"></path></svg></span></span>
<!---->
</li>
</ul>
<ul class="ant-cascader-menu">
<li class="ant-cascader-menu-item ant-cascader-menu-item-expand" title="Hangzhou" role="menuitem">Hangzhou<span class="ant-cascader-menu-item-expand-icon"><span role="img" aria-label="right" class="anticon anticon-right"><svg focusable="false" class="" data-icon="right" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M765.7 486.8L314.9 134.7A7.97 7.97 0 00302 141v77.3c0 4.9 2.3 9.6 6.1 12.6l360 281.1-360 281.1c-3.9 3-6.1 7.7-6.1 12.6V883c0 6.7 7.7 10.4 12.9 6.3l450.8-352.1a31.96 31.96 0 000-50.4z"></path></svg></span></span>
<!---->
</li>
</ul>
</div>
<ul class="ant-cascader-menu" role="menu">
<li class="ant-cascader-menu-item ant-cascader-menu-item-expand ant-cascader-menu-item-active" role="menuitemcheckbox" title="Zhejiang" aria-checked="false" data-path-key="zhejiang">
<!---->
<div class="ant-cascader-menu-item-content">Zhejiang</div>
<div class="ant-cascader-menu-item-expand-icon"><span role="img" aria-label="right" class="anticon anticon-right"><svg focusable="false" class="" data-icon="right" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M765.7 486.8L314.9 134.7A7.97 7.97 0 00302 141v77.3c0 4.9 2.3 9.6 6.1 12.6l360 281.1-360 281.1c-3.9 3-6.1 7.7-6.1 12.6V883c0 6.7 7.7 10.4 12.9 6.3l450.8-352.1a31.96 31.96 0 000-50.4z"></path></svg></span></div>
<!---->
</li>
<li class="ant-cascader-menu-item ant-cascader-menu-item-expand" role="menuitemcheckbox" title="Jiangsu" aria-checked="false" data-path-key="jiangsu">
<!---->
<div class="ant-cascader-menu-item-content">Jiangsu</div>
<div class="ant-cascader-menu-item-expand-icon"><span role="img" aria-label="right" class="anticon anticon-right"><svg focusable="false" class="" data-icon="right" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M765.7 486.8L314.9 134.7A7.97 7.97 0 00302 141v77.3c0 4.9 2.3 9.6 6.1 12.6l360 281.1-360 281.1c-3.9 3-6.1 7.7-6.1 12.6V883c0 6.7 7.7 10.4 12.9 6.3l450.8-352.1a31.96 31.96 0 000-50.4z"></path></svg></span></div>
<!---->
</li>
</ul>
<ul class="ant-cascader-menu" role="menu">
<li class="ant-cascader-menu-item ant-cascader-menu-item-expand" role="menuitemcheckbox" title="Hangzhou" aria-checked="false" data-path-key="zhejiang__RC_CASCADER_SPLIT__hangzhou">
<!---->
<div class="ant-cascader-menu-item-content">Hangzhou</div>
<div class="ant-cascader-menu-item-expand-icon"><span role="img" aria-label="right" class="anticon anticon-right"><svg focusable="false" class="" data-icon="right" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M765.7 486.8L314.9 134.7A7.97 7.97 0 00302 141v77.3c0 4.9 2.3 9.6 6.1 12.6l360 281.1-360 281.1c-3.9 3-6.1 7.7-6.1 12.6V883c0 6.7 7.7 10.4 12.9 6.3l450.8-352.1a31.96 31.96 0 000-50.4z"></path></svg></span></div>
<!---->
</li>
</ul>
`;
exports[`Cascader can be selected 2`] = `
<div>
<ul class="ant-cascader-menu">
<li class="ant-cascader-menu-item ant-cascader-menu-item-expand ant-cascader-menu-item-active" title="Zhejiang" role="menuitem">Zhejiang<span class="ant-cascader-menu-item-expand-icon"><span role="img" aria-label="right" class="anticon anticon-right"><svg focusable="false" class="" data-icon="right" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M765.7 486.8L314.9 134.7A7.97 7.97 0 00302 141v77.3c0 4.9 2.3 9.6 6.1 12.6l360 281.1-360 281.1c-3.9 3-6.1 7.7-6.1 12.6V883c0 6.7 7.7 10.4 12.9 6.3l450.8-352.1a31.96 31.96 0 000-50.4z"></path></svg></span></span>
<!---->
</li>
<li class="ant-cascader-menu-item ant-cascader-menu-item-expand" title="Jiangsu" role="menuitem">Jiangsu<span class="ant-cascader-menu-item-expand-icon"><span role="img" aria-label="right" class="anticon anticon-right"><svg focusable="false" class="" data-icon="right" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M765.7 486.8L314.9 134.7A7.97 7.97 0 00302 141v77.3c0 4.9 2.3 9.6 6.1 12.6l360 281.1-360 281.1c-3.9 3-6.1 7.7-6.1 12.6V883c0 6.7 7.7 10.4 12.9 6.3l450.8-352.1a31.96 31.96 0 000-50.4z"></path></svg></span></span>
<!---->
</li>
</ul>
<ul class="ant-cascader-menu">
<li class="ant-cascader-menu-item ant-cascader-menu-item-expand ant-cascader-menu-item-active" title="Hangzhou" role="menuitem">Hangzhou<span class="ant-cascader-menu-item-expand-icon"><span role="img" aria-label="right" class="anticon anticon-right"><svg focusable="false" class="" data-icon="right" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M765.7 486.8L314.9 134.7A7.97 7.97 0 00302 141v77.3c0 4.9 2.3 9.6 6.1 12.6l360 281.1-360 281.1c-3.9 3-6.1 7.7-6.1 12.6V883c0 6.7 7.7 10.4 12.9 6.3l450.8-352.1a31.96 31.96 0 000-50.4z"></path></svg></span></span>
<!---->
</li>
</ul>
<ul class="ant-cascader-menu">
<li class="ant-cascader-menu-item" title="West Lake" role="menuitem">West Lake
<!---->
<!---->
</li>
</ul>
</div>
<ul class="ant-cascader-menu" role="menu">
<li class="ant-cascader-menu-item ant-cascader-menu-item-expand ant-cascader-menu-item-active" role="menuitemcheckbox" title="Zhejiang" aria-checked="false" data-path-key="zhejiang">
<!---->
<div class="ant-cascader-menu-item-content">Zhejiang</div>
<div class="ant-cascader-menu-item-expand-icon"><span role="img" aria-label="right" class="anticon anticon-right"><svg focusable="false" class="" data-icon="right" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M765.7 486.8L314.9 134.7A7.97 7.97 0 00302 141v77.3c0 4.9 2.3 9.6 6.1 12.6l360 281.1-360 281.1c-3.9 3-6.1 7.7-6.1 12.6V883c0 6.7 7.7 10.4 12.9 6.3l450.8-352.1a31.96 31.96 0 000-50.4z"></path></svg></span></div>
<!---->
</li>
<li class="ant-cascader-menu-item ant-cascader-menu-item-expand" role="menuitemcheckbox" title="Jiangsu" aria-checked="false" data-path-key="jiangsu">
<!---->
<div class="ant-cascader-menu-item-content">Jiangsu</div>
<div class="ant-cascader-menu-item-expand-icon"><span role="img" aria-label="right" class="anticon anticon-right"><svg focusable="false" class="" data-icon="right" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M765.7 486.8L314.9 134.7A7.97 7.97 0 00302 141v77.3c0 4.9 2.3 9.6 6.1 12.6l360 281.1-360 281.1c-3.9 3-6.1 7.7-6.1 12.6V883c0 6.7 7.7 10.4 12.9 6.3l450.8-352.1a31.96 31.96 0 000-50.4z"></path></svg></span></div>
<!---->
</li>
</ul>
<ul class="ant-cascader-menu" role="menu">
<li class="ant-cascader-menu-item ant-cascader-menu-item-expand ant-cascader-menu-item-active" role="menuitemcheckbox" title="Hangzhou" aria-checked="false" data-path-key="zhejiang__RC_CASCADER_SPLIT__hangzhou">
<!---->
<div class="ant-cascader-menu-item-content">Hangzhou</div>
<div class="ant-cascader-menu-item-expand-icon"><span role="img" aria-label="right" class="anticon anticon-right"><svg focusable="false" class="" data-icon="right" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M765.7 486.8L314.9 134.7A7.97 7.97 0 00302 141v77.3c0 4.9 2.3 9.6 6.1 12.6l360 281.1-360 281.1c-3.9 3-6.1 7.7-6.1 12.6V883c0 6.7 7.7 10.4 12.9 6.3l450.8-352.1a31.96 31.96 0 000-50.4z"></path></svg></span></div>
<!---->
</li>
</ul>
<ul class="ant-cascader-menu" role="menu">
<li class="ant-cascader-menu-item" role="menuitemcheckbox" title="West Lake" aria-checked="false" data-path-key="zhejiang__RC_CASCADER_SPLIT__hangzhou__RC_CASCADER_SPLIT__xihu">
<!---->
<div class="ant-cascader-menu-item-content">West Lake</div>
<!---->
<!---->
</li>
</ul>
`;
exports[`Cascader can be selected 3`] = `
<div>
<ul class="ant-cascader-menu">
<li class="ant-cascader-menu-item ant-cascader-menu-item-expand ant-cascader-menu-item-active" title="Zhejiang" role="menuitem">Zhejiang<span class="ant-cascader-menu-item-expand-icon"><span role="img" aria-label="right" class="anticon anticon-right"><svg focusable="false" class="" data-icon="right" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M765.7 486.8L314.9 134.7A7.97 7.97 0 00302 141v77.3c0 4.9 2.3 9.6 6.1 12.6l360 281.1-360 281.1c-3.9 3-6.1 7.7-6.1 12.6V883c0 6.7 7.7 10.4 12.9 6.3l450.8-352.1a31.96 31.96 0 000-50.4z"></path></svg></span></span>
<!---->
</li>
<li class="ant-cascader-menu-item ant-cascader-menu-item-expand" title="Jiangsu" role="menuitem">Jiangsu<span class="ant-cascader-menu-item-expand-icon"><span role="img" aria-label="right" class="anticon anticon-right"><svg focusable="false" class="" data-icon="right" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M765.7 486.8L314.9 134.7A7.97 7.97 0 00302 141v77.3c0 4.9 2.3 9.6 6.1 12.6l360 281.1-360 281.1c-3.9 3-6.1 7.7-6.1 12.6V883c0 6.7 7.7 10.4 12.9 6.3l450.8-352.1a31.96 31.96 0 000-50.4z"></path></svg></span></span>
<!---->
</li>
</ul>
<ul class="ant-cascader-menu">
<li class="ant-cascader-menu-item ant-cascader-menu-item-expand ant-cascader-menu-item-active" title="Hangzhou" role="menuitem">Hangzhou<span class="ant-cascader-menu-item-expand-icon"><span role="img" aria-label="right" class="anticon anticon-right"><svg focusable="false" class="" data-icon="right" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M765.7 486.8L314.9 134.7A7.97 7.97 0 00302 141v77.3c0 4.9 2.3 9.6 6.1 12.6l360 281.1-360 281.1c-3.9 3-6.1 7.7-6.1 12.6V883c0 6.7 7.7 10.4 12.9 6.3l450.8-352.1a31.96 31.96 0 000-50.4z"></path></svg></span></span>
<!---->
</li>
</ul>
<ul class="ant-cascader-menu">
<li class="ant-cascader-menu-item ant-cascader-menu-item-active" title="West Lake" role="menuitem">West Lake
<!---->
<!---->
</li>
</ul>
</div>
<ul class="ant-cascader-menu" role="menu">
<li class="ant-cascader-menu-item ant-cascader-menu-item-expand ant-cascader-menu-item-active" role="menuitemcheckbox" title="Zhejiang" aria-checked="true" data-path-key="zhejiang">
<!---->
<div class="ant-cascader-menu-item-content">Zhejiang</div>
<div class="ant-cascader-menu-item-expand-icon"><span role="img" aria-label="right" class="anticon anticon-right"><svg focusable="false" class="" data-icon="right" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M765.7 486.8L314.9 134.7A7.97 7.97 0 00302 141v77.3c0 4.9 2.3 9.6 6.1 12.6l360 281.1-360 281.1c-3.9 3-6.1 7.7-6.1 12.6V883c0 6.7 7.7 10.4 12.9 6.3l450.8-352.1a31.96 31.96 0 000-50.4z"></path></svg></span></div>
<!---->
</li>
<li class="ant-cascader-menu-item ant-cascader-menu-item-expand" role="menuitemcheckbox" title="Jiangsu" aria-checked="false" data-path-key="jiangsu">
<!---->
<div class="ant-cascader-menu-item-content">Jiangsu</div>
<div class="ant-cascader-menu-item-expand-icon"><span role="img" aria-label="right" class="anticon anticon-right"><svg focusable="false" class="" data-icon="right" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M765.7 486.8L314.9 134.7A7.97 7.97 0 00302 141v77.3c0 4.9 2.3 9.6 6.1 12.6l360 281.1-360 281.1c-3.9 3-6.1 7.7-6.1 12.6V883c0 6.7 7.7 10.4 12.9 6.3l450.8-352.1a31.96 31.96 0 000-50.4z"></path></svg></span></div>
<!---->
</li>
</ul>
<ul class="ant-cascader-menu" role="menu">
<li class="ant-cascader-menu-item ant-cascader-menu-item-expand ant-cascader-menu-item-active" role="menuitemcheckbox" title="Hangzhou" aria-checked="true" data-path-key="zhejiang__RC_CASCADER_SPLIT__hangzhou">
<!---->
<div class="ant-cascader-menu-item-content">Hangzhou</div>
<div class="ant-cascader-menu-item-expand-icon"><span role="img" aria-label="right" class="anticon anticon-right"><svg focusable="false" class="" data-icon="right" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M765.7 486.8L314.9 134.7A7.97 7.97 0 00302 141v77.3c0 4.9 2.3 9.6 6.1 12.6l360 281.1-360 281.1c-3.9 3-6.1 7.7-6.1 12.6V883c0 6.7 7.7 10.4 12.9 6.3l450.8-352.1a31.96 31.96 0 000-50.4z"></path></svg></span></div>
<!---->
</li>
</ul>
<ul class="ant-cascader-menu" role="menu">
<li class="ant-cascader-menu-item ant-cascader-menu-item-active" role="menuitemcheckbox" title="West Lake" aria-checked="true" data-path-key="zhejiang__RC_CASCADER_SPLIT__hangzhou__RC_CASCADER_SPLIT__xihu">
<!---->
<div class="ant-cascader-menu-item-content">West Lake</div>
<!---->
<!---->
</li>
</ul>
`;
exports[`Cascader popup correctly when panel is open 1`] = `
<div>
<!---->
<div class="ant-cascader-menus" style="pointer-events: none;">
<div>
<ul class="ant-cascader-menu">
<li class="ant-cascader-menu-item ant-cascader-menu-item-expand" title="Zhejiang" role="menuitem">Zhejiang<span class="ant-cascader-menu-item-expand-icon"><span role="img" aria-label="right" class="anticon anticon-right"><svg focusable="false" class="" data-icon="right" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M765.7 486.8L314.9 134.7A7.97 7.97 0 00302 141v77.3c0 4.9 2.3 9.6 6.1 12.6l360 281.1-360 281.1c-3.9 3-6.1 7.7-6.1 12.6V883c0 6.7 7.7 10.4 12.9 6.3l450.8-352.1a31.96 31.96 0 000-50.4z"></path></svg></span></span>
<!---->
</li>
<li class="ant-cascader-menu-item ant-cascader-menu-item-expand" title="Jiangsu" role="menuitem">Jiangsu<span class="ant-cascader-menu-item-expand-icon"><span role="img" aria-label="right" class="anticon anticon-right"><svg focusable="false" class="" data-icon="right" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M765.7 486.8L314.9 134.7A7.97 7.97 0 00302 141v77.3c0 4.9 2.3 9.6 6.1 12.6l360 281.1-360 281.1c-3.9 3-6.1 7.7-6.1 12.6V883c0 6.7 7.7 10.4 12.9 6.3l450.8-352.1a31.96 31.96 0 000-50.4z"></path></svg></span></span>
<!---->
</li>
</ul>
</div>
<div class="ant-cascader-menus">
<ul class="ant-cascader-menu" role="menu">
<li class="ant-cascader-menu-item ant-cascader-menu-item-expand" role="menuitemcheckbox" title="Zhejiang" aria-checked="false" data-path-key="zhejiang">
<!---->
<div class="ant-cascader-menu-item-content">Zhejiang</div>
<div class="ant-cascader-menu-item-expand-icon"><span role="img" aria-label="right" class="anticon anticon-right"><svg focusable="false" class="" data-icon="right" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M765.7 486.8L314.9 134.7A7.97 7.97 0 00302 141v77.3c0 4.9 2.3 9.6 6.1 12.6l360 281.1-360 281.1c-3.9 3-6.1 7.7-6.1 12.6V883c0 6.7 7.7 10.4 12.9 6.3l450.8-352.1a31.96 31.96 0 000-50.4z"></path></svg></span></div>
<!---->
</li>
<li class="ant-cascader-menu-item ant-cascader-menu-item-expand" role="menuitemcheckbox" title="Jiangsu" aria-checked="false" data-path-key="jiangsu">
<!---->
<div class="ant-cascader-menu-item-content">Jiangsu</div>
<div class="ant-cascader-menu-item-expand-icon"><span role="img" aria-label="right" class="anticon anticon-right"><svg focusable="false" class="" data-icon="right" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M765.7 486.8L314.9 134.7A7.97 7.97 0 00302 141v77.3c0 4.9 2.3 9.6 6.1 12.6l360 281.1-360 281.1c-3.9 3-6.1 7.7-6.1 12.6V883c0 6.7 7.7 10.4 12.9 6.3l450.8-352.1a31.96 31.96 0 000-50.4z"></path></svg></span></div>
<!---->
</li>
</ul>
</div>
</div>
`;
exports[`Cascader popup correctly with defaultValue 1`] = `
<div>
<!---->
<div class="ant-cascader-menus ant-slide-up-enter-from ant-slide-up-enter-active" style="pointer-events: none; opacity: 0;">
<div>
<ul class="ant-cascader-menu">
<li class="ant-cascader-menu-item ant-cascader-menu-item-expand ant-cascader-menu-item-active" title="Zhejiang" role="menuitem">Zhejiang<span class="ant-cascader-menu-item-expand-icon"><span role="img" aria-label="right" class="anticon anticon-right"><svg focusable="false" class="" data-icon="right" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M765.7 486.8L314.9 134.7A7.97 7.97 0 00302 141v77.3c0 4.9 2.3 9.6 6.1 12.6l360 281.1-360 281.1c-3.9 3-6.1 7.7-6.1 12.6V883c0 6.7 7.7 10.4 12.9 6.3l450.8-352.1a31.96 31.96 0 000-50.4z"></path></svg></span></span>
<!---->
</li>
<li class="ant-cascader-menu-item ant-cascader-menu-item-expand" title="Jiangsu" role="menuitem">Jiangsu<span class="ant-cascader-menu-item-expand-icon"><span role="img" aria-label="right" class="anticon anticon-right"><svg focusable="false" class="" data-icon="right" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M765.7 486.8L314.9 134.7A7.97 7.97 0 00302 141v77.3c0 4.9 2.3 9.6 6.1 12.6l360 281.1-360 281.1c-3.9 3-6.1 7.7-6.1 12.6V883c0 6.7 7.7 10.4 12.9 6.3l450.8-352.1a31.96 31.96 0 000-50.4z"></path></svg></span></span>
<!---->
</li>
</ul>
<ul class="ant-cascader-menu">
<li class="ant-cascader-menu-item ant-cascader-menu-item-expand ant-cascader-menu-item-active" title="Hangzhou" role="menuitem">Hangzhou<span class="ant-cascader-menu-item-expand-icon"><span role="img" aria-label="right" class="anticon anticon-right"><svg focusable="false" class="" data-icon="right" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M765.7 486.8L314.9 134.7A7.97 7.97 0 00302 141v77.3c0 4.9 2.3 9.6 6.1 12.6l360 281.1-360 281.1c-3.9 3-6.1 7.7-6.1 12.6V883c0 6.7 7.7 10.4 12.9 6.3l450.8-352.1a31.96 31.96 0 000-50.4z"></path></svg></span></span>
<!---->
</li>
</ul>
<ul class="ant-cascader-menu">
<li class="ant-cascader-menu-item" title="West Lake" role="menuitem">West Lake
<!---->
<!---->
</li>
</ul>
</div>
<div class="ant-cascader-menus">
<ul class="ant-cascader-menu" role="menu">
<li class="ant-cascader-menu-item ant-cascader-menu-item-expand ant-cascader-menu-item-active" role="menuitemcheckbox" title="Zhejiang" aria-checked="true" data-path-key="zhejiang">
<!---->
<div class="ant-cascader-menu-item-content">Zhejiang</div>
<div class="ant-cascader-menu-item-expand-icon"><span role="img" aria-label="right" class="anticon anticon-right"><svg focusable="false" class="" data-icon="right" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M765.7 486.8L314.9 134.7A7.97 7.97 0 00302 141v77.3c0 4.9 2.3 9.6 6.1 12.6l360 281.1-360 281.1c-3.9 3-6.1 7.7-6.1 12.6V883c0 6.7 7.7 10.4 12.9 6.3l450.8-352.1a31.96 31.96 0 000-50.4z"></path></svg></span></div>
<!---->
</li>
<li class="ant-cascader-menu-item ant-cascader-menu-item-expand" role="menuitemcheckbox" title="Jiangsu" aria-checked="false" data-path-key="jiangsu">
<!---->
<div class="ant-cascader-menu-item-content">Jiangsu</div>
<div class="ant-cascader-menu-item-expand-icon"><span role="img" aria-label="right" class="anticon anticon-right"><svg focusable="false" class="" data-icon="right" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M765.7 486.8L314.9 134.7A7.97 7.97 0 00302 141v77.3c0 4.9 2.3 9.6 6.1 12.6l360 281.1-360 281.1c-3.9 3-6.1 7.7-6.1 12.6V883c0 6.7 7.7 10.4 12.9 6.3l450.8-352.1a31.96 31.96 0 000-50.4z"></path></svg></span></div>
<!---->
</li>
</ul>
<ul class="ant-cascader-menu" role="menu">
<li class="ant-cascader-menu-item ant-cascader-menu-item-expand ant-cascader-menu-item-active" role="menuitemcheckbox" title="Hangzhou" aria-checked="true" data-path-key="zhejiang__RC_CASCADER_SPLIT__hangzhou">
<!---->
<div class="ant-cascader-menu-item-content">Hangzhou</div>
<div class="ant-cascader-menu-item-expand-icon"><span role="img" aria-label="right" class="anticon anticon-right"><svg focusable="false" class="" data-icon="right" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M765.7 486.8L314.9 134.7A7.97 7.97 0 00302 141v77.3c0 4.9 2.3 9.6 6.1 12.6l360 281.1-360 281.1c-3.9 3-6.1 7.7-6.1 12.6V883c0 6.7 7.7 10.4 12.9 6.3l450.8-352.1a31.96 31.96 0 000-50.4z"></path></svg></span></div>
<!---->
</li>
</ul>
<ul class="ant-cascader-menu" role="menu">
<li class="ant-cascader-menu-item" role="menuitemcheckbox" title="West Lake" aria-checked="true" data-path-key="zhejiang__RC_CASCADER_SPLIT__hangzhou__RC_CASCADER_SPLIT__xihu">
<!---->
<div class="ant-cascader-menu-item-content">West Lake</div>
<!---->
<!---->
</li>
</ul>
</div>
</div>
`;
exports[`Cascader support controlled mode 1`] = `<span class="ant-cascader-picker" tabindex="0"><span class="ant-cascader-picker-label">Zhejiang / Hangzhou / West Lake</span><input autocomplete="off" type="text" readonly="" class="ant-input ant-cascader-input"><span tabindex="-1" role="img" aria-label="close-circle" class="anticon anticon-close-circle ant-cascader-picker-clear"><svg focusable="false" class="" data-icon="close-circle" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm165.4 618.2l-66-.3L512 563.4l-99.3 118.4-66.1.3c-4.4 0-8-3.5-8-8 0-1.9.7-3.7 1.9-5.2l130.1-155L340.5 359a8.32 8.32 0 01-1.9-5.2c0-4.4 3.6-8 8-8l66.1.3L512 464.6l99.3-118.4 66-.3c4.4 0 8 3.5 8 8 0 1.9-.7 3.7-1.9 5.2L553.5 514l130 155c1.2 1.5 1.9 3.3 1.9 5.2 0 4.4-3.6 8-8 8z"></path></svg></span><span role="img" aria-label="down" class="anticon anticon-down ant-cascader-picker-arrow"><svg focusable="false" class="" data-icon="down" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M884 256h-75c-5.1 0-9.9 2.5-12.9 6.6L512 654.2 227.9 262.6c-3-4.1-7.8-6.6-12.9-6.6h-75c-6.5 0-10.3 7.4-6.5 12.7l352.6 486.1c12.8 17.6 39 17.6 51.7 0l352.6-486.1c3.9-5.3.1-12.7-6.4-12.7z"></path></svg></span></span>`;
exports[`Cascader support controlled mode 1`] = `
<div class="ant-select ant-cascader ant-select-single ant-select-allow-clear ant-select-show-arrow">
<!---->
<!---->
<div class="ant-select-selector"><span class="ant-select-selection-search"><input id="rc_select_TEST_OR_SSR" autocomplete="off" class="ant-select-selection-search-input" style="opacity: 0;" role="combobox" aria-haspopup="listbox" aria-owns="rc_select_TEST_OR_SSR_list" aria-autocomplete="list" aria-controls="rc_select_TEST_OR_SSR_list" readonly="" unselectable="on" type="search"></span><span class="ant-select-selection-item" title="Zhejiang">Zhejiang</span>
<!---->
</div><span class="ant-select-arrow" style="user-select: none;" unselectable="on" aria-hidden="true"><span role="img" aria-label="down" class="anticon anticon-down ant-select-suffix"><svg focusable="false" class="" data-icon="down" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M884 256h-75c-5.1 0-9.9 2.5-12.9 6.6L512 654.2 227.9 262.6c-3-4.1-7.8-6.6-12.9-6.6h-75c-6.5 0-10.3 7.4-6.5 12.7l352.6 486.1c12.8 17.6 39 17.6 51.7 0l352.6-486.1c3.9-5.3.1-12.7-6.4-12.7z"></path></svg></span></span><span class="ant-select-clear" style="user-select: none;" unselectable="on" aria-hidden="true"><span role="img" aria-label="close-circle" class="anticon anticon-close-circle"><svg focusable="false" class="" data-icon="close-circle" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm165.4 618.2l-66-.3L512 563.4l-99.3 118.4-66.1.3c-4.4 0-8-3.5-8-8 0-1.9.7-3.7 1.9-5.2l130.1-155L340.5 359a8.32 8.32 0 01-1.9-5.2c0-4.4 3.6-8 8-8l66.1.3L512 464.6l99.3-118.4 66-.3c4.4 0 8 3.5 8 8 0 1.9-.7 3.7-1.9 5.2L553.5 514l130 155c1.2 1.5 1.9 3.3 1.9 5.2 0 4.4-3.6 8-8 8z"></path></svg></span></span>
</div>
`;

View File

@ -46,6 +46,14 @@ function filter(inputValue, path) {
return path.some(option => option.label.toLowerCase().indexOf(inputValue.toLowerCase()) > -1);
}
function toggleOpen(wrapper) {
wrapper.find('.ant-select-selector').trigger('mousedown');
}
function isOpen(wrapper) {
return !!wrapper.findComponent({ name: 'Trigger' }).props().popupVisible;
}
describe('Cascader', () => {
focusTest(Cascader);
beforeEach(() => {
@ -65,7 +73,7 @@ describe('Cascader', () => {
it('popup correctly when panel is open', async () => {
const wrapper = mount(Cascader, { props: { options }, sync: false, attachTo: 'body' });
await asyncExpect(() => {
wrapper.find('input').trigger('click');
toggleOpen(wrapper);
});
expect($$('.ant-cascader-menus').length).toBe(1);
await asyncExpect(() => {
@ -95,7 +103,7 @@ describe('Cascader', () => {
});
await asyncExpect(() => {
wrapper.find('input').trigger('click');
toggleOpen(wrapper);
});
expect($$('.ant-cascader-menus').length).toBe(1);
await asyncExpect(() => {
@ -106,9 +114,8 @@ describe('Cascader', () => {
it('can be selected', async () => {
const wrapper = mount(Cascader, { props: { options }, sync: false });
await asyncExpect(() => {
wrapper.find('input').trigger('click');
toggleOpen(wrapper);
});
await asyncExpect(() => {
$$('.ant-cascader-menu')[0].querySelectorAll('.ant-cascader-menu-item')[0].click();
});
@ -134,23 +141,36 @@ describe('Cascader', () => {
});
});
it('backspace should work with `Cascader[showSearch]`', async () => {
fit('backspace should work with `Cascader[showSearch]`', async () => {
const wrapper = mount(Cascader, { props: { options, showSearch: true }, sync: false });
await asyncExpect(() => {
wrapper.find('input').element.value = '123';
wrapper.find('input').trigger('input');
});
await asyncExpect(() => {
expect(wrapper.vm.inputValue).toBe('123');
expect(isOpen(wrapper)).toBeTruthy();
});
await asyncExpect(() => {
wrapper.find('input').element.keyCode = KeyCode.BACKSPACE;
wrapper.find('input').trigger('keydown');
});
await asyncExpect(() => {
// trigger onKeyDown will not trigger onChange by default, so the value is still '123'
expect(wrapper.vm.inputValue).toBe('123');
expect(isOpen(wrapper)).toBeTruthy();
});
await asyncExpect(() => {
wrapper.find('input').element.value = '';
wrapper.find('input').trigger('input');
});
await asyncExpect(() => {
expect(isOpen(wrapper)).toBeTruthy();
});
// await asyncExpect(() => {
// wrapper.find('input').element.keyCode = KeyCode.BACKSPACE;
// wrapper.find('input').trigger('keydown');
// });
// await asyncExpect(() => {
// expect(isOpen(wrapper)).toBeFalsy();
// }, 0);
});
describe('limit filtered item count', () => {
@ -191,7 +211,6 @@ describe('Cascader', () => {
});
it('negative limit', async () => {
const errorSpy = jest.spyOn(console, 'error').mockImplementation(() => {});
const wrapper = mount(Cascader, {
props: { options, showSearch: { filter, limit: -1 } },
sync: false,
@ -203,9 +222,6 @@ describe('Cascader', () => {
await asyncExpect(() => {
expect($$('.ant-cascader-menu-item').length).toBe(2);
}, 0);
expect(errorSpy).toBeCalledWith(
"Warning: [antdv: Cascader] 'limit' of showSearch in Cascader should be positive number or false.",
);
});
});
});

View File

@ -20,12 +20,8 @@ Cascade selection box for selecting province/city/district.
</template>
<script lang="ts">
import { defineComponent, ref } from 'vue';
interface Option {
value: string;
label: string;
children?: Option[];
}
const options: Option[] = [
import type { CascaderProps } from 'ant-design-vue';
const options: CascaderProps['options'] = [
{
value: 'zhejiang',
label: 'Zhejiang',

View File

@ -16,16 +16,17 @@ Allow only select parent options.
</docs>
<template>
<a-cascader v-model:value="value" :options="options" change-on-select />
<a-cascader
v-model:value="value"
:options="options"
placeholder="Please select"
change-on-select
/>
</template>
<script lang="ts">
import { defineComponent, ref } from 'vue';
interface Option {
value: string;
label: string;
children?: Option[];
}
const options: Option[] = [
import type { CascaderProps } from 'ant-design-vue';
const options: CascaderProps['options'] = [
{
value: 'zhejiang',
label: 'Zhejiang',

View File

@ -16,7 +16,12 @@ For instance, add an external link after the selected value.
</docs>
<template>
<a-cascader v-model:value="value" :options="options" style="width: 100%">
<a-cascader
v-model:value="value"
placeholder="Please select"
:options="options"
style="width: 100%"
>
<template #displayRender="{ labels, selectedOptions }">
<span v-for="(label, index) in labels" :key="selectedOptions[index].value">
<span v-if="index === labels.length - 1">
@ -33,14 +38,8 @@ For instance, add an external link after the selected value.
</template>
<script lang="ts">
import { defineComponent, ref } from 'vue';
interface Option {
value: string;
label: string;
children?: Option[];
code?: number;
[key: string]: any;
}
const options: Option[] = [
import type { CascaderProps } from 'ant-design-vue';
const options: CascaderProps['options'] = [
{
value: 'zhejiang',
label: 'Zhejiang',
@ -78,7 +77,7 @@ const options: Option[] = [
];
export default defineComponent({
setup() {
const handleAreaClick = (e: Event, label: string, option: Option) => {
const handleAreaClick = (e: Event, label: string, option: CascaderProps['options'][number]) => {
e.stopPropagation();
console.log('clicked', label, option);
};

View File

@ -18,21 +18,20 @@ Separate trigger button and result.
<template>
<span>
{{ text }} &nbsp;
<a-cascader v-model:value="value" :options="options" @change="onChange">
<a-cascader
v-model:value="value"
placeholder="Please select"
:options="options"
@change="onChange"
>
<a href="#">Change city</a>
</a-cascader>
</span>
</template>
<script lang="ts">
import { defineComponent, ref } from 'vue';
interface Option {
value: string;
label: string;
children?: Option[];
code?: number;
[key: string]: any;
}
const options: Option[] = [
import type { CascaderProps } from 'ant-design-vue';
const options: CascaderProps['options'] = [
{
value: 'zhejiang',
label: 'Zhejiang',
@ -72,7 +71,7 @@ export default defineComponent({
const value = ref<string[]>([]);
const text = ref<string>('Unselect');
const onChange = (_value: string, selectedOptions: Option[]) => {
const onChange: CascaderProps['onChange'] = (_value, selectedOptions) => {
text.value = selectedOptions.map(o => o.label).join(', ');
};

View File

@ -16,19 +16,12 @@ Disable option by specifying the `disabled` property in `options`.
</docs>
<template>
<a-cascader v-model:value="value" :options="options" />
<a-cascader v-model:value="value" placeholder="Please select" :options="options" />
</template>
<script lang="ts">
import { defineComponent, ref } from 'vue';
interface Option {
value: string;
label: string;
disabled?: boolean;
children?: Option[];
code?: number;
[key: string]: any;
}
const options: Option[] = [
import type { CascaderProps } from 'ant-design-vue';
const options: CascaderProps['options'] = [
{
value: 'zhejiang',
label: 'Zhejiang',

View File

@ -25,14 +25,8 @@ Custom Field Names
</template>
<script lang="ts">
import { defineComponent, ref } from 'vue';
interface Option {
code: string;
name: string;
disabled?: boolean;
items?: Option[];
[key: string]: any;
}
const options: Option[] = [
import type { CascaderProps } from 'ant-design-vue';
const options: CascaderProps['options'] = [
{
code: 'zhejiang',
name: 'Zhejiang',

View File

@ -19,19 +19,14 @@ Hover to expand sub menu, click to select option.
<a-cascader
v-model:value="value"
:options="options"
:display-render="displayRender"
expand-trigger="hover"
placeholder="Please select"
/>
</template>
<script lang="ts">
import { defineComponent, ref } from 'vue';
interface Option {
value: string;
label: string;
children?: Option[];
}
const options: Option[] = [
import type { CascaderProps } from 'ant-design-vue';
const options: CascaderProps['options'] = [
{
value: 'zhejiang',
label: 'Zhejiang',
@ -67,14 +62,9 @@ const options: Option[] = [
];
export default defineComponent({
setup() {
const displayRender = ({ labels }: { labels: string[] }) => {
return labels[labels.length - 1];
};
return {
value: ref<string[]>([]),
options,
displayRender,
};
},
});

View File

@ -11,6 +11,7 @@
<lazy />
<fields-name />
<suffix />
<multipleVue />
</demo-sort>
</template>
<script>
@ -25,6 +26,7 @@ import Search from './search.vue';
import Size from './size.vue';
import FieldsName from './fields-name.vue';
import Suffix from './suffix.vue';
import multipleVue from './multiple.vue';
import CN from '../index.zh-CN.md';
import US from '../index.en-US.md';
@ -44,6 +46,7 @@ export default defineComponent({
Size,
FieldsName,
Suffix,
multipleVue,
},
});
</script>

View File

@ -28,16 +28,11 @@ Load options lazily with `loadData`.
</template>
<script lang="ts">
import { defineComponent, ref } from 'vue';
interface Option {
value: string;
label: string;
loading?: boolean;
isLeaf?: boolean;
children?: Option[];
}
import type { CascaderProps } from 'ant-design-vue';
export default defineComponent({
setup() {
const options = ref<Option[]>([
const options = ref<CascaderProps['options']>([
{
value: 'zhejiang',
label: 'Zhejiang',
@ -50,7 +45,7 @@ export default defineComponent({
},
]);
const loadData = (selectedOptions: Option[]) => {
const loadData: CascaderProps['loadData'] = selectedOptions => {
const targetOption = selectedOptions[selectedOptions.length - 1];
targetOption.loading = true;

View File

@ -0,0 +1,72 @@
<docs>
---
order: 5.1
version: 3.0.0
title:
zh-CN: 多选
en-US: Multiple
---
## zh-CN
一次性选择多个选项
## en-US
Select multiple options
</docs>
<template>
<a-cascader
v-model:value="value"
style="width: 233px"
multiple
max-tag-count="responsive"
:options="options"
placeholder="Please select"
></a-cascader>
</template>
<script lang="ts">
import { defineComponent, ref } from 'vue';
import type { CascaderProps } from 'ant-design-vue';
const options: CascaderProps['options'] = [
{
label: 'Light',
value: 'light',
children: new Array(20)
.fill(null)
.map((_, index) => ({ label: `Number ${index}`, value: index })),
},
{
label: 'Bamboo',
value: 'bamboo',
children: [
{
label: 'Little',
value: 'little',
children: [
{
label: 'Toy Fish',
value: 'fish',
},
{
label: 'Toy Cards',
value: 'cards',
},
{
label: 'Toy Bird',
value: 'bird',
},
],
},
],
},
];
export default defineComponent({
setup() {
return {
value: ref<string[]>([]),
options,
};
},
});
</script>

View File

@ -27,13 +27,9 @@ Search and select options directly.
</template>
<script lang="ts">
import { defineComponent, ref } from 'vue';
interface Option {
value: string;
label: string;
disabled?: boolean;
children?: Option[];
}
const options: Option[] = [
import type { CascaderProps } from 'ant-design-vue';
import type { ShowSearchType } from 'ant-design-vue/es/cascader';
const options: CascaderProps['options'] = [
{
value: 'zhejiang',
label: 'Zhejiang',
@ -74,7 +70,7 @@ const options: Option[] = [
];
export default defineComponent({
setup() {
const filter = (inputValue: string, path: Option[]) => {
const filter: ShowSearchType['filter'] = (inputValue, path) => {
return path.some(option => option.label.toLowerCase().indexOf(inputValue.toLowerCase()) > -1);
};

View File

@ -16,24 +16,20 @@ Cascade selection box of different sizes.
</docs>
<template>
<a-cascader v-model:value="value" size="large" :options="options" />
<a-cascader v-model:value="value" placeholder="Please select" size="large" :options="options" />
<br />
<br />
<a-cascader v-model:value="value" :options="options" />
<a-cascader v-model:value="value" placeholder="Please select" :options="options" />
<br />
<br />
<a-cascader v-model:value="value" size="small" :options="options" />
<a-cascader v-model:value="value" placeholder="Please select" size="small" :options="options" />
<br />
<br />
</template>
<script lang="ts">
import { defineComponent, ref } from 'vue';
interface Option {
value: string;
label: string;
children?: Option[];
}
const options: Option[] = [
import type { CascaderProps } from 'ant-design-vue';
const options: CascaderProps['options'] = [
{
value: 'zhejiang',
label: 'Zhejiang',

View File

@ -35,12 +35,8 @@ Custom suffix icon
<script lang="ts">
import { SmileOutlined } from '@ant-design/icons-vue';
import { defineComponent, ref } from 'vue';
interface Option {
value: string;
label: string;
children?: Option[];
}
const options: Option[] = [
import type { CascaderProps } from 'ant-design-vue';
const options: CascaderProps['options'] = [
{
value: 'zhejiang',
label: 'Zhejiang',

View File

@ -19,29 +19,34 @@ Cascade selection box.
<a-cascader :options="options" v-model:value="value" />
```
| Property | Description | Type | Default |
| --- | --- | --- | --- |
| allowClear | whether allow clear | boolean | true |
| autofocus | get focus when component mounted | boolean | false |
| changeOnSelect | change value on each selection if set to true, see above demo for details | boolean | false |
| defaultValue | initial selected value | string\[] \| number\[] | \[] |
| disabled | whether disabled select | boolean | false |
| displayRender | render function of displaying selected options, you can use #displayRender="{labels, selectedOptions}" | `({labels, selectedOptions}) => VNode` | `labels => labels.join(' / ')` |
| expandTrigger | expand current item when click or hover, one of 'click' 'hover' | string | 'click' |
| fieldNames | custom field name for label and value and children | object | `{ label: 'label', value: 'value', children: 'children' }` |
| getPopupContainer | Parent Node which the selector should be rendered to. Default to `body`. When position issues happen, try to modify it into scrollable content and position it relative. | Function(triggerNode) | () => document.body |
| loadData | To load option lazily, and it cannot work with `showSearch` | `(selectedOptions) => void` | - |
| notFoundContent | Specify content to show when no result matches. | string | 'Not Found' |
| options | data options of cascade | [Option](#option)[] | - |
| placeholder | input placeholder | string | 'Please select' |
| popupClassName | additional className of popup overlay | string | - |
| popupStyle | additional style of popup overlay | object | {} |
| popupPlacement | use preset popup align config from builtinPlacements`bottomLeft` `bottomRight` `topLeft` `topRight` | string | `bottomLeft` |
| popupVisible | set visible of cascader popup | boolean | - |
| showSearch | Whether show search input in single mode. | boolean \| [object](#showsearch) | false |
| size | input size, one of `large` `default` `small` | string | `default` |
| suffixIcon | The custom suffix icon | string \| VNode \| slot | - |
| value(v-model) | selected value | string\[] \| number\[] | - |
| Property | Description | Type | Default | Version |
| --- | --- | --- | --- | --- |
| allowClear | whether allow clear | boolean | true | |
| autofocus | get focus when component mounted | boolean | false | |
| changeOnSelect | change value on each selection if set to true, see above demo for details | boolean | false | |
| disabled | whether disabled select | boolean | false | |
| displayRender | render function of displaying selected options, you can use #displayRender="{labels, selectedOptions}" | `({labels, selectedOptions}) => VNode` | `labels => labels.join(' / ')` | |
| expandTrigger | expand current item when click or hover, one of 'click' 'hover' | string | 'click' | |
| fieldNames | custom field name for label and value and children | object | `{ label: 'label', value: 'value', children: 'children' }` | |
| getPopupContainer | Parent Node which the selector should be rendered to. Default to `body`. When position issues happen, try to modify it into scrollable content and position it relative. | Function(triggerNode) | () => document.body | |
| loadData | To load option lazily, and it cannot work with `showSearch` | `(selectedOptions) => void` | - | |
| notFoundContent | Specify content to show when no result matches. | string \| slot | 'Not Found' | |
| options | data options of cascade | [Option](#option)[] | - | |
| placeholder | input placeholder | string | 'Please select' | |
| showSearch | Whether show search input in single mode. | boolean \| [object](#showsearch) | false | |
| size | input size, one of `large` `default` `small` | string | `default` | |
| suffixIcon | The custom suffix icon | string \| VNode \| slot | - | |
| value(v-model) | selected value | string\[] \| number\[] | - | |
| expandIcon | Customize the current item expand icon | slot | - | 3.0 |
| maxTagCount | Max tag count to show. `responsive` will cost render performance | number \| `responsive` | - | 3.0 |
| maxTagPlaceholder | Placeholder for not showing tags | v-slot \| function(omittedValues) | - | 3.0 |
| dropdownClassName | additional className of popup overlay | string | - | 3.0 |
| dropdownStyle | additional style of popup overlay | CSSProperties | {} | 3.0 |
| open | set visible of cascader popup | boolean | - | 3.0 |
| placement | use preset popup align config from builtinPlacements`bottomLeft` `bottomRight` `topLeft` `topRight` | string | `bottomLeft` | 3.0 |
| tagRender | Customize tag render when `multiple` | slot | - | 3.0 |
| multiple | Support multiple or not | boolean | - | 3.0 |
| searchValue | Set search valueNeed work with `showSearch` | string | - | 3.0 |
### showSearch
@ -60,7 +65,7 @@ Fields in `showSearch`:
| Events Name | Description | Arguments | version |
| --- | --- | --- | --- | --- |
| change | callback when finishing cascader select | `(value, selectedOptions) => void` | - | |
| popupVisibleChange | callback when popup shown or hidden | `(value) => void` | - | |
| dropdownVisibleChange | callback when popup shown or hidden | `(value) => void` | - | 3.0 |
| search | callback when input value change | `(value) => void` | - | 1.5.4 |
### Option

View File

@ -1,646 +1,277 @@
import type { PropType, CSSProperties, ExtractPropTypes } from 'vue';
import { inject, provide, defineComponent } from 'vue';
import PropTypes from '../_util/vue-types';
import VcCascader from '../vc-cascader';
import arrayTreeFilter from 'array-tree-filter';
import classNames from '../_util/classNames';
import KeyCode from '../_util/KeyCode';
import Input from '../input';
import CloseCircleFilled from '@ant-design/icons-vue/CloseCircleFilled';
import DownOutlined from '@ant-design/icons-vue/DownOutlined';
import type { ShowSearchType, FieldNames, BaseOptionType, DefaultOptionType } from '../vc-cascader';
import VcCascader, { cascaderProps as vcCascaderProps } from '../vc-cascader';
import RightOutlined from '@ant-design/icons-vue/RightOutlined';
import RedoOutlined from '@ant-design/icons-vue/RedoOutlined';
import {
hasProp,
getOptionProps,
isValidElement,
getComponent,
splitAttrs,
findDOMNode,
getSlot,
} from '../_util/props-util';
import BaseMixin from '../_util/BaseMixin';
import { cloneElement } from '../_util/vnode';
import warning from '../_util/warning';
import { defaultConfigProvider } from '../config-provider';
import LeftOutlined from '@ant-design/icons-vue/LeftOutlined';
import getIcons from '../select/utils/iconUtil';
import type { VueNode } from '../_util/type';
import { tuple, withInstall } from '../_util/type';
import type { RenderEmptyHandler } from '../config-provider/renderEmpty';
import { useInjectFormItemContext } from '../form/FormItemContext';
import { withInstall } from '../_util/type';
import omit from '../_util/omit';
import { computed, defineComponent, ref, watchEffect } from 'vue';
import type { ExtractPropTypes, PropType } from 'vue';
import PropTypes from '../_util/vue-types';
import { initDefaultProps } from '../_util/props-util';
import useConfigInject from '../_util/hooks/useConfigInject';
import classNames from '../_util/classNames';
import type { SizeType } from '../config-provider';
import devWarning from '../vc-util/devWarning';
import { getTransitionName } from '../_util/transition';
import { useInjectFormItemContext } from '../form';
import type { ValueType } from '../vc-cascader/Cascader';
export interface CascaderOptionType {
value?: string | number;
label?: VueNode;
disabled?: boolean;
// Align the design since we use `rc-select` in root. This help:
// - List search content will show all content
// - Hover opacity style
// - Search filter match case
export type { BaseOptionType, DefaultOptionType, ShowSearchType };
export type FieldNamesType = FieldNames;
export type FilledFieldNamesType = Required<FieldNamesType>;
function highlightKeyword(str: string, lowerKeyword: string, prefixCls: string | undefined) {
const cells = str
.toLowerCase()
.split(lowerKeyword)
.reduce((list, cur, index) => (index === 0 ? [cur] : [...list, lowerKeyword, cur]), []);
const fillCells: VueNode[] = [];
let start = 0;
cells.forEach((cell, index) => {
const end = start + cell.length;
let originWorld: VueNode = str.slice(start, end);
start = end;
if (index % 2 === 1) {
originWorld = (
<span class={`${prefixCls}-menu-item-keyword`} key="seperator">
{originWorld}
</span>
);
}
fillCells.push(originWorld);
});
return fillCells;
}
const defaultSearchRender: ShowSearchType['render'] = ({
inputValue,
path,
prefixCls,
fieldNames,
}) => {
const optionList: VueNode[] = [];
// We do lower here to save perf
const lower = inputValue.toLowerCase();
path.forEach((node, index) => {
if (index !== 0) {
optionList.push(' / ');
}
let label = (node as any)[fieldNames.label!];
const type = typeof label;
if (type === 'string' || type === 'number') {
label = highlightKeyword(String(label), lower, prefixCls);
}
optionList.push(label);
});
return optionList;
};
export interface CascaderOptionType extends DefaultOptionType {
isLeaf?: boolean;
loading?: boolean;
children?: CascaderOptionType[];
[key: string]: any;
}
export function cascaderProps<DataNodeType extends CascaderOptionType = CascaderOptionType>() {
return {
...omit(vcCascaderProps(), ['customSlots', 'checkable', 'options']),
multiple: { type: Boolean, default: undefined },
size: String as PropType<SizeType>,
bordered: { type: Boolean, default: undefined },
export interface FieldNamesType {
value?: string;
label?: string;
children?: string;
}
export interface FilledFieldNamesType {
value: string;
label: string;
children: string;
}
// const CascaderOptionType = PropTypes.shape({
// value: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
// label: PropTypes.any,
// disabled: PropTypes.looseBool,
// children: PropTypes.array,
// key: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
// }).loose;
// const FieldNamesType = PropTypes.shape({
// value: PropTypes.string.isRequired,
// label: PropTypes.string.isRequired,
// children: PropTypes.string,
// }).loose;
export interface ShowSearchType {
filter?: (inputValue: string, path: CascaderOptionType[], names: FilledFieldNamesType) => boolean;
render?: (
inputValue: string,
path: CascaderOptionType[],
prefixCls: string | undefined,
names: FilledFieldNamesType,
) => VueNode;
sort?: (
a: CascaderOptionType[],
b: CascaderOptionType[],
inputValue: string,
names: FilledFieldNamesType,
) => number;
matchInputWidth?: boolean;
limit?: number | false;
}
export interface EmptyFilteredOptionsType {
disabled: boolean;
[key: string]: any;
}
export interface FilteredOptionsType extends EmptyFilteredOptionsType {
__IS_FILTERED_OPTION: boolean;
path: CascaderOptionType[];
}
// const ShowSearchType = PropTypes.shape({
// filter: PropTypes.func,
// render: PropTypes.func,
// sort: PropTypes.func,
// matchInputWidth: PropTypes.looseBool,
// limit: withUndefined(PropTypes.oneOfType([Boolean, Number])),
// }).loose;
function noop() {}
const cascaderProps = {
/** 可选项数据源 */
options: { type: Array as PropType<CascaderOptionType[]>, default: [] },
/** 默认的选中项 */
defaultValue: PropTypes.array,
/** 指定选中项 */
value: PropTypes.array,
/** 选择完成后的回调 */
// onChange?: (value: string[], selectedOptions?: CascaderOptionType[]) => void;
/** 选择后展示的渲染函数 */
displayRender: PropTypes.func,
transitionName: PropTypes.string,
popupStyle: PropTypes.object.def(() => ({})),
/** 自定义浮层类名 */
popupClassName: PropTypes.string,
/** 浮层预设位置:`bottomLeft` `bottomRight` `topLeft` `topRight` */
popupPlacement: PropTypes.oneOf(tuple('bottomLeft', 'bottomRight', 'topLeft', 'topRight')).def(
'bottomLeft',
),
/** 输入框占位文本*/
placeholder: PropTypes.string.def('Please select'),
/** 输入框大小,可选 `large` `default` `small` */
size: PropTypes.oneOf(tuple('large', 'default', 'small')),
/** 禁用*/
disabled: PropTypes.looseBool.def(false),
/** 是否支持清除*/
allowClear: PropTypes.looseBool.def(true),
showSearch: {
type: [Boolean, Object] as PropType<boolean | ShowSearchType | undefined>,
default: undefined as PropType<boolean | ShowSearchType | undefined>,
},
notFoundContent: PropTypes.any,
loadData: PropTypes.func,
/** 次级菜单的展开方式,可选 'click' 和 'hover' */
expandTrigger: PropTypes.oneOf(tuple('click', 'hover')),
/** 当此项为 true 时,点选每级菜单选项值都会发生变化 */
changeOnSelect: PropTypes.looseBool,
/** 浮层可见变化时回调 */
// onPopupVisibleChange?: (popupVisible: boolean) => void;
prefixCls: PropTypes.string,
inputPrefixCls: PropTypes.string,
getPopupContainer: PropTypes.func,
popupVisible: PropTypes.looseBool,
fieldNames: { type: Object as PropType<FieldNamesType> },
autofocus: PropTypes.looseBool,
suffixIcon: PropTypes.any,
showSearchRender: PropTypes.any,
onChange: PropTypes.func,
onPopupVisibleChange: PropTypes.func,
onFocus: PropTypes.func,
onBlur: PropTypes.func,
onSearch: PropTypes.func,
'onUpdate:value': PropTypes.func,
};
export type CascaderProps = Partial<ExtractPropTypes<typeof cascaderProps>>;
// We limit the filtered item count by default
const defaultLimit = 50;
function defaultFilterOption(
inputValue: string,
path: CascaderOptionType[],
names: FilledFieldNamesType,
) {
return path.some(option => option[names.label].indexOf(inputValue) > -1);
}
function defaultSortFilteredOption(
a: CascaderOptionType[],
b: CascaderOptionType[],
inputValue: string,
names: FilledFieldNamesType,
) {
function callback(elem: CascaderOptionType) {
return elem[names.label].indexOf(inputValue) > -1;
}
return a.findIndex(callback) - b.findIndex(callback);
}
function getFilledFieldNames(props: any) {
const fieldNames = (props.fieldNames || {}) as FieldNamesType;
const names: FilledFieldNamesType = {
children: fieldNames.children || 'children',
label: fieldNames.label || 'label',
value: fieldNames.value || 'value',
suffixIcon: PropTypes.any,
options: Array as PropType<DataNodeType[]>,
'onUpdate:value': Function as PropType<(value: ValueType) => void>,
};
return names;
}
function flattenTree(
options: CascaderOptionType[],
props: any,
ancestor: CascaderOptionType[] = [],
) {
const names: FilledFieldNamesType = getFilledFieldNames(props);
let flattenOptions = [];
const childrenName = names.children;
options.forEach(option => {
const path = ancestor.concat(option);
if (props.changeOnSelect || !option[childrenName] || !option[childrenName].length) {
flattenOptions.push(path);
}
if (option[childrenName]) {
flattenOptions = flattenOptions.concat(flattenTree(option[childrenName], props, path));
}
});
return flattenOptions;
}
export type CascaderProps = Partial<ExtractPropTypes<ReturnType<typeof cascaderProps>>>;
const defaultDisplayRender = ({ labels }) => labels.join(' / ');
export interface CascaderRef {
focus: () => void;
blur: () => void;
}
const Cascader = defineComponent({
name: 'ACascader',
mixins: [BaseMixin],
inheritAttrs: false,
props: cascaderProps,
setup() {
props: initDefaultProps(cascaderProps(), {
bordered: true,
choiceTransitionName: '',
allowClear: true,
}),
setup(props, { attrs, expose, slots, emit }) {
const formItemContext = useInjectFormItemContext();
return {
configProvider: inject('configProvider', defaultConfigProvider),
localeData: inject('localeData', {} as any),
cachedOptions: [],
popupRef: undefined,
input: undefined,
formItemContext,
};
},
data() {
const { value, defaultValue, popupVisible, showSearch, options } = this.$props;
return {
sValue: (value || defaultValue || []) as any[],
inputValue: '',
inputFocused: false,
sPopupVisible: popupVisible as boolean,
flattenOptions: showSearch
? flattenTree(options as CascaderOptionType[], this.$props)
: undefined,
};
},
watch: {
value(val) {
this.setState({ sValue: val || [] });
},
popupVisible(val) {
this.setState({ sPopupVisible: val });
},
options(val) {
if (this.showSearch) {
this.setState({ flattenOptions: flattenTree(val, this.$props as any) });
}
},
},
// model: {
// prop: 'value',
// event: 'change',
// },
created() {
provide('savePopupRef', this.savePopupRef);
},
methods: {
savePopupRef(ref: any) {
this.popupRef = ref;
},
highlightKeyword(str: string, keyword: string, prefixCls: string | undefined) {
return str
.split(keyword)
.map((node, index) =>
index === 0
? node
: [<span class={`${prefixCls}-menu-item-keyword`}>{keyword}</span>, node],
);
},
defaultRenderFilteredOption(opt: {
inputValue: string;
path: CascaderOptionType[];
prefixCls: string | undefined;
names: FilledFieldNamesType;
}) {
const { inputValue, path, prefixCls, names } = opt;
return path.map((option, index) => {
const label = option[names.label];
const node =
label.indexOf(inputValue) > -1
? this.highlightKeyword(label, inputValue, prefixCls)
: label;
return index === 0 ? node : [' / ', node];
});
},
saveInput(node: any) {
this.input = node;
},
handleChange(value: any, selectedOptions: CascaderOptionType[]) {
this.setState({ inputValue: '' });
if (selectedOptions[0].__IS_FILTERED_OPTION) {
const unwrappedValue = value[0];
const unwrappedSelectedOptions = selectedOptions[0].path;
this.setValue(unwrappedValue, unwrappedSelectedOptions);
return;
}
this.setValue(value, selectedOptions);
},
handlePopupVisibleChange(popupVisible: boolean) {
if (!hasProp(this, 'popupVisible')) {
this.setState((state: any) => ({
sPopupVisible: popupVisible,
inputFocused: popupVisible,
inputValue: popupVisible ? state.inputValue : '',
}));
}
this.$emit('popupVisibleChange', popupVisible);
},
handleInputFocus(e: InputEvent) {
this.$emit('focus', e);
},
handleInputBlur(e: InputEvent) {
this.setState({
inputFocused: false,
});
this.$emit('blur', e);
this.formItemContext.onFieldBlur();
},
handleInputClick(e: MouseEvent & { nativeEvent?: any }) {
const { inputFocused, sPopupVisible } = this;
// Prevent `Trigger` behavior.
if (inputFocused || sPopupVisible) {
e.stopPropagation();
if (e.nativeEvent && e.nativeEvent.stopImmediatePropagation) {
e.nativeEvent.stopImmediatePropagation();
}
}
},
handleKeyDown(e: KeyboardEvent) {
if (e.keyCode === KeyCode.BACKSPACE || e.keyCode === KeyCode.SPACE) {
e.stopPropagation();
}
},
handleInputChange(e: Event) {
const inputValue = (e.target as HTMLInputElement).value;
this.setState({ inputValue });
this.$emit('search', inputValue);
},
setValue(value: string[] | number[], selectedOptions: CascaderOptionType[] = []) {
if (!hasProp(this, 'value')) {
this.setState({ sValue: value });
}
this.$emit('update:value', value);
this.$emit('change', value, selectedOptions);
this.formItemContext.onFieldChange();
},
getLabel() {
const { options } = this;
const names = getFilledFieldNames(this.$props);
const displayRender = getComponent(this, 'displayRender', {}, false) || defaultDisplayRender;
const value = this.sValue;
const unwrappedValue = Array.isArray(value[0]) ? value[0] : value;
const selectedOptions = arrayTreeFilter<CascaderOptionType>(
options as CascaderOptionType[],
(o, level) => o[names.value] === unwrappedValue[level],
{ childrenKeyName: names.children },
);
const labels = selectedOptions.map(o => o[names.label]);
return displayRender({ labels, selectedOptions });
},
clearSelection(e: MouseEvent) {
e.preventDefault();
e.stopPropagation();
if (!this.inputValue) {
this.setValue([]);
this.handlePopupVisibleChange(false);
} else {
this.setState({ inputValue: '' });
}
},
generateFilteredOptions(
prefixCls: string | undefined,
renderEmpty: RenderEmptyHandler,
): EmptyFilteredOptionsType[] | FilteredOptionsType[] {
const { showSearch, notFoundContent } = this;
const names: FilledFieldNamesType = getFilledFieldNames(this.$props);
const {
filter = defaultFilterOption,
// render = this.defaultRenderFilteredOption,
sort = defaultSortFilteredOption,
limit = defaultLimit,
} = showSearch as ShowSearchType;
const render =
(showSearch as ShowSearchType).render ||
getComponent(this, 'showSearchRender') ||
this.defaultRenderFilteredOption;
const { flattenOptions = [], inputValue } = this.$data;
// Limit the filter if needed
let filtered: Array<CascaderOptionType[]>;
if (limit > 0) {
filtered = [];
let matchCount = 0;
// Perf optimization to filter items only below the limit
flattenOptions.some(path => {
const match = filter(inputValue, path, names);
if (match) {
filtered.push(path);
matchCount += 1;
}
return matchCount >= limit;
});
} else {
warning(
typeof limit !== 'number',
'Cascader',
"'limit' of showSearch in Cascader should be positive number or false.",
);
filtered = flattenOptions.filter(path => filter(inputValue, path, names));
}
filtered.sort((a, b) => sort(a, b, inputValue, names));
if (filtered.length > 0) {
return filtered.map(path => {
return {
__IS_FILTERED_OPTION: true,
path,
[names.label]: render({ inputValue, path, prefixCls, names }),
[names.value]: path.map(o => o[names.value]),
disabled: path.some(o => !!o.disabled),
};
});
}
return [
{
[names.label]: notFoundContent || renderEmpty('Cascader'),
[names.value]: 'ANT_CASCADER_NOT_FOUND',
disabled: true,
},
];
},
focus() {
this.input && this.input.focus();
},
blur() {
this.input && this.input.blur();
},
},
render() {
const { sPopupVisible, inputValue, configProvider, localeData } = this;
const { sValue: value, inputFocused } = this.$data;
const props = getOptionProps(this);
let suffixIcon = getComponent(this, 'suffixIcon');
suffixIcon = Array.isArray(suffixIcon) ? suffixIcon[0] : suffixIcon;
const { getPopupContainer: getContextPopupContainer } = configProvider;
const {
prefixCls: customizePrefixCls,
inputPrefixCls: customizeInputPrefixCls,
placeholder = localeData.placeholder,
size,
disabled,
allowClear,
showSearch = false,
notFoundContent,
...otherProps
} = props as any;
const { onEvents, extraAttrs } = splitAttrs(this.$attrs);
const {
class: className,
style,
id = this.formItemContext.id.value,
...restAttrs
} = extraAttrs;
const getPrefixCls = this.configProvider.getPrefixCls;
const renderEmpty = this.configProvider.renderEmpty;
const rootPrefixCls = getPrefixCls();
const prefixCls = getPrefixCls('cascader', customizePrefixCls);
const inputPrefixCls = getPrefixCls('input', customizeInputPrefixCls);
const sizeCls = classNames({
[`${inputPrefixCls}-lg`]: size === 'large',
[`${inputPrefixCls}-sm`]: size === 'small',
});
const clearIcon =
(allowClear && !disabled && value.length > 0) || inputValue ? (
<CloseCircleFilled
class={`${prefixCls}-picker-clear`}
onClick={this.clearSelection}
key="clear-icon"
/>
) : null;
const arrowCls = classNames({
[`${prefixCls}-picker-arrow`]: true,
[`${prefixCls}-picker-arrow-expand`]: sPopupVisible,
});
const pickerCls = classNames(className, `${prefixCls}-picker`, {
[`${prefixCls}-picker-with-value`]: inputValue,
[`${prefixCls}-picker-disabled`]: disabled,
[`${prefixCls}-picker-${size}`]: !!size,
[`${prefixCls}-picker-show-search`]: !!showSearch,
[`${prefixCls}-picker-focused`]: inputFocused,
});
// Fix bug of https://github.com/facebook/react/pull/5004
// and https://fb.me/react-unknown-prop
const tempInputProps = omit(otherProps, [
'popupStyle',
'options',
'popupPlacement',
'transitionName',
'displayRender',
'changeOnSelect',
'expandTrigger',
'popupVisible',
'getPopupContainer',
'loadData',
'popupClassName',
'filterOption',
'renderFilteredOption',
'sortFilteredOption',
'notFoundContent',
'defaultValue',
'fieldNames',
'onChange',
'onPopupVisibleChange',
'onFocus',
'onBlur',
'onSearch',
'onUpdate:value',
]);
let options = props.options;
const names = getFilledFieldNames(this.$props);
if (options && options.length > 0) {
if (inputValue) {
options = this.generateFilteredOptions(prefixCls, renderEmpty);
}
} else {
options = [
{
[names.label]: notFoundContent || renderEmpty('Cascader'),
[names.value]: 'ANT_CASCADER_NOT_FOUND',
disabled: true,
},
];
}
// Dropdown menu should keep previous status until it is fully closed.
if (!sPopupVisible) {
options = this.cachedOptions;
} else {
this.cachedOptions = options;
}
const dropdownMenuColumnStyle: CSSProperties = {};
const isNotFound =
(options || []).length === 1 && options[0].value === 'ANT_CASCADER_NOT_FOUND';
if (isNotFound) {
dropdownMenuColumnStyle.height = 'auto'; // Height of one row.
}
// The default value of `matchInputWidth` is `true`
const resultListMatchInputWidth = showSearch.matchInputWidth !== false;
if (resultListMatchInputWidth && (inputValue || isNotFound) && this.input) {
dropdownMenuColumnStyle.width = findDOMNode(this.input.input).offsetWidth + 'px';
}
// showSearchfocusblurinputref='picker'
const inputProps = {
...restAttrs,
...tempInputProps,
id,
prefixCls: inputPrefixCls,
placeholder: value && value.length > 0 ? undefined : placeholder,
value: inputValue,
disabled,
readonly: !showSearch,
autocomplete: 'off',
class: `${prefixCls}-input ${sizeCls}`,
onFocus: this.handleInputFocus,
onClick: showSearch ? this.handleInputClick : noop,
onBlur: showSearch ? this.handleInputBlur : props.onBlur,
onKeydown: this.handleKeyDown,
onChange: showSearch ? this.handleInputChange : noop,
};
const children = getSlot(this);
const inputIcon = (suffixIcon &&
(isValidElement(suffixIcon) ? (
cloneElement(suffixIcon, {
class: `${prefixCls}-picker-arrow`,
})
) : (
<span class={`${prefixCls}-picker-arrow`}>{suffixIcon}</span>
))) || <DownOutlined class={arrowCls} />;
const input = children.length ? (
children
) : (
<span class={pickerCls} style={style}>
<span class={`${prefixCls}-picker-label`}>{this.getLabel()}</span>
<Input {...inputProps} ref={this.saveInput} />
{clearIcon}
{inputIcon}
</span>
);
const expandIcon = <RightOutlined />;
const loadingIcon = (
<span class={`${prefixCls}-menu-item-loading-icon`}>
<RedoOutlined spin />
</span>
);
const getPopupContainer = props.getPopupContainer || getContextPopupContainer;
const cascaderProps = {
...props,
prefixCls: cascaderPrefixCls,
rootPrefixCls,
getPrefixCls,
direction,
getPopupContainer,
options,
prefixCls,
value,
popupVisible: sPopupVisible,
dropdownMenuColumnStyle,
expandIcon,
loadingIcon,
...onEvents,
onPopupVisibleChange: this.handlePopupVisibleChange,
onChange: this.handleChange,
transitionName: getTransitionName(rootPrefixCls, 'slide-up', props.transitionName),
renderEmpty,
size,
} = useConfigInject('cascader', props);
const prefixCls = computed(() => getPrefixCls('select', props.prefixCls));
const isRtl = computed(() => direction.value === 'rtl');
// =================== Warning =====================
if (process.env.NODE_ENV !== 'production') {
watchEffect(() => {
devWarning(
!props.multiple || !props.displayRender || !slots.displayRender,
'Cascader',
'`displayRender` not work on `multiple`. Please use `tagRender` instead.',
);
});
}
// ==================== Search =====================
const mergedShowSearch = computed(() => {
if (!props.showSearch) {
return props.showSearch;
}
let searchConfig: ShowSearchType = {
render: defaultSearchRender,
};
if (typeof props.showSearch === 'object') {
searchConfig = {
...searchConfig,
...props.showSearch,
};
}
return searchConfig;
});
// =================== Dropdown ====================
const mergedDropdownClassName = computed(() =>
classNames(
props.dropdownClassName || props.popupClassName,
`${cascaderPrefixCls.value}-dropdown`,
{
[`${cascaderPrefixCls.value}-dropdown-rtl`]: isRtl.value,
},
),
);
const selectRef = ref<CascaderRef>();
expose({
focus() {
selectRef.value?.focus();
},
blur() {
selectRef.value?.blur();
},
} as CascaderRef);
const handleChange: CascaderProps['onChange'] = (...args) => {
emit('update:value', args[0]);
emit('change', ...args);
formItemContext.onFieldChange();
};
const handleBlur: CascaderProps['onBlur'] = (...args) => {
emit('blur', ...args);
formItemContext.onFieldBlur();
};
return () => {
const {
notFoundContent = slots.notFoundContent?.(),
expandIcon = slots.expandIcon?.(),
multiple,
bordered,
allowClear,
choiceTransitionName,
transitionName,
id = formItemContext.id.value,
...restProps
} = props;
// =================== No Found ====================
const mergedNotFoundContent = notFoundContent || renderEmpty.value('Cascader');
// ===================== Icon ======================
let mergedExpandIcon = expandIcon;
if (!expandIcon) {
mergedExpandIcon = isRtl.value ? <LeftOutlined /> : <RightOutlined />;
}
const loadingIcon = (
<span class={`${prefixCls.value}-menu-item-loading-icon`}>
<RedoOutlined spin />
</span>
);
// ===================== Icons =====================
const { suffixIcon, removeIcon, clearIcon } = getIcons(
{
...props,
multiple,
prefixCls: prefixCls.value,
},
slots,
);
return (
<VcCascader
{...restProps}
{...attrs}
id={id}
prefixCls={prefixCls.value}
class={[
cascaderPrefixCls.value,
{
[`${prefixCls.value}-lg`]: size.value === 'large',
[`${prefixCls.value}-sm`]: size.value === 'small',
[`${prefixCls.value}-rtl`]: isRtl.value,
[`${prefixCls.value}-borderless`]: !bordered,
},
attrs.class,
]}
direction={direction.value}
notFoundContent={mergedNotFoundContent}
allowClear={allowClear}
showSearch={mergedShowSearch.value}
expandIcon={mergedExpandIcon}
inputIcon={suffixIcon}
removeIcon={removeIcon}
clearIcon={clearIcon}
loadingIcon={loadingIcon}
checkable={!!multiple}
dropdownClassName={mergedDropdownClassName.value}
dropdownPrefixCls={cascaderPrefixCls.value}
choiceTransitionName={getTransitionName(rootPrefixCls.value, '', choiceTransitionName)}
transitionName={getTransitionName(rootPrefixCls.value, 'slide-up', transitionName)}
getPopupContainer={getPopupContainer.value}
customSlots={{
...slots,
checkable: () => <span class={`${cascaderPrefixCls.value}-checkbox-inner`} />,
}}
displayRender={props.displayRender || slots.displayRender}
maxTagPlaceholder={props.maxTagPlaceholder || slots.maxTagPlaceholder}
onChange={handleChange}
onBlur={handleBlur}
v-slots={slots}
ref={selectRef}
/>
);
};
return <VcCascader {...cascaderProps}>{input}</VcCascader>;
},
});

View File

@ -20,29 +20,35 @@ cover: https://gw.alipayobjects.com/zos/alicdn/UdS8y8xyZ/Cascader.svg
<a-cascader :options="options" v-model:value="value" />
```
| 参数 | 说明 | 类型 | 默认值 |
| --- | --- | --- | --- |
| allowClear | 是否支持清除 | boolean | true |
| autofocus | 自动获取焦点 | boolean | false |
| changeOnSelect | 当此项为 true 时,点选每级菜单选项值都会发生变化,具体见上面的演示 | boolean | false |
| defaultValue | 默认的选中项 | string\[] \| number\[] | \[] |
| disabled | 禁用 | boolean | false |
| displayRender | 选择后展示的渲染函数,可使用 #displayRender="{labels, selectedOptions}" | `({labels, selectedOptions}) => VNode` | `labels => labels.join(' / ')` |
| expandTrigger | 次级菜单的展开方式,可选 'click' 和 'hover' | string | 'click' |
| fieldNames | 自定义 options 中 label name children 的字段 | object | `{ label: 'label', value: 'value', children: 'children' }` |
| getPopupContainer | 菜单渲染父节点。默认渲染到 body 上,如果你遇到菜单滚动定位问题,试试修改为滚动的区域,并相对其定位。 | Function(triggerNode) | () => document.body |
| loadData | 用于动态加载选项,无法与 `showSearch` 一起使用 | `(selectedOptions) => void` | - |
| notFoundContent | 当下拉列表为空时显示的内容 | string | 'Not Found' |
| options | 可选项数据源 | [Option](#option)[] | - |
| placeholder | 输入框占位文本 | string | '请选择' |
| popupClassName | 自定义浮层类名 | string | - |
| popupStyle | 自定义浮层样式 | object | {} |
| popupPlacement | 浮层预设位置:`bottomLeft` `bottomRight` `topLeft` `topRight` | Enum | `bottomLeft` |
| popupVisible | 控制浮层显隐 | boolean | - |
| showSearch | 在选择框中显示搜索框 | boolean \| [object](#showsearch) | false |
| size | 输入框大小,可选 `large` `default` `small` | string | `default` |
| suffixIcon | 自定义的选择框后缀图标 | string \| VNode \| slot | - |
| value(v-model) | 指定选中项 | string\[] \| number\[] | - |
| 参数 | 说明 | 类型 | 默认值 | Version |
| --- | --- | --- | --- | --- |
| allowClear | 是否支持清除 | boolean | true | |
| autofocus | 自动获取焦点 | boolean | false | |
| changeOnSelect | 当此项为 true 时,点选每级菜单选项值都会发生变化,具体见上面的演示 | boolean | false | |
| defaultValue | 默认的选中项 | string\[] \| number\[] | \[] | |
| disabled | 禁用 | boolean | false | |
| displayRender | 选择后展示的渲染函数,可使用 #displayRender="{labels, selectedOptions}" | `({labels, selectedOptions}) => VNode` | `labels => labels.join(' / ')` | |
| expandTrigger | 次级菜单的展开方式,可选 'click' 和 'hover' | string | 'click' | |
| fieldNames | 自定义 options 中 label name children 的字段 | object | `{ label: 'label', value: 'value', children: 'children' }` | |
| getPopupContainer | 菜单渲染父节点。默认渲染到 body 上,如果你遇到菜单滚动定位问题,试试修改为滚动的区域,并相对其定位。 | Function(triggerNode) | () => document.body | |
| loadData | 用于动态加载选项,无法与 `showSearch` 一起使用 | `(selectedOptions) => void` | - | |
| notFoundContent | 当下拉列表为空时显示的内容 | string \| slot | 'Not Found' | |
| options | 可选项数据源 | [Option](#option)[] | - | |
| placeholder | 输入框占位文本 | string | '请选择' | |
| showSearch | 在选择框中显示搜索框 | boolean \| [object](#showsearch) | false | |
| size | 输入框大小,可选 `large` `default` `small` | string | `default` | |
| suffixIcon | 自定义的选择框后缀图标 | string \| VNode \| slot | - | |
| value(v-model) | 指定选中项 | string\[] \| number\[] | - | |
| expandIcon | 自定义次级菜单展开图标 | slot | - | 3.0 |
| maxTagCount | 最多显示多少个 tag响应式模式会对性能产生损耗 | number \| `responsive` | - | 3.0 |
| maxTagPlaceholder | 隐藏 tag 时显示的内容 | v-slot \| function(omittedValues) | - | 3.0 |
| dropdownClassName | 自定义浮层类名 | string | - | 3.0 |
| dropdownStyle | 自定义浮层样式 | CSSProperties | {} | 3.0 |
| open | 控制浮层显隐 | boolean | - | 3.0 |
| placement | 浮层预设位置:`bottomLeft` `bottomRight` `topLeft` `topRight` | string | `bottomLeft` | 3.0 |
| tagRender | 自定义 tag 内容,多选时生效 | (props) => ReactNode | - | 3.0 |
| multiple | 支持多选节点 | boolean | - | 3.0 |
| searchValue | 设置搜索的值,需要与 `showSearch` 配合使用 | string | - | 3.0 |
### showSearch
@ -58,18 +64,18 @@ cover: https://gw.alipayobjects.com/zos/alicdn/UdS8y8xyZ/Cascader.svg
### 事件
| 事件名称 | 说明 | 回调参数 | 版本 |
| ------------------ | ------------------- | ---------------------------------- | ---- | ----- |
| change | 选择完成后的回调 | `(value, selectedOptions) => void` | - | |
| popupVisibleChange | 显示/隐藏浮层的回调 | `(value) => void` | - | |
| search | 输入框变化时的回调 | `(value) => void` | - | 1.5.4 |
| 事件名称 | 说明 | 回调参数 | 版本 |
| --------------------- | ---------------------- | ---------------------------------- | ---- | --- |
| change | 选择完成后的回调 | `(value, selectedOptions) => void` | - | |
| dropdownVisibleChange | 显示/隐藏浮层的回调 | `(value) => void` | - | 3.0 |
| search | 监听搜索,返回输入的值 | `(value) => void` | - | 3.0 |
### Option
```ts
interface Option {
value: string | number;
label?: VNode;
label?: any;
disabled?: boolean;
children?: Option[];
}

View File

@ -1,169 +1,38 @@
@import '../../style/themes/index';
@import '../../style/mixins/index';
@import '../../input/style/mixin';
@import '../../checkbox/style/mixin';
@cascader-prefix-cls: ~'@{ant-prefix}-cascader';
.antCheckboxFn(@checkbox-prefix-cls: ~'@{cascader-prefix-cls}-checkbox');
.@{cascader-prefix-cls} {
.reset-component();
width: 184px;
&-input.@{ant-prefix}-input {
// Keep it static for https://github.com/ant-design/ant-design/issues/16738
position: static;
width: 100%;
// https://github.com/ant-design/ant-design/issues/17582
padding-right: 24px;
// Add important to fix https://github.com/ant-design/ant-design/issues/5078
// because input.less will compile after cascader.less
background-color: transparent !important;
cursor: pointer;
}
&-picker-show-search &-input.@{ant-prefix}-input {
position: relative;
}
&-picker {
.reset-component();
position: relative;
display: inline-block;
background-color: @cascader-bg;
border-radius: @border-radius-base;
outline: 0;
cursor: pointer;
transition: color 0.3s;
&-with-value &-label {
color: transparent;
}
&-disabled {
color: @disabled-color;
background: @input-disabled-bg;
cursor: not-allowed;
.@{cascader-prefix-cls}-input {
cursor: not-allowed;
}
}
&:focus .@{cascader-prefix-cls}-input {
.active();
}
&-show-search&-focused {
color: @disabled-color;
}
&-label {
position: absolute;
top: 50%;
left: 0;
width: 100%;
height: 20px;
margin-top: -10px;
padding: 0 20px 0 @control-padding-horizontal;
overflow: hidden;
line-height: 20px;
white-space: nowrap;
text-overflow: ellipsis;
}
&-clear {
position: absolute;
top: 50%;
right: @control-padding-horizontal;
z-index: 2;
width: 12px;
height: 12px;
margin-top: -6px;
color: @disabled-color;
font-size: @font-size-sm;
line-height: 12px;
background: @component-background;
cursor: pointer;
opacity: 0;
transition: color 0.3s ease, opacity 0.15s ease;
&:hover {
color: @text-color-secondary;
}
}
&:hover &-clear {
opacity: 1;
}
// arrow
&-arrow {
position: absolute;
top: 50%;
right: @control-padding-horizontal;
z-index: 1;
width: 12px;
height: 12px;
margin-top: -6px;
color: @disabled-color;
font-size: 12px;
line-height: 12px;
transition: transform 0.2s;
&&-expand {
transform: rotate(180deg);
}
}
}
// https://github.com/ant-design/ant-design/pull/12407#issuecomment-424657810
&-picker-label:hover + &-input {
.hover();
}
&-picker-small &-picker-clear,
&-picker-small &-picker-arrow {
right: @control-padding-horizontal-sm;
&-checkbox {
top: 0;
margin-right: @padding-xs;
}
&-menus {
position: absolute;
z-index: @zindex-dropdown;
font-size: @cascader-dropdown-font-size;
white-space: nowrap;
background: @cascader-menu-bg;
border-radius: @border-radius-base;
box-shadow: @box-shadow-base;
display: flex;
flex-wrap: nowrap;
align-items: flex-start;
ul,
ol {
margin: 0;
list-style: none;
}
&-empty,
&-hidden {
display: none;
}
&.slide-up-enter.slide-up-enter-active&-placement-bottomLeft,
&.slide-up-appear.slide-up-appear-active&-placement-bottomLeft {
animation-name: antSlideUpIn;
}
&.slide-up-enter.slide-up-enter-active&-placement-topLeft,
&.slide-up-appear.slide-up-appear-active&-placement-topLeft {
animation-name: antSlideDownIn;
}
&.slide-up-leave.slide-up-leave-active&-placement-bottomLeft {
animation-name: antSlideUpOut;
}
&.slide-up-leave.slide-up-leave-active&-placement-topLeft {
animation-name: antSlideDownOut;
&.@{cascader-prefix-cls}-menu-empty {
.@{cascader-prefix-cls}-menu {
width: 100%;
height: auto;
}
}
}
&-menu {
display: inline-block;
min-width: 111px;
height: 180px;
margin: 0;
margin: -@dropdown-edge-child-vertical-padding 0;
padding: @cascader-dropdown-edge-child-vertical-padding 0;
overflow: auto;
vertical-align: top;
@ -171,60 +40,65 @@
border-right: @border-width-base @border-style-base @cascader-menu-border-color-split;
-ms-overflow-style: -ms-autohiding-scrollbar; // https://github.com/ant-design/ant-design/issues/11857
&:first-child {
border-radius: @border-radius-base 0 0 @border-radius-base;
}
&:last-child {
margin-right: -1px;
border-right-color: transparent;
border-radius: 0 @border-radius-base @border-radius-base 0;
}
&:only-child {
border-radius: @border-radius-base;
}
}
&-menu-item {
padding: @cascader-dropdown-vertical-padding @control-padding-horizontal;
line-height: @cascader-dropdown-line-height;
white-space: nowrap;
cursor: pointer;
transition: all 0.3s;
&:hover {
background: @item-hover-bg;
}
&-disabled {
color: @disabled-color;
cursor: not-allowed;
&:hover {
background: transparent;
}
}
&-active:not(&-disabled) {
&,
&:hover {
font-weight: @select-item-selected-font-weight;
background-color: @cascader-item-selected-bg;
}
}
&-expand {
position: relative;
padding-right: 24px;
}
&-item {
display: flex;
flex-wrap: nowrap;
align-items: center;
padding: @cascader-dropdown-vertical-padding @control-padding-horizontal;
overflow: hidden;
line-height: @cascader-dropdown-line-height;
white-space: nowrap;
text-overflow: ellipsis;
cursor: pointer;
transition: all 0.3s;
&-expand &-expand-icon,
&-loading-icon {
.iconfont-size-under-12px(10px);
&:hover {
background: @item-hover-bg;
}
position: absolute;
right: @control-padding-horizontal;
color: @text-color-secondary;
.@{cascader-prefix-cls}-menu-item-disabled& {
&-disabled {
color: @disabled-color;
}
}
cursor: not-allowed;
& &-keyword {
color: @highlight-color;
&:hover {
background: transparent;
}
}
.@{cascader-prefix-cls}-menu-empty & {
color: @disabled-color;
cursor: default;
pointer-events: none;
}
&-active:not(&-disabled) {
&,
&:hover {
font-weight: @select-item-selected-font-weight;
background-color: @cascader-item-selected-bg;
}
}
&-content {
flex: auto;
}
&-expand &-expand-icon,
&-loading-icon {
margin-left: @padding-xss;
color: @text-color-secondary;
font-size: 10px;
.@{cascader-prefix-cls}-menu-item-disabled& {
color: @disabled-color;
}
}
&-keyword {
color: @highlight-color;
}
}
}
}
@import './rtl';

View File

@ -3,4 +3,4 @@ import './index.less';
// style dependencies
import '../../empty/style';
import '../../input/style';
import '../../select/style';

View File

@ -0,0 +1,19 @@
// We can not import reference of `./index` directly since it will make dead loop in less
@import (reference) '../../style/themes/index';
@cascader-prefix-cls: ~'@{ant-prefix}-cascader';
.@{cascader-prefix-cls}-rtl {
.@{cascader-prefix-cls}-menu-item {
&-expand-icon,
&-loading-icon {
margin-right: @padding-xss;
margin-left: 0;
}
}
.@{cascader-prefix-cls}-checkbox {
top: 0;
margin-right: 0;
margin-left: @padding-xs;
}
}

View File

@ -11,6 +11,7 @@ import { getTimeProps, Components } from '.';
import { computed, defineComponent, nextTick, onMounted, ref } from 'vue';
import useConfigInject from '../../_util/hooks/useConfigInject';
import classNames from '../../_util/classNames';
import type { CommonProps, RangePickerProps } from './props';
import { commonProps, rangePickerProps } from './props';
import type { PanelMode, RangeValue } from '../../vc-picker/interface';
import type { RangePickerSharedProps } from '../../vc-picker/RangePicker';
@ -41,7 +42,8 @@ export default function generateRangePicker<DateType, ExtraProps = {}>(
'renderExtraFooter',
// 'separator',
],
setup(props, { expose, slots, attrs, emit }) {
setup(_props, { expose, slots, attrs, emit }) {
const props = _props as unknown as CommonProps<DateType> & RangePickerProps<DateType>;
const formItemContext = useInjectFormItemContext();
devWarning(
!attrs.getCalendarContainer,

View File

@ -11,6 +11,7 @@ import { getTimeProps, Components } from '.';
import { computed, defineComponent, nextTick, onMounted, ref } from 'vue';
import useConfigInject from '../../_util/hooks/useConfigInject';
import classNames from '../../_util/classNames';
import type { CommonProps, DatePickerProps } from './props';
import { commonProps, datePickerProps } from './props';
import devWarning from '../../vc-util/devWarning';
@ -21,14 +22,15 @@ export default function generateSinglePicker<DateType, ExtraProps = {}>(
extraProps: ExtraProps,
) {
function getPicker(picker?: PickerMode, displayName?: string) {
const comProps = {
...commonProps<DateType>(),
...datePickerProps<DateType>(),
...extraProps,
};
return defineComponent({
name: displayName,
inheritAttrs: false,
props: {
...commonProps<DateType>(),
...datePickerProps<DateType>(),
...extraProps,
},
props: comProps,
slots: [
'suffixIcon',
// 'clearIcon',
@ -41,7 +43,11 @@ export default function generateSinglePicker<DateType, ExtraProps = {}>(
'renderExtraFooter',
'monthCellRender',
],
setup(props, { slots, expose, attrs, emit }) {
setup(_props, { slots, expose, attrs, emit }) {
// vue 3.2.7
const props = _props as unknown as CommonProps<DateType> &
DatePickerProps<DateType> &
ExtraProps;
const formItemContext = useInjectFormItemContext();
devWarning(
!(props.monthCellContentRender || slots.monthCellContentRender),

View File

@ -18,7 +18,7 @@ import type { SharedTimeProps } from '../../vc-picker/panels/TimePanel';
import type { RangeDateRender, RangeInfo, RangeType } from '../../vc-picker/RangePicker';
import type { VueNode } from '../../_util/type';
function commonProps<DateType>() {
function commonProps<DateType = any>() {
return {
id: String,
dropdownClassName: String,
@ -138,7 +138,7 @@ export interface CommonProps<DateType> {
valueFormat?: string;
}
function datePickerProps<DateType>() {
function datePickerProps<DateType = any>() {
return {
defaultPickerValue: { type: [String, Object] as PropType<DateType | string> },
defaultValue: { type: [String, Object] as PropType<DateType | string> },

View File

@ -41,17 +41,23 @@ exports[`renders ./components/empty/demo/config-provider.vue correctly 1`] = `
<!---->
</div>
<h3>TreeSelect</h3>
<div style="width: 200px;" class="ant-select ant-tree-select ant-select-single ant-select-show-arrow">
<div style="width: 200px;" class="ant-select ant-tree-select ant-select-single ant-select-show-arrow" customslots="[object Object]">
<!---->
<!---->
<div class="ant-select-selector"><span class="ant-select-selection-search"><input id="rc_select_TEST_OR_SSR" autocomplete="off" class="ant-select-selection-search-input" style="opacity: 0;" role="combobox" aria-haspopup="listbox" aria-owns="rc_select_TEST_OR_SSR_list" aria-autocomplete="list" aria-controls="rc_select_TEST_OR_SSR_list" aria-activedescendant="rc_select_TEST_OR_SSR_list_0" readonly="" unselectable="on" type="search"></span>
<div class="ant-select-selector"><span class="ant-select-selection-search"><input id="rc_select_TEST_OR_SSR" autocomplete="off" class="ant-select-selection-search-input" style="opacity: 0;" role="combobox" aria-haspopup="listbox" aria-owns="rc_select_TEST_OR_SSR_list" aria-autocomplete="list" aria-controls="rc_select_TEST_OR_SSR_list" readonly="" unselectable="on" type="search"></span>
<!----><span class="ant-select-selection-placeholder"><!----></span>
</div><span class="ant-select-arrow" style="user-select: none;" unselectable="on" aria-hidden="true"><span role="img" aria-label="down" class="anticon anticon-down ant-select-suffix"><svg focusable="false" class="" data-icon="down" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M884 256h-75c-5.1 0-9.9 2.5-12.9 6.6L512 654.2 227.9 262.6c-3-4.1-7.8-6.6-12.9-6.6h-75c-6.5 0-10.3 7.4-6.5 12.7l352.6 486.1c12.8 17.6 39 17.6 51.7 0l352.6-486.1c3.9-5.3.1-12.7-6.4-12.7z"></path></svg></span></span>
<!---->
</div>
<h3>Cascader</h3>
<!----><span class="ant-cascader-picker ant-cascader-picker-show-search" style="width: 200px;" tabindex="0"><span class="ant-cascader-picker-label"></span><input placeholder="Please select" autocomplete="off" type="text" class="ant-input ant-cascader-input">
<!----><span role="img" aria-label="down" class="anticon anticon-down ant-cascader-picker-arrow"><svg focusable="false" class="" data-icon="down" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M884 256h-75c-5.1 0-9.9 2.5-12.9 6.6L512 654.2 227.9 262.6c-3-4.1-7.8-6.6-12.9-6.6h-75c-6.5 0-10.3 7.4-6.5 12.7l352.6 486.1c12.8 17.6 39 17.6 51.7 0l352.6-486.1c3.9-5.3.1-12.7-6.4-12.7z"></path></svg></span></span>
<div style="width: 200px;" class="ant-select ant-cascader ant-select-single ant-select-allow-clear ant-select-show-arrow ant-select-show-search">
<!---->
<!---->
<div class="ant-select-selector"><span class="ant-select-selection-search"><input id="rc_select_TEST_OR_SSR" autocomplete="off" class="ant-select-selection-search-input" role="combobox" aria-haspopup="listbox" aria-owns="rc_select_TEST_OR_SSR_list" aria-autocomplete="list" aria-controls="rc_select_TEST_OR_SSR_list" type="search"></span>
<!----><span class="ant-select-selection-placeholder"><!----></span>
</div><span class="ant-select-arrow" style="user-select: none;" unselectable="on" aria-hidden="true"><span role="img" aria-label="down" class="anticon anticon-down ant-select-suffix"><svg focusable="false" class="" data-icon="down" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M884 256h-75c-5.1 0-9.9 2.5-12.9 6.6L512 654.2 227.9 262.6c-3-4.1-7.8-6.6-12.9-6.6h-75c-6.5 0-10.3 7.4-6.5 12.7l352.6 486.1c12.8 17.6 39 17.6 51.7 0l352.6-486.1c3.9-5.3.1-12.7-6.4-12.7z"></path></svg></span></span>
<!---->
</div>
<h3>Transfer</h3>
<div class="ant-transfer">
<div class="ant-transfer-list">

View File

@ -68,8 +68,14 @@ exports[`renders ./components/input-number/demo/addon.vue correctly 1`] = `
<div class="ant-input-number-input-wrap"><input autocomplete="off" role="spinbutton" aria-valuenow="100" step="1" class="ant-input-number-input"></div>
</div>
<div class="ant-input-number-group-addon">
<!----><span class="ant-cascader-picker" style="width: 150px;" tabindex="0"><span class="ant-cascader-picker-label"></span><input placeholder="cascader" autocomplete="off" type="text" readonly="" class="ant-input ant-cascader-input">
<!----><span role="img" aria-label="down" class="anticon anticon-down ant-cascader-picker-arrow"><svg focusable="false" class="" data-icon="down" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M884 256h-75c-5.1 0-9.9 2.5-12.9 6.6L512 654.2 227.9 262.6c-3-4.1-7.8-6.6-12.9-6.6h-75c-6.5 0-10.3 7.4-6.5 12.7l352.6 486.1c12.8 17.6 39 17.6 51.7 0l352.6-486.1c3.9-5.3.1-12.7-6.4-12.7z"></path></svg></span></span>
<div style="width: 150px;" class="ant-select ant-cascader ant-select-single ant-select-allow-clear ant-select-show-arrow">
<!---->
<!---->
<div class="ant-select-selector"><span class="ant-select-selection-search"><input id="rc_select_TEST_OR_SSR" autocomplete="off" class="ant-select-selection-search-input" style="opacity: 0;" role="combobox" aria-haspopup="listbox" aria-owns="rc_select_TEST_OR_SSR_list" aria-autocomplete="list" aria-controls="rc_select_TEST_OR_SSR_list" readonly="" unselectable="on" type="search"></span>
<!----><span class="ant-select-selection-placeholder">cascader</span>
</div><span class="ant-select-arrow" style="user-select: none;" unselectable="on" aria-hidden="true"><span role="img" aria-label="down" class="anticon anticon-down ant-select-suffix"><svg focusable="false" class="" data-icon="down" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M884 256h-75c-5.1 0-9.9 2.5-12.9 6.6L512 654.2 227.9 262.6c-3-4.1-7.8-6.6-12.9-6.6h-75c-6.5 0-10.3 7.4-6.5 12.7l352.6 486.1c12.8 17.6 39 17.6 51.7 0l352.6-486.1c3.9-5.3.1-12.7-6.4-12.7z"></path></svg></span></span>
<!---->
</div>
</div>
</div>
</div>

View File

@ -79,8 +79,14 @@ exports[`renders ./components/input/demo/group.vue correctly 1`] = `
</div><span class="ant-select-arrow" style="user-select: none;" unselectable="on" aria-hidden="true"><span role="img" aria-label="down" class="anticon anticon-down ant-select-suffix"><svg focusable="false" class="" data-icon="down" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M884 256h-75c-5.1 0-9.9 2.5-12.9 6.6L512 654.2 227.9 262.6c-3-4.1-7.8-6.6-12.9-6.6h-75c-6.5 0-10.3 7.4-6.5 12.7l352.6 486.1c12.8 17.6 39 17.6 51.7 0l352.6-486.1c3.9-5.3.1-12.7-6.4-12.7z"></path></svg></span></span>
<!---->
</div>
<!----><span class="ant-cascader-picker" style="width: 70%;" tabindex="0"><span class="ant-cascader-picker-label"></span><input placeholder="Select Address" autocomplete="off" type="text" readonly="" class="ant-input ant-cascader-input">
<!----><span role="img" aria-label="down" class="anticon anticon-down ant-cascader-picker-arrow"><svg focusable="false" class="" data-icon="down" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M884 256h-75c-5.1 0-9.9 2.5-12.9 6.6L512 654.2 227.9 262.6c-3-4.1-7.8-6.6-12.9-6.6h-75c-6.5 0-10.3 7.4-6.5 12.7l352.6 486.1c12.8 17.6 39 17.6 51.7 0l352.6-486.1c3.9-5.3.1-12.7-6.4-12.7z"></path></svg></span></span></span></div>
<div style="width: 70%;" class="ant-select ant-cascader ant-select-single ant-select-allow-clear ant-select-show-arrow">
<!---->
<!---->
<div class="ant-select-selector"><span class="ant-select-selection-search"><input id="rc_select_TEST_OR_SSR" autocomplete="off" class="ant-select-selection-search-input" style="opacity: 0;" role="combobox" aria-haspopup="listbox" aria-owns="rc_select_TEST_OR_SSR_list" aria-autocomplete="list" aria-controls="rc_select_TEST_OR_SSR_list" readonly="" unselectable="on" type="search"></span>
<!----><span class="ant-select-selection-placeholder">Select Address</span>
</div><span class="ant-select-arrow" style="user-select: none;" unselectable="on" aria-hidden="true"><span role="img" aria-label="down" class="anticon anticon-down ant-select-suffix"><svg focusable="false" class="" data-icon="down" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M884 256h-75c-5.1 0-9.9 2.5-12.9 6.6L512 654.2 227.9 262.6c-3-4.1-7.8-6.6-12.9-6.6h-75c-6.5 0-10.3 7.4-6.5 12.7l352.6 486.1c12.8 17.6 39 17.6 51.7 0l352.6-486.1c3.9-5.3.1-12.7-6.4-12.7z"></path></svg></span></span>
<!---->
</div></span></div>
`;
exports[`renders ./components/input/demo/password-input.vue correctly 1`] = `<span class="ant-input-affix-wrapper ant-input-password"><!----><input placeholder="input password" type="password" class="ant-input"><span class="ant-input-suffix"><!----><span tabindex="-1" role="img" aria-label="eye-invisible" class="anticon anticon-eye-invisible ant-input-password-icon"><svg focusable="false" class="" data-icon="eye-invisible" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M942.2 486.2Q889.47 375.11 816.7 305l-50.88 50.88C807.31 395.53 843.45 447.4 874.7 512 791.5 684.2 673.4 766 512 766q-72.67 0-133.87-22.38L323 798.75Q408 838 512 838q288.3 0 430.2-300.3a60.29 60.29 0 000-51.5zm-63.57-320.64L836 122.88a8 8 0 00-11.32 0L715.31 232.2Q624.86 186 512 186q-288.3 0-430.2 300.3a60.3 60.3 0 000 51.5q56.69 119.4 136.5 191.41L112.48 835a8 8 0 000 11.31L155.17 889a8 8 0 0011.31 0l712.15-712.12a8 8 0 000-11.32zM149.3 512C232.6 339.8 350.7 258 512 258c54.54 0 104.13 9.36 149.12 28.39l-70.3 70.3a176 176 0 00-238.13 238.13l-83.42 83.42C223.1 637.49 183.3 582.28 149.3 512zm246.7 0a112.11 112.11 0 01146.2-106.69L401.31 546.2A112 112 0 01396 512z"></path><path d="M508 624c-3.46 0-6.87-.16-10.25-.47l-52.82 52.82a176.09 176.09 0 00227.42-227.42l-52.82 52.82c.31 3.38.47 6.79.47 10.25a111.94 111.94 0 01-112 112z"></path></svg></span></span></span>`;

View File

@ -94,7 +94,7 @@ exports[`renders ./components/layout/demo/fixed.vue correctly 1`] = `
<li class="ant-menu-overflow-item ant-menu-item ant-menu-item-only-child" style="opacity: 1; order: 2;" role="menuitem" tabindex="-1" data-menu-id="3" aria-disabled="false">
<!----><span class="ant-menu-title-content">nav 3</span>
</li>
<li class="ant-menu-overflow-item ant-menu-overflow-item-rest ant-menu-submenu ant-menu-submenu-horizontal" style="opacity: 0; height: 0px; overflow-y: hidden; order: 9007199254740991; pointer-events: none; position: absolute;" aria-hidden="true" role="none" data-submenu-id="$$__vc-menu-more__key">
<li class="ant-menu-overflow-item ant-menu-overflow-item ant-menu-overflow-item-rest ant-menu-submenu ant-menu-submenu-horizontal" style="opacity: 0; height: 0px; overflow-y: hidden; order: 9007199254740991; pointer-events: none; position: absolute;" aria-hidden="true" role="none" data-submenu-id="$$__vc-menu-more__key">
<!---->
<div class="ant-menu-submenu-title" tabindex="-1" data-menu-id="$$__vc-menu-more__key" aria-expanded="false" aria-haspopup="true" aria-controls="$$__vc-menu-more__key-popup" aria-disabled="false"><span class="ant-menu-title-content"><span role="img" aria-label="ellipsis" class="anticon anticon-ellipsis"><svg focusable="false" class="" data-icon="ellipsis" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M176 511a56 56 0 10112 0 56 56 0 10-112 0zm280 0a56 56 0 10112 0 56 56 0 10-112 0zm280 0a56 56 0 10112 0 56 56 0 10-112 0z"></path></svg></span></span><i class="ant-menu-submenu-arrow"></i></div>
<!---->
@ -317,7 +317,7 @@ exports[`renders ./components/layout/demo/top.vue correctly 1`] = `
<li class="ant-menu-overflow-item ant-menu-item ant-menu-item-only-child" style="opacity: 1; order: 2;" role="menuitem" tabindex="-1" data-menu-id="3" aria-disabled="false">
<!----><span class="ant-menu-title-content">nav 3</span>
</li>
<li class="ant-menu-overflow-item ant-menu-overflow-item-rest ant-menu-submenu ant-menu-submenu-horizontal" style="opacity: 0; height: 0px; overflow-y: hidden; order: 9007199254740991; pointer-events: none; position: absolute;" aria-hidden="true" role="none" data-submenu-id="$$__vc-menu-more__key">
<li class="ant-menu-overflow-item ant-menu-overflow-item ant-menu-overflow-item-rest ant-menu-submenu ant-menu-submenu-horizontal" style="opacity: 0; height: 0px; overflow-y: hidden; order: 9007199254740991; pointer-events: none; position: absolute;" aria-hidden="true" role="none" data-submenu-id="$$__vc-menu-more__key">
<!---->
<div class="ant-menu-submenu-title" tabindex="-1" data-menu-id="$$__vc-menu-more__key" aria-expanded="false" aria-haspopup="true" aria-controls="$$__vc-menu-more__key-popup" aria-disabled="false"><span class="ant-menu-title-content"><span role="img" aria-label="ellipsis" class="anticon anticon-ellipsis"><svg focusable="false" class="" data-icon="ellipsis" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M176 511a56 56 0 10112 0 56 56 0 10-112 0zm280 0a56 56 0 10112 0 56 56 0 10-112 0zm280 0a56 56 0 10112 0 56 56 0 10-112 0z"></path></svg></span></span><i class="ant-menu-submenu-arrow"></i></div>
<!---->
@ -356,7 +356,7 @@ exports[`renders ./components/layout/demo/top-side.vue correctly 1`] = `
<li class="ant-menu-overflow-item ant-menu-item ant-menu-item-only-child" style="opacity: 1; order: 2;" role="menuitem" tabindex="-1" data-menu-id="3" aria-disabled="false">
<!----><span class="ant-menu-title-content">nav 3</span>
</li>
<li class="ant-menu-overflow-item ant-menu-overflow-item-rest ant-menu-submenu ant-menu-submenu-horizontal" style="opacity: 0; height: 0px; overflow-y: hidden; order: 9007199254740991; pointer-events: none; position: absolute;" aria-hidden="true" role="none" data-submenu-id="$$__vc-menu-more__key">
<li class="ant-menu-overflow-item ant-menu-overflow-item ant-menu-overflow-item-rest ant-menu-submenu ant-menu-submenu-horizontal" style="opacity: 0; height: 0px; overflow-y: hidden; order: 9007199254740991; pointer-events: none; position: absolute;" aria-hidden="true" role="none" data-submenu-id="$$__vc-menu-more__key">
<!---->
<div class="ant-menu-submenu-title" tabindex="-1" data-menu-id="$$__vc-menu-more__key" aria-expanded="false" aria-haspopup="true" aria-controls="$$__vc-menu-more__key-popup" aria-disabled="false"><span class="ant-menu-title-content"><span role="img" aria-label="ellipsis" class="anticon anticon-ellipsis"><svg focusable="false" class="" data-icon="ellipsis" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M176 511a56 56 0 10112 0 56 56 0 10-112 0zm280 0a56 56 0 10112 0 56 56 0 10-112 0zm280 0a56 56 0 10112 0 56 56 0 10-112 0z"></path></svg></span></span><i class="ant-menu-submenu-arrow"></i></div>
<!---->
@ -486,7 +486,7 @@ exports[`renders ./components/layout/demo/top-side-2.vue correctly 1`] = `
<li class="ant-menu-overflow-item ant-menu-item ant-menu-item-only-child" style="opacity: 1; order: 2;" role="menuitem" tabindex="-1" data-menu-id="3" aria-disabled="false">
<!----><span class="ant-menu-title-content">nav 3</span>
</li>
<li class="ant-menu-overflow-item ant-menu-overflow-item-rest ant-menu-submenu ant-menu-submenu-horizontal" style="opacity: 0; height: 0px; overflow-y: hidden; order: 9007199254740991; pointer-events: none; position: absolute;" aria-hidden="true" role="none" data-submenu-id="$$__vc-menu-more__key">
<li class="ant-menu-overflow-item ant-menu-overflow-item ant-menu-overflow-item-rest ant-menu-submenu ant-menu-submenu-horizontal" style="opacity: 0; height: 0px; overflow-y: hidden; order: 9007199254740991; pointer-events: none; position: absolute;" aria-hidden="true" role="none" data-submenu-id="$$__vc-menu-more__key">
<!---->
<div class="ant-menu-submenu-title" tabindex="-1" data-menu-id="$$__vc-menu-more__key" aria-expanded="false" aria-haspopup="true" aria-controls="$$__vc-menu-more__key-popup" aria-disabled="false"><span class="ant-menu-title-content"><span role="img" aria-label="ellipsis" class="anticon anticon-ellipsis"><svg focusable="false" class="" data-icon="ellipsis" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M176 511a56 56 0 10112 0 56 56 0 10-112 0zm280 0a56 56 0 10112 0 56 56 0 10-112 0zm280 0a56 56 0 10112 0 56 56 0 10-112 0z"></path></svg></span></span><i class="ant-menu-submenu-arrow"></i></div>
<!---->

View File

@ -15,7 +15,7 @@ exports[`renders ./components/menu/demo/horizontal.vue correctly 1`] = `
<li class="ant-menu-overflow-item ant-menu-item ant-menu-item-only-child" style="opacity: 1; order: 3;" role="menuitem" tabindex="-1" data-menu-id="alipay" aria-disabled="false">
<!----><span class="ant-menu-title-content"><a href="https://antdv.com" target="_blank" rel="noopener noreferrer"> Navigation Four - Link </a></span>
</li>
<li class="ant-menu-overflow-item ant-menu-overflow-item-rest ant-menu-submenu ant-menu-submenu-horizontal" style="opacity: 0; height: 0px; overflow-y: hidden; order: 9007199254740991; pointer-events: none; position: absolute;" aria-hidden="true" role="none" data-submenu-id="$$__vc-menu-more__key">
<li class="ant-menu-overflow-item ant-menu-overflow-item ant-menu-overflow-item-rest ant-menu-submenu ant-menu-submenu-horizontal" style="opacity: 0; height: 0px; overflow-y: hidden; order: 9007199254740991; pointer-events: none; position: absolute;" aria-hidden="true" role="none" data-submenu-id="$$__vc-menu-more__key">
<!---->
<div class="ant-menu-submenu-title" tabindex="-1" data-menu-id="$$__vc-menu-more__key" aria-expanded="false" aria-haspopup="true" aria-controls="$$__vc-menu-more__key-popup" aria-disabled="false"><span class="ant-menu-title-content"><span role="img" aria-label="ellipsis" class="anticon anticon-ellipsis"><svg focusable="false" class="" data-icon="ellipsis" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M176 511a56 56 0 10112 0 56 56 0 10-112 0zm280 0a56 56 0 10112 0 56 56 0 10-112 0zm280 0a56 56 0 10112 0 56 56 0 10-112 0z"></path></svg></span></span><i class="ant-menu-submenu-arrow"></i></div>
<!---->

View File

@ -236,7 +236,7 @@ exports[`renders ./components/select/demo/option-label-prop.vue correctly 1`] =
<!---->
<div class="ant-select-selector">
<div class="ant-select-selection-overflow">
<div class="ant-select-selection-overflow-item" style="opacity: 1;"><span class="ant-select-selection-item" title="China"><span class="ant-select-selection-item-content">China</span><span class="ant-select-selection-item-remove" style="user-select: none;" unselectable="on" aria-hidden="true"><span role="img" aria-label="close" class="anticon anticon-close"><svg focusable="false" class="" data-icon="close" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M563.8 512l262.5-312.9c4.4-5.2.7-13.1-6.1-13.1h-79.8c-4.7 0-9.2 2.1-12.3 5.7L511.6 449.8 295.1 191.7c-3-3.6-7.5-5.7-12.3-5.7H203c-6.8 0-10.5 7.9-6.1 13.1L459.4 512 196.9 824.9A7.95 7.95 0 00203 838h79.8c4.7 0 9.2-2.1 12.3-5.7l216.5-258.1 216.5 258.1c3 3.6 7.5 5.7 12.3 5.7h79.8c6.8 0 10.5-7.9 6.1-13.1L563.8 512z"></path></svg></span></span></span></div>
<div class="ant-select-selection-overflow-item" style="opacity: 1;"><span class="ant-select-selection-item"><span class="ant-select-selection-item-content"><span role="img" aria-label="China">🇨🇳</span> &nbsp;&nbsp;China (中国) </span><span class="ant-select-selection-item-remove" style="user-select: none;" unselectable="on" aria-hidden="true"><span role="img" aria-label="close" class="anticon anticon-close"><svg focusable="false" class="" data-icon="close" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M563.8 512l262.5-312.9c4.4-5.2.7-13.1-6.1-13.1h-79.8c-4.7 0-9.2 2.1-12.3 5.7L511.6 449.8 295.1 191.7c-3-3.6-7.5-5.7-12.3-5.7H203c-6.8 0-10.5 7.9-6.1 13.1L459.4 512 196.9 824.9A7.95 7.95 0 00203 838h79.8c4.7 0 9.2-2.1 12.3-5.7l216.5-258.1 216.5 258.1c3 3.6 7.5 5.7 12.3 5.7h79.8c6.8 0 10.5-7.9 6.1-13.1L563.8 512z"></path></svg></span></span></span></div>
<!---->
<div class="ant-select-selection-overflow-item ant-select-selection-overflow-item-suffix" style="opacity: 1;">
<div class="ant-select-selection-search" style="width: 0px;"><input id="rc_select_TEST_OR_SSR" autocomplete="off" class="ant-select-selection-search-input" style="opacity: 0;" role="combobox" aria-haspopup="listbox" aria-owns="rc_select_TEST_OR_SSR_list" aria-autocomplete="list" aria-controls="rc_select_TEST_OR_SSR_list" aria-activedescendant="rc_select_TEST_OR_SSR_list_0" readonly="" unselectable="on" type="search"><span class="ant-select-selection-search-mirror" aria-hidden="true">&nbsp;</span></div>
@ -271,6 +271,31 @@ exports[`renders ./components/select/demo/option-label-prop.vue correctly 1`] =
<div class="ant-space-item"><span>Note: v-slot:option support from v2.2.5</span></div>
<!---->
</div>
<br>
<br>
<div class="ant-space ant-space-vertical" style="width: 100%;">
<div class="ant-space-item" style="margin-bottom: 8px;">
<div style="width: 100%;" class="ant-select ant-select-multiple ant-select-show-search">
<!---->
<!---->
<div class="ant-select-selector">
<div class="ant-select-selection-overflow">
<div class="ant-select-selection-overflow-item" style="opacity: 1;"><span><span class="ant-tag" style="margin-right: 3px;">China (中国)&nbsp;&nbsp; <span role="img" aria-label="china">🇨🇳</span><span tabindex="-1" role="img" aria-label="close" class="anticon anticon-close ant-tag-close-icon"><svg focusable="false" class="" data-icon="close" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M563.8 512l262.5-312.9c4.4-5.2.7-13.1-6.1-13.1h-79.8c-4.7 0-9.2 2.1-12.3 5.7L511.6 449.8 295.1 191.7c-3-3.6-7.5-5.7-12.3-5.7H203c-6.8 0-10.5 7.9-6.1 13.1L459.4 512 196.9 824.9A7.95 7.95 0 00203 838h79.8c4.7 0 9.2-2.1 12.3-5.7l216.5-258.1 216.5 258.1c3 3.6 7.5 5.7 12.3 5.7h79.8c6.8 0 10.5-7.9 6.1-13.1L563.8 512z"></path></svg></span></span></span></div>
<!---->
<div class="ant-select-selection-overflow-item ant-select-selection-overflow-item-suffix" style="opacity: 1;">
<div class="ant-select-selection-search" style="width: 0px;"><input id="rc_select_TEST_OR_SSR" autocomplete="off" class="ant-select-selection-search-input" style="opacity: 0;" role="combobox" aria-haspopup="listbox" aria-owns="rc_select_TEST_OR_SSR_list" aria-autocomplete="list" aria-controls="rc_select_TEST_OR_SSR_list" aria-activedescendant="rc_select_TEST_OR_SSR_list_0" readonly="" unselectable="on" type="search"><span class="ant-select-selection-search-mirror" aria-hidden="true">&nbsp;</span></div>
</div>
</div>
<!---->
</div>
<!---->
<!---->
</div>
</div>
<!---->
<div class="ant-space-item"><span>Note: v-slot:tagRender support from v3.0</span></div>
<!---->
</div>
`;
exports[`renders ./components/select/demo/responsive.vue correctly 1`] = `
@ -300,7 +325,7 @@ exports[`renders ./components/select/demo/responsive.vue correctly 1`] = `
<div class="ant-select-selection-overflow">
<div class="ant-select-selection-overflow-item" style="opacity: 1;"><span class="ant-select-selection-item" title="Long Label: a10"><span class="ant-select-selection-item-content">Long Label: a10</span><span class="ant-select-selection-item-remove" style="user-select: none;" unselectable="on" aria-hidden="true"><span role="img" aria-label="close" class="anticon anticon-close"><svg focusable="false" class="" data-icon="close" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M563.8 512l262.5-312.9c4.4-5.2.7-13.1-6.1-13.1h-79.8c-4.7 0-9.2 2.1-12.3 5.7L511.6 449.8 295.1 191.7c-3-3.6-7.5-5.7-12.3-5.7H203c-6.8 0-10.5 7.9-6.1 13.1L459.4 512 196.9 824.9A7.95 7.95 0 00203 838h79.8c4.7 0 9.2-2.1 12.3-5.7l216.5-258.1 216.5 258.1c3 3.6 7.5 5.7 12.3 5.7h79.8c6.8 0 10.5-7.9 6.1-13.1L563.8 512z"></path></svg></span></span></span></div>
<div class="ant-select-selection-overflow-item" style="opacity: 1;"><span class="ant-select-selection-item" title="Long Label: c12"><span class="ant-select-selection-item-content">Long Label: c12</span><span class="ant-select-selection-item-remove" style="user-select: none;" unselectable="on" aria-hidden="true"><span role="img" aria-label="close" class="anticon anticon-close"><svg focusable="false" class="" data-icon="close" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M563.8 512l262.5-312.9c4.4-5.2.7-13.1-6.1-13.1h-79.8c-4.7 0-9.2 2.1-12.3 5.7L511.6 449.8 295.1 191.7c-3-3.6-7.5-5.7-12.3-5.7H203c-6.8 0-10.5 7.9-6.1 13.1L459.4 512 196.9 824.9A7.95 7.95 0 00203 838h79.8c4.7 0 9.2-2.1 12.3-5.7l216.5-258.1 216.5 258.1c3 3.6 7.5 5.7 12.3 5.7h79.8c6.8 0 10.5-7.9 6.1-13.1L563.8 512z"></path></svg></span></span></span></div>
<div class="ant-select-selection-overflow-item-rest" style="opacity: 1;"><span class="ant-select-selection-item" title="+ 3 ..."><span class="ant-select-selection-item-content">+ 3 ...</span>
<div class="ant-select-selection-overflow-item ant-select-selection-overflow-item-rest" style="opacity: 1;"><span class="ant-select-selection-item"><span class="ant-select-selection-item-content"><span style="color: red;">+ 3 ...</span></span>
<!----></span>
</div>
<div class="ant-select-selection-overflow-item ant-select-selection-overflow-item-suffix" style="opacity: 1;">
@ -324,7 +349,7 @@ exports[`renders ./components/select/demo/responsive.vue correctly 1`] = `
<!---->
<div class="ant-select-selector">
<div class="ant-select-selection-overflow">
<div class="ant-select-selection-overflow-item-rest" style="opacity: 0; height: 0px; overflow-y: hidden; order: 9007199254740991; pointer-events: none; position: absolute;" aria-hidden="true"><span class="ant-select-selection-item" title="+ 4 ..."><span class="ant-select-selection-item-content">+ 4 ...</span>
<div class="ant-select-selection-overflow-item ant-select-selection-overflow-item-rest" style="opacity: 0; height: 0px; overflow-y: hidden; order: 9007199254740991; pointer-events: none; position: absolute;" aria-hidden="true"><span class="ant-select-selection-item" title="+ 4 ..."><span class="ant-select-selection-item-content">+ 4 ...</span>
<!----></span>
</div>
<div class="ant-select-selection-overflow-item ant-select-selection-overflow-item-suffix" style="opacity: 1; order: 0;">
@ -361,11 +386,11 @@ exports[`renders ./components/select/demo/responsive.vue correctly 1`] = `
<!---->
<div class="ant-select-selector">
<div class="ant-select-selection-overflow">
<div class="ant-select-selection-overflow-item" style="opacity: 1;"><span class="ant-select-selection-item" title="Long Label..."><span class="ant-select-selection-item-content">Long Label...</span><span class="ant-select-selection-item-remove" style="user-select: none;" unselectable="on" aria-hidden="true"><span role="img" aria-label="close" class="anticon anticon-close"><svg focusable="false" class="" data-icon="close" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M563.8 512l262.5-312.9c4.4-5.2.7-13.1-6.1-13.1h-79.8c-4.7 0-9.2 2.1-12.3 5.7L511.6 449.8 295.1 191.7c-3-3.6-7.5-5.7-12.3-5.7H203c-6.8 0-10.5 7.9-6.1 13.1L459.4 512 196.9 824.9A7.95 7.95 0 00203 838h79.8c4.7 0 9.2-2.1 12.3-5.7l216.5-258.1 216.5 258.1c3 3.6 7.5 5.7 12.3 5.7h79.8c6.8 0 10.5-7.9 6.1-13.1L563.8 512z"></path></svg></span></span></span></div>
<div class="ant-select-selection-overflow-item" style="opacity: 1;"><span class="ant-select-selection-item" title="Long Label..."><span class="ant-select-selection-item-content">Long Label...</span><span class="ant-select-selection-item-remove" style="user-select: none;" unselectable="on" aria-hidden="true"><span role="img" aria-label="close" class="anticon anticon-close"><svg focusable="false" class="" data-icon="close" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M563.8 512l262.5-312.9c4.4-5.2.7-13.1-6.1-13.1h-79.8c-4.7 0-9.2 2.1-12.3 5.7L511.6 449.8 295.1 191.7c-3-3.6-7.5-5.7-12.3-5.7H203c-6.8 0-10.5 7.9-6.1 13.1L459.4 512 196.9 824.9A7.95 7.95 0 00203 838h79.8c4.7 0 9.2-2.1 12.3-5.7l216.5-258.1 216.5 258.1c3 3.6 7.5 5.7 12.3 5.7h79.8c6.8 0 10.5-7.9 6.1-13.1L563.8 512z"></path></svg></span></span></span></div>
<div class="ant-select-selection-overflow-item" style="opacity: 1;"><span class="ant-select-selection-item" title="Long Label..."><span class="ant-select-selection-item-content">Long Label...</span><span class="ant-select-selection-item-remove" style="user-select: none;" unselectable="on" aria-hidden="true"><span role="img" aria-label="close" class="anticon anticon-close"><svg focusable="false" class="" data-icon="close" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M563.8 512l262.5-312.9c4.4-5.2.7-13.1-6.1-13.1h-79.8c-4.7 0-9.2 2.1-12.3 5.7L511.6 449.8 295.1 191.7c-3-3.6-7.5-5.7-12.3-5.7H203c-6.8 0-10.5 7.9-6.1 13.1L459.4 512 196.9 824.9A7.95 7.95 0 00203 838h79.8c4.7 0 9.2-2.1 12.3-5.7l216.5-258.1 216.5 258.1c3 3.6 7.5 5.7 12.3 5.7h79.8c6.8 0 10.5-7.9 6.1-13.1L563.8 512z"></path></svg></span></span></span></div>
<div class="ant-select-selection-overflow-item" style="opacity: 1;"><span class="ant-select-selection-item" title="Long Label..."><span class="ant-select-selection-item-content">Long Label...</span><span class="ant-select-selection-item-remove" style="user-select: none;" unselectable="on" aria-hidden="true"><span role="img" aria-label="close" class="anticon anticon-close"><svg focusable="false" class="" data-icon="close" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M563.8 512l262.5-312.9c4.4-5.2.7-13.1-6.1-13.1h-79.8c-4.7 0-9.2 2.1-12.3 5.7L511.6 449.8 295.1 191.7c-3-3.6-7.5-5.7-12.3-5.7H203c-6.8 0-10.5 7.9-6.1 13.1L459.4 512 196.9 824.9A7.95 7.95 0 00203 838h79.8c4.7 0 9.2-2.1 12.3-5.7l216.5-258.1 216.5 258.1c3 3.6 7.5 5.7 12.3 5.7h79.8c6.8 0 10.5-7.9 6.1-13.1L563.8 512z"></path></svg></span></span></span></div>
<div class="ant-select-selection-overflow-item" style="opacity: 1;"><span class="ant-select-selection-item" title="Long Label..."><span class="ant-select-selection-item-content">Long Label...</span><span class="ant-select-selection-item-remove" style="user-select: none;" unselectable="on" aria-hidden="true"><span role="img" aria-label="close" class="anticon anticon-close"><svg focusable="false" class="" data-icon="close" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M563.8 512l262.5-312.9c4.4-5.2.7-13.1-6.1-13.1h-79.8c-4.7 0-9.2 2.1-12.3 5.7L511.6 449.8 295.1 191.7c-3-3.6-7.5-5.7-12.3-5.7H203c-6.8 0-10.5 7.9-6.1 13.1L459.4 512 196.9 824.9A7.95 7.95 0 00203 838h79.8c4.7 0 9.2-2.1 12.3-5.7l216.5-258.1 216.5 258.1c3 3.6 7.5 5.7 12.3 5.7h79.8c6.8 0 10.5-7.9 6.1-13.1L563.8 512z"></path></svg></span></span></span></div>
<div class="ant-select-selection-overflow-item" style="opacity: 1;"><span class="ant-select-selection-item" title="Long Label: a10"><span class="ant-select-selection-item-content">Long Label...</span><span class="ant-select-selection-item-remove" style="user-select: none;" unselectable="on" aria-hidden="true"><span role="img" aria-label="close" class="anticon anticon-close"><svg focusable="false" class="" data-icon="close" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M563.8 512l262.5-312.9c4.4-5.2.7-13.1-6.1-13.1h-79.8c-4.7 0-9.2 2.1-12.3 5.7L511.6 449.8 295.1 191.7c-3-3.6-7.5-5.7-12.3-5.7H203c-6.8 0-10.5 7.9-6.1 13.1L459.4 512 196.9 824.9A7.95 7.95 0 00203 838h79.8c4.7 0 9.2-2.1 12.3-5.7l216.5-258.1 216.5 258.1c3 3.6 7.5 5.7 12.3 5.7h79.8c6.8 0 10.5-7.9 6.1-13.1L563.8 512z"></path></svg></span></span></span></div>
<div class="ant-select-selection-overflow-item" style="opacity: 1;"><span class="ant-select-selection-item" title="Long Label: c12"><span class="ant-select-selection-item-content">Long Label...</span><span class="ant-select-selection-item-remove" style="user-select: none;" unselectable="on" aria-hidden="true"><span role="img" aria-label="close" class="anticon anticon-close"><svg focusable="false" class="" data-icon="close" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M563.8 512l262.5-312.9c4.4-5.2.7-13.1-6.1-13.1h-79.8c-4.7 0-9.2 2.1-12.3 5.7L511.6 449.8 295.1 191.7c-3-3.6-7.5-5.7-12.3-5.7H203c-6.8 0-10.5 7.9-6.1 13.1L459.4 512 196.9 824.9A7.95 7.95 0 00203 838h79.8c4.7 0 9.2-2.1 12.3-5.7l216.5-258.1 216.5 258.1c3 3.6 7.5 5.7 12.3 5.7h79.8c6.8 0 10.5-7.9 6.1-13.1L563.8 512z"></path></svg></span></span></span></div>
<div class="ant-select-selection-overflow-item" style="opacity: 1;"><span class="ant-select-selection-item" title="Long Label: h17"><span class="ant-select-selection-item-content">Long Label...</span><span class="ant-select-selection-item-remove" style="user-select: none;" unselectable="on" aria-hidden="true"><span role="img" aria-label="close" class="anticon anticon-close"><svg focusable="false" class="" data-icon="close" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M563.8 512l262.5-312.9c4.4-5.2.7-13.1-6.1-13.1h-79.8c-4.7 0-9.2 2.1-12.3 5.7L511.6 449.8 295.1 191.7c-3-3.6-7.5-5.7-12.3-5.7H203c-6.8 0-10.5 7.9-6.1 13.1L459.4 512 196.9 824.9A7.95 7.95 0 00203 838h79.8c4.7 0 9.2-2.1 12.3-5.7l216.5-258.1 216.5 258.1c3 3.6 7.5 5.7 12.3 5.7h79.8c6.8 0 10.5-7.9 6.1-13.1L563.8 512z"></path></svg></span></span></span></div>
<div class="ant-select-selection-overflow-item" style="opacity: 1;"><span class="ant-select-selection-item" title="Long Label: j19"><span class="ant-select-selection-item-content">Long Label...</span><span class="ant-select-selection-item-remove" style="user-select: none;" unselectable="on" aria-hidden="true"><span role="img" aria-label="close" class="anticon anticon-close"><svg focusable="false" class="" data-icon="close" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M563.8 512l262.5-312.9c4.4-5.2.7-13.1-6.1-13.1h-79.8c-4.7 0-9.2 2.1-12.3 5.7L511.6 449.8 295.1 191.7c-3-3.6-7.5-5.7-12.3-5.7H203c-6.8 0-10.5 7.9-6.1 13.1L459.4 512 196.9 824.9A7.95 7.95 0 00203 838h79.8c4.7 0 9.2-2.1 12.3-5.7l216.5-258.1 216.5 258.1c3 3.6 7.5 5.7 12.3 5.7h79.8c6.8 0 10.5-7.9 6.1-13.1L563.8 512z"></path></svg></span></span></span></div>
<div class="ant-select-selection-overflow-item" style="opacity: 1;"><span class="ant-select-selection-item" title="Long Label: k20"><span class="ant-select-selection-item-content">Long Label...</span><span class="ant-select-selection-item-remove" style="user-select: none;" unselectable="on" aria-hidden="true"><span role="img" aria-label="close" class="anticon anticon-close"><svg focusable="false" class="" data-icon="close" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M563.8 512l262.5-312.9c4.4-5.2.7-13.1-6.1-13.1h-79.8c-4.7 0-9.2 2.1-12.3 5.7L511.6 449.8 295.1 191.7c-3-3.6-7.5-5.7-12.3-5.7H203c-6.8 0-10.5 7.9-6.1 13.1L459.4 512 196.9 824.9A7.95 7.95 0 00203 838h79.8c4.7 0 9.2-2.1 12.3-5.7l216.5-258.1 216.5 258.1c3 3.6 7.5 5.7 12.3 5.7h79.8c6.8 0 10.5-7.9 6.1-13.1L563.8 512z"></path></svg></span></span></span></div>
<!---->
<div class="ant-select-selection-overflow-item ant-select-selection-overflow-item-suffix" style="opacity: 1;">
<div class="ant-select-selection-search" style="width: 0px;"><input id="rc_select_TEST_OR_SSR" autocomplete="off" class="ant-select-selection-search-input" style="opacity: 0;" role="combobox" aria-haspopup="listbox" aria-owns="rc_select_TEST_OR_SSR_list" aria-autocomplete="list" aria-controls="rc_select_TEST_OR_SSR_list" aria-activedescendant="rc_select_TEST_OR_SSR_list_0" readonly="" unselectable="on" type="search"><span class="ant-select-selection-search-mirror" aria-hidden="true">&nbsp;</span></div>
@ -422,7 +447,7 @@ exports[`renders ./components/select/demo/select-users.vue correctly 1`] = `
`;
exports[`renders ./components/select/demo/size.vue correctly 1`] = `
<div class="ant-radio-group ant-radio-group-outline ant-radio-group-default"><label class="ant-radio-button-wrapper"><span class="ant-radio-button"><input type="radio" class="ant-radio-button-input" value="large"><span class="ant-radio-button-inner"></span></span><span>Large</span></label><label class="ant-radio-button-wrapper ant-radio-button-wrapper-checked"><span class="ant-radio-button ant-radio-button-checked"><input type="radio" class="ant-radio-button-input" value="default"><span class="ant-radio-button-inner"></span></span><span>Default</span></label><label class="ant-radio-button-wrapper"><span class="ant-radio-button"><input type="radio" class="ant-radio-button-input" value="small"><span class="ant-radio-button-inner"></span></span><span>Small</span></label></div>
<div class="ant-radio-group ant-radio-group-outline ant-radio-group-default"><label class="ant-radio-button-wrapper"><span class="ant-radio-button"><input type="radio" class="ant-radio-button-input" value="large"><span class="ant-radio-button-inner"></span></span><span>Large</span></label><label class="ant-radio-button-wrapper ant-radio-button-wrapper-checked"><span class="ant-radio-button ant-radio-button-checked"><input type="radio" class="ant-radio-button-input" value="middle"><span class="ant-radio-button-inner"></span></span><span>Middle</span></label><label class="ant-radio-button-wrapper"><span class="ant-radio-button"><input type="radio" class="ant-radio-button-input" value="small"><span class="ant-radio-button-inner"></span></span><span>Small</span></label></div>
<br>
<br>
<div class="ant-space ant-space-vertical">

View File

@ -10,10 +10,13 @@ title:
使用 `optionLabelProp` 指定回填到选择框的 `Option` 属性
或者使用 `tagRender` 插槽自定义渲染节点
## en-US
Spacified the prop name of Option which will be rendered in select box.
or use `tagRender` slot for custom rendering of tags.
</docs>
<template>
@ -23,7 +26,7 @@ Spacified the prop name of Option which will be rendered in select box.
mode="multiple"
style="width: 100%"
placeholder="select one country"
option-label-prop="label"
option-label-prop="children"
>
<a-select-option value="china" label="China">
<span role="img" aria-label="China">🇨🇳</span>
@ -58,6 +61,29 @@ Spacified the prop name of Option which will be rendered in select box.
</a-select>
<span>Note: v-slot:option support from v2.2.5</span>
</a-space>
<br />
<br />
<a-space direction="vertical" style="width: 100%">
<a-select
v-model:value="value"
mode="multiple"
style="width: 100%"
placeholder="select one country"
:options="options"
>
<template #option="{ value: val, label, icon }">
<span role="img" :aria-label="val">{{ icon }}</span>
&nbsp;&nbsp;{{ label }}
</template>
<template #tagRender="{ value: val, label, closable, onClose, option }">
<a-tag :closable="closable" style="margin-right: 3px" @close="onClose">
{{ label }}&nbsp;&nbsp;
<span role="img" :aria-label="val">{{ option.icon }}</span>
</a-tag>
</template>
</a-select>
<span>Note: v-slot:tagRender support from v3.0</span>
</a-space>
</template>
<script lang="ts">
import { defineComponent, ref, watch } from 'vue';
@ -91,7 +117,6 @@ export default defineComponent({
watch(value, val => {
console.log(`selected:`, val);
});
return {
options,
value,

View File

@ -34,7 +34,11 @@ Set a number and automatically fold after exceeding.
placeholder="Select Item..."
:max-tag-count="maxTagCount"
:options="options"
></a-select>
>
<template #maxTagPlaceholder="omittedValues">
<span style="color: red">+ {{ omittedValues.length }} ...</span>
</template>
</a-select>
<h2>maxTagCount: responsive</h2>
<a-select
v-model:value="value"

View File

@ -19,7 +19,7 @@ The height of the input field for the select defaults to 32px. If size is set to
<template>
<a-radio-group v-model:value="size">
<a-radio-button value="large">Large</a-radio-button>
<a-radio-button value="default">Default</a-radio-button>
<a-radio-button value="middle">Middle</a-radio-button>
<a-radio-button value="small">Small</a-radio-button>
</a-radio-group>
<br />
@ -61,7 +61,7 @@ export default defineComponent({
return {
popupScroll,
size: ref<SelectProps['size']>('default'),
size: ref<SelectProps['size']>('middle'),
value1: ref('a1'),
value2: ref(['a1', 'b2']),
value3: ref(['a1', 'b2']),

View File

@ -55,6 +55,7 @@ Select component to select value from options.
| clearIcon | The custom clear icon | VNode \| slot | - | |
| menuItemSelectedIcon | The custom menuItemSelected icon | VNode \| slot | - | |
| tokenSeparators | Separator used to tokenize on tag/multiple mode | string\[] | | |
| tagRender | Customize tag render, only applies when `mode` is set to `multiple` or `tags` | slot \| (props) => any | - | |
| value(v-model) | Current selected option. | string\|number\|string\[]\|number\[] | - | |
| options | Data of the selectOption, manual construction work is no longer needed if this property has been set | array&lt;{value, label, [disabled, key, title]}> | \[] | |
| option | custom render option by slot | v-slot:option="{value, label, [disabled, key, title]}" | - | 2.2.5 |

View File

@ -1,21 +1,23 @@
import type { App, PropType, Plugin, ExtractPropTypes } from 'vue';
import { computed, defineComponent, ref } from 'vue';
import classNames from '../_util/classNames';
import type { BaseSelectRef } from '../vc-select';
import RcSelect, { selectProps as vcSelectProps, Option, OptGroup } from '../vc-select';
import type { OptionProps as OptionPropsType } from '../vc-select/Option';
import type { BaseOptionType, DefaultOptionType } from '../vc-select/Select';
import type { OptionProps } from '../vc-select/Option';
import getIcons from './utils/iconUtil';
import PropTypes from '../_util/vue-types';
import { tuple } from '../_util/type';
import useConfigInject from '../_util/hooks/useConfigInject';
import omit from '../_util/omit';
import { useInjectFormItemContext } from '../form/FormItemContext';
import { getTransitionName } from '../_util/transition';
import type { SizeType } from '../config-provider';
import { initDefaultProps } from '../_util/props-util';
type RawValue = string | number;
export type OptionProps = OptionPropsType;
export type OptionType = typeof Option;
export type { OptionProps, BaseSelectRef as RefSelectProps, BaseOptionType, DefaultOptionType };
export interface LabeledValue {
key?: string;
@ -35,23 +37,28 @@ export const selectProps = () => ({
notFoundContent: PropTypes.any,
suffixIcon: PropTypes.any,
itemIcon: PropTypes.any,
size: PropTypes.oneOf(tuple('small', 'middle', 'large', 'default')),
mode: PropTypes.oneOf(tuple('multiple', 'tags', 'SECRET_COMBOBOX_MODE_DO_NOT_USE')),
bordered: PropTypes.looseBool.def(true),
transitionName: PropTypes.string,
choiceTransitionName: PropTypes.string.def(''),
size: String as PropType<SizeType>,
mode: String as PropType<'multiple' | 'tags' | 'SECRET_COMBOBOX_MODE_DO_NOT_USE'>,
bordered: { type: Boolean, default: true },
transitionName: String,
choiceTransitionName: { type: String, default: '' },
'onUpdate:value': Function as PropType<(val: SelectValue) => void>,
});
export type SelectProps = Partial<ExtractPropTypes<ReturnType<typeof selectProps>>>;
const SECRET_COMBOBOX_MODE_DO_NOT_USE = 'SECRET_COMBOBOX_MODE_DO_NOT_USE';
const Select = defineComponent({
name: 'ASelect',
Option,
OptGroup,
inheritAttrs: false,
props: selectProps(),
SECRET_COMBOBOX_MODE_DO_NOT_USE: 'SECRET_COMBOBOX_MODE_DO_NOT_USE',
emits: ['change', 'update:value', 'blur'],
props: initDefaultProps(selectProps(), {
listHeight: 256,
listItemHeight: 24,
}),
SECRET_COMBOBOX_MODE_DO_NOT_USE,
// emits: ['change', 'update:value', 'blur'],
slots: [
'notFoundContent',
'suffixIcon',
@ -61,20 +68,22 @@ const Select = defineComponent({
'dropdownRender',
'option',
'placeholder',
'tagRender',
'maxTagPlaceholder',
],
setup(props, { attrs, emit, slots, expose }) {
const selectRef = ref();
const selectRef = ref<BaseSelectRef>();
const formItemContext = useInjectFormItemContext();
const focus = () => {
if (selectRef.value) {
selectRef.value.focus();
}
selectRef.value?.focus();
};
const blur = () => {
if (selectRef.value) {
selectRef.value.blur();
}
selectRef.value?.blur();
};
const scrollTo: BaseSelectRef['scrollTo'] = arg => {
selectRef.value?.scrollTo(arg);
};
const mode = computed(() => {
@ -84,7 +93,7 @@ const Select = defineComponent({
return undefined;
}
if (mode === Select.SECRET_COMBOBOX_MODE_DO_NOT_USE) {
if (mode === SECRET_COMBOBOX_MODE_DO_NOT_USE) {
return 'combobox';
}
@ -103,19 +112,21 @@ const Select = defineComponent({
[`${prefixCls.value}-borderless`]: !props.bordered,
}),
);
const triggerChange = (...args: any[]) => {
const triggerChange: SelectProps['onChange'] = (...args) => {
emit('update:value', args[0]);
emit('change', ...args);
formItemContext.onFieldChange();
};
const handleBlur = (e: InputEvent) => {
const handleBlur: SelectProps['onBlur'] = e => {
emit('blur', e);
formItemContext.onFieldBlur();
};
expose({
blur,
focus,
scrollTo,
});
const isMultiple = computed(() => mode.value === 'multiple' || mode.value === 'tags');
return () => {
const {
notFoundContent,
@ -131,8 +142,6 @@ const Select = defineComponent({
const { renderEmpty, getPopupContainer: getContextPopupContainer } = configProvider;
const isMultiple = mode.value === 'multiple' || mode.value === 'tags';
// ===================== Empty =====================
let mergedNotFound: any;
if (notFoundContent !== undefined) {
@ -149,7 +158,7 @@ const Select = defineComponent({
const { suffixIcon, itemIcon, removeIcon, clearIcon } = getIcons(
{
...props,
multiple: isMultiple,
multiple: isMultiple.value,
prefixCls: prefixCls.value,
},
slots,
@ -195,9 +204,10 @@ const Select = defineComponent({
dropdownRender={selectProps.dropdownRender || slots.dropdownRender}
v-slots={{ option: slots.option }}
transitionName={transitionName.value}
>
{slots.default?.()}
</RcSelect>
children={slots.default?.()}
tagRender={props.tagRender || slots.tagRender}
maxTagPlaceholder={props.maxTagPlaceholder || slots.maxTagPlaceholder}
></RcSelect>
);
};
},

View File

@ -56,6 +56,7 @@ cover: https://gw.alipayobjects.com/zos/alicdn/_0XzgOis7/Select.svg
| clearIcon | 自定义的多选框清空图标 | VNode \| slot | - | |
| menuItemSelectedIcon | 自定义当前选中的条目图标 | VNode \| slot | - | |
| tokenSeparators | 在 tags 和 multiple 模式下自动分词的分隔符 | string\[] | | |
| tagRender | 自定义 tag 内容 render仅在 `mode``multiple``tags` 时生效 | slot \| (props) => any | - | 3.0 |
| value(v-model) | 指定当前选中的条目 | string\|string\[]\|number\|number\[] | - | |
| options | options 数据,如果设置则不需要手动构造 selectOption 节点 | array&lt;{value, label, [disabled, key, title]}> | \[] | |
| option | 通过 option 插槽,自定义节点 | v-slot:option="{value, label, [disabled, key, title]}" | - | 2.2.5 |

View File

@ -1,7 +1,6 @@
@import '../../style/themes/index';
@import '../../style/mixins/index';
@import '../../input/style/mixin';
@import './single';
@import './multiple';
@ -59,6 +58,7 @@
&::-webkit-search-cancel-button {
display: none;
/* stylelint-disable-next-line property-no-vendor-prefix */
-webkit-appearance: none;
}
}
@ -83,6 +83,7 @@
&-selection-item {
flex: 1;
overflow: hidden;
font-weight: normal;
white-space: nowrap;
text-overflow: ellipsis;
@ -117,7 +118,7 @@
&-arrow {
.iconfont-mixin();
position: absolute;
top: 53%;
top: 50%;
right: @control-padding-horizontal - 1px;
width: @font-size-sm;
height: @font-size-sm;
@ -167,9 +168,11 @@
opacity: 0;
transition: color 0.3s ease, opacity 0.15s ease;
text-rendering: auto;
&::before {
display: block;
}
&:hover {
color: @text-color-secondary;
}
@ -286,6 +289,9 @@
}
&-disabled {
&.@{select-prefix-cls}-item-option-selected {
background-color: @select-multiple-disabled-background;
}
color: @disabled-color;
cursor: not-allowed;
}

View File

@ -1,4 +1,5 @@
@import './index';
@import (reference) '../../style/themes/index';
@select-prefix-cls: ~'@{ant-prefix}-select';
@select-overflow-prefix-cls: ~'@{select-prefix-cls}-selection-overflow';
@select-multiple-item-border-width: 1px;
@ -128,8 +129,6 @@
.@{select-prefix-cls}-selection-search {
position: relative;
max-width: 100%;
margin-top: @select-multiple-item-spacing-half;
margin-bottom: @select-multiple-item-spacing-half;
margin-inline-start: @input-padding-horizontal-base - @input-padding-vertical-base;
&-input,

View File

@ -1,4 +1,5 @@
@import './index';
@import (reference) '../../style/themes/index';
@select-prefix-cls: ~'@{ant-prefix}-select';
@selection-item-padding: ceil(@font-size-base * 1.25);
@ -39,14 +40,15 @@
}
.@{select-prefix-cls}-selection-placeholder {
transition: none;
pointer-events: none;
}
// For common baseline align
&::after,
// For '' value baseline align
/* For '' value baseline align */
.@{select-prefix-cls}-selection-item::after,
// For undefined value baseline align
/* For undefined value baseline align */
.@{select-prefix-cls}-selection-placeholder::after {
display: inline-block;
width: 0;

View File

@ -940,19 +940,24 @@ exports[`renders ./components/transfer/demo/tree-transfer.vue correctly 1`] = `
<div class="ant-tree-list-holder">
<div>
<div style="display: flex; flex-direction: column;" class="ant-tree-list-holder-inner">
<div class="ant-tree-treenode ant-tree-treenode-switcher-open"><span aria-hidden="true" class="ant-tree-indent"></span><span class="ant-tree-switcher ant-tree-switcher-noop"><!----></span><span class="ant-tree-checkbox"><span class="ant-tree-checkbox-inner"></span></span><span title="0-0" class="ant-tree-node-content-wrapper ant-tree-node-content-wrapper-normal"><!----><span class="ant-tree-title">0-0</span>
<div class="ant-tree-treenode ant-tree-treenode-switcher-open" draggable="false" aria-grabbed="false"><span aria-hidden="true" class="ant-tree-indent"></span>
<!----><span class="ant-tree-switcher ant-tree-switcher-noop"><!----></span><span class="ant-tree-checkbox"><span class="ant-tree-checkbox-inner"></span></span><span title="0-0" class="ant-tree-node-content-wrapper ant-tree-node-content-wrapper-normal"><!----><span class="ant-tree-title">0-0</span>
<!----></span>
</div>
<div class="ant-tree-treenode ant-tree-treenode-switcher-open"><span aria-hidden="true" class="ant-tree-indent"></span><span class="ant-tree-switcher ant-tree-switcher_open"><span role="img" aria-label="caret-down" class="anticon anticon-caret-down ant-tree-switcher-icon"><svg focusable="false" class="" data-icon="caret-down" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="0 0 1024 1024"><path d="M840.4 300H183.6c-19.7 0-30.7 20.8-18.5 35l328.4 380.8c9.4 10.9 27.5 10.9 37 0L858.9 335c12.2-14.2 1.2-35-18.5-35z"></path></svg></span></span><span class="ant-tree-checkbox"><span class="ant-tree-checkbox-inner"></span></span><span title="0-1" class="ant-tree-node-content-wrapper ant-tree-node-content-wrapper-open"><!----><span class="ant-tree-title">0-1</span>
<div class="ant-tree-treenode ant-tree-treenode-switcher-open" draggable="false" aria-grabbed="false"><span aria-hidden="true" class="ant-tree-indent"></span>
<!----><span class="ant-tree-switcher ant-tree-switcher_open"><span role="img" aria-label="caret-down" class="anticon anticon-caret-down ant-tree-switcher-icon"><svg focusable="false" class="" data-icon="caret-down" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="0 0 1024 1024"><path d="M840.4 300H183.6c-19.7 0-30.7 20.8-18.5 35l328.4 380.8c9.4 10.9 27.5 10.9 37 0L858.9 335c12.2-14.2 1.2-35-18.5-35z"></path></svg></span></span><span class="ant-tree-checkbox"><span class="ant-tree-checkbox-inner"></span></span><span title="0-1" class="ant-tree-node-content-wrapper ant-tree-node-content-wrapper-open"><!----><span class="ant-tree-title">0-1</span>
<!----></span>
</div>
<div class="ant-tree-treenode ant-tree-treenode-switcher-open"><span aria-hidden="true" class="ant-tree-indent"><span class="ant-tree-indent-unit"></span></span><span class="ant-tree-switcher ant-tree-switcher-noop"><!----></span><span class="ant-tree-checkbox"><span class="ant-tree-checkbox-inner"></span></span><span title="0-1-0" class="ant-tree-node-content-wrapper ant-tree-node-content-wrapper-normal"><!----><span class="ant-tree-title">0-1-0</span>
<div class="ant-tree-treenode ant-tree-treenode-switcher-open" draggable="false" aria-grabbed="false"><span aria-hidden="true" class="ant-tree-indent"><span class="ant-tree-indent-unit"></span></span>
<!----><span class="ant-tree-switcher ant-tree-switcher-noop"><!----></span><span class="ant-tree-checkbox"><span class="ant-tree-checkbox-inner"></span></span><span title="0-1-0" class="ant-tree-node-content-wrapper ant-tree-node-content-wrapper-normal"><!----><span class="ant-tree-title">0-1-0</span>
<!----></span>
</div>
<div class="ant-tree-treenode ant-tree-treenode-switcher-open ant-tree-treenode-leaf-last"><span aria-hidden="true" class="ant-tree-indent"><span class="ant-tree-indent-unit"></span></span><span class="ant-tree-switcher ant-tree-switcher-noop"><!----></span><span class="ant-tree-checkbox"><span class="ant-tree-checkbox-inner"></span></span><span title="0-1-1" class="ant-tree-node-content-wrapper ant-tree-node-content-wrapper-normal"><!----><span class="ant-tree-title">0-1-1</span>
<div class="ant-tree-treenode ant-tree-treenode-switcher-open ant-tree-treenode-leaf-last" draggable="false" aria-grabbed="false"><span aria-hidden="true" class="ant-tree-indent"><span class="ant-tree-indent-unit"></span></span>
<!----><span class="ant-tree-switcher ant-tree-switcher-noop"><!----></span><span class="ant-tree-checkbox"><span class="ant-tree-checkbox-inner"></span></span><span title="0-1-1" class="ant-tree-node-content-wrapper ant-tree-node-content-wrapper-normal"><!----><span class="ant-tree-title">0-1-1</span>
<!----></span>
</div>
<div class="ant-tree-treenode ant-tree-treenode-switcher-open ant-tree-treenode-leaf-last"><span aria-hidden="true" class="ant-tree-indent"></span><span class="ant-tree-switcher ant-tree-switcher-noop"><!----></span><span class="ant-tree-checkbox"><span class="ant-tree-checkbox-inner"></span></span><span title="0-3" class="ant-tree-node-content-wrapper ant-tree-node-content-wrapper-normal"><!----><span class="ant-tree-title">0-3</span>
<div class="ant-tree-treenode ant-tree-treenode-switcher-open ant-tree-treenode-leaf-last" draggable="false" aria-grabbed="false"><span aria-hidden="true" class="ant-tree-indent"></span>
<!----><span class="ant-tree-switcher ant-tree-switcher-noop"><!----></span><span class="ant-tree-checkbox"><span class="ant-tree-checkbox-inner"></span></span><span title="0-3" class="ant-tree-node-content-wrapper ant-tree-node-content-wrapper-normal"><!----><span class="ant-tree-title">0-3</span>
<!----></span>
</div>
</div>

View File

@ -1,10 +1,10 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`renders ./components/tree-select/demo/async.vue correctly 1`] = `
<div style="width: 100%;" class="ant-select ant-tree-select ant-select-single ant-select-show-arrow">
<div style="width: 100%;" class="ant-select ant-tree-select ant-select-single ant-select-show-arrow" customslots="[object Object]">
<!---->
<!---->
<div class="ant-select-selector"><span class="ant-select-selection-search"><input id="rc_select_TEST_OR_SSR" autocomplete="off" class="ant-select-selection-search-input" style="opacity: 0;" role="combobox" aria-haspopup="listbox" aria-owns="rc_select_TEST_OR_SSR_list" aria-autocomplete="list" aria-controls="rc_select_TEST_OR_SSR_list" aria-activedescendant="rc_select_TEST_OR_SSR_list_0" readonly="" unselectable="on" type="search"></span>
<div class="ant-select-selector"><span class="ant-select-selection-search"><input id="rc_select_TEST_OR_SSR" autocomplete="off" class="ant-select-selection-search-input" style="opacity: 0;" role="combobox" aria-haspopup="listbox" aria-owns="rc_select_TEST_OR_SSR_list" aria-autocomplete="list" aria-controls="rc_select_TEST_OR_SSR_list" readonly="" unselectable="on" type="search"></span>
<!----><span class="ant-select-selection-placeholder">Please select</span>
</div><span class="ant-select-arrow" style="user-select: none;" unselectable="on" aria-hidden="true"><span role="img" aria-label="down" class="anticon anticon-down ant-select-suffix"><svg focusable="false" class="" data-icon="down" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M884 256h-75c-5.1 0-9.9 2.5-12.9 6.6L512 654.2 227.9 262.6c-3-4.1-7.8-6.6-12.9-6.6h-75c-6.5 0-10.3 7.4-6.5 12.7l352.6 486.1c12.8 17.6 39 17.6 51.7 0l352.6-486.1c3.9-5.3.1-12.7-6.4-12.7z"></path></svg></span></span>
<!---->
@ -12,10 +12,10 @@ exports[`renders ./components/tree-select/demo/async.vue correctly 1`] = `
`;
exports[`renders ./components/tree-select/demo/basic.vue correctly 1`] = `
<div style="width: 100%;" class="ant-select ant-tree-select ant-select-single ant-select-allow-clear ant-select-show-arrow ant-select-show-search">
<div style="width: 100%;" class="ant-select ant-tree-select ant-select-single ant-select-allow-clear ant-select-show-arrow ant-select-show-search" customslots="[object Object]">
<!---->
<!---->
<div class="ant-select-selector"><span class="ant-select-selection-search"><input id="rc_select_TEST_OR_SSR" autocomplete="off" class="ant-select-selection-search-input" role="combobox" aria-haspopup="listbox" aria-owns="rc_select_TEST_OR_SSR_list" aria-autocomplete="list" aria-controls="rc_select_TEST_OR_SSR_list" aria-activedescendant="rc_select_TEST_OR_SSR_list_0" type="search"></span>
<div class="ant-select-selector"><span class="ant-select-selection-search"><input id="rc_select_TEST_OR_SSR" autocomplete="off" class="ant-select-selection-search-input" role="combobox" aria-haspopup="listbox" aria-owns="rc_select_TEST_OR_SSR_list" aria-autocomplete="list" aria-controls="rc_select_TEST_OR_SSR_list" type="search"></span>
<!----><span class="ant-select-selection-placeholder">Please select</span>
</div><span class="ant-select-arrow" style="user-select: none;" unselectable="on" aria-hidden="true"><span role="img" aria-label="down" class="anticon anticon-down ant-select-suffix"><svg focusable="false" class="" data-icon="down" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M884 256h-75c-5.1 0-9.9 2.5-12.9 6.6L512 654.2 227.9 262.6c-3-4.1-7.8-6.6-12.9-6.6h-75c-6.5 0-10.3 7.4-6.5 12.7l352.6 486.1c12.8 17.6 39 17.6 51.7 0l352.6-486.1c3.9-5.3.1-12.7-6.4-12.7z"></path></svg></span></span>
<!---->
@ -23,7 +23,7 @@ exports[`renders ./components/tree-select/demo/basic.vue correctly 1`] = `
`;
exports[`renders ./components/tree-select/demo/checkable.vue correctly 1`] = `
<div style="width: 100%;" class="ant-select ant-tree-select ant-select-multiple ant-select-allow-clear ant-select-show-search">
<div style="width: 100%;" class="ant-select ant-tree-select ant-select-multiple ant-select-allow-clear ant-select-show-search" customslots="[object Object]">
<!---->
<!---->
<div class="ant-select-selector">
@ -31,7 +31,27 @@ exports[`renders ./components/tree-select/demo/checkable.vue correctly 1`] = `
<div class="ant-select-selection-overflow-item" style="opacity: 1;"><span class="ant-select-selection-item" title="Node1"><span class="ant-select-selection-item-content">Node1</span><span class="ant-select-selection-item-remove" style="user-select: none;" unselectable="on" aria-hidden="true"><span role="img" aria-label="close" class="anticon anticon-close"><svg focusable="false" class="" data-icon="close" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M563.8 512l262.5-312.9c4.4-5.2.7-13.1-6.1-13.1h-79.8c-4.7 0-9.2 2.1-12.3 5.7L511.6 449.8 295.1 191.7c-3-3.6-7.5-5.7-12.3-5.7H203c-6.8 0-10.5 7.9-6.1 13.1L459.4 512 196.9 824.9A7.95 7.95 0 00203 838h79.8c4.7 0 9.2-2.1 12.3-5.7l216.5-258.1 216.5 258.1c3 3.6 7.5 5.7 12.3 5.7h79.8c6.8 0 10.5-7.9 6.1-13.1L563.8 512z"></path></svg></span></span></span></div>
<!---->
<div class="ant-select-selection-overflow-item ant-select-selection-overflow-item-suffix" style="opacity: 1;">
<div class="ant-select-selection-search" style="width: 0px;"><input id="rc_select_TEST_OR_SSR" autocomplete="off" class="ant-select-selection-search-input" style="opacity: 0;" role="combobox" aria-haspopup="listbox" aria-owns="rc_select_TEST_OR_SSR_list" aria-autocomplete="list" aria-controls="rc_select_TEST_OR_SSR_list" aria-activedescendant="rc_select_TEST_OR_SSR_list_0" readonly="" unselectable="on" type="search"><span class="ant-select-selection-search-mirror" aria-hidden="true">&nbsp;</span></div>
<div class="ant-select-selection-search" style="width: 0px;"><input id="rc_select_TEST_OR_SSR" autocomplete="off" class="ant-select-selection-search-input" style="opacity: 0;" role="combobox" aria-haspopup="listbox" aria-owns="rc_select_TEST_OR_SSR_list" aria-autocomplete="list" aria-controls="rc_select_TEST_OR_SSR_list" readonly="" unselectable="on" type="search"><span class="ant-select-selection-search-mirror" aria-hidden="true">&nbsp;</span></div>
</div>
</div>
<!---->
</div>
<!----><span class="ant-select-clear" style="user-select: none;" unselectable="on" aria-hidden="true"><span role="img" aria-label="close-circle" class="anticon anticon-close-circle"><svg focusable="false" class="" data-icon="close-circle" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm165.4 618.2l-66-.3L512 563.4l-99.3 118.4-66.1.3c-4.4 0-8-3.5-8-8 0-1.9.7-3.7 1.9-5.2l130.1-155L340.5 359a8.32 8.32 0 01-1.9-5.2c0-4.4 3.6-8 8-8l66.1.3L512 464.6l99.3-118.4 66-.3c4.4 0 8 3.5 8 8 0 1.9-.7 3.7-1.9 5.2L553.5 514l130 155c1.2 1.5 1.9 3.3 1.9 5.2 0 4.4-3.6 8-8 8z"></path></svg></span></span>
</div>
`;
exports[`renders ./components/tree-select/demo/custom-tag-render.vue correctly 1`] = `
<div style="width: 100%;" class="ant-select ant-tree-select ant-select-multiple ant-select-allow-clear ant-select-show-search" customslots="[object Object]">
<!---->
<!---->
<div class="ant-select-selector">
<div class="ant-select-selection-overflow">
<div class="ant-select-selection-overflow-item" style="opacity: 1;"><span><span class="ant-tag ant-tag-pink" style="margin-right: 3px;">parent 1&nbsp;&nbsp; <span tabindex="-1" role="img" aria-label="close" class="anticon anticon-close ant-tag-close-icon"><svg focusable="false" class="" data-icon="close" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M563.8 512l262.5-312.9c4.4-5.2.7-13.1-6.1-13.1h-79.8c-4.7 0-9.2 2.1-12.3 5.7L511.6 449.8 295.1 191.7c-3-3.6-7.5-5.7-12.3-5.7H203c-6.8 0-10.5 7.9-6.1 13.1L459.4 512 196.9 824.9A7.95 7.95 0 00203 838h79.8c4.7 0 9.2-2.1 12.3-5.7l216.5-258.1 216.5 258.1c3 3.6 7.5 5.7 12.3 5.7h79.8c6.8 0 10.5-7.9 6.1-13.1L563.8 512z"></path></svg></span></span></span></div>
<div class="ant-select-selection-overflow-item" style="opacity: 1;"><span><span class="ant-tag ant-tag-orange" style="margin-right: 3px;">parent 1-0&nbsp;&nbsp; <span tabindex="-1" role="img" aria-label="close" class="anticon anticon-close ant-tag-close-icon"><svg focusable="false" class="" data-icon="close" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M563.8 512l262.5-312.9c4.4-5.2.7-13.1-6.1-13.1h-79.8c-4.7 0-9.2 2.1-12.3 5.7L511.6 449.8 295.1 191.7c-3-3.6-7.5-5.7-12.3-5.7H203c-6.8 0-10.5 7.9-6.1 13.1L459.4 512 196.9 824.9A7.95 7.95 0 00203 838h79.8c4.7 0 9.2-2.1 12.3-5.7l216.5-258.1 216.5 258.1c3 3.6 7.5 5.7 12.3 5.7h79.8c6.8 0 10.5-7.9 6.1-13.1L563.8 512z"></path></svg></span></span></span></div>
<div class="ant-select-selection-overflow-item" style="opacity: 1;"><span><span class="ant-tag ant-tag-green" style="margin-right: 3px;">my leaf&nbsp;&nbsp; <span tabindex="-1" role="img" aria-label="close" class="anticon anticon-close ant-tag-close-icon"><svg focusable="false" class="" data-icon="close" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M563.8 512l262.5-312.9c4.4-5.2.7-13.1-6.1-13.1h-79.8c-4.7 0-9.2 2.1-12.3 5.7L511.6 449.8 295.1 191.7c-3-3.6-7.5-5.7-12.3-5.7H203c-6.8 0-10.5 7.9-6.1 13.1L459.4 512 196.9 824.9A7.95 7.95 0 00203 838h79.8c4.7 0 9.2-2.1 12.3-5.7l216.5-258.1 216.5 258.1c3 3.6 7.5 5.7 12.3 5.7h79.8c6.8 0 10.5-7.9 6.1-13.1L563.8 512z"></path></svg></span></span></span></div>
<!---->
<div class="ant-select-selection-overflow-item ant-select-selection-overflow-item-suffix" style="opacity: 1;">
<div class="ant-select-selection-search" style="width: 0px;"><input id="rc_select_TEST_OR_SSR" autocomplete="off" class="ant-select-selection-search-input" style="opacity: 0;" role="combobox" aria-haspopup="listbox" aria-owns="rc_select_TEST_OR_SSR_list" aria-autocomplete="list" aria-controls="rc_select_TEST_OR_SSR_list" readonly="" unselectable="on" type="search"><span class="ant-select-selection-search-mirror" aria-hidden="true">&nbsp;</span></div>
</div>
</div>
<!---->
@ -41,10 +61,10 @@ exports[`renders ./components/tree-select/demo/checkable.vue correctly 1`] = `
`;
exports[`renders ./components/tree-select/demo/highlight.vue correctly 1`] = `
<div style="width: 100%;" class="ant-select ant-tree-select ant-select-single ant-select-allow-clear ant-select-show-arrow ant-select-show-search">
<div style="width: 100%;" class="ant-select ant-tree-select ant-select-single ant-select-allow-clear ant-select-show-arrow ant-select-show-search" customslots="[object Object]">
<!---->
<!---->
<div class="ant-select-selector"><span class="ant-select-selection-search"><input id="rc_select_TEST_OR_SSR" autocomplete="off" class="ant-select-selection-search-input" role="combobox" aria-haspopup="listbox" aria-owns="rc_select_TEST_OR_SSR_list" aria-autocomplete="list" aria-controls="rc_select_TEST_OR_SSR_list" aria-activedescendant="rc_select_TEST_OR_SSR_list_0" type="search"></span>
<div class="ant-select-selector"><span class="ant-select-selection-search"><input id="rc_select_TEST_OR_SSR" autocomplete="off" class="ant-select-selection-search-input" role="combobox" aria-haspopup="listbox" aria-owns="rc_select_TEST_OR_SSR_list" aria-autocomplete="list" aria-controls="rc_select_TEST_OR_SSR_list" type="search"></span>
<!----><span class="ant-select-selection-placeholder">Please select</span>
</div><span class="ant-select-arrow" style="user-select: none;" unselectable="on" aria-hidden="true"><span role="img" aria-label="down" class="anticon anticon-down ant-select-suffix"><svg focusable="false" class="" data-icon="down" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M884 256h-75c-5.1 0-9.9 2.5-12.9 6.6L512 654.2 227.9 262.6c-3-4.1-7.8-6.6-12.9-6.6h-75c-6.5 0-10.3 7.4-6.5 12.7l352.6 486.1c12.8 17.6 39 17.6 51.7 0l352.6-486.1c3.9-5.3.1-12.7-6.4-12.7z"></path></svg></span></span>
<!---->
@ -52,14 +72,14 @@ exports[`renders ./components/tree-select/demo/highlight.vue correctly 1`] = `
`;
exports[`renders ./components/tree-select/demo/multiple.vue correctly 1`] = `
<div style="width: 100%;" class="ant-select ant-tree-select ant-select-multiple ant-select-allow-clear ant-select-show-search" multiple="true">
<div style="width: 100%;" class="ant-select ant-tree-select ant-select-multiple ant-select-allow-clear ant-select-show-search" customslots="[object Object]">
<!---->
<!---->
<div class="ant-select-selector">
<div class="ant-select-selection-overflow">
<!---->
<div class="ant-select-selection-overflow-item ant-select-selection-overflow-item-suffix" style="opacity: 1;">
<div class="ant-select-selection-search" style="width: 0px;"><input id="rc_select_TEST_OR_SSR" autocomplete="off" class="ant-select-selection-search-input" style="opacity: 0;" role="combobox" aria-haspopup="listbox" aria-owns="rc_select_TEST_OR_SSR_list" aria-autocomplete="list" aria-controls="rc_select_TEST_OR_SSR_list" aria-activedescendant="rc_select_TEST_OR_SSR_list_0" readonly="" unselectable="on" type="search"><span class="ant-select-selection-search-mirror" aria-hidden="true">&nbsp;</span></div>
<div class="ant-select-selection-search" style="width: 0px;"><input id="rc_select_TEST_OR_SSR" autocomplete="off" class="ant-select-selection-search-input" style="opacity: 0;" role="combobox" aria-haspopup="listbox" aria-owns="rc_select_TEST_OR_SSR_list" aria-autocomplete="list" aria-controls="rc_select_TEST_OR_SSR_list" readonly="" unselectable="on" type="search"><span class="ant-select-selection-search-mirror" aria-hidden="true">&nbsp;</span></div>
</div>
</div><span class="ant-select-selection-placeholder">Please select</span>
</div>
@ -71,10 +91,10 @@ exports[`renders ./components/tree-select/demo/multiple.vue correctly 1`] = `
exports[`renders ./components/tree-select/demo/suffix.vue correctly 1`] = `
<div class="ant-space ant-space-vertical" style="width: 100%;">
<div class="ant-space-item" style="margin-bottom: 8px;">
<div style="width: 100%;" class="ant-select ant-tree-select ant-select-single ant-select-allow-clear ant-select-show-arrow ant-select-show-search">
<div style="width: 100%;" class="ant-select ant-tree-select ant-select-single ant-select-allow-clear ant-select-show-arrow ant-select-show-search" customslots="[object Object]">
<!---->
<!---->
<div class="ant-select-selector"><span class="ant-select-selection-search"><input id="rc_select_TEST_OR_SSR" autocomplete="off" class="ant-select-selection-search-input" role="combobox" aria-haspopup="listbox" aria-owns="rc_select_TEST_OR_SSR_list" aria-autocomplete="list" aria-controls="rc_select_TEST_OR_SSR_list" aria-activedescendant="rc_select_TEST_OR_SSR_list_0" type="search"></span>
<div class="ant-select-selector"><span class="ant-select-selection-search"><input id="rc_select_TEST_OR_SSR" autocomplete="off" class="ant-select-selection-search-input" role="combobox" aria-haspopup="listbox" aria-owns="rc_select_TEST_OR_SSR_list" aria-autocomplete="list" aria-controls="rc_select_TEST_OR_SSR_list" type="search"></span>
<!----><span class="ant-select-selection-placeholder">Please select</span>
</div><span class="ant-select-arrow" style="user-select: none;" unselectable="on" aria-hidden="true"><span role="img" aria-label="smile" class="anticon anticon-smile"><svg focusable="false" class="" data-icon="smile" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M288 421a48 48 0 1096 0 48 48 0 10-96 0zm352 0a48 48 0 1096 0 48 48 0 10-96 0zM512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm263 711c-34.2 34.2-74 61-118.3 79.8C611 874.2 562.3 884 512 884c-50.3 0-99-9.8-144.8-29.2A370.4 370.4 0 01248.9 775c-34.2-34.2-61-74-79.8-118.3C149.8 611 140 562.3 140 512s9.8-99 29.2-144.8A370.4 370.4 0 01249 248.9c34.2-34.2 74-61 118.3-79.8C413 149.8 461.7 140 512 140c50.3 0 99 9.8 144.8 29.2A370.4 370.4 0 01775.1 249c34.2 34.2 61 74 79.8 118.3C874.2 413 884 461.7 884 512s-9.8 99-29.2 144.8A368.89 368.89 0 01775 775zM664 533h-48.1c-4.2 0-7.8 3.2-8.1 7.4C604 589.9 562.5 629 512 629s-92.1-39.1-95.8-88.6c-.3-4.2-3.9-7.4-8.1-7.4H360a8 8 0 00-8 8.4c4.4 84.3 74.5 151.6 160 151.6s155.6-67.3 160-151.6a8 8 0 00-8-8.4z"></path></svg></span></span>
<!---->
@ -82,14 +102,14 @@ exports[`renders ./components/tree-select/demo/suffix.vue correctly 1`] = `
</div>
<!---->
<div class="ant-space-item">
<div style="width: 100%;" class="ant-select ant-tree-select ant-select-multiple ant-select-allow-clear ant-select-show-arrow ant-select-show-search" multiple="true">
<div style="width: 100%;" class="ant-select ant-tree-select ant-select-multiple ant-select-allow-clear ant-select-show-arrow ant-select-show-search" customslots="[object Object]">
<!---->
<!---->
<div class="ant-select-selector">
<div class="ant-select-selection-overflow">
<!---->
<div class="ant-select-selection-overflow-item ant-select-selection-overflow-item-suffix" style="opacity: 1;">
<div class="ant-select-selection-search" style="width: 0px;"><input id="rc_select_TEST_OR_SSR" autocomplete="off" class="ant-select-selection-search-input" style="opacity: 0;" role="combobox" aria-haspopup="listbox" aria-owns="rc_select_TEST_OR_SSR_list" aria-autocomplete="list" aria-controls="rc_select_TEST_OR_SSR_list" aria-activedescendant="rc_select_TEST_OR_SSR_list_0" readonly="" unselectable="on" type="search"><span class="ant-select-selection-search-mirror" aria-hidden="true">&nbsp;</span></div>
<div class="ant-select-selection-search" style="width: 0px;"><input id="rc_select_TEST_OR_SSR" autocomplete="off" class="ant-select-selection-search-input" style="opacity: 0;" role="combobox" aria-haspopup="listbox" aria-owns="rc_select_TEST_OR_SSR_list" aria-autocomplete="list" aria-controls="rc_select_TEST_OR_SSR_list" readonly="" unselectable="on" type="search"><span class="ant-select-selection-search-mirror" aria-hidden="true">&nbsp;</span></div>
</div>
</div><span class="ant-select-selection-placeholder">Please select</span>
</div><span class="ant-select-arrow" style="user-select: none;" unselectable="on" aria-hidden="true"><span role="img" aria-label="smile" class="anticon anticon-smile"><svg focusable="false" class="" data-icon="smile" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M288 421a48 48 0 1096 0 48 48 0 10-96 0zm352 0a48 48 0 1096 0 48 48 0 10-96 0zM512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm263 711c-34.2 34.2-74 61-118.3 79.8C611 874.2 562.3 884 512 884c-50.3 0-99-9.8-144.8-29.2A370.4 370.4 0 01248.9 775c-34.2-34.2-61-74-79.8-118.3C149.8 611 140 562.3 140 512s9.8-99 29.2-144.8A370.4 370.4 0 01249 248.9c34.2-34.2 74-61 118.3-79.8C413 149.8 461.7 140 512 140c50.3 0 99 9.8 144.8 29.2A370.4 370.4 0 01775.1 249c34.2 34.2 61 74 79.8 118.3C874.2 413 884 461.7 884 512s-9.8 99-29.2 144.8A368.89 368.89 0 01775 775zM664 533h-48.1c-4.2 0-7.8 3.2-8.1 7.4C604 589.9 562.5 629 512 629s-92.1-39.1-95.8-88.6c-.3-4.2-3.9-7.4-8.1-7.4H360a8 8 0 00-8 8.4c4.4 84.3 74.5 151.6 160 151.6s155.6-67.3 160-151.6a8 8 0 00-8-8.4z"></path></svg></span></span>
@ -100,13 +120,46 @@ exports[`renders ./components/tree-select/demo/suffix.vue correctly 1`] = `
</div>
`;
exports[`renders ./components/tree-select/demo/treeData.vue correctly 1`] = `
<div style="width: 100%;" class="ant-select ant-tree-select ant-select-single ant-select-show-arrow">
exports[`renders ./components/tree-select/demo/tree-line.vue correctly 1`] = `
<div class="ant-space ant-space-vertical">
<div class="ant-space-item" style="margin-bottom: 8px;"><button type="button" role="switch" aria-checked="true" class="ant-switch-checked ant-switch">
<!----><span class="ant-switch-inner">treeLine</span>
</button></div>
<!---->
<div class="ant-space-item" style="margin-bottom: 8px;"><button type="button" role="switch" aria-checked="false" class="ant-switch">
<!----><span class="ant-switch-inner">showLeafIcon</span>
</button></div>
<!---->
<div class="ant-space-item">
<div style="width: 300px;" class="ant-select ant-tree-select ant-select-single ant-select-show-arrow" customslots="[object Object]">
<!---->
<!---->
<div class="ant-select-selector"><span class="ant-select-selection-search"><input id="rc_select_TEST_OR_SSR" autocomplete="off" class="ant-select-selection-search-input" style="opacity: 0;" role="combobox" aria-haspopup="listbox" aria-owns="rc_select_TEST_OR_SSR_list" aria-autocomplete="list" aria-controls="rc_select_TEST_OR_SSR_list" readonly="" unselectable="on" type="search"></span>
<!----><span class="ant-select-selection-placeholder">Please select</span>
</div><span class="ant-select-arrow" style="user-select: none;" unselectable="on" aria-hidden="true"><span role="img" aria-label="down" class="anticon anticon-down ant-select-suffix"><svg focusable="false" class="" data-icon="down" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M884 256h-75c-5.1 0-9.9 2.5-12.9 6.6L512 654.2 227.9 262.6c-3-4.1-7.8-6.6-12.9-6.6h-75c-6.5 0-10.3 7.4-6.5 12.7l352.6 486.1c12.8 17.6 39 17.6 51.7 0l352.6-486.1c3.9-5.3.1-12.7-6.4-12.7z"></path></svg></span></span>
<!---->
</div>
</div>
<!---->
</div>
`;
exports[`renders ./components/tree-select/demo/virtual-scroll.vue correctly 1`] = `
<div style="width: 100%;" height="233" class="ant-select ant-tree-select ant-select-multiple ant-select-show-search" customslots="[object Object]">
<!---->
<!---->
<div class="ant-select-selector">
<div class="ant-select-selection-overflow">
<div class="ant-select-selection-overflow-item" style="opacity: 1;"><span class="ant-select-selection-item" title="0-0-0"><span class="ant-select-selection-item-content">0-0-0</span><span class="ant-select-selection-item-remove" style="user-select: none;" unselectable="on" aria-hidden="true"><span role="img" aria-label="close" class="anticon anticon-close"><svg focusable="false" class="" data-icon="close" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M563.8 512l262.5-312.9c4.4-5.2.7-13.1-6.1-13.1h-79.8c-4.7 0-9.2 2.1-12.3 5.7L511.6 449.8 295.1 191.7c-3-3.6-7.5-5.7-12.3-5.7H203c-6.8 0-10.5 7.9-6.1 13.1L459.4 512 196.9 824.9A7.95 7.95 0 00203 838h79.8c4.7 0 9.2-2.1 12.3-5.7l216.5-258.1 216.5 258.1c3 3.6 7.5 5.7 12.3 5.7h79.8c6.8 0 10.5-7.9 6.1-13.1L563.8 512z"></path></svg></span></span></span></div>
<div class="ant-select-selection-overflow-item" style="opacity: 1;"><span class="ant-select-selection-item" title="0-0-1"><span class="ant-select-selection-item-content">0-0-1</span><span class="ant-select-selection-item-remove" style="user-select: none;" unselectable="on" aria-hidden="true"><span role="img" aria-label="close" class="anticon anticon-close"><svg focusable="false" class="" data-icon="close" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M563.8 512l262.5-312.9c4.4-5.2.7-13.1-6.1-13.1h-79.8c-4.7 0-9.2 2.1-12.3 5.7L511.6 449.8 295.1 191.7c-3-3.6-7.5-5.7-12.3-5.7H203c-6.8 0-10.5 7.9-6.1 13.1L459.4 512 196.9 824.9A7.95 7.95 0 00203 838h79.8c4.7 0 9.2-2.1 12.3-5.7l216.5-258.1 216.5 258.1c3 3.6 7.5 5.7 12.3 5.7h79.8c6.8 0 10.5-7.9 6.1-13.1L563.8 512z"></path></svg></span></span></span></div>
<!---->
<div class="ant-select-selection-overflow-item ant-select-selection-overflow-item-suffix" style="opacity: 1;">
<div class="ant-select-selection-search" style="width: 0px;"><input id="rc_select_TEST_OR_SSR" autocomplete="off" class="ant-select-selection-search-input" style="opacity: 0;" role="combobox" aria-haspopup="listbox" aria-owns="rc_select_TEST_OR_SSR_list" aria-autocomplete="list" aria-controls="rc_select_TEST_OR_SSR_list" readonly="" unselectable="on" type="search"><span class="ant-select-selection-search-mirror" aria-hidden="true">&nbsp;</span></div>
</div>
</div>
<!---->
</div>
<!---->
<div class="ant-select-selector"><span class="ant-select-selection-search"><input id="rc_select_TEST_OR_SSR" autocomplete="off" class="ant-select-selection-search-input" style="opacity: 0;" role="combobox" aria-haspopup="listbox" aria-owns="rc_select_TEST_OR_SSR_list" aria-autocomplete="list" aria-controls="rc_select_TEST_OR_SSR_list" aria-activedescendant="rc_select_TEST_OR_SSR_list_0" readonly="" unselectable="on" type="search"></span>
<!----><span class="ant-select-selection-placeholder">Please select</span>
</div><span class="ant-select-arrow" style="user-select: none;" unselectable="on" aria-hidden="true"><span role="img" aria-label="down" class="anticon anticon-down ant-select-suffix"><svg focusable="false" class="" data-icon="down" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M884 256h-75c-5.1 0-9.9 2.5-12.9 6.6L512 654.2 227.9 262.6c-3-4.1-7.8-6.6-12.9-6.6h-75c-6.5 0-10.3 7.4-6.5 12.7l352.6 486.1c12.8 17.6 39 17.6 51.7 0l352.6-486.1c3.9-5.3.1-12.7-6.4-12.7z"></path></svg></span></span>
<!---->
</div>
`;

View File

@ -27,9 +27,9 @@ Multiple and checkable.
/>
</template>
<script lang="ts">
import { TreeSelect } from 'ant-design-vue';
import type { TreeSelectProps } from 'ant-design-vue';
import { defineComponent, ref, watch } from 'vue';
import { TreeSelect } from 'ant-design-vue';
const SHOW_PARENT = TreeSelect.SHOW_PARENT;
const treeData: TreeSelectProps['treeData'] = [

View File

@ -0,0 +1,93 @@
<docs>
---
order: 24
title:
zh-CN: 自定义选择标签
en-US: Custom Tag Render
---
## zh-CN
允许自定义选择标签的样式仅支持多选模式单选可直接使用 `title` 插槽
## en-US
Allows for custom rendering of tags.
</docs>
<template>
<a-tree-select
v-model:value="value"
show-search
style="width: 100%"
:dropdown-style="{ maxHeight: '400px', overflow: 'auto' }"
placeholder="Please select"
allow-clear
multiple
:show-checked-strategy="SHOW_ALL"
tree-default-expand-all
:tree-data="treeData"
>
<template #tagRender="{ label, closable, onClose, option }">
<a-tag :closable="closable" :color="option.color" style="margin-right: 3px" @close="onClose">
{{ label }}&nbsp;&nbsp;
</a-tag>
</template>
<template #title="{ value: val, title }">
<b v-if="val === 'parent 1-1'" style="color: #08c">{{ val }}</b>
<template v-else>{{ title }}</template>
</template>
</a-tree-select>
</template>
<script lang="ts">
import type { TreeSelectProps } from 'ant-design-vue';
import { defineComponent, ref, watch } from 'vue';
import { TreeSelect } from 'ant-design-vue';
const SHOW_ALL = TreeSelect.SHOW_ALL;
export default defineComponent({
setup() {
const value = ref<string[]>(['parent 1', 'parent 1-0', 'leaf1']);
const treeData = ref<TreeSelectProps['treeData']>([
{
title: 'parent 1',
value: 'parent 1',
color: 'pink',
children: [
{
title: 'parent 1-0',
value: 'parent 1-0',
color: 'orange',
children: [
{
title: 'my leaf',
value: 'leaf1',
color: 'green',
},
{
title: 'your leaf',
value: 'leaf2',
color: 'cyan',
},
],
},
{
title: 'parent 1-1',
value: 'parent 1-1',
color: 'blue',
},
],
},
]);
watch(value, () => {
console.log('select', value.value);
});
return {
value,
treeData,
SHOW_ALL,
};
},
});
</script>

View File

@ -7,6 +7,9 @@
<suffix />
<async />
<Highlight />
<treeLineVue />
<virtualScrollVue />
<customTagRenderVue />
</demo-sort>
</template>
<script lang="ts">
@ -17,6 +20,9 @@ import Checkable from './checkable.vue';
import Suffix from './suffix.vue';
import Async from './async.vue';
import Highlight from './highlight.vue';
import treeLineVue from './tree-line.vue';
import virtualScrollVue from './virtual-scroll.vue';
import customTagRenderVue from './custom-tag-render.vue';
import CN from '../index.zh-CN.md';
import US from '../index.en-US.md';
import { defineComponent } from 'vue';
@ -32,6 +38,9 @@ export default defineComponent({
Suffix,
Async,
Highlight,
treeLineVue,
virtualScrollVue,
customTagRenderVue,
},
setup() {
return {};

View File

@ -27,7 +27,12 @@ Multiple selection usage.
multiple
tree-default-expand-all
:tree-data="treeData"
></a-tree-select>
>
<template #title="{ value: val, title }">
<b v-if="val === 'parent 1-1'" style="color: #08c">{{ val }}</b>
<template v-else>{{ title }}</template>
</template>
</a-tree-select>
</template>
<script lang="ts">
import type { TreeSelectProps } from 'ant-design-vue';

View File

@ -0,0 +1,91 @@
<docs>
---
order: 6
title:
zh-CN: 线性样式
en-US: Show Tree Line
---
## zh-CN
通过 `treeLine` 配置线性样式
## en-US
Use `treeLine` to show the line style.
</docs>
<template>
<a-space direction="vertical">
<a-switch
v-model:checked="treeLine"
checked-children="treeLine"
un-checked-children="treeLine"
></a-switch>
<a-switch
v-model:checked="showLeafIcon"
:disabled="!treeLine"
checked-children="showLeafIcon"
un-checked-children="showLeafIcon"
></a-switch>
<a-tree-select
v-model:value="value"
style="width: 300px"
placeholder="Please select"
:tree-line="treeLine && { showLeafIcon }"
:tree-data="treeData"
>
<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>
</a-space>
</template>
<script lang="ts">
import type { TreeSelectProps } from 'ant-design-vue';
import { defineComponent, ref, watch } from 'vue';
export default defineComponent({
setup() {
const treeLine = ref(true);
const showLeafIcon = ref(false);
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',
},
],
},
]);
watch(value, () => {
console.log(value.value);
});
return {
treeLine,
showLeafIcon,
value,
treeData,
};
},
});
</script>

View File

@ -1,74 +0,0 @@
<docs>
---
order: 2
title:
zh-CN: 从数据直接生成
en-US: Generate form tree data
---
## zh-CN
使用 `treeData` JSON 数据直接生成树结构
## en-US
The tree structure can be populated using `treeData` property. This is a quick and easy way to provide the tree content.
</docs>
<template>
<a-tree-select
v-model:value="value"
style="width: 100%"
:dropdown-style="{ maxHeight: '400px', overflow: 'auto' }"
:tree-data="treeData"
placeholder="Please select"
tree-default-expand-all
>
<template #title="{ key, value: val, title }">
<span v-if="key === '0-0-1'" style="color: #08c">Child Node1 {{ val }}</span>
<template v-else>{{ title }}</template>
</template>
</a-tree-select>
</template>
<script lang="ts">
import type { TreeSelectProps } from 'ant-design-vue';
import { defineComponent, ref, watch } from 'vue';
const treeData: TreeSelectProps['treeData'] = [
{
title: 'Node1',
value: '0-0',
key: '0-0',
children: [
{
value: '0-0-1',
key: '0-0-1',
},
{
title: 'Child Node2',
value: '0-0-2',
key: '0-0-2',
},
],
},
{
title: 'Node2',
value: '0-1',
key: '0-1',
},
];
export default defineComponent({
setup() {
const value = ref<string>();
watch(value, () => {
console.log(value.value);
});
return {
value,
treeData,
};
},
});
</script>

View File

@ -0,0 +1,73 @@
<docs>
---
order: 9
title:
zh-CN: 虚拟滚动
en-US: Virtual scroll
---
## zh-CN
使用 `height` 属性则切换为虚拟滚动
## en-US
Use virtual list through `height` prop.
</docs>
<template>
<a-tree-select
v-model:value="checkedKeys"
style="width: 100%"
tree-checkable
tree-default-expand-all
:show-checked-strategy="SHOW_PARENT"
:height="233"
:tree-data="treeData"
:max-tag-count="10"
>
<template #title="{ title, value }">
<span v-if="value === '0-0-1-0'" style="color: #1890ff">{{ title }}</span>
<template v-else>{{ title }}</template>
</template>
</a-tree-select>
</template>
<script lang="ts">
import type { TreeSelectProps } from 'ant-design-vue';
import { TreeSelect } from 'ant-design-vue';
import { defineComponent, ref, watch } from 'vue';
const SHOW_PARENT = TreeSelect.SHOW_PARENT;
function dig(path = '0', level = 3) {
const list: TreeSelectProps['treeData'] = [];
for (let i = 0; i < 10; i += 1) {
const value = `${path}-${i}`;
const treeNode: TreeSelectProps['treeData'][number] = {
title: value,
value,
};
if (level > 0) {
treeNode.children = dig(value, level - 1);
}
list.push(treeNode);
}
return list;
}
export default defineComponent({
setup() {
const checkedKeys = ref<string[]>(['0-0-0', '0-0-1']);
watch(checkedKeys, () => {
console.log('checkedKeys', checkedKeys);
});
return {
treeData: dig(),
checkedKeys,
SHOW_PARENT,
};
},
});
</script>

View File

@ -47,6 +47,7 @@ Tree selection control.
| treeDefaultExpandAll | Whether to expand all treeNodes by default | boolean | false | |
| treeDefaultExpandedKeys | Default expanded treeNodes | string\[] \| number\[] | - | |
| treeExpandedKeys(v-model) | Set expanded keys | string\[] \| number\[] | - | |
| 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' | |
| value(v-model) | To set the current selected treeNode(s). | string\|string\[] | - | |

View File

@ -10,7 +10,7 @@ import VcTreeSelect, {
import classNames from '../_util/classNames';
import initDefaultProps from '../_util/props-util/initDefaultProps';
import type { SizeType } from '../config-provider';
import type { DefaultValueType, FieldNames } from '../vc-tree-select/interface';
import type { FieldNames, Key } from '../vc-tree-select/interface';
import omit from '../_util/omit';
import PropTypes from '../_util/vue-types';
import useConfigInject from '../_util/hooks/useConfigInject';
@ -21,6 +21,9 @@ import type { AntTreeNodeProps } from '../tree/Tree';
import { warning } from '../vc-util/warning';
import { flattenChildren } from '../_util/props-util';
import { useInjectFormItemContext } from '../form/FormItemContext';
import type { BaseSelectRef } from '../vc-select';
import type { BaseOptionType, DefaultOptionType } from '../vc-tree-select/TreeSelect';
import type { TreeProps } from '../tree';
const getTransitionName = (rootPrefixCls: string, motion: string, transitionName?: string) => {
if (transitionName !== undefined) {
@ -34,29 +37,42 @@ type RawValue = string | number;
export interface LabeledValue {
key?: string;
value: RawValue;
label: any;
label?: any;
}
export type SelectValue = RawValue | RawValue[] | LabeledValue | LabeledValue[];
export interface RefTreeSelectProps {
focus: () => void;
blur: () => void;
export type RefTreeSelectProps = BaseSelectRef;
export function treeSelectProps<
ValueType = any,
OptionType extends BaseOptionType | DefaultOptionType = DefaultOptionType,
>() {
return {
...omit(vcTreeSelectProps<ValueType, OptionType>(), [
'showTreeIcon',
'treeMotion',
'inputIcon',
'getInputElement',
'treeLine',
'customSlots',
]),
suffixIcon: PropTypes.any,
size: { type: String as PropType<SizeType> },
bordered: { type: Boolean, default: undefined },
treeLine: { type: [Boolean, Object] as PropType<TreeProps['showLine']>, default: undefined },
replaceFields: { type: Object as PropType<FieldNames> },
'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> },
};
}
export const treeSelectProps = {
...omit(vcTreeSelectProps<DefaultValueType>(), ['showTreeIcon', 'treeMotion', 'inputIcon']),
suffixIcon: PropTypes.any,
size: { type: String as PropType<SizeType> },
bordered: { type: Boolean, default: undefined },
replaceFields: { type: Object as PropType<FieldNames> },
};
export type TreeSelectProps = Partial<ExtractPropTypes<typeof treeSelectProps>>;
export type TreeSelectProps = Partial<ExtractPropTypes<ReturnType<typeof treeSelectProps>>>;
const TreeSelect = defineComponent({
TreeNode,
name: 'ATreeSelect',
inheritAttrs: false,
props: initDefaultProps(treeSelectProps, {
props: initDefaultProps(treeSelectProps(), {
choiceTransitionName: '',
listHeight: 256,
treeIcon: false,
@ -135,18 +151,18 @@ const TreeSelect = defineComponent({
},
});
const handleChange = (...args: any[]) => {
const handleChange: TreeSelectProps['onChange'] = (...args: any[]) => {
emit('update:value', args[0]);
emit('change', ...args);
formItemContext.onFieldChange();
};
const handleTreeExpand = (...args: any[]) => {
emit('update:treeExpandedKeys', args[0]);
emit('treeExpand', ...args);
const handleTreeExpand: TreeSelectProps['onTreeExpand'] = (keys: Key[]) => {
emit('update:treeExpandedKeys', keys);
emit('treeExpand', keys);
};
const handleSearch = (...args: any[]) => {
emit('update:searchValue', args[0]);
emit('search', ...args);
const handleSearch: TreeSelectProps['onSearch'] = (value: string) => {
emit('update:searchValue', value);
emit('search', value);
};
const handleBlur = () => {
emit('blur');
@ -190,6 +206,10 @@ const TreeSelect = defineComponent({
'removeIcon',
'clearIcon',
'switcherIcon',
'bordered',
'onUpdate:value',
'onUpdate:treeExpandedKeys',
'onUpdate:searchValue',
]);
const mergedClassName = classNames(
@ -242,6 +262,11 @@ const TreeSelect = defineComponent({
}}
{...otherProps}
transitionName={transitionName.value}
customSlots={{
...slots,
treeCheckable: () => <span class={`${prefixCls.value}-tree-checkbox-inner`} />,
}}
maxTagPlaceholder={props.maxTagPlaceholder || slots.maxTagPlaceholder}
/>
);
};

View File

@ -48,6 +48,7 @@ cover: https://gw.alipayobjects.com/zos/alicdn/Ax4DA0njr/TreeSelect.svg
| treeDefaultExpandAll | 默认展开所有树节点 | boolean | false | |
| treeDefaultExpandedKeys | 默认展开的树节点 | string\[] \| number\[] | - | |
| treeExpandedKeys(v-model) | 设置展开的树节点 | string\[] \| number\[] | - | |
| treeLine | 是否展示线条样式,请参考 [Tree - showLine](/components/tree/#components-tree-demo-line) | boolean \| object | false | 3.0 |
| treeNodeFilterProp | 输入项过滤对应的 treeNode 属性 | string | 'value' | |
| treeNodeLabelProp | 作为显示的 prop 设置 | string | 'title' | |
| value(v-model) | 指定当前选中的条目 | string/string\[] | - | |

View File

@ -11,7 +11,7 @@
.@{tree-select-prefix-cls} {
// ======================= Dropdown =======================
&-dropdown {
padding: @padding-xs (@padding-xs / 2) 0;
padding: @padding-xs (@padding-xs / 2);
&-rtl {
direction: rtl;
@ -24,8 +24,6 @@
align-items: stretch;
.@{select-tree-prefix-cls}-treenode {
padding-bottom: @padding-xs;
.@{select-tree-prefix-cls}-node-content-wrapper {
flex: auto;
}

View File

@ -1,34 +0,0 @@
import { flattenChildren, isValidElement } from '../_util/props-util';
export function convertChildrenToData(nodes: any[]): any[] {
return flattenChildren(nodes)
.map(node => {
if (!isValidElement(node) || !node.type) {
return null;
}
const { default: d, ...restSlot } = node.children || {};
const children = d ? d() : [];
const {
key,
props: { value, ...restProps },
} = node;
const data = {
key,
value,
...restProps,
};
Object.keys(restSlot).forEach(p => {
if (typeof restSlot[p] === 'function') {
data[p] = <>{restSlot[p]()}</>;
}
});
const childData = convertChildrenToData(children);
if (childData.length) {
data.children = childData;
}
return data;
})
.filter(data => data);
}

View File

@ -81,7 +81,10 @@ export interface AntTreeNodeDropEvent {
export const treeProps = () => {
return {
...vcTreeProps(),
showLine: { type: Boolean, default: undefined },
showLine: {
type: [Boolean, Object] as PropType<boolean | { showLeafIcon: boolean }>,
default: undefined,
},
/** 是否支持多选 */
multiple: { type: Boolean, default: undefined },
/** 是否自动展开父节点 */

View File

@ -13,11 +13,13 @@ exports[`renders ./components/tree/demo/accordion.vue correctly 1`] = `
<div class="ant-tree-list-holder">
<div>
<div style="display: flex; flex-direction: column;" class="ant-tree-list-holder-inner">
<div class="ant-tree-treenode ant-tree-treenode-switcher-close"><span aria-hidden="true" class="ant-tree-indent"></span><span class="ant-tree-switcher ant-tree-switcher_close"><span role="img" aria-label="caret-down" class="anticon anticon-caret-down ant-tree-switcher-icon"><svg focusable="false" class="" data-icon="caret-down" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="0 0 1024 1024"><path d="M840.4 300H183.6c-19.7 0-30.7 20.8-18.5 35l328.4 380.8c9.4 10.9 27.5 10.9 37 0L858.9 335c12.2-14.2 1.2-35-18.5-35z"></path></svg></span></span>
<div class="ant-tree-treenode ant-tree-treenode-switcher-close" draggable="false" aria-grabbed="false"><span aria-hidden="true" class="ant-tree-indent"></span>
<!----><span class="ant-tree-switcher ant-tree-switcher_close"><span role="img" aria-label="caret-down" class="anticon anticon-caret-down ant-tree-switcher-icon"><svg focusable="false" class="" data-icon="caret-down" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="0 0 1024 1024"><path d="M840.4 300H183.6c-19.7 0-30.7 20.8-18.5 35l328.4 380.8c9.4 10.9 27.5 10.9 37 0L858.9 335c12.2-14.2 1.2-35-18.5-35z"></path></svg></span></span>
<!----><span title="" class="ant-tree-node-content-wrapper ant-tree-node-content-wrapper-close"><!----><span class="ant-tree-title">parent 1</span>
<!----></span>
</div>
<div class="ant-tree-treenode ant-tree-treenode-switcher-close ant-tree-treenode-leaf-last"><span aria-hidden="true" class="ant-tree-indent"></span><span class="ant-tree-switcher ant-tree-switcher_close"><span role="img" aria-label="caret-down" class="anticon anticon-caret-down ant-tree-switcher-icon"><svg focusable="false" class="" data-icon="caret-down" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="0 0 1024 1024"><path d="M840.4 300H183.6c-19.7 0-30.7 20.8-18.5 35l328.4 380.8c9.4 10.9 27.5 10.9 37 0L858.9 335c12.2-14.2 1.2-35-18.5-35z"></path></svg></span></span>
<div class="ant-tree-treenode ant-tree-treenode-switcher-close ant-tree-treenode-leaf-last" draggable="false" aria-grabbed="false"><span aria-hidden="true" class="ant-tree-indent"></span>
<!----><span class="ant-tree-switcher ant-tree-switcher_close"><span role="img" aria-label="caret-down" class="anticon anticon-caret-down ant-tree-switcher-icon"><svg focusable="false" class="" data-icon="caret-down" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="0 0 1024 1024"><path d="M840.4 300H183.6c-19.7 0-30.7 20.8-18.5 35l328.4 380.8c9.4 10.9 27.5 10.9 37 0L858.9 335c12.2-14.2 1.2-35-18.5-35z"></path></svg></span></span>
<!----><span title="" class="ant-tree-node-content-wrapper ant-tree-node-content-wrapper-close"><!----><span class="ant-tree-title">parent 2</span>
<!----></span>
</div>
@ -42,22 +44,28 @@ exports[`renders ./components/tree/demo/basic.vue correctly 1`] = `
<div class="ant-tree-list-holder">
<div>
<div style="display: flex; flex-direction: column;" class="ant-tree-list-holder-inner">
<div class="ant-tree-treenode ant-tree-treenode-switcher-open ant-tree-treenode-checkbox-checked ant-tree-treenode-leaf-last"><span aria-hidden="true" class="ant-tree-indent"></span><span class="ant-tree-switcher ant-tree-switcher_open"><span role="img" aria-label="caret-down" class="anticon anticon-caret-down ant-tree-switcher-icon"><svg focusable="false" class="" data-icon="caret-down" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="0 0 1024 1024"><path d="M840.4 300H183.6c-19.7 0-30.7 20.8-18.5 35l328.4 380.8c9.4 10.9 27.5 10.9 37 0L858.9 335c12.2-14.2 1.2-35-18.5-35z"></path></svg></span></span><span class="ant-tree-checkbox ant-tree-checkbox-checked"><span class="ant-tree-checkbox-inner"></span></span><span title="" class="ant-tree-node-content-wrapper ant-tree-node-content-wrapper-open"><!----><span class="ant-tree-title">parent 1</span>
<div class="ant-tree-treenode ant-tree-treenode-switcher-open ant-tree-treenode-checkbox-checked ant-tree-treenode-leaf-last" draggable="false" aria-grabbed="false"><span aria-hidden="true" class="ant-tree-indent"></span>
<!----><span class="ant-tree-switcher ant-tree-switcher_open"><span role="img" aria-label="caret-down" class="anticon anticon-caret-down ant-tree-switcher-icon"><svg focusable="false" class="" data-icon="caret-down" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="0 0 1024 1024"><path d="M840.4 300H183.6c-19.7 0-30.7 20.8-18.5 35l328.4 380.8c9.4 10.9 27.5 10.9 37 0L858.9 335c12.2-14.2 1.2-35-18.5-35z"></path></svg></span></span><span class="ant-tree-checkbox ant-tree-checkbox-checked"><span class="ant-tree-checkbox-inner"></span></span><span title="" class="ant-tree-node-content-wrapper ant-tree-node-content-wrapper-open"><!----><span class="ant-tree-title">parent 1</span>
<!----></span>
</div>
<div class="ant-tree-treenode ant-tree-treenode-disabled ant-tree-treenode-switcher-open ant-tree-treenode-checkbox-checked ant-tree-treenode-selected"><span aria-hidden="true" class="ant-tree-indent"><span class="ant-tree-indent-unit ant-tree-indent-unit-start ant-tree-indent-unit-end"></span></span><span class="ant-tree-switcher ant-tree-switcher_open"><span role="img" aria-label="caret-down" class="anticon anticon-caret-down ant-tree-switcher-icon"><svg focusable="false" class="" data-icon="caret-down" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="0 0 1024 1024"><path d="M840.4 300H183.6c-19.7 0-30.7 20.8-18.5 35l328.4 380.8c9.4 10.9 27.5 10.9 37 0L858.9 335c12.2-14.2 1.2-35-18.5-35z"></path></svg></span></span><span class="ant-tree-checkbox ant-tree-checkbox-checked ant-tree-checkbox-disabled"><span class="ant-tree-checkbox-inner"></span></span><span title="" class="ant-tree-node-content-wrapper ant-tree-node-content-wrapper-open"><!----><span class="ant-tree-title">parent 1-0</span>
<div class="ant-tree-treenode ant-tree-treenode-disabled ant-tree-treenode-switcher-open ant-tree-treenode-checkbox-checked ant-tree-treenode-selected" draggable="false" aria-grabbed="false"><span aria-hidden="true" class="ant-tree-indent"><span class="ant-tree-indent-unit ant-tree-indent-unit-start ant-tree-indent-unit-end"></span></span>
<!----><span class="ant-tree-switcher ant-tree-switcher_open"><span role="img" aria-label="caret-down" class="anticon anticon-caret-down ant-tree-switcher-icon"><svg focusable="false" class="" data-icon="caret-down" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="0 0 1024 1024"><path d="M840.4 300H183.6c-19.7 0-30.7 20.8-18.5 35l328.4 380.8c9.4 10.9 27.5 10.9 37 0L858.9 335c12.2-14.2 1.2-35-18.5-35z"></path></svg></span></span><span class="ant-tree-checkbox ant-tree-checkbox-checked ant-tree-checkbox-disabled"><span class="ant-tree-checkbox-inner"></span></span><span title="" class="ant-tree-node-content-wrapper ant-tree-node-content-wrapper-open"><!----><span class="ant-tree-title">parent 1-0</span>
<!----></span>
</div>
<div class="ant-tree-treenode ant-tree-treenode-switcher-close"><span aria-hidden="true" class="ant-tree-indent"><span class="ant-tree-indent-unit ant-tree-indent-unit-start ant-tree-indent-unit-end"></span><span class="ant-tree-indent-unit ant-tree-indent-unit-start"></span></span><span class="ant-tree-switcher ant-tree-switcher-noop"><!----></span><span class="ant-tree-checkbox ant-tree-checkbox-disabled"><span class="ant-tree-checkbox-inner"></span></span><span title="" class="ant-tree-node-content-wrapper ant-tree-node-content-wrapper-normal"><!----><span class="ant-tree-title">leaf</span>
<div class="ant-tree-treenode ant-tree-treenode-switcher-close" draggable="false" aria-grabbed="false"><span aria-hidden="true" class="ant-tree-indent"><span class="ant-tree-indent-unit ant-tree-indent-unit-start ant-tree-indent-unit-end"></span><span class="ant-tree-indent-unit ant-tree-indent-unit-start"></span></span>
<!----><span class="ant-tree-switcher ant-tree-switcher-noop"><!----></span><span class="ant-tree-checkbox ant-tree-checkbox-disabled"><span class="ant-tree-checkbox-inner"></span></span><span title="" class="ant-tree-node-content-wrapper ant-tree-node-content-wrapper-normal"><!----><span class="ant-tree-title">leaf</span>
<!----></span>
</div>
<div class="ant-tree-treenode ant-tree-treenode-switcher-close ant-tree-treenode-leaf-last"><span aria-hidden="true" class="ant-tree-indent"><span class="ant-tree-indent-unit ant-tree-indent-unit-start ant-tree-indent-unit-end"></span><span class="ant-tree-indent-unit ant-tree-indent-unit-start"></span></span><span class="ant-tree-switcher ant-tree-switcher-noop"><!----></span><span class="ant-tree-checkbox"><span class="ant-tree-checkbox-inner"></span></span><span title="" class="ant-tree-node-content-wrapper ant-tree-node-content-wrapper-normal"><!----><span class="ant-tree-title">leaf</span>
<div class="ant-tree-treenode ant-tree-treenode-switcher-close ant-tree-treenode-leaf-last" draggable="false" aria-grabbed="false"><span aria-hidden="true" class="ant-tree-indent"><span class="ant-tree-indent-unit ant-tree-indent-unit-start ant-tree-indent-unit-end"></span><span class="ant-tree-indent-unit ant-tree-indent-unit-start"></span></span>
<!----><span class="ant-tree-switcher ant-tree-switcher-noop"><!----></span><span class="ant-tree-checkbox"><span class="ant-tree-checkbox-inner"></span></span><span title="" class="ant-tree-node-content-wrapper ant-tree-node-content-wrapper-normal"><!----><span class="ant-tree-title">leaf</span>
<!----></span>
</div>
<div class="ant-tree-treenode ant-tree-treenode-switcher-open ant-tree-treenode-checkbox-checked ant-tree-treenode-leaf-last"><span aria-hidden="true" class="ant-tree-indent"><span class="ant-tree-indent-unit ant-tree-indent-unit-start ant-tree-indent-unit-end"></span></span><span class="ant-tree-switcher ant-tree-switcher_open"><span role="img" aria-label="caret-down" class="anticon anticon-caret-down ant-tree-switcher-icon"><svg focusable="false" class="" data-icon="caret-down" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="0 0 1024 1024"><path d="M840.4 300H183.6c-19.7 0-30.7 20.8-18.5 35l328.4 380.8c9.4 10.9 27.5 10.9 37 0L858.9 335c12.2-14.2 1.2-35-18.5-35z"></path></svg></span></span><span class="ant-tree-checkbox ant-tree-checkbox-checked"><span class="ant-tree-checkbox-inner"></span></span><span title="" class="ant-tree-node-content-wrapper ant-tree-node-content-wrapper-open"><!----><span class="ant-tree-title">parent 1-1</span>
<div class="ant-tree-treenode ant-tree-treenode-switcher-open ant-tree-treenode-checkbox-checked ant-tree-treenode-leaf-last" draggable="false" aria-grabbed="false"><span aria-hidden="true" class="ant-tree-indent"><span class="ant-tree-indent-unit ant-tree-indent-unit-start ant-tree-indent-unit-end"></span></span>
<!----><span class="ant-tree-switcher ant-tree-switcher_open"><span role="img" aria-label="caret-down" class="anticon anticon-caret-down ant-tree-switcher-icon"><svg focusable="false" class="" data-icon="caret-down" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="0 0 1024 1024"><path d="M840.4 300H183.6c-19.7 0-30.7 20.8-18.5 35l328.4 380.8c9.4 10.9 27.5 10.9 37 0L858.9 335c12.2-14.2 1.2-35-18.5-35z"></path></svg></span></span><span class="ant-tree-checkbox ant-tree-checkbox-checked"><span class="ant-tree-checkbox-inner"></span></span><span title="" class="ant-tree-node-content-wrapper ant-tree-node-content-wrapper-open"><!----><span class="ant-tree-title">parent 1-1</span>
<!----></span>
</div>
<div class="ant-tree-treenode ant-tree-treenode-switcher-close ant-tree-treenode-checkbox-checked ant-tree-treenode-leaf-last"><span aria-hidden="true" class="ant-tree-indent"><span class="ant-tree-indent-unit ant-tree-indent-unit-start ant-tree-indent-unit-end"></span><span class="ant-tree-indent-unit ant-tree-indent-unit-end"></span></span><span class="ant-tree-switcher ant-tree-switcher-noop"><!----></span><span class="ant-tree-checkbox ant-tree-checkbox-checked"><span class="ant-tree-checkbox-inner"></span></span><span title="" class="ant-tree-node-content-wrapper ant-tree-node-content-wrapper-normal"><!----><span class="ant-tree-title"><span style="color: rgb(24, 144, 255);">sss</span></span>
<div class="ant-tree-treenode ant-tree-treenode-switcher-close ant-tree-treenode-checkbox-checked ant-tree-treenode-leaf-last" draggable="false" aria-grabbed="false"><span aria-hidden="true" class="ant-tree-indent"><span class="ant-tree-indent-unit ant-tree-indent-unit-start ant-tree-indent-unit-end"></span><span class="ant-tree-indent-unit ant-tree-indent-unit-end"></span></span>
<!----><span class="ant-tree-switcher ant-tree-switcher-noop"><!----></span><span class="ant-tree-checkbox ant-tree-checkbox-checked"><span class="ant-tree-checkbox-inner"></span></span><span title="" class="ant-tree-node-content-wrapper ant-tree-node-content-wrapper-normal"><!----><span class="ant-tree-title"><span style="color: rgb(24, 144, 255);">sss</span></span>
<!----></span>
</div>
</div>
@ -81,39 +89,48 @@ exports[`renders ./components/tree/demo/context-menu.vue correctly 1`] = `
<div class="ant-tree-list-holder">
<div>
<div style="display: flex; flex-direction: column;" class="ant-tree-list-holder-inner">
<div class="ant-tree-treenode ant-tree-treenode-switcher-open ant-tree-treenode-leaf-last"><span aria-hidden="true" class="ant-tree-indent"></span><span class="ant-tree-switcher ant-tree-switcher_open"><span role="img" aria-label="caret-down" class="anticon anticon-caret-down ant-tree-switcher-icon"><svg focusable="false" class="" data-icon="caret-down" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="0 0 1024 1024"><path d="M840.4 300H183.6c-19.7 0-30.7 20.8-18.5 35l328.4 380.8c9.4 10.9 27.5 10.9 37 0L858.9 335c12.2-14.2 1.2-35-18.5-35z"></path></svg></span></span>
<div class="ant-tree-treenode ant-tree-treenode-switcher-open ant-tree-treenode-leaf-last" draggable="false" aria-grabbed="false"><span aria-hidden="true" class="ant-tree-indent"></span>
<!----><span class="ant-tree-switcher ant-tree-switcher_open"><span role="img" aria-label="caret-down" class="anticon anticon-caret-down ant-tree-switcher-icon"><svg focusable="false" class="" data-icon="caret-down" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="0 0 1024 1024"><path d="M840.4 300H183.6c-19.7 0-30.7 20.8-18.5 35l328.4 380.8c9.4 10.9 27.5 10.9 37 0L858.9 335c12.2-14.2 1.2-35-18.5-35z"></path></svg></span></span>
<!----><span title="" class="ant-tree-node-content-wrapper ant-tree-node-content-wrapper-open"><!----><span class="ant-tree-title"><!----><span class="ant-dropdown-trigger">0-0</span></span>
<!----></span>
</div>
<div class="ant-tree-treenode ant-tree-treenode-switcher-open"><span aria-hidden="true" class="ant-tree-indent"><span class="ant-tree-indent-unit ant-tree-indent-unit-start ant-tree-indent-unit-end"></span></span><span class="ant-tree-switcher ant-tree-switcher_open"><span role="img" aria-label="caret-down" class="anticon anticon-caret-down ant-tree-switcher-icon"><svg focusable="false" class="" data-icon="caret-down" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="0 0 1024 1024"><path d="M840.4 300H183.6c-19.7 0-30.7 20.8-18.5 35l328.4 380.8c9.4 10.9 27.5 10.9 37 0L858.9 335c12.2-14.2 1.2-35-18.5-35z"></path></svg></span></span>
<div class="ant-tree-treenode ant-tree-treenode-switcher-open" draggable="false" aria-grabbed="false"><span aria-hidden="true" class="ant-tree-indent"><span class="ant-tree-indent-unit ant-tree-indent-unit-start ant-tree-indent-unit-end"></span></span>
<!----><span class="ant-tree-switcher ant-tree-switcher_open"><span role="img" aria-label="caret-down" class="anticon anticon-caret-down ant-tree-switcher-icon"><svg focusable="false" class="" data-icon="caret-down" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="0 0 1024 1024"><path d="M840.4 300H183.6c-19.7 0-30.7 20.8-18.5 35l328.4 380.8c9.4 10.9 27.5 10.9 37 0L858.9 335c12.2-14.2 1.2-35-18.5-35z"></path></svg></span></span>
<!----><span title="" class="ant-tree-node-content-wrapper ant-tree-node-content-wrapper-open"><!----><span class="ant-tree-title"><!----><span class="ant-dropdown-trigger">0-0-0</span></span>
<!----></span>
</div>
<div class="ant-tree-treenode ant-tree-treenode-switcher-close"><span aria-hidden="true" class="ant-tree-indent"><span class="ant-tree-indent-unit ant-tree-indent-unit-start ant-tree-indent-unit-end"></span><span class="ant-tree-indent-unit ant-tree-indent-unit-start"></span></span><span class="ant-tree-switcher ant-tree-switcher-noop"><!----></span>
<div class="ant-tree-treenode ant-tree-treenode-switcher-close" draggable="false" aria-grabbed="false"><span aria-hidden="true" class="ant-tree-indent"><span class="ant-tree-indent-unit ant-tree-indent-unit-start ant-tree-indent-unit-end"></span><span class="ant-tree-indent-unit ant-tree-indent-unit-start"></span></span>
<!----><span class="ant-tree-switcher ant-tree-switcher-noop"><!----></span>
<!----><span title="" class="ant-tree-node-content-wrapper ant-tree-node-content-wrapper-normal"><!----><span class="ant-tree-title"><!----><span class="ant-dropdown-trigger">0-0-0-0</span></span>
<!----></span>
</div>
<div class="ant-tree-treenode ant-tree-treenode-switcher-close"><span aria-hidden="true" class="ant-tree-indent"><span class="ant-tree-indent-unit ant-tree-indent-unit-start ant-tree-indent-unit-end"></span><span class="ant-tree-indent-unit ant-tree-indent-unit-start"></span></span><span class="ant-tree-switcher ant-tree-switcher-noop"><!----></span>
<div class="ant-tree-treenode ant-tree-treenode-switcher-close" draggable="false" aria-grabbed="false"><span aria-hidden="true" class="ant-tree-indent"><span class="ant-tree-indent-unit ant-tree-indent-unit-start ant-tree-indent-unit-end"></span><span class="ant-tree-indent-unit ant-tree-indent-unit-start"></span></span>
<!----><span class="ant-tree-switcher ant-tree-switcher-noop"><!----></span>
<!----><span title="" class="ant-tree-node-content-wrapper ant-tree-node-content-wrapper-normal"><!----><span class="ant-tree-title"><!----><span class="ant-dropdown-trigger">0-0-0-1</span></span>
<!----></span>
</div>
<div class="ant-tree-treenode ant-tree-treenode-switcher-close ant-tree-treenode-leaf-last"><span aria-hidden="true" class="ant-tree-indent"><span class="ant-tree-indent-unit ant-tree-indent-unit-start ant-tree-indent-unit-end"></span><span class="ant-tree-indent-unit ant-tree-indent-unit-start"></span></span><span class="ant-tree-switcher ant-tree-switcher-noop"><!----></span>
<div class="ant-tree-treenode ant-tree-treenode-switcher-close ant-tree-treenode-leaf-last" draggable="false" aria-grabbed="false"><span aria-hidden="true" class="ant-tree-indent"><span class="ant-tree-indent-unit ant-tree-indent-unit-start ant-tree-indent-unit-end"></span><span class="ant-tree-indent-unit ant-tree-indent-unit-start"></span></span>
<!----><span class="ant-tree-switcher ant-tree-switcher-noop"><!----></span>
<!----><span title="" class="ant-tree-node-content-wrapper ant-tree-node-content-wrapper-normal"><!----><span class="ant-tree-title"><!----><span class="ant-dropdown-trigger">0-0-0-2</span></span>
<!----></span>
</div>
<div class="ant-tree-treenode ant-tree-treenode-switcher-open ant-tree-treenode-leaf-last"><span aria-hidden="true" class="ant-tree-indent"><span class="ant-tree-indent-unit ant-tree-indent-unit-start ant-tree-indent-unit-end"></span></span><span class="ant-tree-switcher ant-tree-switcher_open"><span role="img" aria-label="caret-down" class="anticon anticon-caret-down ant-tree-switcher-icon"><svg focusable="false" class="" data-icon="caret-down" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="0 0 1024 1024"><path d="M840.4 300H183.6c-19.7 0-30.7 20.8-18.5 35l328.4 380.8c9.4 10.9 27.5 10.9 37 0L858.9 335c12.2-14.2 1.2-35-18.5-35z"></path></svg></span></span>
<div class="ant-tree-treenode ant-tree-treenode-switcher-open ant-tree-treenode-leaf-last" draggable="false" aria-grabbed="false"><span aria-hidden="true" class="ant-tree-indent"><span class="ant-tree-indent-unit ant-tree-indent-unit-start ant-tree-indent-unit-end"></span></span>
<!----><span class="ant-tree-switcher ant-tree-switcher_open"><span role="img" aria-label="caret-down" class="anticon anticon-caret-down ant-tree-switcher-icon"><svg focusable="false" class="" data-icon="caret-down" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="0 0 1024 1024"><path d="M840.4 300H183.6c-19.7 0-30.7 20.8-18.5 35l328.4 380.8c9.4 10.9 27.5 10.9 37 0L858.9 335c12.2-14.2 1.2-35-18.5-35z"></path></svg></span></span>
<!----><span title="" class="ant-tree-node-content-wrapper ant-tree-node-content-wrapper-open"><!----><span class="ant-tree-title"><!----><span class="ant-dropdown-trigger">0-0-1</span></span>
<!----></span>
</div>
<div class="ant-tree-treenode ant-tree-treenode-switcher-close"><span aria-hidden="true" class="ant-tree-indent"><span class="ant-tree-indent-unit ant-tree-indent-unit-start ant-tree-indent-unit-end"></span><span class="ant-tree-indent-unit ant-tree-indent-unit-end"></span></span><span class="ant-tree-switcher ant-tree-switcher-noop"><!----></span>
<div class="ant-tree-treenode ant-tree-treenode-switcher-close" draggable="false" aria-grabbed="false"><span aria-hidden="true" class="ant-tree-indent"><span class="ant-tree-indent-unit ant-tree-indent-unit-start ant-tree-indent-unit-end"></span><span class="ant-tree-indent-unit ant-tree-indent-unit-end"></span></span>
<!----><span class="ant-tree-switcher ant-tree-switcher-noop"><!----></span>
<!----><span title="" class="ant-tree-node-content-wrapper ant-tree-node-content-wrapper-normal"><!----><span class="ant-tree-title"><!----><span class="ant-dropdown-trigger">0-0-1-0</span></span>
<!----></span>
</div>
<div class="ant-tree-treenode ant-tree-treenode-switcher-close"><span aria-hidden="true" class="ant-tree-indent"><span class="ant-tree-indent-unit ant-tree-indent-unit-start ant-tree-indent-unit-end"></span><span class="ant-tree-indent-unit ant-tree-indent-unit-end"></span></span><span class="ant-tree-switcher ant-tree-switcher-noop"><!----></span>
<div class="ant-tree-treenode ant-tree-treenode-switcher-close" draggable="false" aria-grabbed="false"><span aria-hidden="true" class="ant-tree-indent"><span class="ant-tree-indent-unit ant-tree-indent-unit-start ant-tree-indent-unit-end"></span><span class="ant-tree-indent-unit ant-tree-indent-unit-end"></span></span>
<!----><span class="ant-tree-switcher ant-tree-switcher-noop"><!----></span>
<!----><span title="" class="ant-tree-node-content-wrapper ant-tree-node-content-wrapper-normal"><!----><span class="ant-tree-title"><!----><span class="ant-dropdown-trigger">0-0-1-1</span></span>
<!----></span>
</div>
<div class="ant-tree-treenode ant-tree-treenode-switcher-close ant-tree-treenode-leaf-last"><span aria-hidden="true" class="ant-tree-indent"><span class="ant-tree-indent-unit ant-tree-indent-unit-start ant-tree-indent-unit-end"></span><span class="ant-tree-indent-unit ant-tree-indent-unit-end"></span></span><span class="ant-tree-switcher ant-tree-switcher-noop"><!----></span>
<div class="ant-tree-treenode ant-tree-treenode-switcher-close ant-tree-treenode-leaf-last" draggable="false" aria-grabbed="false"><span aria-hidden="true" class="ant-tree-indent"><span class="ant-tree-indent-unit ant-tree-indent-unit-start ant-tree-indent-unit-end"></span><span class="ant-tree-indent-unit ant-tree-indent-unit-end"></span></span>
<!----><span class="ant-tree-switcher ant-tree-switcher-noop"><!----></span>
<!----><span title="" class="ant-tree-node-content-wrapper ant-tree-node-content-wrapper-normal"><!----><span class="ant-tree-title"><!----><span class="ant-dropdown-trigger">0-0-1-2</span></span>
<!----></span>
</div>
@ -138,15 +155,18 @@ exports[`renders ./components/tree/demo/customized-icon.vue correctly 1`] = `
<div class="ant-tree-list-holder">
<div>
<div style="display: flex; flex-direction: column;" class="ant-tree-list-holder-inner">
<div class="ant-tree-treenode ant-tree-treenode-switcher-open ant-tree-treenode-leaf-last"><span aria-hidden="true" class="ant-tree-indent"></span><span class="ant-tree-switcher ant-tree-switcher_open"><span role="img" aria-label="down" class="anticon anticon-down"><svg focusable="false" class="" data-icon="down" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M884 256h-75c-5.1 0-9.9 2.5-12.9 6.6L512 654.2 227.9 262.6c-3-4.1-7.8-6.6-12.9-6.6h-75c-6.5 0-10.3 7.4-6.5 12.7l352.6 486.1c12.8 17.6 39 17.6 51.7 0l352.6-486.1c3.9-5.3.1-12.7-6.4-12.7z"></path></svg></span></span>
<div class="ant-tree-treenode ant-tree-treenode-switcher-open ant-tree-treenode-leaf-last" draggable="false" aria-grabbed="false"><span aria-hidden="true" class="ant-tree-indent"></span>
<!----><span class="ant-tree-switcher ant-tree-switcher_open"><span role="img" aria-label="down" class="anticon anticon-down"><svg focusable="false" class="" data-icon="down" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M884 256h-75c-5.1 0-9.9 2.5-12.9 6.6L512 654.2 227.9 262.6c-3-4.1-7.8-6.6-12.9-6.6h-75c-6.5 0-10.3 7.4-6.5 12.7l352.6 486.1c12.8 17.6 39 17.6 51.7 0l352.6-486.1c3.9-5.3.1-12.7-6.4-12.7z"></path></svg></span></span>
<!----><span title="parent 1" class="ant-tree-node-content-wrapper ant-tree-node-content-wrapper-open"><span class="ant-tree-iconEle ant-tree-icon__customize"><span role="img" aria-label="smile" class="anticon anticon-smile"><svg focusable="false" class="" data-icon="smile" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M288 421a48 48 0 1096 0 48 48 0 10-96 0zm352 0a48 48 0 1096 0 48 48 0 10-96 0zM512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm263 711c-34.2 34.2-74 61-118.3 79.8C611 874.2 562.3 884 512 884c-50.3 0-99-9.8-144.8-29.2A370.4 370.4 0 01248.9 775c-34.2-34.2-61-74-79.8-118.3C149.8 611 140 562.3 140 512s9.8-99 29.2-144.8A370.4 370.4 0 01249 248.9c34.2-34.2 74-61 118.3-79.8C413 149.8 461.7 140 512 140c50.3 0 99 9.8 144.8 29.2A370.4 370.4 0 01775.1 249c34.2 34.2 61 74 79.8 118.3C874.2 413 884 461.7 884 512s-9.8 99-29.2 144.8A368.89 368.89 0 01775 775zM664 533h-48.1c-4.2 0-7.8 3.2-8.1 7.4C604 589.9 562.5 629 512 629s-92.1-39.1-95.8-88.6c-.3-4.2-3.9-7.4-8.1-7.4H360a8 8 0 00-8 8.4c4.4 84.3 74.5 151.6 160 151.6s155.6-67.3 160-151.6a8 8 0 00-8-8.4z"></path></svg></span></span><span class="ant-tree-title">parent 1</span>
<!----></span>
</div>
<div class="ant-tree-treenode ant-tree-treenode-switcher-open ant-tree-treenode-selected"><span aria-hidden="true" class="ant-tree-indent"><span class="ant-tree-indent-unit ant-tree-indent-unit-start ant-tree-indent-unit-end"></span></span><span class="ant-tree-switcher ant-tree-switcher-noop"><!----></span>
<div class="ant-tree-treenode ant-tree-treenode-switcher-open ant-tree-treenode-selected" draggable="false" aria-grabbed="false"><span aria-hidden="true" class="ant-tree-indent"><span class="ant-tree-indent-unit ant-tree-indent-unit-start ant-tree-indent-unit-end"></span></span>
<!----><span class="ant-tree-switcher ant-tree-switcher-noop"><!----></span>
<!----><span title="leaf" class="ant-tree-node-content-wrapper ant-tree-node-content-wrapper-normal ant-tree-node-selected"><span class="ant-tree-iconEle ant-tree-icon__customize"><span role="img" aria-label="smile" class="anticon anticon-smile"><svg focusable="false" class="" data-icon="smile" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M288 421a48 48 0 1096 0 48 48 0 10-96 0zm352 0a48 48 0 1096 0 48 48 0 10-96 0zM512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm263 711c-34.2 34.2-74 61-118.3 79.8C611 874.2 562.3 884 512 884c-50.3 0-99-9.8-144.8-29.2A370.4 370.4 0 01248.9 775c-34.2-34.2-61-74-79.8-118.3C149.8 611 140 562.3 140 512s9.8-99 29.2-144.8A370.4 370.4 0 01249 248.9c34.2-34.2 74-61 118.3-79.8C413 149.8 461.7 140 512 140c50.3 0 99 9.8 144.8 29.2A370.4 370.4 0 01775.1 249c34.2 34.2 61 74 79.8 118.3C874.2 413 884 461.7 884 512s-9.8 99-29.2 144.8A368.89 368.89 0 01775 775zM664 533h-48.1c-4.2 0-7.8 3.2-8.1 7.4C604 589.9 562.5 629 512 629s-92.1-39.1-95.8-88.6c-.3-4.2-3.9-7.4-8.1-7.4H360a8 8 0 00-8 8.4c4.4 84.3 74.5 151.6 160 151.6s155.6-67.3 160-151.6a8 8 0 00-8-8.4z"></path></svg></span></span><span class="ant-tree-title">leaf</span>
<!----></span>
</div>
<div class="ant-tree-treenode ant-tree-treenode-switcher-open ant-tree-treenode-leaf-last"><span aria-hidden="true" class="ant-tree-indent"><span class="ant-tree-indent-unit ant-tree-indent-unit-start ant-tree-indent-unit-end"></span></span><span class="ant-tree-switcher ant-tree-switcher-noop"><!----></span>
<div class="ant-tree-treenode ant-tree-treenode-switcher-open ant-tree-treenode-leaf-last" draggable="false" aria-grabbed="false"><span aria-hidden="true" class="ant-tree-indent"><span class="ant-tree-indent-unit ant-tree-indent-unit-start ant-tree-indent-unit-end"></span></span>
<!----><span class="ant-tree-switcher ant-tree-switcher-noop"><!----></span>
<!----><span title="leaf" class="ant-tree-node-content-wrapper ant-tree-node-content-wrapper-normal"><span class="ant-tree-iconEle ant-tree-icon__customize"><span role="img" aria-label="frown" class="anticon anticon-frown"><svg focusable="false" class="" data-icon="frown" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M288 421a48 48 0 1096 0 48 48 0 10-96 0zm352 0a48 48 0 1096 0 48 48 0 10-96 0zM512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm263 711c-34.2 34.2-74 61-118.3 79.8C611 874.2 562.3 884 512 884c-50.3 0-99-9.8-144.8-29.2A370.4 370.4 0 01248.9 775c-34.2-34.2-61-74-79.8-118.3C149.8 611 140 562.3 140 512s9.8-99 29.2-144.8A370.4 370.4 0 01249 248.9c34.2-34.2 74-61 118.3-79.8C413 149.8 461.7 140 512 140c50.3 0 99 9.8 144.8 29.2A370.4 370.4 0 01775.1 249c34.2 34.2 61 74 79.8 118.3C874.2 413 884 461.7 884 512s-9.8 99-29.2 144.8A368.89 368.89 0 01775 775zM512 533c-85.5 0-155.6 67.3-160 151.6a8 8 0 008 8.4h48.1c4.2 0 7.8-3.2 8.1-7.4C420 636.1 461.5 597 512 597s92.1 39.1 95.8 88.6c.3 4.2 3.9 7.4 8.1 7.4H664a8 8 0 008-8.4C667.6 600.3 597.5 533 512 533z"></path></svg></span></span><span class="ant-tree-title">leaf</span>
<!----></span>
</div>
@ -171,27 +191,33 @@ exports[`renders ./components/tree/demo/directory.vue correctly 1`] = `
<div class="ant-tree-list-holder">
<div>
<div style="display: flex; flex-direction: column;" class="ant-tree-list-holder-inner">
<div class="ant-tree-treenode ant-tree-treenode-switcher-open"><span aria-hidden="true" class="ant-tree-indent"></span><span class="ant-tree-switcher ant-tree-switcher_open"><span role="img" aria-label="caret-down" class="anticon anticon-caret-down ant-tree-switcher-icon"><svg focusable="false" class="" data-icon="caret-down" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="0 0 1024 1024"><path d="M840.4 300H183.6c-19.7 0-30.7 20.8-18.5 35l328.4 380.8c9.4 10.9 27.5 10.9 37 0L858.9 335c12.2-14.2 1.2-35-18.5-35z"></path></svg></span></span>
<div class="ant-tree-treenode ant-tree-treenode-switcher-open" draggable="false" aria-grabbed="false"><span aria-hidden="true" class="ant-tree-indent"></span>
<!----><span class="ant-tree-switcher ant-tree-switcher_open"><span role="img" aria-label="caret-down" class="anticon anticon-caret-down ant-tree-switcher-icon"><svg focusable="false" class="" data-icon="caret-down" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="0 0 1024 1024"><path d="M840.4 300H183.6c-19.7 0-30.7 20.8-18.5 35l328.4 380.8c9.4 10.9 27.5 10.9 37 0L858.9 335c12.2-14.2 1.2-35-18.5-35z"></path></svg></span></span>
<!----><span title="parent 0" class="ant-tree-node-content-wrapper ant-tree-node-content-wrapper-open"><span class="ant-tree-iconEle ant-tree-icon__customize"><span role="img" aria-label="folder-open" class="anticon anticon-folder-open"><svg focusable="false" class="" data-icon="folder-open" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M928 444H820V330.4c0-17.7-14.3-32-32-32H473L355.7 186.2a8.15 8.15 0 00-5.5-2.2H96c-17.7 0-32 14.3-32 32v592c0 17.7 14.3 32 32 32h698c13 0 24.8-7.9 29.7-20l134-332c1.5-3.8 2.3-7.9 2.3-12 0-17.7-14.3-32-32-32zM136 256h188.5l119.6 114.4H748V444H238c-13 0-24.8 7.9-29.7 20L136 643.2V256zm635.3 512H159l103.3-256h612.4L771.3 768z"></path></svg></span></span><span class="ant-tree-title">parent 0</span>
<!----></span>
</div>
<div class="ant-tree-treenode"><span aria-hidden="true" class="ant-tree-indent"><span class="ant-tree-indent-unit ant-tree-indent-unit-start"></span></span><span class="ant-tree-switcher ant-tree-switcher-noop"><!----></span>
<div class="ant-tree-treenode" draggable="false" aria-grabbed="false"><span aria-hidden="true" class="ant-tree-indent"><span class="ant-tree-indent-unit ant-tree-indent-unit-start"></span></span>
<!----><span class="ant-tree-switcher ant-tree-switcher-noop"><!----></span>
<!----><span title="leaf 0-0" class="ant-tree-node-content-wrapper ant-tree-node-content-wrapper-normal"><span class="ant-tree-iconEle ant-tree-icon__customize"><span role="img" aria-label="file" class="anticon anticon-file"><svg focusable="false" class="" data-icon="file" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M854.6 288.6L639.4 73.4c-6-6-14.1-9.4-22.6-9.4H192c-17.7 0-32 14.3-32 32v832c0 17.7 14.3 32 32 32h640c17.7 0 32-14.3 32-32V311.3c0-8.5-3.4-16.7-9.4-22.7zM790.2 326H602V137.8L790.2 326zm1.8 562H232V136h302v216a42 42 0 0042 42h216v494z"></path></svg></span></span><span class="ant-tree-title">leaf 0-0</span>
<!----></span>
</div>
<div class="ant-tree-treenode ant-tree-treenode-leaf-last"><span aria-hidden="true" class="ant-tree-indent"><span class="ant-tree-indent-unit ant-tree-indent-unit-start"></span></span><span class="ant-tree-switcher ant-tree-switcher-noop"><!----></span>
<div class="ant-tree-treenode ant-tree-treenode-leaf-last" draggable="false" aria-grabbed="false"><span aria-hidden="true" class="ant-tree-indent"><span class="ant-tree-indent-unit ant-tree-indent-unit-start"></span></span>
<!----><span class="ant-tree-switcher ant-tree-switcher-noop"><!----></span>
<!----><span title="leaf 0-1" class="ant-tree-node-content-wrapper ant-tree-node-content-wrapper-normal"><span class="ant-tree-iconEle ant-tree-icon__customize"><span role="img" aria-label="file" class="anticon anticon-file"><svg focusable="false" class="" data-icon="file" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M854.6 288.6L639.4 73.4c-6-6-14.1-9.4-22.6-9.4H192c-17.7 0-32 14.3-32 32v832c0 17.7 14.3 32 32 32h640c17.7 0 32-14.3 32-32V311.3c0-8.5-3.4-16.7-9.4-22.7zM790.2 326H602V137.8L790.2 326zm1.8 562H232V136h302v216a42 42 0 0042 42h216v494z"></path></svg></span></span><span class="ant-tree-title">leaf 0-1</span>
<!----></span>
</div>
<div class="ant-tree-treenode ant-tree-treenode-switcher-open ant-tree-treenode-leaf-last"><span aria-hidden="true" class="ant-tree-indent"></span><span class="ant-tree-switcher ant-tree-switcher_open"><span role="img" aria-label="caret-down" class="anticon anticon-caret-down ant-tree-switcher-icon"><svg focusable="false" class="" data-icon="caret-down" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="0 0 1024 1024"><path d="M840.4 300H183.6c-19.7 0-30.7 20.8-18.5 35l328.4 380.8c9.4 10.9 27.5 10.9 37 0L858.9 335c12.2-14.2 1.2-35-18.5-35z"></path></svg></span></span>
<div class="ant-tree-treenode ant-tree-treenode-switcher-open ant-tree-treenode-leaf-last" draggable="false" aria-grabbed="false"><span aria-hidden="true" class="ant-tree-indent"></span>
<!----><span class="ant-tree-switcher ant-tree-switcher_open"><span role="img" aria-label="caret-down" class="anticon anticon-caret-down ant-tree-switcher-icon"><svg focusable="false" class="" data-icon="caret-down" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="0 0 1024 1024"><path d="M840.4 300H183.6c-19.7 0-30.7 20.8-18.5 35l328.4 380.8c9.4 10.9 27.5 10.9 37 0L858.9 335c12.2-14.2 1.2-35-18.5-35z"></path></svg></span></span>
<!----><span title="parent 1" class="ant-tree-node-content-wrapper ant-tree-node-content-wrapper-open"><span class="ant-tree-iconEle ant-tree-icon__customize"><span role="img" aria-label="folder-open" class="anticon anticon-folder-open"><svg focusable="false" class="" data-icon="folder-open" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M928 444H820V330.4c0-17.7-14.3-32-32-32H473L355.7 186.2a8.15 8.15 0 00-5.5-2.2H96c-17.7 0-32 14.3-32 32v592c0 17.7 14.3 32 32 32h698c13 0 24.8-7.9 29.7-20l134-332c1.5-3.8 2.3-7.9 2.3-12 0-17.7-14.3-32-32-32zM136 256h188.5l119.6 114.4H748V444H238c-13 0-24.8 7.9-29.7 20L136 643.2V256zm635.3 512H159l103.3-256h612.4L771.3 768z"></path></svg></span></span><span class="ant-tree-title">parent 1</span>
<!----></span>
</div>
<div class="ant-tree-treenode"><span aria-hidden="true" class="ant-tree-indent"><span class="ant-tree-indent-unit ant-tree-indent-unit-end"></span></span><span class="ant-tree-switcher ant-tree-switcher-noop"><!----></span>
<div class="ant-tree-treenode" draggable="false" aria-grabbed="false"><span aria-hidden="true" class="ant-tree-indent"><span class="ant-tree-indent-unit ant-tree-indent-unit-end"></span></span>
<!----><span class="ant-tree-switcher ant-tree-switcher-noop"><!----></span>
<!----><span title="leaf 1-0" class="ant-tree-node-content-wrapper ant-tree-node-content-wrapper-normal"><span class="ant-tree-iconEle ant-tree-icon__customize"><span role="img" aria-label="file" class="anticon anticon-file"><svg focusable="false" class="" data-icon="file" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M854.6 288.6L639.4 73.4c-6-6-14.1-9.4-22.6-9.4H192c-17.7 0-32 14.3-32 32v832c0 17.7 14.3 32 32 32h640c17.7 0 32-14.3 32-32V311.3c0-8.5-3.4-16.7-9.4-22.7zM790.2 326H602V137.8L790.2 326zm1.8 562H232V136h302v216a42 42 0 0042 42h216v494z"></path></svg></span></span><span class="ant-tree-title">leaf 1-0</span>
<!----></span>
</div>
<div class="ant-tree-treenode ant-tree-treenode-leaf-last"><span aria-hidden="true" class="ant-tree-indent"><span class="ant-tree-indent-unit ant-tree-indent-unit-end"></span></span><span class="ant-tree-switcher ant-tree-switcher-noop"><!----></span>
<div class="ant-tree-treenode ant-tree-treenode-leaf-last" draggable="false" aria-grabbed="false"><span aria-hidden="true" class="ant-tree-indent"><span class="ant-tree-indent-unit ant-tree-indent-unit-end"></span></span>
<!----><span class="ant-tree-switcher ant-tree-switcher-noop"><!----></span>
<!----><span title="leaf 1-1" class="ant-tree-node-content-wrapper ant-tree-node-content-wrapper-normal"><span class="ant-tree-iconEle ant-tree-icon__customize"><span role="img" aria-label="file" class="anticon anticon-file"><svg focusable="false" class="" data-icon="file" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M854.6 288.6L639.4 73.4c-6-6-14.1-9.4-22.6-9.4H192c-17.7 0-32 14.3-32 32v832c0 17.7 14.3 32 32 32h640c17.7 0 32-14.3 32-32V311.3c0-8.5-3.4-16.7-9.4-22.7zM790.2 326H602V137.8L790.2 326zm1.8 562H232V136h302v216a42 42 0 0042 42h216v494z"></path></svg></span></span><span class="ant-tree-title">leaf 1-1</span>
<!----></span>
</div>
@ -216,16 +242,19 @@ exports[`renders ./components/tree/demo/draggable.vue correctly 1`] = `
<div class="ant-tree-list-holder">
<div>
<div style="display: flex; flex-direction: column;" class="ant-tree-list-holder-inner">
<div class="ant-tree-treenode ant-tree-treenode-switcher-close"><span aria-hidden="true" class="ant-tree-indent"></span><span class="ant-tree-switcher ant-tree-switcher_close"><span role="img" aria-label="caret-down" class="anticon anticon-caret-down ant-tree-switcher-icon"><svg focusable="false" class="" data-icon="caret-down" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="0 0 1024 1024"><path d="M840.4 300H183.6c-19.7 0-30.7 20.8-18.5 35l328.4 380.8c9.4 10.9 27.5 10.9 37 0L858.9 335c12.2-14.2 1.2-35-18.5-35z"></path></svg></span></span>
<!----><span title="0-0" class="ant-tree-node-content-wrapper ant-tree-node-content-wrapper-close draggable" draggable="true" aria-grabbed="true"><!----><span class="ant-tree-title">0-0</span>
<div class="ant-tree-treenode ant-tree-treenode-switcher-close ant-tree-treenode-draggable" draggable="true" aria-grabbed="false"><span aria-hidden="true" class="ant-tree-indent"></span>
<!----><span class="ant-tree-switcher ant-tree-switcher_close"><span role="img" aria-label="caret-down" class="anticon anticon-caret-down ant-tree-switcher-icon"><svg focusable="false" class="" data-icon="caret-down" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="0 0 1024 1024"><path d="M840.4 300H183.6c-19.7 0-30.7 20.8-18.5 35l328.4 380.8c9.4 10.9 27.5 10.9 37 0L858.9 335c12.2-14.2 1.2-35-18.5-35z"></path></svg></span></span>
<!----><span title="0-0" class="ant-tree-node-content-wrapper ant-tree-node-content-wrapper-close"><!----><span class="ant-tree-title">0-0</span>
<!----></span>
</div>
<div class="ant-tree-treenode ant-tree-treenode-switcher-close"><span aria-hidden="true" class="ant-tree-indent"></span><span class="ant-tree-switcher ant-tree-switcher_close"><span role="img" aria-label="caret-down" class="anticon anticon-caret-down ant-tree-switcher-icon"><svg focusable="false" class="" data-icon="caret-down" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="0 0 1024 1024"><path d="M840.4 300H183.6c-19.7 0-30.7 20.8-18.5 35l328.4 380.8c9.4 10.9 27.5 10.9 37 0L858.9 335c12.2-14.2 1.2-35-18.5-35z"></path></svg></span></span>
<!----><span title="0-1" class="ant-tree-node-content-wrapper ant-tree-node-content-wrapper-close draggable" draggable="true" aria-grabbed="true"><!----><span class="ant-tree-title">0-1</span>
<div class="ant-tree-treenode ant-tree-treenode-switcher-close ant-tree-treenode-draggable" draggable="true" aria-grabbed="false"><span aria-hidden="true" class="ant-tree-indent"></span>
<!----><span class="ant-tree-switcher ant-tree-switcher_close"><span role="img" aria-label="caret-down" class="anticon anticon-caret-down ant-tree-switcher-icon"><svg focusable="false" class="" data-icon="caret-down" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="0 0 1024 1024"><path d="M840.4 300H183.6c-19.7 0-30.7 20.8-18.5 35l328.4 380.8c9.4 10.9 27.5 10.9 37 0L858.9 335c12.2-14.2 1.2-35-18.5-35z"></path></svg></span></span>
<!----><span title="0-1" class="ant-tree-node-content-wrapper ant-tree-node-content-wrapper-close"><!----><span class="ant-tree-title">0-1</span>
<!----></span>
</div>
<div class="ant-tree-treenode ant-tree-treenode-switcher-close ant-tree-treenode-leaf-last"><span aria-hidden="true" class="ant-tree-indent"></span><span class="ant-tree-switcher ant-tree-switcher-noop"><!----></span>
<!----><span title="0-2" class="ant-tree-node-content-wrapper ant-tree-node-content-wrapper-normal draggable" draggable="true" aria-grabbed="true"><!----><span class="ant-tree-title">0-2</span>
<div class="ant-tree-treenode ant-tree-treenode-switcher-close ant-tree-treenode-leaf-last ant-tree-treenode-draggable" draggable="true" aria-grabbed="false"><span aria-hidden="true" class="ant-tree-indent"></span>
<!----><span class="ant-tree-switcher ant-tree-switcher-noop"><!----></span>
<!----><span title="0-2" class="ant-tree-node-content-wrapper ant-tree-node-content-wrapper-normal"><!----><span class="ant-tree-title">0-2</span>
<!----></span>
</div>
</div>
@ -249,15 +278,18 @@ exports[`renders ./components/tree/demo/dynamic.vue correctly 1`] = `
<div class="ant-tree-list-holder">
<div>
<div style="display: flex; flex-direction: column;" class="ant-tree-list-holder-inner">
<div class="ant-tree-treenode ant-tree-treenode-switcher-close"><span aria-hidden="true" class="ant-tree-indent"></span><span class="ant-tree-switcher ant-tree-switcher_close"><span role="img" aria-label="caret-down" class="anticon anticon-caret-down ant-tree-switcher-icon"><svg focusable="false" class="" data-icon="caret-down" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="0 0 1024 1024"><path d="M840.4 300H183.6c-19.7 0-30.7 20.8-18.5 35l328.4 380.8c9.4 10.9 27.5 10.9 37 0L858.9 335c12.2-14.2 1.2-35-18.5-35z"></path></svg></span></span>
<div class="ant-tree-treenode ant-tree-treenode-switcher-close" draggable="false" aria-grabbed="false"><span aria-hidden="true" class="ant-tree-indent"></span>
<!----><span class="ant-tree-switcher ant-tree-switcher_close"><span role="img" aria-label="caret-down" class="anticon anticon-caret-down ant-tree-switcher-icon"><svg focusable="false" class="" data-icon="caret-down" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="0 0 1024 1024"><path d="M840.4 300H183.6c-19.7 0-30.7 20.8-18.5 35l328.4 380.8c9.4 10.9 27.5 10.9 37 0L858.9 335c12.2-14.2 1.2-35-18.5-35z"></path></svg></span></span>
<!----><span title="Expand to load" class="ant-tree-node-content-wrapper ant-tree-node-content-wrapper-close"><!----><span class="ant-tree-title">Expand to load</span>
<!----></span>
</div>
<div class="ant-tree-treenode ant-tree-treenode-switcher-close"><span aria-hidden="true" class="ant-tree-indent"></span><span class="ant-tree-switcher ant-tree-switcher_close"><span role="img" aria-label="caret-down" class="anticon anticon-caret-down ant-tree-switcher-icon"><svg focusable="false" class="" data-icon="caret-down" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="0 0 1024 1024"><path d="M840.4 300H183.6c-19.7 0-30.7 20.8-18.5 35l328.4 380.8c9.4 10.9 27.5 10.9 37 0L858.9 335c12.2-14.2 1.2-35-18.5-35z"></path></svg></span></span>
<div class="ant-tree-treenode ant-tree-treenode-switcher-close" draggable="false" aria-grabbed="false"><span aria-hidden="true" class="ant-tree-indent"></span>
<!----><span class="ant-tree-switcher ant-tree-switcher_close"><span role="img" aria-label="caret-down" class="anticon anticon-caret-down ant-tree-switcher-icon"><svg focusable="false" class="" data-icon="caret-down" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="0 0 1024 1024"><path d="M840.4 300H183.6c-19.7 0-30.7 20.8-18.5 35l328.4 380.8c9.4 10.9 27.5 10.9 37 0L858.9 335c12.2-14.2 1.2-35-18.5-35z"></path></svg></span></span>
<!----><span title="Expand to load" class="ant-tree-node-content-wrapper ant-tree-node-content-wrapper-close"><!----><span class="ant-tree-title">Expand to load</span>
<!----></span>
</div>
<div class="ant-tree-treenode ant-tree-treenode-leaf-last"><span aria-hidden="true" class="ant-tree-indent"></span><span class="ant-tree-switcher ant-tree-switcher-noop"><!----></span>
<div class="ant-tree-treenode ant-tree-treenode-leaf-last" draggable="false" aria-grabbed="false"><span aria-hidden="true" class="ant-tree-indent"></span>
<!----><span class="ant-tree-switcher ant-tree-switcher-noop"><!----></span>
<!----><span title="Tree Node" class="ant-tree-node-content-wrapper ant-tree-node-content-wrapper-normal"><!----><span class="ant-tree-title">Tree Node</span>
<!----></span>
</div>
@ -288,35 +320,43 @@ exports[`renders ./components/tree/demo/line.vue correctly 1`] = `
<div class="ant-tree-list-holder">
<div>
<div style="display: flex; flex-direction: column;" class="ant-tree-list-holder-inner">
<div class="ant-tree-treenode ant-tree-treenode-switcher-open"><span aria-hidden="true" class="ant-tree-indent"></span><span class="ant-tree-switcher ant-tree-switcher_open"><span role="img" aria-label="minus-square" class="anticon anticon-minus-square ant-tree-switcher-line-icon"><svg focusable="false" class="" data-icon="minus-square" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M328 544h368c4.4 0 8-3.6 8-8v-48c0-4.4-3.6-8-8-8H328c-4.4 0-8 3.6-8 8v48c0 4.4 3.6 8 8 8z"></path><path d="M880 112H144c-17.7 0-32 14.3-32 32v736c0 17.7 14.3 32 32 32h736c17.7 0 32-14.3 32-32V144c0-17.7-14.3-32-32-32zm-40 728H184V184h656v656z"></path></svg></span></span>
<div class="ant-tree-treenode ant-tree-treenode-switcher-open" draggable="false" aria-grabbed="false"><span aria-hidden="true" class="ant-tree-indent"></span>
<!----><span class="ant-tree-switcher ant-tree-switcher_open"><span role="img" aria-label="minus-square" class="anticon anticon-minus-square ant-tree-switcher-line-icon"><svg focusable="false" class="" data-icon="minus-square" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M328 544h368c4.4 0 8-3.6 8-8v-48c0-4.4-3.6-8-8-8H328c-4.4 0-8 3.6-8 8v48c0 4.4 3.6 8 8 8z"></path><path d="M880 112H144c-17.7 0-32 14.3-32 32v736c0 17.7 14.3 32 32 32h736c17.7 0 32-14.3 32-32V144c0-17.7-14.3-32-32-32zm-40 728H184V184h656v656z"></path></svg></span></span>
<!----><span title="" class="ant-tree-node-content-wrapper ant-tree-node-content-wrapper-open"><!----><span class="ant-tree-title">parent 1</span>
<!----></span>
</div>
<div class="ant-tree-treenode ant-tree-treenode-switcher-open"><span aria-hidden="true" class="ant-tree-indent"><span class="ant-tree-indent-unit ant-tree-indent-unit-start"></span></span><span class="ant-tree-switcher ant-tree-switcher_open"><span role="img" aria-label="minus-square" class="anticon anticon-minus-square ant-tree-switcher-line-icon"><svg focusable="false" class="" data-icon="minus-square" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M328 544h368c4.4 0 8-3.6 8-8v-48c0-4.4-3.6-8-8-8H328c-4.4 0-8 3.6-8 8v48c0 4.4 3.6 8 8 8z"></path><path d="M880 112H144c-17.7 0-32 14.3-32 32v736c0 17.7 14.3 32 32 32h736c17.7 0 32-14.3 32-32V144c0-17.7-14.3-32-32-32zm-40 728H184V184h656v656z"></path></svg></span></span>
<div class="ant-tree-treenode ant-tree-treenode-switcher-open" draggable="false" aria-grabbed="false"><span aria-hidden="true" class="ant-tree-indent"><span class="ant-tree-indent-unit ant-tree-indent-unit-start"></span></span>
<!----><span class="ant-tree-switcher ant-tree-switcher_open"><span role="img" aria-label="minus-square" class="anticon anticon-minus-square ant-tree-switcher-line-icon"><svg focusable="false" class="" data-icon="minus-square" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M328 544h368c4.4 0 8-3.6 8-8v-48c0-4.4-3.6-8-8-8H328c-4.4 0-8 3.6-8 8v48c0 4.4 3.6 8 8 8z"></path><path d="M880 112H144c-17.7 0-32 14.3-32 32v736c0 17.7 14.3 32 32 32h736c17.7 0 32-14.3 32-32V144c0-17.7-14.3-32-32-32zm-40 728H184V184h656v656z"></path></svg></span></span>
<!----><span title="" class="ant-tree-node-content-wrapper ant-tree-node-content-wrapper-open"><!----><span class="ant-tree-title">parent 1-0</span>
<!----></span>
</div>
<div class="ant-tree-treenode ant-tree-treenode-switcher-close"><span aria-hidden="true" class="ant-tree-indent"><span class="ant-tree-indent-unit ant-tree-indent-unit-start"></span><span class="ant-tree-indent-unit ant-tree-indent-unit-start"></span></span><span class="ant-tree-switcher ant-tree-switcher-noop"><span role="img" aria-label="file" class="anticon anticon-file ant-tree-switcher-line-icon"><svg focusable="false" class="" data-icon="file" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M854.6 288.6L639.4 73.4c-6-6-14.1-9.4-22.6-9.4H192c-17.7 0-32 14.3-32 32v832c0 17.7 14.3 32 32 32h640c17.7 0 32-14.3 32-32V311.3c0-8.5-3.4-16.7-9.4-22.7zM790.2 326H602V137.8L790.2 326zm1.8 562H232V136h302v216a42 42 0 0042 42h216v494z"></path></svg></span></span>
<div class="ant-tree-treenode ant-tree-treenode-switcher-close" draggable="false" aria-grabbed="false"><span aria-hidden="true" class="ant-tree-indent"><span class="ant-tree-indent-unit ant-tree-indent-unit-start"></span><span class="ant-tree-indent-unit ant-tree-indent-unit-start"></span></span>
<!----><span class="ant-tree-switcher ant-tree-switcher-noop"><span role="img" aria-label="file" class="anticon anticon-file ant-tree-switcher-line-icon"><svg focusable="false" class="" data-icon="file" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M854.6 288.6L639.4 73.4c-6-6-14.1-9.4-22.6-9.4H192c-17.7 0-32 14.3-32 32v832c0 17.7 14.3 32 32 32h640c17.7 0 32-14.3 32-32V311.3c0-8.5-3.4-16.7-9.4-22.7zM790.2 326H602V137.8L790.2 326zm1.8 562H232V136h302v216a42 42 0 0042 42h216v494z"></path></svg></span></span>
<!----><span title="" class="ant-tree-node-content-wrapper ant-tree-node-content-wrapper-normal"><!----><span class="ant-tree-title">leaf</span>
<!----></span>
</div>
<div class="ant-tree-treenode ant-tree-treenode-switcher-close"><span aria-hidden="true" class="ant-tree-indent"><span class="ant-tree-indent-unit ant-tree-indent-unit-start"></span><span class="ant-tree-indent-unit ant-tree-indent-unit-start"></span></span><span class="ant-tree-switcher ant-tree-switcher-noop"><span role="img" aria-label="file" class="anticon anticon-file ant-tree-switcher-line-icon"><svg focusable="false" class="" data-icon="file" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M854.6 288.6L639.4 73.4c-6-6-14.1-9.4-22.6-9.4H192c-17.7 0-32 14.3-32 32v832c0 17.7 14.3 32 32 32h640c17.7 0 32-14.3 32-32V311.3c0-8.5-3.4-16.7-9.4-22.7zM790.2 326H602V137.8L790.2 326zm1.8 562H232V136h302v216a42 42 0 0042 42h216v494z"></path></svg></span></span>
<div class="ant-tree-treenode ant-tree-treenode-switcher-close" draggable="false" aria-grabbed="false"><span aria-hidden="true" class="ant-tree-indent"><span class="ant-tree-indent-unit ant-tree-indent-unit-start"></span><span class="ant-tree-indent-unit ant-tree-indent-unit-start"></span></span>
<!----><span class="ant-tree-switcher ant-tree-switcher-noop"><span role="img" aria-label="file" class="anticon anticon-file ant-tree-switcher-line-icon"><svg focusable="false" class="" data-icon="file" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M854.6 288.6L639.4 73.4c-6-6-14.1-9.4-22.6-9.4H192c-17.7 0-32 14.3-32 32v832c0 17.7 14.3 32 32 32h640c17.7 0 32-14.3 32-32V311.3c0-8.5-3.4-16.7-9.4-22.7zM790.2 326H602V137.8L790.2 326zm1.8 562H232V136h302v216a42 42 0 0042 42h216v494z"></path></svg></span></span>
<!----><span title="" class="ant-tree-node-content-wrapper ant-tree-node-content-wrapper-normal"><!----><span class="ant-tree-title"><div>multiple line title</div><div>multiple line title</div></span>
<!----></span>
</div>
<div class="ant-tree-treenode ant-tree-treenode-switcher-close ant-tree-treenode-leaf-last"><span aria-hidden="true" class="ant-tree-indent"><span class="ant-tree-indent-unit ant-tree-indent-unit-start"></span><span class="ant-tree-indent-unit ant-tree-indent-unit-start"></span></span><span class="ant-tree-switcher ant-tree-switcher-noop"><span role="img" aria-label="file" class="anticon anticon-file ant-tree-switcher-line-icon"><svg focusable="false" class="" data-icon="file" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M854.6 288.6L639.4 73.4c-6-6-14.1-9.4-22.6-9.4H192c-17.7 0-32 14.3-32 32v832c0 17.7 14.3 32 32 32h640c17.7 0 32-14.3 32-32V311.3c0-8.5-3.4-16.7-9.4-22.7zM790.2 326H602V137.8L790.2 326zm1.8 562H232V136h302v216a42 42 0 0042 42h216v494z"></path></svg></span></span>
<div class="ant-tree-treenode ant-tree-treenode-switcher-close ant-tree-treenode-leaf-last" draggable="false" aria-grabbed="false"><span aria-hidden="true" class="ant-tree-indent"><span class="ant-tree-indent-unit ant-tree-indent-unit-start"></span><span class="ant-tree-indent-unit ant-tree-indent-unit-start"></span></span>
<!----><span class="ant-tree-switcher ant-tree-switcher-noop"><span role="img" aria-label="file" class="anticon anticon-file ant-tree-switcher-line-icon"><svg focusable="false" class="" data-icon="file" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M854.6 288.6L639.4 73.4c-6-6-14.1-9.4-22.6-9.4H192c-17.7 0-32 14.3-32 32v832c0 17.7 14.3 32 32 32h640c17.7 0 32-14.3 32-32V311.3c0-8.5-3.4-16.7-9.4-22.7zM790.2 326H602V137.8L790.2 326zm1.8 562H232V136h302v216a42 42 0 0042 42h216v494z"></path></svg></span></span>
<!----><span title="" class="ant-tree-node-content-wrapper ant-tree-node-content-wrapper-normal"><!----><span class="ant-tree-title">leaf</span>
<!----></span>
</div>
<div class="ant-tree-treenode ant-tree-treenode-switcher-close"><span aria-hidden="true" class="ant-tree-indent"><span class="ant-tree-indent-unit ant-tree-indent-unit-start"></span></span><span class="ant-tree-switcher ant-tree-switcher_close"><span role="img" aria-label="plus-square" class="anticon anticon-plus-square ant-tree-switcher-line-icon"><svg focusable="false" class="" data-icon="plus-square" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M328 544h152v152c0 4.4 3.6 8 8 8h48c4.4 0 8-3.6 8-8V544h152c4.4 0 8-3.6 8-8v-48c0-4.4-3.6-8-8-8H544V328c0-4.4-3.6-8-8-8h-48c-4.4 0-8 3.6-8 8v152H328c-4.4 0-8 3.6-8 8v48c0 4.4 3.6 8 8 8z"></path><path d="M880 112H144c-17.7 0-32 14.3-32 32v736c0 17.7 14.3 32 32 32h736c17.7 0 32-14.3 32-32V144c0-17.7-14.3-32-32-32zm-40 728H184V184h656v656z"></path></svg></span></span>
<div class="ant-tree-treenode ant-tree-treenode-switcher-close" draggable="false" aria-grabbed="false"><span aria-hidden="true" class="ant-tree-indent"><span class="ant-tree-indent-unit ant-tree-indent-unit-start"></span></span>
<!----><span class="ant-tree-switcher ant-tree-switcher_close"><span role="img" aria-label="plus-square" class="anticon anticon-plus-square ant-tree-switcher-line-icon"><svg focusable="false" class="" data-icon="plus-square" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M328 544h152v152c0 4.4 3.6 8 8 8h48c4.4 0 8-3.6 8-8V544h152c4.4 0 8-3.6 8-8v-48c0-4.4-3.6-8-8-8H544V328c0-4.4-3.6-8-8-8h-48c-4.4 0-8 3.6-8 8v152H328c-4.4 0-8 3.6-8 8v48c0 4.4 3.6 8 8 8z"></path><path d="M880 112H144c-17.7 0-32 14.3-32 32v736c0 17.7 14.3 32 32 32h736c17.7 0 32-14.3 32-32V144c0-17.7-14.3-32-32-32zm-40 728H184V184h656v656z"></path></svg></span></span>
<!----><span title="" class="ant-tree-node-content-wrapper ant-tree-node-content-wrapper-close"><!----><span class="ant-tree-title">parent 1-1</span>
<!----></span>
</div>
<div class="ant-tree-treenode ant-tree-treenode-switcher-close ant-tree-treenode-leaf-last"><span aria-hidden="true" class="ant-tree-indent"><span class="ant-tree-indent-unit ant-tree-indent-unit-start"></span></span><span class="ant-tree-switcher ant-tree-switcher_close"><span role="img" aria-label="smile" class="anticon anticon-smile"><svg focusable="false" class="" data-icon="smile" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm0 820c-205.4 0-372-166.6-372-372s166.6-372 372-372 372 166.6 372 372-166.6 372-372 372z" fill="#1890ff"></path><path d="M512 140c-205.4 0-372 166.6-372 372s166.6 372 372 372 372-166.6 372-372-166.6-372-372-372zM288 421a48.01 48.01 0 0196 0 48.01 48.01 0 01-96 0zm224 272c-85.5 0-155.6-67.3-160-151.6a8 8 0 018-8.4h48.1c4.2 0 7.8 3.2 8.1 7.4C420 589.9 461.5 629 512 629s92.1-39.1 95.8-88.6c.3-4.2 3.9-7.4 8.1-7.4H664a8 8 0 018 8.4C667.6 625.7 597.5 693 512 693zm176-224a48.01 48.01 0 010-96 48.01 48.01 0 010 96z" fill="#e6f7ff"></path><path d="M288 421a48 48 0 1096 0 48 48 0 10-96 0zm376 112h-48.1c-4.2 0-7.8 3.2-8.1 7.4-3.7 49.5-45.3 88.6-95.8 88.6s-92-39.1-95.8-88.6c-.3-4.2-3.9-7.4-8.1-7.4H360a8 8 0 00-8 8.4c4.4 84.3 74.5 151.6 160 151.6s155.6-67.3 160-151.6a8 8 0 00-8-8.4zm-24-112a48 48 0 1096 0 48 48 0 10-96 0z" fill="#1890ff"></path></svg></span></span>
<div class="ant-tree-treenode ant-tree-treenode-switcher-close ant-tree-treenode-leaf-last" draggable="false" aria-grabbed="false"><span aria-hidden="true" class="ant-tree-indent"><span class="ant-tree-indent-unit ant-tree-indent-unit-start"></span></span>
<!----><span class="ant-tree-switcher ant-tree-switcher_close"><span role="img" aria-label="smile" class="anticon anticon-smile"><svg focusable="false" class="" data-icon="smile" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm0 820c-205.4 0-372-166.6-372-372s166.6-372 372-372 372 166.6 372 372-166.6 372-372 372z" fill="#1890ff"></path><path d="M512 140c-205.4 0-372 166.6-372 372s166.6 372 372 372 372-166.6 372-372-166.6-372-372-372zM288 421a48.01 48.01 0 0196 0 48.01 48.01 0 01-96 0zm224 272c-85.5 0-155.6-67.3-160-151.6a8 8 0 018-8.4h48.1c4.2 0 7.8 3.2 8.1 7.4C420 589.9 461.5 629 512 629s92.1-39.1 95.8-88.6c.3-4.2 3.9-7.4 8.1-7.4H664a8 8 0 018 8.4C667.6 625.7 597.5 693 512 693zm176-224a48.01 48.01 0 010-96 48.01 48.01 0 010 96z" fill="#e6f7ff"></path><path d="M288 421a48 48 0 1096 0 48 48 0 10-96 0zm376 112h-48.1c-4.2 0-7.8 3.2-8.1 7.4-3.7 49.5-45.3 88.6-95.8 88.6s-92-39.1-95.8-88.6c-.3-4.2-3.9-7.4-8.1-7.4H360a8 8 0 00-8 8.4c4.4 84.3 74.5 151.6 160 151.6s155.6-67.3 160-151.6a8 8 0 00-8-8.4zm-24-112a48 48 0 1096 0 48 48 0 10-96 0z" fill="#1890ff"></path></svg></span></span>
<!----><span title="" class="ant-tree-node-content-wrapper ant-tree-node-content-wrapper-close"><!----><span class="ant-tree-title">parent 1-2</span>
<!----></span>
</div>
<div class="ant-tree-treenode ant-tree-treenode-switcher-close ant-tree-treenode-leaf-last"><span aria-hidden="true" class="ant-tree-indent"></span><span class="ant-tree-switcher ant-tree-switcher_close"><span role="img" aria-label="plus-square" class="anticon anticon-plus-square ant-tree-switcher-line-icon"><svg focusable="false" class="" data-icon="plus-square" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M328 544h152v152c0 4.4 3.6 8 8 8h48c4.4 0 8-3.6 8-8V544h152c4.4 0 8-3.6 8-8v-48c0-4.4-3.6-8-8-8H544V328c0-4.4-3.6-8-8-8h-48c-4.4 0-8 3.6-8 8v152H328c-4.4 0-8 3.6-8 8v48c0 4.4 3.6 8 8 8z"></path><path d="M880 112H144c-17.7 0-32 14.3-32 32v736c0 17.7 14.3 32 32 32h736c17.7 0 32-14.3 32-32V144c0-17.7-14.3-32-32-32zm-40 728H184V184h656v656z"></path></svg></span></span>
<div class="ant-tree-treenode ant-tree-treenode-switcher-close ant-tree-treenode-leaf-last" draggable="false" aria-grabbed="false"><span aria-hidden="true" class="ant-tree-indent"></span>
<!----><span class="ant-tree-switcher ant-tree-switcher_close"><span role="img" aria-label="plus-square" class="anticon anticon-plus-square ant-tree-switcher-line-icon"><svg focusable="false" class="" data-icon="plus-square" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M328 544h152v152c0 4.4 3.6 8 8 8h48c4.4 0 8-3.6 8-8V544h152c4.4 0 8-3.6 8-8v-48c0-4.4-3.6-8-8-8H544V328c0-4.4-3.6-8-8-8h-48c-4.4 0-8 3.6-8 8v152H328c-4.4 0-8 3.6-8 8v48c0 4.4 3.6 8 8 8z"></path><path d="M880 112H144c-17.7 0-32 14.3-32 32v736c0 17.7 14.3 32 32 32h736c17.7 0 32-14.3 32-32V144c0-17.7-14.3-32-32-32zm-40 728H184V184h656v656z"></path></svg></span></span>
<!----><span title="" class="ant-tree-node-content-wrapper ant-tree-node-content-wrapper-close"><!----><span class="ant-tree-title">parent 2</span>
<!----></span>
</div>
@ -342,22 +382,28 @@ exports[`renders ./components/tree/demo/replaceFields.vue correctly 1`] = `
<div class="ant-tree-list-holder">
<div>
<div style="display: flex; flex-direction: column;" class="ant-tree-list-holder-inner">
<div class="ant-tree-treenode ant-tree-treenode-switcher-open ant-tree-treenode-checkbox-checked ant-tree-treenode-leaf-last"><span aria-hidden="true" class="ant-tree-indent"></span><span class="ant-tree-switcher ant-tree-switcher_open"><span role="img" aria-label="caret-down" class="anticon anticon-caret-down ant-tree-switcher-icon"><svg focusable="false" class="" data-icon="caret-down" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="0 0 1024 1024"><path d="M840.4 300H183.6c-19.7 0-30.7 20.8-18.5 35l328.4 380.8c9.4 10.9 27.5 10.9 37 0L858.9 335c12.2-14.2 1.2-35-18.5-35z"></path></svg></span></span><span class="ant-tree-checkbox ant-tree-checkbox-checked"><span class="ant-tree-checkbox-inner"></span></span><span title="" class="ant-tree-node-content-wrapper ant-tree-node-content-wrapper-open"><!----><span class="ant-tree-title">parent 1</span>
<div class="ant-tree-treenode ant-tree-treenode-switcher-open ant-tree-treenode-checkbox-checked ant-tree-treenode-leaf-last" draggable="false" aria-grabbed="false"><span aria-hidden="true" class="ant-tree-indent"></span>
<!----><span class="ant-tree-switcher ant-tree-switcher_open"><span role="img" aria-label="caret-down" class="anticon anticon-caret-down ant-tree-switcher-icon"><svg focusable="false" class="" data-icon="caret-down" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="0 0 1024 1024"><path d="M840.4 300H183.6c-19.7 0-30.7 20.8-18.5 35l328.4 380.8c9.4 10.9 27.5 10.9 37 0L858.9 335c12.2-14.2 1.2-35-18.5-35z"></path></svg></span></span><span class="ant-tree-checkbox ant-tree-checkbox-checked"><span class="ant-tree-checkbox-inner"></span></span><span title="" class="ant-tree-node-content-wrapper ant-tree-node-content-wrapper-open"><!----><span class="ant-tree-title">parent 1</span>
<!----></span>
</div>
<div class="ant-tree-treenode ant-tree-treenode-disabled ant-tree-treenode-switcher-open ant-tree-treenode-checkbox-checked ant-tree-treenode-selected"><span aria-hidden="true" class="ant-tree-indent"><span class="ant-tree-indent-unit ant-tree-indent-unit-start ant-tree-indent-unit-end"></span></span><span class="ant-tree-switcher ant-tree-switcher_open"><span role="img" aria-label="caret-down" class="anticon anticon-caret-down ant-tree-switcher-icon"><svg focusable="false" class="" data-icon="caret-down" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="0 0 1024 1024"><path d="M840.4 300H183.6c-19.7 0-30.7 20.8-18.5 35l328.4 380.8c9.4 10.9 27.5 10.9 37 0L858.9 335c12.2-14.2 1.2-35-18.5-35z"></path></svg></span></span><span class="ant-tree-checkbox ant-tree-checkbox-checked ant-tree-checkbox-disabled"><span class="ant-tree-checkbox-inner"></span></span><span title="" class="ant-tree-node-content-wrapper ant-tree-node-content-wrapper-open"><!----><span class="ant-tree-title">张晨成</span>
<div class="ant-tree-treenode ant-tree-treenode-disabled ant-tree-treenode-switcher-open ant-tree-treenode-checkbox-checked ant-tree-treenode-selected" draggable="false" aria-grabbed="false"><span aria-hidden="true" class="ant-tree-indent"><span class="ant-tree-indent-unit ant-tree-indent-unit-start ant-tree-indent-unit-end"></span></span>
<!----><span class="ant-tree-switcher ant-tree-switcher_open"><span role="img" aria-label="caret-down" class="anticon anticon-caret-down ant-tree-switcher-icon"><svg focusable="false" class="" data-icon="caret-down" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="0 0 1024 1024"><path d="M840.4 300H183.6c-19.7 0-30.7 20.8-18.5 35l328.4 380.8c9.4 10.9 27.5 10.9 37 0L858.9 335c12.2-14.2 1.2-35-18.5-35z"></path></svg></span></span><span class="ant-tree-checkbox ant-tree-checkbox-checked ant-tree-checkbox-disabled"><span class="ant-tree-checkbox-inner"></span></span><span title="" class="ant-tree-node-content-wrapper ant-tree-node-content-wrapper-open"><!----><span class="ant-tree-title">张晨成</span>
<!----></span>
</div>
<div class="ant-tree-treenode ant-tree-treenode-switcher-close"><span aria-hidden="true" class="ant-tree-indent"><span class="ant-tree-indent-unit ant-tree-indent-unit-start ant-tree-indent-unit-end"></span><span class="ant-tree-indent-unit ant-tree-indent-unit-start"></span></span><span class="ant-tree-switcher ant-tree-switcher-noop"><!----></span><span class="ant-tree-checkbox ant-tree-checkbox-disabled"><span class="ant-tree-checkbox-inner"></span></span><span title="" class="ant-tree-node-content-wrapper ant-tree-node-content-wrapper-normal"><!----><span class="ant-tree-title">leaf</span>
<div class="ant-tree-treenode ant-tree-treenode-switcher-close" draggable="false" aria-grabbed="false"><span aria-hidden="true" class="ant-tree-indent"><span class="ant-tree-indent-unit ant-tree-indent-unit-start ant-tree-indent-unit-end"></span><span class="ant-tree-indent-unit ant-tree-indent-unit-start"></span></span>
<!----><span class="ant-tree-switcher ant-tree-switcher-noop"><!----></span><span class="ant-tree-checkbox ant-tree-checkbox-disabled"><span class="ant-tree-checkbox-inner"></span></span><span title="" class="ant-tree-node-content-wrapper ant-tree-node-content-wrapper-normal"><!----><span class="ant-tree-title">leaf</span>
<!----></span>
</div>
<div class="ant-tree-treenode ant-tree-treenode-switcher-close ant-tree-treenode-leaf-last"><span aria-hidden="true" class="ant-tree-indent"><span class="ant-tree-indent-unit ant-tree-indent-unit-start ant-tree-indent-unit-end"></span><span class="ant-tree-indent-unit ant-tree-indent-unit-start"></span></span><span class="ant-tree-switcher ant-tree-switcher-noop"><!----></span><span class="ant-tree-checkbox"><span class="ant-tree-checkbox-inner"></span></span><span title="" class="ant-tree-node-content-wrapper ant-tree-node-content-wrapper-normal"><!----><span class="ant-tree-title">leaf</span>
<div class="ant-tree-treenode ant-tree-treenode-switcher-close ant-tree-treenode-leaf-last" draggable="false" aria-grabbed="false"><span aria-hidden="true" class="ant-tree-indent"><span class="ant-tree-indent-unit ant-tree-indent-unit-start ant-tree-indent-unit-end"></span><span class="ant-tree-indent-unit ant-tree-indent-unit-start"></span></span>
<!----><span class="ant-tree-switcher ant-tree-switcher-noop"><!----></span><span class="ant-tree-checkbox"><span class="ant-tree-checkbox-inner"></span></span><span title="" class="ant-tree-node-content-wrapper ant-tree-node-content-wrapper-normal"><!----><span class="ant-tree-title">leaf</span>
<!----></span>
</div>
<div class="ant-tree-treenode ant-tree-treenode-switcher-open ant-tree-treenode-checkbox-checked ant-tree-treenode-leaf-last"><span aria-hidden="true" class="ant-tree-indent"><span class="ant-tree-indent-unit ant-tree-indent-unit-start ant-tree-indent-unit-end"></span></span><span class="ant-tree-switcher ant-tree-switcher_open"><span role="img" aria-label="caret-down" class="anticon anticon-caret-down ant-tree-switcher-icon"><svg focusable="false" class="" data-icon="caret-down" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="0 0 1024 1024"><path d="M840.4 300H183.6c-19.7 0-30.7 20.8-18.5 35l328.4 380.8c9.4 10.9 27.5 10.9 37 0L858.9 335c12.2-14.2 1.2-35-18.5-35z"></path></svg></span></span><span class="ant-tree-checkbox ant-tree-checkbox-checked"><span class="ant-tree-checkbox-inner"></span></span><span title="" class="ant-tree-node-content-wrapper ant-tree-node-content-wrapper-open"><!----><span class="ant-tree-title"><span style="color: rgb(24, 144, 255);">parent 1-1</span></span>
<div class="ant-tree-treenode ant-tree-treenode-switcher-open ant-tree-treenode-checkbox-checked ant-tree-treenode-leaf-last" draggable="false" aria-grabbed="false"><span aria-hidden="true" class="ant-tree-indent"><span class="ant-tree-indent-unit ant-tree-indent-unit-start ant-tree-indent-unit-end"></span></span>
<!----><span class="ant-tree-switcher ant-tree-switcher_open"><span role="img" aria-label="caret-down" class="anticon anticon-caret-down ant-tree-switcher-icon"><svg focusable="false" class="" data-icon="caret-down" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="0 0 1024 1024"><path d="M840.4 300H183.6c-19.7 0-30.7 20.8-18.5 35l328.4 380.8c9.4 10.9 27.5 10.9 37 0L858.9 335c12.2-14.2 1.2-35-18.5-35z"></path></svg></span></span><span class="ant-tree-checkbox ant-tree-checkbox-checked"><span class="ant-tree-checkbox-inner"></span></span><span title="" class="ant-tree-node-content-wrapper ant-tree-node-content-wrapper-open"><!----><span class="ant-tree-title"><span style="color: rgb(24, 144, 255);">parent 1-1</span></span>
<!----></span>
</div>
<div class="ant-tree-treenode ant-tree-treenode-switcher-close ant-tree-treenode-checkbox-checked ant-tree-treenode-leaf-last"><span aria-hidden="true" class="ant-tree-indent"><span class="ant-tree-indent-unit ant-tree-indent-unit-start ant-tree-indent-unit-end"></span><span class="ant-tree-indent-unit ant-tree-indent-unit-end"></span></span><span class="ant-tree-switcher ant-tree-switcher-noop"><!----></span><span class="ant-tree-checkbox ant-tree-checkbox-checked"><span class="ant-tree-checkbox-inner"></span></span><span title="" class="ant-tree-node-content-wrapper ant-tree-node-content-wrapper-normal"><!----><span class="ant-tree-title">zcvc</span>
<div class="ant-tree-treenode ant-tree-treenode-switcher-close ant-tree-treenode-checkbox-checked ant-tree-treenode-leaf-last" draggable="false" aria-grabbed="false"><span aria-hidden="true" class="ant-tree-indent"><span class="ant-tree-indent-unit ant-tree-indent-unit-start ant-tree-indent-unit-end"></span><span class="ant-tree-indent-unit ant-tree-indent-unit-end"></span></span>
<!----><span class="ant-tree-switcher ant-tree-switcher-noop"><!----></span><span class="ant-tree-checkbox ant-tree-checkbox-checked"><span class="ant-tree-checkbox-inner"></span></span><span title="" class="ant-tree-node-content-wrapper ant-tree-node-content-wrapper-normal"><!----><span class="ant-tree-title">zcvc</span>
<!----></span>
</div>
</div>
@ -382,15 +428,18 @@ exports[`renders ./components/tree/demo/search.vue correctly 1`] = `
<div class="ant-tree-list-holder">
<div>
<div style="display: flex; flex-direction: column;" class="ant-tree-list-holder-inner">
<div class="ant-tree-treenode ant-tree-treenode-switcher-close"><span aria-hidden="true" class="ant-tree-indent"></span><span class="ant-tree-switcher ant-tree-switcher_close"><span role="img" aria-label="caret-down" class="anticon anticon-caret-down ant-tree-switcher-icon"><svg focusable="false" class="" data-icon="caret-down" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="0 0 1024 1024"><path d="M840.4 300H183.6c-19.7 0-30.7 20.8-18.5 35l328.4 380.8c9.4 10.9 27.5 10.9 37 0L858.9 335c12.2-14.2 1.2-35-18.5-35z"></path></svg></span></span>
<div class="ant-tree-treenode ant-tree-treenode-switcher-close" draggable="false" aria-grabbed="false"><span aria-hidden="true" class="ant-tree-indent"></span>
<!----><span class="ant-tree-switcher ant-tree-switcher_close"><span role="img" aria-label="caret-down" class="anticon anticon-caret-down ant-tree-switcher-icon"><svg focusable="false" class="" data-icon="caret-down" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="0 0 1024 1024"><path d="M840.4 300H183.6c-19.7 0-30.7 20.8-18.5 35l328.4 380.8c9.4 10.9 27.5 10.9 37 0L858.9 335c12.2-14.2 1.2-35-18.5-35z"></path></svg></span></span>
<!----><span title="" class="ant-tree-node-content-wrapper ant-tree-node-content-wrapper-close"><!----><span class="ant-tree-title"><span> <span style="color: rgb(255, 85, 0);"></span> 0-0</span></span>
<!----></span>
</div>
<div class="ant-tree-treenode ant-tree-treenode-switcher-close"><span aria-hidden="true" class="ant-tree-indent"></span><span class="ant-tree-switcher ant-tree-switcher_close"><span role="img" aria-label="caret-down" class="anticon anticon-caret-down ant-tree-switcher-icon"><svg focusable="false" class="" data-icon="caret-down" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="0 0 1024 1024"><path d="M840.4 300H183.6c-19.7 0-30.7 20.8-18.5 35l328.4 380.8c9.4 10.9 27.5 10.9 37 0L858.9 335c12.2-14.2 1.2-35-18.5-35z"></path></svg></span></span>
<div class="ant-tree-treenode ant-tree-treenode-switcher-close" draggable="false" aria-grabbed="false"><span aria-hidden="true" class="ant-tree-indent"></span>
<!----><span class="ant-tree-switcher ant-tree-switcher_close"><span role="img" aria-label="caret-down" class="anticon anticon-caret-down ant-tree-switcher-icon"><svg focusable="false" class="" data-icon="caret-down" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="0 0 1024 1024"><path d="M840.4 300H183.6c-19.7 0-30.7 20.8-18.5 35l328.4 380.8c9.4 10.9 27.5 10.9 37 0L858.9 335c12.2-14.2 1.2-35-18.5-35z"></path></svg></span></span>
<!----><span title="" class="ant-tree-node-content-wrapper ant-tree-node-content-wrapper-close"><!----><span class="ant-tree-title"><span> <span style="color: rgb(255, 85, 0);"></span> 0-1</span></span>
<!----></span>
</div>
<div class="ant-tree-treenode ant-tree-treenode-switcher-close ant-tree-treenode-leaf-last"><span aria-hidden="true" class="ant-tree-indent"></span><span class="ant-tree-switcher ant-tree-switcher-noop"><!----></span>
<div class="ant-tree-treenode ant-tree-treenode-switcher-close ant-tree-treenode-leaf-last" draggable="false" aria-grabbed="false"><span aria-hidden="true" class="ant-tree-indent"></span>
<!----><span class="ant-tree-switcher ant-tree-switcher-noop"><!----></span>
<!----><span title="" class="ant-tree-node-content-wrapper ant-tree-node-content-wrapper-normal"><!----><span class="ant-tree-title"><span> <span style="color: rgb(255, 85, 0);"></span> 0-2</span></span>
<!----></span>
</div>
@ -416,31 +465,38 @@ exports[`renders ./components/tree/demo/switcher-icon.vue correctly 1`] = `
<div class="ant-tree-list-holder">
<div>
<div style="display: flex; flex-direction: column;" class="ant-tree-list-holder-inner">
<div class="ant-tree-treenode ant-tree-treenode-switcher-open ant-tree-treenode-leaf-last"><span aria-hidden="true" class="ant-tree-indent"></span><span class="ant-tree-switcher ant-tree-switcher_open"><span role="img" aria-label="down" class="anticon anticon-down"><svg focusable="false" class="" data-icon="down" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M884 256h-75c-5.1 0-9.9 2.5-12.9 6.6L512 654.2 227.9 262.6c-3-4.1-7.8-6.6-12.9-6.6h-75c-6.5 0-10.3 7.4-6.5 12.7l352.6 486.1c12.8 17.6 39 17.6 51.7 0l352.6-486.1c3.9-5.3.1-12.7-6.4-12.7z"></path></svg></span></span>
<div class="ant-tree-treenode ant-tree-treenode-switcher-open ant-tree-treenode-leaf-last" draggable="false" aria-grabbed="false"><span aria-hidden="true" class="ant-tree-indent"></span>
<!----><span class="ant-tree-switcher ant-tree-switcher_open"><span role="img" aria-label="down" class="anticon anticon-down"><svg focusable="false" class="" data-icon="down" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M884 256h-75c-5.1 0-9.9 2.5-12.9 6.6L512 654.2 227.9 262.6c-3-4.1-7.8-6.6-12.9-6.6h-75c-6.5 0-10.3 7.4-6.5 12.7l352.6 486.1c12.8 17.6 39 17.6 51.7 0l352.6-486.1c3.9-5.3.1-12.7-6.4-12.7z"></path></svg></span></span>
<!----><span title="parent 1" class="ant-tree-node-content-wrapper ant-tree-node-content-wrapper-open"><!----><span class="ant-tree-title">parent 1</span>
<!----></span>
</div>
<div class="ant-tree-treenode ant-tree-treenode-switcher-open"><span aria-hidden="true" class="ant-tree-indent"><span class="ant-tree-indent-unit ant-tree-indent-unit-start ant-tree-indent-unit-end"></span></span><span class="ant-tree-switcher ant-tree-switcher_open"><span role="img" aria-label="down" class="anticon anticon-down"><svg focusable="false" class="" data-icon="down" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M884 256h-75c-5.1 0-9.9 2.5-12.9 6.6L512 654.2 227.9 262.6c-3-4.1-7.8-6.6-12.9-6.6h-75c-6.5 0-10.3 7.4-6.5 12.7l352.6 486.1c12.8 17.6 39 17.6 51.7 0l352.6-486.1c3.9-5.3.1-12.7-6.4-12.7z"></path></svg></span></span>
<div class="ant-tree-treenode ant-tree-treenode-switcher-open" draggable="false" aria-grabbed="false"><span aria-hidden="true" class="ant-tree-indent"><span class="ant-tree-indent-unit ant-tree-indent-unit-start ant-tree-indent-unit-end"></span></span>
<!----><span class="ant-tree-switcher ant-tree-switcher_open"><span role="img" aria-label="down" class="anticon anticon-down"><svg focusable="false" class="" data-icon="down" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M884 256h-75c-5.1 0-9.9 2.5-12.9 6.6L512 654.2 227.9 262.6c-3-4.1-7.8-6.6-12.9-6.6h-75c-6.5 0-10.3 7.4-6.5 12.7l352.6 486.1c12.8 17.6 39 17.6 51.7 0l352.6-486.1c3.9-5.3.1-12.7-6.4-12.7z"></path></svg></span></span>
<!----><span title="parent 1-0" class="ant-tree-node-content-wrapper ant-tree-node-content-wrapper-open"><!----><span class="ant-tree-title">parent 1-0</span>
<!----></span>
</div>
<div class="ant-tree-treenode ant-tree-treenode-switcher-close"><span aria-hidden="true" class="ant-tree-indent"><span class="ant-tree-indent-unit ant-tree-indent-unit-start ant-tree-indent-unit-end"></span><span class="ant-tree-indent-unit ant-tree-indent-unit-start"></span></span><span class="ant-tree-switcher ant-tree-switcher-noop"><span role="img" aria-label="file" class="anticon anticon-file ant-tree-switcher-line-icon"><svg focusable="false" class="" data-icon="file" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M854.6 288.6L639.4 73.4c-6-6-14.1-9.4-22.6-9.4H192c-17.7 0-32 14.3-32 32v832c0 17.7 14.3 32 32 32h640c17.7 0 32-14.3 32-32V311.3c0-8.5-3.4-16.7-9.4-22.7zM790.2 326H602V137.8L790.2 326zm1.8 562H232V136h302v216a42 42 0 0042 42h216v494z"></path></svg></span></span>
<div class="ant-tree-treenode ant-tree-treenode-switcher-close" draggable="false" aria-grabbed="false"><span aria-hidden="true" class="ant-tree-indent"><span class="ant-tree-indent-unit ant-tree-indent-unit-start ant-tree-indent-unit-end"></span><span class="ant-tree-indent-unit ant-tree-indent-unit-start"></span></span>
<!----><span class="ant-tree-switcher ant-tree-switcher-noop"><span role="img" aria-label="file" class="anticon anticon-file ant-tree-switcher-line-icon"><svg focusable="false" class="" data-icon="file" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M854.6 288.6L639.4 73.4c-6-6-14.1-9.4-22.6-9.4H192c-17.7 0-32 14.3-32 32v832c0 17.7 14.3 32 32 32h640c17.7 0 32-14.3 32-32V311.3c0-8.5-3.4-16.7-9.4-22.7zM790.2 326H602V137.8L790.2 326zm1.8 562H232V136h302v216a42 42 0 0042 42h216v494z"></path></svg></span></span>
<!----><span title="leaf" class="ant-tree-node-content-wrapper ant-tree-node-content-wrapper-normal"><!----><span class="ant-tree-title">leaf</span>
<!----></span>
</div>
<div class="ant-tree-treenode ant-tree-treenode-switcher-close"><span aria-hidden="true" class="ant-tree-indent"><span class="ant-tree-indent-unit ant-tree-indent-unit-start ant-tree-indent-unit-end"></span><span class="ant-tree-indent-unit ant-tree-indent-unit-start"></span></span><span class="ant-tree-switcher ant-tree-switcher-noop"><span role="img" aria-label="file" class="anticon anticon-file ant-tree-switcher-line-icon"><svg focusable="false" class="" data-icon="file" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M854.6 288.6L639.4 73.4c-6-6-14.1-9.4-22.6-9.4H192c-17.7 0-32 14.3-32 32v832c0 17.7 14.3 32 32 32h640c17.7 0 32-14.3 32-32V311.3c0-8.5-3.4-16.7-9.4-22.7zM790.2 326H602V137.8L790.2 326zm1.8 562H232V136h302v216a42 42 0 0042 42h216v494z"></path></svg></span></span>
<div class="ant-tree-treenode ant-tree-treenode-switcher-close" draggable="false" aria-grabbed="false"><span aria-hidden="true" class="ant-tree-indent"><span class="ant-tree-indent-unit ant-tree-indent-unit-start ant-tree-indent-unit-end"></span><span class="ant-tree-indent-unit ant-tree-indent-unit-start"></span></span>
<!----><span class="ant-tree-switcher ant-tree-switcher-noop"><span role="img" aria-label="file" class="anticon anticon-file ant-tree-switcher-line-icon"><svg focusable="false" class="" data-icon="file" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M854.6 288.6L639.4 73.4c-6-6-14.1-9.4-22.6-9.4H192c-17.7 0-32 14.3-32 32v832c0 17.7 14.3 32 32 32h640c17.7 0 32-14.3 32-32V311.3c0-8.5-3.4-16.7-9.4-22.7zM790.2 326H602V137.8L790.2 326zm1.8 562H232V136h302v216a42 42 0 0042 42h216v494z"></path></svg></span></span>
<!----><span title="leaf" class="ant-tree-node-content-wrapper ant-tree-node-content-wrapper-normal"><!----><span class="ant-tree-title">leaf</span>
<!----></span>
</div>
<div class="ant-tree-treenode ant-tree-treenode-switcher-close ant-tree-treenode-leaf-last"><span aria-hidden="true" class="ant-tree-indent"><span class="ant-tree-indent-unit ant-tree-indent-unit-start ant-tree-indent-unit-end"></span><span class="ant-tree-indent-unit ant-tree-indent-unit-start"></span></span><span class="ant-tree-switcher ant-tree-switcher-noop"><span role="img" aria-label="file" class="anticon anticon-file ant-tree-switcher-line-icon"><svg focusable="false" class="" data-icon="file" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M854.6 288.6L639.4 73.4c-6-6-14.1-9.4-22.6-9.4H192c-17.7 0-32 14.3-32 32v832c0 17.7 14.3 32 32 32h640c17.7 0 32-14.3 32-32V311.3c0-8.5-3.4-16.7-9.4-22.7zM790.2 326H602V137.8L790.2 326zm1.8 562H232V136h302v216a42 42 0 0042 42h216v494z"></path></svg></span></span>
<div class="ant-tree-treenode ant-tree-treenode-switcher-close ant-tree-treenode-leaf-last" draggable="false" aria-grabbed="false"><span aria-hidden="true" class="ant-tree-indent"><span class="ant-tree-indent-unit ant-tree-indent-unit-start ant-tree-indent-unit-end"></span><span class="ant-tree-indent-unit ant-tree-indent-unit-start"></span></span>
<!----><span class="ant-tree-switcher ant-tree-switcher-noop"><span role="img" aria-label="file" class="anticon anticon-file ant-tree-switcher-line-icon"><svg focusable="false" class="" data-icon="file" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M854.6 288.6L639.4 73.4c-6-6-14.1-9.4-22.6-9.4H192c-17.7 0-32 14.3-32 32v832c0 17.7 14.3 32 32 32h640c17.7 0 32-14.3 32-32V311.3c0-8.5-3.4-16.7-9.4-22.7zM790.2 326H602V137.8L790.2 326zm1.8 562H232V136h302v216a42 42 0 0042 42h216v494z"></path></svg></span></span>
<!----><span title="leaf" class="ant-tree-node-content-wrapper ant-tree-node-content-wrapper-normal"><!----><span class="ant-tree-title">leaf</span>
<!----></span>
</div>
<div class="ant-tree-treenode ant-tree-treenode-switcher-close"><span aria-hidden="true" class="ant-tree-indent"><span class="ant-tree-indent-unit ant-tree-indent-unit-start ant-tree-indent-unit-end"></span></span><span class="ant-tree-switcher ant-tree-switcher_close"><span role="img" aria-label="down" class="anticon anticon-down"><svg focusable="false" class="" data-icon="down" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M884 256h-75c-5.1 0-9.9 2.5-12.9 6.6L512 654.2 227.9 262.6c-3-4.1-7.8-6.6-12.9-6.6h-75c-6.5 0-10.3 7.4-6.5 12.7l352.6 486.1c12.8 17.6 39 17.6 51.7 0l352.6-486.1c3.9-5.3.1-12.7-6.4-12.7z"></path></svg></span></span>
<div class="ant-tree-treenode ant-tree-treenode-switcher-close" draggable="false" aria-grabbed="false"><span aria-hidden="true" class="ant-tree-indent"><span class="ant-tree-indent-unit ant-tree-indent-unit-start ant-tree-indent-unit-end"></span></span>
<!----><span class="ant-tree-switcher ant-tree-switcher_close"><span role="img" aria-label="down" class="anticon anticon-down"><svg focusable="false" class="" data-icon="down" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M884 256h-75c-5.1 0-9.9 2.5-12.9 6.6L512 654.2 227.9 262.6c-3-4.1-7.8-6.6-12.9-6.6h-75c-6.5 0-10.3 7.4-6.5 12.7l352.6 486.1c12.8 17.6 39 17.6 51.7 0l352.6-486.1c3.9-5.3.1-12.7-6.4-12.7z"></path></svg></span></span>
<!----><span title="parent 1-1" class="ant-tree-node-content-wrapper ant-tree-node-content-wrapper-close"><!----><span class="ant-tree-title">parent 1-1</span>
<!----></span>
</div>
<div class="ant-tree-treenode ant-tree-treenode-switcher-close ant-tree-treenode-leaf-last"><span aria-hidden="true" class="ant-tree-indent"><span class="ant-tree-indent-unit ant-tree-indent-unit-start ant-tree-indent-unit-end"></span></span><span class="ant-tree-switcher ant-tree-switcher_close"><span role="img" aria-label="down" class="anticon anticon-down"><svg focusable="false" class="" data-icon="down" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M884 256h-75c-5.1 0-9.9 2.5-12.9 6.6L512 654.2 227.9 262.6c-3-4.1-7.8-6.6-12.9-6.6h-75c-6.5 0-10.3 7.4-6.5 12.7l352.6 486.1c12.8 17.6 39 17.6 51.7 0l352.6-486.1c3.9-5.3.1-12.7-6.4-12.7z"></path></svg></span></span>
<div class="ant-tree-treenode ant-tree-treenode-switcher-close ant-tree-treenode-leaf-last" draggable="false" aria-grabbed="false"><span aria-hidden="true" class="ant-tree-indent"><span class="ant-tree-indent-unit ant-tree-indent-unit-start ant-tree-indent-unit-end"></span></span>
<!----><span class="ant-tree-switcher ant-tree-switcher_close"><span role="img" aria-label="down" class="anticon anticon-down"><svg focusable="false" class="" data-icon="down" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M884 256h-75c-5.1 0-9.9 2.5-12.9 6.6L512 654.2 227.9 262.6c-3-4.1-7.8-6.6-12.9-6.6h-75c-6.5 0-10.3 7.4-6.5 12.7l352.6 486.1c12.8 17.6 39 17.6 51.7 0l352.6-486.1c3.9-5.3.1-12.7-6.4-12.7z"></path></svg></span></span>
<!----><span title="parent 1-2" class="ant-tree-node-content-wrapper ant-tree-node-content-wrapper-close"><!----><span class="ant-tree-title">parent 1-2</span>
<!----></span>
</div>
@ -465,34 +521,44 @@ exports[`renders ./components/tree/demo/virtual-scroll.vue correctly 1`] = `
<div class="ant-tree-list-holder" style="max-height: 233px; overflow-y: hidden;">
<div style="height: 311080px; position: relative; overflow: hidden;">
<div style="display: flex; flex-direction: column; transform: translateY(0px); position: absolute; left: 0px; right: 0px; top: 0px;" class="ant-tree-list-holder-inner">
<div class="ant-tree-treenode ant-tree-treenode-switcher-open ant-tree-treenode-checkbox-indeterminate"><span aria-hidden="true" class="ant-tree-indent"></span><span class="ant-tree-switcher ant-tree-switcher_open"><span role="img" aria-label="caret-down" class="anticon anticon-caret-down ant-tree-switcher-icon"><svg focusable="false" class="" data-icon="caret-down" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="0 0 1024 1024"><path d="M840.4 300H183.6c-19.7 0-30.7 20.8-18.5 35l328.4 380.8c9.4 10.9 27.5 10.9 37 0L858.9 335c12.2-14.2 1.2-35-18.5-35z"></path></svg></span></span><span class="ant-tree-checkbox ant-tree-checkbox-indeterminate"><span class="ant-tree-checkbox-inner"></span></span><span title="" class="ant-tree-node-content-wrapper ant-tree-node-content-wrapper-open"><!----><span class="ant-tree-title">0-0</span>
<div class="ant-tree-treenode ant-tree-treenode-switcher-open ant-tree-treenode-checkbox-indeterminate" draggable="false" aria-grabbed="false"><span aria-hidden="true" class="ant-tree-indent"></span>
<!----><span class="ant-tree-switcher ant-tree-switcher_open"><span role="img" aria-label="caret-down" class="anticon anticon-caret-down ant-tree-switcher-icon"><svg focusable="false" class="" data-icon="caret-down" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="0 0 1024 1024"><path d="M840.4 300H183.6c-19.7 0-30.7 20.8-18.5 35l328.4 380.8c9.4 10.9 27.5 10.9 37 0L858.9 335c12.2-14.2 1.2-35-18.5-35z"></path></svg></span></span><span class="ant-tree-checkbox ant-tree-checkbox-indeterminate"><span class="ant-tree-checkbox-inner"></span></span><span title="" class="ant-tree-node-content-wrapper ant-tree-node-content-wrapper-open"><!----><span class="ant-tree-title">0-0</span>
<!----></span>
</div>
<div class="ant-tree-treenode ant-tree-treenode-switcher-open ant-tree-treenode-checkbox-checked ant-tree-treenode-selected"><span aria-hidden="true" class="ant-tree-indent"><span class="ant-tree-indent-unit ant-tree-indent-unit-start"></span></span><span class="ant-tree-switcher ant-tree-switcher_open"><span role="img" aria-label="caret-down" class="anticon anticon-caret-down ant-tree-switcher-icon"><svg focusable="false" class="" data-icon="caret-down" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="0 0 1024 1024"><path d="M840.4 300H183.6c-19.7 0-30.7 20.8-18.5 35l328.4 380.8c9.4 10.9 27.5 10.9 37 0L858.9 335c12.2-14.2 1.2-35-18.5-35z"></path></svg></span></span><span class="ant-tree-checkbox ant-tree-checkbox-checked"><span class="ant-tree-checkbox-inner"></span></span><span title="" class="ant-tree-node-content-wrapper ant-tree-node-content-wrapper-open ant-tree-node-selected"><!----><span class="ant-tree-title">0-0-0</span>
<div class="ant-tree-treenode ant-tree-treenode-switcher-open ant-tree-treenode-checkbox-checked ant-tree-treenode-selected" draggable="false" aria-grabbed="false"><span aria-hidden="true" class="ant-tree-indent"><span class="ant-tree-indent-unit ant-tree-indent-unit-start"></span></span>
<!----><span class="ant-tree-switcher ant-tree-switcher_open"><span role="img" aria-label="caret-down" class="anticon anticon-caret-down ant-tree-switcher-icon"><svg focusable="false" class="" data-icon="caret-down" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="0 0 1024 1024"><path d="M840.4 300H183.6c-19.7 0-30.7 20.8-18.5 35l328.4 380.8c9.4 10.9 27.5 10.9 37 0L858.9 335c12.2-14.2 1.2-35-18.5-35z"></path></svg></span></span><span class="ant-tree-checkbox ant-tree-checkbox-checked"><span class="ant-tree-checkbox-inner"></span></span><span title="" class="ant-tree-node-content-wrapper ant-tree-node-content-wrapper-open ant-tree-node-selected"><!----><span class="ant-tree-title">0-0-0</span>
<!----></span>
</div>
<div class="ant-tree-treenode ant-tree-treenode-switcher-open ant-tree-treenode-checkbox-checked"><span aria-hidden="true" class="ant-tree-indent"><span class="ant-tree-indent-unit ant-tree-indent-unit-start"></span><span class="ant-tree-indent-unit ant-tree-indent-unit-start"></span></span><span class="ant-tree-switcher ant-tree-switcher_open"><span role="img" aria-label="caret-down" class="anticon anticon-caret-down ant-tree-switcher-icon"><svg focusable="false" class="" data-icon="caret-down" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="0 0 1024 1024"><path d="M840.4 300H183.6c-19.7 0-30.7 20.8-18.5 35l328.4 380.8c9.4 10.9 27.5 10.9 37 0L858.9 335c12.2-14.2 1.2-35-18.5-35z"></path></svg></span></span><span class="ant-tree-checkbox ant-tree-checkbox-checked"><span class="ant-tree-checkbox-inner"></span></span><span title="" class="ant-tree-node-content-wrapper ant-tree-node-content-wrapper-open"><!----><span class="ant-tree-title">0-0-0-0</span>
<div class="ant-tree-treenode ant-tree-treenode-switcher-open ant-tree-treenode-checkbox-checked" draggable="false" aria-grabbed="false"><span aria-hidden="true" class="ant-tree-indent"><span class="ant-tree-indent-unit ant-tree-indent-unit-start"></span><span class="ant-tree-indent-unit ant-tree-indent-unit-start"></span></span>
<!----><span class="ant-tree-switcher ant-tree-switcher_open"><span role="img" aria-label="caret-down" class="anticon anticon-caret-down ant-tree-switcher-icon"><svg focusable="false" class="" data-icon="caret-down" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="0 0 1024 1024"><path d="M840.4 300H183.6c-19.7 0-30.7 20.8-18.5 35l328.4 380.8c9.4 10.9 27.5 10.9 37 0L858.9 335c12.2-14.2 1.2-35-18.5-35z"></path></svg></span></span><span class="ant-tree-checkbox ant-tree-checkbox-checked"><span class="ant-tree-checkbox-inner"></span></span><span title="" class="ant-tree-node-content-wrapper ant-tree-node-content-wrapper-open"><!----><span class="ant-tree-title">0-0-0-0</span>
<!----></span>
</div>
<div class="ant-tree-treenode ant-tree-treenode-switcher-open ant-tree-treenode-checkbox-checked"><span aria-hidden="true" class="ant-tree-indent"><span class="ant-tree-indent-unit ant-tree-indent-unit-start"></span><span class="ant-tree-indent-unit ant-tree-indent-unit-start"></span><span class="ant-tree-indent-unit ant-tree-indent-unit-start"></span></span><span class="ant-tree-switcher ant-tree-switcher-noop"><!----></span><span class="ant-tree-checkbox ant-tree-checkbox-checked"><span class="ant-tree-checkbox-inner"></span></span><span title="" class="ant-tree-node-content-wrapper ant-tree-node-content-wrapper-normal"><!----><span class="ant-tree-title">0-0-0-0-0</span>
<div class="ant-tree-treenode ant-tree-treenode-switcher-open ant-tree-treenode-checkbox-checked" draggable="false" aria-grabbed="false"><span aria-hidden="true" class="ant-tree-indent"><span class="ant-tree-indent-unit ant-tree-indent-unit-start"></span><span class="ant-tree-indent-unit ant-tree-indent-unit-start"></span><span class="ant-tree-indent-unit ant-tree-indent-unit-start"></span></span>
<!----><span class="ant-tree-switcher ant-tree-switcher-noop"><!----></span><span class="ant-tree-checkbox ant-tree-checkbox-checked"><span class="ant-tree-checkbox-inner"></span></span><span title="" class="ant-tree-node-content-wrapper ant-tree-node-content-wrapper-normal"><!----><span class="ant-tree-title">0-0-0-0-0</span>
<!----></span>
</div>
<div class="ant-tree-treenode ant-tree-treenode-switcher-open ant-tree-treenode-checkbox-checked"><span aria-hidden="true" class="ant-tree-indent"><span class="ant-tree-indent-unit ant-tree-indent-unit-start"></span><span class="ant-tree-indent-unit ant-tree-indent-unit-start"></span><span class="ant-tree-indent-unit ant-tree-indent-unit-start"></span></span><span class="ant-tree-switcher ant-tree-switcher-noop"><!----></span><span class="ant-tree-checkbox ant-tree-checkbox-checked"><span class="ant-tree-checkbox-inner"></span></span><span title="" class="ant-tree-node-content-wrapper ant-tree-node-content-wrapper-normal"><!----><span class="ant-tree-title">0-0-0-0-1</span>
<div class="ant-tree-treenode ant-tree-treenode-switcher-open ant-tree-treenode-checkbox-checked" draggable="false" aria-grabbed="false"><span aria-hidden="true" class="ant-tree-indent"><span class="ant-tree-indent-unit ant-tree-indent-unit-start"></span><span class="ant-tree-indent-unit ant-tree-indent-unit-start"></span><span class="ant-tree-indent-unit ant-tree-indent-unit-start"></span></span>
<!----><span class="ant-tree-switcher ant-tree-switcher-noop"><!----></span><span class="ant-tree-checkbox ant-tree-checkbox-checked"><span class="ant-tree-checkbox-inner"></span></span><span title="" class="ant-tree-node-content-wrapper ant-tree-node-content-wrapper-normal"><!----><span class="ant-tree-title">0-0-0-0-1</span>
<!----></span>
</div>
<div class="ant-tree-treenode ant-tree-treenode-switcher-open ant-tree-treenode-checkbox-checked"><span aria-hidden="true" class="ant-tree-indent"><span class="ant-tree-indent-unit ant-tree-indent-unit-start"></span><span class="ant-tree-indent-unit ant-tree-indent-unit-start"></span><span class="ant-tree-indent-unit ant-tree-indent-unit-start"></span></span><span class="ant-tree-switcher ant-tree-switcher-noop"><!----></span><span class="ant-tree-checkbox ant-tree-checkbox-checked"><span class="ant-tree-checkbox-inner"></span></span><span title="" class="ant-tree-node-content-wrapper ant-tree-node-content-wrapper-normal"><!----><span class="ant-tree-title">0-0-0-0-2</span>
<div class="ant-tree-treenode ant-tree-treenode-switcher-open ant-tree-treenode-checkbox-checked" draggable="false" aria-grabbed="false"><span aria-hidden="true" class="ant-tree-indent"><span class="ant-tree-indent-unit ant-tree-indent-unit-start"></span><span class="ant-tree-indent-unit ant-tree-indent-unit-start"></span><span class="ant-tree-indent-unit ant-tree-indent-unit-start"></span></span>
<!----><span class="ant-tree-switcher ant-tree-switcher-noop"><!----></span><span class="ant-tree-checkbox ant-tree-checkbox-checked"><span class="ant-tree-checkbox-inner"></span></span><span title="" class="ant-tree-node-content-wrapper ant-tree-node-content-wrapper-normal"><!----><span class="ant-tree-title">0-0-0-0-2</span>
<!----></span>
</div>
<div class="ant-tree-treenode ant-tree-treenode-switcher-open ant-tree-treenode-checkbox-checked"><span aria-hidden="true" class="ant-tree-indent"><span class="ant-tree-indent-unit ant-tree-indent-unit-start"></span><span class="ant-tree-indent-unit ant-tree-indent-unit-start"></span><span class="ant-tree-indent-unit ant-tree-indent-unit-start"></span></span><span class="ant-tree-switcher ant-tree-switcher-noop"><!----></span><span class="ant-tree-checkbox ant-tree-checkbox-checked"><span class="ant-tree-checkbox-inner"></span></span><span title="" class="ant-tree-node-content-wrapper ant-tree-node-content-wrapper-normal"><!----><span class="ant-tree-title">0-0-0-0-3</span>
<div class="ant-tree-treenode ant-tree-treenode-switcher-open ant-tree-treenode-checkbox-checked" draggable="false" aria-grabbed="false"><span aria-hidden="true" class="ant-tree-indent"><span class="ant-tree-indent-unit ant-tree-indent-unit-start"></span><span class="ant-tree-indent-unit ant-tree-indent-unit-start"></span><span class="ant-tree-indent-unit ant-tree-indent-unit-start"></span></span>
<!----><span class="ant-tree-switcher ant-tree-switcher-noop"><!----></span><span class="ant-tree-checkbox ant-tree-checkbox-checked"><span class="ant-tree-checkbox-inner"></span></span><span title="" class="ant-tree-node-content-wrapper ant-tree-node-content-wrapper-normal"><!----><span class="ant-tree-title">0-0-0-0-3</span>
<!----></span>
</div>
<div class="ant-tree-treenode ant-tree-treenode-switcher-open ant-tree-treenode-checkbox-checked"><span aria-hidden="true" class="ant-tree-indent"><span class="ant-tree-indent-unit ant-tree-indent-unit-start"></span><span class="ant-tree-indent-unit ant-tree-indent-unit-start"></span><span class="ant-tree-indent-unit ant-tree-indent-unit-start"></span></span><span class="ant-tree-switcher ant-tree-switcher-noop"><!----></span><span class="ant-tree-checkbox ant-tree-checkbox-checked"><span class="ant-tree-checkbox-inner"></span></span><span title="" class="ant-tree-node-content-wrapper ant-tree-node-content-wrapper-normal"><!----><span class="ant-tree-title">0-0-0-0-4</span>
<div class="ant-tree-treenode ant-tree-treenode-switcher-open ant-tree-treenode-checkbox-checked" draggable="false" aria-grabbed="false"><span aria-hidden="true" class="ant-tree-indent"><span class="ant-tree-indent-unit ant-tree-indent-unit-start"></span><span class="ant-tree-indent-unit ant-tree-indent-unit-start"></span><span class="ant-tree-indent-unit ant-tree-indent-unit-start"></span></span>
<!----><span class="ant-tree-switcher ant-tree-switcher-noop"><!----></span><span class="ant-tree-checkbox ant-tree-checkbox-checked"><span class="ant-tree-checkbox-inner"></span></span><span title="" class="ant-tree-node-content-wrapper ant-tree-node-content-wrapper-normal"><!----><span class="ant-tree-title">0-0-0-0-4</span>
<!----></span>
</div>
<div class="ant-tree-treenode ant-tree-treenode-switcher-open ant-tree-treenode-checkbox-checked"><span aria-hidden="true" class="ant-tree-indent"><span class="ant-tree-indent-unit ant-tree-indent-unit-start"></span><span class="ant-tree-indent-unit ant-tree-indent-unit-start"></span><span class="ant-tree-indent-unit ant-tree-indent-unit-start"></span></span><span class="ant-tree-switcher ant-tree-switcher-noop"><!----></span><span class="ant-tree-checkbox ant-tree-checkbox-checked"><span class="ant-tree-checkbox-inner"></span></span><span title="" class="ant-tree-node-content-wrapper ant-tree-node-content-wrapper-normal"><!----><span class="ant-tree-title">0-0-0-0-5</span>
<div class="ant-tree-treenode ant-tree-treenode-switcher-open ant-tree-treenode-checkbox-checked" draggable="false" aria-grabbed="false"><span aria-hidden="true" class="ant-tree-indent"><span class="ant-tree-indent-unit ant-tree-indent-unit-start"></span><span class="ant-tree-indent-unit ant-tree-indent-unit-start"></span><span class="ant-tree-indent-unit ant-tree-indent-unit-start"></span></span>
<!----><span class="ant-tree-switcher ant-tree-switcher-noop"><!----></span><span class="ant-tree-checkbox ant-tree-checkbox-checked"><span class="ant-tree-checkbox-inner"></span></span><span title="" class="ant-tree-node-content-wrapper ant-tree-node-content-wrapper-normal"><!----><span class="ant-tree-title">0-0-0-0-5</span>
<!----></span>
</div>
<div class="ant-tree-treenode ant-tree-treenode-switcher-open ant-tree-treenode-checkbox-checked"><span aria-hidden="true" class="ant-tree-indent"><span class="ant-tree-indent-unit ant-tree-indent-unit-start"></span><span class="ant-tree-indent-unit ant-tree-indent-unit-start"></span><span class="ant-tree-indent-unit ant-tree-indent-unit-start"></span></span><span class="ant-tree-switcher ant-tree-switcher-noop"><!----></span><span class="ant-tree-checkbox ant-tree-checkbox-checked"><span class="ant-tree-checkbox-inner"></span></span><span title="" class="ant-tree-node-content-wrapper ant-tree-node-content-wrapper-normal"><!----><span class="ant-tree-title">0-0-0-0-6</span>
<div class="ant-tree-treenode ant-tree-treenode-switcher-open ant-tree-treenode-checkbox-checked" draggable="false" aria-grabbed="false"><span aria-hidden="true" class="ant-tree-indent"><span class="ant-tree-indent-unit ant-tree-indent-unit-start"></span><span class="ant-tree-indent-unit ant-tree-indent-unit-start"></span><span class="ant-tree-indent-unit ant-tree-indent-unit-start"></span></span>
<!----><span class="ant-tree-switcher ant-tree-switcher-noop"><!----></span><span class="ant-tree-checkbox ant-tree-checkbox-checked"><span class="ant-tree-checkbox-inner"></span></span><span title="" class="ant-tree-node-content-wrapper ant-tree-node-content-wrapper-normal"><!----><span class="ant-tree-title">0-0-0-0-6</span>
<!----></span>
</div>
</div>

View File

@ -36,7 +36,7 @@ Almost anything can be represented in a tree structure. Examples include directo
| selectedKeys(v-model) | (Controlled) Specifies the keys of the selected treeNodes | string\[] \| number\[] | - | |
| showIcon | 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 | |
| switcherIcon | customize collapse/expand icon of tree node | slot | - | |
| showLine | Shows a connecting line | boolean | false | |
| showLine | Shows a connecting line | boolean \| {showLeafIcon: boolean}(3.0+) | false | |
| title | custom title | slot | | 2.0.0 |
### Events

View File

@ -37,7 +37,7 @@ cover: https://gw.alipayobjects.com/zos/alicdn/Xh-oWqg9k/Tree.svg
| selectedKeys(v-model) | (受控)设置选中的树节点 | string\[] \| number\[] | - | |
| showIcon | 是否展示 TreeNode title 前的图标,没有默认样式,如设置为 true需要自行定义图标相关样式 | boolean | false | |
| switcherIcon | 自定义树节点的展开/折叠图标 | slot | - | |
| showLine | 是否展示连接线 | boolean | false | |
| showLine | 是否展示连接线 | boolean \| {showLeafIcon: boolean}(3.0+) | false | |
| title | 自定义标题 | slot | | 2.0.0 |
### 事件

View File

@ -1,388 +0,0 @@
import { getComponent, getSlot, hasProp, getEvents } from '../_util/props-util';
import PropTypes from '../_util/vue-types';
import Trigger from '../vc-trigger';
import Menus from './Menus';
import KeyCode from '../_util/KeyCode';
import arrayTreeFilter from 'array-tree-filter';
import shallowEqualArrays from 'shallow-equal/arrays';
import BaseMixin from '../_util/BaseMixin';
import { cloneElement } from '../_util/vnode';
import { defineComponent } from 'vue';
import isEqual from 'lodash-es/isEqual';
const BUILT_IN_PLACEMENTS = {
bottomLeft: {
points: ['tl', 'bl'],
offset: [0, 4],
overflow: {
adjustX: 1,
adjustY: 1,
},
},
topLeft: {
points: ['bl', 'tl'],
offset: [0, -4],
overflow: {
adjustX: 1,
adjustY: 1,
},
},
bottomRight: {
points: ['tr', 'br'],
offset: [0, 4],
overflow: {
adjustX: 1,
adjustY: 1,
},
},
topRight: {
points: ['br', 'tr'],
offset: [0, -4],
overflow: {
adjustX: 1,
adjustY: 1,
},
},
};
export default defineComponent({
name: 'Cascader',
mixins: [BaseMixin],
inheritAttrs: false,
// model: {
// prop: 'value',
// event: 'change',
// },
props: {
value: PropTypes.array,
defaultValue: PropTypes.array,
options: PropTypes.array,
// onChange: PropTypes.func,
// onPopupVisibleChange: PropTypes.func,
popupVisible: PropTypes.looseBool,
disabled: PropTypes.looseBool.def(false),
transitionName: PropTypes.string.def(''),
popupClassName: PropTypes.string.def(''),
popupStyle: PropTypes.object.def(() => ({})),
popupPlacement: PropTypes.string.def('bottomLeft'),
prefixCls: PropTypes.string.def('rc-cascader'),
dropdownMenuColumnStyle: PropTypes.object,
builtinPlacements: PropTypes.object.def(BUILT_IN_PLACEMENTS),
loadData: PropTypes.func,
changeOnSelect: PropTypes.looseBool,
// onKeyDown: PropTypes.func,
expandTrigger: PropTypes.string.def('click'),
fieldNames: PropTypes.object.def(() => ({
label: 'label',
value: 'value',
children: 'children',
})),
expandIcon: PropTypes.any,
loadingIcon: PropTypes.any,
getPopupContainer: PropTypes.func,
},
data() {
let initialValue = [];
const { value, defaultValue, popupVisible } = this;
if (hasProp(this, 'value')) {
initialValue = value || [];
} else if (hasProp(this, 'defaultValue')) {
initialValue = defaultValue || [];
}
this.children = undefined;
// warning(!('filedNames' in props),
// '`filedNames` of Cascader is a typo usage and deprecated, please use `fieldNames` instead.');
this.defaultFieldNames = { label: 'label', value: 'value', children: 'children' };
return {
sPopupVisible: popupVisible,
sActiveValue: initialValue,
sValue: initialValue,
};
},
watch: {
value(val, oldValue) {
if (!shallowEqualArrays(val, oldValue)) {
const newValues = {
sValue: val || [],
};
// allow activeValue diff from value
// https://github.com/ant-design/ant-design/issues/2767
if (!hasProp(this, 'loadData')) {
newValues.sActiveValue = val || [];
}
this.setState(newValues);
}
},
popupVisible(val) {
this.setState({
sPopupVisible: val,
});
},
},
methods: {
getPopupDOMNode() {
return this.trigger.getPopupDomNode();
},
getFieldName(name) {
const { defaultFieldNames, fieldNames } = this;
return fieldNames[name] || defaultFieldNames[name];
},
getFieldNames() {
return this.fieldNames;
},
getCurrentLevelOptions() {
const { options = [], sActiveValue = [] } = this;
const result = arrayTreeFilter(
options,
(o, level) => isEqual(o[this.getFieldName('value')], sActiveValue[level]),
{ childrenKeyName: this.getFieldName('children') },
);
if (result[result.length - 2]) {
return result[result.length - 2][this.getFieldName('children')];
}
return [...options].filter(o => !o.disabled);
},
getActiveOptions(activeValue) {
return arrayTreeFilter(
this.options || [],
(o, level) => isEqual(o[this.getFieldName('value')], activeValue[level]),
{ childrenKeyName: this.getFieldName('children') },
);
},
setPopupVisible(popupVisible) {
if (!hasProp(this, 'popupVisible')) {
this.setState({ sPopupVisible: popupVisible });
}
// sync activeValue with value when panel open
if (popupVisible && !this.sPopupVisible) {
this.setState({
sActiveValue: this.sValue,
});
}
this.__emit('popupVisibleChange', popupVisible);
},
handleChange(options, setProps, e) {
if (e.type !== 'keydown' || e.keyCode === KeyCode.ENTER) {
const value = options.map(o => o[this.getFieldName('value')]);
this.__emit('change', value, options);
this.setPopupVisible(setProps.visible);
}
},
handlePopupVisibleChange(popupVisible) {
this.setPopupVisible(popupVisible);
},
handleMenuSelect(targetOption, menuIndex, e) {
// Keep focused state for keyboard support
const triggerNode = this.trigger.getRootDomNode();
if (triggerNode && triggerNode.focus) {
triggerNode.focus();
}
const { changeOnSelect, loadData, expandTrigger } = this;
if (!targetOption || targetOption.disabled) {
return;
}
let { sActiveValue } = this;
sActiveValue = sActiveValue.slice(0, menuIndex + 1);
sActiveValue[menuIndex] = targetOption[this.getFieldName('value')];
const activeOptions = this.getActiveOptions(sActiveValue);
if (
targetOption.isLeaf === false &&
!targetOption[this.getFieldName('children')] &&
loadData
) {
if (changeOnSelect) {
this.handleChange(activeOptions, { visible: true }, e);
}
this.setState({ sActiveValue });
loadData(activeOptions);
return;
}
const newState = {};
if (
!targetOption[this.getFieldName('children')] ||
!targetOption[this.getFieldName('children')].length
) {
this.handleChange(activeOptions, { visible: false }, e);
// set value to activeValue when select leaf option
newState.sValue = sActiveValue;
// add e.type judgement to prevent `onChange` being triggered by mouseEnter
} else if (changeOnSelect && (e.type === 'click' || e.type === 'keydown')) {
if (expandTrigger === 'hover') {
this.handleChange(activeOptions, { visible: false }, e);
} else {
this.handleChange(activeOptions, { visible: true }, e);
}
// set value to activeValue on every select
newState.sValue = sActiveValue;
}
newState.sActiveValue = sActiveValue;
// not change the value by keyboard
if (hasProp(this, 'value') || (e.type === 'keydown' && e.keyCode !== KeyCode.ENTER)) {
delete newState.sValue;
}
this.setState(newState);
},
handleItemDoubleClick() {
const { changeOnSelect } = this.$props;
if (changeOnSelect) {
this.setPopupVisible(false);
}
},
handleKeyDown(e) {
const children = this.children;
// https://github.com/ant-design/ant-design/issues/6717
// Don't bind keyboard support when children specify the onKeyDown
if (children) {
const keydown = getEvents(children).onKeydown;
if (keydown) {
keydown(e);
return;
}
}
const activeValue = [...this.sActiveValue];
const currentLevel = activeValue.length - 1 < 0 ? 0 : activeValue.length - 1;
const currentOptions = this.getCurrentLevelOptions();
const currentIndex = currentOptions
.map(o => o[this.getFieldName('value')])
.findIndex(val => isEqual(activeValue[currentLevel], val));
if (
e.keyCode !== KeyCode.DOWN &&
e.keyCode !== KeyCode.UP &&
e.keyCode !== KeyCode.LEFT &&
e.keyCode !== KeyCode.RIGHT &&
e.keyCode !== KeyCode.ENTER &&
e.keyCode !== KeyCode.SPACE &&
e.keyCode !== KeyCode.BACKSPACE &&
e.keyCode !== KeyCode.ESC &&
e.keyCode !== KeyCode.TAB
) {
return;
}
// Press any keys above to reopen menu
if (
!this.sPopupVisible &&
e.keyCode !== KeyCode.BACKSPACE &&
e.keyCode !== KeyCode.LEFT &&
e.keyCode !== KeyCode.RIGHT &&
e.keyCode !== KeyCode.ESC &&
e.keyCode !== KeyCode.TAB
) {
this.setPopupVisible(true);
return;
}
if (e.keyCode === KeyCode.DOWN || e.keyCode === KeyCode.UP) {
e.preventDefault();
let nextIndex = currentIndex;
if (nextIndex !== -1) {
if (e.keyCode === KeyCode.DOWN) {
nextIndex += 1;
nextIndex = nextIndex >= currentOptions.length ? 0 : nextIndex;
} else {
nextIndex -= 1;
nextIndex = nextIndex < 0 ? currentOptions.length - 1 : nextIndex;
}
} else {
nextIndex = 0;
}
activeValue[currentLevel] = currentOptions[nextIndex][this.getFieldName('value')];
} else if (e.keyCode === KeyCode.LEFT || e.keyCode === KeyCode.BACKSPACE) {
e.preventDefault();
activeValue.splice(activeValue.length - 1, 1);
} else if (e.keyCode === KeyCode.RIGHT) {
e.preventDefault();
if (
currentOptions[currentIndex] &&
currentOptions[currentIndex][this.getFieldName('children')]
) {
activeValue.push(
currentOptions[currentIndex][this.getFieldName('children')][0][
this.getFieldName('value')
],
);
}
} else if (e.keyCode === KeyCode.ESC || e.keyCode === KeyCode.TAB) {
this.setPopupVisible(false);
return;
}
if (!activeValue || activeValue.length === 0) {
this.setPopupVisible(false);
}
const activeOptions = this.getActiveOptions(activeValue);
const targetOption = activeOptions[activeOptions.length - 1];
this.handleMenuSelect(targetOption, activeOptions.length - 1, e);
this.__emit('keydown', e);
},
saveTrigger(node) {
this.trigger = node;
},
},
render() {
const {
$props,
sActiveValue,
handleMenuSelect,
sPopupVisible,
handlePopupVisibleChange,
handleKeyDown,
} = this;
const {
prefixCls,
transitionName,
popupClassName,
options = [],
disabled,
builtinPlacements,
popupPlacement,
...restProps
} = $props;
// Did not show popup when there is no options
let menus = <div />;
let emptyMenuClassName = '';
if (options && options.length > 0) {
const loadingIcon = getComponent(this, 'loadingIcon');
const expandIcon = getComponent(this, 'expandIcon') || '>';
const menusProps = {
...$props,
...this.$attrs,
fieldNames: this.getFieldNames(),
defaultFieldNames: this.defaultFieldNames,
activeValue: sActiveValue,
visible: sPopupVisible,
loadingIcon,
expandIcon,
onSelect: handleMenuSelect,
onItemDoubleClick: this.handleItemDoubleClick,
};
menus = <Menus {...menusProps} />;
} else {
emptyMenuClassName = ` ${prefixCls}-menus-empty`;
}
const triggerProps = {
...restProps,
...this.$attrs,
disabled,
popupPlacement,
builtinPlacements,
popupTransitionName: transitionName,
action: disabled ? [] : ['click'],
popupVisible: disabled ? false : sPopupVisible,
prefixCls: `${prefixCls}-menus`,
popupClassName: popupClassName + emptyMenuClassName,
popup: menus,
onPopupVisibleChange: handlePopupVisibleChange,
ref: this.saveTrigger,
};
const children = getSlot(this);
this.children = children;
return (
<Trigger {...triggerProps}>
{children &&
cloneElement(children[0], {
onKeydown: handleKeyDown,
tabindex: disabled ? undefined : 0,
})}
</Trigger>
);
},
});

View File

@ -0,0 +1,566 @@
import { computed, defineComponent, ref, toRef, toRefs, watchEffect } from 'vue';
import type { CSSProperties, ExtractPropTypes, PropType, Ref } from 'vue';
import type { BaseSelectRef, BaseSelectProps } from '../vc-select';
import type { DisplayValueType, Placement } from '../vc-select/BaseSelect';
import { baseSelectPropsWithoutPrivate } from '../vc-select/BaseSelect';
import omit from '../_util/omit';
import type { Key, VueNode } from '../_util/type';
import PropTypes from '../_util/vue-types';
import { initDefaultProps } from '../_util/props-util';
import useId from '../vc-select/hooks/useId';
import useMergedState from '../_util/hooks/useMergedState';
import { fillFieldNames, toPathKey, toPathKeys } from './utils/commonUtil';
import useEntities from './hooks/useEntities';
import useSearchConfig from './hooks/useSearchConfig';
import useSearchOptions from './hooks/useSearchOptions';
import useMissingValues from './hooks/useMissingValues';
import { formatStrategyValues, toPathOptions } from './utils/treeUtil';
import { conductCheck } from '../vc-tree/utils/conductUtil';
import useDisplayValues from './hooks/useDisplayValues';
import { useProvideCascader } from './context';
import OptionList from './OptionList';
import { BaseSelect } from '../vc-select';
import devWarning from '../vc-util/devWarning';
export interface ShowSearchType<OptionType extends BaseOptionType = DefaultOptionType> {
filter?: (inputValue: string, options: OptionType[], fieldNames: FieldNames) => boolean;
render?: (arg?: {
inputValue: string;
path: OptionType[];
prefixCls: string;
fieldNames: FieldNames;
}) => any;
sort?: (a: OptionType[], b: OptionType[], inputValue: string, fieldNames: FieldNames) => number;
matchInputWidth?: boolean;
limit?: number | false;
}
export interface FieldNames {
label?: string;
value?: string;
children?: string;
}
export interface InternalFieldNames extends Required<FieldNames> {
key: string;
}
export type SingleValueType = (string | number)[];
export type ValueType = SingleValueType | SingleValueType[];
export interface BaseOptionType {
disabled?: boolean;
[name: string]: any;
}
export interface DefaultOptionType extends BaseOptionType {
label?: any;
value?: string | number | null;
children?: DefaultOptionType[];
}
function baseCascaderProps<OptionType extends BaseOptionType = DefaultOptionType>() {
return {
...omit(baseSelectPropsWithoutPrivate(), ['tokenSeparators', 'mode', 'showSearch']),
// MISC
id: String,
prefixCls: String,
fieldNames: Object as PropType<FieldNames>,
children: Array as PropType<VueNode[]>,
// Value
value: { type: [String, Number, Array] as PropType<ValueType> },
defaultValue: { type: [String, Number, Array] as PropType<ValueType> },
changeOnSelect: { type: Boolean, default: undefined },
onChange: Function as PropType<
(value: ValueType, selectedOptions?: OptionType[] | OptionType[][]) => void
>,
displayRender: Function as PropType<
(opt: { labels: string[]; selectedOptions?: OptionType[] }) => any
>,
checkable: { type: Boolean, default: undefined },
// Search
showSearch: {
type: [Boolean, Object] as PropType<boolean | ShowSearchType<OptionType>>,
default: undefined as boolean | ShowSearchType<OptionType>,
},
searchValue: String,
onSearch: Function as PropType<(value: string) => void>,
// Trigger
expandTrigger: String as PropType<'hover' | 'click'>,
// Options
options: Array as PropType<OptionType[]>,
/** @private Internal usage. Do not use in your production. */
dropdownPrefixCls: String,
loadData: Function as PropType<(selectOptions: OptionType[]) => void>,
// Open
/** @deprecated Use `open` instead */
popupVisible: { type: Boolean, default: undefined },
/** @deprecated Use `dropdownClassName` instead */
popupClassName: String,
dropdownClassName: String,
dropdownMenuColumnStyle: {
type: Object as PropType<CSSProperties>,
default: undefined as CSSProperties,
},
/** @deprecated Use `dropdownStyle` instead */
popupStyle: { type: Object as PropType<CSSProperties>, default: undefined as CSSProperties },
dropdownStyle: { type: Object as PropType<CSSProperties>, default: undefined as CSSProperties },
/** @deprecated Use `placement` instead */
popupPlacement: String as PropType<Placement>,
placement: String as PropType<Placement>,
/** @deprecated Use `onDropdownVisibleChange` instead */
onPopupVisibleChange: Function as PropType<(open: boolean) => void>,
onDropdownVisibleChange: Function as PropType<(open: boolean) => void>,
// Icon
expandIcon: PropTypes.any,
loadingIcon: PropTypes.any,
};
}
export type BaseCascaderProps = Partial<ExtractPropTypes<ReturnType<typeof baseCascaderProps>>>;
type OnSingleChange<OptionType> = (value: SingleValueType, selectOptions: OptionType[]) => void;
type OnMultipleChange<OptionType> = (
value: SingleValueType[],
selectOptions: OptionType[][],
) => void;
export function singleCascaderProps<OptionType extends BaseOptionType = DefaultOptionType>() {
return {
...baseCascaderProps(),
checkable: Boolean as PropType<false>,
onChange: Function as PropType<OnSingleChange<OptionType>>,
};
}
export type SingleCascaderProps = Partial<ExtractPropTypes<ReturnType<typeof singleCascaderProps>>>;
export function multipleCascaderProps<OptionType extends BaseOptionType = DefaultOptionType>() {
return {
...baseCascaderProps(),
checkable: Boolean as PropType<true>,
onChange: Function as PropType<OnMultipleChange<OptionType>>,
};
}
export type MultipleCascaderProps = Partial<
ExtractPropTypes<ReturnType<typeof singleCascaderProps>>
>;
export function internalCascaderProps<OptionType extends BaseOptionType = DefaultOptionType>() {
return {
...baseCascaderProps(),
onChange: Function as PropType<
(value: ValueType, selectOptions: OptionType[] | OptionType[][]) => void
>,
customSlots: Object as PropType<Record<string, Function>>,
};
}
export type CascaderProps = Partial<ExtractPropTypes<ReturnType<typeof internalCascaderProps>>>;
export type CascaderRef = Omit<BaseSelectRef, 'scrollTo'>;
function isMultipleValue(value: ValueType): value is SingleValueType[] {
return Array.isArray(value) && Array.isArray(value[0]);
}
function toRawValues(value: ValueType): SingleValueType[] {
if (!value) {
return [];
}
if (isMultipleValue(value)) {
return value;
}
return value.length === 0 ? [] : [value];
}
export default defineComponent({
name: 'Cascader',
inheritAttrs: false,
props: initDefaultProps(internalCascaderProps(), {}),
setup(props, { attrs, expose, slots }) {
const mergedId = useId(toRef(props, 'id'));
const multiple = computed(() => !!props.checkable);
// =========================== Values ===========================
const [rawValues, setRawValues] = useMergedState<ValueType, Ref<SingleValueType[]>>(
props.defaultValue,
{
value: computed(() => props.value),
postState: toRawValues,
},
);
// ========================= FieldNames =========================
const mergedFieldNames = computed(() => fillFieldNames(props.fieldNames));
// =========================== Option ===========================
const mergedOptions = computed(() => props.options || []);
// Only used in multiple mode, this fn will not call in single mode
const pathKeyEntities = useEntities(mergedOptions, mergedFieldNames);
/** Convert path key back to value format */
const getValueByKeyPath = (pathKeys: Key[]): SingleValueType[] => {
const ketPathEntities = pathKeyEntities.value;
return pathKeys.map(pathKey => {
const { nodes } = ketPathEntities[pathKey];
return nodes.map(node => node[mergedFieldNames.value.value]);
});
};
// =========================== Search ===========================
const [mergedSearchValue, setSearchValue] = useMergedState('', {
value: computed(() => props.searchValue),
postState: search => search || '',
});
const onInternalSearch: BaseSelectProps['onSearch'] = (searchText, info) => {
setSearchValue(searchText);
if (info.source !== 'blur' && props.onSearch) {
props.onSearch(searchText);
}
};
const { showSearch: mergedShowSearch, searchConfig: mergedSearchConfig } = useSearchConfig(
toRef(props, 'showSearch'),
);
const searchOptions = useSearchOptions(
mergedSearchValue,
mergedOptions,
mergedFieldNames,
computed(() => props.dropdownPrefixCls || props.prefixCls),
mergedSearchConfig,
toRef(props, 'changeOnSelect'),
);
// =========================== Values ===========================
const missingValuesInfo = useMissingValues(mergedOptions, mergedFieldNames, rawValues);
// Fill `rawValues` with checked conduction values
const [checkedValues, halfCheckedValues, missingCheckedValues] = [
ref<SingleValueType[]>([]),
ref<SingleValueType[]>([]),
ref<SingleValueType[]>([]),
];
watchEffect(() => {
const [existValues, missingValues] = missingValuesInfo.value;
if (!multiple.value || !rawValues.value.length) {
[checkedValues.value, halfCheckedValues.value, missingCheckedValues.value] = [
existValues,
[],
missingValues,
];
return;
}
const keyPathValues = toPathKeys(existValues);
const ketPathEntities = pathKeyEntities.value;
const { checkedKeys, halfCheckedKeys } = conductCheck(keyPathValues, true, ketPathEntities);
// Convert key back to value cells
[checkedValues.value, halfCheckedValues.value, missingCheckedValues.value] = [
getValueByKeyPath(checkedKeys),
getValueByKeyPath(halfCheckedKeys),
missingValues,
];
});
const deDuplicatedValues = computed(() => {
const checkedKeys = toPathKeys(checkedValues.value);
const deduplicateKeys = formatStrategyValues(checkedKeys, pathKeyEntities.value);
return [...missingCheckedValues.value, ...getValueByKeyPath(deduplicateKeys)];
});
const displayValues = useDisplayValues(
deDuplicatedValues,
mergedOptions,
mergedFieldNames,
multiple,
toRef(props, 'displayRender'),
);
// =========================== Change ===========================
const triggerChange = (nextValues: ValueType) => {
setRawValues(nextValues);
// Save perf if no need trigger event
if (props.onChange) {
const nextRawValues = toRawValues(nextValues);
const valueOptions = nextRawValues.map(valueCells =>
toPathOptions(valueCells, mergedOptions.value, mergedFieldNames.value).map(
valueOpt => valueOpt.option,
),
);
const triggerValues = multiple.value ? nextRawValues : nextRawValues[0];
const triggerOptions = multiple.value ? valueOptions : valueOptions[0];
props.onChange(triggerValues, triggerOptions);
}
};
// =========================== Select ===========================
const onInternalSelect = (valuePath: SingleValueType) => {
if (!multiple.value) {
triggerChange(valuePath);
} else {
// Prepare conduct required info
const pathKey = toPathKey(valuePath);
const checkedPathKeys = toPathKeys(checkedValues.value);
const halfCheckedPathKeys = toPathKeys(halfCheckedValues.value);
const existInChecked = checkedPathKeys.includes(pathKey);
const existInMissing = missingCheckedValues.value.some(
valueCells => toPathKey(valueCells) === pathKey,
);
// Do update
let nextCheckedValues = checkedValues.value;
let nextMissingValues = missingCheckedValues.value;
if (existInMissing && !existInChecked) {
// Missing value only do filter
nextMissingValues = missingCheckedValues.value.filter(
valueCells => toPathKey(valueCells) !== pathKey,
);
} else {
// Update checked key first
const nextRawCheckedKeys = existInChecked
? checkedPathKeys.filter(key => key !== pathKey)
: [...checkedPathKeys, pathKey];
// Conduction by selected or not
let checkedKeys: Key[];
if (existInChecked) {
({ checkedKeys } = conductCheck(
nextRawCheckedKeys,
{ checked: false, halfCheckedKeys: halfCheckedPathKeys },
pathKeyEntities.value,
));
} else {
({ checkedKeys } = conductCheck(nextRawCheckedKeys, true, pathKeyEntities.value));
}
// Roll up to parent level keys
const deDuplicatedKeys = formatStrategyValues(checkedKeys, pathKeyEntities.value);
nextCheckedValues = getValueByKeyPath(deDuplicatedKeys);
}
triggerChange([...nextMissingValues, ...nextCheckedValues]);
}
};
// Display Value change logic
const onDisplayValuesChange: BaseSelectProps['onDisplayValuesChange'] = (_, info) => {
if (info.type === 'clear') {
triggerChange([]);
return;
}
// Cascader do not support `add` type. Only support `remove`
const { valueCells } = info.values[0] as DisplayValueType & { valueCells: SingleValueType };
onInternalSelect(valueCells);
};
// ============================ Open ============================
if (process.env.NODE_ENV !== 'production') {
watchEffect(() => {
devWarning(
!props.onPopupVisibleChange,
'Cascader',
'`popupVisibleChange` is deprecated. Please use `dropdownVisibleChange` instead.',
);
devWarning(
props.popupVisible === undefined,
'Cascader',
'`popupVisible` is deprecated. Please use `open` instead.',
);
devWarning(
props.popupClassName === undefined,
'Cascader',
'`popupClassName` is deprecated. Please use `dropdownClassName` instead.',
);
devWarning(
props.popupPlacement === undefined,
'Cascader',
'`popupPlacement` is deprecated. Please use `placement` instead.',
);
devWarning(
props.popupStyle === undefined,
'Cascader',
'`popupStyle` is deprecated. Please use `dropdownStyle` instead.',
);
});
}
const mergedOpen = computed(() => (props.open !== undefined ? props.open : props.popupVisible));
const mergedDropdownClassName = computed(() => props.dropdownClassName || props.popupClassName);
const mergedDropdownStyle = computed(() => props.dropdownStyle || props.popupStyle || {});
const mergedPlacement = computed(() => props.placement || props.popupPlacement);
const onInternalDropdownVisibleChange = (nextVisible: boolean) => {
props.onDropdownVisibleChange?.(nextVisible);
props.onPopupVisibleChange?.(nextVisible);
};
const {
changeOnSelect,
checkable,
dropdownPrefixCls,
loadData,
expandTrigger,
expandIcon,
loadingIcon,
dropdownMenuColumnStyle,
customSlots,
} = toRefs(props);
useProvideCascader({
options: mergedOptions,
fieldNames: mergedFieldNames,
values: checkedValues,
halfValues: halfCheckedValues,
changeOnSelect,
onSelect: onInternalSelect,
checkable,
searchOptions,
dropdownPrefixCls,
loadData,
expandTrigger,
expandIcon,
loadingIcon,
dropdownMenuColumnStyle,
customSlots,
});
const selectRef = ref<BaseSelectRef>();
expose({
focus() {
selectRef.value?.focus();
},
blur() {
selectRef.value?.blur();
},
scrollTo(arg) {
selectRef.value?.scrollTo(arg);
},
} as BaseSelectRef);
const pickProps = computed(() => {
return omit(props, [
'id',
'prefixCls',
'fieldNames',
// Value
'defaultValue',
'value',
'changeOnSelect',
'onChange',
'displayRender',
'checkable',
// Search
'searchValue',
'onSearch',
'showSearch',
// Trigger
'expandTrigger',
// Options
'options',
'dropdownPrefixCls',
'loadData',
// Open
'popupVisible',
'open',
'popupClassName',
'dropdownClassName',
'dropdownMenuColumnStyle',
'popupPlacement',
'placement',
'onDropdownVisibleChange',
'onPopupVisibleChange',
// Icon
'expandIcon',
'loadingIcon',
'customSlots',
// Children
'children',
]);
});
return () => {
const emptyOptions = !(mergedSearchValue.value ? searchOptions.value : mergedOptions.value)
.length;
const dropdownStyle: CSSProperties =
// Search to match width
(mergedSearchValue.value && mergedSearchConfig.value.matchInputWidth) ||
// Empty keep the width
emptyOptions
? {}
: {
minWidth: 'auto',
};
return (
<BaseSelect
{...pickProps.value}
{...attrs}
// MISC
ref={selectRef}
id={mergedId}
prefixCls={props.prefixCls}
dropdownMatchSelectWidth={false}
dropdownStyle={{ ...mergedDropdownStyle.value, ...dropdownStyle }}
// Value
displayValues={displayValues.value}
onDisplayValuesChange={onDisplayValuesChange}
mode={multiple.value ? 'multiple' : undefined}
// Search
searchValue={mergedSearchValue.value}
onSearch={onInternalSearch}
showSearch={mergedShowSearch.value}
// Options
OptionList={OptionList}
emptyOptions={emptyOptions}
// Open
open={mergedOpen.value}
dropdownClassName={mergedDropdownClassName.value}
placement={mergedPlacement.value}
onDropdownVisibleChange={onInternalDropdownVisibleChange}
// Children
getRawInputElement={() => slots.default?.()}
v-slots={slots}
/>
);
};
},
});

View File

@ -1,182 +0,0 @@
import { getComponent, findDOMNode } from '../_util/props-util';
import PropTypes from '../_util/vue-types';
import arrayTreeFilter from 'array-tree-filter';
import BaseMixin from '../_util/BaseMixin';
import isEqual from 'lodash-es/isEqual';
export default {
name: 'CascaderMenus',
mixins: [BaseMixin],
inheritAttrs: false,
props: {
value: PropTypes.array.def([]),
activeValue: PropTypes.array.def([]),
options: PropTypes.array,
prefixCls: PropTypes.string.def('rc-cascader-menus'),
expandTrigger: PropTypes.string.def('click'),
// onSelect: PropTypes.func,
visible: PropTypes.looseBool.def(false),
dropdownMenuColumnStyle: PropTypes.object,
defaultFieldNames: PropTypes.object,
fieldNames: PropTypes.object,
expandIcon: PropTypes.any,
loadingIcon: PropTypes.any,
},
data() {
this.menuItems = {};
return {};
},
watch: {
visible(val) {
if (val) {
this.$nextTick(() => {
this.scrollActiveItemToView();
});
}
},
},
mounted() {
this.$nextTick(() => {
this.scrollActiveItemToView();
});
},
methods: {
getFieldName(name) {
const { fieldNames, defaultFieldNames } = this.$props;
//
return fieldNames[name] || defaultFieldNames[name];
},
getOption(option, menuIndex) {
const { prefixCls, expandTrigger } = this;
const loadingIcon = getComponent(this, 'loadingIcon');
const expandIcon = getComponent(this, 'expandIcon');
const onSelect = e => {
this.__emit('select', option, menuIndex, e);
};
const onItemDoubleClick = e => {
this.__emit('itemDoubleClick', option, menuIndex, e);
};
const key = option[this.getFieldName('value')];
let expandProps = {
onClick: onSelect,
onDblclick: onItemDoubleClick,
};
let menuItemCls = `${prefixCls}-menu-item`;
let expandIconNode = null;
const hasChildren =
option[this.getFieldName('children')] && option[this.getFieldName('children')].length > 0;
if (hasChildren || option.isLeaf === false) {
menuItemCls += ` ${prefixCls}-menu-item-expand`;
if (!option.loading) {
expandIconNode = <span class={`${prefixCls}-menu-item-expand-icon`}>{expandIcon}</span>;
}
}
if (expandTrigger === 'hover' && (hasChildren || option.isLeaf === false)) {
expandProps = {
onMouseenter: this.delayOnSelect.bind(this, onSelect),
onMouseleave: this.delayOnSelect.bind(this),
onClick: onSelect,
};
}
if (this.isActiveOption(option, menuIndex)) {
menuItemCls += ` ${prefixCls}-menu-item-active`;
expandProps.ref = this.saveMenuItem(menuIndex);
}
if (option.disabled) {
menuItemCls += ` ${prefixCls}-menu-item-disabled`;
}
let loadingIconNode = null;
if (option.loading) {
menuItemCls += ` ${prefixCls}-menu-item-loading`;
loadingIconNode = loadingIcon || null;
}
let title = '';
if (option.title) {
title = option.title;
} else if (typeof option[this.getFieldName('label')] === 'string') {
title = option[this.getFieldName('label')];
}
return (
<li
key={Array.isArray(key) ? key.join('__ant__') : key}
class={menuItemCls}
title={title}
{...expandProps}
role="menuitem"
onMousedown={e => e.preventDefault()}
>
{option[this.getFieldName('label')]}
{expandIconNode}
{loadingIconNode}
</li>
);
},
getActiveOptions(values) {
const activeValue = values || this.activeValue;
const options = this.options;
return arrayTreeFilter(
options,
(o, level) => isEqual(o[this.getFieldName('value')], activeValue[level]),
{ childrenKeyName: this.getFieldName('children') },
);
},
getShowOptions() {
const { options } = this;
const result = this.getActiveOptions()
.map(activeOption => activeOption[this.getFieldName('children')])
.filter(activeOption => !!activeOption);
result.unshift(options);
return result;
},
delayOnSelect(onSelect, ...args) {
if (this.delayTimer) {
clearTimeout(this.delayTimer);
this.delayTimer = null;
}
if (typeof onSelect === 'function') {
this.delayTimer = setTimeout(() => {
onSelect(args);
this.delayTimer = null;
}, 150);
}
},
scrollActiveItemToView() {
// scroll into view
const optionsLength = this.getShowOptions().length;
for (let i = 0; i < optionsLength; i++) {
const itemComponent = this.menuItems[i];
if (itemComponent) {
const target = findDOMNode(itemComponent);
target.parentNode.scrollTop = target.offsetTop;
}
}
},
isActiveOption(option, menuIndex) {
const { activeValue = [] } = this;
return isEqual(activeValue[menuIndex], option[this.getFieldName('value')]);
},
saveMenuItem(index) {
return node => {
this.menuItems[index] = node;
};
},
},
render() {
const { prefixCls, dropdownMenuColumnStyle } = this;
return (
<div>
{this.getShowOptions().map((options, menuIndex) => (
<ul class={`${prefixCls}-menu`} key={menuIndex} style={dropdownMenuColumnStyle}>
{options.map(option => this.getOption(option, menuIndex))}
</ul>
))}
</div>
);
},
};

View File

@ -0,0 +1,44 @@
import type { MouseEventHandler } from '../../_util/EventInterface';
import { useInjectCascader } from '../context';
export interface CheckboxProps {
prefixCls: string;
checked?: boolean;
halfChecked?: boolean;
disabled?: boolean;
onClick?: MouseEventHandler;
}
export default function Checkbox({
prefixCls,
checked,
halfChecked,
disabled,
onClick,
}: CheckboxProps) {
const { customSlots, checkable } = useInjectCascader();
const mergedCheckable = checkable.value !== false ? customSlots.value.checkable : checkable.value;
const customCheckbox =
typeof mergedCheckable === 'function'
? mergedCheckable()
: typeof mergedCheckable === 'boolean'
? null
: mergedCheckable;
return (
<span
class={{
[prefixCls]: true,
[`${prefixCls}-checked`]: checked,
[`${prefixCls}-indeterminate`]: !checked && halfChecked,
[`${prefixCls}-disabled`]: disabled,
}}
onClick={onClick}
>
{customCheckbox}
</span>
);
}
Checkbox.props = ['prefixCls', 'checked', 'halfChecked', 'disabled', 'onClick'];
Checkbox.displayName = 'Checkbox';
Checkbox.inheritAttrs = false;

View File

@ -0,0 +1,176 @@
import { isLeaf, toPathKey } from '../utils/commonUtil';
import Checkbox from './Checkbox';
import type { DefaultOptionType, SingleValueType } from '../Cascader';
import { SEARCH_MARK } from '../hooks/useSearchOptions';
import type { Key } from '../../_util/type';
import { useInjectCascader } from '../context';
export interface ColumnProps {
prefixCls: string;
multiple?: boolean;
options: DefaultOptionType[];
/** Current Column opened item key */
activeValue?: Key;
/** The value path before current column */
prevValuePath: Key[];
onToggleOpen: (open: boolean) => void;
onSelect: (valuePath: SingleValueType, leaf: boolean) => void;
onActive: (valuePath: SingleValueType) => void;
checkedSet: Set<Key>;
halfCheckedSet: Set<Key>;
loadingKeys: Key[];
isSelectable: (option: DefaultOptionType) => boolean;
}
export default function Column({
prefixCls,
multiple,
options,
activeValue,
prevValuePath,
onToggleOpen,
onSelect,
onActive,
checkedSet,
halfCheckedSet,
loadingKeys,
isSelectable,
}: ColumnProps) {
const menuPrefixCls = `${prefixCls}-menu`;
const menuItemPrefixCls = `${prefixCls}-menu-item`;
const {
fieldNames,
changeOnSelect,
expandTrigger,
expandIcon: expandIconRef,
loadingIcon: loadingIconRef,
dropdownMenuColumnStyle,
customSlots,
} = useInjectCascader();
const expandIcon = expandIconRef.value ?? customSlots.value.expandIcon?.();
const loadingIcon = loadingIconRef.value ?? customSlots.value.loadingIcon?.();
const hoverOpen = expandTrigger.value === 'hover';
// ============================ Render ============================
return (
<ul class={menuPrefixCls} role="menu">
{options.map(option => {
const { disabled } = option;
const searchOptions = option[SEARCH_MARK];
const label = option[fieldNames.value.label];
const value = option[fieldNames.value.value];
const isMergedLeaf = isLeaf(option, fieldNames.value);
// Get real value of option. Search option is different way.
const fullPath = searchOptions
? searchOptions.map(opt => opt[fieldNames.value.value])
: [...prevValuePath, value];
const fullPathKey = toPathKey(fullPath);
const isLoading = loadingKeys.includes(fullPathKey);
// >>>>> checked
const checked = checkedSet.has(fullPathKey);
// >>>>> halfChecked
const halfChecked = halfCheckedSet.has(fullPathKey);
// >>>>> Open
const triggerOpenPath = () => {
if (!disabled && (!hoverOpen || !isMergedLeaf)) {
onActive(fullPath);
}
};
// >>>>> Selection
const triggerSelect = () => {
if (isSelectable(option)) {
onSelect(fullPath, isMergedLeaf);
}
};
// >>>>> Title
let title: string;
if (typeof option.title === 'string') {
title = option.title;
} else if (typeof label === 'string') {
title = label;
}
// >>>>> Render
return (
<li
key={fullPathKey}
class={[
menuItemPrefixCls,
{
[`${menuItemPrefixCls}-expand`]: !isMergedLeaf,
[`${menuItemPrefixCls}-active`]: activeValue === value,
[`${menuItemPrefixCls}-disabled`]: disabled,
[`${menuItemPrefixCls}-loading`]: isLoading,
},
]}
style={dropdownMenuColumnStyle.value}
role="menuitemcheckbox"
title={title}
aria-checked={checked}
data-path-key={fullPathKey}
onClick={() => {
triggerOpenPath();
if (!multiple || isMergedLeaf) {
triggerSelect();
}
}}
onDblclick={() => {
if (changeOnSelect.value) {
onToggleOpen(false);
}
}}
onMouseenter={() => {
if (hoverOpen) {
triggerOpenPath();
}
}}
>
{multiple && (
<Checkbox
prefixCls={`${prefixCls}-checkbox`}
checked={checked}
halfChecked={halfChecked}
disabled={disabled}
onClick={(e: MouseEvent) => {
e.stopPropagation();
triggerSelect();
}}
/>
)}
<div class={`${menuItemPrefixCls}-content`}>{option[fieldNames.value.label]}</div>
{!isLoading && expandIcon && !isMergedLeaf && (
<div class={`${menuItemPrefixCls}-expand-icon`}>{expandIcon}</div>
)}
{isLoading && loadingIcon && (
<div class={`${menuItemPrefixCls}-loading-icon`}>{loadingIcon}</div>
)}
</li>
);
})}
</ul>
);
}
Column.props = [
'prefixCls',
'multiple',
'options',
'activeValue',
'prevValuePath',
'onToggleOpen',
'onSelect',
'onActive',
'checkedSet',
'halfCheckedSet',
'loadingKeys',
'isSelectable',
];
Column.displayName = 'Column';
Column.inheritAttrs = false;

View File

@ -0,0 +1,228 @@
/* eslint-disable default-case */
import Column from './Column';
import type { DefaultOptionType, SingleValueType } from '../Cascader';
import { isLeaf, toPathKey, toPathKeys, toPathValueStr } from '../utils/commonUtil';
import useActive from './useActive';
import useKeyboard from './useKeyboard';
import { toPathOptions } from '../utils/treeUtil';
import { computed, defineComponent, ref, shallowRef, watchEffect } from 'vue';
import { useBaseProps } from '../../vc-select';
import { useInjectCascader } from '../context';
import type { Key } from '../../_util/type';
import type { EventHandler } from '../../_util/EventInterface';
export default defineComponent({
name: 'OptionList',
inheritAttrs: false,
setup(_props, context) {
const { attrs, slots } = context;
const baseProps = useBaseProps();
const containerRef = ref<HTMLDivElement>();
const rtl = computed(() => baseProps.direction === 'rtl');
const {
options,
values,
halfValues,
fieldNames,
changeOnSelect,
onSelect,
searchOptions,
dropdownPrefixCls,
loadData,
expandTrigger,
customSlots,
} = useInjectCascader();
const mergedPrefixCls = computed(() => dropdownPrefixCls.value || baseProps.prefixCls);
// ========================= loadData =========================
const loadingKeys = shallowRef<string[]>([]);
const internalLoadData = (valueCells: Key[]) => {
// Do not load when search
if (!loadData.value || baseProps.searchValue) {
return;
}
const optionList = toPathOptions(valueCells, options.value, fieldNames.value);
const rawOptions = optionList.map(({ option }) => option);
const lastOption = rawOptions[rawOptions.length - 1];
if (lastOption && !isLeaf(lastOption, fieldNames.value)) {
const pathKey = toPathKey(valueCells);
loadingKeys.value = [...loadingKeys.value, pathKey];
loadData.value(rawOptions);
}
};
watchEffect(() => {
if (loadingKeys.value.length) {
loadingKeys.value.forEach(loadingKey => {
const valueStrCells = toPathValueStr(loadingKey);
const optionList = toPathOptions(
valueStrCells,
options.value,
fieldNames.value,
true,
).map(({ option }) => option);
const lastOption = optionList[optionList.length - 1];
if (
!lastOption ||
lastOption[fieldNames.value.children] ||
isLeaf(lastOption, fieldNames.value)
) {
loadingKeys.value = loadingKeys.value.filter(key => key !== loadingKey);
}
});
}
});
// ========================== Values ==========================
const checkedSet = computed(() => new Set(toPathKeys(values.value)));
const halfCheckedSet = computed(() => new Set(toPathKeys(halfValues.value)));
// ====================== Accessibility =======================
const [activeValueCells, setActiveValueCells] = useActive();
// =========================== Path ===========================
const onPathOpen = (nextValueCells: Key[]) => {
setActiveValueCells(nextValueCells);
// Trigger loadData
internalLoadData(nextValueCells);
};
const isSelectable = (option: DefaultOptionType) => {
const { disabled } = option;
const isMergedLeaf = isLeaf(option, fieldNames.value);
return !disabled && (isMergedLeaf || changeOnSelect.value || baseProps.multiple);
};
const onPathSelect = (valuePath: SingleValueType, leaf: boolean, fromKeyboard = false) => {
onSelect(valuePath);
if (
!baseProps.multiple &&
(leaf || (changeOnSelect.value && (expandTrigger.value === 'hover' || fromKeyboard)))
) {
baseProps.toggleOpen(false);
}
};
// ========================== Option ==========================
const mergedOptions = computed(() => {
if (baseProps.searchValue) {
return searchOptions.value;
}
return options.value;
});
// ========================== Column ==========================
const optionColumns = computed(() => {
const optionList = [{ options: mergedOptions.value }];
let currentList = mergedOptions.value;
for (let i = 0; i < activeValueCells.value.length; i += 1) {
const activeValueCell = activeValueCells.value[i];
const currentOption = currentList.find(
option => option[fieldNames.value.value] === activeValueCell,
);
const subOptions = currentOption?.[fieldNames.value.children];
if (!subOptions?.length) {
break;
}
currentList = subOptions;
optionList.push({ options: subOptions });
}
return optionList;
});
// ========================= Keyboard =========================
const onKeyboardSelect = (selectValueCells: SingleValueType, option: DefaultOptionType) => {
if (isSelectable(option)) {
onPathSelect(selectValueCells, isLeaf(option, fieldNames.value), true);
}
};
useKeyboard(
context,
mergedOptions,
fieldNames,
activeValueCells,
onPathOpen,
containerRef,
onKeyboardSelect,
);
const onListMouseDown: EventHandler = event => {
event.preventDefault();
};
return () => {
// ========================== Render ==========================
const {
notFoundContent = slots.notFoundContent?.() || customSlots.value.notFoundContent?.(),
multiple,
toggleOpen,
} = baseProps;
// >>>>> Empty
const isEmpty = !optionColumns.value[0]?.options?.length;
const emptyList: DefaultOptionType[] = [
{
[fieldNames.value.label as 'label']: notFoundContent,
[fieldNames.value.value as 'value']: '__EMPTY__',
disabled: true,
},
];
const columnProps = {
...attrs,
multiple: !isEmpty && multiple,
onSelect: onPathSelect,
onActive: onPathOpen,
onToggleOpen: toggleOpen,
checkedSet: checkedSet.value,
halfCheckedSet: halfCheckedSet.value,
loadingKeys: loadingKeys.value,
isSelectable,
};
// >>>>> Columns
const mergedOptionColumns = isEmpty ? [{ options: emptyList }] : optionColumns.value;
const columnNodes = mergedOptionColumns.map((col, index) => {
const prevValuePath = activeValueCells.value.slice(0, index);
const activeValue = activeValueCells.value[index];
return (
<Column
key={index}
{...columnProps}
prefixCls={mergedPrefixCls.value}
options={col.options}
prevValuePath={prevValuePath}
activeValue={activeValue}
/>
);
});
return (
<div
class={[
`${mergedPrefixCls.value}-menus`,
{
[`${mergedPrefixCls.value}-menu-empty`]: isEmpty,
[`${mergedPrefixCls.value}-rtl`]: rtl.value,
},
]}
onMousedown={onListMouseDown}
ref={containerRef}
>
{columnNodes}
</div>
);
};
},
});

View File

@ -0,0 +1,31 @@
import { useInjectCascader } from '../context';
import type { Ref } from 'vue';
import { watch } from 'vue';
import { useBaseProps } from '../../vc-select';
import type { Key } from '../../_util/type';
import useState from '../../_util/hooks/useState';
/**
* Control the active open options path.
*/
export default (): [Ref<Key[]>, (activeValueCells: Key[]) => void] => {
const baseProps = useBaseProps();
const { values } = useInjectCascader();
// Record current dropdown active options
// This also control the open status
const [activeValueCells, setActiveValueCells] = useState<Key[]>([]);
watch(
() => baseProps.open,
() => {
if (baseProps.open && !baseProps.multiple) {
const firstValueCells = values.value[0];
setActiveValueCells(firstValueCells || []);
}
},
{ immediate: true },
);
return [activeValueCells, setActiveValueCells];
};

View File

@ -0,0 +1,188 @@
import type { RefOptionListProps } from '../../vc-select/OptionList';
import type { Key } from 'ant-design-vue/es/_util/type';
import type { Ref, SetupContext } from 'vue';
import { computed, ref, watchEffect } from 'vue';
import type { DefaultOptionType, InternalFieldNames, SingleValueType } from '../Cascader';
import { toPathKey } from '../utils/commonUtil';
import { useBaseProps } from '../../vc-select';
import KeyCode from '../../_util/KeyCode';
export default (
context: SetupContext,
options: Ref<DefaultOptionType[]>,
fieldNames: Ref<InternalFieldNames>,
activeValueCells: Ref<Key[]>,
setActiveValueCells: (activeValueCells: Key[]) => void,
containerRef: Ref<HTMLElement>,
onKeyBoardSelect: (valueCells: SingleValueType, option: DefaultOptionType) => void,
) => {
const baseProps = useBaseProps();
const rtl = computed(() => baseProps.direction === 'rtl');
const [validActiveValueCells, lastActiveIndex, lastActiveOptions] = [
ref<Key[]>([]),
ref<number>(),
ref<DefaultOptionType[]>([]),
];
watchEffect(() => {
let activeIndex = -1;
let currentOptions = options.value;
const mergedActiveIndexes: number[] = [];
const mergedActiveValueCells: Key[] = [];
const len = activeValueCells.value.length;
// Fill validate active value cells and index
for (let i = 0; i < len; i += 1) {
// Mark the active index for current options
const nextActiveIndex = currentOptions.findIndex(
option => option[fieldNames.value.value] === activeValueCells.value[i],
);
if (nextActiveIndex === -1) {
break;
}
activeIndex = nextActiveIndex;
mergedActiveIndexes.push(activeIndex);
mergedActiveValueCells.push(activeValueCells.value[i]);
currentOptions = currentOptions[activeIndex][fieldNames.value.children];
}
// Fill last active options
let activeOptions = options.value;
for (let i = 0; i < mergedActiveIndexes.length - 1; i += 1) {
activeOptions = activeOptions[mergedActiveIndexes[i]][fieldNames.value.children];
}
[validActiveValueCells.value, lastActiveIndex.value, lastActiveOptions.value] = [
mergedActiveValueCells,
activeIndex,
activeOptions,
];
});
// Update active value cells and scroll to target element
const internalSetActiveValueCells = (next: Key[]) => {
setActiveValueCells(next);
const ele = containerRef.value?.querySelector(`li[data-path-key="${toPathKey(next)}"]`);
ele?.scrollIntoView?.({ block: 'nearest' });
};
// Same options offset
const offsetActiveOption = (offset: number) => {
const len = lastActiveOptions.value.length;
let currentIndex = lastActiveIndex.value;
if (currentIndex === -1 && offset < 0) {
currentIndex = len;
}
for (let i = 0; i < len; i += 1) {
currentIndex = (currentIndex + offset + len) % len;
const option = lastActiveOptions.value[currentIndex];
if (option && !option.disabled) {
const value = option[fieldNames.value.value];
const nextActiveCells = validActiveValueCells.value.slice(0, -1).concat(value);
internalSetActiveValueCells(nextActiveCells);
return;
}
}
};
// Different options offset
const prevColumn = () => {
if (validActiveValueCells.value.length > 1) {
const nextActiveCells = validActiveValueCells.value.slice(0, -1);
internalSetActiveValueCells(nextActiveCells);
} else {
baseProps.toggleOpen(false);
}
};
const nextColumn = () => {
const nextOptions: DefaultOptionType[] =
lastActiveOptions.value[lastActiveIndex.value]?.[fieldNames.value.children] || [];
const nextOption = nextOptions.find(option => !option.disabled);
if (nextOption) {
const nextActiveCells = [...validActiveValueCells.value, nextOption[fieldNames.value.value]];
internalSetActiveValueCells(nextActiveCells);
}
};
context.expose({
// scrollTo: treeRef.current?.scrollTo,
onKeydown: event => {
const { which } = event;
switch (which) {
// >>> Arrow keys
case KeyCode.UP:
case KeyCode.DOWN: {
let offset = 0;
if (which === KeyCode.UP) {
offset = -1;
} else if (which === KeyCode.DOWN) {
offset = 1;
}
if (offset !== 0) {
offsetActiveOption(offset);
}
break;
}
case KeyCode.LEFT: {
if (rtl.value) {
nextColumn();
} else {
prevColumn();
}
break;
}
case KeyCode.RIGHT: {
if (rtl.value) {
prevColumn();
} else {
nextColumn();
}
break;
}
case KeyCode.BACKSPACE: {
if (!baseProps.searchValue) {
prevColumn();
}
break;
}
// >>> Select
case KeyCode.ENTER: {
if (validActiveValueCells.value.length) {
onKeyBoardSelect(
validActiveValueCells.value,
lastActiveOptions.value[lastActiveIndex.value],
);
}
break;
}
// >>> Close
case KeyCode.ESC: {
baseProps.toggleOpen(false);
if (open) {
event.stopPropagation();
}
}
}
},
onKeyup: () => {},
} as RefOptionListProps);
};

View File

@ -1,168 +0,0 @@
.effect() {
animation-duration: 0.3s;
animation-fill-mode: both;
transform-origin: 0 0;
}
.rc-cascader {
font-size: 12px;
&-menus {
font-size: 12px;
overflow: hidden;
background: #fff;
position: absolute;
border: 1px solid #d9d9d9;
border-radius: 6px;
box-shadow: 0 0 4px rgba(0, 0, 0, 0.17);
white-space: nowrap;
&-hidden {
display: none;
}
&.slide-up-enter,
&.slide-up-appear {
.effect();
opacity: 0;
animation-timing-function: cubic-bezier(0.08, 0.82, 0.17, 1);
animation-play-state: paused;
}
&.slide-up-leave {
.effect();
opacity: 1;
animation-timing-function: cubic-bezier(0.6, 0.04, 0.98, 0.34);
animation-play-state: paused;
}
&.slide-up-enter.slide-up-enter-active&-placement-bottomLeft,
&.slide-up-appear.slide-up-appear-active&-placement-bottomLeft {
animation-name: SlideUpIn;
animation-play-state: running;
}
&.slide-up-enter.slide-up-enter-active&-placement-topLeft,
&.slide-up-appear.slide-up-appear-active&-placement-topLeft {
animation-name: SlideDownIn;
animation-play-state: running;
}
&.slide-up-leave.slide-up-leave-active&-placement-bottomLeft {
animation-name: SlideUpOut;
animation-play-state: running;
}
&.slide-up-leave.slide-up-leave-active&-placement-topLeft {
animation-name: SlideDownOut;
animation-play-state: running;
}
}
&-menu {
display: inline-block;
width: 100px;
height: 192px;
list-style: none;
margin: 0;
padding: 0;
border-right: 1px solid #e9e9e9;
overflow: auto;
&:last-child {
border-right: 0;
}
}
&-menu-item {
height: 32px;
line-height: 32px;
padding: 0 16px;
cursor: pointer;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
transition: all 0.3s ease;
position: relative;
&:hover {
background: tint(#2db7f5, 90%);
}
&-disabled {
cursor: not-allowed;
color: #ccc;
&:hover {
background: transparent;
}
}
&-loading:after {
position: absolute;
right: 12px;
content: 'loading';
color: #aaa;
font-style: italic;
}
&-active {
background: tint(#2db7f5, 80%);
&:hover {
background: tint(#2db7f5, 80%);
}
}
&-expand {
position: relative;
&:after {
content: '>';
font-size: 12px;
color: #999;
position: absolute;
right: 16px;
line-height: 32px;
}
}
}
}
@keyframes SlideUpIn {
0% {
opacity: 0;
transform-origin: 0% 0%;
transform: scaleY(0.8);
}
100% {
opacity: 1;
transform-origin: 0% 0%;
transform: scaleY(1);
}
}
@keyframes SlideUpOut {
0% {
opacity: 1;
transform-origin: 0% 0%;
transform: scaleY(1);
}
100% {
opacity: 0;
transform-origin: 0% 0%;
transform: scaleY(0.8);
}
}
@keyframes SlideDownIn {
0% {
opacity: 0;
transform-origin: 0% 100%;
transform: scaleY(0.8);
}
100% {
opacity: 1;
transform-origin: 0% 100%;
transform: scaleY(1);
}
}
@keyframes SlideDownOut {
0% {
opacity: 1;
transform-origin: 0% 100%;
transform: scaleY(1);
}
100% {
opacity: 0;
transform-origin: 0% 100%;
transform: scaleY(0.8);
}
}

View File

@ -0,0 +1,36 @@
import type { CSSProperties, InjectionKey, Ref } from 'vue';
import { inject, provide } from 'vue';
import type { VueNode } from '../_util/type';
import type {
BaseCascaderProps,
InternalFieldNames,
DefaultOptionType,
SingleValueType,
} from './Cascader';
export interface CascaderContextProps {
options: Ref<BaseCascaderProps['options']>;
fieldNames: Ref<InternalFieldNames>;
values: Ref<SingleValueType[]>;
halfValues: Ref<SingleValueType[]>;
changeOnSelect: Ref<boolean>;
onSelect: (valuePath: SingleValueType) => void;
checkable: Ref<boolean | VueNode>;
searchOptions: Ref<DefaultOptionType[]>;
dropdownPrefixCls?: Ref<string>;
loadData: Ref<(selectOptions: DefaultOptionType[]) => void>;
expandTrigger: Ref<'hover' | 'click'>;
expandIcon: Ref<VueNode>;
loadingIcon: Ref<VueNode>;
dropdownMenuColumnStyle: Ref<CSSProperties>;
customSlots: Ref<Record<string, Function>>;
}
const CascaderContextKey: InjectionKey<CascaderContextProps> = Symbol('CascaderContextKey');
export const useProvideCascader = (props: CascaderContextProps) => {
provide(CascaderContextKey, props);
};
export const useInjectCascader = () => {
return inject(CascaderContextKey);
};

View File

@ -0,0 +1,60 @@
import { toPathOptions } from '../utils/treeUtil';
import type {
DefaultOptionType,
SingleValueType,
BaseCascaderProps,
InternalFieldNames,
} from '../Cascader';
import { toPathKey } from '../utils/commonUtil';
import type { Ref } from 'vue';
import { computed } from 'vue';
import { isValidElement } from '../../_util/props-util';
import { cloneElement } from '../../_util/vnode';
export default (
rawValues: Ref<SingleValueType[]>,
options: Ref<DefaultOptionType[]>,
fieldNames: Ref<InternalFieldNames>,
multiple: Ref<boolean>,
displayRender: Ref<BaseCascaderProps['displayRender']>,
) => {
return computed(() => {
const mergedDisplayRender =
displayRender.value ||
// Default displayRender
(({ labels }) => {
const mergedLabels = multiple.value ? labels.slice(-1) : labels;
const SPLIT = ' / ';
if (mergedLabels.every(label => ['string', 'number'].includes(typeof label))) {
return mergedLabels.join(SPLIT);
}
// If exist non-string value, use VueNode instead
return mergedLabels.reduce((list, label, index) => {
const keyedLabel = isValidElement(label) ? cloneElement(label, { key: index }) : label;
if (index === 0) {
return [keyedLabel];
}
return [...list, SPLIT, keyedLabel];
}, []);
});
return rawValues.value.map(valueCells => {
const valueOptions = toPathOptions(valueCells, options.value, fieldNames.value);
const label = mergedDisplayRender({
labels: valueOptions.map(({ option, value }) => option?.[fieldNames.value.label] ?? value),
selectedOptions: valueOptions.map(({ option }) => option),
});
return {
label,
value: toPathKey(valueCells),
valueCells,
};
});
});
};

View File

@ -0,0 +1,36 @@
import { convertDataToEntities } from '../../vc-tree/utils/treeUtil';
import type { DataEntity } from '../../vc-tree/interface';
import type { DefaultOptionType, InternalFieldNames } from '../Cascader';
import { VALUE_SPLIT } from '../utils/commonUtil';
import type { Ref } from 'vue';
import { computed } from 'vue';
export interface OptionsInfo {
keyEntities: Record<string, DataEntity>;
pathKeyEntities: Record<string, DataEntity>;
}
/** Lazy parse options data into conduct-able info to avoid perf issue in single mode */
export default (options: Ref<DefaultOptionType[]>, fieldNames: Ref<InternalFieldNames>) => {
const entities = computed(() => {
return (
convertDataToEntities(options.value as any, {
fieldNames: fieldNames.value,
initWrapper: wrapper => ({
...wrapper,
pathKeyEntities: {},
}),
processEntity: (entity, wrapper: any) => {
const pathKey = entity.nodes.map(node => node[fieldNames.value.value]).join(VALUE_SPLIT);
wrapper.pathKeyEntities[pathKey] = entity;
// Overwrite origin key.
// this is very hack but we need let conduct logic work with connect path
entity.key = pathKey;
},
}) as any
).pathKeyEntities;
});
return entities;
};

View File

@ -0,0 +1,26 @@
import type { Ref } from 'vue';
import { computed } from 'vue';
import type { SingleValueType, DefaultOptionType, InternalFieldNames } from '../Cascader';
import { toPathOptions } from '../utils/treeUtil';
export default (
options: Ref<DefaultOptionType[]>,
fieldNames: Ref<InternalFieldNames>,
rawValues: Ref<SingleValueType[]>,
) => {
return computed(() => {
const missingValues: SingleValueType[] = [];
const existsValues: SingleValueType[] = [];
rawValues.value.forEach(valueCell => {
const pathOptions = toPathOptions(valueCell, options.value, fieldNames.value);
if (pathOptions.every(opt => opt.option)) {
existsValues.push(valueCell);
} else {
missingValues.push(valueCell);
}
});
return [existsValues, missingValues];
});
};

View File

@ -0,0 +1,41 @@
import type { BaseCascaderProps, ShowSearchType } from '../Cascader';
import type { Ref } from 'vue';
import { ref, watchEffect } from 'vue';
import { warning } from '../../vc-util/warning';
// Convert `showSearch` to unique config
export default function useSearchConfig(showSearch?: Ref<BaseCascaderProps['showSearch']>) {
const mergedShowSearch = ref(false);
const mergedSearchConfig = ref<ShowSearchType>({});
watchEffect(() => {
if (!showSearch.value) {
mergedShowSearch.value = false;
mergedSearchConfig.value = {};
return;
}
let searchConfig: ShowSearchType = {
matchInputWidth: true,
limit: 50,
};
if (showSearch.value && typeof showSearch.value === 'object') {
searchConfig = {
...searchConfig,
...showSearch.value,
};
}
if (searchConfig.limit <= 0) {
delete searchConfig.limit;
if (process.env.NODE_ENV !== 'production') {
warning(false, "'limit' of showSearch should be positive number or false.");
}
}
mergedShowSearch.value = true;
mergedSearchConfig.value = searchConfig;
return;
});
return { showSearch: mergedShowSearch, searchConfig: mergedSearchConfig };
}

View File

@ -0,0 +1,76 @@
import type { Ref } from 'vue';
import { computed } from 'vue';
import type { DefaultOptionType, ShowSearchType, InternalFieldNames } from '../Cascader';
export const SEARCH_MARK = '__rc_cascader_search_mark__';
const defaultFilter: ShowSearchType['filter'] = (search, options, { label }) =>
options.some(opt => String(opt[label]).toLowerCase().includes(search.toLowerCase()));
const defaultRender: ShowSearchType['render'] = ({ path, fieldNames }) =>
path.map(opt => opt[fieldNames.label]).join(' / ');
export default (
search: Ref<string>,
options: Ref<DefaultOptionType[]>,
fieldNames: Ref<InternalFieldNames>,
prefixCls: Ref<string>,
config: Ref<ShowSearchType>,
changeOnSelect: Ref<boolean>,
) => {
return computed(() => {
const { filter = defaultFilter, render = defaultRender, limit = 50, sort } = config.value;
const filteredOptions: DefaultOptionType[] = [];
if (!search.value) {
return [];
}
function dig(list: DefaultOptionType[], pathOptions: DefaultOptionType[]) {
list.forEach(option => {
// Perf saving when `sort` is disabled and `limit` is provided
if (!sort && limit > 0 && filteredOptions.length >= limit) {
return;
}
const connectedPathOptions = [...pathOptions, option];
const children = option[fieldNames.value.children];
// If current option is filterable
if (
// If is leaf option
!children ||
// If is changeOnSelect
changeOnSelect.value
) {
if (filter(search.value, connectedPathOptions, { label: fieldNames.value.label })) {
filteredOptions.push({
...option,
[fieldNames.value.label as 'label']: render({
inputValue: search.value,
path: connectedPathOptions,
prefixCls: prefixCls.value,
fieldNames: fieldNames.value,
}),
[SEARCH_MARK]: connectedPathOptions,
});
}
}
if (children) {
dig(option[fieldNames.value.children] as DefaultOptionType[], connectedPathOptions);
}
});
}
dig(options.value, []);
// Do sort
if (sort) {
filteredOptions.sort((a, b) => {
return sort(a[SEARCH_MARK], b[SEARCH_MARK], search.value, fieldNames.value);
});
}
return limit > 0 ? filteredOptions.slice(0, limit as number) : filteredOptions;
});
};

View File

@ -1,3 +0,0 @@
// based on rc-cascader 0.17.4
import Cascader from './Cascader';
export default Cascader;

View File

@ -0,0 +1,12 @@
// rc-cascader@3.0.0-alpha.6
import Cascader, { internalCascaderProps as cascaderProps } from './Cascader';
export type {
CascaderProps,
FieldNames,
ShowSearchType,
DefaultOptionType,
BaseOptionType,
} from './Cascader';
export { cascaderProps };
export default Cascader;

View File

@ -0,0 +1,35 @@
import type {
DefaultOptionType,
FieldNames,
InternalFieldNames,
SingleValueType,
} from '../Cascader';
export const VALUE_SPLIT = '__RC_CASCADER_SPLIT__';
export function toPathKey(value: SingleValueType) {
return value.join(VALUE_SPLIT);
}
export function toPathKeys(value: SingleValueType[]) {
return value.map(toPathKey);
}
export function toPathValueStr(pathKey: string) {
return pathKey.split(VALUE_SPLIT);
}
export function fillFieldNames(fieldNames?: FieldNames): InternalFieldNames {
const { label, value, children } = fieldNames || {};
const val = value || 'value';
return {
label: label || 'label',
value: val,
key: val,
children: children || 'children',
};
}
export function isLeaf(option: DefaultOptionType, fieldNames: FieldNames) {
return option.isLeaf ?? !option[fieldNames.children]?.length;
}

View File

@ -0,0 +1,54 @@
import type { Key } from '../../_util/type';
import type { SingleValueType, DefaultOptionType, InternalFieldNames } from '../Cascader';
import type { OptionsInfo } from '../hooks/useEntities';
export function formatStrategyValues(
pathKeys: Key[],
keyPathEntities: OptionsInfo['pathKeyEntities'],
) {
const valueSet = new Set(pathKeys);
return pathKeys.filter(key => {
const entity = keyPathEntities[key];
const parent = entity ? entity.parent : null;
if (parent && !parent.node.disabled && valueSet.has(parent.key)) {
return false;
}
return true;
});
}
export function toPathOptions(
valueCells: SingleValueType,
options: DefaultOptionType[],
fieldNames: InternalFieldNames,
// Used for loadingKeys which saved loaded keys as string
stringMode = false,
) {
let currentList = options;
const valueOptions: {
value: SingleValueType[number];
index: number;
option: DefaultOptionType;
}[] = [];
for (let i = 0; i < valueCells.length; i += 1) {
const valueCell = valueCells[i];
const foundIndex = currentList?.findIndex(option => {
const val = option[fieldNames.value];
return stringMode ? String(val) === String(valueCell) : val === valueCell;
});
const foundOption = foundIndex !== -1 ? currentList?.[foundIndex] : null;
valueOptions.push({
value: foundOption?.[fieldNames.value] ?? valueCell,
index: foundIndex,
option: foundOption,
});
currentList = foundOption?.[fieldNames.children];
}
return valueOptions;
}

View File

@ -307,7 +307,7 @@ const Overflow = defineComponent({
let restNode = () => null;
const restContextProps = {
order: displayRest ? mergedDisplayCount.value : Number.MAX_SAFE_INTEGER,
className: `${itemPrefixCls.value}-rest`,
className: `${itemPrefixCls.value} ${itemPrefixCls.value}-rest`,
registerSize: registerOverflowSize,
display: displayRest,
};

View File

@ -0,0 +1,895 @@
import { getSeparatedContent } from './utils/valueUtil';
import type { RefTriggerProps } from './SelectTrigger';
import SelectTrigger from './SelectTrigger';
import type { RefSelectorProps } from './Selector';
import Selector from './Selector';
import useSelectTriggerControl from './hooks/useSelectTriggerControl';
import useDelayReset from './hooks/useDelayReset';
import TransBtn from './TransBtn';
import useLock from './hooks/useLock';
import type { BaseSelectContextProps } from './hooks/useBaseProps';
import { useProvideBaseSelectProps } from './hooks/useBaseProps';
import type { Key, VueNode } from '../_util/type';
import type {
FocusEventHandler,
KeyboardEventHandler,
MouseEventHandler,
} from '../_util/EventInterface';
import type { ScrollTo } from '../vc-virtual-list/List';
import {
computed,
defineComponent,
getCurrentInstance,
onBeforeUnmount,
onMounted,
provide,
ref,
toRefs,
watch,
watchEffect,
} from 'vue';
import type { CSSProperties, ExtractPropTypes, PropType, VNode } from 'vue';
import PropTypes from '../_util/vue-types';
import { initDefaultProps, isValidElement } from '../_util/props-util';
import isMobile from '../vc-util/isMobile';
import KeyCode from '../_util/KeyCode';
import { toReactive } from '../_util/toReactive';
import classNames from '../_util/classNames';
import createRef from '../_util/createRef';
import type { BaseOptionType } from './Select';
import useInjectLegacySelectContext from '../vc-tree-select/LegacyContext';
import { cloneElement } from '../_util/vnode';
const DEFAULT_OMIT_PROPS = [
'value',
'onChange',
'removeIcon',
'placeholder',
'autofocus',
'maxTagCount',
'maxTagTextLength',
'maxTagPlaceholder',
'choiceTransitionName',
'onInputKeyDown',
'onPopupScroll',
'tabindex',
'OptionList',
'notFoundContent',
] as const;
export type RenderNode = VueNode | ((props: any) => VueNode);
export type RenderDOMFunc = (props: any) => HTMLElement;
export type Mode = 'multiple' | 'tags' | 'combobox';
export type Placement = 'bottomLeft' | 'bottomRight' | 'topLeft' | 'topRight';
export type RawValueType = string | number;
export interface RefOptionListProps {
onKeydown: KeyboardEventHandler;
onKeyup: KeyboardEventHandler;
scrollTo?: (index: number) => void;
}
export type CustomTagProps = {
label: any;
value: any;
disabled: boolean;
onClose: (event?: MouseEvent) => void;
closable: boolean;
option: BaseOptionType;
};
export interface DisplayValueType {
key?: Key;
value?: RawValueType;
label?: any;
disabled?: boolean;
option?: BaseOptionType;
}
export type BaseSelectRef = {
focus: () => void;
blur: () => void;
scrollTo: ScrollTo;
};
const baseSelectPrivateProps = () => {
return {
prefixCls: String,
id: String,
omitDomProps: Array as PropType<string[]>,
// >>> Value
displayValues: Array as PropType<DisplayValueType[]>,
onDisplayValuesChange: Function as PropType<
(
values: DisplayValueType[],
info: {
type: 'add' | 'remove' | 'clear';
values: DisplayValueType[];
},
) => void
>,
// >>> Active
/** Current dropdown list active item string value */
activeValue: String,
/** Link search input with target element */
activeDescendantId: String,
onActiveValueChange: Function as PropType<(value: string | null) => void>,
// >>> Search
searchValue: String,
/** Trigger onSearch, return false to prevent trigger open event */
onSearch: Function as PropType<
(
searchValue: string,
info: {
source:
| 'typing' //User typing
| 'effect' // Code logic trigger
| 'submit' // tag mode only
| 'blur'; // Not trigger event
},
) => void
>,
/** Trigger when search text match the `tokenSeparators`. Will provide split content */
onSearchSplit: Function as PropType<(words: string[]) => void>,
maxLength: Number,
OptionList: PropTypes.any,
/** Tell if provided `options` is empty */
emptyOptions: Boolean,
};
};
export type DropdownObject = {
menuNode?: VueNode;
props?: Record<string, any>;
};
export type DropdownRender = (opt?: DropdownObject) => VueNode;
export const baseSelectPropsWithoutPrivate = () => {
return {
showSearch: { type: Boolean, default: undefined },
tagRender: { type: Function as PropType<(props: CustomTagProps) => any> },
direction: { type: String as PropType<'ltr' | 'rtl'> },
// MISC
tabindex: Number,
autofocus: Boolean,
notFoundContent: PropTypes.any,
placeholder: PropTypes.any,
onClear: Function as PropType<() => void>,
choiceTransitionName: String,
// >>> Mode
mode: String as PropType<Mode>,
// >>> Status
disabled: { type: Boolean, default: undefined },
loading: { type: Boolean, default: undefined },
// >>> Open
open: { type: Boolean, default: undefined },
defaultOpen: { type: Boolean, default: undefined },
onDropdownVisibleChange: { type: Function as PropType<(open: boolean) => void> },
// >>> Customize Input
/** @private Internal usage. Do not use in your production. */
getInputElement: { type: Function as PropType<() => any> },
/** @private Internal usage. Do not use in your production. */
getRawInputElement: { type: Function as PropType<() => any> },
// >>> Selector
maxTagTextLength: Number,
maxTagCount: { type: [String, Number] as PropType<number | 'responsive'> },
maxTagPlaceholder: PropTypes.any,
// >>> Search
tokenSeparators: { type: Array as PropType<string[]> },
// >>> Icons
allowClear: { type: Boolean, default: undefined },
showArrow: { type: Boolean, default: undefined },
inputIcon: PropTypes.any,
/** Clear all icon */
clearIcon: PropTypes.any,
/** Selector remove icon */
removeIcon: PropTypes.any,
// >>> Dropdown
animation: String,
transitionName: String,
dropdownStyle: { type: Object as PropType<CSSProperties> },
dropdownClassName: String,
dropdownMatchSelectWidth: {
type: [Boolean, Number] as PropType<boolean | number>,
default: undefined,
},
dropdownRender: { type: Function as PropType<DropdownRender> },
dropdownAlign: PropTypes.any,
placement: {
type: String as PropType<Placement>,
},
getPopupContainer: { type: Function as PropType<RenderDOMFunc> },
// >>> Focus
showAction: { type: Array as PropType<('focus' | 'click')[]> },
onBlur: { type: Function as PropType<(e: FocusEvent) => void> },
onFocus: { type: Function as PropType<(e: FocusEvent) => void> },
// >>> Rest Events
onKeyup: Function as PropType<(e: KeyboardEvent) => void>,
onKeydown: Function as PropType<(e: KeyboardEvent) => void>,
onMousedown: Function as PropType<(e: MouseEvent) => void>,
onPopupScroll: Function as PropType<(e: UIEvent) => void>,
onInputKeyDown: Function as PropType<(e: KeyboardEvent) => void>,
onMouseenter: Function as PropType<(e: MouseEvent) => void>,
onMouseleave: Function as PropType<(e: MouseEvent) => void>,
onClick: Function as PropType<(e: MouseEvent) => void>,
};
};
const baseSelectProps = () => {
return {
...baseSelectPrivateProps(),
...baseSelectPropsWithoutPrivate(),
};
};
export type BaseSelectPrivateProps = Partial<
ExtractPropTypes<ReturnType<typeof baseSelectPrivateProps>>
>;
export type BaseSelectProps = Partial<ExtractPropTypes<ReturnType<typeof baseSelectProps>>>;
export type BaseSelectPropsWithoutPrivate = Omit<BaseSelectProps, keyof BaseSelectPrivateProps>;
export function isMultiple(mode: Mode) {
return mode === 'tags' || mode === 'multiple';
}
export default defineComponent({
name: 'BaseSelect',
inheritAttrs: false,
props: initDefaultProps(baseSelectProps(), { showAction: [], notFoundContent: 'Not Found' }),
setup(props, { attrs, expose, slots }) {
const multiple = computed(() => isMultiple(props.mode));
const mergedShowSearch = computed(() =>
props.showSearch !== undefined
? props.showSearch
: multiple.value || props.mode === 'combobox',
);
const mobile = ref(false);
onMounted(() => {
mobile.value = isMobile();
});
const legacyTreeSelectContext = useInjectLegacySelectContext();
// ============================== Refs ==============================
const containerRef = ref<HTMLDivElement>(null);
const selectorDomRef = createRef();
const triggerRef = ref<RefTriggerProps>(null);
const selectorRef = ref<RefSelectorProps>(null);
const listRef = ref<RefOptionListProps>(null);
/** Used for component focused management */
const [mockFocused, setMockFocused, cancelSetMockFocused] = useDelayReset();
const focus = () => {
selectorRef.value?.focus();
};
const blur = () => {
selectorRef.value?.blur();
};
expose({
focus,
blur,
scrollTo: arg => listRef.value?.scrollTo(arg),
});
const mergedSearchValue = computed(() => {
if (props.mode !== 'combobox') {
return props.searchValue;
}
const val = props.displayValues[0]?.value;
return typeof val === 'string' || typeof val === 'number' ? String(val) : '';
});
// ============================== Open ==============================
const initOpen = props.open !== undefined ? props.open : props.defaultOpen;
const innerOpen = ref(initOpen);
const mergedOpen = ref(initOpen);
const setInnerOpen = (val: boolean) => {
innerOpen.value = props.open !== undefined ? props.open : val;
mergedOpen.value = innerOpen.value;
};
watch(
() => props.open,
() => {
setInnerOpen(props.open);
},
);
// Not trigger `open` in `combobox` when `notFoundContent` is empty
const emptyListContent = computed(() => !props.notFoundContent && props.emptyOptions);
watchEffect(() => {
mergedOpen.value = innerOpen.value;
if (
props.disabled ||
(emptyListContent.value && mergedOpen.value && props.mode === 'combobox')
) {
mergedOpen.value = false;
}
});
const triggerOpen = computed(() => (emptyListContent.value ? false : mergedOpen.value));
const onToggleOpen = (newOpen?: boolean) => {
const nextOpen = newOpen !== undefined ? newOpen : !mergedOpen.value;
if (innerOpen.value !== nextOpen && !props.disabled) {
setInnerOpen(nextOpen);
if (props.onDropdownVisibleChange) {
props.onDropdownVisibleChange(nextOpen);
}
}
};
const tokenWithEnter = computed(() =>
(props.tokenSeparators || []).some(tokenSeparator => ['\n', '\r\n'].includes(tokenSeparator)),
);
const onInternalSearch = (searchText: string, fromTyping: boolean, isCompositing: boolean) => {
let ret = true;
let newSearchText = searchText;
props.onActiveValueChange?.(null);
// Check if match the `tokenSeparators`
const patchLabels: string[] = isCompositing
? null
: getSeparatedContent(searchText, props.tokenSeparators);
// Ignore combobox since it's not split-able
if (props.mode !== 'combobox' && patchLabels) {
newSearchText = '';
props.onSearchSplit?.(patchLabels);
// Should close when paste finish
onToggleOpen(false);
// Tell Selector that break next actions
ret = false;
}
if (props.onSearch && mergedSearchValue.value !== newSearchText) {
props.onSearch(newSearchText, {
source: fromTyping ? 'typing' : 'effect',
});
}
return ret;
};
// Only triggered when menu is closed & mode is tags
// If menu is open, OptionList will take charge
// If mode isn't tags, press enter is not meaningful when you can't see any option
const onInternalSearchSubmit = (searchText: string) => {
// prevent empty tags from appearing when you click the Enter button
if (!searchText || !searchText.trim()) {
return;
}
props.onSearch?.(searchText, { source: 'submit' });
};
// Close will clean up single mode search text
watch(
mergedOpen,
() => {
if (!mergedOpen.value && !multiple.value && props.mode !== 'combobox') {
onInternalSearch('', false, false);
}
},
{ immediate: true },
);
// ============================ Disabled ============================
// Close dropdown & remove focus state when disabled change
watch(
() => props.disabled,
() => {
if (innerOpen.value && !!props.disabled) {
setInnerOpen(false);
}
},
{ immediate: true },
);
// ============================ Keyboard ============================
/**
* We record input value here to check if can press to clean up by backspace
* - null: Key is not down, this is reset by key up
* - true: Search text is empty when first time backspace down
* - false: Search text is not empty when first time backspace down
*/
const [getClearLock, setClearLock] = useLock();
// KeyDown
const onInternalKeyDown: KeyboardEventHandler = (event, ...rest) => {
const clearLock = getClearLock();
const { which } = event;
if (which === KeyCode.ENTER) {
// Do not submit form when type in the input
if (props.mode !== 'combobox') {
event.preventDefault();
}
// We only manage open state here, close logic should handle by list component
if (!mergedOpen.value) {
onToggleOpen(true);
}
}
setClearLock(!!mergedSearchValue.value);
// Remove value by `backspace`
if (
which === KeyCode.BACKSPACE &&
!clearLock &&
multiple.value &&
!mergedSearchValue.value &&
props.displayValues.length
) {
const cloneDisplayValues = [...props.displayValues];
let removedDisplayValue = null;
for (let i = cloneDisplayValues.length - 1; i >= 0; i -= 1) {
const current = cloneDisplayValues[i];
if (!current.disabled) {
cloneDisplayValues.splice(i, 1);
removedDisplayValue = current;
break;
}
}
if (removedDisplayValue) {
props.onDisplayValuesChange(cloneDisplayValues, {
type: 'remove',
values: [removedDisplayValue],
});
}
}
if (mergedOpen.value && listRef.value) {
listRef.value.onKeydown(event, ...rest);
}
props.onKeydown?.(event, ...rest);
};
// KeyUp
const onInternalKeyUp: KeyboardEventHandler = (event: KeyboardEvent, ...rest) => {
if (mergedOpen.value && listRef.value) {
listRef.value.onKeyup(event, ...rest);
}
if (props.onKeyup) {
props.onKeyup(event, ...rest);
}
};
// ============================ Selector ============================
const onSelectorRemove = (val: DisplayValueType) => {
const newValues = props.displayValues.filter(i => i !== val);
props.onDisplayValuesChange(newValues, {
type: 'remove',
values: [val],
});
};
// ========================== Focus / Blur ==========================
/** Record real focus status */
const focusRef = ref(false);
const onContainerFocus: FocusEventHandler = (...args) => {
setMockFocused(true);
if (!props.disabled) {
if (props.onFocus && !focusRef.value) {
props.onFocus(...args);
}
// `showAction` should handle `focus` if set
if (props.showAction && props.showAction.includes('focus')) {
onToggleOpen(true);
}
}
focusRef.value = true;
};
const onContainerBlur: FocusEventHandler = (...args) => {
setMockFocused(false, () => {
focusRef.value = false;
onToggleOpen(false);
});
if (props.disabled) {
return;
}
const searchVal = mergedSearchValue.value;
if (searchVal) {
// `tags` mode should move `searchValue` into values
if (props.mode === 'tags') {
props.onSearch(searchVal, { source: 'submit' });
} else if (props.mode === 'multiple') {
// `multiple` mode only clean the search value but not trigger event
props.onSearch('', {
source: 'blur',
});
}
}
if (props.onBlur) {
props.onBlur(...args);
}
};
provide('VCSelectContainerEvent', {
focus: onContainerFocus,
blur: onContainerBlur,
});
// Give focus back of Select
const activeTimeoutIds: any[] = [];
onMounted(() => {
activeTimeoutIds.forEach(timeoutId => clearTimeout(timeoutId));
activeTimeoutIds.splice(0, activeTimeoutIds.length);
});
onBeforeUnmount(() => {
activeTimeoutIds.forEach(timeoutId => clearTimeout(timeoutId));
activeTimeoutIds.splice(0, activeTimeoutIds.length);
});
const onInternalMouseDown: MouseEventHandler = (event, ...restArgs) => {
const { target } = event;
const popupElement: HTMLDivElement = triggerRef.value?.getPopupElement();
// We should give focus back to selector if clicked item is not focusable
if (popupElement && popupElement.contains(target as HTMLElement)) {
const timeoutId: any = setTimeout(() => {
const index = activeTimeoutIds.indexOf(timeoutId);
if (index !== -1) {
activeTimeoutIds.splice(index, 1);
}
cancelSetMockFocused();
if (!mobile.value && !popupElement.contains(document.activeElement)) {
selectorRef.value?.focus();
}
});
activeTimeoutIds.push(timeoutId);
}
props.onMousedown?.(event, ...restArgs);
};
// ============================= Dropdown ==============================
const containerWidth = ref<number>(null);
const instance = getCurrentInstance();
const onPopupMouseEnter = () => {
// We need force update here since popup dom is render async
instance.update();
};
onMounted(() => {
watch(
triggerOpen,
() => {
if (triggerOpen.value) {
const newWidth = Math.ceil(containerRef.value?.offsetWidth);
if (containerWidth.value !== newWidth && !Number.isNaN(newWidth)) {
containerWidth.value = newWidth;
}
}
},
{ immediate: true },
);
});
// Close when click on non-select element
useSelectTriggerControl([containerRef, triggerRef], triggerOpen, onToggleOpen);
useProvideBaseSelectProps(
toReactive({
...toRefs(props),
open: mergedOpen,
triggerOpen,
showSearch: mergedShowSearch,
multiple,
toggleOpen: onToggleOpen,
} as unknown as BaseSelectContextProps),
);
return () => {
const {
prefixCls,
id,
open,
defaultOpen,
mode,
// Search related
showSearch,
searchValue,
onSearch,
// Icons
allowClear,
clearIcon,
showArrow,
inputIcon,
// Others
disabled,
loading,
getInputElement,
getPopupContainer,
placement,
// Dropdown
animation,
transitionName,
dropdownStyle,
dropdownClassName,
dropdownMatchSelectWidth,
dropdownRender,
dropdownAlign,
showAction,
direction,
// Tags
tokenSeparators,
tagRender,
// Events
onPopupScroll,
onDropdownVisibleChange,
onFocus,
onBlur,
onKeyup,
onKeydown,
onMousedown,
onClear,
omitDomProps,
getRawInputElement,
displayValues,
onDisplayValuesChange,
emptyOptions,
activeDescendantId,
activeValue,
OptionList,
...restProps
} = { ...props, ...attrs } as BaseSelectProps;
// ============================= Input ==============================
// Only works in `combobox`
const customizeInputElement: any =
(mode === 'combobox' && getInputElement && getInputElement()) || null;
// Used for customize replacement for `vc-cascader`
const customizeRawInputElement: any =
typeof getRawInputElement === 'function' && getRawInputElement();
const domProps = {
...restProps,
} as Omit<keyof typeof restProps, typeof DEFAULT_OMIT_PROPS[number]>;
// Used for raw custom input trigger
let onTriggerVisibleChange: null | ((newOpen: boolean) => void);
if (customizeRawInputElement) {
onTriggerVisibleChange = (newOpen: boolean) => {
onToggleOpen(newOpen);
};
}
DEFAULT_OMIT_PROPS.forEach(propName => {
delete domProps[propName];
});
omitDomProps?.forEach(propName => {
delete domProps[propName];
});
// ============================= Arrow ==============================
const mergedShowArrow =
showArrow !== undefined ? showArrow : loading || (!multiple.value && mode !== 'combobox');
let arrowNode: VNode | JSX.Element;
if (mergedShowArrow) {
arrowNode = (
<TransBtn
class={classNames(`${prefixCls}-arrow`, {
[`${prefixCls}-arrow-loading`]: loading,
})}
customizeIcon={inputIcon}
customizeIconProps={{
loading,
searchValue: mergedSearchValue.value,
open: mergedOpen.value,
focused: mockFocused.value,
showSearch: mergedShowSearch.value,
}}
/>
);
}
// ============================= Clear ==============================
let clearNode: VNode | JSX.Element;
const onClearMouseDown: MouseEventHandler = () => {
onClear?.();
onDisplayValuesChange([], {
type: 'clear',
values: displayValues,
});
onInternalSearch('', false, false);
};
if (!disabled && allowClear && (displayValues.length || mergedSearchValue.value)) {
clearNode = (
<TransBtn
class={`${prefixCls}-clear`}
onMousedown={onClearMouseDown}
customizeIcon={clearIcon}
>
×
</TransBtn>
);
}
// =========================== OptionList ===========================
const optionList = (
<OptionList
ref={listRef}
v-slots={{ ...legacyTreeSelectContext.customSlots, option: slots.option }}
/>
);
// ============================= Select =============================
const mergedClassName = classNames(prefixCls, attrs.class, {
[`${prefixCls}-focused`]: mockFocused.value,
[`${prefixCls}-multiple`]: multiple.value,
[`${prefixCls}-single`]: !multiple.value,
[`${prefixCls}-allow-clear`]: allowClear,
[`${prefixCls}-show-arrow`]: mergedShowArrow,
[`${prefixCls}-disabled`]: disabled,
[`${prefixCls}-loading`]: loading,
[`${prefixCls}-open`]: mergedOpen.value,
[`${prefixCls}-customize-input`]: customizeInputElement,
[`${prefixCls}-show-search`]: mergedShowSearch.value,
});
// >>> Selector
const selectorNode = (
<SelectTrigger
ref={triggerRef}
disabled={disabled}
prefixCls={prefixCls}
visible={triggerOpen.value}
popupElement={optionList}
containerWidth={containerWidth.value}
animation={animation}
transitionName={transitionName}
dropdownStyle={dropdownStyle}
dropdownClassName={dropdownClassName}
direction={direction}
dropdownMatchSelectWidth={dropdownMatchSelectWidth}
dropdownRender={dropdownRender}
dropdownAlign={dropdownAlign}
placement={placement}
getPopupContainer={getPopupContainer}
empty={emptyOptions}
getTriggerDOMNode={() => selectorDomRef.current}
onPopupVisibleChange={onTriggerVisibleChange}
onPopupMouseEnter={onPopupMouseEnter}
v-slots={{
default: () => {
return customizeRawInputElement ? (
isValidElement(customizeRawInputElement) &&
cloneElement(
customizeRawInputElement,
{
ref: selectorDomRef,
},
false,
true,
)
) : (
<Selector
{...props}
domRef={selectorDomRef}
prefixCls={prefixCls}
inputElement={customizeInputElement}
ref={selectorRef}
id={id}
showSearch={mergedShowSearch.value}
mode={mode}
activeDescendantId={activeDescendantId}
tagRender={tagRender}
values={displayValues}
open={mergedOpen.value}
onToggleOpen={onToggleOpen}
activeValue={activeValue}
searchValue={mergedSearchValue.value}
onSearch={onInternalSearch}
onSearchSubmit={onInternalSearchSubmit}
onRemove={onSelectorRemove}
tokenWithEnter={tokenWithEnter.value}
/>
);
},
}}
></SelectTrigger>
);
// >>> Render
let renderNode: VNode | JSX.Element;
// Render raw
if (customizeRawInputElement) {
renderNode = selectorNode;
} else {
renderNode = (
<div
{...domProps}
class={mergedClassName}
ref={containerRef}
onMousedown={onInternalMouseDown}
onKeydown={onInternalKeyDown}
onKeyup={onInternalKeyUp}
// onFocus={onContainerFocus}
// onBlur={onContainerBlur}
>
{mockFocused.value && !mergedOpen.value && (
<span
style={{
width: 0,
height: 0,
display: 'flex',
overflow: 'hidden',
opacity: 0,
}}
aria-live="polite"
>
{/* Merge into one string to make screen reader work as expect */}
{`${displayValues
.map(({ label, value }) =>
['number', 'string'].includes(typeof label) ? label : value,
)
.join(', ')}`}
</span>
)}
{selectorNode}
{arrowNode}
{clearNode}
</div>
);
}
return renderNode;
};
},
});

View File

@ -1,8 +1,8 @@
import type { FunctionalComponent } from 'vue';
import type { OptionGroupData } from './interface';
import type { DefaultOptionType } from './Select';
export type OptGroupProps = Omit<OptionGroupData, 'options'>;
export type OptGroupProps = Omit<DefaultOptionType, 'options'>;
export interface OptionGroupFC extends FunctionalComponent<OptGroupProps> {
/** Legacy for check if is a Option Group */

View File

@ -1,8 +1,8 @@
import type { FunctionalComponent } from 'vue';
import type { OptionCoreData } from './interface';
import type { DefaultOptionType } from './Select';
export interface OptionProps extends Omit<OptionCoreData, 'label'> {
export interface OptionProps extends Omit<DefaultOptionType, 'label'> {
/** Save for customize data */
[prop: string]: any; // eslint-disable-line @typescript-eslint/no-explicit-any
}

View File

@ -1,22 +1,12 @@
import TransBtn from './TransBtn';
import PropTypes from '../_util/vue-types';
import KeyCode from '../_util/KeyCode';
import classNames from '../_util/classNames';
import pickAttrs from '../_util/pickAttrs';
import { isValidElement } from '../_util/props-util';
import createRef from '../_util/createRef';
import type { PropType } from 'vue';
import { computed, defineComponent, nextTick, reactive, watch } from 'vue';
import List from '../vc-virtual-list';
import type {
OptionsType as SelectOptionsType,
OptionData,
RenderNode,
OnActiveValue,
FieldNames,
} from './interface';
import type { RawValueType, FlattenOptionsType } from './interface/generator';
import { fillFieldNames } from './utils/valueUtil';
import useMemo from '../_util/hooks/useMemo';
import { isPlatformMac } from './utils/platformUtil';
@ -28,78 +18,28 @@ export interface RefOptionListProps {
import type { EventHandler } from '../_util/EventInterface';
import omit from '../_util/omit';
export interface OptionListProps<OptionType extends object> {
prefixCls: string;
id: string;
options: OptionType[];
fieldNames?: FieldNames;
flattenOptions: FlattenOptionsType<OptionType>;
height: number;
itemHeight: number;
values: Set<RawValueType>;
multiple: boolean;
open: boolean;
defaultActiveFirstOption?: boolean;
notFoundContent?: any;
menuItemSelectedIcon?: RenderNode;
childrenAsData: boolean;
searchValue: string;
virtual: boolean;
direction?: 'ltr' | 'rtl';
onSelect: (value: RawValueType, option: { selected: boolean }) => void;
onToggleOpen: (open?: boolean) => void;
/** Tell Select that some value is now active to make accessibility work */
onActiveValue: OnActiveValue;
onScroll: EventHandler;
/** Tell Select that mouse enter the popup to force re-render */
onMouseenter?: EventHandler;
}
const OptionListProps = {
prefixCls: PropTypes.string,
id: PropTypes.string,
options: PropTypes.array,
fieldNames: PropTypes.object,
flattenOptions: PropTypes.array,
height: PropTypes.number,
itemHeight: PropTypes.number,
values: PropTypes.any,
multiple: PropTypes.looseBool,
open: PropTypes.looseBool,
defaultActiveFirstOption: PropTypes.looseBool,
notFoundContent: PropTypes.any,
menuItemSelectedIcon: PropTypes.any,
childrenAsData: PropTypes.looseBool,
searchValue: PropTypes.string,
virtual: PropTypes.looseBool,
direction: PropTypes.string,
onSelect: PropTypes.func,
onToggleOpen: { type: Function as PropType<(open?: boolean) => void> },
/** Tell Select that some value is now active to make accessibility work */
onActiveValue: PropTypes.func,
onScroll: PropTypes.func,
/** Tell Select that mouse enter the popup to force re-render */
onMouseenter: PropTypes.func,
};
import useBaseProps from './hooks/useBaseProps';
import type { RawValueType } from './Select';
import useSelectProps from './SelectContext';
// export interface OptionListProps<OptionsType extends object[]> {
export type OptionListProps = Record<string, never>;
/**
* Using virtual list of option display.
* Will fallback to dom if use customize render.
*/
const OptionList = defineComponent<OptionListProps<SelectOptionsType[number]>, { state?: any }>({
const OptionList = defineComponent({
name: 'OptionList',
inheritAttrs: false,
slots: ['option'],
setup(props) {
const itemPrefixCls = computed(() => `${props.prefixCls}-item`);
setup(_, { expose, slots }) {
const baseProps = useBaseProps();
const props = useSelectProps();
const itemPrefixCls = computed(() => `${baseProps.prefixCls}-item`);
const memoFlattenOptions = useMemo(
() => props.flattenOptions,
[() => props.open, () => props.flattenOptions],
[() => baseProps.open, () => props.flattenOptions],
next => next[0],
);
@ -124,7 +64,7 @@ const OptionList = defineComponent<OptionListProps<SelectOptionsType[number]>, {
const current = (index + i * offset + len) % len;
const { group, data } = memoFlattenOptions.value[current];
if (!group && !(data as OptionData).disabled) {
if (!group && !data.disabled) {
return current;
}
}
@ -152,7 +92,7 @@ const OptionList = defineComponent<OptionListProps<SelectOptionsType[number]>, {
// Auto active first item when list length or searchValue changed
watch(
[() => memoFlattenOptions.value.length, () => props.searchValue],
[() => memoFlattenOptions.value.length, () => baseProps.searchValue],
() => {
setActive(props.defaultActiveFirstOption !== false ? getEnabledActiveIndex(0) : -1);
},
@ -161,10 +101,10 @@ const OptionList = defineComponent<OptionListProps<SelectOptionsType[number]>, {
// Auto scroll to item position in single mode
watch(
[() => props.open, () => props.searchValue],
[() => baseProps.open, () => baseProps.searchValue],
() => {
if (!props.multiple && props.open && props.values.size === 1) {
const value = Array.from(props.values)[0];
if (!baseProps.multiple && baseProps.open && props.rawValues.size === 1) {
const value = Array.from(props.rawValues)[0];
const index = memoFlattenOptions.value.findIndex(({ data }) => data.value === value);
if (index !== -1) {
setActive(index);
@ -174,7 +114,7 @@ const OptionList = defineComponent<OptionListProps<SelectOptionsType[number]>, {
}
}
// Force trigger scrollbar visible when open
if (props.open) {
if (baseProps.open) {
nextTick(() => {
listRef.current?.scrollTo(undefined);
});
@ -186,262 +126,253 @@ const OptionList = defineComponent<OptionListProps<SelectOptionsType[number]>, {
// ========================== Values ==========================
const onSelectValue = (value?: RawValueType) => {
if (value !== undefined) {
props.onSelect(value, { selected: !props.values.has(value) });
props.onSelect(value, { selected: !props.rawValues.has(value) });
}
// Single mode should always close by select
if (!props.multiple) {
props.onToggleOpen(false);
if (!baseProps.multiple) {
baseProps.toggleOpen(false);
}
};
const getLabel = (item: Record<string, any>) => item.label;
function renderItem(index: number) {
const item = memoFlattenOptions.value[index];
if (!item) return null;
const itemData = (item.data || {}) as OptionData;
const { value, label, children } = itemData;
const itemData = item.data || {};
const { value } = itemData;
const { group } = item;
const attrs = pickAttrs(itemData, true);
const mergedLabel = props.childrenAsData ? children : label;
const mergedLabel = getLabel(item);
return item ? (
<div
aria-label={typeof mergedLabel === 'string' ? mergedLabel : undefined}
aria-label={typeof mergedLabel === 'string' && !group ? mergedLabel : null}
{...attrs}
key={index}
role="option"
id={`${props.id}_list_${index}`}
aria-selected={props.values.has(value)}
role={group ? 'presentation' : 'option'}
id={`${baseProps.id}_list_${index}`}
aria-selected={props.rawValues.has(value)}
>
{value}
</div>
) : null;
}
return {
memoFlattenOptions,
renderItem,
listRef,
state,
onListMouseDown,
itemPrefixCls,
setActive,
onSelectValue,
onKeydown: (event: KeyboardEvent) => {
const { which, ctrlKey } = event;
switch (which) {
// >>> Arrow keys & ctrl + n/p on Mac
case KeyCode.N:
case KeyCode.P:
case KeyCode.UP:
case KeyCode.DOWN: {
let offset = 0;
if (which === KeyCode.UP) {
offset = -1;
} else if (which === KeyCode.DOWN) {
const onKeydown = (event: KeyboardEvent) => {
const { which, ctrlKey } = event;
switch (which) {
// >>> Arrow keys & ctrl + n/p on Mac
case KeyCode.N:
case KeyCode.P:
case KeyCode.UP:
case KeyCode.DOWN: {
let offset = 0;
if (which === KeyCode.UP) {
offset = -1;
} else if (which === KeyCode.DOWN) {
offset = 1;
} else if (isPlatformMac() && ctrlKey) {
if (which === KeyCode.N) {
offset = 1;
} else if (isPlatformMac() && ctrlKey) {
if (which === KeyCode.N) {
offset = 1;
} else if (which === KeyCode.P) {
offset = -1;
}
} else if (which === KeyCode.P) {
offset = -1;
}
if (offset !== 0) {
const nextActiveIndex = getEnabledActiveIndex(state.activeIndex + offset, offset);
scrollIntoView(nextActiveIndex);
setActive(nextActiveIndex, true);
}
break;
}
// >>> Select
case KeyCode.ENTER: {
// value
const item = memoFlattenOptions.value[state.activeIndex];
if (item && !item.data.disabled) {
onSelectValue(item.data.value);
} else {
onSelectValue(undefined);
}
if (props.open) {
event.preventDefault();
}
break;
if (offset !== 0) {
const nextActiveIndex = getEnabledActiveIndex(state.activeIndex + offset, offset);
scrollIntoView(nextActiveIndex);
setActive(nextActiveIndex, true);
}
// >>> Close
case KeyCode.ESC: {
props.onToggleOpen(false);
if (props.open) {
event.stopPropagation();
}
break;
}
// >>> Select
case KeyCode.ENTER: {
// value
const item = memoFlattenOptions.value[state.activeIndex];
if (item && !item.data.disabled) {
onSelectValue(item.data.value);
} else {
onSelectValue(undefined);
}
if (baseProps.open) {
event.preventDefault();
}
break;
}
// >>> Close
case KeyCode.ESC: {
baseProps.toggleOpen(false);
if (baseProps.open) {
event.stopPropagation();
}
}
},
onKeyup: () => {},
scrollTo: (index: number) => {
scrollIntoView(index);
},
}
};
},
render() {
const {
renderItem,
listRef,
onListMouseDown,
itemPrefixCls,
setActive,
onSelectValue,
memoFlattenOptions,
$slots,
} = this as any;
const {
id,
childrenAsData,
values,
height,
itemHeight,
menuItemSelectedIcon,
notFoundContent,
virtual,
fieldNames,
onScroll,
onMouseenter,
} = this.$props;
const renderOption = $slots.option;
const { activeIndex } = this.state;
const omitFieldNameList = Object.values(fillFieldNames(fieldNames));
// ========================== Render ==========================
if (memoFlattenOptions.length === 0) {
const onKeyup = () => {};
const scrollTo = (index: number) => {
scrollIntoView(index);
};
expose({
onKeydown,
onKeyup,
scrollTo,
});
return () => {
// const {
// renderItem,
// listRef,
// onListMouseDown,
// itemPrefixCls,
// setActive,
// onSelectValue,
// memoFlattenOptions,
// $slots,
// } = this as any;
const { id, notFoundContent, onPopupScroll } = baseProps;
const { menuItemSelectedIcon, rawValues, fieldNames, virtual, listHeight, listItemHeight } =
props;
const renderOption = slots.option;
const { activeIndex } = state;
const omitFieldNameList = Object.keys(fieldNames).map(key => fieldNames[key]);
// ========================== Render ==========================
if (memoFlattenOptions.value.length === 0) {
return (
<div
role="listbox"
id={`${id}_list`}
class={`${itemPrefixCls.value}-empty`}
onMousedown={onListMouseDown}
>
{notFoundContent}
</div>
);
}
return (
<div
role="listbox"
id={`${id}_list`}
class={`${itemPrefixCls}-empty`}
onMousedown={onListMouseDown}
>
{notFoundContent}
</div>
);
}
return (
<>
<div role="listbox" id={`${id}_list`} style={{ height: 0, width: 0, overflow: 'hidden' }}>
{renderItem(activeIndex - 1)}
{renderItem(activeIndex)}
{renderItem(activeIndex + 1)}
</div>
<List
itemKey="key"
ref={listRef}
data={memoFlattenOptions}
height={height}
itemHeight={itemHeight}
fullHeight={false}
onMousedown={onListMouseDown}
onScroll={onScroll}
virtual={virtual}
onMouseenter={onMouseenter}
v-slots={{
default: ({ group, groupOption, data, label, value }, itemIndex) => {
const { key } = data;
// Group
if (group) {
<>
<div role="listbox" id={`${id}_list`} style={{ height: 0, width: 0, overflow: 'hidden' }}>
{renderItem(activeIndex - 1)}
{renderItem(activeIndex)}
{renderItem(activeIndex + 1)}
</div>
<List
itemKey="key"
ref={listRef}
data={memoFlattenOptions.value}
height={listHeight}
itemHeight={listItemHeight}
fullHeight={false}
onMousedown={onListMouseDown}
onScroll={onPopupScroll}
virtual={virtual}
v-slots={{
default: (item, itemIndex) => {
const { group, groupOption, data, label, value } = item;
const { key } = data;
// Group
if (group) {
return (
<div class={classNames(itemPrefixCls.value, `${itemPrefixCls.value}-group`)}>
{renderOption ? renderOption(data) : label !== undefined ? label : key}
</div>
);
}
const {
disabled,
title,
children,
style,
class: cls,
className,
...otherProps
} = data;
const passedProps = omit(otherProps, omitFieldNameList);
// Option
const selected = rawValues.has(value);
const optionPrefixCls = `${itemPrefixCls.value}-option`;
const optionClassName = classNames(
itemPrefixCls.value,
optionPrefixCls,
cls,
className,
{
[`${optionPrefixCls}-grouped`]: groupOption,
[`${optionPrefixCls}-active`]: activeIndex === itemIndex && !disabled,
[`${optionPrefixCls}-disabled`]: disabled,
[`${optionPrefixCls}-selected`]: selected,
},
);
const mergedLabel = getLabel(item);
const iconVisible =
!menuItemSelectedIcon || typeof menuItemSelectedIcon === 'function' || selected;
const content = mergedLabel || value;
// https://github.com/ant-design/ant-design/issues/26717
let optionTitle =
typeof content === 'string' || typeof content === 'number'
? content.toString()
: undefined;
if (title !== undefined) {
optionTitle = title;
}
return (
<div class={classNames(itemPrefixCls, `${itemPrefixCls}-group`)}>
{renderOption ? renderOption(data) : label !== undefined ? label : key}
<div
{...passedProps}
aria-selected={selected}
class={optionClassName}
title={optionTitle}
onMousemove={e => {
if (otherProps.onMousemove) {
otherProps.onMousemove(e);
}
if (activeIndex === itemIndex || disabled) {
return;
}
setActive(itemIndex);
}}
onClick={e => {
if (!disabled) {
onSelectValue(value);
}
if (otherProps.onClick) {
otherProps.onClick(e);
}
}}
style={style}
>
<div class={`${optionPrefixCls}-content`}>
{renderOption ? renderOption(data) : content}
</div>
{isValidElement(menuItemSelectedIcon) || selected}
{iconVisible && (
<TransBtn
class={`${itemPrefixCls.value}-option-state`}
customizeIcon={menuItemSelectedIcon}
customizeIconProps={{ isSelected: selected }}
>
{selected ? '✓' : null}
</TransBtn>
)}
</div>
);
}
const {
disabled,
title,
children,
style,
class: cls,
className,
...otherProps
} = data;
const passedProps = omit(otherProps, omitFieldNameList);
// Option
const selected = values.has(value);
const optionPrefixCls = `${itemPrefixCls}-option`;
const optionClassName = classNames(itemPrefixCls, optionPrefixCls, cls, className, {
[`${optionPrefixCls}-grouped`]: groupOption,
[`${optionPrefixCls}-active`]: activeIndex === itemIndex && !disabled,
[`${optionPrefixCls}-disabled`]: disabled,
[`${optionPrefixCls}-selected`]: selected,
});
const mergedLabel = childrenAsData ? children : label;
const iconVisible =
!menuItemSelectedIcon || typeof menuItemSelectedIcon === 'function' || selected;
const content = mergedLabel || value;
// https://github.com/ant-design/ant-design/issues/26717
let optionTitle =
typeof content === 'string' || typeof content === 'number'
? content.toString()
: undefined;
if (title !== undefined) {
optionTitle = title;
}
return (
<div
{...passedProps}
aria-selected={selected}
class={optionClassName}
title={optionTitle}
onMousemove={e => {
if (otherProps.onMousemove) {
otherProps.onMousemove(e);
}
if (activeIndex === itemIndex || disabled) {
return;
}
setActive(itemIndex);
}}
onClick={e => {
if (!disabled) {
onSelectValue(value);
}
if (otherProps.onClick) {
otherProps.onClick(e);
}
}}
style={style}
>
<div class={`${optionPrefixCls}-content`}>
{renderOption ? renderOption(data) : content}
</div>
{isValidElement(menuItemSelectedIcon) || selected}
{iconVisible && (
<TransBtn
class={`${itemPrefixCls}-option-state`}
customizeIcon={menuItemSelectedIcon}
customizeIconProps={{ isSelected: selected }}
>
{selected ? '✓' : null}
</TransBtn>
)}
</div>
);
},
}}
></List>
</>
);
},
}}
></List>
</>
);
};
},
});
OptionList.props = OptionListProps;
export default OptionList;

View File

@ -1,6 +1,6 @@
/**
* To match accessibility requirement, we always provide an input in the component.
* Other element will not set `tabIndex` to avoid `onBlur` sequence problem.
* Other element will not set `tabindex` to avoid `onBlur` sequence problem.
* For focused select, we set `aria-live="polite"` to update the accessibility content.
*
* ref:
@ -29,76 +29,614 @@
* - `combobox` mode not support `optionLabelProp`
*/
import type { OptionsType as SelectOptionsType } from './interface';
import SelectOptionList from './OptionList';
import Option from './Option';
import OptGroup from './OptGroup';
import { convertChildrenToData as convertSelectChildrenToData } from './utils/legacyUtil';
import {
getLabeledValue as getSelectLabeledValue,
filterOptions as selectDefaultFilterOptions,
isValueDisabled as isSelectValueDisabled,
findValueOption as findSelectValueOption,
flattenOptions,
fillOptionsWithMissingValue,
} from './utils/valueUtil';
import type { SelectProps } from './generate';
import generateSelector, { selectBaseProps } from './generate';
import type { DefaultValueType } from './interface/generator';
import BaseSelect, { baseSelectPropsWithoutPrivate, isMultiple } from './BaseSelect';
import type { DisplayValueType, BaseSelectRef, BaseSelectProps } from './BaseSelect';
import OptionList from './OptionList';
import useOptions from './hooks/useOptions';
import type { SelectContextProps } from './SelectContext';
import { useProvideSelectProps } from './SelectContext';
import useId from './hooks/useId';
import { fillFieldNames, flattenOptions, injectPropsWithOption } from './utils/valueUtil';
import warningProps from './utils/warningPropsUtil';
import { defineComponent, ref } from 'vue';
import { toArray } from './utils/commonUtil';
import useFilterOptions from './hooks/useFilterOptions';
import useCache from './hooks/useCache';
import type { Key, VueNode } from '../_util/type';
import { computed, defineComponent, ref, toRef, watchEffect } from 'vue';
import type { ExtractPropTypes, PropType } from 'vue';
import PropTypes from '../_util/vue-types';
import { initDefaultProps } from '../_util/props-util';
import useMergedState from '../_util/hooks/useMergedState';
import useState from '../_util/hooks/useState';
import { toReactive } from '../_util/toReactive';
import omit from '../_util/omit';
const RefSelect = generateSelector<SelectOptionsType[number]>({
prefixCls: 'rc-select',
components: {
optionList: SelectOptionList as any,
},
convertChildrenToData: convertSelectChildrenToData,
flattenOptions,
getLabeledValue: getSelectLabeledValue,
filterOptions: selectDefaultFilterOptions,
isValueDisabled: isSelectValueDisabled,
findValueOption: findSelectValueOption,
warningProps,
fillOptionsWithMissingValue,
});
const OMIT_DOM_PROPS = ['inputValue'];
export type ExportedSelectProps<T extends DefaultValueType = DefaultValueType> = SelectProps<
SelectOptionsType[number],
T
>;
export type OnActiveValue = (
active: RawValueType,
index: number,
info?: { source?: 'keyboard' | 'mouse' },
) => void;
export function selectProps<T>() {
return selectBaseProps<SelectOptionsType[number], T>();
export type OnInternalSelect = (value: RawValueType, info: { selected: boolean }) => void;
export type RawValueType = string | number;
export interface LabelInValueType {
label: any;
value: RawValueType;
/** @deprecated `key` is useless since it should always same as `value` */
key?: Key;
}
const Select = defineComponent({
export type DraftValueType =
| RawValueType
| LabelInValueType
| DisplayValueType
| (RawValueType | LabelInValueType | DisplayValueType)[];
export type FilterFunc<OptionType> = (inputValue: string, option?: OptionType) => boolean;
export interface FieldNames {
value?: string;
label?: string;
options?: string;
}
export interface BaseOptionType {
disabled?: boolean;
[name: string]: any;
}
export interface DefaultOptionType extends BaseOptionType {
label?: any;
value?: string | number | null;
children?: Omit<DefaultOptionType, 'children'>[];
}
export type SelectHandler<ValueType = any, OptionType extends BaseOptionType = DefaultOptionType> =
| ((value: RawValueType | LabelInValueType, option: OptionType) => void)
| ((value: ValueType, option: OptionType) => void);
export function selectProps<
ValueType = any,
OptionType extends BaseOptionType = DefaultOptionType,
>() {
return {
...baseSelectPropsWithoutPrivate(),
prefixCls: String,
id: String,
backfill: { type: Boolean, default: undefined },
// >>> Field Names
fieldNames: Object as PropType<FieldNames>,
// >>> Search
/** @deprecated Use `searchValue` instead */
inputValue: String,
searchValue: String,
onSearch: Function as PropType<(value: string) => void>,
autoClearSearchValue: { type: Boolean, default: undefined },
// >>> Select
onSelect: Function as PropType<SelectHandler<ValueType, OptionType>>,
onDeselect: Function as PropType<SelectHandler<ValueType, OptionType>>,
// >>> Options
/**
* In Select, `false` means do nothing.
* In TreeSelect, `false` will highlight match item.
* It's by design.
*/
filterOption: {
type: [Boolean, Function] as PropType<boolean | FilterFunc<OptionType>>,
default: undefined,
},
filterSort: Function as PropType<(optionA: OptionType, optionB: OptionType) => number>,
optionFilterProp: String,
optionLabelProp: String,
options: Array as PropType<OptionType[]>,
defaultActiveFirstOption: { type: Boolean, default: undefined },
virtual: { type: Boolean, default: undefined },
listHeight: Number,
listItemHeight: Number,
// >>> Icon
menuItemSelectedIcon: PropTypes.any,
mode: String as PropType<'combobox' | 'multiple' | 'tags'>,
labelInValue: { type: Boolean, default: undefined },
value: PropTypes.any,
defaultValue: PropTypes.any,
onChange: Function as PropType<(value: ValueType, option: OptionType | OptionType[]) => void>,
children: Array as PropType<VueNode[]>,
};
}
export type SelectProps = Partial<ExtractPropTypes<ReturnType<typeof selectProps>>>;
function isRawValue(value: DraftValueType): value is RawValueType {
return !value || typeof value !== 'object';
}
export default defineComponent({
name: 'Select',
inheritAttrs: false,
Option,
OptGroup,
props: RefSelect.props,
setup(props, { attrs, expose, slots }) {
const selectRef = ref();
props: initDefaultProps(selectProps(), {
prefixCls: 'vc-select',
autoClearSearchValue: true,
listHeight: 200,
listItemHeight: 20,
}),
setup(props, { expose, attrs, slots }) {
const mergedId = useId(toRef(props, 'id'));
const multiple = computed(() => isMultiple(props.mode));
const childrenAsData = computed(() => !!(!props.options && props.children));
const mergedFilterOption = computed(() => {
if (props.filterOption === undefined && props.mode === 'combobox') {
return false;
}
return props.filterOption;
});
// ========================= FieldNames =========================
const mergedFieldNames = computed(() => fillFieldNames(props.fieldNames, childrenAsData.value));
// =========================== Search ===========================
const [mergedSearchValue, setSearchValue] = useMergedState('', {
value: computed(() =>
props.searchValue !== undefined ? props.searchValue : props.inputValue,
),
postState: search => search || '',
});
// =========================== Option ===========================
const parsedOptions = useOptions(
toRef(props, 'options'),
toRef(props, 'children'),
mergedFieldNames,
);
const { valueOptions, labelOptions, options: mergedOptions } = parsedOptions;
// ========================= Wrap Value =========================
const convert2LabelValues = (draftValues: DraftValueType) => {
// Convert to array
const valueList = toArray(draftValues);
// Convert to labelInValue type
return valueList.map(val => {
let rawValue: RawValueType;
let rawLabel: any;
let rawKey: Key;
let rawDisabled: boolean | undefined;
// Fill label & value
if (isRawValue(val)) {
rawValue = val;
} else {
rawKey = val.key;
rawLabel = val.label;
rawValue = val.value ?? rawKey;
}
const option = valueOptions.value.get(rawValue);
if (option) {
// Fill missing props
if (rawLabel === undefined)
rawLabel = option?.[props.optionLabelProp || mergedFieldNames.value.label];
if (rawKey === undefined) rawKey = option?.key ?? rawValue;
rawDisabled = option?.disabled;
// Warning if label not same as provided
// if (process.env.NODE_ENV !== 'production' && !isRawValue(val)) {
// const optionLabel = option?.[mergedFieldNames.value.label];
// if (optionLabel !== undefined && optionLabel !== rawLabel) {
// warning(false, '`label` of `value` is not same as `label` in Select options.');
// }
// }
}
return {
label: rawLabel,
value: rawValue,
key: rawKey,
disabled: rawDisabled,
option,
};
});
};
// =========================== Values ===========================
const [internalValue, setInternalValue] = useMergedState(props.defaultValue, {
value: toRef(props, 'value'),
});
// Merged value with LabelValueType
const rawLabeledValues = computed(() => {
const values = convert2LabelValues(internalValue.value);
// combobox no need save value when it's empty
if (props.mode === 'combobox' && !values[0]?.value) {
return [];
}
return values;
});
// Fill label with cache to avoid option remove
const [mergedValues, getMixedOption] = useCache(rawLabeledValues, valueOptions);
const displayValues = computed(() => {
// `null` need show as placeholder instead
// https://github.com/ant-design/ant-design/issues/25057
if (!props.mode && mergedValues.value.length === 1) {
const firstValue = mergedValues.value[0];
if (
firstValue.value === null &&
(firstValue.label === null || firstValue.label === undefined)
) {
return [];
}
}
return mergedValues.value.map(item => ({
...item,
label: item.label ?? item.value,
}));
});
/** Convert `displayValues` to raw value type set */
const rawValues = computed(() => new Set(mergedValues.value.map(val => val.value)));
watchEffect(
() => {
if (props.mode === 'combobox') {
const strValue = mergedValues.value[0]?.value;
if (strValue !== undefined && strValue !== null) {
setSearchValue(String(strValue));
}
}
},
{ flush: 'post' },
);
// ======================= Display Option =======================
// Create a placeholder item if not exist in `options`
const createTagOption = (val: RawValueType, label?: any) => {
const mergedLabel = label ?? val;
return {
[mergedFieldNames.value.value]: val,
[mergedFieldNames.value.label]: mergedLabel,
} as DefaultOptionType;
};
// Fill tag as option if mode is `tags`
const filledTagOptions = computed(() => {
if (props.mode !== 'tags') {
return mergedOptions.value;
}
// >>> Tag mode
const cloneOptions = [...mergedOptions.value];
// Check if value exist in options (include new patch item)
const existOptions = (val: RawValueType) => valueOptions.value.has(val);
// Fill current value as option
[...mergedValues.value]
.sort((a, b) => (a.value < b.value ? -1 : 1))
.forEach(item => {
const val = item.value;
if (!existOptions(val)) {
cloneOptions.push(createTagOption(val, item.label));
}
});
return cloneOptions;
});
const filteredOptions = useFilterOptions(
filledTagOptions,
mergedFieldNames,
mergedSearchValue,
mergedFilterOption,
toRef(props, 'optionFilterProp'),
);
// Fill options with search value if needed
const filledSearchOptions = computed(() => {
if (
props.mode !== 'tags' ||
!mergedSearchValue.value ||
filteredOptions.value.some(
item => item[props.optionFilterProp || 'value'] === mergedSearchValue.value,
)
) {
return filteredOptions.value;
}
// Fill search value as option
return [createTagOption(mergedSearchValue.value), ...filteredOptions.value];
});
const orderedFilteredOptions = computed(() => {
if (!props.filterSort) {
return filledSearchOptions.value;
}
return [...filledSearchOptions.value].sort((a, b) => props.filterSort(a, b));
});
const displayOptions = computed(() =>
flattenOptions(orderedFilteredOptions.value, {
fieldNames: mergedFieldNames.value,
childrenAsData: childrenAsData.value,
}),
);
// =========================== Change ===========================
const triggerChange = (values: DraftValueType) => {
const labeledValues = convert2LabelValues(values);
setInternalValue(labeledValues);
if (
props.onChange &&
// Trigger event only when value changed
(labeledValues.length !== mergedValues.value.length ||
labeledValues.some((newVal, index) => mergedValues.value[index]?.value !== newVal?.value))
) {
const returnValues = props.labelInValue ? labeledValues : labeledValues.map(v => v.value);
const returnOptions = labeledValues.map(v =>
injectPropsWithOption(getMixedOption(v.value)),
);
props.onChange(
// Value
multiple.value ? returnValues : returnValues[0],
// Option
multiple.value ? returnOptions : returnOptions[0],
);
}
};
// ======================= Accessibility ========================
const [activeValue, setActiveValue] = useState<string>(null);
const [accessibilityIndex, setAccessibilityIndex] = useState(0);
const mergedDefaultActiveFirstOption = computed(() =>
props.defaultActiveFirstOption !== undefined
? props.defaultActiveFirstOption
: props.mode !== 'combobox',
);
const onActiveValue: OnActiveValue = (active, index, { source = 'keyboard' } = {}) => {
setAccessibilityIndex(index);
if (props.backfill && props.mode === 'combobox' && active !== null && source === 'keyboard') {
setActiveValue(String(active));
}
};
// ========================= OptionList =========================
const triggerSelect = (val: RawValueType, selected: boolean) => {
const getSelectEnt = (): [RawValueType | LabelInValueType, DefaultOptionType] => {
const option = getMixedOption(val);
return [
props.labelInValue
? {
label: option?.[mergedFieldNames.value.label],
value: val,
key: option.key ?? val,
}
: val,
injectPropsWithOption(option),
];
};
if (selected && props.onSelect) {
const [wrappedValue, option] = getSelectEnt();
props.onSelect(wrappedValue, option);
} else if (!selected && props.onDeselect) {
const [wrappedValue, option] = getSelectEnt();
props.onDeselect(wrappedValue, option);
}
};
// Used for OptionList selection
const onInternalSelect = (val, info) => {
let cloneValues: (RawValueType | DisplayValueType)[];
// Single mode always trigger select only with option list
const mergedSelect = multiple.value ? info.selected : true;
if (mergedSelect) {
cloneValues = multiple.value ? [...mergedValues.value, val] : [val];
} else {
cloneValues = mergedValues.value.filter(v => v.value !== val);
}
triggerChange(cloneValues);
triggerSelect(val, mergedSelect);
// Clean search value if single or configured
if (props.mode === 'combobox') {
// setSearchValue(String(val));
setActiveValue('');
} else if (!multiple.value || props.autoClearSearchValue) {
setSearchValue('');
setActiveValue('');
}
};
// ======================= Display Change =======================
// BaseSelect display values change
const onDisplayValuesChange: BaseSelectProps['onDisplayValuesChange'] = (nextValues, info) => {
triggerChange(nextValues);
if (info.type === 'remove' || info.type === 'clear') {
info.values.forEach(item => {
triggerSelect(item.value, false);
});
}
};
// =========================== Search ===========================
const onInternalSearch: BaseSelectProps['onSearch'] = (searchText, info) => {
setSearchValue(searchText);
setActiveValue(null);
// [Submit] Tag mode should flush input
if (info.source === 'submit') {
const formatted = (searchText || '').trim();
// prevent empty tags from appearing when you click the Enter button
if (formatted) {
const newRawValues = Array.from(new Set<RawValueType>([...rawValues.value, formatted]));
triggerChange(newRawValues);
triggerSelect(formatted, true);
setSearchValue('');
}
return;
}
if (info.source !== 'blur') {
if (props.mode === 'combobox') {
triggerChange(searchText);
}
props.onSearch?.(searchText);
}
};
const onInternalSearchSplit: BaseSelectProps['onSearchSplit'] = words => {
let patchValues: RawValueType[] = words;
if (props.mode !== 'tags') {
patchValues = words
.map(word => {
const opt = labelOptions.value.get(word);
return opt?.value;
})
.filter(val => val !== undefined);
}
const newRawValues = Array.from(new Set<RawValueType>([...rawValues.value, ...patchValues]));
triggerChange(newRawValues);
newRawValues.forEach(newRawValue => {
triggerSelect(newRawValue, true);
});
};
const realVirtual = computed(
() => props.virtual !== false && props.dropdownMatchSelectWidth !== false,
);
useProvideSelectProps(
toReactive({
...parsedOptions,
flattenOptions: displayOptions,
onActiveValue,
defaultActiveFirstOption: mergedDefaultActiveFirstOption,
onSelect: onInternalSelect,
menuItemSelectedIcon: toRef(props, 'menuItemSelectedIcon'),
rawValues,
fieldNames: mergedFieldNames,
virtual: realVirtual,
listHeight: toRef(props, 'listHeight'),
listItemHeight: toRef(props, 'listItemHeight'),
childrenAsData,
} as unknown as SelectContextProps),
);
// ========================== Warning ===========================
if (process.env.NODE_ENV !== 'production') {
watchEffect(
() => {
warningProps(props);
},
{ flush: 'post' },
);
}
const selectRef = ref<BaseSelectRef>();
expose({
focus: () => {
focus() {
selectRef.value?.focus();
},
blur: () => {
blur() {
selectRef.value?.blur();
},
scrollTo(arg) {
selectRef.value?.scrollTo(arg);
},
} as BaseSelectRef);
const pickProps = computed(() => {
return omit(props, [
'id',
'mode',
'prefixCls',
'backfill',
'fieldNames',
// Search
'inputValue',
'searchValue',
'onSearch',
'autoClearSearchValue',
// Select
'onSelect',
'onDeselect',
'dropdownMatchSelectWidth',
// Options
'filterOption',
'filterSort',
'optionFilterProp',
'optionLabelProp',
'options',
'children',
'defaultActiveFirstOption',
'menuItemSelectedIcon',
'virtual',
'listHeight',
'listItemHeight',
// Value
'value',
'defaultValue',
'labelInValue',
'onChange',
]);
});
return () => {
return (
<RefSelect
ref={selectRef}
{...(props as any)}
<BaseSelect
{...pickProps.value}
{...attrs}
// >>> MISC
id={mergedId}
prefixCls={props.prefixCls}
ref={selectRef}
omitDomProps={OMIT_DOM_PROPS}
mode={props.mode}
// >>> Values
displayValues={displayValues.value}
onDisplayValuesChange={onDisplayValuesChange}
// >>> Search
searchValue={mergedSearchValue.value}
onSearch={onInternalSearch}
onSearchSplit={onInternalSearchSplit}
dropdownMatchSelectWidth={props.dropdownMatchSelectWidth}
// >>> OptionList
OptionList={OptionList}
emptyOptions={!displayOptions.value.length}
// >>> Accessibility
activeValue={activeValue.value}
activeDescendantId={`${mergedId}_list_${accessibilityIndex.value}`}
v-slots={slots}
children={slots.default?.() || []}
/>
);
};
},
});
export default Select;

View File

@ -0,0 +1,36 @@
/**
* BaseSelect provide some parsed data into context.
* You can use this hooks to get them.
*/
import type { InjectionKey } from 'vue';
import { inject, provide } from 'vue';
import type { RawValueType, RenderNode } from './BaseSelect';
import type { FlattenOptionData } from './interface';
import type { BaseOptionType, FieldNames, OnActiveValue, OnInternalSelect } from './Select';
// Use any here since we do not get the type during compilation
export interface SelectContextProps {
options: BaseOptionType[];
flattenOptions: FlattenOptionData<BaseOptionType>[];
onActiveValue: OnActiveValue;
defaultActiveFirstOption?: boolean;
onSelect: OnInternalSelect;
menuItemSelectedIcon?: RenderNode;
rawValues: Set<RawValueType>;
fieldNames?: FieldNames;
virtual?: boolean;
listHeight?: number;
listItemHeight?: number;
childrenAsData?: boolean;
}
const SelectContextKey: InjectionKey<SelectContextProps> = Symbol('SelectContextKey');
export function useProvideSelectProps(props: SelectContextProps) {
return provide(SelectContextKey, props);
}
export default function useSelectProps() {
return inject(SelectContextKey, {} as SelectContextProps);
}

View File

@ -1,19 +1,12 @@
import Trigger from '../vc-trigger';
import PropTypes from '../_util/vue-types';
import { getSlot } from '../_util/props-util';
import classNames from '../_util/classNames';
import createRef from '../_util/createRef';
import type { CSSProperties } from 'vue';
import { defineComponent } from 'vue';
import type { RenderDOMFunc } from './interface';
import type { DropdownRender } from './interface/generator';
import type { Placement } from './generate';
import { computed, ref, defineComponent } from 'vue';
import type { VueNode } from '../_util/type';
import type { DropdownRender, Placement, RenderDOMFunc } from './BaseSelect';
const getBuiltInPlacements = (dropdownMatchSelectWidth: number | boolean) => {
// Enable horizontal overflow auto-adjustment when a custom dropdown width is provided
const adjustX = typeof dropdownMatchSelectWidth !== 'number' ? 0 : 1;
const getBuiltInPlacements = (adjustX: number) => {
return {
bottomLeft: {
points: ['tl', 'bl'],
@ -49,6 +42,19 @@ const getBuiltInPlacements = (dropdownMatchSelectWidth: number | boolean) => {
},
};
};
const getAdjustX = (
adjustXDependencies: Pick<SelectTriggerProps, 'autoAdjustOverflow' | 'dropdownMatchSelectWidth'>,
) => {
const { autoAdjustOverflow, dropdownMatchSelectWidth } = adjustXDependencies;
if (!!autoAdjustOverflow) return 1;
// Enable horizontal overflow auto-adjustment when a custom dropdown width is provided
return typeof dropdownMatchSelectWidth !== 'number' ? 0 : 1;
};
export interface RefTriggerProps {
getPopupElement: () => HTMLDivElement;
}
export interface SelectTriggerProps {
prefixCls: string;
disabled: boolean;
@ -66,103 +72,122 @@ export interface SelectTriggerProps {
getPopupContainer?: RenderDOMFunc;
dropdownAlign: object;
empty: boolean;
autoAdjustOverflow?: boolean;
getTriggerDOMNode: () => any;
onPopupVisibleChange?: (visible: boolean) => void;
onPopupMouseEnter: () => void;
}
const SelectTrigger = defineComponent<SelectTriggerProps, { popupRef: any }>({
name: 'SelectTrigger',
inheritAttrs: false,
created() {
this.popupRef = createRef();
},
props: {
dropdownAlign: PropTypes.object,
visible: PropTypes.looseBool,
disabled: PropTypes.looseBool,
dropdownClassName: PropTypes.string,
dropdownStyle: PropTypes.object,
placement: PropTypes.string,
empty: PropTypes.looseBool,
autoAdjustOverflow: PropTypes.looseBool,
prefixCls: PropTypes.string,
popupClassName: PropTypes.string,
animation: PropTypes.string,
transitionName: PropTypes.string,
getPopupContainer: PropTypes.func,
dropdownRender: PropTypes.func,
containerWidth: PropTypes.number,
dropdownMatchSelectWidth: PropTypes.oneOfType([Number, Boolean]).def(true),
popupElement: PropTypes.any,
direction: PropTypes.string,
getTriggerDOMNode: PropTypes.func,
onPopupVisibleChange: PropTypes.func,
onPopupMouseEnter: PropTypes.func,
} as any,
setup(props, { slots, attrs, expose }) {
const builtInPlacements = computed(() => {
const { autoAdjustOverflow, dropdownMatchSelectWidth } = props;
return getBuiltInPlacements(
getAdjustX({
autoAdjustOverflow,
dropdownMatchSelectWidth,
}),
);
});
const popupRef = ref();
expose({
getPopupElement: () => {
return popupRef.value;
},
});
return () => {
const { empty = false, ...restProps } = { ...props, ...attrs };
const {
visible,
dropdownAlign,
prefixCls,
popupElement,
dropdownClassName,
dropdownStyle,
direction = 'ltr',
placement,
dropdownMatchSelectWidth,
containerWidth,
dropdownRender,
animation,
transitionName,
getPopupContainer,
getTriggerDOMNode,
onPopupVisibleChange,
onPopupMouseEnter,
} = restProps as SelectTriggerProps;
const dropdownPrefixCls = `${prefixCls}-dropdown`;
methods: {
getPopupElement() {
return this.popupRef.current;
},
},
let popupNode = popupElement;
if (dropdownRender) {
popupNode = dropdownRender({ menuNode: popupElement, props });
}
render() {
const { empty = false, ...props } = { ...this.$props, ...this.$attrs };
const {
visible,
dropdownAlign,
prefixCls,
popupElement,
dropdownClassName,
dropdownStyle,
direction = 'ltr',
placement,
dropdownMatchSelectWidth,
containerWidth,
dropdownRender,
animation,
transitionName,
getPopupContainer,
getTriggerDOMNode,
} = props as SelectTriggerProps;
const dropdownPrefixCls = `${prefixCls}-dropdown`;
const mergedTransitionName = animation ? `${dropdownPrefixCls}-${animation}` : transitionName;
let popupNode = popupElement;
if (dropdownRender) {
popupNode = dropdownRender({ menuNode: popupElement, props });
}
const popupStyle = { minWidth: `${containerWidth}px`, ...dropdownStyle };
const builtInPlacements = getBuiltInPlacements(dropdownMatchSelectWidth);
const mergedTransitionName = animation ? `${dropdownPrefixCls}-${animation}` : transitionName;
const popupStyle = { minWidth: `${containerWidth}px`, ...dropdownStyle };
if (typeof dropdownMatchSelectWidth === 'number') {
popupStyle.width = `${dropdownMatchSelectWidth}px`;
} else if (dropdownMatchSelectWidth) {
popupStyle.width = `${containerWidth}px`;
}
return (
<Trigger
{...props}
showAction={[]}
hideAction={[]}
popupPlacement={placement || (direction === 'rtl' ? 'bottomRight' : 'bottomLeft')}
builtinPlacements={builtInPlacements}
prefixCls={dropdownPrefixCls}
popupTransitionName={mergedTransitionName}
popup={<div ref={this.popupRef}>{popupNode}</div>}
popupAlign={dropdownAlign}
popupVisible={visible}
getPopupContainer={getPopupContainer}
popupClassName={classNames(dropdownClassName, {
[`${dropdownPrefixCls}-empty`]: empty,
})}
popupStyle={popupStyle}
getTriggerDOMNode={getTriggerDOMNode}
>
{getSlot(this)[0]}
</Trigger>
);
if (typeof dropdownMatchSelectWidth === 'number') {
popupStyle.width = `${dropdownMatchSelectWidth}px`;
} else if (dropdownMatchSelectWidth) {
popupStyle.width = `${containerWidth}px`;
}
return (
<Trigger
{...props}
showAction={onPopupVisibleChange ? ['click'] : []}
hideAction={onPopupVisibleChange ? ['click'] : []}
popupPlacement={placement || (direction === 'rtl' ? 'bottomRight' : 'bottomLeft')}
builtinPlacements={builtInPlacements.value}
prefixCls={dropdownPrefixCls}
popupTransitionName={mergedTransitionName}
popupAlign={dropdownAlign}
popupVisible={visible}
getPopupContainer={getPopupContainer}
popupClassName={classNames(dropdownClassName, {
[`${dropdownPrefixCls}-empty`]: empty,
})}
popupStyle={popupStyle}
getTriggerDOMNode={getTriggerDOMNode}
onPopupVisibleChange={onPopupVisibleChange}
v-slots={{
default: slots.default,
popup: () => (
<div ref={popupRef} onMouseenter={onPopupMouseEnter}>
{popupNode}
</div>
),
}}
></Trigger>
);
};
},
});
SelectTrigger.props = {
dropdownAlign: PropTypes.object,
visible: PropTypes.looseBool,
disabled: PropTypes.looseBool,
dropdownClassName: PropTypes.string,
dropdownStyle: PropTypes.object,
placement: PropTypes.string,
empty: PropTypes.looseBool,
prefixCls: PropTypes.string,
popupClassName: PropTypes.string,
animation: PropTypes.string,
transitionName: PropTypes.string,
getPopupContainer: PropTypes.func,
dropdownRender: PropTypes.func,
containerWidth: PropTypes.number,
dropdownMatchSelectWidth: PropTypes.oneOfType([Number, Boolean]).def(true),
popupElement: PropTypes.any,
direction: PropTypes.string,
getTriggerDOMNode: PropTypes.func,
};
export default SelectTrigger;

View File

@ -16,7 +16,7 @@ interface InputProps {
autofocus: boolean;
autocomplete: string;
editable: boolean;
accessibilityIndex: number;
activeDescendantId?: string;
value: string;
open: boolean;
tabindex: number | string;
@ -45,7 +45,7 @@ const Input = defineComponent({
autofocus: PropTypes.looseBool,
autocomplete: PropTypes.string,
editable: PropTypes.looseBool,
accessibilityIndex: PropTypes.number,
activeDescendantId: PropTypes.string,
value: PropTypes.string,
open: PropTypes.looseBool,
tabindex: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
@ -86,7 +86,7 @@ const Input = defineComponent({
autofocus,
autocomplete,
editable,
accessibilityIndex,
activeDescendantId,
value,
onKeydown,
onMousedown,
@ -131,7 +131,7 @@ const Input = defineComponent({
'aria-owns': `${id}_list`,
'aria-autocomplete': 'list',
'aria-controls': `${id}_list`,
'aria-activedescendant': `${id}_list_${accessibilityIndex}`,
'aria-activedescendant': activeDescendantId,
...attrs,
value: editable ? value : '',
readonly: !editable,
@ -178,7 +178,7 @@ const Input = defineComponent({
onOriginBlur && onOriginBlur(args[0]);
onBlur && onBlur(args[0]);
this.VCSelectContainerEvent?.blur(args[0]);
}, 200);
}, 100);
},
},
inputNode.type === 'textarea' ? {} : { type: 'search' },

View File

@ -1,12 +1,4 @@
import TransBtn from '../TransBtn';
import type {
LabelValueType,
RawValueType,
CustomTagProps,
DefaultValueType,
DisplayLabelValueType,
} from '../interface/generator';
import type { RenderNode } from '../interface';
import type { InnerSelectorProps } from './interface';
import Input from './Input';
import type { Ref, PropType } from 'vue';
@ -16,6 +8,9 @@ import pickAttrs from '../../_util/pickAttrs';
import PropTypes from '../../_util/vue-types';
import type { VueNode } from '../../_util/type';
import Overflow from '../../vc-overflow';
import type { DisplayValueType, RenderNode, CustomTagProps, RawValueType } from '../BaseSelect';
import type { BaseOptionType } from '../Select';
import useInjectLegacySelectContext from '../../vc-tree-select/LegacyContext';
type SelectorProps = InnerSelectorProps & {
// Icon
@ -24,7 +19,7 @@ type SelectorProps = InnerSelectorProps & {
// Tags
maxTagCount?: number | 'responsive';
maxTagTextLength?: number;
maxTagPlaceholder?: VueNode | ((omittedValues: LabelValueType[]) => VueNode);
maxTagPlaceholder?: VueNode | ((omittedValues: DisplayValueType[]) => VueNode);
tokenSeparators?: string[];
tagRender?: (props: CustomTagProps) => VueNode;
onToggleOpen: any;
@ -33,7 +28,7 @@ type SelectorProps = InnerSelectorProps & {
choiceTransitionName?: string;
// Event
onSelect: (value: RawValueType, option: { selected: boolean }) => void;
onRemove: (value: DisplayValueType) => void;
};
const props = {
@ -49,7 +44,7 @@ const props = {
showSearch: PropTypes.looseBool,
autofocus: PropTypes.looseBool,
autocomplete: PropTypes.string,
accessibilityIndex: PropTypes.number,
activeDescendantId: PropTypes.string,
tabindex: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
removeIcon: PropTypes.any,
@ -58,12 +53,12 @@ const props = {
maxTagCount: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
maxTagTextLength: PropTypes.number,
maxTagPlaceholder: PropTypes.any.def(
() => (omittedValues: LabelValueType[]) => `+ ${omittedValues.length} ...`,
() => (omittedValues: DisplayValueType[]) => `+ ${omittedValues.length} ...`,
),
tagRender: PropTypes.func,
onToggleOpen: { type: Function as PropType<(open?: boolean) => void> },
onSelect: PropTypes.func,
onRemove: PropTypes.func,
onInputChange: PropTypes.func,
onInputPaste: PropTypes.func,
onInputKeyDown: PropTypes.func,
@ -85,7 +80,7 @@ const SelectSelector = defineComponent<SelectorProps>({
const measureRef = ref();
const inputWidth = ref(0);
const focused = ref(false);
const legacyTreeSelectContext = useInjectLegacySelectContext();
const selectionPrefixCls = computed(() => `${props.prefixCls}-selection`);
// ===================== Search ======================
@ -111,6 +106,7 @@ const SelectSelector = defineComponent<SelectorProps>({
// ===================== Render ======================
// >>> Render Selector Node. Includes Item & Rest
function defaultRenderSelector(
title: VueNode,
content: VueNode,
itemDisabled: boolean,
closable?: boolean,
@ -122,9 +118,7 @@ const SelectSelector = defineComponent<SelectorProps>({
[`${selectionPrefixCls.value}-item-disabled`]: itemDisabled,
})}
title={
typeof content === 'string' || typeof content === 'number'
? content.toString()
: undefined
typeof title === 'string' || typeof title === 'number' ? title.toString() : undefined
}
>
<span class={`${selectionPrefixCls.value}-item-content`}>{content}</span>
@ -143,31 +137,38 @@ const SelectSelector = defineComponent<SelectorProps>({
}
function customizeRenderSelector(
value: DefaultValueType,
value: RawValueType,
content: VueNode,
itemDisabled: boolean,
closable: boolean,
onClose: (e: MouseEvent) => void,
option: BaseOptionType,
) {
const onMouseDown = (e: MouseEvent) => {
onPreventMouseDown(e);
props.onToggleOpen(!open);
};
let originData = option;
// For TreeSelect
if (legacyTreeSelectContext.keyEntities) {
originData = legacyTreeSelectContext.keyEntities[value]?.node || {};
}
return (
<span onMousedown={onMouseDown}>
<span key={value} onMousedown={onMouseDown}>
{props.tagRender({
label: content,
value,
disabled: itemDisabled,
closable,
onClose,
option: originData,
})}
</span>
);
}
function renderItem({ disabled: itemDisabled, label, value }: DisplayLabelValueType) {
function renderItem(valueItem: DisplayValueType) {
const { disabled: itemDisabled, label, value, option } = valueItem;
const closable = !props.disabled && !itemDisabled;
let displayLabel = label;
@ -183,24 +184,22 @@ const SelectSelector = defineComponent<SelectorProps>({
}
const onClose = (event?: MouseEvent) => {
if (event) event.stopPropagation();
props.onSelect(value, { selected: false });
props.onRemove?.(valueItem);
};
return typeof props.tagRender === 'function'
? customizeRenderSelector(value, displayLabel, itemDisabled, closable, onClose)
: defaultRenderSelector(displayLabel, itemDisabled, closable, onClose);
? customizeRenderSelector(value, displayLabel, itemDisabled, closable, onClose, option)
: defaultRenderSelector(label, displayLabel, itemDisabled, closable, onClose);
}
function renderRest(omittedValues: DisplayLabelValueType[]) {
const {
maxTagPlaceholder = (omittedValues: LabelValueType[]) => `+ ${omittedValues.length} ...`,
} = props;
function renderRest(omittedValues: DisplayValueType[]) {
const { maxTagPlaceholder = omittedValues => `+ ${omittedValues.length} ...` } = props;
const content =
typeof maxTagPlaceholder === 'function'
? maxTagPlaceholder(omittedValues)
: maxTagPlaceholder;
return defaultRenderSelector(content, false);
return defaultRenderSelector(content, content, false);
}
return () => {
@ -214,7 +213,7 @@ const SelectSelector = defineComponent<SelectorProps>({
disabled,
autofocus,
autocomplete,
accessibilityIndex,
activeDescendantId,
tabindex,
onInputChange,
onInputPaste,
@ -241,7 +240,7 @@ const SelectSelector = defineComponent<SelectorProps>({
autofocus={autofocus}
autocomplete={autocomplete}
editable={inputEditable.value}
accessibilityIndex={accessibilityIndex}
activeDescendantId={activeDescendantId}
value={inputValue.value}
onKeydown={onInputKeyDown}
onMousedown={onInputMouseDown}

View File

@ -3,13 +3,12 @@ import Input from './Input';
import type { InnerSelectorProps } from './interface';
import { Fragment, computed, defineComponent, ref, watch } from 'vue';
import PropTypes from '../../_util/vue-types';
import { useInjectTreeSelectContext } from '../../vc-tree-select/Context';
import type { VueNode } from '../../_util/type';
import useInjectLegacySelectContext from '../../vc-tree-select/LegacyContext';
interface SelectorProps extends InnerSelectorProps {
inputElement: VueNode;
activeValue: string;
backfill?: boolean;
}
const props = {
inputElement: PropTypes.any,
@ -25,7 +24,7 @@ const props = {
showSearch: PropTypes.looseBool,
autofocus: PropTypes.looseBool,
autocomplete: PropTypes.string,
accessibilityIndex: PropTypes.number,
activeDescendantId: PropTypes.string,
tabindex: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
activeValue: PropTypes.string,
backfill: PropTypes.looseBool,
@ -51,7 +50,7 @@ const SingleSelector = defineComponent<SelectorProps>({
}
return inputValue;
});
const treeSelectContext = useInjectTreeSelectContext();
const legacyTreeSelectContext = useInjectLegacySelectContext();
watch(
[combobox, () => props.activeValue],
() => {
@ -64,7 +63,7 @@ const SingleSelector = defineComponent<SelectorProps>({
// Not show text when closed expect combobox mode
const hasTextInput = computed(() =>
props.mode !== 'combobox' && !props.open ? false : !!inputValue.value,
props.mode !== 'combobox' && !props.open && !props.showSearch ? false : !!inputValue.value,
);
const title = computed(() => {
@ -74,6 +73,18 @@ const SingleSelector = defineComponent<SelectorProps>({
: undefined;
});
const renderPlaceholder = () => {
if (props.values[0]) {
return null;
}
const hiddenStyle = hasTextInput.value ? { visibility: 'hidden' as const } : undefined;
return (
<span class={`${props.prefixCls}-selection-placeholder`} style={hiddenStyle}>
{props.placeholder}
</span>
);
};
return () => {
const {
inputElement,
@ -84,9 +95,8 @@ const SingleSelector = defineComponent<SelectorProps>({
disabled,
autofocus,
autocomplete,
accessibilityIndex,
activeDescendantId,
open,
placeholder,
tabindex,
onInputKeyDown,
onInputMouseDown,
@ -98,13 +108,17 @@ const SingleSelector = defineComponent<SelectorProps>({
const item = values[0];
let titleNode = null;
// custom tree-select title by slot
if (item && treeSelectContext.value.slots) {
// For TreeSelect
if (item && legacyTreeSelectContext.customSlots) {
const key = item.key ?? item.value;
const originData = legacyTreeSelectContext.keyEntities[key]?.node || {};
titleNode =
treeSelectContext.value.slots[item?.option?.data?.slots?.title] ||
treeSelectContext.value.slots.title ||
legacyTreeSelectContext.customSlots[originData.slots?.title] ||
legacyTreeSelectContext.customSlots.title ||
item.label;
if (typeof titleNode === 'function') {
titleNode = titleNode(item.option?.data || {});
titleNode = titleNode(originData);
}
// else if (treeSelectContext.value.slots.titleRender) {
// // title titleRender title titleRender
@ -126,7 +140,7 @@ const SingleSelector = defineComponent<SelectorProps>({
autofocus={autofocus}
autocomplete={autocomplete}
editable={inputEditable.value}
accessibilityIndex={accessibilityIndex}
activeDescendantId={activeDescendantId}
value={inputValue.value}
onKeydown={onInputKeyDown}
onMousedown={onInputMouseDown}
@ -145,14 +159,12 @@ const SingleSelector = defineComponent<SelectorProps>({
{/* Display value */}
{!combobox.value && item && !hasTextInput.value && (
<span class={`${prefixCls}-selection-item`} title={title.value}>
<Fragment key={item.key || item.value}>{titleNode}</Fragment>
<Fragment key={item.key ?? item.value}>{titleNode}</Fragment>
</span>
)}
{/* Display placeholder */}
{!item && !hasTextInput.value && (
<span class={`${prefixCls}-selection-placeholder`}>{placeholder}</span>
)}
{renderPlaceholder()}
</>
);
};

View File

@ -11,8 +11,8 @@
import KeyCode from '../../_util/KeyCode';
import MultipleSelector from './MultipleSelector';
import SingleSelector from './SingleSelector';
import type { LabelValueType, RawValueType, CustomTagProps } from '../interface/generator';
import type { RenderNode, Mode } from '../interface';
import type { CustomTagProps, DisplayValueType, Mode, RenderNode } from '../BaseSelect';
import { isValidateOpenKey } from '../utils/keyUtil';
import useLock from '../hooks/useLock';
import type { PropType } from 'vue';
import { defineComponent } from 'vue';
@ -20,22 +20,22 @@ import createRef from '../../_util/createRef';
import PropTypes from '../../_util/vue-types';
import type { VueNode } from '../../_util/type';
import type { EventHandler } from '../../_util/EventInterface';
import type { ScrollTo } from '../../vc-virtual-list/List';
export interface SelectorProps {
id: string;
prefixCls: string;
showSearch?: boolean;
open: boolean;
/** Display in the Selector value, it's not same as `value` prop */
values: LabelValueType[];
multiple: boolean;
values: DisplayValueType[];
multiple?: boolean;
mode: Mode;
searchValue: string;
activeValue: string;
inputElement: VueNode;
autofocus?: boolean;
accessibilityIndex: number;
activeDescendantId?: string;
tabindex?: number | string;
disabled?: boolean;
placeholder?: VueNode;
@ -44,7 +44,7 @@ export interface SelectorProps {
// Tags
maxTagCount?: number | 'responsive';
maxTagTextLength?: number;
maxTagPlaceholder?: VueNode | ((omittedValues: LabelValueType[]) => VueNode);
maxTagPlaceholder?: VueNode | ((omittedValues: DisplayValueType[]) => VueNode);
tagRender?: (props: CustomTagProps) => VueNode;
/** Check if `tokenSeparators` contains `\n` or `\r\n` */
@ -57,7 +57,7 @@ export interface SelectorProps {
/** `onSearch` returns go next step boolean to check if need do toggle open */
onSearch: (searchText: string, fromTyping: boolean, isCompositing: boolean) => boolean;
onSearchSubmit: (searchText: string) => void;
onSelect: (value: RawValueType, option: { selected: boolean }) => void;
onRemove: (value: DisplayValueType) => void;
onInputKeyDown?: (e: KeyboardEvent) => void;
/**
@ -66,6 +66,11 @@ export interface SelectorProps {
*/
domRef: () => HTMLDivElement;
}
export interface RefSelectorProps {
focus: () => void;
blur: () => void;
scrollTo?: ScrollTo;
}
const Selector = defineComponent<SelectorProps>({
name: 'Selector',
@ -84,7 +89,7 @@ const Selector = defineComponent<SelectorProps>({
inputElement: PropTypes.any,
autofocus: PropTypes.looseBool,
accessibilityIndex: PropTypes.number,
activeDescendantId: PropTypes.string,
tabindex: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
disabled: PropTypes.looseBool,
placeholder: PropTypes.any,
@ -106,7 +111,7 @@ const Selector = defineComponent<SelectorProps>({
/** `onSearch` returns go next step boolean to check if need do toggle open */
onSearch: PropTypes.func,
onSearchSubmit: PropTypes.func,
onSelect: PropTypes.func,
onRemove: PropTypes.func,
onInputKeyDown: { type: Function as PropType<EventHandler> },
/**
@ -115,7 +120,7 @@ const Selector = defineComponent<SelectorProps>({
*/
domRef: PropTypes.func,
} as any,
setup(props) {
setup(props, { expose }) {
const inputRef = createRef();
let compositionStatus = false;
@ -139,7 +144,7 @@ const Selector = defineComponent<SelectorProps>({
props.onSearchSubmit((event.target as HTMLInputElement).value);
}
if (![KeyCode.SHIFT, KeyCode.TAB, KeyCode.BACKSPACE, KeyCode.ESC].includes(which)) {
if (isValidateOpenKey(which)) {
props.onToggleOpen(true);
}
};
@ -227,58 +232,44 @@ const Selector = defineComponent<SelectorProps>({
props.onToggleOpen();
}
};
return {
expose({
focus: () => {
inputRef.current.focus();
},
blur: () => {
inputRef.current.blur();
},
onMousedown,
onClick,
onInputPaste,
inputRef,
onInternalInputKeyDown,
onInternalInputMouseDown,
onInputChange,
onInputCompositionEnd,
onInputCompositionStart,
});
return () => {
const { prefixCls, domRef, mode } = props as SelectorProps;
const sharedProps = {
inputRef,
onInputKeyDown: onInternalInputKeyDown,
onInputMouseDown: onInternalInputMouseDown,
onInputChange,
onInputPaste,
onInputCompositionStart,
onInputCompositionEnd,
};
const selectNode =
mode === 'multiple' || mode === 'tags' ? (
<MultipleSelector {...props} {...sharedProps} />
) : (
<SingleSelector {...props} {...sharedProps} />
);
return (
<div
ref={domRef}
class={`${prefixCls}-selector`}
onClick={onClick}
onMousedown={onMousedown}
>
{selectNode}
</div>
);
};
},
render() {
const { prefixCls, domRef, multiple } = this.$props as SelectorProps;
const {
onMousedown,
onClick,
inputRef,
onInputPaste,
onInternalInputKeyDown,
onInternalInputMouseDown,
onInputChange,
onInputCompositionStart,
onInputCompositionEnd,
} = this as any;
const sharedProps = {
inputRef,
onInputKeyDown: onInternalInputKeyDown,
onInputMouseDown: onInternalInputMouseDown,
onInputChange,
onInputPaste,
onInputCompositionStart,
onInputCompositionEnd,
};
const selectNode = multiple ? (
<MultipleSelector {...this.$props} {...sharedProps} />
) : (
<SingleSelector {...this.$props} {...sharedProps} />
);
return (
<div ref={domRef} class={`${prefixCls}-selector`} onClick={onClick} onMousedown={onMousedown}>
{selectNode}
</div>
);
},
});
export default Selector;

View File

@ -1,8 +1,8 @@
import type { RefObject } from '../../_util/createRef';
import type { Mode } from '../interface';
import type { LabelValueType } from '../interface/generator';
import type { EventHandler } from '../../_util/EventInterface';
import type { VueNode } from '../../_util/type';
import type { Mode, DisplayValueType } from '../BaseSelect';
export interface InnerSelectorProps {
prefixCls: string;
@ -13,10 +13,10 @@ export interface InnerSelectorProps {
disabled?: boolean;
autofocus?: boolean;
autocomplete?: string;
values: LabelValueType[];
values: DisplayValueType[];
showSearch?: boolean;
searchValue: string;
accessibilityIndex: number;
activeDescendantId: string;
open: boolean;
tabindex?: number | string;
onInputKeyDown: EventHandler;

View File

@ -1,10 +1,11 @@
import type { FunctionalComponent } from 'vue';
import type { VueNode } from '../_util/type';
import PropTypes from '../_util/vue-types';
import type { RenderNode } from './BaseSelect';
export interface TransBtnProps {
class: string;
customizeIcon: VueNode | ((props?: any) => VueNode);
customizeIcon: RenderNode;
customizeIconProps?: any;
onMousedown?: (payload: MouseEvent) => void;
onClick?: (payload: MouseEvent) => void;

View File

@ -1,345 +0,0 @@
@select-prefix: ~'rc-select';
* {
box-sizing: border-box;
}
.search-input-without-border() {
.@{select-prefix}-selection-search-input {
border: none;
outline: none;
background: rgba(255, 0, 0, 0.2);
width: 100%;
}
}
.@{select-prefix} {
display: inline-block;
font-size: 12px;
width: 100px;
position: relative;
&-disabled {
&,
& input {
cursor: not-allowed;
}
.@{select-prefix}-selector {
opacity: 0.3;
}
}
&-show-arrow&-loading {
.@{select-prefix}-arrow {
&-icon::after {
box-sizing: border-box;
width: 12px;
height: 12px;
border-radius: 100%;
border: 2px solid #999;
border-top-color: transparent;
border-bottom-color: transparent;
transform: none;
margin-top: 4px;
animation: rcSelectLoadingIcon 0.5s infinite;
}
}
}
// ============== Selector ===============
.@{select-prefix}-selection-placeholder {
opacity: 0.4;
}
// ============== Search ===============
.@{select-prefix}-selection-search-input {
appearance: none;
&::-webkit-search-cancel-button {
display: none;
appearance: none;
}
}
// --------------- Single ----------------
&-single {
.@{select-prefix}-selector {
display: flex;
position: relative;
.@{select-prefix}-selection-search {
width: 100%;
&-input {
width: 100%;
}
}
.@{select-prefix}-selection-item,
.@{select-prefix}-selection-placeholder {
position: absolute;
top: 1px;
left: 3px;
pointer-events: none;
}
}
// Not customize
&:not(.@{select-prefix}-customize-input) {
.@{select-prefix}-selector {
padding: 1px;
border: 1px solid #000;
.search-input-without-border();
}
}
}
// -------------- Multiple ---------------
&-multiple .@{select-prefix}-selector {
display: flex;
flex-wrap: wrap;
padding: 1px;
border: 1px solid #000;
.@{select-prefix}-selection-item {
flex: none;
background: #bbb;
border-radius: 4px;
margin-right: 2px;
padding: 0 8px;
&-disabled {
cursor: not-allowed;
opacity: 0.5;
}
}
.@{select-prefix}-selection-search {
position: relative;
&-input,
&-mirror {
padding: 1px;
font-family: system-ui;
}
&-mirror {
position: absolute;
z-index: 999;
white-space: nowrap;
position: none;
left: 0;
top: 0;
visibility: hidden;
}
}
.search-input-without-border();
}
// ================ Icons ================
&-allow-clear {
&.@{select-prefix}-multiple .@{select-prefix}-selector {
padding-right: 20px;
}
.@{select-prefix}-clear {
position: absolute;
right: 20px;
top: 0;
}
}
&-show-arrow {
&.@{select-prefix}-multiple .@{select-prefix}-selector {
padding-right: 20px;
}
.@{select-prefix}-arrow {
pointer-events: none;
position: absolute;
right: 5px;
top: 0;
&-icon::after {
content: '';
border: 5px solid transparent;
width: 0;
height: 0;
display: inline-block;
border-top-color: #999;
transform: translateY(5px);
}
}
}
// =============== Focused ===============
&-focused {
.@{select-prefix}-selector {
border-color: blue !important;
}
}
// ============== Dropdown ===============
&-dropdown {
border: 1px solid green;
min-height: 100px;
position: absolute;
background: #fff;
&-hidden {
display: none;
}
}
// =============== Option ================
&-item {
font-size: 16px;
line-height: 1.5;
padding: 4px 16px;
// >>> Group
&-group {
color: #999;
font-weight: bold;
font-size: 80%;
}
// >>> Option
&-option {
position: relative;
&-grouped {
padding-left: 24px;
}
.@{select-prefix}-item-option-state {
position: absolute;
right: 0;
top: 4px;
pointer-events: none;
}
// ------- Active -------
&-active {
background: green;
}
// ------ Disabled ------
&-disabled {
color: #999;
}
}
// >>> Empty
&-empty {
text-align: center;
color: #999;
}
}
}
.@{select-prefix}-selection__choice-zoom {
transition: all 0.3s;
}
.@{select-prefix}-selection__choice-zoom-appear {
opacity: 0;
transform: scale(0.5);
&&-active {
opacity: 1;
transform: scale(1);
}
}
.@{select-prefix}-selection__choice-zoom-leave {
opacity: 1;
transform: scale(1);
&&-active {
opacity: 0;
transform: scale(0.5);
}
}
.effect() {
animation-duration: 0.3s;
animation-fill-mode: both;
transform-origin: 0 0;
}
.@{select-prefix}-dropdown {
&-slide-up-enter,
&-slide-up-appear {
.effect();
opacity: 0;
animation-timing-function: cubic-bezier(0.08, 0.82, 0.17, 1);
animation-play-state: paused;
}
&-slide-up-leave {
.effect();
opacity: 1;
animation-timing-function: cubic-bezier(0.6, 0.04, 0.98, 0.34);
animation-play-state: paused;
}
&-slide-up-enter&-slide-up-enter-active&-placement-bottomLeft,
&-slide-up-appear&-slide-up-appear-active&-placement-bottomLeft {
animation-name: rcSelectDropdownSlideUpIn;
animation-play-state: running;
}
&-slide-up-leave&-slide-up-leave-active&-placement-bottomLeft {
animation-name: rcSelectDropdownSlideUpOut;
animation-play-state: running;
}
&-slide-up-enter&-slide-up-enter-active&-placement-topLeft,
&-slide-up-appear&-slide-up-appear-active&-placement-topLeft {
animation-name: rcSelectDropdownSlideDownIn;
animation-play-state: running;
}
&-slide-up-leave&-slide-up-leave-active&-placement-topLeft {
animation-name: rcSelectDropdownSlideDownOut;
animation-play-state: running;
}
}
@keyframes rcSelectDropdownSlideUpIn {
0% {
opacity: 0;
transform-origin: 0% 0%;
transform: scaleY(0);
}
100% {
opacity: 1;
transform-origin: 0% 0%;
transform: scaleY(1);
}
}
@keyframes rcSelectDropdownSlideUpOut {
0% {
opacity: 1;
transform-origin: 0% 0%;
transform: scaleY(1);
}
100% {
opacity: 0;
transform-origin: 0% 0%;
transform: scaleY(0);
}
}
@keyframes rcSelectLoadingIcon {
0% {
transform: rotate(0);
}
100% {
transform: rotate(360deg);
}
}

View File

@ -1,136 +0,0 @@
import createRef from '../../_util/createRef';
/* eslint-disable no-console */
import Select, { Option } from '..';
import '../assets/index.less';
import { nextTick } from 'vue';
const Combobox = {
data() {
this.textareaRef = createRef();
this.timeoutId;
return {
disabled: false,
value: '',
options: [],
};
},
mounted() {
nextTick(() => {
console.log('Ref:', this.textareaRef.current);
});
},
methods: {
onChange(value, option) {
console.log('onChange', value, option);
this.value = value;
},
onKeyDown(e) {
const { value } = this;
if (e.keyCode === 13) {
console.log('onEnter', value);
}
},
onSelect(v, option) {
console.log('onSelect', v, option);
},
onSearch(text) {
console.log('onSearch:', text);
},
onAsyncChange(value) {
window.clearTimeout(this.timeoutId);
console.log(value);
this.options = [];
//const value = String(Math.random());
this.timeoutId = window.setTimeout(() => {
this.options = [{ value }, { value: `${value}-${value}` }];
}, 1000);
},
toggleDisabled() {
const { disabled } = this;
this.disabled = !disabled;
},
},
render() {
const { value, disabled } = this;
return (
<div>
<h2>combobox</h2>
<p>
<button type="button" onClick={this.toggleDisabled}>
toggle disabled
</button>
<button
type="button"
onClick={() => {
this.value = '';
}}
>
reset
</button>
</p>
<div>
<Select
disabled={disabled}
style={{ width: '500px' }}
onChange={this.onChange}
onSelect={this.onSelect}
onSearch={this.onSearch}
onInputKeyDown={this.onKeyDown}
notFoundContent=""
allowClear
placeholder="please select"
value={value}
mode="combobox"
backfill
onFocus={() => console.log('focus')}
onBlur={() => console.log('blur')}
>
<Option value="jack">
<b style={{ color: 'red' }}>jack</b>
</Option>
<Option value="lucy">lucy</Option>
<Option value="disabled" disabled>
disabled
</Option>
<Option value="yiminghe">yiminghe</Option>
<Option value="竹林星光">竹林星光</Option>
</Select>
<h3>Customize Input Element</h3>
<Select
mode="combobox"
style={{ width: '200px' }}
getInputElement={() => (
<textarea style={{ background: 'red' }} rows={3} ref={this.textareaRef} />
)}
options={[{ value: 'light' }, { value: 'bamboo' }]}
allowClear
placeholder="2333"
/>
<h3>Async Input Element</h3>
<Select
mode="combobox"
notFoundContent={null}
style={{ width: '200px' }}
options={this.options}
onChange={this.onAsyncChange}
/>
</div>
</div>
);
},
};
export default Combobox;
/* eslint-enable */

View File

@ -1,10 +0,0 @@
// input {
// // height: 24px;
// // line-height: 24px;
// border: 1px solid #333;
// border-radius: 4px;
// }
// button {
// border: 1px solid #333;
// }

View File

@ -1,35 +0,0 @@
import jsonp from 'jsonp';
import querystring from 'querystring';
let timeout;
let currentValue;
export function fetch(value, callback) {
if (timeout) {
clearTimeout(timeout);
timeout = null;
}
currentValue = value;
function fake() {
const str = querystring.encode({
code: 'utf-8',
q: value,
});
jsonp(`http://suggest.taobao.com/sug?${str}`, (err, d) => {
if (currentValue === value) {
const { result } = d;
const data = [];
result.forEach(r => {
data.push({
value: r[0],
text: r[0],
});
});
callback(data);
}
});
}
timeout = setTimeout(fake, 300);
}

View File

@ -1,99 +0,0 @@
/* eslint-disable no-console */
import Select, { Option } from '..';
import '../assets/index.less';
const Controlled = {
data: () => ({
destroy: false,
value: 9,
open: true,
}),
methods: {
onChange(e) {
let value;
if (e && e.target) {
({ value } = e.target);
} else {
value = e;
}
console.log('onChange', value);
this.value = value;
},
onDestroy() {
this.destroy = true;
},
onBlur(v) {
console.log('onBlur', v);
},
onFocus() {
console.log('onFocus');
},
onDropdownVisibleChange(open) {
this.open = open;
},
getPopupContainer(node) {
return node.parentNode;
},
},
render() {
const { open, destroy, value } = this;
if (destroy) {
return null;
}
return (
<div style={{ margin: '20px' }}>
<h2>controlled Select</h2>
<div style={{ width: '300px' }}>
<Select
id="my-select"
value={value}
placeholder="placeholder"
listHeight={200}
style={{ width: '500px' }}
onBlur={this.onBlur}
onFocus={this.onFocus}
open={open}
optionLabelProp="children"
optionFilterProp="text"
onChange={this.onChange}
onDropdownVisibleChange={this.onDropdownVisibleChange}
//getPopupContainer={this.getPopupContainer}
>
<Option value="01" text="jack" title="jack">
<b
style={{
color: 'red',
}}
>
jack
</b>
</Option>
<Option value="11" text="lucy">
lucy
</Option>
<Option value="21" disabled text="disabled">
disabled
</Option>
<Option value="31" text="yiminghe">
yiminghe
</Option>
{[0, 1, 2, 3, 4, 5, 6, 7, 8, 9].map(i => (
<Option key={i} value={i} text={String(i)}>
{i}-text
</Option>
))}
</Select>
</div>
</div>
);
},
};
export default Controlled;
/* eslint-enable */

View File

@ -1,118 +0,0 @@
/* eslint-disable no-console */
import Select, { Option } from '..';
import '../assets/index.less';
const children = [];
for (let i = 10; i < 36; i += 1) {
children.push(
<Option
key={i.toString(36) + i}
disabled={i === 10}
title={`中文${i}`}
v-slots={{ default: () => `中文${i}` }}
></Option>,
);
}
const Test = {
data: () => ({
state: {
useAnim: true,
showArrow: false,
loading: false,
value: ['a10'],
},
}),
methods: {
setState(state) {
Object.assign(this.state, state);
},
onChange(value, options) {
console.log('onChange', value, options);
this.setState({
value,
});
},
onSelect(...args) {
console.log(args);
},
onDeselect(...args) {
console.log(args);
},
useAnim(e) {
this.setState({
useAnim: e.target.checked,
});
},
showArrow(e) {
this.setState({
showArrow: e.target.checked,
});
},
loading(e) {
this.setState({
loading: e.target.checked,
});
},
},
render() {
const { useAnim, showArrow, loading, value } = this.state;
return (
<div style="margin: 20px">
<h2>multiple selectscroll the menu</h2>
<p>
<label html-for="useAnim">
anim
<input id="useAnim" checked={useAnim} type="checkbox" onChange={this.useAnim} />
</label>
<p />
<label html-for="showArrow">
showArrow
<input id="showArrow" checked={showArrow} type="checkbox" onChange={this.showArrow} />
</label>
</p>
<p>
<label html-for="loading">
loading
<input id="loading" checked={loading} type="checkbox" onChange={this.loading} />
</label>
</p>
<div style={{ width: '300px' }}>
<Select
value={value}
animation={useAnim ? 'slide-up' : null}
choiceTransitionName="rc-select-selection__choice-zoom"
style={{ width: '500px' }}
mode="multiple"
loading={loading}
showArrow={showArrow}
allowClear
optionFilterProp="children"
optionLabelProp="children"
onSelect={this.onSelect}
onDeselect={this.onDeselect}
placeholder="please select"
onChange={this.onChange}
onFocus={() => console.log('focus')}
onBlur={v => console.log('blur', v)}
tokenSeparators={[' ', ',']}
>
{children}
</Select>
</div>
</div>
);
},
};
export default Test;
/* eslint-enable */

Some files were not shown because too many files have changed in this diff Show More