AntD Select + AutoComplete


簡單介紹一下 AntD Select 的用法

mode: undefined, tag, multiple

  • undefine: 默認值,就是dropdown,
  • tag: 跟multple 唯一的區別就是,輸入的值不在list 內,敲回車,自動插入.
  • multiple, 就是多值選擇。

一些約定

  • showSearch: 看這段代碼 const mergedShowSearch = showSearch !== undefined ? showSearch : isMultiple || mode === 'combobox'; 結論: 如果顯示設置,優先級最高,隱含的其次,也就是 tags, multiple, combobox 也是可以showSearch.
    • 類似的開關還有很多,例如: showArrow 在其他的組件中,這個約定也是類似的。當然這些開關只是第一層,開關,默認有這個開關就可以打開,但是也有一些相關的屬性進一步控制這個開關,例如 filterOption=false 就可以關閉search的功能,這個在SearchBox 或者Email 這種組件中使用。例如以下代碼,盡管,我們打開了showSearch 開關,但是我們通過filterOption={false}關閉了該功能,我們通過onSearchevent來刷新options
const options = this.state.data.map(d => <Option key={d.value}>{d.text}</Option>);
<Select
        showSearch
        value={this.state.value}
        placeholder={this.props.placeholder}
        showArrow={false}
        filterOption={false}
        onSearch={this.handleSearch}
        onChange={this.handleChange}
        notFoundContent={null}
      >
        {options}
      </Select>

談談 Select 里面的animation.

首先,我們可以通過transitionName 參數來提供一個transtionName, 看這段代碼:transitionName={getTransitionName(rootPrefixCls, 'slide-up', props.transitionName)}, 如果不提供,默認就是slide-up 這個transitionName,
第一個問題,這個transtionName 有神馬用途,或者,用它會發生什么,首先它會傳遞個CSSMotion ,如果有同學對CSSMotion 不理解,參考CSSMotion.
簡單說一下,下來菜單在展開或者關閉的過程,className 會如此變化。

  • 展開
    • .ant-slide-up-enter(Dom 已經存在) 或者 .ant-slide-up-appear(Dom 剛剛創建)
    • .ant-slide-up-enter-(prepare/start/active) 依次發生變化, 一般我們只關心start 與active
    • 最后動畫結束,與動畫相關的類名全部移除。
  • 關閉
    • .ant-slide-leave
    • .ant-slide-up-leave-(parepare/start/active) 依次發生,同樣,我們只關心start 跟active
    • 最后動畫結束,與動畫相關的類名全部移除,附加上ant-select-dropdown-hidden 類.

下面是slide-up 相關的css ,變化的就是opacitytransform 兩個屬性

.motion-common(@duration: @animation-duration-base) {
  animation-duration: @duration;
  animation-fill-mode: both;
}

.motion-common-leave(@duration: @animation-duration-base) {
  animation-duration: @duration;
  animation-fill-mode: both;
}

.make-motion(@className, @keyframeName, @duration: @animation-duration-base) {
  .@{className}-enter,
  .@{className}-appear {
    .motion-common(@duration);

    animation-play-state: paused;
  }
  .@{className}-leave {
    .motion-common-leave(@duration);

    animation-play-state: paused;
  }
  .@{className}-enter.@{className}-enter-active,
  .@{className}-appear.@{className}-appear-active {
    animation-name: ~'@{keyframeName}In';
    animation-play-state: running;
  }
  .@{className}-leave.@{className}-leave-active {
    animation-name: ~'@{keyframeName}Out';
    animation-play-state: running;
    pointer-events: none;
  }
}


.slide-motion(@className, @keyframeName) {
  @name: ~'@{ant-prefix}-@{className}';
  .make-motion(@name, @keyframeName);
  .@{name}-enter,
  .@{name}-appear {
    opacity: 0;
    animation-timing-function: @ease-out-quint;
  }
  .@{name}-leave {
    animation-timing-function: @ease-in-quint;
  }
}

.slide-motion(slide-up, antSlideUp);
@keyframes antSlideUpIn {
  0% {
    transform: scaleY(0.8);
    transform-origin: 0% 0%;
    opacity: 0;
  }
  100% {
    transform: scaleY(1);
    transform-origin: 0% 0%;
    opacity: 1;
  }
}

談談Select 里面的Option OptionGroup

首先,Rc-Select 里面的Option 類型是 export type OptionsType = (OptionData | OptionGroupData)[]; 我們來看一下代碼片段。

export interface OptionCoreData {
  key?: Key;
  disabled?: boolean;
  value: Key;
  title?: string;
  className?: string;
  style?: React.CSSProperties;
  label?: React.ReactNode;
  /** @deprecated Only works when use `children` as option data */
  children?: React.ReactNode;
}

export interface OptionData extends OptionCoreData {
  /** Save for customize data */
  [prop: string]: any; // eslint-disable-line @typescript-eslint/no-explicit-any
}

export interface OptionGroupData {
  key?: Key;
  label?: React.ReactNode;
  options: OptionData[];
  className?: string;
  style?: React.CSSProperties;

  /** Save for customize data */
  [prop: string]: any; // eslint-disable-line @typescript-eslint/no-explicit-any
}

export type OptionsType = (OptionData | OptionGroupData)[];

同時Select 暴露出了Option, OptGroup 來用於提供Options, 常用於AntD Select. 看一下OptGroup類型定義. 它沒有Options 屬性。Options 直接來源於children.

export interface OptGroupProps extends Omit<OptionGroupData, 'options'> {
  children?: React.ReactNode;
}

export interface OptionGroupFC extends React.FC<OptGroupProps> {
  /** Legacy for check if is a Option Group */
  isSelectOptGroup: boolean;
}

const OptGroup: OptionGroupFC = () => null;
OptGroup.isSelectOptGroup = true;

export default OptGroup;

這個是Option的定義,它沒有label這個屬性,label 直接來源於children.

export interface OptionProps extends Omit<OptionCoreData, 'label'> {
  children: React.ReactNode;

  /** Save for customize data */
  [prop: string]: any; // eslint-disable-line @typescript-eslint/no-explicit-any
}

export interface OptionFC extends React.FC<OptionProps> {
  /** Legacy for check if is a Option Group */
  isSelectOption: boolean;
}

/** This is a placeholder, not real render in dom */
const Option: OptionFC = () => null;
Option.isSelectOption = true;

export default Option;

對於AutoComplete 這個組件,它也是基於Select 來做的,它的Options 是通過Options這個屬性來提供的,它的代碼片段如下

export interface DataSourceItemObject {
  value: string;
  text: string;
}
export type DataSourceItemType = DataSourceItemObject | React.ReactNode;

export interface AutoCompleteProps
  extends Omit<
    InternalSelectProps<string>,
    'inputIcon' | 'loading' | 'mode' | 'optionLabelProp' | 'labelInValue'
  > {
  dataSource?: DataSourceItemType[];
}

那么對於分組這種情況,它的Options 應該張這樣

[
    {label:xxx,options:[xxx,xxx,...]},
    {label:xxx,options:[xxx,xxx,...]},
    {label:xxx,options:[xxx,xxx,...]},
    {label:xxx,options:[xxx,xxx,...]},
]

對於未分組的情況,它的Options 應該長這樣

[
    {label:xx,value:xxx,key:xxx},
    {label:xx,value:xxx,key:xxx},
    {label:xx,value:xxx,key:xxx},
    {label:xx,value:xxx,key:xxx},
    ...
]

在Rc-Select/utils/valueUtil.tsx 里面有這個函數flattenOptions 來解析以上這些數據結構.

export function flattenOptions(options: SelectOptionsType): FlattenOptionData[] {
  const flattenList: FlattenOptionData[] = [];

  function dig(list: SelectOptionsType, isGroupOption: boolean) {
    list.forEach((data) => {
      if (isGroupOption || !('options' in data)) {
        // Option
        flattenList.push({
          key: getKey(data, flattenList.length),
          groupOption: isGroupOption,
          data,
        });
      } else {
        // Option Group
        flattenList.push({
          key: getKey(data, flattenList.length),
          group: true,
          data,
        });

        dig(data.options, true);
      }
    });
  }

  dig(options, false);

  return flattenList;
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM