react-native-input-select 2.1.4 → 2.1.6
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 +14 -0
- package/lib/commonjs/asset/close.png +0 -0
- package/lib/commonjs/components/CheckBox/index.js +11 -8
- package/lib/commonjs/components/CheckBox/index.js.map +1 -1
- package/lib/commonjs/components/Dropdown/Dropdown.js +26 -11
- package/lib/commonjs/components/Dropdown/Dropdown.js.map +1 -1
- package/lib/commonjs/components/Dropdown/DropdownSelectedItem.js +53 -0
- package/lib/commonjs/components/Dropdown/DropdownSelectedItem.js.map +1 -0
- package/lib/commonjs/components/Dropdown/{DropdownSelectedItemsView.js → DropdownSelectedItemsContainer.js} +41 -46
- package/lib/commonjs/components/Dropdown/DropdownSelectedItemsContainer.js.map +1 -0
- package/lib/commonjs/components/List/DropdownFlatList.js +1 -1
- package/lib/commonjs/components/List/DropdownListItem.js.map +1 -0
- package/lib/commonjs/components/List/DropdownSectionList.js +1 -1
- package/lib/commonjs/hooks/use-selection-handler.js +13 -7
- package/lib/commonjs/hooks/use-selection-handler.js.map +1 -1
- package/lib/commonjs/index.js +13 -6
- package/lib/commonjs/index.js.map +1 -1
- package/lib/commonjs/styles/colors.js.map +1 -1
- package/lib/commonjs/styles/input.js.map +1 -1
- package/lib/commonjs/utils/index.js +30 -13
- package/lib/commonjs/utils/index.js.map +1 -1
- package/lib/module/asset/close.png +0 -0
- package/lib/module/components/CheckBox/index.js +11 -8
- package/lib/module/components/CheckBox/index.js.map +1 -1
- package/lib/module/components/Dropdown/Dropdown.js +27 -12
- package/lib/module/components/Dropdown/Dropdown.js.map +1 -1
- package/lib/module/components/Dropdown/DropdownSelectedItem.js +46 -0
- package/lib/module/components/Dropdown/DropdownSelectedItem.js.map +1 -0
- package/lib/module/components/Dropdown/{DropdownSelectedItemsView.js → DropdownSelectedItemsContainer.js} +42 -47
- package/lib/module/components/Dropdown/DropdownSelectedItemsContainer.js.map +1 -0
- package/lib/module/components/List/DropdownFlatList.js +1 -1
- package/lib/module/components/List/DropdownFlatList.js.map +1 -1
- package/lib/module/components/List/DropdownListItem.js.map +1 -0
- package/lib/module/components/List/DropdownSectionList.js +1 -1
- package/lib/module/components/List/DropdownSectionList.js.map +1 -1
- package/lib/module/hooks/use-selection-handler.js +13 -7
- package/lib/module/hooks/use-selection-handler.js.map +1 -1
- package/lib/module/index.js +14 -7
- package/lib/module/index.js.map +1 -1
- package/lib/module/styles/colors.js.map +1 -1
- package/lib/module/styles/input.js.map +1 -1
- package/lib/module/utils/index.js +27 -11
- package/lib/module/utils/index.js.map +1 -1
- package/lib/typescript/src/components/CheckBox/index.d.ts.map +1 -1
- package/lib/typescript/src/components/Dropdown/Dropdown.d.ts +3 -1
- package/lib/typescript/src/components/Dropdown/Dropdown.d.ts.map +1 -1
- package/lib/typescript/src/components/Dropdown/DropdownSelectedItem.d.ts +16 -0
- package/lib/typescript/src/components/Dropdown/DropdownSelectedItem.d.ts.map +1 -0
- package/lib/typescript/src/components/Dropdown/DropdownSelectedItemsContainer.d.ts +16 -0
- package/lib/typescript/src/components/Dropdown/DropdownSelectedItemsContainer.d.ts.map +1 -0
- package/lib/typescript/src/components/List/DropdownListItem.d.ts.map +1 -0
- package/lib/typescript/src/hooks/use-selection-handler.d.ts +2 -1
- package/lib/typescript/src/hooks/use-selection-handler.d.ts.map +1 -1
- package/lib/typescript/src/index.d.ts +1 -0
- package/lib/typescript/src/index.d.ts.map +1 -1
- package/lib/typescript/src/styles/colors.d.ts +10 -1
- package/lib/typescript/src/styles/colors.d.ts.map +1 -1
- package/lib/typescript/src/styles/input.d.ts +23 -1
- package/lib/typescript/src/styles/input.d.ts.map +1 -1
- package/lib/typescript/src/types/index.types.d.ts +13 -6
- package/lib/typescript/src/types/index.types.d.ts.map +1 -1
- package/lib/typescript/src/utils/index.d.ts +5 -4
- package/lib/typescript/src/utils/index.d.ts.map +1 -1
- package/package.json +5 -5
- package/src/asset/close.png +0 -0
- package/src/components/CheckBox/index.tsx +16 -11
- package/src/components/Dropdown/Dropdown.tsx +29 -10
- package/src/components/Dropdown/DropdownSelectedItem.tsx +78 -0
- package/src/components/Dropdown/DropdownSelectedItemsContainer.tsx +164 -0
- package/src/components/List/DropdownFlatList.tsx +1 -1
- package/src/components/List/DropdownSectionList.tsx +1 -1
- package/src/hooks/use-selection-handler.ts +20 -7
- package/src/index.tsx +12 -12
- package/src/styles/colors.ts +1 -1
- package/src/styles/input.ts +1 -1
- package/src/types/index.types.ts +14 -6
- package/src/utils/index.ts +55 -23
- package/lib/commonjs/components/Dropdown/DropdownListItem.js.map +0 -1
- package/lib/commonjs/components/Dropdown/DropdownSelectedItemsView.js.map +0 -1
- package/lib/module/components/Dropdown/DropdownListItem.js.map +0 -1
- package/lib/module/components/Dropdown/DropdownSelectedItemsView.js.map +0 -1
- package/lib/typescript/src/components/Dropdown/DropdownListItem.d.ts.map +0 -1
- package/lib/typescript/src/components/Dropdown/DropdownSelectedItemsView.d.ts +0 -4
- package/lib/typescript/src/components/Dropdown/DropdownSelectedItemsView.d.ts.map +0 -1
- package/src/components/Dropdown/DropdownSelectedItemsView.tsx +0 -134
- /package/lib/commonjs/components/{Dropdown → List}/DropdownListItem.js +0 -0
- /package/lib/module/components/{Dropdown → List}/DropdownListItem.js +0 -0
- /package/lib/typescript/src/components/{Dropdown → List}/DropdownListItem.d.ts +0 -0
- /package/src/components/{Dropdown → List}/DropdownListItem.tsx +0 -0
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
import { View, Text, StyleSheet } from 'react-native';
|
|
3
|
-
import
|
|
2
|
+
import { View, Text, StyleSheet, Image, TouchableOpacity } from 'react-native';
|
|
3
|
+
import DropdownSelectedItemsContainer, {
|
|
4
|
+
DropdownSelectedItemsContainerProps,
|
|
5
|
+
} from './DropdownSelectedItemsContainer';
|
|
4
6
|
import { colors } from '../../styles/colors';
|
|
5
7
|
import { typography } from '../../styles/typography';
|
|
8
|
+
import { TDropdownInputProps } from 'src/types/index.types';
|
|
6
9
|
|
|
7
10
|
const Dropdown = ({
|
|
8
11
|
testID,
|
|
@@ -10,12 +13,14 @@ const Dropdown = ({
|
|
|
10
13
|
placeholder,
|
|
11
14
|
helperText,
|
|
12
15
|
error,
|
|
13
|
-
|
|
16
|
+
selectionData,
|
|
14
17
|
openModal,
|
|
15
|
-
closeModal,
|
|
16
18
|
isMultiple,
|
|
17
19
|
selectedItem,
|
|
18
20
|
selectedItems,
|
|
21
|
+
optionLabel,
|
|
22
|
+
optionValue,
|
|
23
|
+
selectedItemsControls,
|
|
19
24
|
dropdownIcon,
|
|
20
25
|
labelStyle,
|
|
21
26
|
dropdownStyle,
|
|
@@ -30,7 +35,8 @@ const Dropdown = ({
|
|
|
30
35
|
primaryColor,
|
|
31
36
|
disabled,
|
|
32
37
|
setIndexOfSelectedItem,
|
|
33
|
-
|
|
38
|
+
handleMultipleSelections,
|
|
39
|
+
}: TDropdownInputProps & DropdownSelectedItemsContainerProps) => {
|
|
34
40
|
return (
|
|
35
41
|
<View
|
|
36
42
|
style={[styles.dropdownInputContainer, dropdownContainerStyle]}
|
|
@@ -42,18 +48,17 @@ const Dropdown = ({
|
|
|
42
48
|
<Text style={[styles.label, labelStyle]}>{label}</Text>
|
|
43
49
|
)}
|
|
44
50
|
|
|
45
|
-
<
|
|
51
|
+
<DropdownSelectedItemsContainer
|
|
46
52
|
placeholder={placeholder}
|
|
47
53
|
error={error}
|
|
48
|
-
|
|
54
|
+
selectionData={selectionData}
|
|
49
55
|
openModal={openModal}
|
|
50
|
-
closeModal={closeModal}
|
|
51
56
|
isMultiple={isMultiple}
|
|
52
57
|
selectedItem={selectedItem}
|
|
53
58
|
selectedItems={selectedItems}
|
|
54
|
-
|
|
59
|
+
optionLabel={optionLabel}
|
|
60
|
+
optionValue={optionValue}
|
|
55
61
|
dropdownStyle={dropdownStyle}
|
|
56
|
-
dropdownIconStyle={dropdownIconStyle}
|
|
57
62
|
selectedItemStyle={selectedItemStyle}
|
|
58
63
|
multipleSelectedItemStyle={multipleSelectedItemStyle}
|
|
59
64
|
dropdownErrorStyle={dropdownErrorStyle}
|
|
@@ -61,6 +66,8 @@ const Dropdown = ({
|
|
|
61
66
|
disabled={disabled}
|
|
62
67
|
placeholderStyle={placeholderStyle}
|
|
63
68
|
setIndexOfSelectedItem={setIndexOfSelectedItem}
|
|
69
|
+
handleMultipleSelections={handleMultipleSelections}
|
|
70
|
+
selectedItemsControls={selectedItemsControls}
|
|
64
71
|
/>
|
|
65
72
|
|
|
66
73
|
{error && error !== '' && (
|
|
@@ -72,6 +79,17 @@ const Dropdown = ({
|
|
|
72
79
|
{helperText}
|
|
73
80
|
</Text>
|
|
74
81
|
)}
|
|
82
|
+
|
|
83
|
+
{/* Trailing Icon */}
|
|
84
|
+
<TouchableOpacity
|
|
85
|
+
style={[styles.iconStyle, dropdownIconStyle]}
|
|
86
|
+
onPress={() => openModal()}
|
|
87
|
+
testID="dropdown-trailing-icon"
|
|
88
|
+
>
|
|
89
|
+
{dropdownIcon || (
|
|
90
|
+
<Image source={require('../../asset/arrow-down.png')} />
|
|
91
|
+
)}
|
|
92
|
+
</TouchableOpacity>
|
|
75
93
|
</View>
|
|
76
94
|
);
|
|
77
95
|
};
|
|
@@ -82,6 +100,7 @@ const styles = StyleSheet.create({
|
|
|
82
100
|
helper: { marginTop: 8, color: colors.primary, ...typography.caption },
|
|
83
101
|
dropdownInputContainer: { marginBottom: 23, width: '100%' },
|
|
84
102
|
blackText: { color: colors.black },
|
|
103
|
+
iconStyle: { position: 'absolute', right: 25, top: 60 },
|
|
85
104
|
});
|
|
86
105
|
|
|
87
106
|
export default Dropdown;
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import React, { ReactNode } from 'react';
|
|
2
|
+
import {
|
|
3
|
+
Text,
|
|
4
|
+
Pressable,
|
|
5
|
+
StyleSheet,
|
|
6
|
+
Image,
|
|
7
|
+
TouchableOpacity,
|
|
8
|
+
ImageStyle,
|
|
9
|
+
ViewStyle,
|
|
10
|
+
TextStyle,
|
|
11
|
+
} from 'react-native';
|
|
12
|
+
import { extractTextStylesFromArray } from '../../utils';
|
|
13
|
+
|
|
14
|
+
export interface DropdownSelectedItemProps {
|
|
15
|
+
onPress: () => void;
|
|
16
|
+
style?: (TextStyle | ViewStyle)[];
|
|
17
|
+
label: string | ReactNode;
|
|
18
|
+
removeItemIcon?: ReactNode;
|
|
19
|
+
onRemoveItem?: () => void;
|
|
20
|
+
showRemoveIcon?: boolean;
|
|
21
|
+
disabled: boolean;
|
|
22
|
+
closeIconStyles?: ImageStyle;
|
|
23
|
+
testId?: string;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const DropdownSelectedItem = ({
|
|
27
|
+
onPress,
|
|
28
|
+
style,
|
|
29
|
+
label,
|
|
30
|
+
removeItemIcon,
|
|
31
|
+
onRemoveItem,
|
|
32
|
+
showRemoveIcon,
|
|
33
|
+
closeIconStyles,
|
|
34
|
+
testId,
|
|
35
|
+
disabled,
|
|
36
|
+
...rest
|
|
37
|
+
}: DropdownSelectedItemProps) => {
|
|
38
|
+
return (
|
|
39
|
+
<TouchableOpacity
|
|
40
|
+
onPress={() => onPress()}
|
|
41
|
+
{...rest}
|
|
42
|
+
style={[styles.dropdownInputContent, ...(style ?? [])]}
|
|
43
|
+
testID={`dropdown-selected-item-${testId}`}
|
|
44
|
+
>
|
|
45
|
+
<Text style={extractTextStylesFromArray(style)}>{label}</Text>
|
|
46
|
+
|
|
47
|
+
{showRemoveIcon && (
|
|
48
|
+
<Pressable
|
|
49
|
+
onPress={disabled ? null : () => onRemoveItem?.()}
|
|
50
|
+
testID={`dropdown-selected-item-remove-icon-${testId}`}
|
|
51
|
+
>
|
|
52
|
+
{removeItemIcon || (
|
|
53
|
+
<Image
|
|
54
|
+
source={require('../../asset/close.png')}
|
|
55
|
+
style={[styles.removeItemIcon, closeIconStyles]}
|
|
56
|
+
/>
|
|
57
|
+
)}
|
|
58
|
+
</Pressable>
|
|
59
|
+
)}
|
|
60
|
+
</TouchableOpacity>
|
|
61
|
+
);
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
const styles = StyleSheet.create({
|
|
65
|
+
dropdownInputContent: {
|
|
66
|
+
display: 'flex',
|
|
67
|
+
justifyContent: 'center',
|
|
68
|
+
alignItems: 'center',
|
|
69
|
+
flexDirection: 'row',
|
|
70
|
+
gap: 8,
|
|
71
|
+
},
|
|
72
|
+
removeItemIcon: {
|
|
73
|
+
height: 10,
|
|
74
|
+
width: 10,
|
|
75
|
+
},
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
export default DropdownSelectedItem;
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import {
|
|
3
|
+
View,
|
|
4
|
+
Pressable,
|
|
5
|
+
ScrollView,
|
|
6
|
+
StyleSheet,
|
|
7
|
+
TextStyle,
|
|
8
|
+
} from 'react-native';
|
|
9
|
+
import { colors } from '../../styles/colors';
|
|
10
|
+
import { inputStyles } from '../../styles/input';
|
|
11
|
+
import DropdownSelectedItem from './DropdownSelectedItem';
|
|
12
|
+
import {
|
|
13
|
+
TDropdownInputProps,
|
|
14
|
+
TFlatListItem,
|
|
15
|
+
TSelectedItem,
|
|
16
|
+
TSelectedItemsControls,
|
|
17
|
+
TSelectedItemWithReactComponent,
|
|
18
|
+
} from 'src/types/index.types';
|
|
19
|
+
|
|
20
|
+
export interface DropdownSelectedItemsContainerProps {
|
|
21
|
+
openModal: () => void;
|
|
22
|
+
selectedItem: TSelectedItemWithReactComponent;
|
|
23
|
+
selectedItems: TSelectedItemWithReactComponent[];
|
|
24
|
+
optionLabel: string;
|
|
25
|
+
optionValue: string;
|
|
26
|
+
setIndexOfSelectedItem: (label: string) => void;
|
|
27
|
+
selectedItemsControls?: TSelectedItemsControls;
|
|
28
|
+
selectionData: TFlatListItem | TFlatListItem[];
|
|
29
|
+
handleMultipleSelections?: (value: TSelectedItem) => void;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const DropdownSelectedItemsContainer = ({
|
|
33
|
+
placeholder,
|
|
34
|
+
error,
|
|
35
|
+
selectionData = [],
|
|
36
|
+
openModal,
|
|
37
|
+
isMultiple,
|
|
38
|
+
selectedItem,
|
|
39
|
+
selectedItems,
|
|
40
|
+
optionLabel,
|
|
41
|
+
optionValue,
|
|
42
|
+
dropdownStyle,
|
|
43
|
+
placeholderStyle = {},
|
|
44
|
+
selectedItemStyle = {},
|
|
45
|
+
multipleSelectedItemStyle = {},
|
|
46
|
+
dropdownErrorStyle,
|
|
47
|
+
primaryColor,
|
|
48
|
+
disabled = false,
|
|
49
|
+
setIndexOfSelectedItem,
|
|
50
|
+
selectedItemsControls,
|
|
51
|
+
handleMultipleSelections,
|
|
52
|
+
}: TDropdownInputProps & DropdownSelectedItemsContainerProps) => {
|
|
53
|
+
const openActions = (label: string) => {
|
|
54
|
+
openModal();
|
|
55
|
+
setIndexOfSelectedItem(label); // immediately scrolls to list item with the specified label when modal
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
return (
|
|
59
|
+
<Pressable
|
|
60
|
+
onPress={() => openModal()}
|
|
61
|
+
style={({ pressed }) => [
|
|
62
|
+
pressed && {
|
|
63
|
+
...inputStyles.inputFocusState,
|
|
64
|
+
borderColor: primaryColor,
|
|
65
|
+
},
|
|
66
|
+
{ ...inputStyles.input, ...dropdownStyle },
|
|
67
|
+
error && //this must be last
|
|
68
|
+
error !== '' &&
|
|
69
|
+
!pressed && {
|
|
70
|
+
...inputStyles.inputFocusErrorState,
|
|
71
|
+
...dropdownErrorStyle,
|
|
72
|
+
},
|
|
73
|
+
]}
|
|
74
|
+
disabled={disabled}
|
|
75
|
+
aria-disabled={disabled}
|
|
76
|
+
testID="react-native-input-select-dropdown-input-container"
|
|
77
|
+
>
|
|
78
|
+
<ScrollView
|
|
79
|
+
horizontal
|
|
80
|
+
alwaysBounceHorizontal
|
|
81
|
+
showsHorizontalScrollIndicator={false}
|
|
82
|
+
>
|
|
83
|
+
<View
|
|
84
|
+
style={styles.selectedItemsContainer}
|
|
85
|
+
onStartShouldSetResponder={() => true}
|
|
86
|
+
>
|
|
87
|
+
{isMultiple ? (
|
|
88
|
+
(selectionData as TFlatListItem[])?.map((data, i) => {
|
|
89
|
+
const label = data[optionLabel];
|
|
90
|
+
const value = data[optionValue];
|
|
91
|
+
|
|
92
|
+
return (
|
|
93
|
+
<DropdownSelectedItem
|
|
94
|
+
onPress={() => openActions(label as string)}
|
|
95
|
+
key={`react-native-input-select-list-item-${Math.random()}-${i}`}
|
|
96
|
+
style={[
|
|
97
|
+
styles.selectedItems,
|
|
98
|
+
{ backgroundColor: primaryColor },
|
|
99
|
+
multipleSelectedItemStyle,
|
|
100
|
+
]}
|
|
101
|
+
closeIconStyles={{
|
|
102
|
+
tintColor:
|
|
103
|
+
(multipleSelectedItemStyle as TextStyle)?.color ||
|
|
104
|
+
styles.selectedItems.color,
|
|
105
|
+
}}
|
|
106
|
+
label={label}
|
|
107
|
+
disabled={disabled || (data.disabled as boolean)}
|
|
108
|
+
showRemoveIcon={selectedItemsControls?.showRemoveIcon || true}
|
|
109
|
+
removeItemIcon={selectedItemsControls?.removeItemIcon}
|
|
110
|
+
onRemoveItem={() => {
|
|
111
|
+
handleMultipleSelections?.(value as TSelectedItem);
|
|
112
|
+
selectedItemsControls?.onRemoveItem?.(); //user defined control
|
|
113
|
+
}}
|
|
114
|
+
testId={`${i}`}
|
|
115
|
+
/>
|
|
116
|
+
);
|
|
117
|
+
})
|
|
118
|
+
) : (
|
|
119
|
+
<DropdownSelectedItem
|
|
120
|
+
onPress={() =>
|
|
121
|
+
openActions(
|
|
122
|
+
String((selectionData as TFlatListItem)?.[optionLabel] ?? '')
|
|
123
|
+
)
|
|
124
|
+
}
|
|
125
|
+
style={[styles.blackText, selectedItemStyle]}
|
|
126
|
+
label={(selectionData as TFlatListItem)[optionLabel] as string}
|
|
127
|
+
disabled={disabled}
|
|
128
|
+
/>
|
|
129
|
+
)}
|
|
130
|
+
|
|
131
|
+
{/* Placeholder */}
|
|
132
|
+
{selectedItem === '' && selectedItems?.length === 0 && (
|
|
133
|
+
<DropdownSelectedItem
|
|
134
|
+
onPress={() => openModal()}
|
|
135
|
+
style={[styles.blackText, placeholderStyle]}
|
|
136
|
+
label={placeholder ?? 'Select an option'}
|
|
137
|
+
disabled={disabled}
|
|
138
|
+
/>
|
|
139
|
+
)}
|
|
140
|
+
</View>
|
|
141
|
+
</ScrollView>
|
|
142
|
+
</Pressable>
|
|
143
|
+
);
|
|
144
|
+
};
|
|
145
|
+
|
|
146
|
+
const styles = StyleSheet.create({
|
|
147
|
+
selectedItemsContainer: {
|
|
148
|
+
flexDirection: 'row',
|
|
149
|
+
flexWrap: 'nowrap',
|
|
150
|
+
alignItems: 'center',
|
|
151
|
+
},
|
|
152
|
+
selectedItems: {
|
|
153
|
+
color: colors.white,
|
|
154
|
+
paddingHorizontal: 10,
|
|
155
|
+
paddingVertical: 5,
|
|
156
|
+
borderRadius: 10,
|
|
157
|
+
backgroundColor: colors.primary,
|
|
158
|
+
marginRight: 10,
|
|
159
|
+
overflow: 'hidden',
|
|
160
|
+
},
|
|
161
|
+
blackText: { color: colors.black },
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
export default DropdownSelectedItemsContainer;
|
|
@@ -1,7 +1,7 @@
|
|
|
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 './DropdownListItem';
|
|
5
5
|
import { ItemSeparatorComponent, ListEmptyComponent } from '../Others';
|
|
6
6
|
import { TFlatList } from '../../types/index.types';
|
|
7
7
|
|
|
@@ -1,7 +1,7 @@
|
|
|
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 './DropdownListItem';
|
|
5
5
|
import {
|
|
6
6
|
ItemSeparatorComponent,
|
|
7
7
|
ListEmptyComponent,
|
|
@@ -4,6 +4,7 @@ import { TSelectedItem } from '../types/index.types';
|
|
|
4
4
|
interface UseSelectionHandlerProps {
|
|
5
5
|
initialSelectedValue: TSelectedItem | TSelectedItem[]; // Can be a single value or an array
|
|
6
6
|
isMultiple: boolean;
|
|
7
|
+
minSelectableItems?: number;
|
|
7
8
|
maxSelectableItems?: number;
|
|
8
9
|
onValueChange: (selectedItems: TSelectedItem | TSelectedItem[]) => void;
|
|
9
10
|
closeModal: () => void;
|
|
@@ -13,12 +14,13 @@ interface UseSelectionHandlerProps {
|
|
|
13
14
|
export const useSelectionHandler = ({
|
|
14
15
|
initialSelectedValue,
|
|
15
16
|
isMultiple,
|
|
17
|
+
minSelectableItems = 0,
|
|
16
18
|
maxSelectableItems,
|
|
17
19
|
onValueChange,
|
|
18
20
|
closeModal,
|
|
19
21
|
autoCloseOnSelect,
|
|
20
22
|
}: UseSelectionHandlerProps) => {
|
|
21
|
-
// Initialize state based on whether it
|
|
23
|
+
// Initialize state based on whether it is multiple selection or not
|
|
22
24
|
const [selectedItem, setSelectedItem] = useState<TSelectedItem>(
|
|
23
25
|
isMultiple ? '' : (initialSelectedValue as TSelectedItem)
|
|
24
26
|
);
|
|
@@ -29,8 +31,11 @@ export const useSelectionHandler = ({
|
|
|
29
31
|
const handleSingleSelection = useCallback(
|
|
30
32
|
(value: TSelectedItem) => {
|
|
31
33
|
if (selectedItem === value) {
|
|
32
|
-
|
|
33
|
-
|
|
34
|
+
// Deselect item if minSelectableItems is not reached
|
|
35
|
+
if (minSelectableItems === 0) {
|
|
36
|
+
setSelectedItem('');
|
|
37
|
+
onValueChange(''); // Send null to parent when deselected
|
|
38
|
+
}
|
|
34
39
|
} else {
|
|
35
40
|
setSelectedItem(value);
|
|
36
41
|
onValueChange(value); // Send selected value to parent
|
|
@@ -40,7 +45,13 @@ export const useSelectionHandler = ({
|
|
|
40
45
|
}
|
|
41
46
|
}
|
|
42
47
|
},
|
|
43
|
-
[
|
|
48
|
+
[
|
|
49
|
+
selectedItem,
|
|
50
|
+
minSelectableItems,
|
|
51
|
+
onValueChange,
|
|
52
|
+
autoCloseOnSelect,
|
|
53
|
+
closeModal,
|
|
54
|
+
]
|
|
44
55
|
);
|
|
45
56
|
|
|
46
57
|
const handleMultipleSelections = useCallback(
|
|
@@ -49,8 +60,10 @@ export const useSelectionHandler = ({
|
|
|
49
60
|
let selectedValues = [...prevVal];
|
|
50
61
|
|
|
51
62
|
if (selectedValues.includes(value)) {
|
|
52
|
-
//
|
|
53
|
-
|
|
63
|
+
// Only remove item if it doesn't drop below the minimum required
|
|
64
|
+
if (selectedValues.length > minSelectableItems) {
|
|
65
|
+
selectedValues = selectedValues.filter((item) => item !== value);
|
|
66
|
+
}
|
|
54
67
|
} else {
|
|
55
68
|
// Add item
|
|
56
69
|
if (
|
|
@@ -66,7 +79,7 @@ export const useSelectionHandler = ({
|
|
|
66
79
|
return selectedValues;
|
|
67
80
|
});
|
|
68
81
|
},
|
|
69
|
-
[maxSelectableItems, onValueChange]
|
|
82
|
+
[minSelectableItems, maxSelectableItems, onValueChange]
|
|
70
83
|
);
|
|
71
84
|
|
|
72
85
|
// Return the relevant state and handlers
|
package/src/index.tsx
CHANGED
|
@@ -18,7 +18,7 @@ import type {
|
|
|
18
18
|
DropdownSelectHandle,
|
|
19
19
|
TSelectedItem,
|
|
20
20
|
} from './types/index.types';
|
|
21
|
-
import { extractPropertyFromArray,
|
|
21
|
+
import { extractPropertyFromArray, getSelectionsData } from './utils';
|
|
22
22
|
import {
|
|
23
23
|
useSelectionHandler,
|
|
24
24
|
useModal,
|
|
@@ -63,7 +63,9 @@ export const DropdownSelect = forwardRef<DropdownSelectHandle, DropdownProps>(
|
|
|
63
63
|
searchControls,
|
|
64
64
|
modalControls,
|
|
65
65
|
checkboxControls,
|
|
66
|
+
selectedItemsControls,
|
|
66
67
|
autoCloseOnSelect = true,
|
|
68
|
+
minSelectableItems,
|
|
67
69
|
maxSelectableItems,
|
|
68
70
|
...rest
|
|
69
71
|
},
|
|
@@ -138,6 +140,7 @@ export const DropdownSelect = forwardRef<DropdownSelectHandle, DropdownProps>(
|
|
|
138
140
|
} = useSelectionHandler({
|
|
139
141
|
initialSelectedValue: selectedValue,
|
|
140
142
|
isMultiple,
|
|
143
|
+
minSelectableItems,
|
|
141
144
|
maxSelectableItems,
|
|
142
145
|
onValueChange,
|
|
143
146
|
closeModal: () => closeModal(),
|
|
@@ -149,14 +152,9 @@ export const DropdownSelect = forwardRef<DropdownSelectHandle, DropdownProps>(
|
|
|
149
152
|
? setSelectedItems(selectedValue as TSelectedItem[])
|
|
150
153
|
: setSelectedItem(selectedValue as TSelectedItem);
|
|
151
154
|
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
setSelectedItems,
|
|
156
|
-
setSelectedItem,
|
|
157
|
-
isMultiple,
|
|
158
|
-
onValueChange,
|
|
159
|
-
]);
|
|
155
|
+
// setSelectedItems already updates selectedValue, so omit it from dependency array to avoid infinite loop
|
|
156
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
157
|
+
}, [setSelectedItems, setSelectedItem, isMultiple, onValueChange]);
|
|
160
158
|
|
|
161
159
|
/*===========================================
|
|
162
160
|
* List type
|
|
@@ -198,18 +196,19 @@ export const DropdownSelect = forwardRef<DropdownSelectHandle, DropdownProps>(
|
|
|
198
196
|
placeholder={placeholder}
|
|
199
197
|
helperText={helperText}
|
|
200
198
|
error={error}
|
|
201
|
-
|
|
199
|
+
selectionData={getSelectionsData({
|
|
202
200
|
isMultiple,
|
|
203
|
-
optionLabel,
|
|
204
201
|
optionValue,
|
|
205
202
|
selectedItem,
|
|
206
203
|
selectedItems,
|
|
207
204
|
modifiedOptions,
|
|
208
205
|
})}
|
|
206
|
+
optionLabel={optionLabel}
|
|
207
|
+
optionValue={optionValue}
|
|
209
208
|
selectedItem={selectedItem}
|
|
210
209
|
selectedItems={selectedItems}
|
|
210
|
+
selectedItemsControls={selectedItemsControls}
|
|
211
211
|
openModal={() => openModal()}
|
|
212
|
-
closeModal={() => closeModal()}
|
|
213
212
|
labelStyle={labelStyle}
|
|
214
213
|
dropdownIcon={dropdownIcon}
|
|
215
214
|
dropdownStyle={dropdownStyle}
|
|
@@ -225,6 +224,7 @@ export const DropdownSelect = forwardRef<DropdownSelectHandle, DropdownProps>(
|
|
|
225
224
|
disabled={disabled}
|
|
226
225
|
placeholderStyle={placeholderStyle}
|
|
227
226
|
setIndexOfSelectedItem={setIndexOfSelectedItem}
|
|
227
|
+
handleMultipleSelections={handleMultipleSelections}
|
|
228
228
|
{...rest}
|
|
229
229
|
/>
|
|
230
230
|
<CustomModal
|
package/src/styles/colors.ts
CHANGED
package/src/styles/input.ts
CHANGED
package/src/types/index.types.ts
CHANGED
|
@@ -13,18 +13,19 @@ export type DropdownProps = CommonDropdownProps &
|
|
|
13
13
|
TListProps;
|
|
14
14
|
|
|
15
15
|
export type CommonDropdownProps = {
|
|
16
|
-
testID?: string;
|
|
17
|
-
label?: string;
|
|
18
16
|
options: TFlatList | TSectionList;
|
|
19
|
-
optionLabel?: string;
|
|
20
|
-
optionValue?: string;
|
|
21
17
|
onValueChange: (selectedItems: TSelectedItem | TSelectedItem[]) => void;
|
|
22
18
|
selectedValue: TSelectedItem | TSelectedItem[];
|
|
19
|
+
optionLabel?: string;
|
|
20
|
+
optionValue?: string;
|
|
23
21
|
autoCloseOnSelect?: boolean;
|
|
22
|
+
minSelectableItems?: number;
|
|
24
23
|
maxSelectableItems?: number;
|
|
25
24
|
};
|
|
26
25
|
|
|
27
26
|
export type TDropdownInputProps = {
|
|
27
|
+
testID?: string;
|
|
28
|
+
label?: string;
|
|
28
29
|
placeholder?: string;
|
|
29
30
|
error?: string;
|
|
30
31
|
helperText?: string;
|
|
@@ -38,8 +39,8 @@ export type TDropdownInputProps = {
|
|
|
38
39
|
dropdownErrorStyle?: ViewStyle;
|
|
39
40
|
dropdownErrorTextStyle?: TextStyle;
|
|
40
41
|
dropdownHelperTextStyle?: TextStyle;
|
|
41
|
-
selectedItemStyle?: TextStyle;
|
|
42
|
-
multipleSelectedItemStyle?: TextStyle;
|
|
42
|
+
selectedItemStyle?: TextStyle | ViewStyle;
|
|
43
|
+
multipleSelectedItemStyle?: TextStyle | ViewStyle;
|
|
43
44
|
primaryColor?: ColorValue;
|
|
44
45
|
disabled?: boolean;
|
|
45
46
|
placeholderStyle?: TextStyle;
|
|
@@ -50,6 +51,7 @@ type TControls = {
|
|
|
50
51
|
checkboxControls?: TCheckboxControls;
|
|
51
52
|
modalControls?: TCustomModalControls;
|
|
52
53
|
listControls?: TListControls;
|
|
54
|
+
selectedItemsControls?: TSelectedItemsControls;
|
|
53
55
|
};
|
|
54
56
|
|
|
55
57
|
type TSearchControls = {
|
|
@@ -95,6 +97,12 @@ type TListControls = {
|
|
|
95
97
|
keyboardShouldPersistTaps?: 'always' | 'never' | 'handled';
|
|
96
98
|
};
|
|
97
99
|
|
|
100
|
+
export type TSelectedItemsControls = {
|
|
101
|
+
showRemoveIcon?: boolean;
|
|
102
|
+
removeItemIcon?: React.ReactNode;
|
|
103
|
+
onRemoveItem?: () => void;
|
|
104
|
+
};
|
|
105
|
+
|
|
98
106
|
export type TSelectedItem = string | number | boolean | undefined;
|
|
99
107
|
export type TSelectedItemWithReactComponent =
|
|
100
108
|
| TSelectedItem
|
package/src/utils/index.ts
CHANGED
|
@@ -1,10 +1,6 @@
|
|
|
1
|
+
import { TextStyle, ViewStyle } from 'react-native';
|
|
1
2
|
import { TSelectedItem } from '../types/index.types';
|
|
2
|
-
import {
|
|
3
|
-
TFlatList,
|
|
4
|
-
TFlatListItem,
|
|
5
|
-
TSectionList,
|
|
6
|
-
TSelectedItemWithReactComponent,
|
|
7
|
-
} from '../types/index.types';
|
|
3
|
+
import { TFlatList, TFlatListItem, TSectionList } from '../types/index.types';
|
|
8
4
|
|
|
9
5
|
export const extractPropertyFromArray = (arr: any[], property: string) => {
|
|
10
6
|
let extractedValue = arr?.map((item: any) => item[property]);
|
|
@@ -31,40 +27,76 @@ export const isSectionList = (options: TFlatList | TSectionList): boolean => {
|
|
|
31
27
|
* @description get the labels of the items that were selected from the options array for either multiple or single selections
|
|
32
28
|
* @returns
|
|
33
29
|
*/
|
|
34
|
-
export const
|
|
30
|
+
export const getSelectionsData = ({
|
|
35
31
|
isMultiple,
|
|
36
|
-
optionLabel,
|
|
37
32
|
optionValue,
|
|
38
33
|
selectedItem,
|
|
39
34
|
selectedItems,
|
|
40
35
|
modifiedOptions,
|
|
41
36
|
}: {
|
|
42
37
|
isMultiple: boolean;
|
|
43
|
-
optionLabel: string;
|
|
44
38
|
optionValue: string;
|
|
45
39
|
selectedItem: TSelectedItem;
|
|
46
40
|
selectedItems: TSelectedItem[];
|
|
47
41
|
modifiedOptions: TFlatList;
|
|
48
|
-
}) => {
|
|
42
|
+
}): TFlatListItem | TFlatListItem[] => {
|
|
49
43
|
// Multiple select
|
|
50
|
-
if (isMultiple
|
|
51
|
-
let
|
|
44
|
+
if (isMultiple) {
|
|
45
|
+
let currentSelections: TFlatListItem[] = [];
|
|
52
46
|
|
|
53
|
-
selectedItems
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
47
|
+
Array.isArray(selectedItems) &&
|
|
48
|
+
selectedItems.forEach((element: TSelectedItem) => {
|
|
49
|
+
const currentSelection = modifiedOptions?.find(
|
|
50
|
+
(item: TFlatListItem) => item[optionValue] === element
|
|
51
|
+
);
|
|
57
52
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
53
|
+
// Only push if currentSelection is defined and is of the correct type
|
|
54
|
+
if (currentSelection) {
|
|
55
|
+
currentSelections.push(currentSelection);
|
|
56
|
+
}
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
return currentSelections;
|
|
63
60
|
}
|
|
64
61
|
|
|
65
62
|
// Single select
|
|
66
|
-
let
|
|
63
|
+
let current = modifiedOptions?.find(
|
|
67
64
|
(item: TFlatListItem) => item[optionValue] === selectedItem
|
|
68
65
|
);
|
|
69
|
-
return
|
|
66
|
+
return current ? current : {};
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
const textStyleKeys = [
|
|
70
|
+
'color',
|
|
71
|
+
'fontSize',
|
|
72
|
+
'fontFamily',
|
|
73
|
+
'fontWeight',
|
|
74
|
+
'fontStyle',
|
|
75
|
+
'textAlign',
|
|
76
|
+
'lineHeight',
|
|
77
|
+
'textDecorationLine',
|
|
78
|
+
'textDecorationStyle',
|
|
79
|
+
'textDecorationColor',
|
|
80
|
+
'textShadowColor',
|
|
81
|
+
'textShadowOffset',
|
|
82
|
+
'textShadowRadius',
|
|
83
|
+
'letterSpacing',
|
|
84
|
+
'textTransform',
|
|
85
|
+
];
|
|
86
|
+
|
|
87
|
+
export const extractTextStylesFromArray = (
|
|
88
|
+
styleArray: (ViewStyle & TextStyle)[] = []
|
|
89
|
+
) => {
|
|
90
|
+
const extractedStyles: Record<string, any> = {};
|
|
91
|
+
for (const styleObject of styleArray) {
|
|
92
|
+
if (styleObject && typeof styleObject === 'object') {
|
|
93
|
+
// Ensure it's a valid style object
|
|
94
|
+
for (const prop in styleObject) {
|
|
95
|
+
if (textStyleKeys.includes(prop)) {
|
|
96
|
+
extractedStyles[prop] = (styleObject as Record<string, any>)[prop];
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
return extractedStyles;
|
|
70
102
|
};
|