acud
Version:
acg react 组件库
158 lines (157 loc) • 7.14 kB
TypeScript
/**
* To match accessibility requirement, we always provide an input in the component.
* 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:
* - keyboard: https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Roles/listbox_role#Keyboard_interactions
*/
import * as React from 'react';
import { ScrollTo } from '../../virtual-list/List';
import { RenderNode, Mode, RenderDOMFunc } from './interface';
import { GetLabeledValue, FilterOptions, FilterFunc, DefaultValueType, RawValueType, LabelValueType, Key, ExpandKey, DefaultExpandKey, FlattenOptionsType, SingleType, OnClear, SelectSource, CustomTagProps } from './interface/generator';
import { OptionProps } from './Option';
import { OptionListProps, RefOptionListProps } from './OptionList';
export interface RefSelectProps {
focus: () => void;
blur: () => void;
scrollTo?: ScrollTo;
}
export interface SelectProps<OptionsType extends object[], ValueType> extends React.AriaAttributes {
prefixCls?: string;
id?: string;
className?: string;
style?: React.CSSProperties;
options?: OptionsType;
children?: React.ReactNode;
mode?: Mode;
value?: ValueType;
defaultValue?: ValueType;
labelInValue?: boolean;
/** Config max length of input. This is only work when `mode` is `combobox` */
maxLength?: number;
inputValue?: string;
searchValue?: string;
optionFilterProp?: string;
/**
* In Select, `false` means do nothing.
* In TreeSelect, `false` will highlight match item.
* It's by design.
*/
filterOption?: boolean | FilterFunc<OptionsType[number]>;
filterSort?: (optionA: OptionsType[number], optionB: OptionsType[number]) => number;
showSearch?: boolean;
autoClearSearchValue?: boolean;
onSearch?: (value: string) => void;
onClear?: OnClear;
suffixIconPre?: React.ReactNode;
allowClear?: boolean;
clearIcon?: React.ReactNode | ((props: any) => React.ReactNode);
showArrow?: boolean;
showTitle?: boolean;
showSelectAll?: boolean;
inputIcon?: RenderNode;
removeIcon?: React.ReactNode;
menuItemSelectedIcon?: RenderNode;
showAllSelected?: boolean;
open?: boolean;
defaultOpen?: boolean;
listHeight?: number;
listItemHeight?: number;
dropdownStyle?: React.CSSProperties;
dropdownClassName?: string;
dropdownMatchSelectWidth?: boolean | number;
virtual?: boolean;
dropdownRender?: (menu: React.ReactElement) => React.ReactElement;
dropdownAlign?: any;
animation?: string;
transitionName?: string;
getPopupContainer?: RenderDOMFunc;
direction?: string;
disabled?: boolean;
loading?: boolean;
autoFocus?: boolean;
defaultActiveFirstOption?: boolean;
notFoundContent?: React.ReactNode;
placeholder?: React.ReactNode;
placement?: 'bottomLeft' | 'bottomRight' | 'topLeft' | 'topRight';
backfill?: boolean;
getInputElement?: () => JSX.Element;
optionLabelProp?: string;
maxTagTextLength?: number;
maxTagCount?: number | 'responsive';
maxTagPlaceholder?: React.ReactNode | ((omittedValues: LabelValueType[]) => React.ReactNode);
tokenSeparators?: string[];
groupSelectorRender?: (groupLabel: string, value: string | number) => React.ReactElement;
tagRender?: (props: CustomTagProps) => React.ReactElement;
optionRender?: (label: React.ReactNode, option?: OptionProps) => React.ReactNode;
showAction?: ('focus' | 'click')[];
tabIndex?: number;
renderEmpty?: (componentName?: string, des?: string | undefined) => React.ReactNode;
keepExpand?: boolean;
defaultExpandGroupKey?: DefaultExpandKey;
popupOverflow?: 'viewport' | 'scroll';
onKeyUp?: React.KeyboardEventHandler<HTMLDivElement>;
onKeyDown?: React.KeyboardEventHandler<HTMLDivElement>;
onPopupScroll?: React.UIEventHandler<HTMLDivElement>;
onDropdownVisibleChange?: (open: boolean) => void;
onSelect?: (value: SingleType<ValueType>, option: OptionsType[number]) => void;
onDeselect?: (value: SingleType<ValueType>, option: OptionsType[number]) => void;
onInputKeyDown?: React.KeyboardEventHandler<HTMLInputElement>;
onClick?: React.MouseEventHandler;
onChange?: (value: ValueType, option: OptionsType[number] | OptionsType) => void;
onBlur?: React.FocusEventHandler<HTMLElement>;
onFocus?: React.FocusEventHandler<HTMLElement>;
onMouseDown?: React.MouseEventHandler<HTMLDivElement>;
onMouseEnter?: React.MouseEventHandler<HTMLDivElement>;
onMouseLeave?: React.MouseEventHandler<HTMLDivElement>;
onExpandChange?: (key: ExpandKey) => void;
choiceTransitionName?: string;
/**
* Only used in current version for internal event process.
* Do not use in production environment.
*/
internalProps?: {
mark?: string;
onClear?: OnClear;
skipTriggerChange?: boolean;
skipTriggerSelect?: boolean;
onRawSelect?: (value: RawValueType, option: OptionsType[number], source: SelectSource) => void;
onRawDeselect?: (value: RawValueType, option: OptionsType[number], source: SelectSource) => void;
};
}
export interface GenerateConfig<OptionsType extends object[]> {
prefixCls: string;
components: {
optionList: React.ForwardRefExoticComponent<React.PropsWithoutRef<Omit<OptionListProps<OptionsType>, 'options'> & {
options: OptionsType;
}> & React.RefAttributes<RefOptionListProps>>;
};
/** Convert jsx tree into `OptionsType` */
convertChildrenToData: (children: React.ReactNode) => OptionsType;
/** Flatten nest options into raw option list */
flattenOptions: (options: OptionsType, props: any) => FlattenOptionsType<OptionsType>;
/** Convert single raw value into { label, value } format. Will be called by each value */
getLabeledValue: GetLabeledValue<FlattenOptionsType<OptionsType>>;
filterOptions: FilterOptions<OptionsType>;
findValueOption: ((values: RawValueType[], options: FlattenOptionsType<OptionsType>) => OptionsType) | ((values: RawValueType[], options: FlattenOptionsType<OptionsType>, info?: {
prevValueOptions?: OptionsType[];
}) => OptionsType);
/** Check if a value is disabled */
isValueDisabled: (value: RawValueType, options: FlattenOptionsType<OptionsType>) => boolean;
warningProps?: (props: any) => void;
fillOptionsWithMissingValue?: (options: OptionsType, value: DefaultValueType, optionLabelProp: string, labelInValue: boolean) => OptionsType;
omitDOMProps?: (props: object) => {
[index: string]: any;
};
}
/**
* This function is in internal usage.
* Do not use it in your prod env since we may refactor this.
*/
export default function generateSelector<OptionsType extends {
value?: RawValueType;
label?: React.ReactNode;
key?: Key;
disabled?: boolean;
}[]>(config: GenerateConfig<OptionsType>): React.ForwardRefExoticComponent<SelectProps<OptionsType, DefaultValueType> & React.RefAttributes<RefSelectProps>>;