From 59ac0a2f968d064355541fdcdfca1aebb6afe2f2 Mon Sep 17 00:00:00 2001 From: Hoa Phan Date: Fri, 3 May 2024 20:59:40 +0700 Subject: [PATCH] feat: add props excludeItems, excludeSearchItems --- README.md | 4 ++ example/src/dropdown/Dropdown1.tsx | 6 +++ example/src/dropdown/MultiSelectAll.tsx | 6 +++ src/components/Dropdown/index.tsx | 54 ++++++++++++++++++++++--- src/components/Dropdown/model.ts | 2 + src/components/MultiSelect/index.tsx | 54 ++++++++++++++++++++++--- src/components/MultiSelect/model.ts | 2 + 7 files changed, 118 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index 4e38b06..69a6537 100644 --- a/README.md +++ b/README.md @@ -86,6 +86,8 @@ yarn add react-native-element-dropdown | accessibilityLabel | String | No | Set an accessibilityLabel on the view, so that people who use VoiceOver know what element they have selected | | itemAccessibilityLabelField | String | No | Add this field to the input data. Ex: DATA = [{itemAccessibilityLabelField: '', label: '', value:: ''}]| | closeModalWhenSelectedItem | Boolean | No | By default, closeModalWhenSelectedItem is set to true. When closeModalWhenSelectedItem is set to false, the Modal won't close when an item is selected. | +| excludeItems | Item[] | No | The array containing the items to be excluded. | +| excludeSearchItems | Item[] | No | The array containing the items to be excluded. | @@ -143,6 +145,8 @@ yarn add react-native-element-dropdown | itemTestIDField | String | No | Add this field to the input data. Ex: DATA = [{itemTestIDField: '', label: '', value:: ''}]| | accessibilityLabel | String | No | Set an accessibilityLabel on the view, so that people who use VoiceOver know what element they have selected | | itemAccessibilityLabelField | String | No | Add this field to the input data. Ex: DATA = [{itemAccessibilityLabelField: '', label: '', value:: ''}]| +| excludeItems | Item[] | No | The array containing the items to be excluded. | +| excludeSearchItems | Item[] | No | The array containing the items to be excluded. | diff --git a/example/src/dropdown/Dropdown1.tsx b/example/src/dropdown/Dropdown1.tsx index e50624a..9b95a8a 100644 --- a/example/src/dropdown/Dropdown1.tsx +++ b/example/src/dropdown/Dropdown1.tsx @@ -15,6 +15,11 @@ const data = [ { label: 'Item 8', value: '8', search: 'Item 8' }, ]; +const excludeItem = [ + { label: 'Item 7', value: '7', search: 'Item 7' }, + { label: 'Item 8', value: '8', search: 'Item 8' }, +]; + const DropdownComponent = () => { const [value, setValue] = useState(); const [isFocus, setIsFocus] = useState(false); @@ -40,6 +45,7 @@ const DropdownComponent = () => { inputSearchStyle={styles.inputSearchStyle} iconStyle={styles.iconStyle} data={data} + excludeSearchItems={excludeItem} autoScroll search maxHeight={300} diff --git a/example/src/dropdown/MultiSelectAll.tsx b/example/src/dropdown/MultiSelectAll.tsx index f0a59fa..a8ce200 100644 --- a/example/src/dropdown/MultiSelectAll.tsx +++ b/example/src/dropdown/MultiSelectAll.tsx @@ -13,6 +13,11 @@ const data = [ { label: 'Item 8', value: '8' }, ]; +const excludeItem = [ + { label: 'Item 7', value: '7', search: 'Item 7' }, + { label: 'Item 8', value: '8', search: 'Item 8' }, +]; + const MultiSelectComponent = () => { const [selected, setSelected] = useState([]); const ref = useRef(null); @@ -54,6 +59,7 @@ const MultiSelectComponent = () => { backgroundColor={'rgba(0,0,0,0.2)'} search data={data} + excludeItems={excludeItem} labelField="label" valueField="value" placeholder="Multiselect All" diff --git a/src/components/Dropdown/index.tsx b/src/components/Dropdown/index.tsx index f25c653..eef6ed8 100644 --- a/src/components/Dropdown/index.tsx +++ b/src/components/Dropdown/index.tsx @@ -1,3 +1,4 @@ +/* eslint-disable @typescript-eslint/no-shadow */ import _ from 'lodash'; import React, { JSXElementConstructor, @@ -89,6 +90,8 @@ const DropdownComponent: ( itemAccessibilityLabelField, mode = 'default', closeModalWhenSelectedItem = true, + excludeItems = [], + excludeSearchItems = [], } = props; const ref = useRef(null); @@ -123,8 +126,25 @@ const DropdownComponent: ( // eslint-disable-next-line react-hooks/exhaustive-deps }, []); + const excludeData = useCallback( + (data: any[]) => { + if (excludeItems.length > 0) { + const getData = _.differenceWith( + data, + excludeItems, + (obj1, obj2) => _.get(obj1, valueField) === _.get(obj2, valueField) + ); + return getData || []; + } else { + return data || []; + } + }, + [excludeItems, valueField] + ); + useEffect(() => { - setListData([...data]); + const filterData = excludeData(data); + setListData([...filterData]); if (searchText) { onSearch(searchText); } @@ -277,7 +297,8 @@ const DropdownComponent: ( _measure(); setVisible(!visible); - setListData(data); + const filterData = excludeData(data); + setListData(filterData); if (!visible) { if (onFocus) { @@ -333,12 +354,35 @@ const DropdownComponent: ( const dataSearch = data.filter( searchQuery ? propSearchFunction : defaultFilterFunction ); - setListData(dataSearch); + + if (excludeSearchItems.length > 0 || excludeItems.length > 0) { + const excludeSearchData = _.differenceWith( + dataSearch, + excludeSearchItems, + (obj1, obj2) => + _.get(obj1, valueField) === _.get(obj2, valueField) + ); + + const filterData = excludeData(excludeSearchData); + setListData(filterData); + } else { + setListData(dataSearch); + } } else { - setListData(data); + const filterData = excludeData(data); + setListData(filterData); } }, - [data, searchField, labelField, searchQuery] + [ + data, + searchQuery, + excludeSearchItems, + excludeItems, + searchField, + labelField, + valueField, + excludeData, + ] ); const onSelect = useCallback( diff --git a/src/components/Dropdown/model.ts b/src/components/Dropdown/model.ts index a48f386..0e029b2 100644 --- a/src/components/Dropdown/model.ts +++ b/src/components/Dropdown/model.ts @@ -55,6 +55,8 @@ export interface DropdownProps { inverted?: boolean; mode?: 'default' | 'modal' | 'auto'; closeModalWhenSelectedItem?: boolean; + excludeItems?: T[]; + excludeSearchItems?: T[]; onChange: (item: T) => void; renderLeftIcon?: (visible?: boolean) => JSX.Element | null | undefined; renderRightIcon?: (visible?: boolean) => JSX.Element | null | undefined; diff --git a/src/components/MultiSelect/index.tsx b/src/components/MultiSelect/index.tsx index ee37a13..1a3cd2e 100644 --- a/src/components/MultiSelect/index.tsx +++ b/src/components/MultiSelect/index.tsx @@ -93,6 +93,8 @@ const MultiSelectComponent: ( itemAccessibilityLabelField, visibleSelectedItem = true, mode = 'default', + excludeItems = [], + excludeSearchItems = [], } = props; const ref = useRef(null); @@ -127,8 +129,26 @@ const MultiSelectComponent: ( // eslint-disable-next-line react-hooks/exhaustive-deps }, []); + const excludeData = useCallback( + (data: any[]) => { + if (excludeItems.length > 0) { + const getData = _.differenceWith( + data, + excludeItems, + (obj1, obj2) => _.get(obj1, valueField) === _.get(obj2, valueField) + ); + return getData || []; + } else { + return data || []; + } + }, + [excludeItems, valueField] + ); + useEffect(() => { - setListData([...data]); + const filterData = excludeData(data); + setListData([...filterData]); + if (searchText) { onSearch(searchText); } @@ -244,7 +264,8 @@ const MultiSelectComponent: ( _measure(); setVisible(!visible); - setListData(data); + const filterData = excludeData(data); + setListData(filterData); if (!visible) { if (onFocus) { @@ -299,12 +320,35 @@ const MultiSelectComponent: ( const dataSearch = data.filter( searchQuery ? propSearchFunction : defaultFilterFunction ); - setListData(dataSearch); + + if (excludeSearchItems.length > 0 || excludeItems.length > 0) { + const excludeSearchData = _.differenceWith( + dataSearch, + excludeSearchItems, + (obj1, obj2) => + _.get(obj1, valueField) === _.get(obj2, valueField) + ); + + const filterData = excludeData(excludeSearchData); + setListData(filterData); + } else { + setListData(dataSearch); + } } else { - setListData(data); + const filterData = excludeData(data); + setListData(filterData); } }, - [data, searchField, labelField, searchQuery] + [ + data, + searchQuery, + excludeSearchItems, + excludeItems, + searchField, + labelField, + valueField, + excludeData, + ] ); const onSelect = useCallback( diff --git a/src/components/MultiSelect/model.ts b/src/components/MultiSelect/model.ts index c4f018f..4ac4d70 100644 --- a/src/components/MultiSelect/model.ts +++ b/src/components/MultiSelect/model.ts @@ -57,6 +57,8 @@ export interface MultiSelectProps { itemAccessibilityLabelField?: string; inverted?: boolean; mode?: 'default' | 'modal' | 'auto'; + excludeItems?: T[]; + excludeSearchItems?: T[]; onChange: (value: string[]) => void; renderLeftIcon?: (visible?: boolean) => JSX.Element | null | undefined; renderRightIcon?: (visible?: boolean) => JSX.Element | null | undefined;