react-native-input-select 1.3.18 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +38 -36
- package/lib/commonjs/components/CheckBox/index.js +4 -2
- package/lib/commonjs/components/CheckBox/index.js.map +1 -1
- package/lib/commonjs/components/CustomModal/index.js +11 -9
- package/lib/commonjs/components/CustomModal/index.js.map +1 -1
- package/lib/commonjs/components/Dropdown/Dropdown.js +3 -2
- package/lib/commonjs/components/Dropdown/Dropdown.js.map +1 -1
- package/lib/commonjs/components/Dropdown/DropdownListItem.js +1 -1
- package/lib/commonjs/components/Dropdown/DropdownListItem.js.map +1 -1
- package/lib/commonjs/components/Dropdown/DropdownSelectedItemsView.js +13 -13
- package/lib/commonjs/components/Dropdown/DropdownSelectedItemsView.js.map +1 -1
- package/lib/commonjs/components/Input/index.js.map +1 -1
- package/lib/commonjs/components/{Dropdown → List}/DropdownFlatList.js +7 -5
- package/lib/commonjs/components/List/DropdownFlatList.js.map +1 -0
- package/lib/commonjs/components/{Dropdown → List}/DropdownSectionList.js +6 -4
- package/lib/commonjs/components/List/DropdownSectionList.js.map +1 -0
- package/lib/commonjs/hooks/index.js +61 -0
- package/lib/commonjs/hooks/index.js.map +1 -0
- package/lib/commonjs/hooks/use-index-of-selected-item.js +49 -0
- package/lib/commonjs/hooks/use-index-of-selected-item.js.map +1 -0
- package/lib/commonjs/hooks/use-modal.js +40 -0
- package/lib/commonjs/hooks/use-modal.js.map +1 -0
- package/lib/commonjs/hooks/use-search.js +58 -0
- package/lib/commonjs/hooks/use-search.js.map +1 -0
- package/lib/commonjs/hooks/use-select-all.js +70 -0
- package/lib/commonjs/hooks/use-select-all.js.map +1 -0
- package/lib/commonjs/hooks/use-selection-handler.js +62 -0
- package/lib/commonjs/hooks/use-selection-handler.js.map +1 -0
- package/lib/commonjs/index.js +118 -224
- package/lib/commonjs/index.js.map +1 -1
- package/lib/commonjs/utils/index.js +40 -5
- package/lib/commonjs/utils/index.js.map +1 -1
- package/lib/module/components/CheckBox/index.js +4 -2
- package/lib/module/components/CheckBox/index.js.map +1 -1
- package/lib/module/components/CustomModal/index.js +11 -8
- package/lib/module/components/CustomModal/index.js.map +1 -1
- package/lib/module/components/Dropdown/Dropdown.js +3 -2
- package/lib/module/components/Dropdown/Dropdown.js.map +1 -1
- package/lib/module/components/Dropdown/DropdownListItem.js +1 -1
- package/lib/module/components/Dropdown/DropdownListItem.js.map +1 -1
- package/lib/module/components/Dropdown/DropdownSelectedItemsView.js +13 -13
- package/lib/module/components/Dropdown/DropdownSelectedItemsView.js.map +1 -1
- package/lib/module/components/Input/index.js.map +1 -1
- package/lib/module/components/{Dropdown → List}/DropdownFlatList.js +7 -5
- package/lib/module/components/List/DropdownFlatList.js.map +1 -0
- package/lib/module/components/{Dropdown → List}/DropdownSectionList.js +6 -4
- package/lib/module/components/List/DropdownSectionList.js.map +1 -0
- package/lib/module/hooks/index.js +6 -0
- package/lib/module/hooks/index.js.map +1 -0
- package/lib/module/hooks/use-index-of-selected-item.js +42 -0
- package/lib/module/hooks/use-index-of-selected-item.js.map +1 -0
- package/lib/module/hooks/use-modal.js +33 -0
- package/lib/module/hooks/use-modal.js.map +1 -0
- package/lib/module/hooks/use-search.js +51 -0
- package/lib/module/hooks/use-search.js.map +1 -0
- package/lib/module/hooks/use-select-all.js +63 -0
- package/lib/module/hooks/use-select-all.js.map +1 -0
- package/lib/module/hooks/use-selection-handler.js +55 -0
- package/lib/module/hooks/use-selection-handler.js.map +1 -0
- package/lib/module/index.js +121 -226
- package/lib/module/index.js.map +1 -1
- package/lib/module/utils/index.js +36 -4
- package/lib/module/utils/index.js.map +1 -1
- package/lib/typescript/src/components/CheckBox/checkbox.types.d.ts +4 -2
- package/lib/typescript/src/components/CheckBox/checkbox.types.d.ts.map +1 -1
- package/lib/typescript/src/components/CheckBox/index.d.ts +1 -1
- package/lib/typescript/src/components/CheckBox/index.d.ts.map +1 -1
- package/lib/typescript/src/components/CustomModal/index.d.ts +4 -2
- package/lib/typescript/src/components/CustomModal/index.d.ts.map +1 -1
- package/lib/typescript/src/components/Dropdown/Dropdown.d.ts +1 -1
- package/lib/typescript/src/components/Dropdown/Dropdown.d.ts.map +1 -1
- package/lib/typescript/src/components/Dropdown/DropdownListItem.d.ts.map +1 -1
- package/lib/typescript/src/components/Dropdown/DropdownSelectedItemsView.d.ts +1 -1
- package/lib/typescript/src/components/Input/index.d.ts +5 -1
- package/lib/typescript/src/components/Input/index.d.ts.map +1 -1
- package/lib/typescript/src/components/{Dropdown → List}/DropdownFlatList.d.ts +1 -1
- package/lib/typescript/src/components/List/DropdownFlatList.d.ts.map +1 -0
- package/lib/typescript/src/components/List/DropdownSectionList.d.ts.map +1 -0
- package/lib/typescript/src/hooks/index.d.ts +6 -0
- package/lib/typescript/src/hooks/index.d.ts.map +1 -0
- package/lib/typescript/src/hooks/use-index-of-selected-item.d.ts +23 -0
- package/lib/typescript/src/hooks/use-index-of-selected-item.d.ts.map +1 -0
- package/lib/typescript/src/hooks/use-modal.d.ts +15 -0
- package/lib/typescript/src/hooks/use-modal.d.ts.map +1 -0
- package/lib/typescript/src/hooks/use-search.d.ts +16 -0
- package/lib/typescript/src/hooks/use-search.d.ts.map +1 -0
- package/lib/typescript/src/hooks/use-select-all.d.ts +18 -0
- package/lib/typescript/src/hooks/use-select-all.d.ts.map +1 -0
- package/lib/typescript/src/hooks/use-selection-handler.d.ts +19 -0
- package/lib/typescript/src/hooks/use-selection-handler.d.ts.map +1 -0
- package/lib/typescript/src/index.d.ts +19 -2
- package/lib/typescript/src/index.d.ts.map +1 -1
- package/lib/typescript/src/types/index.types.d.ts +54 -38
- package/lib/typescript/src/types/index.types.d.ts.map +1 -1
- package/lib/typescript/src/utils/index.d.ts +17 -3
- package/lib/typescript/src/utils/index.d.ts.map +1 -1
- package/package.json +21 -7
- package/src/components/CheckBox/checkbox.types.ts +2 -2
- package/src/components/CheckBox/index.tsx +8 -6
- package/src/components/CustomModal/index.tsx +14 -17
- package/src/components/Dropdown/Dropdown.tsx +3 -2
- package/src/components/Dropdown/DropdownListItem.tsx +1 -3
- package/src/components/Dropdown/DropdownSelectedItemsView.tsx +12 -12
- package/src/components/Input/index.tsx +13 -2
- package/src/components/{Dropdown → List}/DropdownFlatList.tsx +11 -8
- package/src/components/{Dropdown → List}/DropdownSectionList.tsx +10 -7
- package/src/hooks/index.ts +5 -0
- package/src/hooks/use-index-of-selected-item.ts +49 -0
- package/src/hooks/use-modal.ts +44 -0
- package/src/hooks/use-search.ts +95 -0
- package/src/hooks/use-select-all.ts +79 -0
- package/src/hooks/use-selection-handler.ts +81 -0
- package/src/index.tsx +300 -443
- package/src/types/index.types.ts +67 -40
- package/src/utils/index.ts +60 -3
- package/lib/commonjs/components/Dropdown/DropdownFlatList.js.map +0 -1
- package/lib/commonjs/components/Dropdown/DropdownSectionList.js.map +0 -1
- package/lib/module/components/Dropdown/DropdownFlatList.js.map +0 -1
- package/lib/module/components/Dropdown/DropdownSectionList.js.map +0 -1
- package/lib/typescript/src/components/Dropdown/DropdownFlatList.d.ts.map +0 -1
- package/lib/typescript/src/components/Dropdown/DropdownSectionList.d.ts.map +0 -1
- /package/lib/typescript/src/components/{Dropdown → List}/DropdownSectionList.d.ts +0 -0
|
@@ -14,7 +14,7 @@ import { inputStyles } from '../../styles/input';
|
|
|
14
14
|
const DropdownSelectedItemsView = ({
|
|
15
15
|
placeholder,
|
|
16
16
|
error,
|
|
17
|
-
|
|
17
|
+
labelsOfSelectedItems,
|
|
18
18
|
openModal,
|
|
19
19
|
isMultiple,
|
|
20
20
|
selectedItem,
|
|
@@ -30,6 +30,10 @@ const DropdownSelectedItemsView = ({
|
|
|
30
30
|
disabled,
|
|
31
31
|
setIndexOfSelectedItem,
|
|
32
32
|
}: any) => {
|
|
33
|
+
const openActions = (label: string) => {
|
|
34
|
+
openModal();
|
|
35
|
+
setIndexOfSelectedItem(label); // immediately scrolls to list item with the specified label when modal
|
|
36
|
+
};
|
|
33
37
|
return (
|
|
34
38
|
<Pressable
|
|
35
39
|
onPress={() => openModal()}
|
|
@@ -47,6 +51,8 @@ const DropdownSelectedItemsView = ({
|
|
|
47
51
|
},
|
|
48
52
|
]}
|
|
49
53
|
disabled={disabled}
|
|
54
|
+
aria-disabled={disabled}
|
|
55
|
+
testID="react-native-input-select-dropdown-input-container"
|
|
50
56
|
>
|
|
51
57
|
<ScrollView
|
|
52
58
|
horizontal
|
|
@@ -58,13 +64,10 @@ const DropdownSelectedItemsView = ({
|
|
|
58
64
|
onStartShouldSetResponder={() => true}
|
|
59
65
|
>
|
|
60
66
|
{isMultiple ? (
|
|
61
|
-
|
|
67
|
+
labelsOfSelectedItems?.map((label: string, i: Number) => (
|
|
62
68
|
<DropdownContent
|
|
63
|
-
onPress={() =>
|
|
64
|
-
|
|
65
|
-
setIndexOfSelectedItem(label); // immediately scrolls to list item with the specified label when modal
|
|
66
|
-
}}
|
|
67
|
-
key={`react-native-input-select-${Math.random()}-${i}`}
|
|
69
|
+
onPress={() => openActions(label)}
|
|
70
|
+
key={`react-native-input-select-list-item-${Math.random()}-${i}`}
|
|
68
71
|
style={[
|
|
69
72
|
styles.selectedItems,
|
|
70
73
|
{ backgroundColor: primaryColor },
|
|
@@ -76,12 +79,9 @@ const DropdownSelectedItemsView = ({
|
|
|
76
79
|
))
|
|
77
80
|
) : (
|
|
78
81
|
<DropdownContent
|
|
79
|
-
onPress={() =>
|
|
80
|
-
openModal();
|
|
81
|
-
setIndexOfSelectedItem(getSelectedItemsLabel()); // immediately scrolls to list item with the specified label when modal
|
|
82
|
-
}}
|
|
82
|
+
onPress={() => openActions(labelsOfSelectedItems)}
|
|
83
83
|
style={[styles.blackText, selectedItemStyle]}
|
|
84
|
-
label={
|
|
84
|
+
label={labelsOfSelectedItems}
|
|
85
85
|
disabled={disabled}
|
|
86
86
|
/>
|
|
87
87
|
)}
|
|
@@ -1,5 +1,13 @@
|
|
|
1
1
|
import React, { useState } from 'react';
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
TextInput,
|
|
4
|
+
StyleSheet,
|
|
5
|
+
View,
|
|
6
|
+
Platform,
|
|
7
|
+
TextInputProps,
|
|
8
|
+
ViewStyle,
|
|
9
|
+
ColorValue,
|
|
10
|
+
} from 'react-native';
|
|
3
11
|
import { inputStyles } from '../../styles/input';
|
|
4
12
|
|
|
5
13
|
export const Input = ({
|
|
@@ -10,7 +18,10 @@ export const Input = ({
|
|
|
10
18
|
primaryColor,
|
|
11
19
|
textInputContainerStyle,
|
|
12
20
|
...rest
|
|
13
|
-
}:
|
|
21
|
+
}: {
|
|
22
|
+
primaryColor?: ColorValue;
|
|
23
|
+
textInputContainerStyle?: ViewStyle;
|
|
24
|
+
} & TextInputProps) => {
|
|
14
25
|
const [isFocused, setFocus] = useState(false);
|
|
15
26
|
|
|
16
27
|
return (
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
/* eslint-disable react-native/no-inline-styles */
|
|
2
2
|
import React, { useEffect, useRef } from 'react';
|
|
3
3
|
import { FlatList, FlatListProps, StyleSheet } from 'react-native';
|
|
4
|
-
import DropdownListItem from '
|
|
4
|
+
import DropdownListItem from '../Dropdown/DropdownListItem';
|
|
5
5
|
import { ItemSeparatorComponent, ListEmptyComponent } from '../Others';
|
|
6
|
-
import { TFlatList } from '
|
|
6
|
+
import { TFlatList } from '../../types/index.types';
|
|
7
7
|
|
|
8
8
|
const DropdownFlatList = ({
|
|
9
9
|
options,
|
|
@@ -31,7 +31,7 @@ const DropdownFlatList = ({
|
|
|
31
31
|
const flatlistRef = useRef<FlatList<TFlatList>>(null);
|
|
32
32
|
|
|
33
33
|
const scrollToItem = (index: number) => {
|
|
34
|
-
flatlistRef
|
|
34
|
+
flatlistRef?.current?.scrollToIndex({
|
|
35
35
|
index,
|
|
36
36
|
animated: true,
|
|
37
37
|
});
|
|
@@ -43,8 +43,15 @@ const DropdownFlatList = ({
|
|
|
43
43
|
}
|
|
44
44
|
}, [listIndex]);
|
|
45
45
|
|
|
46
|
+
const itemSeparator = () => (
|
|
47
|
+
<ItemSeparatorComponent
|
|
48
|
+
itemSeparatorStyle={listComponentStyles?.itemSeparatorStyle}
|
|
49
|
+
/>
|
|
50
|
+
);
|
|
51
|
+
|
|
46
52
|
return (
|
|
47
53
|
<FlatList
|
|
54
|
+
testID="react-native-input-select-flat-list"
|
|
48
55
|
data={options}
|
|
49
56
|
extraData={isMultiple ? selectedItems : selectedItem}
|
|
50
57
|
initialNumToRender={5}
|
|
@@ -61,11 +68,7 @@ const DropdownFlatList = ({
|
|
|
61
68
|
contentContainerStyle={[
|
|
62
69
|
isSearchable ? { paddingTop: 0 } : styles.contentContainerStyle,
|
|
63
70
|
]}
|
|
64
|
-
ItemSeparatorComponent={
|
|
65
|
-
<ItemSeparatorComponent
|
|
66
|
-
itemSeparatorStyle={listComponentStyles?.itemSeparatorStyle}
|
|
67
|
-
/>
|
|
68
|
-
)}
|
|
71
|
+
ItemSeparatorComponent={itemSeparator}
|
|
69
72
|
renderItem={(item) =>
|
|
70
73
|
_renderItem(item, {
|
|
71
74
|
optionLabel,
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
/* eslint-disable react-native/no-inline-styles */
|
|
2
2
|
import React, { useEffect, useState, useRef } from 'react';
|
|
3
3
|
import { SectionList, StyleSheet } from 'react-native';
|
|
4
|
-
import DropdownListItem from '
|
|
4
|
+
import DropdownListItem from '../Dropdown/DropdownListItem';
|
|
5
5
|
import {
|
|
6
6
|
ItemSeparatorComponent,
|
|
7
7
|
ListEmptyComponent,
|
|
8
8
|
SectionHeaderTitle,
|
|
9
9
|
} from '../Others';
|
|
10
10
|
import { extractPropertyFromArray } from '../../utils';
|
|
11
|
-
import { TSectionList } from '
|
|
11
|
+
import { TSectionList } from '../../types/index.types';
|
|
12
12
|
|
|
13
13
|
const DropdownSectionList = ({
|
|
14
14
|
options,
|
|
@@ -79,8 +79,15 @@ const DropdownSectionList = ({
|
|
|
79
79
|
}
|
|
80
80
|
}, [listIndex]);
|
|
81
81
|
|
|
82
|
+
const itemSeparator = () => (
|
|
83
|
+
<ItemSeparatorComponent
|
|
84
|
+
itemSeparatorStyle={listComponentStyles?.itemSeparatorStyle}
|
|
85
|
+
/>
|
|
86
|
+
);
|
|
87
|
+
|
|
82
88
|
return (
|
|
83
89
|
<SectionList
|
|
90
|
+
testID="react-native-input-select-section-list"
|
|
84
91
|
sections={options}
|
|
85
92
|
extraData={isMultiple ? selectedItems : selectedItem}
|
|
86
93
|
initialNumToRender={5}
|
|
@@ -97,11 +104,7 @@ const DropdownSectionList = ({
|
|
|
97
104
|
contentContainerStyle={[
|
|
98
105
|
isSearchable ? { paddingTop: 0 } : styles.contentContainerStyle,
|
|
99
106
|
]}
|
|
100
|
-
ItemSeparatorComponent={
|
|
101
|
-
<ItemSeparatorComponent
|
|
102
|
-
itemSeparatorStyle={listComponentStyles?.itemSeparatorStyle}
|
|
103
|
-
/>
|
|
104
|
-
)}
|
|
107
|
+
ItemSeparatorComponent={itemSeparator}
|
|
105
108
|
renderItem={(item) =>
|
|
106
109
|
_renderItem(item, {
|
|
107
110
|
optionLabel,
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { useState, useCallback } from 'react';
|
|
2
|
+
import type { TFlatListItem, TSectionListItem } from '../types/index.types';
|
|
3
|
+
|
|
4
|
+
interface UseIndexOfSelectedItemProps {
|
|
5
|
+
options: (TFlatListItem | TSectionListItem)[];
|
|
6
|
+
optionLabel: string;
|
|
7
|
+
isSectionList: boolean;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
*
|
|
12
|
+
* @description for scrollToIndex in Sectionlist and Flatlist
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
export const useIndexOfSelectedItem = ({
|
|
16
|
+
options,
|
|
17
|
+
optionLabel,
|
|
18
|
+
isSectionList,
|
|
19
|
+
}: UseIndexOfSelectedItemProps) => {
|
|
20
|
+
const [listIndex, setListIndex] = useState<{
|
|
21
|
+
sectionIndex?: number;
|
|
22
|
+
itemIndex: number;
|
|
23
|
+
}>({ itemIndex: -1, sectionIndex: -1 });
|
|
24
|
+
|
|
25
|
+
const setIndexOfSelectedItem = useCallback(
|
|
26
|
+
(selectedLabel: string) => {
|
|
27
|
+
if (isSectionList) {
|
|
28
|
+
(options as TSectionListItem[]).forEach((section, sectionIndex) => {
|
|
29
|
+
const itemIndex = section.data.findIndex(
|
|
30
|
+
(item) => item[optionLabel] === selectedLabel
|
|
31
|
+
);
|
|
32
|
+
if (itemIndex !== -1) {
|
|
33
|
+
setListIndex({ sectionIndex, itemIndex });
|
|
34
|
+
}
|
|
35
|
+
});
|
|
36
|
+
} else {
|
|
37
|
+
const itemIndex = (options as TFlatListItem[]).findIndex(
|
|
38
|
+
(item) => item[optionLabel] === selectedLabel
|
|
39
|
+
);
|
|
40
|
+
if (itemIndex !== -1) {
|
|
41
|
+
setListIndex({ itemIndex });
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
},
|
|
45
|
+
[options, optionLabel, isSectionList]
|
|
46
|
+
);
|
|
47
|
+
|
|
48
|
+
return { listIndex, setListIndex, setIndexOfSelectedItem };
|
|
49
|
+
};
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { useState } from 'react';
|
|
2
|
+
import { ModalProps, Platform } from 'react-native';
|
|
3
|
+
import { TCustomModalControls } from '../types/index.types';
|
|
4
|
+
|
|
5
|
+
interface UseModalProps {
|
|
6
|
+
resetOptionsRelatedState: () => void;
|
|
7
|
+
disabled?: boolean;
|
|
8
|
+
modalProps?: ModalProps;
|
|
9
|
+
modalControls?: TCustomModalControls;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export const useModal = ({
|
|
13
|
+
resetOptionsRelatedState,
|
|
14
|
+
disabled,
|
|
15
|
+
modalProps,
|
|
16
|
+
modalControls,
|
|
17
|
+
}: UseModalProps) => {
|
|
18
|
+
const [isVisible, setIsVisible] = useState(false);
|
|
19
|
+
|
|
20
|
+
const openModal = () => {
|
|
21
|
+
if (disabled) return;
|
|
22
|
+
setIsVisible(true);
|
|
23
|
+
resetOptionsRelatedState();
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
const closeModal = () => {
|
|
27
|
+
setIsVisible(false);
|
|
28
|
+
resetOptionsRelatedState();
|
|
29
|
+
|
|
30
|
+
// iOS supports the onDismiss prop but android does not, so we do this explicitly here
|
|
31
|
+
// https://reactnative.dev/docs/modal#ondismiss-ios
|
|
32
|
+
if (Platform.OS === 'android') {
|
|
33
|
+
modalControls?.modalProps?.closeModal?.(); //kept for backwards compatibility
|
|
34
|
+
modalProps?.onDismiss?.(); //kept for backwards compatibility
|
|
35
|
+
modalControls?.modalProps?.onDismiss?.();
|
|
36
|
+
}
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
return {
|
|
40
|
+
isVisible,
|
|
41
|
+
openModal: () => openModal(),
|
|
42
|
+
closeModal: () => closeModal(),
|
|
43
|
+
};
|
|
44
|
+
};
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { useState, useCallback, useEffect } from 'react';
|
|
2
|
+
import type {
|
|
3
|
+
TFlatList,
|
|
4
|
+
TSectionList,
|
|
5
|
+
TFlatListItem,
|
|
6
|
+
TSectionListItem,
|
|
7
|
+
} from '../types/index.types';
|
|
8
|
+
import { escapeRegExp, isSectionList } from '../utils';
|
|
9
|
+
|
|
10
|
+
interface UseSearchProps {
|
|
11
|
+
initialOptions: TFlatList | TSectionList;
|
|
12
|
+
optionLabel: string;
|
|
13
|
+
optionValue: string;
|
|
14
|
+
searchCallback: (value: string) => void;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export const useSearch = ({
|
|
18
|
+
initialOptions,
|
|
19
|
+
optionLabel,
|
|
20
|
+
optionValue,
|
|
21
|
+
searchCallback,
|
|
22
|
+
}: UseSearchProps) => {
|
|
23
|
+
const [searchValue, setSearchValue] = useState<string>('');
|
|
24
|
+
const [filteredOptions, setFilteredOptions] = useState<
|
|
25
|
+
TFlatList | TSectionList
|
|
26
|
+
>(initialOptions);
|
|
27
|
+
|
|
28
|
+
useEffect(() => {
|
|
29
|
+
setFilteredOptions(initialOptions);
|
|
30
|
+
return () => {};
|
|
31
|
+
}, [initialOptions]);
|
|
32
|
+
|
|
33
|
+
const searchFlatList = useCallback(
|
|
34
|
+
(flatList: TFlatList, regexFilter: RegExp) => {
|
|
35
|
+
return flatList.filter((item: TFlatListItem) => {
|
|
36
|
+
return (
|
|
37
|
+
item[optionLabel].toString().toLowerCase().search(regexFilter) !==
|
|
38
|
+
-1 ||
|
|
39
|
+
item[optionValue].toString().toLowerCase().search(regexFilter) !== -1
|
|
40
|
+
);
|
|
41
|
+
});
|
|
42
|
+
},
|
|
43
|
+
[optionLabel, optionValue]
|
|
44
|
+
);
|
|
45
|
+
|
|
46
|
+
const searchSectionList = useCallback(
|
|
47
|
+
(sectionList: TSectionList, regexFilter: RegExp) => {
|
|
48
|
+
return sectionList.map((listItem: TSectionListItem) => {
|
|
49
|
+
// A section list is the combination of several flat lists
|
|
50
|
+
const filteredData = searchFlatList(listItem.data, regexFilter);
|
|
51
|
+
|
|
52
|
+
return { ...listItem, data: filteredData };
|
|
53
|
+
});
|
|
54
|
+
},
|
|
55
|
+
[searchFlatList]
|
|
56
|
+
);
|
|
57
|
+
|
|
58
|
+
const isSection = isSectionList(initialOptions);
|
|
59
|
+
|
|
60
|
+
const onSearch = useCallback(
|
|
61
|
+
(value: string) => {
|
|
62
|
+
searchCallback?.(value);
|
|
63
|
+
|
|
64
|
+
const searchText = escapeRegExp(value).toLowerCase().trim();
|
|
65
|
+
const regexFilter = new RegExp(searchText, 'i');
|
|
66
|
+
|
|
67
|
+
const searchResults = isSection
|
|
68
|
+
? searchSectionList(initialOptions as TSectionList, regexFilter)
|
|
69
|
+
: searchFlatList(initialOptions as TFlatList, regexFilter);
|
|
70
|
+
|
|
71
|
+
setFilteredOptions(searchResults);
|
|
72
|
+
},
|
|
73
|
+
[
|
|
74
|
+
initialOptions,
|
|
75
|
+
isSection,
|
|
76
|
+
searchCallback,
|
|
77
|
+
searchFlatList,
|
|
78
|
+
searchSectionList,
|
|
79
|
+
]
|
|
80
|
+
);
|
|
81
|
+
|
|
82
|
+
useEffect(() => {
|
|
83
|
+
if (searchValue) {
|
|
84
|
+
onSearch(searchValue);
|
|
85
|
+
}
|
|
86
|
+
}, [onSearch, searchValue]);
|
|
87
|
+
|
|
88
|
+
return {
|
|
89
|
+
searchValue,
|
|
90
|
+
setSearchValue,
|
|
91
|
+
filteredOptions,
|
|
92
|
+
setFilteredOptions,
|
|
93
|
+
isSectionList: isSection,
|
|
94
|
+
};
|
|
95
|
+
};
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { useCallback, useEffect, useState } from 'react';
|
|
2
|
+
import { TFlatListItem, TSelectedItem } from '../types/index.types';
|
|
3
|
+
import { removeDisabledItems } from '../utils';
|
|
4
|
+
|
|
5
|
+
interface UseCheckSelectAllProps {
|
|
6
|
+
options: TFlatListItem[];
|
|
7
|
+
selectedItems: TSelectedItem[];
|
|
8
|
+
isMultiple: boolean;
|
|
9
|
+
onValueChange: (selectedValues: TSelectedItem[]) => void;
|
|
10
|
+
listControls?: {
|
|
11
|
+
selectAllCallback?: () => void;
|
|
12
|
+
unselectAllCallback?: () => void;
|
|
13
|
+
};
|
|
14
|
+
optionValue: string;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export const useSelectAll = ({
|
|
18
|
+
options,
|
|
19
|
+
selectedItems,
|
|
20
|
+
isMultiple,
|
|
21
|
+
onValueChange,
|
|
22
|
+
listControls,
|
|
23
|
+
optionValue,
|
|
24
|
+
}: UseCheckSelectAllProps) => {
|
|
25
|
+
const [selectAll, setSelectAll] = useState<boolean>(false);
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* @description Handle "Select All" logic
|
|
29
|
+
*/
|
|
30
|
+
const handleSelectAll = useCallback(() => {
|
|
31
|
+
setSelectAll((prevVal) => {
|
|
32
|
+
let selectedValues: TSelectedItem[] = [];
|
|
33
|
+
|
|
34
|
+
// Remove disabled items from selection
|
|
35
|
+
const filteredOptions = removeDisabledItems(options);
|
|
36
|
+
|
|
37
|
+
// if everything has not been selected, select all the values in the list
|
|
38
|
+
if (!prevVal) {
|
|
39
|
+
selectedValues = filteredOptions.map(
|
|
40
|
+
(obj) => obj[optionValue]
|
|
41
|
+
) as TSelectedItem[];
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
onValueChange(selectedValues); // Send selected values to parent
|
|
45
|
+
return !prevVal;
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
if (typeof listControls?.selectAllCallback === 'function' && !selectAll) {
|
|
49
|
+
listControls.selectAllCallback();
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
if (typeof listControls?.unselectAllCallback === 'function' && selectAll) {
|
|
53
|
+
listControls.unselectAllCallback();
|
|
54
|
+
}
|
|
55
|
+
}, [options, optionValue, listControls, selectAll, onValueChange]);
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Check if all items are selected
|
|
59
|
+
*/
|
|
60
|
+
const checkSelectAll = useCallback(() => {
|
|
61
|
+
if (removeDisabledItems(options)?.length === selectedItems?.length) {
|
|
62
|
+
setSelectAll(true);
|
|
63
|
+
} else {
|
|
64
|
+
setSelectAll(false);
|
|
65
|
+
}
|
|
66
|
+
}, [options, selectedItems]);
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* if the user decides to select the options one by one, this hook
|
|
70
|
+
* runs to check if everything has been selected so that the `selectAll checkbox` can be selected
|
|
71
|
+
*/
|
|
72
|
+
useEffect(() => {
|
|
73
|
+
if (isMultiple) {
|
|
74
|
+
checkSelectAll();
|
|
75
|
+
}
|
|
76
|
+
}, [checkSelectAll, isMultiple, selectedItems]);
|
|
77
|
+
|
|
78
|
+
return { selectAll, handleSelectAll };
|
|
79
|
+
};
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { useState, useCallback } from 'react';
|
|
2
|
+
import { TSelectedItem } from '../types/index.types';
|
|
3
|
+
|
|
4
|
+
interface UseSelectionHandlerProps {
|
|
5
|
+
initialSelectedValue: TSelectedItem | TSelectedItem[]; // Can be a single value or an array
|
|
6
|
+
isMultiple: boolean;
|
|
7
|
+
maxSelectableItems?: number;
|
|
8
|
+
onValueChange: (selectedItems: TSelectedItem | TSelectedItem[]) => void;
|
|
9
|
+
closeModal: () => void;
|
|
10
|
+
autoCloseOnSelect: boolean;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export const useSelectionHandler = ({
|
|
14
|
+
initialSelectedValue,
|
|
15
|
+
isMultiple,
|
|
16
|
+
maxSelectableItems,
|
|
17
|
+
onValueChange,
|
|
18
|
+
closeModal,
|
|
19
|
+
autoCloseOnSelect,
|
|
20
|
+
}: UseSelectionHandlerProps) => {
|
|
21
|
+
// Initialize state based on whether it's multiple selection or not
|
|
22
|
+
const [selectedItem, setSelectedItem] = useState<TSelectedItem>(
|
|
23
|
+
isMultiple ? '' : (initialSelectedValue as TSelectedItem)
|
|
24
|
+
);
|
|
25
|
+
const [selectedItems, setSelectedItems] = useState<TSelectedItem[]>(
|
|
26
|
+
isMultiple ? (initialSelectedValue as TSelectedItem[]) : []
|
|
27
|
+
);
|
|
28
|
+
|
|
29
|
+
const handleSingleSelection = useCallback(
|
|
30
|
+
(value: TSelectedItem) => {
|
|
31
|
+
if (selectedItem === value) {
|
|
32
|
+
setSelectedItem('');
|
|
33
|
+
onValueChange(''); // Send null to parent when deselected
|
|
34
|
+
} else {
|
|
35
|
+
setSelectedItem(value);
|
|
36
|
+
onValueChange(value); // Send selected value to parent
|
|
37
|
+
|
|
38
|
+
if (autoCloseOnSelect) {
|
|
39
|
+
closeModal(); // close modal upon selection
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
},
|
|
43
|
+
[selectedItem, onValueChange, autoCloseOnSelect, closeModal]
|
|
44
|
+
);
|
|
45
|
+
|
|
46
|
+
const handleMultipleSelections = useCallback(
|
|
47
|
+
(value: TSelectedItem) => {
|
|
48
|
+
setSelectedItems((prevVal) => {
|
|
49
|
+
let selectedValues = [...prevVal];
|
|
50
|
+
|
|
51
|
+
if (selectedValues.includes(value)) {
|
|
52
|
+
// Remove item
|
|
53
|
+
selectedValues = selectedValues.filter((item) => item !== value);
|
|
54
|
+
} else {
|
|
55
|
+
// Add item
|
|
56
|
+
if (
|
|
57
|
+
maxSelectableItems &&
|
|
58
|
+
selectedValues.length >= maxSelectableItems
|
|
59
|
+
) {
|
|
60
|
+
return selectedValues;
|
|
61
|
+
}
|
|
62
|
+
selectedValues.push(value);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
onValueChange(selectedValues); // Send selected values to parent
|
|
66
|
+
return selectedValues;
|
|
67
|
+
});
|
|
68
|
+
},
|
|
69
|
+
[maxSelectableItems, onValueChange]
|
|
70
|
+
);
|
|
71
|
+
|
|
72
|
+
// Return the relevant state and handlers
|
|
73
|
+
return {
|
|
74
|
+
selectedItem,
|
|
75
|
+
selectedItems,
|
|
76
|
+
handleSingleSelection,
|
|
77
|
+
handleMultipleSelections,
|
|
78
|
+
setSelectedItems, // Expose for potential manual control
|
|
79
|
+
setSelectedItem, // Expose for potential manual control
|
|
80
|
+
};
|
|
81
|
+
};
|