Skip to content

Commit

Permalink
fix: Search and select all issues
Browse files Browse the repository at this point in the history
  • Loading branch information
azeezat committed Sep 7, 2023
1 parent ee7f98a commit 2b01adb
Show file tree
Hide file tree
Showing 4 changed files with 59 additions and 28 deletions.
24 changes: 20 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
[![NPM](https://nodei.co/npm/react-native-input-select.png?downloads=true)](https://nodei.co/npm/react-native-input-select/)

[![npm version](https://badge.fury.io/js/react-native-input-select.svg)](https://badge.fury.io/js/react-native-input-select) [![GitHub stars](https://img.shields.io/github/stars/azeezat/react-native-select?style=social)](https://github.com/azeezat/react-native-select/stargazers) [![CodeQL](https://github.com/azeezat/react-native-select/actions/workflows/codeql.yml/badge.svg)](https://github.com/azeezat/react-native-select/actions/workflows/codeql.yml) [![Release & Publish to NPM](https://github.com/azeezat/react-native-select/actions/workflows/release-and-publish-to-npm.yml/badge.svg)](https://github.com/azeezat/react-native-select/actions/workflows/release-and-publish-to-npm.yml)

[![npm version](https://badge.fury.io/js/react-native-input-select.svg)](https://badge.fury.io/js/react-native-input-select) [![GitHub stars](https://img.shields.io/github/stars/azeezat/react-native-select?style=social)](https://github.com/azeezat/react-native-select/stargazers) [![CodeQL](https://github.com/azeezat/react-native-select/actions/workflows/codeql.yml/badge.svg)](https://github.com/azeezat/react-native-select/actions/workflows/codeql.yml) [![Release & Publish to NPM](https://github.com/azeezat/react-native-select/actions/workflows/release-and-publish-to-npm.yml/badge.svg)](https://github.com/azeezat/react-native-select/actions/workflows/release-and-publish-to-npm.yml)

# react-native-input-select

Expand Down Expand Up @@ -238,6 +237,7 @@ For more examples visit our [wiki page](https://github.com/azeezat/react-native-
| <img width="456" alt="Screenshot 2023-05-16 at 6 17 09 AM" src="https://github.com/azeezat/react-native-select/assets/9849221/d695657f-d840-4368-b841-42af479d6543"> | <img width="456" alt="Screenshot 2023-03-23 at 5 26 58 PM" src="https://user-images.githubusercontent.com/9849221/227393548-28796d7b-9760-43a9-8ed3-fb1618cd1b7d.png"> | <img width="456" alt="Screenshot 2023-03-23 at 5 28 49 PM" src="https://user-images.githubusercontent.com/9849221/227393554-91ed1a92-d229-4814-84d8-5f9095e8d048.png"> |

## Props

| Proptypes | Datatype | Example |
| -------------------------- | ------------------------ | -------------------------------------------------------------------- |
| label | `string` | Countries |
Expand All @@ -258,7 +258,6 @@ For more examples visit our [wiki page](https://github.com/azeezat/react-native-
| dropdownStyle | `Object` | `{borderColor: 'blue', margin: 5, borderWidth:0 ...}` |
| dropdownContainerStyle | `Object` | `{backgroundColor: 'red', width: '30%', ...}` |
| dropdownIconStyle | `Object` | `{top: 10 , right: 10, ...}` |
| searchInputStyle | `Object` | `{backgroundColor: 'red', borderRadius: 0, ...}` |
| selectedItemStyle | `Object` | `{fontWeight: '600', color: 'yellow', ...}` |
| multipleSelectedItemStyle | `Object` | `{backgroundColor: 'red', color: 'yellow', ...}` |
| modalBackgroundStyle | `Object` | `{backgroundColor: 'rgba(196, 198, 246, 0.5)'}` |
Expand All @@ -275,6 +274,7 @@ For more examples visit our [wiki page](https://github.com/azeezat/react-native-
| checkboxComponentStyles | `Object` | `{checkboxSize?: number, checkboxStyle?: ViewStyle, checkboxLabelStyle: TextStyle}` |
| checkboxComponent | `React Component` | `<View style={styles.radioButton} />` |
| listControls | `Object` | `{ selectAllText: 'Choose all', unselectAllText: 'Remove all', selectAllCallback?: () => {}, unselectAllCallback?: () => {}}` |
| searchControls | `Object` | `{ searchInputStyle?: ViewStyle | TextStyle, textInputProps: TextInputProps}` |

## Deprecation Notice

Expand All @@ -283,7 +283,7 @@ The following props would be removed in coming releases.
- Individual props `checkboxSize`, `checkboxStyle`, `checkboxLabelStyle` would be replaced with a single object `checkboxComponentStyles` e.g

```js
const checkboxComponentStyles = {
checkboxComponentStyles = {
checkboxSize: 20,
checkboxStyle: {
backgroundColor: 'purple',
Expand All @@ -295,6 +295,22 @@ const checkboxComponentStyles = {
};
```

- `searchInputStyle` would now be inside `searchControls`

```js
searchControls={{
searchInputStyle: {
backgroundColor: 'yellow',
color: 'blue',
fontWeight: '900',
},
textInputProps: {
placeholder: 'Search anything here',
placeholderTextColor: 'gray',
},
}}
```

## Contributing

See the [contributing guide](CONTRIBUTING.md) to learn how to contribute to the repository and the development workflow.
Expand Down
11 changes: 11 additions & 0 deletions example/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,17 @@ export default function App() {
)
}
dropdownIconStyle={users && { top: 20, right: 15 }}
searchControls={{
searchInputStyle: {
backgroundColor: 'yellow',
color: 'blue',
fontWeight: '900',
},
textInputProps: {
placeholder: 'Search anything here',
placeholderTextColor: 'gray',
},
}}
/>

<DropdownSelect
Expand Down
47 changes: 23 additions & 24 deletions src/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ export const DropdownSelect: React.FC<DropdownProps> = ({
multipleSelectedItemStyle,
modalBackgroundStyle,
modalOptionsContainerStyle,
searchInputStyle,
searchInputStyle, // kept for backwards compatibility
primaryColor,
disabled,
checkboxSize, // kept for backwards compatibility
Expand All @@ -59,6 +59,7 @@ export const DropdownSelect: React.FC<DropdownProps> = ({
modalProps,
hideModal = false,
listControls,
searchControls,
...rest
}) => {
const [newOptions, setNewOptions] = useState<TFlatList | TSectionList>([]);
Expand Down Expand Up @@ -91,6 +92,7 @@ export const DropdownSelect: React.FC<DropdownProps> = ({
* List type
*==========================================*/

// check the structure of the new options array to determine if it is a section list or a
const isSectionList = newOptions.some(
(item) => item.title && item.data && Array.isArray(item.data)
);
Expand All @@ -106,19 +108,19 @@ export const DropdownSelect: React.FC<DropdownProps> = ({

const optLabel = optionLabel || DEFAULT_OPTION_LABEL;
const optValue = optionValue || DEFAULT_OPTION_VALUE;
const optionsCopy = JSON.parse(JSON.stringify(options)); //copy of the original options array
const optionsCopy = JSON.parse(JSON.stringify(options)); // copy of the original options array

/*===========================================
* Selection handlers
*==========================================*/
const handleSingleSelection = (value: string | number) => {
if (selectedItem === value) {
setSelectedItem(null);
onValueChange(null); //send value to parent
onValueChange(null); // send value to parent
} else {
setSelectedItem(value);
onValueChange(value); //send value to parent
setOpen(false); //close modal upon selection
onValueChange(value); // send value to parent
setOpen(false); // close modal upon selection
}
};

Expand All @@ -131,19 +133,21 @@ export const DropdownSelect: React.FC<DropdownProps> = ({
} else {
selectedValues.push(value);
}
onValueChange(selectedValues); //send value to parent
onValueChange(selectedValues); // send value to parent
return selectedValues;
});
};

const removeDisabledItems = (items: TFlatList) => {
return items.filter((item: TFlatListItem) => !item.disabled);
};

const handleSelectAll = () => {
setSelectAll((prevVal) => {
const selectedValues = [];

//don't select disabled items
const filteredOptions = modifiedOptions.filter(
(item: TFlatListItem) => !item.disabled
);
// don't select disabled items
const filteredOptions = removeDisabledItems(optionsCopy);

if (!prevVal) {
for (let i = 0; i < filteredOptions.length; i++) {
Expand All @@ -152,21 +156,15 @@ export const DropdownSelect: React.FC<DropdownProps> = ({
}

setSelectedItems(selectedValues);
onValueChange(selectedValues); //send value to parent
onValueChange(selectedValues); // send value to parent
return !prevVal;
});

if (
typeof listControls?.selectAllCallback === 'function' &&
!selectAll
) {
if (typeof listControls?.selectAllCallback === 'function' && !selectAll) {
listControls.selectAllCallback();
}

if (
typeof listControls?.unselectAllCallback === 'function' &&
selectAll
) {
if (typeof listControls?.unselectAllCallback === 'function' && selectAll) {
listControls.unselectAllCallback();
}
};
Expand All @@ -178,8 +176,7 @@ export const DropdownSelect: React.FC<DropdownProps> = ({
(selectedValues: any[]) => {
//if the list contains disabled values, those values will not be selected
if (
modifiedOptions.filter((item: TFlatListItem) => !item.disabled)
.length === selectedValues.length
removeDisabledItems(modifiedOptions).length === selectedValues.length
) {
setSelectAll(true);
} else {
Expand Down Expand Up @@ -229,7 +226,7 @@ export const DropdownSelect: React.FC<DropdownProps> = ({

const regexFilter = new RegExp(searchText, 'i');

//Because Search mutates the initial state, we have to search with a copy of the original array
// Because the options array will be mutated after Search, we have to search with a copy of the original array
const searchResults = isSectionList
? searchSectionList(optionsCopy as TSectionList, regexFilter)
: searchFlatList(optionsCopy as TFlatList, regexFilter);
Expand All @@ -243,7 +240,7 @@ export const DropdownSelect: React.FC<DropdownProps> = ({
item[optLabel].toString().toLowerCase().search(regexFilter) !== -1 ||
item[optValue].toString().toLowerCase().search(regexFilter) !== -1
) {
return item;
return true;
}
return;
});
Expand Down Expand Up @@ -355,14 +352,16 @@ export const DropdownSelect: React.FC<DropdownProps> = ({
modalProps={modalProps}
>
<ListTypeComponent
keyboardShouldPersistTaps="always"
ListHeaderComponent={
<>
{isSearchable && (
<Input
value={searchValue}
onChangeText={(text: string) => onSearch(text)}
style={searchInputStyle}
style={searchControls?.searchInputStyle || searchInputStyle}
primaryColor={primary}
{...searchControls?.textInputProps}
/>
)}
{listHeaderComponent}
Expand Down
5 changes: 5 additions & 0 deletions src/types/index.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import type {
ColorValue,
TextStyle,
ModalProps,
TextInputProps,
} from 'react-native';

export type DropdownProps = {
Expand Down Expand Up @@ -64,6 +65,10 @@ export type DropdownProps = {
selectAllCallback?: () => void;
unselectAllCallback?: () => void;
};
searchControls?: {
searchInputStyle?: ViewStyle | TextStyle;
textInputProps?: TextInputProps;
};
};

export type TFlatList = TFlatListItem[];
Expand Down

0 comments on commit 2b01adb

Please sign in to comment.