refactor: tree-select
							parent
							
								
									8e38ed883c
								
							
						
					
					
						commit
						1581943eb0
					
				| 
						 | 
				
			
			@ -2,13 +2,12 @@ import type { App, PropType, Plugin, ExtractPropTypes } from 'vue';
 | 
			
		|||
import { computed, defineComponent, ref } from 'vue';
 | 
			
		||||
import classNames from '../_util/classNames';
 | 
			
		||||
import { selectProps as vcSelectProps } from '../vc-select';
 | 
			
		||||
import RcSelect, { Option, OptGroup, selectBaseProps } from '../vc-select';
 | 
			
		||||
import RcSelect, { Option, OptGroup } from '../vc-select';
 | 
			
		||||
import type { OptionProps as OptionPropsType } 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 type { SizeType } from '../config-provider';
 | 
			
		||||
import omit from '../_util/omit';
 | 
			
		||||
 | 
			
		||||
type RawValue = string | number;
 | 
			
		||||
| 
						 | 
				
			
			@ -62,7 +61,7 @@ const Select = defineComponent({
 | 
			
		|||
    'option',
 | 
			
		||||
  ],
 | 
			
		||||
  setup(props, { attrs, emit, slots, expose }) {
 | 
			
		||||
    const selectRef = ref(null);
 | 
			
		||||
    const selectRef = ref();
 | 
			
		||||
 | 
			
		||||
    const focus = () => {
 | 
			
		||||
      if (selectRef.value) {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -22,6 +22,7 @@ import getIcons from '../select/utils/iconUtil';
 | 
			
		|||
import renderSwitcherIcon from '../tree/utils/iconUtil';
 | 
			
		||||
import type { AntTreeNodeProps } from '../tree/Tree';
 | 
			
		||||
import { warning } from '../vc-util/warning';
 | 
			
		||||
import { flattenChildren } from '../_util/props-util';
 | 
			
		||||
 | 
			
		||||
const getTransitionName = (rootPrefixCls: string, motion: string, transitionName?: string) => {
 | 
			
		||||
  if (transitionName !== undefined) {
 | 
			
		||||
| 
						 | 
				
			
			@ -80,7 +81,7 @@ const TreeSelect = defineComponent({
 | 
			
		|||
  setup(props, { attrs, slots, expose, emit }) {
 | 
			
		||||
    warning(
 | 
			
		||||
      !(props.treeData === undefined && slots.default),
 | 
			
		||||
      '`children` of Tree is deprecated. Please use `treeData` instead.',
 | 
			
		||||
      '`children` of TreeSelect is deprecated. Please use `treeData` instead.',
 | 
			
		||||
    );
 | 
			
		||||
    watchEffect(() => {
 | 
			
		||||
      devWarning(
 | 
			
		||||
| 
						 | 
				
			
			@ -194,6 +195,10 @@ const TreeSelect = defineComponent({
 | 
			
		|||
        attrs.class,
 | 
			
		||||
      );
 | 
			
		||||
      const rootPrefixCls = configProvider.getPrefixCls();
 | 
			
		||||
      const otherProps: any = {};
 | 
			
		||||
      if (props.treeData === undefined && slots.default) {
 | 
			
		||||
        otherProps.children = flattenChildren(slots.default());
 | 
			
		||||
      }
 | 
			
		||||
      return (
 | 
			
		||||
        <VcTreeSelect
 | 
			
		||||
          {...attrs}
 | 
			
		||||
| 
						 | 
				
			
			@ -227,7 +232,7 @@ const TreeSelect = defineComponent({
 | 
			
		|||
            ...slots,
 | 
			
		||||
            treeCheckable: () => <span class={`${prefixCls.value}-tree-checkbox-inner`} />,
 | 
			
		||||
          }}
 | 
			
		||||
          children={slots.default?.()}
 | 
			
		||||
          {...otherProps}
 | 
			
		||||
        />
 | 
			
		||||
      );
 | 
			
		||||
    };
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -42,7 +42,8 @@ import {
 | 
			
		|||
  flattenOptions,
 | 
			
		||||
  fillOptionsWithMissingValue,
 | 
			
		||||
} from './utils/valueUtil';
 | 
			
		||||
import { selectBaseProps, SelectProps } from './generate';
 | 
			
		||||
import type { SelectProps } from './generate';
 | 
			
		||||
import { selectBaseProps } from './generate';
 | 
			
		||||
import generateSelector from './generate';
 | 
			
		||||
import type { DefaultValueType } from './interface/generator';
 | 
			
		||||
import warningProps from './utils/warningPropsUtil';
 | 
			
		||||
| 
						 | 
				
			
			@ -79,7 +80,7 @@ const Select = defineComponent({
 | 
			
		|||
  OptGroup,
 | 
			
		||||
  props: RefSelect.props,
 | 
			
		||||
  setup(props, { attrs, expose, slots }) {
 | 
			
		||||
    const selectRef = ref(null);
 | 
			
		||||
    const selectRef = ref();
 | 
			
		||||
    expose({
 | 
			
		||||
      focus: () => {
 | 
			
		||||
        selectRef.value?.focus();
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,7 +1,8 @@
 | 
			
		|||
import pickAttrs from '../../_util/pickAttrs';
 | 
			
		||||
import Input from './Input';
 | 
			
		||||
import type { InnerSelectorProps } from './interface';
 | 
			
		||||
import { Fragment, Suspense, VNodeChild } from 'vue';
 | 
			
		||||
import type { VNodeChild } from 'vue';
 | 
			
		||||
import { Fragment } from 'vue';
 | 
			
		||||
import { computed, defineComponent, ref, watch } from 'vue';
 | 
			
		||||
import PropTypes from '../../_util/vue-types';
 | 
			
		||||
import { useInjectTreeSelectContext } from 'ant-design-vue/es/vc-tree-select/Context';
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -52,7 +52,7 @@ export interface SelectorProps {
 | 
			
		|||
  // Motion
 | 
			
		||||
  choiceTransitionName?: string;
 | 
			
		||||
 | 
			
		||||
  onToggleOpen: (open?: boolean) => void;
 | 
			
		||||
  onToggleOpen: (open?: boolean) => void | any;
 | 
			
		||||
  /** `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;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -37,6 +37,7 @@ import useSelectTriggerControl from './hooks/useSelectTriggerControl';
 | 
			
		|||
import useCacheDisplayValue from './hooks/useCacheDisplayValue';
 | 
			
		||||
import useCacheOptions from './hooks/useCacheOptions';
 | 
			
		||||
import type { CSSProperties, DefineComponent, PropType, VNode, VNodeChild } from 'vue';
 | 
			
		||||
import { getCurrentInstance } from 'vue';
 | 
			
		||||
import {
 | 
			
		||||
  computed,
 | 
			
		||||
  defineComponent,
 | 
			
		||||
| 
						 | 
				
			
			@ -275,7 +276,7 @@ export default function generateSelector<
 | 
			
		|||
    slots: ['option'],
 | 
			
		||||
    inheritAttrs: false,
 | 
			
		||||
    props: selectBaseProps<OptionType, DefaultValueType>(),
 | 
			
		||||
    setup(props, { expose }) {
 | 
			
		||||
    setup(props, { expose, attrs, slots }) {
 | 
			
		||||
      const useInternalProps = computed(
 | 
			
		||||
        () => props.internalProps && props.internalProps.mark === INTERNAL_PROPS_MARK,
 | 
			
		||||
      );
 | 
			
		||||
| 
						 | 
				
			
			@ -284,10 +285,10 @@ export default function generateSelector<
 | 
			
		|||
        'Select',
 | 
			
		||||
        'optionFilterProp not support children, please use label instead',
 | 
			
		||||
      );
 | 
			
		||||
      const containerRef = ref(null);
 | 
			
		||||
      const triggerRef = ref(null);
 | 
			
		||||
      const selectorRef = ref(null);
 | 
			
		||||
      const listRef = ref(null);
 | 
			
		||||
      const containerRef = ref();
 | 
			
		||||
      const triggerRef = ref();
 | 
			
		||||
      const selectorRef = ref();
 | 
			
		||||
      const listRef = ref();
 | 
			
		||||
      const tokenWithEnter = computed(() =>
 | 
			
		||||
        (props.tokenSeparators || []).some(tokenSeparator =>
 | 
			
		||||
          ['\n', '\r\n'].includes(tokenSeparator),
 | 
			
		||||
| 
						 | 
				
			
			@ -353,7 +354,7 @@ export default function generateSelector<
 | 
			
		|||
 | 
			
		||||
      // ============================= Option =============================
 | 
			
		||||
      // Set by option list active, it will merge into search input when mode is `combobox`
 | 
			
		||||
      const activeValue = ref(null);
 | 
			
		||||
      const activeValue = ref();
 | 
			
		||||
      const setActiveValue = (val: string) => {
 | 
			
		||||
        activeValue.value = val;
 | 
			
		||||
      };
 | 
			
		||||
| 
						 | 
				
			
			@ -925,7 +926,7 @@ export default function generateSelector<
 | 
			
		|||
      };
 | 
			
		||||
 | 
			
		||||
      // ============================= Popup ==============================
 | 
			
		||||
      const containerWidth = ref(null);
 | 
			
		||||
      const containerWidth = ref<number>(null);
 | 
			
		||||
      onMounted(() => {
 | 
			
		||||
        watch(
 | 
			
		||||
          triggerOpen,
 | 
			
		||||
| 
						 | 
				
			
			@ -952,342 +953,270 @@ export default function generateSelector<
 | 
			
		|||
        blur,
 | 
			
		||||
        scrollTo: (...args: any[]) => listRef.value?.scrollTo(...args),
 | 
			
		||||
      });
 | 
			
		||||
      return {
 | 
			
		||||
        tokenWithEnter,
 | 
			
		||||
        mockFocused,
 | 
			
		||||
        mergedId,
 | 
			
		||||
        containerWidth,
 | 
			
		||||
        onActiveValue,
 | 
			
		||||
        accessibilityIndex,
 | 
			
		||||
        mergedDefaultActiveFirstOption,
 | 
			
		||||
        onInternalMouseDown,
 | 
			
		||||
        onContainerFocus,
 | 
			
		||||
        onContainerBlur,
 | 
			
		||||
        onInternalKeyDown,
 | 
			
		||||
        isMultiple,
 | 
			
		||||
        mergedOpen,
 | 
			
		||||
        displayOptions,
 | 
			
		||||
        displayFlattenOptions,
 | 
			
		||||
        rawValues,
 | 
			
		||||
        onInternalOptionSelect,
 | 
			
		||||
        onToggleOpen,
 | 
			
		||||
        mergedSearchValue,
 | 
			
		||||
        useInternalProps,
 | 
			
		||||
        triggerChange,
 | 
			
		||||
        triggerSearch,
 | 
			
		||||
        mergedRawValue,
 | 
			
		||||
        mergedShowSearch,
 | 
			
		||||
        onInternalKeyUp,
 | 
			
		||||
        triggerOpen,
 | 
			
		||||
        mergedOptions,
 | 
			
		||||
        onInternalSelectionSelect,
 | 
			
		||||
        selectorDomRef,
 | 
			
		||||
        displayValues,
 | 
			
		||||
        activeValue,
 | 
			
		||||
        onSearchSubmit,
 | 
			
		||||
        containerRef,
 | 
			
		||||
        listRef,
 | 
			
		||||
        triggerRef,
 | 
			
		||||
        selectorRef,
 | 
			
		||||
      const instance = getCurrentInstance();
 | 
			
		||||
      const onPopupMouseEnter = () => {
 | 
			
		||||
        // We need force update here since popup dom is render async
 | 
			
		||||
        instance.update();
 | 
			
		||||
      };
 | 
			
		||||
    },
 | 
			
		||||
    methods: {
 | 
			
		||||
      // We need force update here since popup dom is render async
 | 
			
		||||
      onPopupMouseEnter() {
 | 
			
		||||
        (this as any).$forceUpdate();
 | 
			
		||||
      },
 | 
			
		||||
    },
 | 
			
		||||
    render() {
 | 
			
		||||
      const {
 | 
			
		||||
        tokenWithEnter,
 | 
			
		||||
        mockFocused,
 | 
			
		||||
        mergedId,
 | 
			
		||||
        containerWidth,
 | 
			
		||||
        onActiveValue,
 | 
			
		||||
        accessibilityIndex,
 | 
			
		||||
        mergedDefaultActiveFirstOption,
 | 
			
		||||
        onInternalMouseDown,
 | 
			
		||||
        onInternalKeyDown,
 | 
			
		||||
        isMultiple,
 | 
			
		||||
        mergedOpen,
 | 
			
		||||
        displayOptions,
 | 
			
		||||
        displayFlattenOptions,
 | 
			
		||||
        rawValues,
 | 
			
		||||
        onInternalOptionSelect,
 | 
			
		||||
        onToggleOpen,
 | 
			
		||||
        mergedSearchValue,
 | 
			
		||||
        onPopupMouseEnter,
 | 
			
		||||
        useInternalProps,
 | 
			
		||||
        triggerChange,
 | 
			
		||||
        triggerSearch,
 | 
			
		||||
        mergedRawValue,
 | 
			
		||||
        mergedShowSearch,
 | 
			
		||||
        onInternalKeyUp,
 | 
			
		||||
        triggerOpen,
 | 
			
		||||
        mergedOptions,
 | 
			
		||||
        onInternalSelectionSelect,
 | 
			
		||||
        selectorDomRef,
 | 
			
		||||
        displayValues,
 | 
			
		||||
        activeValue,
 | 
			
		||||
        onSearchSubmit,
 | 
			
		||||
        $slots: slots,
 | 
			
		||||
      } = this as any;
 | 
			
		||||
      const {
 | 
			
		||||
        prefixCls = defaultPrefixCls,
 | 
			
		||||
        id,
 | 
			
		||||
      return () => {
 | 
			
		||||
        const {
 | 
			
		||||
          prefixCls = defaultPrefixCls,
 | 
			
		||||
          id,
 | 
			
		||||
 | 
			
		||||
        open,
 | 
			
		||||
        defaultOpen,
 | 
			
		||||
        options,
 | 
			
		||||
        children,
 | 
			
		||||
          open,
 | 
			
		||||
          defaultOpen,
 | 
			
		||||
          options,
 | 
			
		||||
          children,
 | 
			
		||||
 | 
			
		||||
        mode,
 | 
			
		||||
        value,
 | 
			
		||||
        defaultValue,
 | 
			
		||||
        labelInValue,
 | 
			
		||||
          mode,
 | 
			
		||||
          value,
 | 
			
		||||
          defaultValue,
 | 
			
		||||
          labelInValue,
 | 
			
		||||
 | 
			
		||||
        // Search related
 | 
			
		||||
        showSearch,
 | 
			
		||||
        inputValue,
 | 
			
		||||
        searchValue,
 | 
			
		||||
        filterOption,
 | 
			
		||||
        optionFilterProp,
 | 
			
		||||
        autoClearSearchValue,
 | 
			
		||||
        onSearch,
 | 
			
		||||
          // Search related
 | 
			
		||||
          showSearch,
 | 
			
		||||
          inputValue,
 | 
			
		||||
          searchValue,
 | 
			
		||||
          filterOption,
 | 
			
		||||
          optionFilterProp,
 | 
			
		||||
          autoClearSearchValue,
 | 
			
		||||
          onSearch,
 | 
			
		||||
 | 
			
		||||
        // Icons
 | 
			
		||||
        allowClear,
 | 
			
		||||
        clearIcon,
 | 
			
		||||
        showArrow,
 | 
			
		||||
        inputIcon,
 | 
			
		||||
        menuItemSelectedIcon,
 | 
			
		||||
          // Icons
 | 
			
		||||
          allowClear,
 | 
			
		||||
          clearIcon,
 | 
			
		||||
          showArrow,
 | 
			
		||||
          inputIcon,
 | 
			
		||||
          menuItemSelectedIcon,
 | 
			
		||||
 | 
			
		||||
        // Others
 | 
			
		||||
        disabled,
 | 
			
		||||
        loading,
 | 
			
		||||
        defaultActiveFirstOption,
 | 
			
		||||
        notFoundContent = 'Not Found',
 | 
			
		||||
        optionLabelProp,
 | 
			
		||||
        backfill,
 | 
			
		||||
        getInputElement,
 | 
			
		||||
        getPopupContainer,
 | 
			
		||||
          // Others
 | 
			
		||||
          disabled,
 | 
			
		||||
          loading,
 | 
			
		||||
          defaultActiveFirstOption,
 | 
			
		||||
          notFoundContent = 'Not Found',
 | 
			
		||||
          optionLabelProp,
 | 
			
		||||
          backfill,
 | 
			
		||||
          getInputElement,
 | 
			
		||||
          getPopupContainer,
 | 
			
		||||
 | 
			
		||||
        // Dropdown
 | 
			
		||||
        listHeight = 200,
 | 
			
		||||
        listItemHeight = 20,
 | 
			
		||||
        animation,
 | 
			
		||||
        transitionName,
 | 
			
		||||
        virtual,
 | 
			
		||||
        dropdownStyle,
 | 
			
		||||
        dropdownClassName,
 | 
			
		||||
        dropdownMatchSelectWidth,
 | 
			
		||||
        dropdownRender,
 | 
			
		||||
        dropdownAlign,
 | 
			
		||||
        showAction,
 | 
			
		||||
        direction,
 | 
			
		||||
          // Dropdown
 | 
			
		||||
          listHeight = 200,
 | 
			
		||||
          listItemHeight = 20,
 | 
			
		||||
          animation,
 | 
			
		||||
          transitionName,
 | 
			
		||||
          virtual,
 | 
			
		||||
          dropdownStyle,
 | 
			
		||||
          dropdownClassName,
 | 
			
		||||
          dropdownMatchSelectWidth,
 | 
			
		||||
          dropdownRender,
 | 
			
		||||
          dropdownAlign,
 | 
			
		||||
          showAction,
 | 
			
		||||
          direction,
 | 
			
		||||
 | 
			
		||||
        // Tags
 | 
			
		||||
        tokenSeparators,
 | 
			
		||||
        tagRender,
 | 
			
		||||
          // Tags
 | 
			
		||||
          tokenSeparators,
 | 
			
		||||
          tagRender,
 | 
			
		||||
 | 
			
		||||
        // Events
 | 
			
		||||
        onPopupScroll,
 | 
			
		||||
        onDropdownVisibleChange,
 | 
			
		||||
        onFocus,
 | 
			
		||||
        onBlur,
 | 
			
		||||
        onKeyup,
 | 
			
		||||
        onKeydown,
 | 
			
		||||
        onMousedown,
 | 
			
		||||
          // Events
 | 
			
		||||
          onPopupScroll,
 | 
			
		||||
          onDropdownVisibleChange,
 | 
			
		||||
          onFocus,
 | 
			
		||||
          onBlur,
 | 
			
		||||
          onKeyup,
 | 
			
		||||
          onKeydown,
 | 
			
		||||
          onMousedown,
 | 
			
		||||
 | 
			
		||||
        onChange,
 | 
			
		||||
        onSelect,
 | 
			
		||||
        onDeselect,
 | 
			
		||||
        onClear,
 | 
			
		||||
          onChange,
 | 
			
		||||
          onSelect,
 | 
			
		||||
          onDeselect,
 | 
			
		||||
          onClear,
 | 
			
		||||
 | 
			
		||||
        internalProps = {},
 | 
			
		||||
          internalProps = {},
 | 
			
		||||
 | 
			
		||||
        ...restProps
 | 
			
		||||
      } = this.$props; //as SelectProps<OptionType[], ValueType>;
 | 
			
		||||
          ...restProps
 | 
			
		||||
        } = props; //as SelectProps<OptionType[], ValueType>;
 | 
			
		||||
        // ============================= Input ==============================
 | 
			
		||||
        // Only works in `combobox`
 | 
			
		||||
        const customizeInputElement: VNodeChild | JSX.Element =
 | 
			
		||||
          (mode === 'combobox' && getInputElement && getInputElement()) || null;
 | 
			
		||||
 | 
			
		||||
      // ============================= Input ==============================
 | 
			
		||||
      // Only works in `combobox`
 | 
			
		||||
      const customizeInputElement: VNodeChild | JSX.Element =
 | 
			
		||||
        (mode === 'combobox' && getInputElement && getInputElement()) || null;
 | 
			
		||||
 | 
			
		||||
      const domProps = omitDOMProps ? omitDOMProps(restProps) : restProps;
 | 
			
		||||
      DEFAULT_OMIT_PROPS.forEach(prop => {
 | 
			
		||||
        delete domProps[prop];
 | 
			
		||||
      });
 | 
			
		||||
      const popupNode = (
 | 
			
		||||
        <OptionList
 | 
			
		||||
          ref="listRef"
 | 
			
		||||
          prefixCls={prefixCls}
 | 
			
		||||
          id={mergedId}
 | 
			
		||||
          open={mergedOpen}
 | 
			
		||||
          childrenAsData={!options}
 | 
			
		||||
          options={displayOptions}
 | 
			
		||||
          flattenOptions={displayFlattenOptions}
 | 
			
		||||
          multiple={isMultiple}
 | 
			
		||||
          values={rawValues}
 | 
			
		||||
          height={listHeight}
 | 
			
		||||
          itemHeight={listItemHeight}
 | 
			
		||||
          onSelect={onInternalOptionSelect}
 | 
			
		||||
          onToggleOpen={onToggleOpen}
 | 
			
		||||
          onActiveValue={onActiveValue}
 | 
			
		||||
          defaultActiveFirstOption={mergedDefaultActiveFirstOption}
 | 
			
		||||
          notFoundContent={notFoundContent}
 | 
			
		||||
          onScroll={onPopupScroll}
 | 
			
		||||
          searchValue={mergedSearchValue}
 | 
			
		||||
          menuItemSelectedIcon={menuItemSelectedIcon}
 | 
			
		||||
          virtual={virtual !== false && dropdownMatchSelectWidth !== false}
 | 
			
		||||
          onMouseenter={onPopupMouseEnter}
 | 
			
		||||
          v-slots={{ ...slots, option: slots.option }}
 | 
			
		||||
        />
 | 
			
		||||
      );
 | 
			
		||||
 | 
			
		||||
      // ============================= Clear ==============================
 | 
			
		||||
      let clearNode: VNode | JSX.Element;
 | 
			
		||||
      const onClearMouseDown = () => {
 | 
			
		||||
        // Trigger internal `onClear` event
 | 
			
		||||
        if (useInternalProps && internalProps.onClear) {
 | 
			
		||||
          internalProps.onClear();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (onClear) {
 | 
			
		||||
          onClear();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        triggerChange([]);
 | 
			
		||||
        triggerSearch('', false, false);
 | 
			
		||||
      };
 | 
			
		||||
 | 
			
		||||
      if (!disabled && allowClear && (mergedRawValue.length || mergedSearchValue)) {
 | 
			
		||||
        clearNode = (
 | 
			
		||||
          <TransBtn
 | 
			
		||||
            class={`${prefixCls}-clear`}
 | 
			
		||||
            onMousedown={onClearMouseDown}
 | 
			
		||||
            customizeIcon={clearIcon}
 | 
			
		||||
          >
 | 
			
		||||
            ×
 | 
			
		||||
          </TransBtn>
 | 
			
		||||
        );
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      // ============================= Arrow ==============================
 | 
			
		||||
      const mergedShowArrow =
 | 
			
		||||
        showArrow !== undefined ? showArrow : loading || (!isMultiple && 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,
 | 
			
		||||
              open: mergedOpen,
 | 
			
		||||
              focused: mockFocused,
 | 
			
		||||
              showSearch: mergedShowSearch,
 | 
			
		||||
            }}
 | 
			
		||||
        const domProps = omitDOMProps ? omitDOMProps(restProps) : restProps;
 | 
			
		||||
        DEFAULT_OMIT_PROPS.forEach(prop => {
 | 
			
		||||
          delete domProps[prop];
 | 
			
		||||
        });
 | 
			
		||||
        const popupNode = (
 | 
			
		||||
          <OptionList
 | 
			
		||||
            ref={listRef}
 | 
			
		||||
            prefixCls={prefixCls}
 | 
			
		||||
            id={mergedId.value}
 | 
			
		||||
            open={mergedOpen.value}
 | 
			
		||||
            childrenAsData={!options}
 | 
			
		||||
            options={displayOptions.value}
 | 
			
		||||
            flattenOptions={displayFlattenOptions.value}
 | 
			
		||||
            multiple={isMultiple.value}
 | 
			
		||||
            values={rawValues.value}
 | 
			
		||||
            height={listHeight}
 | 
			
		||||
            itemHeight={listItemHeight}
 | 
			
		||||
            onSelect={onInternalOptionSelect}
 | 
			
		||||
            onToggleOpen={onToggleOpen}
 | 
			
		||||
            onActiveValue={onActiveValue}
 | 
			
		||||
            defaultActiveFirstOption={mergedDefaultActiveFirstOption.value}
 | 
			
		||||
            notFoundContent={notFoundContent}
 | 
			
		||||
            onScroll={onPopupScroll}
 | 
			
		||||
            searchValue={mergedSearchValue.value}
 | 
			
		||||
            menuItemSelectedIcon={menuItemSelectedIcon}
 | 
			
		||||
            virtual={virtual !== false && dropdownMatchSelectWidth !== false}
 | 
			
		||||
            onMouseenter={onPopupMouseEnter}
 | 
			
		||||
            v-slots={{ ...slots, option: slots.option }}
 | 
			
		||||
          />
 | 
			
		||||
        );
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      // ============================ Warning =============================
 | 
			
		||||
      if (process.env.NODE_ENV !== 'production' && warningProps) {
 | 
			
		||||
        warningProps(this.$props);
 | 
			
		||||
      }
 | 
			
		||||
        // ============================= Clear ==============================
 | 
			
		||||
        let clearNode: VNode | JSX.Element;
 | 
			
		||||
        const onClearMouseDown = () => {
 | 
			
		||||
          // Trigger internal `onClear` event
 | 
			
		||||
          if (useInternalProps.value && internalProps.onClear) {
 | 
			
		||||
            internalProps.onClear();
 | 
			
		||||
          }
 | 
			
		||||
 | 
			
		||||
      // ============================= Render =============================
 | 
			
		||||
      const mergedClassName = classNames(prefixCls, this.$attrs.class, {
 | 
			
		||||
        [`${prefixCls}-focused`]: mockFocused,
 | 
			
		||||
        [`${prefixCls}-multiple`]: isMultiple,
 | 
			
		||||
        [`${prefixCls}-single`]: !isMultiple,
 | 
			
		||||
        [`${prefixCls}-allow-clear`]: allowClear,
 | 
			
		||||
        [`${prefixCls}-show-arrow`]: mergedShowArrow,
 | 
			
		||||
        [`${prefixCls}-disabled`]: disabled,
 | 
			
		||||
        [`${prefixCls}-loading`]: loading,
 | 
			
		||||
        [`${prefixCls}-open`]: mergedOpen,
 | 
			
		||||
        [`${prefixCls}-customize-input`]: customizeInputElement,
 | 
			
		||||
        [`${prefixCls}-show-search`]: mergedShowSearch,
 | 
			
		||||
      });
 | 
			
		||||
      return (
 | 
			
		||||
        <div
 | 
			
		||||
          {...this.$attrs}
 | 
			
		||||
          class={mergedClassName}
 | 
			
		||||
          {...domProps}
 | 
			
		||||
          ref="containerRef"
 | 
			
		||||
          onMousedown={onInternalMouseDown}
 | 
			
		||||
          onKeydown={onInternalKeyDown}
 | 
			
		||||
          onKeyup={onInternalKeyUp}
 | 
			
		||||
          // onFocus={onContainerFocus} // trigger by input
 | 
			
		||||
          // onBlur={onContainerBlur} // trigger by input
 | 
			
		||||
        >
 | 
			
		||||
          {mockFocused && !mergedOpen && (
 | 
			
		||||
            <span
 | 
			
		||||
              style={{
 | 
			
		||||
                width: 0,
 | 
			
		||||
                height: 0,
 | 
			
		||||
                display: 'flex',
 | 
			
		||||
                overflow: 'hidden',
 | 
			
		||||
                opacity: 0,
 | 
			
		||||
              }}
 | 
			
		||||
              aria-live="polite"
 | 
			
		||||
          if (onClear) {
 | 
			
		||||
            onClear();
 | 
			
		||||
          }
 | 
			
		||||
 | 
			
		||||
          triggerChange([]);
 | 
			
		||||
          triggerSearch('', false, false);
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        if (!disabled && allowClear && (mergedRawValue.value.length || mergedSearchValue.value)) {
 | 
			
		||||
          clearNode = (
 | 
			
		||||
            <TransBtn
 | 
			
		||||
              class={`${prefixCls}-clear`}
 | 
			
		||||
              onMousedown={onClearMouseDown}
 | 
			
		||||
              customizeIcon={clearIcon}
 | 
			
		||||
            >
 | 
			
		||||
              {/* Merge into one string to make screen reader work as expect */}
 | 
			
		||||
              {`${mergedRawValue.join(', ')}`}
 | 
			
		||||
            </span>
 | 
			
		||||
          )}
 | 
			
		||||
          <SelectTrigger
 | 
			
		||||
            ref="triggerRef"
 | 
			
		||||
            disabled={disabled}
 | 
			
		||||
            prefixCls={prefixCls}
 | 
			
		||||
            visible={triggerOpen}
 | 
			
		||||
            popupElement={popupNode}
 | 
			
		||||
            containerWidth={containerWidth}
 | 
			
		||||
            animation={animation}
 | 
			
		||||
            transitionName={transitionName}
 | 
			
		||||
            dropdownStyle={dropdownStyle}
 | 
			
		||||
            dropdownClassName={dropdownClassName}
 | 
			
		||||
            direction={direction}
 | 
			
		||||
            dropdownMatchSelectWidth={dropdownMatchSelectWidth}
 | 
			
		||||
            dropdownRender={dropdownRender as any}
 | 
			
		||||
            dropdownAlign={dropdownAlign}
 | 
			
		||||
            getPopupContainer={getPopupContainer}
 | 
			
		||||
            empty={!mergedOptions.length}
 | 
			
		||||
            getTriggerDOMNode={() => selectorDomRef.current}
 | 
			
		||||
          >
 | 
			
		||||
            <Selector
 | 
			
		||||
              {...this.$props}
 | 
			
		||||
              domRef={selectorDomRef}
 | 
			
		||||
              prefixCls={prefixCls}
 | 
			
		||||
              inputElement={customizeInputElement}
 | 
			
		||||
              ref="selectorRef"
 | 
			
		||||
              id={mergedId}
 | 
			
		||||
              showSearch={mergedShowSearch}
 | 
			
		||||
              mode={mode}
 | 
			
		||||
              accessibilityIndex={accessibilityIndex}
 | 
			
		||||
              multiple={isMultiple}
 | 
			
		||||
              tagRender={tagRender}
 | 
			
		||||
              values={displayValues}
 | 
			
		||||
              open={mergedOpen}
 | 
			
		||||
              onToggleOpen={onToggleOpen}
 | 
			
		||||
              searchValue={mergedSearchValue}
 | 
			
		||||
              activeValue={activeValue}
 | 
			
		||||
              onSearch={triggerSearch}
 | 
			
		||||
              onSearchSubmit={onSearchSubmit}
 | 
			
		||||
              onSelect={onInternalSelectionSelect}
 | 
			
		||||
              tokenWithEnter={tokenWithEnter}
 | 
			
		||||
            />
 | 
			
		||||
          </SelectTrigger>
 | 
			
		||||
              ×
 | 
			
		||||
            </TransBtn>
 | 
			
		||||
          );
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
          {arrowNode}
 | 
			
		||||
          {clearNode}
 | 
			
		||||
        </div>
 | 
			
		||||
      );
 | 
			
		||||
        // ============================= Arrow ==============================
 | 
			
		||||
        const mergedShowArrow =
 | 
			
		||||
          showArrow !== undefined
 | 
			
		||||
            ? showArrow
 | 
			
		||||
            : loading || (!isMultiple.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,
 | 
			
		||||
              }}
 | 
			
		||||
            />
 | 
			
		||||
          );
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // ============================ Warning =============================
 | 
			
		||||
        if (process.env.NODE_ENV !== 'production' && warningProps) {
 | 
			
		||||
          warningProps(props);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // ============================= Render =============================
 | 
			
		||||
        const mergedClassName = classNames(prefixCls, attrs.class, {
 | 
			
		||||
          [`${prefixCls}-focused`]: mockFocused.value,
 | 
			
		||||
          [`${prefixCls}-multiple`]: isMultiple.value,
 | 
			
		||||
          [`${prefixCls}-single`]: !isMultiple.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,
 | 
			
		||||
        });
 | 
			
		||||
        return (
 | 
			
		||||
          <div
 | 
			
		||||
            {...attrs}
 | 
			
		||||
            class={mergedClassName}
 | 
			
		||||
            {...domProps}
 | 
			
		||||
            ref={containerRef}
 | 
			
		||||
            onMousedown={onInternalMouseDown}
 | 
			
		||||
            onKeydown={onInternalKeyDown}
 | 
			
		||||
            onKeyup={onInternalKeyUp}
 | 
			
		||||
            // onFocus={onContainerFocus} // trigger by input
 | 
			
		||||
            // onBlur={onContainerBlur} // trigger by input
 | 
			
		||||
          >
 | 
			
		||||
            {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 */}
 | 
			
		||||
                {`${mergedRawValue.value.join(', ')}`}
 | 
			
		||||
              </span>
 | 
			
		||||
            )}
 | 
			
		||||
            <SelectTrigger
 | 
			
		||||
              ref={triggerRef}
 | 
			
		||||
              disabled={disabled}
 | 
			
		||||
              prefixCls={prefixCls}
 | 
			
		||||
              visible={triggerOpen.value}
 | 
			
		||||
              popupElement={popupNode}
 | 
			
		||||
              containerWidth={containerWidth.value}
 | 
			
		||||
              animation={animation}
 | 
			
		||||
              transitionName={transitionName}
 | 
			
		||||
              dropdownStyle={dropdownStyle}
 | 
			
		||||
              dropdownClassName={dropdownClassName}
 | 
			
		||||
              direction={direction}
 | 
			
		||||
              dropdownMatchSelectWidth={dropdownMatchSelectWidth}
 | 
			
		||||
              dropdownRender={dropdownRender as any}
 | 
			
		||||
              dropdownAlign={dropdownAlign}
 | 
			
		||||
              getPopupContainer={getPopupContainer}
 | 
			
		||||
              empty={!mergedOptions.value.length}
 | 
			
		||||
              getTriggerDOMNode={() => selectorDomRef.current}
 | 
			
		||||
            >
 | 
			
		||||
              <Selector
 | 
			
		||||
                {...props}
 | 
			
		||||
                domRef={selectorDomRef}
 | 
			
		||||
                prefixCls={prefixCls}
 | 
			
		||||
                inputElement={customizeInputElement}
 | 
			
		||||
                ref={selectorRef}
 | 
			
		||||
                id={mergedId.value}
 | 
			
		||||
                showSearch={mergedShowSearch.value}
 | 
			
		||||
                mode={mode}
 | 
			
		||||
                accessibilityIndex={accessibilityIndex.value}
 | 
			
		||||
                multiple={isMultiple.value}
 | 
			
		||||
                tagRender={tagRender}
 | 
			
		||||
                values={displayValues.value}
 | 
			
		||||
                open={mergedOpen.value}
 | 
			
		||||
                onToggleOpen={onToggleOpen}
 | 
			
		||||
                searchValue={mergedSearchValue.value}
 | 
			
		||||
                activeValue={activeValue.value}
 | 
			
		||||
                onSearch={triggerSearch}
 | 
			
		||||
                onSearchSubmit={onSearchSubmit}
 | 
			
		||||
                onSelect={onInternalSelectionSelect}
 | 
			
		||||
                tokenWithEnter={tokenWithEnter.value}
 | 
			
		||||
              />
 | 
			
		||||
            </SelectTrigger>
 | 
			
		||||
 | 
			
		||||
            {arrowNode}
 | 
			
		||||
            {clearNode}
 | 
			
		||||
          </div>
 | 
			
		||||
        );
 | 
			
		||||
      };
 | 
			
		||||
    },
 | 
			
		||||
  });
 | 
			
		||||
  return Select;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -34,7 +34,6 @@ export default defineComponent({
 | 
			
		|||
  inheritAttrs: false,
 | 
			
		||||
  props: optionListProps<DataNode>(),
 | 
			
		||||
  slots: ['notFoundContent', 'menuItemSelectedIcon'],
 | 
			
		||||
  expose: ['scrollTo', 'onKeydown', 'onKeyup'],
 | 
			
		||||
  setup(props, { slots, expose }) {
 | 
			
		||||
    const context = useInjectTreeSelectContext();
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -153,7 +152,7 @@ export default defineComponent({
 | 
			
		|||
          case KeyCode.DOWN:
 | 
			
		||||
          case KeyCode.LEFT:
 | 
			
		||||
          case KeyCode.RIGHT:
 | 
			
		||||
            treeRef.value?.onKeyDown(event);
 | 
			
		||||
            treeRef.value?.onKeydown(event);
 | 
			
		||||
            break;
 | 
			
		||||
 | 
			
		||||
          // >>> Select item
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -8,7 +8,8 @@ export interface TreeNodeProps extends Omit<DataNode, 'children'> {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
/** This is a placeholder, not real render in dom */
 | 
			
		||||
const TreeNode: FunctionalComponent<TreeNodeProps> = () => null;
 | 
			
		||||
const TreeNode: FunctionalComponent<TreeNodeProps> & { isTreeSelectNode: boolean } = () => null;
 | 
			
		||||
TreeNode.inheritAttrs = false;
 | 
			
		||||
TreeNode.displayName = 'ATreeSelectNode';
 | 
			
		||||
TreeNode.isTreeSelectNode = true;
 | 
			
		||||
export default TreeNode;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -161,7 +161,7 @@ export default function generate(config: {
 | 
			
		|||
      });
 | 
			
		||||
 | 
			
		||||
      // ========================== Ref ==========================
 | 
			
		||||
      const selectRef = ref(null);
 | 
			
		||||
      const selectRef = ref();
 | 
			
		||||
 | 
			
		||||
      expose({
 | 
			
		||||
        scrollTo: (...args: any[]) => selectRef.value.scrollTo?.(...args),
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,5 +1,6 @@
 | 
			
		|||
import type { VNodeChild } from 'vue';
 | 
			
		||||
import { camelize } from 'vue';
 | 
			
		||||
import { warning } from '../../vc-util/warning';
 | 
			
		||||
import { isValidElement } from '../../_util/props-util';
 | 
			
		||||
import type {
 | 
			
		||||
  DataNode,
 | 
			
		||||
  LegacyDataNode,
 | 
			
		||||
| 
						 | 
				
			
			@ -10,32 +11,58 @@ import type {
 | 
			
		|||
} from '../interface';
 | 
			
		||||
import TreeNode from '../TreeNode';
 | 
			
		||||
 | 
			
		||||
export function convertChildrenToData(nodes): DataNode[] {
 | 
			
		||||
  return nodes
 | 
			
		||||
    .map(node => {
 | 
			
		||||
      if (!isValidElement(node) || !node.type) {
 | 
			
		||||
function isTreeSelectNode(node: any) {
 | 
			
		||||
  return node && node.type && (node.type as any).isTreeSelectNode;
 | 
			
		||||
}
 | 
			
		||||
export function convertChildrenToData(rootNodes: VNodeChild): DataNode[] {
 | 
			
		||||
  function dig(treeNodes: any[] = []): DataNode[] {
 | 
			
		||||
    return treeNodes.map(treeNode => {
 | 
			
		||||
      // Filter invalidate node
 | 
			
		||||
      if (!isTreeSelectNode(treeNode)) {
 | 
			
		||||
        warning(!treeNode, 'TreeSelect/TreeSelectNode can only accept TreeSelectNode as children.');
 | 
			
		||||
        return null;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      const slots = (treeNode.children as any) || {};
 | 
			
		||||
      const key = treeNode.key as string | number;
 | 
			
		||||
      const props: any = {};
 | 
			
		||||
      for (const [k, v] of Object.entries(treeNode.props)) {
 | 
			
		||||
        props[camelize(k)] = v;
 | 
			
		||||
      }
 | 
			
		||||
      const { isLeaf, checkable, selectable, disabled, disableCheckbox } = props;
 | 
			
		||||
      // 默认值为 undefined
 | 
			
		||||
      const newProps = {
 | 
			
		||||
        isLeaf: isLeaf || isLeaf === '' || undefined,
 | 
			
		||||
        checkable: checkable || checkable === '' || undefined,
 | 
			
		||||
        selectable: selectable || selectable === '' || undefined,
 | 
			
		||||
        disabled: disabled || disabled === '' || undefined,
 | 
			
		||||
        disableCheckbox: disableCheckbox || disableCheckbox === '' || undefined,
 | 
			
		||||
      };
 | 
			
		||||
      const slotsProps = { ...props, ...newProps };
 | 
			
		||||
      const {
 | 
			
		||||
        title = slots.title?.(slotsProps),
 | 
			
		||||
        switcherIcon = slots.switcherIcon?.(slotsProps),
 | 
			
		||||
        ...rest
 | 
			
		||||
      } = props;
 | 
			
		||||
      const children = slots.default?.();
 | 
			
		||||
      const dataNode: DataNode = {
 | 
			
		||||
        ...rest,
 | 
			
		||||
        title,
 | 
			
		||||
        switcherIcon,
 | 
			
		||||
        key,
 | 
			
		||||
        props: { children, value, ...restProps },
 | 
			
		||||
      } = node;
 | 
			
		||||
 | 
			
		||||
      const data = {
 | 
			
		||||
        key,
 | 
			
		||||
        value,
 | 
			
		||||
        ...restProps,
 | 
			
		||||
        isLeaf,
 | 
			
		||||
        ...newProps,
 | 
			
		||||
      };
 | 
			
		||||
 | 
			
		||||
      const childData = convertChildrenToData(children);
 | 
			
		||||
      if (childData.length) {
 | 
			
		||||
        data.children = childData;
 | 
			
		||||
      const parsedChildren = dig(children);
 | 
			
		||||
      if (parsedChildren.length) {
 | 
			
		||||
        dataNode.children = parsedChildren;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      return data;
 | 
			
		||||
    })
 | 
			
		||||
    .filter(data => data);
 | 
			
		||||
      return dataNode;
 | 
			
		||||
    });
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return dig(rootNodes as any[]);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export function fillLegacyProps(dataNode: DataNode): LegacyDataNode {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -96,8 +96,8 @@ export default defineComponent({
 | 
			
		|||
  props: nodeListProps,
 | 
			
		||||
  setup(props, { expose, attrs }) {
 | 
			
		||||
    // =============================== Ref ================================
 | 
			
		||||
    const listRef = ref(null);
 | 
			
		||||
    const indentMeasurerRef = ref(null);
 | 
			
		||||
    const listRef = ref();
 | 
			
		||||
    const indentMeasurerRef = ref();
 | 
			
		||||
    expose({
 | 
			
		||||
      scrollTo: scroll => {
 | 
			
		||||
        listRef.value.scrollTo(scroll);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -870,8 +870,8 @@ export default defineComponent({
 | 
			
		|||
        active: true,
 | 
			
		||||
      });
 | 
			
		||||
    });
 | 
			
		||||
    const onKeyDown = event => {
 | 
			
		||||
      const { onKeyDown, checkable, selectable } = props;
 | 
			
		||||
    const onKeydown = event => {
 | 
			
		||||
      const { onKeydown, checkable, selectable } = props;
 | 
			
		||||
 | 
			
		||||
      // >>>>>>>>>> Direction
 | 
			
		||||
      switch (event.which) {
 | 
			
		||||
| 
						 | 
				
			
			@ -943,13 +943,14 @@ export default defineComponent({
 | 
			
		|||
        }
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      if (onKeyDown) {
 | 
			
		||||
        onKeyDown(event);
 | 
			
		||||
      if (onKeydown) {
 | 
			
		||||
        onKeydown(event);
 | 
			
		||||
      }
 | 
			
		||||
    };
 | 
			
		||||
    expose({
 | 
			
		||||
      onNodeExpand,
 | 
			
		||||
      scrollTo,
 | 
			
		||||
      onKeydown,
 | 
			
		||||
    });
 | 
			
		||||
    onUnmounted(() => {
 | 
			
		||||
      window.removeEventListener('dragend', onWindowDragEnd);
 | 
			
		||||
| 
						 | 
				
			
			@ -1068,7 +1069,7 @@ export default defineComponent({
 | 
			
		|||
              activeItem={activeItem.value}
 | 
			
		||||
              onFocus={onFocus}
 | 
			
		||||
              onBlur={onBlur}
 | 
			
		||||
              onKeydown={onKeyDown}
 | 
			
		||||
              onKeydown={onKeydown}
 | 
			
		||||
              onActiveChange={onActiveChange}
 | 
			
		||||
              onListChangeStart={onListChangeStart}
 | 
			
		||||
              onListChangeEnd={onListChangeEnd}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -146,7 +146,7 @@ export const treeProps = () => ({
 | 
			
		|||
  },
 | 
			
		||||
  onFocus: { type: Function as PropType<(e: FocusEvent) => void> },
 | 
			
		||||
  onBlur: { type: Function as PropType<(e: FocusEvent) => void> },
 | 
			
		||||
  onKeyDown: { type: Function as PropType<EventHandlerNonNull> },
 | 
			
		||||
  onKeydown: { type: Function as PropType<EventHandlerNonNull> },
 | 
			
		||||
  onContextmenu: { type: Function as PropType<EventHandlerNonNull> },
 | 
			
		||||
  onClick: { type: Function as PropType<NodeMouseEventHandler> },
 | 
			
		||||
  onDblclick: { type: Function as PropType<NodeMouseEventHandler> },
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -12,6 +12,7 @@ import { getPosition, isTreeNode } from '../util';
 | 
			
		|||
import { warning } from '../../vc-util/warning';
 | 
			
		||||
import Omit from 'omit.js';
 | 
			
		||||
import type { VNodeChild } from 'vue';
 | 
			
		||||
import { camelize } from 'vue';
 | 
			
		||||
import type { TreeNodeProps } from '../props';
 | 
			
		||||
 | 
			
		||||
export function getKey(key: Key, pos: string) {
 | 
			
		||||
| 
						 | 
				
			
			@ -60,74 +61,58 @@ export function warningWithoutKey(treeData: DataNode[], fieldNames: FieldNames)
 | 
			
		|||
  dig(treeData);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const cacheStringFunction = (fn: (s: string) => string) => {
 | 
			
		||||
  const cache = Object.create(null);
 | 
			
		||||
  return (str: string) => {
 | 
			
		||||
    const hit = cache[str];
 | 
			
		||||
    return hit || (cache[str] = fn(str));
 | 
			
		||||
  };
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const camelizeRE = /-(\w)/g;
 | 
			
		||||
 | 
			
		||||
const camelize = cacheStringFunction((str: string) => {
 | 
			
		||||
  return str.replace(camelizeRE, (_, c) => (c ? c.toUpperCase() : ''));
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Convert `children` of Tree into `treeData` structure.
 | 
			
		||||
 */
 | 
			
		||||
export function convertTreeToData(rootNodes: VNodeChild): DataNode[] {
 | 
			
		||||
  function dig(node: VNodeChild = []): DataNode[] {
 | 
			
		||||
    const treeNodes = node as NodeElement[];
 | 
			
		||||
    return treeNodes
 | 
			
		||||
      .map(treeNode => {
 | 
			
		||||
        // Filter invalidate node
 | 
			
		||||
        if (!isTreeNode(treeNode)) {
 | 
			
		||||
          warning(!treeNode, 'Tree/TreeNode can only accept TreeNode as children.');
 | 
			
		||||
          return null;
 | 
			
		||||
        }
 | 
			
		||||
        const slots = (treeNode.children as any) || {};
 | 
			
		||||
        const key = treeNode.key as string | number;
 | 
			
		||||
        const props: any = {};
 | 
			
		||||
        for (const [k, v] of Object.entries(treeNode.props)) {
 | 
			
		||||
          props[camelize(k)] = v;
 | 
			
		||||
        }
 | 
			
		||||
        const { isLeaf, checkable, selectable, disabled, disableCheckbox } = props;
 | 
			
		||||
        // 默认值为 undefined
 | 
			
		||||
        const newProps = {
 | 
			
		||||
          isLeaf: isLeaf || isLeaf === '' || undefined,
 | 
			
		||||
          checkable: checkable || checkable === '' || undefined,
 | 
			
		||||
          selectable: selectable || selectable === '' || undefined,
 | 
			
		||||
          disabled: disabled || disabled === '' || undefined,
 | 
			
		||||
          disableCheckbox: disableCheckbox || disableCheckbox === '' || undefined,
 | 
			
		||||
        };
 | 
			
		||||
        const slotsProps = { ...props, ...newProps };
 | 
			
		||||
        const {
 | 
			
		||||
          title = slots.title?.(slotsProps),
 | 
			
		||||
          icon = slots.icon?.(slotsProps),
 | 
			
		||||
          switcherIcon = slots.switcherIcon?.(slotsProps),
 | 
			
		||||
          ...rest
 | 
			
		||||
        } = props;
 | 
			
		||||
        const children = slots.default?.();
 | 
			
		||||
        const dataNode: DataNode = {
 | 
			
		||||
          ...rest,
 | 
			
		||||
          title,
 | 
			
		||||
          icon,
 | 
			
		||||
          switcherIcon,
 | 
			
		||||
          key,
 | 
			
		||||
          isLeaf,
 | 
			
		||||
          ...newProps,
 | 
			
		||||
        };
 | 
			
		||||
    return treeNodes.map(treeNode => {
 | 
			
		||||
      // Filter invalidate node
 | 
			
		||||
      if (!isTreeNode(treeNode)) {
 | 
			
		||||
        warning(!treeNode, 'Tree/TreeNode can only accept TreeNode as children.');
 | 
			
		||||
        return null;
 | 
			
		||||
      }
 | 
			
		||||
      const slots = (treeNode.children as any) || {};
 | 
			
		||||
      const key = treeNode.key as string | number;
 | 
			
		||||
      const props: any = {};
 | 
			
		||||
      for (const [k, v] of Object.entries(treeNode.props)) {
 | 
			
		||||
        props[camelize(k)] = v;
 | 
			
		||||
      }
 | 
			
		||||
      const { isLeaf, checkable, selectable, disabled, disableCheckbox } = props;
 | 
			
		||||
      // 默认值为 undefined
 | 
			
		||||
      const newProps = {
 | 
			
		||||
        isLeaf: isLeaf || isLeaf === '' || undefined,
 | 
			
		||||
        checkable: checkable || checkable === '' || undefined,
 | 
			
		||||
        selectable: selectable || selectable === '' || undefined,
 | 
			
		||||
        disabled: disabled || disabled === '' || undefined,
 | 
			
		||||
        disableCheckbox: disableCheckbox || disableCheckbox === '' || undefined,
 | 
			
		||||
      };
 | 
			
		||||
      const slotsProps = { ...props, ...newProps };
 | 
			
		||||
      const {
 | 
			
		||||
        title = slots.title?.(slotsProps),
 | 
			
		||||
        icon = slots.icon?.(slotsProps),
 | 
			
		||||
        switcherIcon = slots.switcherIcon?.(slotsProps),
 | 
			
		||||
        ...rest
 | 
			
		||||
      } = props;
 | 
			
		||||
      const children = slots.default?.();
 | 
			
		||||
      const dataNode: DataNode = {
 | 
			
		||||
        ...rest,
 | 
			
		||||
        title,
 | 
			
		||||
        icon,
 | 
			
		||||
        switcherIcon,
 | 
			
		||||
        key,
 | 
			
		||||
        isLeaf,
 | 
			
		||||
        ...newProps,
 | 
			
		||||
      };
 | 
			
		||||
 | 
			
		||||
        const parsedChildren = dig(children);
 | 
			
		||||
        if (parsedChildren.length) {
 | 
			
		||||
          dataNode.children = parsedChildren;
 | 
			
		||||
        }
 | 
			
		||||
      const parsedChildren = dig(children);
 | 
			
		||||
      if (parsedChildren.length) {
 | 
			
		||||
        dataNode.children = parsedChildren;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
        return dataNode;
 | 
			
		||||
      })
 | 
			
		||||
      .filter((dataNode: DataNode) => dataNode);
 | 
			
		||||
      return dataNode;
 | 
			
		||||
    });
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return dig(rootNodes);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,64 +1,40 @@
 | 
			
		|||
<template>
 | 
			
		||||
  <a-tree-select
 | 
			
		||||
    v-model:value="value"
 | 
			
		||||
    show-search
 | 
			
		||||
    style="width: 100%"
 | 
			
		||||
    :dropdown-style="{ maxHeight: '400px', overflow: 'auto' }"
 | 
			
		||||
    :tree-data="treeData"
 | 
			
		||||
    placeholder="Please select"
 | 
			
		||||
    allow-clear
 | 
			
		||||
    multiple
 | 
			
		||||
    tree-default-expand-all
 | 
			
		||||
  >
 | 
			
		||||
    <template #title="{ key, value }">
 | 
			
		||||
      <span v-if="key === '0-0-1'" style="color: #08c">Child Node1 {{ value }}</span>
 | 
			
		||||
    </template>
 | 
			
		||||
    <a-tree-select-node value="parent 1" title="parent 1">
 | 
			
		||||
      <a-tree-select-node value="parent 1-0" title="parent 1-0">
 | 
			
		||||
        <a-tree-select-node value="leaf1" title="my leaf" />
 | 
			
		||||
        <a-tree-select-node value="leaf2" title="your leaf" />
 | 
			
		||||
      </a-tree-select-node>
 | 
			
		||||
      <a-tree-select-node value="parent 1-1" title="parent 1-1">
 | 
			
		||||
        <a-tree-select-node value="sss">
 | 
			
		||||
          <template #title><b style="color: #08c">sss</b></template>
 | 
			
		||||
        </a-tree-select-node>
 | 
			
		||||
      </a-tree-select-node>
 | 
			
		||||
    </a-tree-select-node>
 | 
			
		||||
  </a-tree-select>
 | 
			
		||||
</template>
 | 
			
		||||
<script lang="ts">
 | 
			
		||||
import { defineComponent, ref, watch } from 'vue';
 | 
			
		||||
 | 
			
		||||
interface TreeDataItem {
 | 
			
		||||
  value: string;
 | 
			
		||||
  key: string;
 | 
			
		||||
  title?: string;
 | 
			
		||||
  slots?: Record<string, string>;
 | 
			
		||||
  children?: TreeDataItem[];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const treeData: TreeDataItem[] = [
 | 
			
		||||
  {
 | 
			
		||||
    title: 'Node1',
 | 
			
		||||
    value: '0-0',
 | 
			
		||||
    key: '0-0',
 | 
			
		||||
    children: [
 | 
			
		||||
      {
 | 
			
		||||
        value: '0-0-1',
 | 
			
		||||
        key: '0-0-1',
 | 
			
		||||
        slots: {
 | 
			
		||||
          title: 'title1',
 | 
			
		||||
        },
 | 
			
		||||
      },
 | 
			
		||||
      {
 | 
			
		||||
        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>();
 | 
			
		||||
    const value = ref<string[]>([]);
 | 
			
		||||
 | 
			
		||||
    watch(value, () => {
 | 
			
		||||
      console.log(value.value);
 | 
			
		||||
      console.log('select', value.value);
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    return {
 | 
			
		||||
      value,
 | 
			
		||||
      treeData,
 | 
			
		||||
    };
 | 
			
		||||
  },
 | 
			
		||||
});
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue