diff --git a/packages/react-cascader/README.md b/packages/react-cascader/README.md
index 42fdff3c91..8429d8cd3c 100644
--- a/packages/react-cascader/README.md
+++ b/packages/react-cascader/README.md
@@ -57,7 +57,71 @@ const Demo = () => {
return (
- console.log(value, seleteds)} />
+ < Cascader
+ allowClear={true}
+ placeholder="请选择"
+ value={[1, 4, 7]}
+ option={options}
+ onChange={(value, seleteds) => console.log(value, seleteds)}
+ />
+
+ )
+};
+ReactDOM.render(, _mount_);
+```
+
+## 搜索选项
+
+
+```jsx
+import ReactDOM from 'react-dom';
+import { Cascader } from 'uiw';
+
+const Demo = () => {
+
+ const options = [
+ {
+ label: '上海市',
+ value: 1,
+ children: [
+ {
+ label: '徐汇区',
+ value: 4,
+ children: [
+ { label: '半淞园路街道', value: 6 },
+ { label: '南京东路街道', value: 7 },
+ { label: '外滩街道', value: 8 },
+ ]
+ },
+ ]
+ },
+ {
+ label: '北京市',
+ value: 9,
+ children: [
+ {
+ label: '崇文区',
+ value: 12,
+ children: [
+ { label: '东花市街道', value: 13 },
+ { label: '体育馆路街道', value: 14 },
+ { label: '前门街道', value: 15 },
+ ]
+ },
+ ]
+ },
+ ];
+
+ return (
+
+ console.log(value, seleteds)}
+ onSearch={(text)=> console.log('text', text)}
+ />
)
};
@@ -138,6 +202,7 @@ const options = [
children: (
console.log(value, seleteds)}
@@ -183,3 +248,4 @@ ReactDOM.render(, _mount_);
| option | 选项菜单 | { value: String \| Number, label: React.ReactNode, children: Array} | - | - |
| value | 指定当前选中的条目,多选时为一个数组 | String[] \| Number[] | - | - |
| onChange | 选中选项调用此函数 | function( isSeleted, value, selectedOptions) | - | - |
+| onSearch | 开启搜索选项 | functionon(searchText) \| Boolean | - | v4.16.1 |
diff --git a/packages/react-cascader/src/index.tsx b/packages/react-cascader/src/index.tsx
index eaa79b2dc3..0e4b2d92e5 100644
--- a/packages/react-cascader/src/index.tsx
+++ b/packages/react-cascader/src/index.tsx
@@ -8,20 +8,22 @@ import './style/index.less';
type ValueType = Array;
type OptionType = { value: string | number; label: React.ReactNode; children?: Array };
+type SearchOptionType = { label: string; options: Array };
export interface CascaderProps extends IProps, DropdownProps {
option?: Array;
value?: ValueType;
onChange?: (isSeleted: boolean, value: ValueType, selectedOptions: Array) => void;
+ onSearch?: boolean | ((searchText: string) => void);
allowClear?: boolean;
placeholder?: string;
- isOpen?: boolean;
}
function Cascader(props: CascaderProps) {
const {
value,
onChange,
+ onSearch,
allowClear,
placeholder,
@@ -36,16 +38,48 @@ function Cascader(props: CascaderProps) {
const [innerIsOpen, setInnerIsOpen] = useState(false);
const [selectedValue, setSelectedValue] = useState>([]);
const [selectIconType, setSelectIconType] = useState('');
+ const [searchText, setSearchText] = useState('');
+ const [searchOn, setSearchOn] = useState(false);
+ const [inputValue, setInputValue] = useState('');
+ const [searchOption, setSearchOption] = useState>();
useEffect(() => {
- const valueTemp: Array = [];
- let optChildren = option;
- value?.map((item) => {
- const findOpt = optChildren.find((opt) => opt.value === item);
- optChildren = findOpt?.children || [];
- valueTemp.push({ label: item, value: item, ...findOpt });
+ if (onSearch) {
+ const tempOptions: Array = [];
+ iteratorOption(option, (opt) => {
+ const label = opt.map((m) => m.label).join(' / ');
+ tempOptions.push({ label, options: opt });
+ });
+ setSearchOption(tempOptions);
+ }
+ }, [onSearch]);
+
+ const iteratorOption = (
+ options: Array,
+ cb: (opt: Array) => void,
+ opts: Array = [],
+ ) => {
+ options.map((opt) => {
+ const optsTemp = [...opts, opt];
+ if (opt.children) {
+ iteratorOption(opt.children, cb, optsTemp);
+ } else {
+ cb?.(optsTemp);
+ }
});
- setSelectedValue(valueTemp);
+ };
+
+ useEffect(() => {
+ if (value) {
+ const valueTemp: Array = [];
+ let optChildren = option;
+ value?.map((item) => {
+ const findOpt = optChildren.find((opt) => opt.value === item);
+ optChildren = findOpt?.children || [];
+ valueTemp.push({ label: item, value: item, ...findOpt });
+ });
+ setSelectedValue(valueTemp);
+ }
}, [value]);
function onVisibleChange(isOpen: boolean) {
@@ -62,6 +96,12 @@ function Cascader(props: CascaderProps) {
setSelectIconType(selectIconType);
}
+ const searchItemClick = (options: Array) => {
+ setSearchText('');
+ setInnerIsOpen(false);
+ handelChange(false, options);
+ };
+
const handleItemClick = (optionsItem: OptionType, level: number) => {
selectedValue.splice(level, selectedValue.length - level, optionsItem);
@@ -76,10 +116,21 @@ function Cascader(props: CascaderProps) {
const onClear = (e: React.MouseEvent) => {
e.stopPropagation();
- console.log(123);
handelChange(false, []);
};
+ const handelSearch = (searchText: string) => {
+ setSearchText(searchText);
+ };
+
+ const inputChange = (e: React.ChangeEvent) => {
+ if (!innerIsOpen) {
+ setInnerIsOpen(!innerIsOpen);
+ }
+ const value = e.target.value;
+ onSearch && handelSearch(value);
+ };
+
const widths = (style?.width as number) * 0.5 || undefined;
const OptionIter = (option: Array, level: number = 0) => {
@@ -116,9 +167,10 @@ function Cascader(props: CascaderProps) {
) as JSX.Element;
};
- const inputValue = useMemo(() => {
- return selectedValue.map((opt) => opt.label).join(' / ');
- }, [selectedValue.length]);
+ useEffect(() => {
+ const inputValue = selectedValue.map((opt) => opt.label).join(' / ');
+ setInputValue(inputValue);
+ }, [selectedValue]);
return (
- {new Array(selectedValue.length + 1)
- .fill(0)
- .map((_, index) => {
- const options = index ? selectedValue[index - 1]?.children! : option;
- return OptionIter(options, index);
- })
- .filter((m) => !!m)}
-
+ !searchText ? (
+
+ {new Array(selectedValue.length + 1)
+ .fill(0)
+ .map((_, index) => {
+ const options = index ? selectedValue[index - 1]?.children! : option;
+ return OptionIter(options, index);
+ })
+ .filter((m) => !!m)}
+
+ ) : (
+
+ )
}
>
renderSelectIcon('leave')} onMouseOver={() => renderSelectIcon('enter')}>
{}}
- placeholder={placeholder}
+ value={searchOn ? searchText : inputValue}
+ onChange={inputChange}
+ placeholder={searchOn ? inputValue : placeholder}
style={{ width: style?.width }}
- readOnly
+ onFocus={() => onSearch && setSearchOn(true)}
+ onBlur={() => onSearch && setSearchOn(false)}
+ readOnly={!onSearch}
addonAfter={
- selectIconType === 'close' && (
-
- )
+
+ {selectIconType === 'close' && (
+
+ )}
+
}
/>