@geotab/zenith 3.0.1 → 3.1.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 (262) hide show
  1. package/README.md +17 -0
  2. package/dist/advancedGroupsFilter/advancedGroupsFilter.d.ts +1 -0
  3. package/dist/advancedGroupsFilter/advancedGroupsFilter.js +2 -2
  4. package/dist/advancedGroupsFilter/advancedGroupsFilterFormSection.js +3 -1
  5. package/dist/chip/chip.js +5 -5
  6. package/dist/commonStyles/zIndex.less +5 -0
  7. package/dist/dateInput/dateInput.d.ts +2 -1
  8. package/dist/dateInput/dateInput.js +11 -2
  9. package/dist/dateRange/dateRange.js +9 -8
  10. package/dist/dropdown/dropdown.d.ts +2 -0
  11. package/dist/dropdown/dropdown.js +6 -5
  12. package/dist/dropdown/dropdownHelper.d.ts +4 -4
  13. package/dist/dropdown/dropdownHelper.js +2 -1
  14. package/dist/dropdown/dropdownList.js +2 -2
  15. package/dist/dropdown/dropdownPopup.d.ts +1 -0
  16. package/dist/dropdown/dropdownPopup.js +2 -2
  17. package/dist/dropdown/dropdownSearchableTrigger.js +1 -1
  18. package/dist/dropdown/stateReducer/stateReducer.d.ts +6 -2
  19. package/dist/dropdown/stateReducer/stateReducer.js +28 -42
  20. package/dist/dropdown/stateReducer/stateReducerHelper.d.ts +2 -0
  21. package/dist/dropdown/stateReducer/stateReducerHelper.js +12 -1
  22. package/dist/dropdown/useDropdownState.d.ts +1 -1
  23. package/dist/dropdown/useDropdownState.js +5 -2
  24. package/dist/filters/components/filtersContainer.d.ts +1 -0
  25. package/dist/filters/components/filtersContainer.js +10 -11
  26. package/dist/filters/components/filtersDateInput.d.ts +10 -0
  27. package/dist/filters/components/filtersDateInput.js +27 -0
  28. package/dist/filters/components/filtersDropdown.d.ts +5 -4
  29. package/dist/filters/components/filtersDropdown.js +3 -3
  30. package/dist/filters/components/filtersEmptySelectedList.d.ts +2 -0
  31. package/dist/filters/components/filtersEmptySelectedList.js +11 -0
  32. package/dist/filters/components/filtersItem.d.ts +5 -4
  33. package/dist/filters/components/filtersItem.js +2 -1
  34. package/dist/filters/components/filtersSaveModal.d.ts +3 -1
  35. package/dist/filters/components/filtersSaveModal.js +2 -2
  36. package/dist/filters/components/filtersSavedChipComponent.js +68 -6
  37. package/dist/filters/components/filtersSearch.d.ts +4 -4
  38. package/dist/filters/components/filtersSearch.js +20 -9
  39. package/dist/filters/components/filtersSearchItemData.js +18 -1
  40. package/dist/filters/components/filtersSearchList.d.ts +5 -1
  41. package/dist/filters/components/filtersSearchList.js +155 -27
  42. package/dist/filters/components/filtersSelect.d.ts +1 -0
  43. package/dist/filters/components/filtersSelect.js +35 -7
  44. package/dist/filters/components/filtersSelectListItem.d.ts +21 -15
  45. package/dist/filters/components/filtersSelectListItem.js +13 -3
  46. package/dist/filters/components/filtersSidePanel.d.ts +15 -0
  47. package/dist/filters/components/filtersSidePanel.js +212 -0
  48. package/dist/filters/components/filtersSidePanelChip.d.ts +9 -0
  49. package/dist/filters/components/filtersSidePanelChip.js +13 -0
  50. package/dist/filters/components/filtersSidePanelDropdown.d.ts +6 -0
  51. package/dist/filters/components/filtersSidePanelDropdown.js +85 -0
  52. package/dist/filters/components/filtersSidePanelItem.d.ts +16 -0
  53. package/dist/filters/components/filtersSidePanelItem.js +67 -0
  54. package/dist/filters/components/filtersSidePanelRange.d.ts +6 -0
  55. package/dist/filters/components/filtersSidePanelRange.js +28 -0
  56. package/dist/filters/filters.d.ts +5 -0
  57. package/dist/filters/filters.js +101 -38
  58. package/dist/filters/filtersHelper.d.ts +4 -2
  59. package/dist/filters/filtersHelper.js +40 -1
  60. package/dist/filters/filtersHooks.d.ts +12 -2
  61. package/dist/filters/filtersHooks.js +21 -3
  62. package/dist/formStepper/formStepper.d.ts +1 -1
  63. package/dist/formStepper/formStepper.js +5 -2
  64. package/dist/groupsFilter/groupsFilter.js +72 -37
  65. package/dist/groupsFilter/groupsFilterMenu.d.ts +1 -0
  66. package/dist/groupsFilter/groupsFilterMenu.js +2 -2
  67. package/dist/groupsFilter/groupsFilterTrigger.js +1 -1
  68. package/dist/images/imageLooking.d.ts +4 -0
  69. package/dist/images/imageLooking.js +16 -0
  70. package/dist/index.css +407 -83
  71. package/dist/index.d.ts +7 -4
  72. package/dist/index.js +26 -15
  73. package/dist/list/list.d.ts +3 -1
  74. package/dist/list/list.js +2 -2
  75. package/dist/mobileSheet/mobileSheet.d.ts +2 -0
  76. package/dist/mobileSheet/mobileSheet.js +4 -4
  77. package/dist/modal/modal.d.ts +1 -0
  78. package/dist/modal/modal.js +2 -2
  79. package/dist/radioGroup/radioGroup.d.ts +1 -0
  80. package/dist/radioGroup/radioGroup.js +3 -2
  81. package/dist/range/range.js +29 -10
  82. package/dist/searchInputRaw/searchInputRaw.js +1 -1
  83. package/dist/sidePanel/sidePanel.d.ts +1 -0
  84. package/dist/sidePanel/sidePanel.js +3 -3
  85. package/dist/tabs/tabItem/tabItem.d.ts +1 -0
  86. package/dist/tabs/tabItem/tabItem.js +2 -2
  87. package/dist/tabs/tabs.d.ts +1 -0
  88. package/dist/tabs/tabs.js +1 -1
  89. package/dist/utils/keyboardHelpers.d.ts +2 -0
  90. package/dist/utils/keyboardHelpers.js +49 -0
  91. package/dist/utils/localization/translations/cs-json.d.ts +1 -0
  92. package/dist/utils/localization/translations/cs-json.js +2 -1
  93. package/dist/utils/localization/translations/de-json.d.ts +1 -0
  94. package/dist/utils/localization/translations/de-json.js +2 -1
  95. package/dist/utils/localization/translations/en-json.d.ts +7 -0
  96. package/dist/utils/localization/translations/en-json.js +8 -1
  97. package/dist/utils/localization/translations/es-json.d.ts +1 -0
  98. package/dist/utils/localization/translations/es-json.js +2 -1
  99. package/dist/utils/localization/translations/fr-FR-json.d.ts +1 -0
  100. package/dist/utils/localization/translations/fr-FR-json.js +2 -1
  101. package/dist/utils/localization/translations/fr-json.d.ts +1 -0
  102. package/dist/utils/localization/translations/fr-json.js +2 -1
  103. package/dist/utils/localization/translations/id-json.d.ts +1 -0
  104. package/dist/utils/localization/translations/id-json.js +2 -1
  105. package/dist/utils/localization/translations/it-json.d.ts +1 -0
  106. package/dist/utils/localization/translations/it-json.js +2 -1
  107. package/dist/utils/localization/translations/ja-json.d.ts +1 -0
  108. package/dist/utils/localization/translations/ja-json.js +2 -1
  109. package/dist/utils/localization/translations/ko-KR-json.d.ts +1 -0
  110. package/dist/utils/localization/translations/ko-KR-json.js +2 -1
  111. package/dist/utils/localization/translations/ms-json.d.ts +1 -0
  112. package/dist/utils/localization/translations/ms-json.js +2 -1
  113. package/dist/utils/localization/translations/nl-json.d.ts +1 -0
  114. package/dist/utils/localization/translations/nl-json.js +2 -1
  115. package/dist/utils/localization/translations/pl-json.d.ts +1 -0
  116. package/dist/utils/localization/translations/pl-json.js +2 -1
  117. package/dist/utils/localization/translations/pt-BR-json.d.ts +1 -0
  118. package/dist/utils/localization/translations/pt-BR-json.js +2 -1
  119. package/dist/utils/localization/translations/sv-json.d.ts +1 -0
  120. package/dist/utils/localization/translations/sv-json.js +2 -1
  121. package/dist/utils/localization/translations/th-json.d.ts +1 -0
  122. package/dist/utils/localization/translations/th-json.js +2 -1
  123. package/dist/utils/localization/translations/tr-json.d.ts +1 -0
  124. package/dist/utils/localization/translations/tr-json.js +2 -1
  125. package/dist/utils/localization/translations/zh-Hans-json.d.ts +1 -0
  126. package/dist/utils/localization/translations/zh-Hans-json.js +2 -1
  127. package/dist/utils/localization/translations/zh-TW-json.d.ts +1 -0
  128. package/dist/utils/localization/translations/zh-TW-json.js +2 -1
  129. package/esm/advancedGroupsFilter/advancedGroupsFilter.d.ts +1 -0
  130. package/esm/advancedGroupsFilter/advancedGroupsFilter.js +2 -2
  131. package/esm/advancedGroupsFilter/advancedGroupsFilterFormSection.js +3 -1
  132. package/esm/chip/chip.js +5 -5
  133. package/esm/dateInput/dateInput.d.ts +2 -1
  134. package/esm/dateInput/dateInput.js +11 -2
  135. package/esm/dateRange/dateRange.js +9 -8
  136. package/esm/dropdown/dropdown.d.ts +2 -0
  137. package/esm/dropdown/dropdown.js +5 -4
  138. package/esm/dropdown/dropdownHelper.d.ts +4 -4
  139. package/esm/dropdown/dropdownHelper.js +2 -1
  140. package/esm/dropdown/dropdownList.js +2 -2
  141. package/esm/dropdown/dropdownPopup.d.ts +1 -0
  142. package/esm/dropdown/dropdownPopup.js +2 -2
  143. package/esm/dropdown/dropdownSearchableTrigger.js +1 -1
  144. package/esm/dropdown/stateReducer/stateReducer.d.ts +6 -2
  145. package/esm/dropdown/stateReducer/stateReducer.js +29 -43
  146. package/esm/dropdown/stateReducer/stateReducerHelper.d.ts +2 -0
  147. package/esm/dropdown/stateReducer/stateReducerHelper.js +10 -0
  148. package/esm/dropdown/useDropdownState.d.ts +1 -1
  149. package/esm/dropdown/useDropdownState.js +5 -2
  150. package/esm/filters/components/filtersContainer.d.ts +1 -0
  151. package/esm/filters/components/filtersContainer.js +12 -13
  152. package/esm/filters/components/filtersDateInput.d.ts +10 -0
  153. package/esm/filters/components/filtersDateInput.js +23 -0
  154. package/esm/filters/components/filtersDropdown.d.ts +5 -4
  155. package/esm/filters/components/filtersDropdown.js +3 -3
  156. package/esm/filters/components/filtersEmptySelectedList.d.ts +2 -0
  157. package/esm/filters/components/filtersEmptySelectedList.js +7 -0
  158. package/esm/filters/components/filtersItem.d.ts +5 -4
  159. package/esm/filters/components/filtersItem.js +2 -1
  160. package/esm/filters/components/filtersSaveModal.d.ts +3 -1
  161. package/esm/filters/components/filtersSaveModal.js +2 -2
  162. package/esm/filters/components/filtersSavedChipComponent.js +68 -6
  163. package/esm/filters/components/filtersSearch.d.ts +4 -4
  164. package/esm/filters/components/filtersSearch.js +20 -9
  165. package/esm/filters/components/filtersSearchItemData.js +18 -1
  166. package/esm/filters/components/filtersSearchList.d.ts +5 -1
  167. package/esm/filters/components/filtersSearchList.js +156 -28
  168. package/esm/filters/components/filtersSelect.d.ts +1 -0
  169. package/esm/filters/components/filtersSelect.js +36 -8
  170. package/esm/filters/components/filtersSelectListItem.d.ts +21 -15
  171. package/esm/filters/components/filtersSelectListItem.js +13 -3
  172. package/esm/filters/components/filtersSidePanel.d.ts +15 -0
  173. package/esm/filters/components/filtersSidePanel.js +208 -0
  174. package/esm/filters/components/filtersSidePanelChip.d.ts +9 -0
  175. package/esm/filters/components/filtersSidePanelChip.js +9 -0
  176. package/esm/filters/components/filtersSidePanelDropdown.d.ts +6 -0
  177. package/esm/filters/components/filtersSidePanelDropdown.js +81 -0
  178. package/esm/filters/components/filtersSidePanelItem.d.ts +16 -0
  179. package/esm/filters/components/filtersSidePanelItem.js +63 -0
  180. package/esm/filters/components/filtersSidePanelRange.d.ts +6 -0
  181. package/esm/filters/components/filtersSidePanelRange.js +24 -0
  182. package/esm/filters/filters.d.ts +5 -0
  183. package/esm/filters/filters.js +101 -38
  184. package/esm/filters/filtersHelper.d.ts +4 -2
  185. package/esm/filters/filtersHelper.js +37 -0
  186. package/esm/filters/filtersHooks.d.ts +12 -2
  187. package/esm/filters/filtersHooks.js +19 -2
  188. package/esm/formStepper/formStepper.d.ts +1 -1
  189. package/esm/formStepper/formStepper.js +5 -2
  190. package/esm/groupsFilter/groupsFilter.js +72 -37
  191. package/esm/groupsFilter/groupsFilterMenu.d.ts +1 -0
  192. package/esm/groupsFilter/groupsFilterMenu.js +2 -2
  193. package/esm/groupsFilter/groupsFilterTrigger.js +1 -1
  194. package/esm/images/imageLooking.d.ts +4 -0
  195. package/esm/images/imageLooking.js +12 -0
  196. package/esm/index.d.ts +7 -4
  197. package/esm/index.js +6 -3
  198. package/esm/list/list.d.ts +3 -1
  199. package/esm/list/list.js +2 -2
  200. package/esm/mobileSheet/mobileSheet.d.ts +2 -0
  201. package/esm/mobileSheet/mobileSheet.js +4 -4
  202. package/esm/modal/modal.d.ts +1 -0
  203. package/esm/modal/modal.js +2 -2
  204. package/esm/radioGroup/radioGroup.d.ts +1 -0
  205. package/esm/radioGroup/radioGroup.js +3 -2
  206. package/esm/range/range.js +29 -10
  207. package/esm/searchInputRaw/searchInputRaw.js +1 -1
  208. package/esm/sidePanel/sidePanel.d.ts +1 -0
  209. package/esm/sidePanel/sidePanel.js +3 -3
  210. package/esm/tabs/tabItem/tabItem.d.ts +1 -0
  211. package/esm/tabs/tabItem/tabItem.js +2 -2
  212. package/esm/tabs/tabs.d.ts +1 -0
  213. package/esm/tabs/tabs.js +1 -1
  214. package/esm/utils/keyboardHelpers.d.ts +2 -0
  215. package/esm/utils/keyboardHelpers.js +44 -0
  216. package/esm/utils/localization/translations/cs-json.d.ts +1 -0
  217. package/esm/utils/localization/translations/cs-json.js +2 -1
  218. package/esm/utils/localization/translations/de-json.d.ts +1 -0
  219. package/esm/utils/localization/translations/de-json.js +2 -1
  220. package/esm/utils/localization/translations/en-json.d.ts +7 -0
  221. package/esm/utils/localization/translations/en-json.js +8 -1
  222. package/esm/utils/localization/translations/es-json.d.ts +1 -0
  223. package/esm/utils/localization/translations/es-json.js +2 -1
  224. package/esm/utils/localization/translations/fr-FR-json.d.ts +1 -0
  225. package/esm/utils/localization/translations/fr-FR-json.js +2 -1
  226. package/esm/utils/localization/translations/fr-json.d.ts +1 -0
  227. package/esm/utils/localization/translations/fr-json.js +2 -1
  228. package/esm/utils/localization/translations/id-json.d.ts +1 -0
  229. package/esm/utils/localization/translations/id-json.js +2 -1
  230. package/esm/utils/localization/translations/it-json.d.ts +1 -0
  231. package/esm/utils/localization/translations/it-json.js +2 -1
  232. package/esm/utils/localization/translations/ja-json.d.ts +1 -0
  233. package/esm/utils/localization/translations/ja-json.js +2 -1
  234. package/esm/utils/localization/translations/ko-KR-json.d.ts +1 -0
  235. package/esm/utils/localization/translations/ko-KR-json.js +2 -1
  236. package/esm/utils/localization/translations/ms-json.d.ts +1 -0
  237. package/esm/utils/localization/translations/ms-json.js +2 -1
  238. package/esm/utils/localization/translations/nl-json.d.ts +1 -0
  239. package/esm/utils/localization/translations/nl-json.js +2 -1
  240. package/esm/utils/localization/translations/pl-json.d.ts +1 -0
  241. package/esm/utils/localization/translations/pl-json.js +2 -1
  242. package/esm/utils/localization/translations/pt-BR-json.d.ts +1 -0
  243. package/esm/utils/localization/translations/pt-BR-json.js +2 -1
  244. package/esm/utils/localization/translations/sv-json.d.ts +1 -0
  245. package/esm/utils/localization/translations/sv-json.js +2 -1
  246. package/esm/utils/localization/translations/th-json.d.ts +1 -0
  247. package/esm/utils/localization/translations/th-json.js +2 -1
  248. package/esm/utils/localization/translations/tr-json.d.ts +1 -0
  249. package/esm/utils/localization/translations/tr-json.js +2 -1
  250. package/esm/utils/localization/translations/zh-Hans-json.d.ts +1 -0
  251. package/esm/utils/localization/translations/zh-Hans-json.js +2 -1
  252. package/esm/utils/localization/translations/zh-TW-json.d.ts +1 -0
  253. package/esm/utils/localization/translations/zh-TW-json.js +2 -1
  254. package/package.json +1 -1
  255. package/dist/filters/components/filtersModal.d.ts +0 -17
  256. package/dist/filters/components/filtersModal.js +0 -107
  257. package/dist/filters/components/filtersModalItem.d.ts +0 -9
  258. package/dist/filters/components/filtersModalItem.js +0 -74
  259. package/esm/filters/components/filtersModal.d.ts +0 -17
  260. package/esm/filters/components/filtersModal.js +0 -103
  261. package/esm/filters/components/filtersModalItem.d.ts +0 -9
  262. package/esm/filters/components/filtersModalItem.js +0 -70
@@ -1,5 +1,5 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { useCallback, useEffect, useMemo, useState } from "react";
1
+ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useCallback, useEffect, useMemo, useRef, useState } from "react";
3
3
  import { List } from "../../list/list";
4
4
  import { useLanguage } from "../../utils/localization/useLanguage";
5
5
  import { Button } from "../../button/button";
@@ -11,61 +11,189 @@ import { useMobile } from "../../commonHelpers/hooks/useMobile";
11
11
  import { useDrive } from "../../utils/theme/useDrive";
12
12
  import { classNames } from "../../commonHelpers/classNames/classNames";
13
13
  import { MAX_TOTAL_RESULTS } from "../filtersHelper";
14
+ import { EmptySearchList } from "../../dataGrid/emptySearchList/emptySearchList";
15
+ import { getNewFocusableItem } from "../../utils/keyboardHelpers";
16
+ import { FOCUSABLE_SELECTOR } from "../../utils/focusableSelector";
14
17
  export const NO_TYPED_SEARCH_ITEM = "noTypedSearchItem";
15
18
  export const KEYWORD_ID = "keywordId";
16
19
  export const KEYWORD_TYPE = "keyword";
17
- export const FiltersSearchList = ({ searchValue, searchData, recentSearches, searchSelection, handleSearchItemClick, onRemoveRecentSearchItem, handleRecentSearchItemClick, getIconByType }) => {
20
+ export const NOT_POPULATED_NAME = "...";
21
+ export const FiltersSearchList = ({ searchValue, searchData, recentSearches, searchSelection, onlyOneType, handleSearchItemClick, onRemoveRecentSearchItem, handleRecentSearchItemClick, getIconByType, handleClearSearchSelection, searchListRef }) => {
18
22
  const { translate } = useLanguage();
19
- const isSearchMode = Boolean(handleSearchItemClick && searchSelection);
23
+ const isSearchModeAvailable = Boolean(handleSearchItemClick && searchSelection);
24
+ const isRecentSearchesModeAvailable = Boolean(recentSearches && handleRecentSearchItemClick && onRemoveRecentSearchItem);
20
25
  const isMobile = useMobile();
21
26
  const isDrive = useDrive();
22
27
  const [removedItems, setRemovedItems] = useState([]);
23
- const [historyMode, setHistoryMode] = useState(false);
28
+ const initialSearchSelectionRef = useRef(searchSelection || []);
29
+ const hasNotPopulatedName = initialSearchSelectionRef.current.length === 0 ? false : initialSearchSelectionRef.current.some(el => el.name === NOT_POPULATED_NAME);
30
+ const getCurrentMode = useCallback(() => {
31
+ if (searchValue) {
32
+ return "search";
33
+ }
34
+ if (isSearchModeAvailable && initialSearchSelectionRef.current.length > 0) {
35
+ return "selected";
36
+ }
37
+ if (isRecentSearchesModeAvailable && (recentSearches === null || recentSearches === void 0 ? void 0 : recentSearches.data) && recentSearches.data.length > 0) {
38
+ return "recent";
39
+ }
40
+ return "";
41
+ }, [searchValue, isSearchModeAvailable, isRecentSearchesModeAvailable, recentSearches === null || recentSearches === void 0 ? void 0 : recentSearches.data]);
42
+ const [currentMode, setCurrentMode] = useState(getCurrentMode());
24
43
  const waitingItemsLimit = 5;
25
- const isRecentSearchesMode = Boolean(recentSearches && handleRecentSearchItemClick && onRemoveRecentSearchItem);
26
44
  // Handle React Query errors
27
45
  useEffect(() => {
28
- if (isSearchMode && (searchData === null || searchData === void 0 ? void 0 : searchData.error)) {
46
+ if (currentMode === "search" && (searchData === null || searchData === void 0 ? void 0 : searchData.error)) {
29
47
  console.error("Failed to fetch search results. Error:", searchData.error);
30
48
  }
31
- }, [isSearchMode, searchData === null || searchData === void 0 ? void 0 : searchData.error]);
49
+ }, [currentMode, searchData === null || searchData === void 0 ? void 0 : searchData.error]);
50
+ useEffect(() => {
51
+ if (currentMode !== "search" && searchValue) {
52
+ setCurrentMode("search");
53
+ }
54
+ }, [searchValue, currentMode]);
55
+ useEffect(() => {
56
+ if (currentMode === "recent" && searchSelection && searchSelection.length === 0) {
57
+ initialSearchSelectionRef.current = [];
58
+ }
59
+ }, [isSearchModeAvailable, isRecentSearchesModeAvailable, currentMode, searchSelection]);
32
60
  // Reset history mode when search value changes
33
61
  useEffect(() => {
34
- if (historyMode && searchValue) {
35
- setHistoryMode(false);
36
- }
37
- }, [historyMode, searchValue]);
38
- const searchItems = useMemo(() => isSearchMode ? (searchData === null || searchData === void 0 ? void 0 : searchData.data) || [] : [], [isSearchMode, searchData === null || searchData === void 0 ? void 0 : searchData.data]);
39
- const recentItems = useMemo(() => isRecentSearchesMode ? (recentSearches === null || recentSearches === void 0 ? void 0 : recentSearches.data) || [] : [], [isRecentSearchesMode, recentSearches === null || recentSearches === void 0 ? void 0 : recentSearches.data]);
40
- const isLoading = searchValue && isSearchMode && (searchData === null || searchData === void 0 ? void 0 : searchData.isLoading) || (isRecentSearchesMode ? recentSearches === null || recentSearches === void 0 ? void 0 : recentSearches.isLoading : false) || false;
41
- const changeMode = useCallback(() => {
42
- setHistoryMode(prev => !prev);
62
+ if (currentMode === "history" && searchValue) {
63
+ setCurrentMode(isSearchModeAvailable ? "search" : "");
64
+ }
65
+ else if (currentMode === "search" && !searchValue) {
66
+ setCurrentMode(getCurrentMode());
67
+ }
68
+ }, [currentMode, getCurrentMode, isRecentSearchesModeAvailable, isSearchModeAvailable, searchValue]);
69
+ useEffect(() => {
70
+ if (searchSelection && searchSelection.length === 0) {
71
+ return;
72
+ }
73
+ searchSelection === null || searchSelection === void 0 ? void 0 : searchSelection.forEach(item => {
74
+ const existingItem = initialSearchSelectionRef.current.find(el => (onlyOneType ? el.id === item.id : el.id === item.id && el.type === item.type));
75
+ if (!existingItem) {
76
+ initialSearchSelectionRef.current = [...initialSearchSelectionRef.current, item];
77
+ }
78
+ else if (hasNotPopulatedName) {
79
+ existingItem.name = item.name;
80
+ existingItem.description = item.description;
81
+ existingItem.type = item.type;
82
+ }
83
+ });
84
+ }, [hasNotPopulatedName, onlyOneType, searchSelection]);
85
+ const searchItems = useMemo(() => currentMode === "search" || currentMode === "" ? (searchData === null || searchData === void 0 ? void 0 : searchData.data) || [] : [], [currentMode, searchData === null || searchData === void 0 ? void 0 : searchData.data]);
86
+ const recentItems = useMemo(() => currentMode === "recent" || currentMode === "history" ? (recentSearches === null || recentSearches === void 0 ? void 0 : recentSearches.data) || [] : [], [currentMode, recentSearches === null || recentSearches === void 0 ? void 0 : recentSearches.data]);
87
+ const isLoading = currentMode === "search" && (searchData === null || searchData === void 0 ? void 0 : searchData.isLoading) || (currentMode === "recent" || currentMode === "history" ? recentSearches === null || recentSearches === void 0 ? void 0 : recentSearches.isLoading : false) || false;
88
+ const changeMode = useCallback((newVal) => {
89
+ setCurrentMode(newVal);
43
90
  setRemovedItems([]);
44
91
  // Note: In React Query implementation, the parent component should handle
45
92
  // refetching data when switching between modes
46
93
  }, []);
47
94
  const handleRemove = useCallback((itemId) => () => {
95
+ var _a;
48
96
  setRemovedItems(prev => [...prev, itemId]);
49
97
  onRemoveRecentSearchItem && onRemoveRecentSearchItem([itemId]);
50
- }, [onRemoveRecentSearchItem]);
98
+ // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
99
+ const firstItem = (_a = searchListRef.current) === null || _a === void 0 ? void 0 : _a.querySelector(FOCUSABLE_SELECTOR);
100
+ firstItem === null || firstItem === void 0 ? void 0 : firstItem.focus();
101
+ }, [onRemoveRecentSearchItem, searchListRef]);
51
102
  const handleRemoveAll = useCallback(() => {
52
103
  setRemovedItems(recentItems.map(item => item.id));
53
104
  onRemoveRecentSearchItem && onRemoveRecentSearchItem(recentItems.map(item => item.id));
54
- changeMode();
55
- }, [onRemoveRecentSearchItem, recentItems, changeMode]);
105
+ changeMode(getCurrentMode());
106
+ }, [recentItems, onRemoveRecentSearchItem, changeMode, getCurrentMode]);
56
107
  const memoizedSearchItemClick = useCallback((itemId, itemType) => () => {
57
- const currentSearchItem = searchItems.find(item => item.id === itemId && item.type === itemType);
108
+ const currentSearchItem = onlyOneType && !itemType ? searchItems.find(item => item.id === itemId)
109
+ : searchItems.find(item => item.id === itemId && item.type === itemType);
58
110
  currentSearchItem && handleSearchItemClick && handleSearchItemClick(currentSearchItem);
59
- }, [handleSearchItemClick, searchItems]);
111
+ }, [handleSearchItemClick, onlyOneType, searchItems]);
112
+ const memoizedSelectedItemClick = useCallback((itemId, itemType) => () => {
113
+ const currentSelectedItem = onlyOneType && !itemType ? initialSearchSelectionRef.current.find(item => item.id === itemId)
114
+ : initialSearchSelectionRef.current.find(item => item.id === itemId && item.type === itemType);
115
+ currentSelectedItem && handleSearchItemClick && handleSearchItemClick(currentSelectedItem);
116
+ }, [handleSearchItemClick, onlyOneType]);
60
117
  const memoizedRecentSearchItemClick = useCallback((itemId, itemType) => () => {
61
118
  const currentSearchItem = recentItems.find(item => item.id === itemId && item.type === itemType);
62
119
  currentSearchItem && handleRecentSearchItemClick && handleRecentSearchItemClick(currentSearchItem);
63
120
  }, [handleRecentSearchItemClick, recentItems]);
121
+ const memoizedClearSelectedItems = useCallback(() => {
122
+ var _a;
123
+ handleClearSearchSelection === null || handleClearSearchSelection === void 0 ? void 0 : handleClearSearchSelection();
124
+ initialSearchSelectionRef.current = [];
125
+ setCurrentMode(isRecentSearchesModeAvailable && ((_a = recentSearches === null || recentSearches === void 0 ? void 0 : recentSearches.data) === null || _a === void 0 ? void 0 : _a.length) ? "recent" : "");
126
+ }, [handleClearSearchSelection, isRecentSearchesModeAvailable, recentSearches]);
127
+ const handleKeyDown = useCallback((event) => {
128
+ const isListItem = event.target instanceof HTMLElement && event.target.dataset.listItem === "true";
129
+ const isButtonItem = event.target instanceof HTMLElement && event.target.dataset.closeButton === "true";
130
+ const getSelector = () => {
131
+ if (isListItem) {
132
+ return `[data-list-item="true"]`;
133
+ }
134
+ if (isButtonItem) {
135
+ return `[data-close-button="true"]`;
136
+ }
137
+ return undefined;
138
+ };
139
+ if (event.key === "ArrowDown") {
140
+ const nextItem = getNewFocusableItem(1, searchListRef.current, getSelector());
141
+ nextItem === null || nextItem === void 0 ? void 0 : nextItem.focus();
142
+ return;
143
+ }
144
+ if (event.key === "ArrowUp") {
145
+ const prevItem = getNewFocusableItem(-1, searchListRef.current, getSelector());
146
+ prevItem === null || prevItem === void 0 ? void 0 : prevItem.focus();
147
+ return;
148
+ }
149
+ if (event.key === "ArrowRight" && isListItem) {
150
+ const nextItem = getNewFocusableItem(1, searchListRef.current, `[data-close-button="true"]`);
151
+ nextItem === null || nextItem === void 0 ? void 0 : nextItem.focus();
152
+ return;
153
+ }
154
+ if (event.key === "ArrowLeft" && isButtonItem) {
155
+ const prevItem = getNewFocusableItem(-1, searchListRef.current, `[data-list-item="true"]`);
156
+ prevItem === null || prevItem === void 0 ? void 0 : prevItem.focus();
157
+ return;
158
+ }
159
+ }, [searchListRef]);
64
160
  const memoizedRecentDataArr = useMemo(() => recentItems.filter(item => !removedItems.includes(item.id)).reverse(), [recentItems, removedItems]);
65
- const memoizedRecentData = useMemo(() => (historyMode ? memoizedRecentDataArr : (searchValue && isSearchMode ? [] : memoizedRecentDataArr.slice(0, waitingItemsLimit))).map(item => (_jsx(FiltersSearchItemData, { id: item.id, name: item.name, isActive: false, secondary: item.description, onClose: handleRemove(item.id), onClick: memoizedRecentSearchItemClick(item.id, item.type || NO_TYPED_SEARCH_ITEM), icon: getIconByType(item.type), isMobile: isMobile, isDrive: isDrive }, `recent-${item.id}`))), [historyMode, memoizedRecentDataArr, searchValue, isSearchMode, handleRemove, memoizedRecentSearchItemClick, getIconByType, isMobile, isDrive]);
66
- const memoizedSearchData = useMemo(() => historyMode || !searchValue && isRecentSearchesMode ? [] : searchItems.slice(0, MAX_TOTAL_RESULTS).map(item => (_jsx(FiltersSearchItemData, { id: item.id, name: item.name, isActive: isSearchMode ? searchSelection.filter(el => el.id === item.id).some(el => el.type === (item.type || NO_TYPED_SEARCH_ITEM)) || false : false, secondary: item.description, onClose: undefined, onClick: isSearchMode ? memoizedSearchItemClick(item.id, item.type || NO_TYPED_SEARCH_ITEM) : undefined, icon: getIconByType(item.type), isMobile: isMobile, isDrive: isDrive }, `search-${item.id}-${item.type || NO_TYPED_SEARCH_ITEM}`))), [historyMode, searchValue, isRecentSearchesMode, searchItems, isSearchMode, searchSelection, memoizedSearchItemClick, getIconByType, isMobile, isDrive]);
67
- const resultsToShow = useMemo(() => isRecentSearchesMode ? [...memoizedRecentData, ...memoizedSearchData]
68
- : memoizedSearchData, [isRecentSearchesMode, memoizedRecentData, memoizedSearchData]);
69
- return _jsxs("div", { className: "zen-filters-search-list", children: [searchValue ? null : _jsxs("div", { className: classNames(["zen-filters-search-list__header", isDrive ? (isMobile ? "zen-filters-search-list__header--drive" : "zen-filters-search-list__header--drive-tablet") : ""]), children: [historyMode ? _jsx(TextIconButton, { className: "zen-filters-search-list__header-back-button", title: translate("Back"), type: "tertiary", onClick: changeMode, icon: IconChevronLeft, iconPosition: ButtonIconPosition.Start })
70
- : null, isRecentSearchesMode ? _jsx("div", { className: "zen-filters-search-list__header-title", children: translate("Recent searches") }) : null, historyMode || !isRecentSearchesMode ? null : _jsx(Button, { type: "tertiary", onClick: changeMode, children: translate("View history") }), historyMode && isRecentSearchesMode ? _jsx(Button, { type: "tertiary", onClick: handleRemoveAll, children: translate("Clear all") }) : null] }), _jsx(List, { className: "zen-filters-search-list__data", isLoading: isLoading, type: "data", waitingItems: waitingItemsLimit, children: resultsToShow })] });
161
+ const memoizedRecentData = useMemo(() => {
162
+ if (currentMode !== "recent" && currentMode !== "history") {
163
+ return [];
164
+ }
165
+ const recentDataArr = currentMode === "history" ? memoizedRecentDataArr : memoizedRecentDataArr.slice(0, waitingItemsLimit);
166
+ return recentDataArr.map(item => (_jsx(FiltersSearchItemData, { id: item.id, name: item.name, isActive: false, secondary: item.description, onClose: handleRemove(item.id), onClick: memoizedRecentSearchItemClick(item.id, item.type), icon: getIconByType(item.type || onlyOneType), isMobile: isMobile, isDrive: isDrive }, `${currentMode}-${item.id}`)));
167
+ }, [currentMode, memoizedRecentDataArr, handleRemove, memoizedRecentSearchItemClick, getIconByType, onlyOneType, isMobile, isDrive]);
168
+ const memoizedSearchData = useMemo(() => currentMode !== "search" && currentMode !== "" ? [] : searchItems.slice(0, MAX_TOTAL_RESULTS).map(item => (_jsx(FiltersSearchItemData, { id: item.id, name: item.name, isActive: isSearchModeAvailable ? (onlyOneType ? searchSelection.some(el => el.id === item.id) : searchSelection.filter(el => el.id === item.id)
169
+ .some(el => el.type === (item.type || NO_TYPED_SEARCH_ITEM)) || false) : false, secondary: item.description, onClose: undefined, onClick: isSearchModeAvailable ? memoizedSearchItemClick(item.id, item.type) : undefined, icon: getIconByType(item.type || onlyOneType), isMobile: isMobile, isDrive: isDrive }, `search-${item.id}-${item.type || NO_TYPED_SEARCH_ITEM}`))), [currentMode, searchItems, isSearchModeAvailable, searchSelection, memoizedSearchItemClick, getIconByType, onlyOneType, isMobile, isDrive]);
170
+ const memoizedSelectedData = useMemo(() => currentMode !== "selected" || initialSearchSelectionRef.current.length === 0 ? [] : initialSearchSelectionRef.current.slice(0, MAX_TOTAL_RESULTS)
171
+ .map(item => (_jsx(FiltersSearchItemData, { id: item.id, name: item.name, isActive: onlyOneType ? (searchSelection || []).some(el => el.id === item.id)
172
+ : (searchSelection || []).filter(el => el.id === item.id).some(el => el.type === (item.type || NO_TYPED_SEARCH_ITEM)) || false, secondary: item.description, onClose: undefined, onClick: memoizedSelectedItemClick(item.id, item.type), icon: getIconByType(item.type || onlyOneType), isMobile: isMobile, isDrive: isDrive }, `selected-${item.id}-${item.type || onlyOneType || NO_TYPED_SEARCH_ITEM}-${item.name}-${hasNotPopulatedName ? "notPopulated" : ""}`))), [currentMode, onlyOneType, searchSelection, memoizedSelectedItemClick, getIconByType, isMobile, isDrive, hasNotPopulatedName]);
173
+ const resultsToShow = useMemo(() => {
174
+ if (currentMode === "search") {
175
+ return memoizedSearchData;
176
+ }
177
+ if (currentMode === "recent" || currentMode === "history") {
178
+ return memoizedRecentData;
179
+ }
180
+ if (currentMode === "selected") {
181
+ return memoizedSelectedData;
182
+ }
183
+ return memoizedSearchData;
184
+ }, [currentMode, memoizedSearchData, memoizedRecentData, memoizedSelectedData]);
185
+ const getSearchHeaderContent = () => {
186
+ if (currentMode === "history") {
187
+ return _jsxs(_Fragment, { children: [_jsx(TextIconButton, { className: "zen-filters-search-list__header-back-button", title: translate("Back"), type: "tertiary", onClick: changeMode.bind(null, "recent"), icon: IconChevronLeft, iconPosition: ButtonIconPosition.Start }), _jsx(Button, { type: "tertiary", onClick: handleRemoveAll, children: translate("Clear all") })] });
188
+ }
189
+ if (currentMode === "recent" || currentMode === "selected") {
190
+ return _jsxs(_Fragment, { children: [_jsxs("div", { className: "zen-filters-search-list__header-title", children: [_jsx(Button, { type: currentMode === "recent" ? "tertiary" : "tertiary-black", className: currentMode === "recent" ? "zen-filters-search-list__header-button--active" : "", title: translate("Recent searches"), onClick: changeMode.bind(null, "recent"), children: translate("Recent searches") }), _jsx(Button, { type: currentMode === "selected" ? "tertiary" : "tertiary-black", className: currentMode === "selected" ? "zen-filters-search-list__header-button--active" : "", title: translate("Currently selected"), onClick: changeMode.bind(null, "selected"), children: translate("Currently selected") })] }), currentMode === "recent" && recentItems.length > resultsToShow.length && _jsx(Button, { type: "tertiary", onClick: changeMode.bind(null, "history"), children: translate("View history") }), currentMode === "selected" && resultsToShow.length > 0 && _jsx(Button, { type: "tertiary", onClick: memoizedClearSelectedItems, children: translate("Clear") })] });
191
+ }
192
+ if (currentMode === "search" && resultsToShow.length > 0) {
193
+ return _jsx("div", { className: "zen-filters-search-list__header-title", children: translate("Top results") });
194
+ }
195
+ return undefined;
196
+ };
197
+ const searchHeaderContent = getSearchHeaderContent();
198
+ return _jsxs("div", { className: "zen-filters-search-list", children: [searchHeaderContent ? _jsx("div", { className: classNames(["zen-filters-search-list__header", isDrive ? (isMobile ? "zen-filters-search-list__header--drive" : "zen-filters-search-list__header--drive-tablet") : ""]), children: searchHeaderContent }) : null, _jsx("div", { ref: searchListRef, children: _jsx(List, { className: "zen-filters-search-list__data", onKeyDown: handleKeyDown, isLoading: currentMode === "selected" ? false : isLoading, type: "data", waitingItems: waitingItemsLimit, children: resultsToShow }) }), !isLoading && resultsToShow.length === 0 ? _jsx(EmptySearchList, { children: translate("No data available") }) : null] });
71
199
  };
@@ -15,6 +15,7 @@ export interface IFiltersSelect extends IFiltersSelectProps {
15
15
  value: IFiltersSearchType;
16
16
  onChange: (newValue: IFiltersSearchType) => void;
17
17
  className?: string;
18
+ selectListRef: React.RefObject<HTMLDivElement>;
18
19
  }
19
20
  export declare const ALL_SELECT_OPTION_ID: string;
20
21
  export interface IFiltersSearchType {
@@ -1,9 +1,10 @@
1
- import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
1
+ import { jsx as _jsx, Fragment as _Fragment } from "react/jsx-runtime";
2
2
  import { useCallback, useMemo, useState } from "react";
3
3
  import { classNames } from "../../commonHelpers/classNames/classNames";
4
4
  import { useLanguage } from "../../utils/localization/useLanguage";
5
5
  import { useUniqueId } from "../../commonHelpers/useUniqueId";
6
6
  import { FiltersSelectListExpandableItem, FiltersSelectListItem } from "./filtersSelectListItem";
7
+ import { getNewFocusableItem } from "../../utils/keyboardHelpers";
7
8
  export const ALL_SELECT_OPTION_ID = "All";
8
9
  export const FiltersSelectDisplayName = "FiltersSelect";
9
10
  export const getFiltersSearchTypeAllOption = () => ({
@@ -13,7 +14,7 @@ export const getAllOption = (translate) => ({
13
14
  id: ALL_SELECT_OPTION_ID,
14
15
  name: translate("All")
15
16
  });
16
- export const FiltersSelect = ({ value, name, onChange, id, items, className }) => {
17
+ export const FiltersSelect = ({ value, onChange, items, className, selectListRef }) => {
17
18
  const { translate } = useLanguage();
18
19
  const [activeId, setActiveId] = useState("");
19
20
  const filtersSelectListId = useUniqueId();
@@ -28,18 +29,45 @@ export const FiltersSelect = ({ value, name, onChange, id, items, className }) =
28
29
  return;
29
30
  }
30
31
  setActiveId(clickedId);
31
- onChange({ [clickedId]: undefined });
32
- }, [activeId, onChange]);
32
+ }, [activeId]);
33
33
  const handleItemClick = useCallback((clickedId, parentId) => () => {
34
+ if (!clickedId) {
35
+ parentId && onChange({ [parentId]: undefined });
36
+ return;
37
+ }
34
38
  if (parentId) {
35
39
  onChange({ [parentId]: clickedId });
36
40
  return;
37
41
  }
38
- setActiveId("");
39
42
  onChange(Object.keys(value).includes(clickedId) ? getFiltersSearchTypeAllOption() : { [clickedId]: undefined });
40
43
  }, [onChange, value]);
41
- const selectListComponent = useMemo(() => preparedItems.map(el => el.subItems ?
42
- _jsx(FiltersSelectListExpandableItem, { id: `${filtersSelectListId}-${el.id}`, name: el.name, isActive: el.id === activeId, className: classNames([value.hasOwnProperty(el.id) ? "zen-filters-select__list-item--active" : ""]), onClick: handleExpandableItemClick(el.id), content: el.id === activeId ? el.subItems.map(subEl => _jsx(FiltersSelectListItem, { className: classNames(["zen-filters-select__sub-item"]), id: `${filtersSelectListId}-${el.id}-${subEl.id}`, name: subEl.name, isActive: value[el.id] === subEl.id, onClick: handleItemClick(subEl.id, el.id) }, `${el.id}-${subEl.id}`)) : _jsx(_Fragment, {}) }, el.id) : _jsx(FiltersSelectListItem, { id: `${filtersSelectListId}-${el.id}`, name: el.name, isActive: Object.keys(value).includes(el.id), onClick: handleItemClick(el.id) }, el.id)), [activeId, filtersSelectListId, handleExpandableItemClick, handleItemClick, preparedItems, value]);
43
- return _jsxs("div", { className: "zen-filters-select", children: [id && name ? _jsx("div", { className: "zen-filters-select__label", id: id, children: name }) : null, _jsx("div", { className: classNames(["zen-filters-select__list", className || ""]), children: selectListComponent })] });
44
+ const handleKeyDown = useCallback((event) => {
45
+ var _a, _b, _c, _d;
46
+ if (event.key === "ArrowDown") {
47
+ const nextElement = ((_a = document.activeElement) === null || _a === void 0 ? void 0 : _a.nextElementSibling) ? (_b = document.activeElement) === null || _b === void 0 ? void 0 : _b.nextElementSibling
48
+ : getNewFocusableItem(1, selectListRef.current);
49
+ nextElement === null || nextElement === void 0 ? void 0 : nextElement.focus();
50
+ return;
51
+ }
52
+ if (event.key === "ArrowUp") {
53
+ const nextElement = ((_c = document.activeElement) === null || _c === void 0 ? void 0 : _c.previousElementSibling) ? (_d = document.activeElement) === null || _d === void 0 ? void 0 : _d.previousElementSibling
54
+ : getNewFocusableItem(-1, selectListRef.current);
55
+ nextElement === null || nextElement === void 0 ? void 0 : nextElement.focus();
56
+ return;
57
+ }
58
+ if (event.key === "ArrowLeft") {
59
+ const nextElement = getNewFocusableItem(-1, selectListRef.current);
60
+ nextElement === null || nextElement === void 0 ? void 0 : nextElement.focus();
61
+ return;
62
+ }
63
+ if (event.key === "ArrowRight") {
64
+ const nextElement = getNewFocusableItem(1, selectListRef.current);
65
+ nextElement === null || nextElement === void 0 ? void 0 : nextElement.focus();
66
+ return;
67
+ }
68
+ }, [selectListRef]);
69
+ const selectListComponent = useMemo(() => preparedItems.map(el => el.subItems && el.subItems.length > 0 ?
70
+ _jsx(FiltersSelectListExpandableItem, { id: `${filtersSelectListId}-${el.id}`, name: el.name, isExpanded: el.id === activeId, isActive: Object.keys(value).includes(el.id) && Boolean(value[el.id]), className: classNames([value.hasOwnProperty(el.id) ? "zen-filters-select__list-item-wrapper--active" : ""]), onExpandItem: handleExpandableItemClick(el.id), onClick: handleItemClick(undefined, el.id), content: el.id === activeId ? el.subItems.map(subEl => _jsx(FiltersSelectListItem, { className: classNames(["zen-filters-select__sub-item"]), id: `${filtersSelectListId}-${el.id}-${subEl.id}`, name: subEl.name, isActive: value[el.id] === subEl.id, onClick: handleItemClick(subEl.id, el.id) }, `${el.id}-${subEl.id}`)) : _jsx(_Fragment, {}) }, `${filtersSelectListId}-${el.id}`) : _jsx(FiltersSelectListItem, { id: `${filtersSelectListId}-${el.id}`, name: el.name, className: "zen-filters-select__list-item--not-expandable", isActive: Object.keys(value).includes(el.id), onClick: handleItemClick(el.id) }, `${filtersSelectListId}-${el.id}`)), [activeId, filtersSelectListId, handleExpandableItemClick, handleItemClick, preparedItems, value]);
71
+ return _jsx("div", { className: "zen-filters-select", ref: selectListRef, onKeyDown: handleKeyDown, children: _jsx("div", { className: classNames(["zen-filters-select__list", className || ""]), children: selectListComponent }) });
44
72
  };
45
73
  FiltersSelect.displayName = FiltersSelectDisplayName;
@@ -1,15 +1,21 @@
1
- export declare const FiltersSelectListItem: ({ id, name, isActive, onClick, className }: {
2
- id: any;
3
- name: any;
4
- isActive: any;
5
- onClick: any;
6
- className?: string | undefined;
7
- }) => import("react/jsx-runtime").JSX.Element;
8
- export declare const FiltersSelectListExpandableItem: ({ id, name, content, isActive, onClick, className }: {
9
- id: any;
10
- name: any;
11
- content: any;
12
- isActive: any;
13
- onClick: any;
14
- className?: string | undefined;
15
- }) => import("react/jsx-runtime").JSX.Element;
1
+ /// <reference types="react" />
2
+ interface IFiltersSelectListItem {
3
+ id: string;
4
+ name: string;
5
+ isActive: boolean;
6
+ onClick: () => void;
7
+ className?: string;
8
+ }
9
+ export declare const FiltersSelectListItem: ({ id, name, isActive, onClick, className }: IFiltersSelectListItem) => import("react/jsx-runtime").JSX.Element;
10
+ interface IFiltersSelectListExpandableItem {
11
+ id: string;
12
+ name: string;
13
+ content: React.ReactNode;
14
+ isActive: boolean;
15
+ isExpanded: boolean;
16
+ onClick: () => void;
17
+ className?: string;
18
+ onExpandItem?: () => void;
19
+ }
20
+ export declare const FiltersSelectListExpandableItem: ({ id, name, content, isActive, isExpanded, onClick, className, onExpandItem }: IFiltersSelectListExpandableItem) => import("react/jsx-runtime").JSX.Element;
21
+ export {};
@@ -1,9 +1,19 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
+ import { useCallback } from "react";
3
+ import { Button } from "../../button/button";
2
4
  import { classNames } from "../../commonHelpers/classNames/classNames";
3
5
  import { useUniqueId } from "../../commonHelpers/useUniqueId";
4
- import { IconChevronBottom } from "../../icons/iconChevronBottom";
6
+ import { IconChevronDownSmall } from "../../icons/iconChevronDownSmall";
7
+ import { useLanguage } from "../../utils/localization/useLanguage";
5
8
  export const FiltersSelectListItem = ({ id, name, isActive, onClick, className = "" }) => _jsx("div", { children: _jsx("button", { type: "button", className: classNames(["zen-filters-select__list-item", isActive ? "zen-filters-select__list-item--active" : "", className]), id: id, onClick: onClick, children: name }) });
6
- export const FiltersSelectListExpandableItem = ({ id, name, content, isActive, onClick, className = "" }) => {
9
+ export const FiltersSelectListExpandableItem = ({ id, name, content, isActive, isExpanded, onClick, className = "", onExpandItem }) => {
7
10
  const ariaControlsId = useUniqueId();
8
- return (_jsxs(_Fragment, { children: [_jsxs("button", { type: "button", "aria-expanded": isActive, "aria-controls": ariaControlsId, className: classNames(["zen-filters-select__list-item", isActive ? "zen-filters-select__list-item--active" : "", className]), id: id, onClick: onClick, children: [name, _jsx(IconChevronBottom, { size: "large", className: classNames(["zen-filters-select__item-icon", isActive ? "zen-filters-select__item-icon--rotated" : ""]) })] }), _jsx("div", { id: ariaControlsId, "aria-labelledby": id, role: "region", className: classNames(["zen-filters-select__item-content", content ? "" : "zen-filters-select__item-content--hidden"]), children: content })] }));
11
+ const { translate } = useLanguage();
12
+ const handleExpandItem = useCallback((e) => {
13
+ e.stopPropagation();
14
+ if (onExpandItem) {
15
+ onExpandItem();
16
+ }
17
+ }, [onExpandItem]);
18
+ return (_jsxs(_Fragment, { children: [_jsxs("div", { className: classNames(["zen-filters-select__list-item-wrapper", isExpanded ? "zen-filters-select__list-item-wrapper--expanded" : "", className]), children: [_jsx("button", { type: "button", className: classNames(["zen-filters-select__list-item"]), id: id, onClick: onClick, children: _jsxs("div", { className: "zen-filters-select__list-item-title", children: [name, isActive ? _jsx("div", { className: "zen-filters-select__item-active-indicator" }) : null] }) }), _jsx(Button, { type: "tertiary-black", onClick: handleExpandItem, className: "zen-filters-select__item-button", "aria-expanded": isExpanded, "aria-controls": ariaControlsId, "aria-label": isExpanded ? `${translate("Collapse")} ${name}` : `${translate("Expand")} ${name}`, children: _jsx(IconChevronDownSmall, { size: "large", className: classNames(["zen-filters-select__item-icon", isExpanded ? "zen-filters-select__item-icon--rotated" : ""]) }) })] }), _jsx("div", { id: ariaControlsId, "aria-labelledby": id, role: "region", className: classNames(["zen-filters-select__item-content", content ? "" : "zen-filters-select__item-content--hidden"]), children: content })] }));
9
19
  };
@@ -0,0 +1,15 @@
1
+ import { ReactElement } from "react";
2
+ import { TFiltersComponentsItemState, TFiltersComponentsProps } from "./filtersItem";
3
+ import "./filtersSidePanel.less";
4
+ export interface IFiltersSidePanel {
5
+ isOpen: boolean;
6
+ onClose: () => void;
7
+ onApply: (state: Record<string, TFiltersComponentsItemState>) => void;
8
+ onPin?: () => void;
9
+ isPinned?: boolean;
10
+ filters: ReactElement<TFiltersComponentsProps>[];
11
+ externalState?: Record<string, TFiltersComponentsItemState>;
12
+ setExternalState?: (newState: Partial<Record<string, TFiltersComponentsItemState>>) => void;
13
+ triggerRef: React.RefObject<HTMLElement>;
14
+ }
15
+ export declare const FiltersSidePanel: ({ isOpen, onApply, onClose, onPin, isPinned, filters, externalState, setExternalState, triggerRef }: IFiltersSidePanel) => import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,208 @@
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
+ import { Fragment, useCallback, useContext, useEffect, useMemo, useRef, useState } from "react";
3
+ import { useLanguage } from "../../utils/localization/useLanguage";
4
+ import { useUniqueId } from "../../commonHelpers/useUniqueId";
5
+ import { useMobile } from "../../commonHelpers/hooks/useMobile";
6
+ import { useBodyScroll } from "../../utils/useBodyScroll";
7
+ import { Shield } from "../../shield/shield";
8
+ import { createPortal } from "react-dom";
9
+ import { CUSTOM_POPUP_COMPONENT_CLASSNAME, SidePanel, SidePanelCloseReason } from "../../sidePanel/sidePanel";
10
+ import { classNames } from "../../commonHelpers/classNames/classNames";
11
+ import { themeContext } from "../../utils/theme/themeContext";
12
+ import { ButtonIconPosition, TextIconButton } from "../../textIconButton/textIconButton";
13
+ import { IconClose } from "../../icons/iconClose";
14
+ import { Button } from "../../button/button";
15
+ import { FooterButtons } from "../../footerButtons/footerButtons";
16
+ import { IconDisk } from "../../icons/iconDisk";
17
+ import { IconPin2 } from "../../icons/iconPin2";
18
+ import { useFiltersSavedItems } from "./filtersSavedItemsProvider";
19
+ import { useDriveClassName } from "../../utils/theme/useDriveClassName";
20
+ import { ALL_SELECT_OPTION_ID, getAllOption } from "./filtersSelect";
21
+ import { createStateObject } from "../filtersHelper";
22
+ import { Tabs } from "../../tabs/tabs";
23
+ import { FiltersSidePanelItem } from "./filtersSidePanelItem";
24
+ import { FiltersSaveModal } from "./filtersSaveModal";
25
+ import { isElementsEqual } from "../../utils/compareElementsUtils/isElementsEqual";
26
+ import { FiltersEmptySelectedList } from "./filtersEmptySelectedList";
27
+ const SELECTED_TAB_ID = "Selected";
28
+ const getSelectedOption = (translate) => ({
29
+ id: SELECTED_TAB_ID,
30
+ name: translate("Selected")
31
+ });
32
+ export const FiltersSidePanel = ({ isOpen, onApply, onClose, onPin, isPinned, filters, externalState, setExternalState, triggerRef }) => {
33
+ var _a;
34
+ const { translate } = useLanguage();
35
+ const isLongTextLanguages = useMemo(() => `${translate("Reset")}${translate("Cancel")}${translate("Apply")}`.length > 31, [translate]);
36
+ const dataShieldId = useUniqueId();
37
+ const iconDriveClassName = useDriveClassName("icon");
38
+ const { dark } = useContext(themeContext);
39
+ const isMobile = useMobile();
40
+ const { setScrollHidden, resetScroll } = useBodyScroll();
41
+ const { onSave } = useFiltersSavedItems();
42
+ const [activeTabId, setActiveTabId] = useState(ALL_SELECT_OPTION_ID);
43
+ const [isSaveModalOpen, setIsSaveModalOpen] = useState(false);
44
+ const [filtersInternalState, setFiltersInternalState] = useState(createStateObject(filters, "state"));
45
+ const [initialExternalState, setInitialExternalState] = useState(externalState);
46
+ const prevIsOpenRef = useRef(false);
47
+ const [selectedIds, setSelectedIds] = useState([]);
48
+ const [errorItems, setErrorItems] = useState(new Set());
49
+ const saveButtonRef = useRef(null);
50
+ const externalMode = useMemo(() => {
51
+ if (externalState === undefined || setExternalState === undefined) {
52
+ return false;
53
+ }
54
+ return true;
55
+ }, [externalState, setExternalState]);
56
+ const handleInternalFiltersStateChange = useCallback((id) => (newState) => {
57
+ setFiltersInternalState((prevState) => (Object.assign(Object.assign({}, prevState), { [id]: newState })));
58
+ }, []);
59
+ const handleExternalFiltersStateChange = useCallback((id) => (newState) => {
60
+ setExternalState && setExternalState({ [id]: newState });
61
+ }, [setExternalState]);
62
+ const currentState = useMemo(() => externalMode ? externalState
63
+ : filtersInternalState, [externalMode, externalState, filtersInternalState]);
64
+ const currentFilters = useMemo(() => filters.filter((item) => currentState.hasOwnProperty(item.props.id)
65
+ && (typeof item.props.visible === "boolean" ? item.props.visible
66
+ : item.props.visible === undefined ? true : (item.props.visible([ALL_SELECT_OPTION_ID], currentState) || item.props.visible([ALL_SELECT_OPTION_ID])))), [currentState, filters]);
67
+ const inhibitedFilters = useMemo(() => {
68
+ const inhibited = new Set();
69
+ currentFilters.forEach((item) => {
70
+ if (item.props.inhibit && item.props.inhibit.length > 0) {
71
+ item.props.inhibit.forEach((id) => inhibited.add(id));
72
+ }
73
+ });
74
+ return inhibited;
75
+ }, [currentFilters]);
76
+ const filteredCurrentFilters = useMemo(() => currentFilters.filter((item) => !externalMode ? !inhibitedFilters.has(item.props.id) : true), [currentFilters, externalMode, inhibitedFilters]);
77
+ const handleReadyForFocus = useCallback((isCurrentOpen) => {
78
+ if (!isCurrentOpen) {
79
+ triggerRef.current.focus();
80
+ return;
81
+ }
82
+ }, [triggerRef]);
83
+ useEffect(() => {
84
+ const handleOpen = () => {
85
+ setScrollHidden();
86
+ };
87
+ const handleClose = () => {
88
+ resetScroll();
89
+ };
90
+ if (isOpen) {
91
+ handleOpen();
92
+ }
93
+ else {
94
+ handleClose();
95
+ }
96
+ return () => { handleClose(); };
97
+ }, [isOpen, setScrollHidden, resetScroll]);
98
+ const handleClearClick = useCallback(() => {
99
+ const newState = createStateObject(filters, "defaultState");
100
+ externalMode && setExternalState ? setExternalState(Object.assign({}, newState)) : setFiltersInternalState(newState);
101
+ }, [externalMode, filters, setExternalState]);
102
+ const handleApplyClick = useCallback(() => {
103
+ const stateForApply = externalMode ? currentState : {};
104
+ if (!externalMode) {
105
+ Object.keys(currentState).forEach(key => {
106
+ if (!inhibitedFilters.has(key)) {
107
+ stateForApply[key] = currentState[key];
108
+ }
109
+ });
110
+ }
111
+ onApply(stateForApply);
112
+ onClose();
113
+ }, [currentState, externalMode, inhibitedFilters, onApply, onClose]);
114
+ const handleCancelClick = useCallback(() => {
115
+ const newState = createStateObject(filters, "state");
116
+ externalMode && setExternalState ? setExternalState(Object.assign({}, newState)) : setFiltersInternalState(newState);
117
+ onClose();
118
+ }, [externalMode, filters, onClose, setExternalState]);
119
+ const handleExternalModeClose = useCallback(() => {
120
+ if (!(externalMode && initialExternalState && setExternalState)) {
121
+ return;
122
+ }
123
+ setExternalState(initialExternalState);
124
+ onClose();
125
+ }, [externalMode, initialExternalState, onClose, setExternalState]);
126
+ const toggleSaveModal = useCallback(() => {
127
+ var _a;
128
+ isSaveModalOpen && ((_a = saveButtonRef.current) === null || _a === void 0 ? void 0 : _a.focus());
129
+ setIsSaveModalOpen(val => !val);
130
+ }, [isSaveModalOpen]);
131
+ const handleSaveFilters = useCallback((filterName) => {
132
+ const stateForApply = externalMode ? currentState : {};
133
+ if (!externalMode) {
134
+ Object.keys(currentState).forEach(key => {
135
+ if (!inhibitedFilters.has(key)) {
136
+ stateForApply[key] = currentState[key];
137
+ }
138
+ });
139
+ }
140
+ onSave && onSave(filterName, stateForApply);
141
+ }, [externalMode, currentState, onSave, inhibitedFilters]);
142
+ const sidePanelItemChangeHandler = useCallback((id) => externalMode ? handleExternalFiltersStateChange(id) : handleInternalFiltersStateChange(id), [externalMode, handleExternalFiltersStateChange, handleInternalFiltersStateChange]);
143
+ const handleErrorCheck = useCallback((id) => (isError) => {
144
+ if (isError && errorItems.has(id) || !isError && !errorItems.has(id)) {
145
+ return;
146
+ }
147
+ const newErrorItems = new Set(errorItems);
148
+ newErrorItems[isError ? "add" : "delete"](id);
149
+ setErrorItems(newErrorItems);
150
+ }, [errorItems]);
151
+ const tabsContent = useMemo(() => filteredCurrentFilters
152
+ .map((item) => (_jsx(FiltersSidePanelItem, { item: item, itemState: currentState[item.props.id], changeHandler: sidePanelItemChangeHandler(item.props.id), errorCheck: handleErrorCheck(item.props.id) }, item.props.id))), [filteredCurrentFilters, currentState, sidePanelItemChangeHandler, handleErrorCheck]);
153
+ const defaultStateObject = useMemo(() => createStateObject(filteredCurrentFilters, "defaultState"), [filteredCurrentFilters]);
154
+ const getSelectedIds = useCallback(() => {
155
+ const selected = [];
156
+ filteredCurrentFilters.forEach((el) => {
157
+ const itemState = currentState[el.props.id];
158
+ const defaultState = defaultStateObject[el.props.id];
159
+ if (!isElementsEqual(itemState, defaultState) && !inhibitedFilters.has(el.props.id)) {
160
+ selected.push(el.props.id);
161
+ }
162
+ });
163
+ return selected;
164
+ }, [currentState, defaultStateObject, filteredCurrentFilters, inhibitedFilters]);
165
+ const tabOptions = useMemo(() => [
166
+ getAllOption(translate),
167
+ Object.assign(Object.assign({}, getSelectedOption(translate)), { quantity: getSelectedIds().length || undefined })
168
+ ], [getSelectedIds, translate]);
169
+ const onTabChange = (newVal) => {
170
+ setActiveTabId(newVal);
171
+ };
172
+ const selectedTabsContent = useMemo(() => selectedIds.length ? filteredCurrentFilters.filter((el) => selectedIds.includes(el.props.id))
173
+ .map((item) => (_jsx(FiltersSidePanelItem, { item: item, itemState: currentState[item.props.id], changeHandler: sidePanelItemChangeHandler(item.props.id), errorCheck: handleErrorCheck(item.props.id) }, item.props.id)))
174
+ : _jsx(FiltersEmptySelectedList, {}), [selectedIds, filteredCurrentFilters, currentState, sidePanelItemChangeHandler, handleErrorCheck]);
175
+ useEffect(() => {
176
+ if (!isOpen) {
177
+ return;
178
+ }
179
+ if (activeTabId === SELECTED_TAB_ID) {
180
+ return;
181
+ }
182
+ setSelectedIds(getSelectedIds());
183
+ }, [activeTabId, getSelectedIds, isOpen]);
184
+ const resetInitialComponentState = useCallback(() => {
185
+ setIsSaveModalOpen(false);
186
+ setActiveTabId(ALL_SELECT_OPTION_ID);
187
+ setFiltersInternalState(createStateObject(filters, "state"));
188
+ setInitialExternalState(externalState);
189
+ setSelectedIds([]);
190
+ setSelectedIds(getSelectedIds());
191
+ }, [externalState, filters, getSelectedIds]);
192
+ useEffect(() => {
193
+ if (isOpen !== prevIsOpenRef.current) {
194
+ prevIsOpenRef.current = isOpen;
195
+ isOpen && resetInitialComponentState();
196
+ }
197
+ }, [isOpen, resetInitialComponentState]);
198
+ const handleHidePanel = useCallback((reason, shieldId) => {
199
+ if (isSaveModalOpen) {
200
+ return;
201
+ }
202
+ if (reason === SidePanelCloseReason.ClickOutside && shieldId === dataShieldId || reason === SidePanelCloseReason.Escape) {
203
+ onClose();
204
+ return;
205
+ }
206
+ }, [isSaveModalOpen, dataShieldId, onClose]);
207
+ return _jsxs(Fragment, { children: [_jsx(Shield, { className: "zen-high-level-shield", dataShieldId: dataShieldId, isVisible: isOpen }), createPortal(_jsx(SidePanel, { triggerRef: triggerRef, isOpen: isOpen, label: translate("All Filters"), onHidePanel: handleHidePanel, className: classNames(["zen-side-panel-filters", dark ? "zen-dark" : ""]), onTransitionEnd: handleReadyForFocus, children: _jsxs("div", { className: "zen-side-panel-filters-wrapper", children: [_jsxs("div", { className: "zen-side-panel-filters__header", children: [_jsx("div", { className: "zen-side-panel-filters__title-wrapper", children: _jsx("div", { className: "zen-side-panel-filters__title zen-ellipsis", children: translate("All Filters") }) }), _jsxs("div", { className: "zen-side-panel-filters__header-buttons", children: [onSave && _jsx(Button, { type: "tertiary", ref: saveButtonRef, className: "zen-side-panel-filters__save-button zen-side-panel-filters__header-button", title: translate("Save"), "aria-label": translate("Save"), onClick: toggleSaveModal, children: _jsx(IconDisk, { description: translate("Save"), className: "svgIcon", size: iconDriveClassName ? "huge" : "large" }) }), onPin && _jsx(Button, { type: "tertiary", className: classNames(["zen-side-panel-filters__pin-button", isPinned ? "zen-side-panel-filters__header-button--active" : "", "zen-side-panel-filters__header-button"]), title: translate("Pin"), "aria-label": translate("Pin"), onClick: onPin, children: _jsx(IconPin2, { description: translate("Pin"), className: "svgIcon", size: iconDriveClassName ? "huge" : "large" }) }), _jsx(TextIconButton, { type: "tertiary-black", className: "zen-side-panel-filters__close-button", icon: IconClose, iconSize: "large", onClick: externalMode ? handleExternalModeClose : onClose, iconPosition: ButtonIconPosition.Start, title: translate("Close") })] })] }), _jsx(Tabs, { activeTabId: activeTabId, className: "zen-side-panel-filters__tab-header", onTabChange: onTabChange, tabs: tabOptions }), _jsxs("div", { className: "zen-side-panel-filters__content", children: [_jsx("div", { id: `panel-${ALL_SELECT_OPTION_ID}`, className: classNames(["zen-side-panel-filters__tab-content", activeTabId === ALL_SELECT_OPTION_ID ? "zen-side-panel-filters__tab-content--active" : "zen-side-panel-filters__tab-content--hidden"]), role: "tabpanel", "aria-labelledby": ALL_SELECT_OPTION_ID, children: activeTabId === ALL_SELECT_OPTION_ID ? tabsContent : null }), _jsx("div", { id: `panel-${SELECTED_TAB_ID}`, className: classNames(["zen-side-panel-filters__tab-content", activeTabId === SELECTED_TAB_ID ? "zen-side-panel-filters__tab-content--active" : "zen-side-panel-filters__tab-content--hidden"]), role: "tabpanel", "aria-labelledby": SELECTED_TAB_ID, children: activeTabId === SELECTED_TAB_ID ? selectedTabsContent : null })] }), _jsx("div", { className: classNames(["zen-side-panel-filters__footer", isMobile ? "zen-side-panel-filters__footer--mobile" : "", isLongTextLanguages && !isMobile ? "zen-side-panel-filters__footer--long-text" : ""]), children: isMobile ? _jsxs(FooterButtons, { children: [_jsx(Button, { className: "zen-side-panel-filters__apply-button", onClick: handleApplyClick, type: "primary", disabled: errorItems.size > 0, children: translate("Apply") }), _jsx(Button, { className: "zen-side-panel-filters__cancel-button", type: "secondary", onClick: handleCancelClick, children: translate("Cancel") }), _jsx(Button, { className: "zen-side-panel-filters__clear-button", onClick: handleClearClick, type: "tertiary", children: translate("Reset") })] }) : _jsxs(_Fragment, { children: [_jsx("div", { className: "zen-side-panel-filters__footer-section", children: _jsx(Button, { type: "tertiary", className: "zen-side-panel-filters__clear-button", onClick: handleClearClick, children: translate("Reset") }) }), _jsxs("div", { className: "zen-side-panel-filters__footer-section", children: [_jsx(Button, { className: "zen-side-panel-filters__cancel-button", type: "secondary", onClick: handleCancelClick, children: translate("Cancel") }), _jsx(Button, { className: "zen-side-panel-filters__apply-button zen-side-panel-filters__apply-button--desktop", onClick: handleApplyClick, type: "primary", disabled: errorItems.size > 0, children: translate("Apply") })] })] }) })] }) }), (_a = document.fullscreenElement) !== null && _a !== void 0 ? _a : document.body), _jsx(FiltersSaveModal, { className: CUSTOM_POPUP_COMPONENT_CLASSNAME, isOpen: isSaveModalOpen, onClose: toggleSaveModal, onSave: handleSaveFilters })] });
208
+ };