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.
Files changed (122) hide show
  1. package/README.md +38 -36
  2. package/lib/commonjs/components/CheckBox/index.js +4 -2
  3. package/lib/commonjs/components/CheckBox/index.js.map +1 -1
  4. package/lib/commonjs/components/CustomModal/index.js +11 -9
  5. package/lib/commonjs/components/CustomModal/index.js.map +1 -1
  6. package/lib/commonjs/components/Dropdown/Dropdown.js +3 -2
  7. package/lib/commonjs/components/Dropdown/Dropdown.js.map +1 -1
  8. package/lib/commonjs/components/Dropdown/DropdownListItem.js +1 -1
  9. package/lib/commonjs/components/Dropdown/DropdownListItem.js.map +1 -1
  10. package/lib/commonjs/components/Dropdown/DropdownSelectedItemsView.js +13 -13
  11. package/lib/commonjs/components/Dropdown/DropdownSelectedItemsView.js.map +1 -1
  12. package/lib/commonjs/components/Input/index.js.map +1 -1
  13. package/lib/commonjs/components/{Dropdown → List}/DropdownFlatList.js +7 -5
  14. package/lib/commonjs/components/List/DropdownFlatList.js.map +1 -0
  15. package/lib/commonjs/components/{Dropdown → List}/DropdownSectionList.js +6 -4
  16. package/lib/commonjs/components/List/DropdownSectionList.js.map +1 -0
  17. package/lib/commonjs/hooks/index.js +61 -0
  18. package/lib/commonjs/hooks/index.js.map +1 -0
  19. package/lib/commonjs/hooks/use-index-of-selected-item.js +49 -0
  20. package/lib/commonjs/hooks/use-index-of-selected-item.js.map +1 -0
  21. package/lib/commonjs/hooks/use-modal.js +40 -0
  22. package/lib/commonjs/hooks/use-modal.js.map +1 -0
  23. package/lib/commonjs/hooks/use-search.js +58 -0
  24. package/lib/commonjs/hooks/use-search.js.map +1 -0
  25. package/lib/commonjs/hooks/use-select-all.js +70 -0
  26. package/lib/commonjs/hooks/use-select-all.js.map +1 -0
  27. package/lib/commonjs/hooks/use-selection-handler.js +62 -0
  28. package/lib/commonjs/hooks/use-selection-handler.js.map +1 -0
  29. package/lib/commonjs/index.js +118 -224
  30. package/lib/commonjs/index.js.map +1 -1
  31. package/lib/commonjs/utils/index.js +40 -5
  32. package/lib/commonjs/utils/index.js.map +1 -1
  33. package/lib/module/components/CheckBox/index.js +4 -2
  34. package/lib/module/components/CheckBox/index.js.map +1 -1
  35. package/lib/module/components/CustomModal/index.js +11 -8
  36. package/lib/module/components/CustomModal/index.js.map +1 -1
  37. package/lib/module/components/Dropdown/Dropdown.js +3 -2
  38. package/lib/module/components/Dropdown/Dropdown.js.map +1 -1
  39. package/lib/module/components/Dropdown/DropdownListItem.js +1 -1
  40. package/lib/module/components/Dropdown/DropdownListItem.js.map +1 -1
  41. package/lib/module/components/Dropdown/DropdownSelectedItemsView.js +13 -13
  42. package/lib/module/components/Dropdown/DropdownSelectedItemsView.js.map +1 -1
  43. package/lib/module/components/Input/index.js.map +1 -1
  44. package/lib/module/components/{Dropdown → List}/DropdownFlatList.js +7 -5
  45. package/lib/module/components/List/DropdownFlatList.js.map +1 -0
  46. package/lib/module/components/{Dropdown → List}/DropdownSectionList.js +6 -4
  47. package/lib/module/components/List/DropdownSectionList.js.map +1 -0
  48. package/lib/module/hooks/index.js +6 -0
  49. package/lib/module/hooks/index.js.map +1 -0
  50. package/lib/module/hooks/use-index-of-selected-item.js +42 -0
  51. package/lib/module/hooks/use-index-of-selected-item.js.map +1 -0
  52. package/lib/module/hooks/use-modal.js +33 -0
  53. package/lib/module/hooks/use-modal.js.map +1 -0
  54. package/lib/module/hooks/use-search.js +51 -0
  55. package/lib/module/hooks/use-search.js.map +1 -0
  56. package/lib/module/hooks/use-select-all.js +63 -0
  57. package/lib/module/hooks/use-select-all.js.map +1 -0
  58. package/lib/module/hooks/use-selection-handler.js +55 -0
  59. package/lib/module/hooks/use-selection-handler.js.map +1 -0
  60. package/lib/module/index.js +121 -226
  61. package/lib/module/index.js.map +1 -1
  62. package/lib/module/utils/index.js +36 -4
  63. package/lib/module/utils/index.js.map +1 -1
  64. package/lib/typescript/src/components/CheckBox/checkbox.types.d.ts +4 -2
  65. package/lib/typescript/src/components/CheckBox/checkbox.types.d.ts.map +1 -1
  66. package/lib/typescript/src/components/CheckBox/index.d.ts +1 -1
  67. package/lib/typescript/src/components/CheckBox/index.d.ts.map +1 -1
  68. package/lib/typescript/src/components/CustomModal/index.d.ts +4 -2
  69. package/lib/typescript/src/components/CustomModal/index.d.ts.map +1 -1
  70. package/lib/typescript/src/components/Dropdown/Dropdown.d.ts +1 -1
  71. package/lib/typescript/src/components/Dropdown/Dropdown.d.ts.map +1 -1
  72. package/lib/typescript/src/components/Dropdown/DropdownListItem.d.ts.map +1 -1
  73. package/lib/typescript/src/components/Dropdown/DropdownSelectedItemsView.d.ts +1 -1
  74. package/lib/typescript/src/components/Input/index.d.ts +5 -1
  75. package/lib/typescript/src/components/Input/index.d.ts.map +1 -1
  76. package/lib/typescript/src/components/{Dropdown → List}/DropdownFlatList.d.ts +1 -1
  77. package/lib/typescript/src/components/List/DropdownFlatList.d.ts.map +1 -0
  78. package/lib/typescript/src/components/List/DropdownSectionList.d.ts.map +1 -0
  79. package/lib/typescript/src/hooks/index.d.ts +6 -0
  80. package/lib/typescript/src/hooks/index.d.ts.map +1 -0
  81. package/lib/typescript/src/hooks/use-index-of-selected-item.d.ts +23 -0
  82. package/lib/typescript/src/hooks/use-index-of-selected-item.d.ts.map +1 -0
  83. package/lib/typescript/src/hooks/use-modal.d.ts +15 -0
  84. package/lib/typescript/src/hooks/use-modal.d.ts.map +1 -0
  85. package/lib/typescript/src/hooks/use-search.d.ts +16 -0
  86. package/lib/typescript/src/hooks/use-search.d.ts.map +1 -0
  87. package/lib/typescript/src/hooks/use-select-all.d.ts +18 -0
  88. package/lib/typescript/src/hooks/use-select-all.d.ts.map +1 -0
  89. package/lib/typescript/src/hooks/use-selection-handler.d.ts +19 -0
  90. package/lib/typescript/src/hooks/use-selection-handler.d.ts.map +1 -0
  91. package/lib/typescript/src/index.d.ts +19 -2
  92. package/lib/typescript/src/index.d.ts.map +1 -1
  93. package/lib/typescript/src/types/index.types.d.ts +54 -38
  94. package/lib/typescript/src/types/index.types.d.ts.map +1 -1
  95. package/lib/typescript/src/utils/index.d.ts +17 -3
  96. package/lib/typescript/src/utils/index.d.ts.map +1 -1
  97. package/package.json +21 -7
  98. package/src/components/CheckBox/checkbox.types.ts +2 -2
  99. package/src/components/CheckBox/index.tsx +8 -6
  100. package/src/components/CustomModal/index.tsx +14 -17
  101. package/src/components/Dropdown/Dropdown.tsx +3 -2
  102. package/src/components/Dropdown/DropdownListItem.tsx +1 -3
  103. package/src/components/Dropdown/DropdownSelectedItemsView.tsx +12 -12
  104. package/src/components/Input/index.tsx +13 -2
  105. package/src/components/{Dropdown → List}/DropdownFlatList.tsx +11 -8
  106. package/src/components/{Dropdown → List}/DropdownSectionList.tsx +10 -7
  107. package/src/hooks/index.ts +5 -0
  108. package/src/hooks/use-index-of-selected-item.ts +49 -0
  109. package/src/hooks/use-modal.ts +44 -0
  110. package/src/hooks/use-search.ts +95 -0
  111. package/src/hooks/use-select-all.ts +79 -0
  112. package/src/hooks/use-selection-handler.ts +81 -0
  113. package/src/index.tsx +300 -443
  114. package/src/types/index.types.ts +67 -40
  115. package/src/utils/index.ts +60 -3
  116. package/lib/commonjs/components/Dropdown/DropdownFlatList.js.map +0 -1
  117. package/lib/commonjs/components/Dropdown/DropdownSectionList.js.map +0 -1
  118. package/lib/module/components/Dropdown/DropdownFlatList.js.map +0 -1
  119. package/lib/module/components/Dropdown/DropdownSectionList.js.map +0 -1
  120. package/lib/typescript/src/components/Dropdown/DropdownFlatList.d.ts.map +0 -1
  121. package/lib/typescript/src/components/Dropdown/DropdownSectionList.d.ts.map +0 -1
  122. /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
- getSelectedItemsLabel,
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
- getSelectedItemsLabel()?.map((label: string, i: Number) => (
67
+ labelsOfSelectedItems?.map((label: string, i: Number) => (
62
68
  <DropdownContent
63
- onPress={() => {
64
- openModal();
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={getSelectedItemsLabel()}
84
+ label={labelsOfSelectedItems}
85
85
  disabled={disabled}
86
86
  />
87
87
  )}
@@ -1,5 +1,13 @@
1
1
  import React, { useState } from 'react';
2
- import { TextInput, StyleSheet, View, Platform } from 'react-native';
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
- }: any) => {
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 './DropdownListItem';
4
+ import DropdownListItem from '../Dropdown/DropdownListItem';
5
5
  import { ItemSeparatorComponent, ListEmptyComponent } from '../Others';
6
- import { TFlatList } from 'src/types/index.types';
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.current?.scrollToIndex({
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 './DropdownListItem';
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 'src/types/index.types';
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,5 @@
1
+ export * from './use-search';
2
+ export * from './use-selection-handler';
3
+ export * from './use-index-of-selected-item';
4
+ export * from './use-modal';
5
+ export * from './use-select-all';
@@ -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
+ };