163 lines
5.1 KiB
Vue
163 lines
5.1 KiB
Vue
/**
|
|
* 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
|
|
*
|
|
* New api:
|
|
* - listHeight
|
|
* - listItemHeight
|
|
* - component
|
|
*
|
|
* Remove deprecated api:
|
|
* - multiple
|
|
* - tags
|
|
* - combobox
|
|
* - firstActiveValue
|
|
* - dropdownMenuStyle
|
|
* - openClassName (Not list in api)
|
|
*
|
|
* Update:
|
|
* - `backfill` only support `combobox` mode
|
|
* - `combobox` mode not support `labelInValue` since it's meaningless
|
|
* - `getInputElement` only support `combobox` mode
|
|
* - `onChange` return OptionData instead of ReactNode
|
|
* - `filterOption` `onChange` `onSelect` accept OptionData instead of ReactNode
|
|
* - `combobox` mode trigger `onChange` will get `undefined` if no `value` match in Option
|
|
* - `combobox` mode not support `optionLabelProp`
|
|
*/
|
|
|
|
import BaseSelect, { baseSelectPropsWithoutPrivate, isMultiple } from './BaseSelect';
|
|
import type {
|
|
DisplayValueType,
|
|
RenderNode,
|
|
BaseSelectRef,
|
|
BaseSelectPropsWithoutPrivate,
|
|
BaseSelectProps,
|
|
} from './BaseSelect';
|
|
import OptionList from './OptionList';
|
|
import Option from './Option';
|
|
import OptGroup from './OptGroup';
|
|
import useOptions from './hooks/useOptions';
|
|
import SelectContext from './SelectContext';
|
|
import useId from './hooks/useId';
|
|
import useRefFunc from './hooks/useRefFunc';
|
|
import { fillFieldNames, flattenOptions, injectPropsWithOption } from './utils/valueUtil';
|
|
import warningProps from './utils/warningPropsUtil';
|
|
import { toArray } from './utils/commonUtil';
|
|
import useFilterOptions from './hooks/useFilterOptions';
|
|
import useCache from './hooks/useCache';
|
|
import type { Key } from '../_util/type';
|
|
import { defineComponent } from 'vue';
|
|
import type { ExtractPropTypes, PropType } from 'vue';
|
|
import PropTypes from '../_util/vue-types';
|
|
|
|
const OMIT_DOM_PROPS = ['inputValue'];
|
|
|
|
export type OnActiveValue = (
|
|
active: RawValueType,
|
|
index: number,
|
|
info?: { source?: 'keyboard' | 'mouse' },
|
|
) => void;
|
|
|
|
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;
|
|
}
|
|
|
|
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,
|
|
children: PropTypes.any,
|
|
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>,
|
|
};
|
|
}
|
|
|
|
export type SelectProps = Partial<ExtractPropTypes<ReturnType<typeof selectProps>>>;
|
|
|
|
export default defineComponent({});
|