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, options: Ref, fieldNames: Ref, prefixCls: Ref, config: Ref, changeOnSelect: Ref, ) => { 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 || children.length === 0 || // 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; }); };