diff --git a/src/components/data-entry/QueryItem/Cascader.stories.tsx b/src/components/data-entry/QueryItem/Cascader.stories.tsx index b0735e661..4b814e6ec 100644 --- a/src/components/data-entry/QueryItem/Cascader.stories.tsx +++ b/src/components/data-entry/QueryItem/Cascader.stories.tsx @@ -107,7 +107,7 @@ export const Icon: Story = { args: { placeholder: "QueryItem.ValueSelector.Cascader Icon", options: exampleOptions, - icon: 'events', + icon: 'event', }, } @@ -127,3 +127,13 @@ export const PreSelectedValue: Story = { onChange: async (values, _) => {console.log(values)}, }, } + +export const LoadData: Story = { + args: { + placeholder: 'QueryItem.ValueSelector.Cascader Load', + options: exampleOptions, + loadData: (value: string) => { + console.log(value) + }, + }, +} diff --git a/src/components/data-entry/QueryItem/Cascader.tsx b/src/components/data-entry/QueryItem/Cascader.tsx index bb19984fb..6a7aa20fe 100644 --- a/src/components/data-entry/QueryItem/Cascader.tsx +++ b/src/components/data-entry/QueryItem/Cascader.tsx @@ -1,6 +1,6 @@ import './query-item.css' import { GetProp } from 'antd' -import { ReactNode, useEffect, useState } from 'react' +import { useCallback, useEffect, useState } from 'react' import { Cascader as BaseCascader, Flex, @@ -10,43 +10,58 @@ import { Icon, } from 'src/components' import { Icons } from 'src/constants/Icons' +import { debounce } from 'src/utils/utils' -export interface CascaderOption { +export interface ICascaderOption { value: string label: string - children?: CascaderOption[] + children?: ICascaderOption[] disabled?: boolean } export interface ICascaderProps { - options: CascaderOption[] - icon?: keyof Pick + options: ICascaderOption[] + icon?: keyof Pick errorMessage?: string placeholder?: string onChange?: (values: (number | string)[], selectedOptions: any) => Promise + loadData?: (value: string) => void value?: (number | string)[] } export const Cascader = (props: ICascaderProps) => { type DefaultOptionType = GetProp[number] - const options: CascaderOption[] = [] + const options: ICascaderOption[] = [] const [items, setItems] = useState(props.options ?? options) const [searchValue, setSearchValue] = useState('') const [selectedValue, setSelectedValue] = useState<(number | string)[]>(props.value ?? []) - const [selectedDisplayValue, setSelectedDisplayValue] = useState("") + const [selectedDisplayValue, setSelectedDisplayValue] = useState(props.value ? (props.value.slice(-1)[0] as any).label : "") const [isOpen, setIsOpen] = useState(false) useEffect(() => { setItems(props.options) }, [props.options]) - const onSearch = (value: string) => { + const onSearch = ({ target: { value: value}}: { target: { value: string}}) => { + if (debouncedLoadData) { + if (value.length > 3) { + if (transformOptionsToPaths(items,[]).filter((path) => filter(value, path)).length == 0) { + debouncedLoadData(value) + } + } + } setSearchValue(value) } - const filter = (inputValue: string, path: DefaultOptionType[]) => - path.some(option => (option.label as string).toLowerCase().indexOf(inputValue.toLowerCase()) > -1) + const filter = (inputValue: string, path: DefaultOptionType[]) => { + return path.some(option => (option.label as string).toLowerCase().indexOf(inputValue.toLowerCase()) > -1) + } + + let debouncedLoadData: (value: string) => void; + if (props.loadData) { + debouncedLoadData = useCallback(debounce(props.loadData, 500),[]) + } const baseProps: IBaseCascaderProps = { getPopupContainer: triggerNode => triggerNode.parentElement, @@ -61,7 +76,7 @@ export const Cascader = (props: ICascaderProps) => { }, dropdownRender: menu => (
- + {menu}
), @@ -90,4 +105,18 @@ export const Cascader = (props: ICascaderProps) => { {props.errorMessage && {props.errorMessage}} ) + + function transformOptionsToPaths(options: DefaultOptionType[], prefixPath: DefaultOptionType[]): DefaultOptionType[][] { + let result: DefaultOptionType[][] = []; + options.forEach((option) => { + if (option.children && option.children.length > 0) { + const newPrefix = prefixPath.concat([{label: option.label, value: option.value}]); + result = result.concat(transformOptionsToPaths(option.children, newPrefix)); + } else { + const path = prefixPath.concat([{label: option.label, value: option.value}]); + result.push(path); + } + }) + return result; + } }