@databiosphere/findable-ui 34.1.0 → 35.0.2

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 (272) hide show
  1. package/.release-please-manifest.json +1 -1
  2. package/CHANGELOG.md +26 -0
  3. package/lib/components/DataDictionary/components/Table/columns/columnDef.d.ts +6 -0
  4. package/lib/components/DataDictionary/components/Table/columns/columnDef.js +33 -0
  5. package/lib/components/DataDictionary/components/Table/columns/columnIdentifier.d.ts +5 -0
  6. package/lib/components/DataDictionary/components/Table/columns/columnIdentifier.js +5 -0
  7. package/lib/components/DataDictionary/components/Table/columns/types.d.ts +2 -0
  8. package/lib/components/DataDictionary/components/Table/components/BasicCell/types.d.ts +4 -0
  9. package/lib/components/DataDictionary/components/Table/components/BasicCell/types.js +1 -0
  10. package/lib/components/Detail/components/Table/components/TableHead/tableHead.d.ts +8 -0
  11. package/lib/components/Detail/components/Table/components/TableHead/tableHead.js +36 -0
  12. package/lib/components/Filter/components/Filter/filter.stories.d.ts +25 -0
  13. package/lib/components/Filter/components/Filter/filter.stories.js +42 -0
  14. package/lib/components/Filter/components/FilterTag/filterTag.stories.d.ts +16 -0
  15. package/lib/components/Filter/components/FilterTag/filterTag.stories.js +17 -0
  16. package/lib/components/Filter/components/Filters/filters.stories.d.ts +6 -0
  17. package/lib/components/Filter/components/Filters/filters.stories.js +91 -0
  18. package/lib/components/Index/components/Cell/cell.d.ts +7 -0
  19. package/lib/components/Index/components/Cell/cell.js +10 -0
  20. package/lib/components/Index/components/EntitiesView/constants.d.ts +1 -0
  21. package/lib/components/Index/components/EntitiesView/constants.js +1 -0
  22. package/lib/components/Index/components/Hero/components/Summaries/summaries.stories.d.ts +13 -0
  23. package/lib/components/Index/components/Hero/components/Summaries/summaries.stories.js +18 -0
  24. package/lib/components/Index/components/Hero/hero.stories.d.ts +23 -0
  25. package/lib/components/Index/components/Hero/hero.stories.js +22 -0
  26. package/lib/components/Index/components/NTag/components/Tooltip/tooltip.d.ts +8 -0
  27. package/lib/components/Index/components/NTag/components/Tooltip/tooltip.js +57 -0
  28. package/lib/components/Index/components/NTag/components/Tooltip/tooltip.styles.d.ts +4 -0
  29. package/lib/components/Index/components/NTag/components/Tooltip/tooltip.styles.js +10 -0
  30. package/lib/components/Index/components/NTag/nTag.d.ts +10 -0
  31. package/lib/components/Index/components/NTag/nTag.js +8 -0
  32. package/lib/components/Index/components/NTagCell/nTagCell.d.ts +11 -0
  33. package/lib/components/Index/components/NTagCell/nTagCell.js +29 -0
  34. package/lib/components/Index/components/NTagCell/nTagCell.stories.d.ts +5 -0
  35. package/lib/components/Index/components/NTagCell/nTagCell.stories.js +16 -0
  36. package/lib/components/Index/components/TitleCell/titleCell.d.ts +6 -0
  37. package/lib/components/Index/components/TitleCell/titleCell.js +10 -0
  38. package/lib/components/Index/components/TitleCell/titleCell.styles.d.ts +3 -0
  39. package/lib/components/Index/components/TitleCell/titleCell.styles.js +6 -0
  40. package/lib/components/Index/index.stories.d.ts +6 -0
  41. package/lib/components/Index/index.stories.js +26 -0
  42. package/lib/components/Layout/components/BackPage/components/BackPageHero/backPageHero.stories.d.ts +6 -0
  43. package/lib/components/Layout/components/BackPage/components/BackPageHero/backPageHero.stories.js +10 -0
  44. package/lib/components/Layout/components/Header/components/Content/components/Actions/components/Authentication/components/AuthenticationMenu/authenticationMenu.stories.d.ts +5 -0
  45. package/lib/components/Layout/components/Header/components/Content/components/Actions/components/Authentication/components/AuthenticationMenu/authenticationMenu.stories.js +29 -0
  46. package/lib/components/Layout/components/Header/components/Content/components/Actions/components/Authentication/components/RequestAuthentication/requestAuthentication.d.ts +6 -0
  47. package/lib/components/Layout/components/Header/components/Content/components/Actions/components/Authentication/components/RequestAuthentication/requestAuthentication.js +13 -0
  48. package/lib/components/Layout/components/Header/components/Content/components/Actions/components/Authentication/components/RequestAuthentication/requestAuthentication.styles.d.ts +36 -0
  49. package/lib/components/Layout/components/Header/components/Content/components/Actions/components/Authentication/components/RequestAuthentication/requestAuthentication.styles.js +9 -0
  50. package/lib/components/Layout/components/Header/components/Content/components/Actions/components/Search/components/SearchButton/searchButton.d.ts +5 -0
  51. package/lib/components/Layout/components/Header/components/Content/components/Actions/components/Search/components/SearchButton/searchButton.js +10 -0
  52. package/lib/components/Layout/components/Header/components/Content/components/Actions/components/Search/components/SearchButton/searchButton.styles.d.ts +36 -0
  53. package/lib/components/Layout/components/Header/components/Content/components/Actions/components/Search/components/SearchButton/searchButton.styles.js +9 -0
  54. package/lib/components/Layout/components/Header/hooks/useMeasureHeader.d.ts +5 -0
  55. package/lib/components/Layout/components/Header/hooks/useMeasureHeader.js +19 -0
  56. package/lib/components/Layout/components/Outline/common/constants.d.ts +2 -0
  57. package/lib/components/Layout/components/Outline/common/constants.js +1 -0
  58. package/lib/components/Login/login.stories.d.ts +6 -0
  59. package/lib/components/Login/login.stories.js +31 -0
  60. package/lib/components/Table/components/CheckboxMenu/checkboxMenu.d.ts +16 -0
  61. package/lib/components/Table/components/CheckboxMenu/checkboxMenu.js +28 -0
  62. package/lib/components/Table/components/CheckboxMenu/checkboxMenu.stories.d.ts +5 -0
  63. package/lib/components/Table/components/CheckboxMenu/checkboxMenu.stories.js +46 -0
  64. package/lib/components/Table/components/CheckboxMenu/checkboxMenu.styles.d.ts +3 -0
  65. package/lib/components/Table/components/CheckboxMenu/checkboxMenu.styles.js +21 -0
  66. package/lib/components/Table/components/EntityViewToggle/entityViewToggle.d.ts +2 -0
  67. package/lib/components/Table/components/EntityViewToggle/entityViewToggle.js +37 -0
  68. package/lib/components/TableCreator/common/constants.d.ts +6 -0
  69. package/lib/components/TableCreator/common/constants.js +19 -0
  70. package/lib/components/TableCreator/common/entities.d.ts +5 -0
  71. package/lib/components/TableCreator/common/entities.js +1 -0
  72. package/lib/components/TableCreator/tableCreator.styles.d.ts +4 -0
  73. package/lib/components/TableCreator/tableCreator.styles.js +4 -0
  74. package/lib/components/common/Alert/alert.stories.d.ts +6 -0
  75. package/lib/components/common/Alert/alert.stories.js +36 -0
  76. package/lib/components/common/Alert/components/AlertText/alertText.styles.d.ts +4 -0
  77. package/lib/components/common/Alert/components/AlertText/alertText.styles.js +19 -0
  78. package/lib/components/common/Banner/components/BannerPrimary/bannerPrimary.d.ts +7 -0
  79. package/lib/components/common/Banner/components/BannerPrimary/bannerPrimary.js +5 -0
  80. package/lib/components/common/Banner/components/BannerPrimary/bannerPrimary.styles.d.ts +3 -0
  81. package/lib/components/common/Banner/components/BannerPrimary/bannerPrimary.styles.js +19 -0
  82. package/lib/components/common/Banner/components/DismissibleBanner/dismissibleBanner.d.ts +10 -0
  83. package/lib/components/common/Banner/components/DismissibleBanner/dismissibleBanner.js +16 -0
  84. package/lib/components/common/Banner/components/SessionTimeout/sessionTimeout.styles.d.ts +3 -0
  85. package/lib/components/common/Banner/components/SessionTimeout/sessionTimeout.styles.js +21 -0
  86. package/lib/components/common/Button/components/LoginButton/loginButton.d.ts +3 -0
  87. package/lib/components/common/Button/components/LoginButton/loginButton.js +6 -0
  88. package/lib/components/common/Button/components/LoginButton/loginButton.styles.d.ts +7 -0
  89. package/lib/components/common/Button/components/LoginButton/loginButton.styles.js +11 -0
  90. package/lib/components/common/IconButton/components/LoadingIconButton/loadingIconButton.d.ts +5 -0
  91. package/lib/components/common/IconButton/components/LoadingIconButton/loadingIconButton.js +10 -0
  92. package/lib/components/common/IconButton/components/LoadingIconButton/loadingIconButton.stories.d.ts +3 -0
  93. package/lib/components/common/IconButton/components/LoadingIconButton/loadingIconButton.stories.js +9 -0
  94. package/lib/hooks/stateSyncManager/hook.d.ts +19 -0
  95. package/lib/hooks/stateSyncManager/hook.js +35 -0
  96. package/lib/hooks/stateSyncManager/hooks/UseBeforePopState/hook.d.ts +1 -0
  97. package/lib/hooks/stateSyncManager/hooks/UseBeforePopState/hook.js +21 -0
  98. package/lib/hooks/stateSyncManager/hooks/UseMetaCommands/hook.d.ts +2 -0
  99. package/lib/hooks/stateSyncManager/hooks/UseMetaCommands/hook.js +23 -0
  100. package/lib/hooks/stateSyncManager/hooks/UseMetaCommands/types.d.ts +4 -0
  101. package/lib/hooks/stateSyncManager/hooks/UseStateSync/hook.d.ts +2 -0
  102. package/lib/hooks/stateSyncManager/hooks/UseStateSync/hook.js +40 -0
  103. package/lib/hooks/stateSyncManager/hooks/UseStateSync/utils.d.ts +30 -0
  104. package/lib/hooks/stateSyncManager/hooks/UseStateSync/utils.js +41 -0
  105. package/lib/hooks/stateSyncManager/types.d.ts +26 -0
  106. package/lib/hooks/stateSyncManager/types.js +1 -0
  107. package/lib/hooks/useAuthentication/common/constants.d.ts +4 -0
  108. package/lib/hooks/useAuthentication/common/constants.js +19 -0
  109. package/lib/hooks/useAuthentication/common/entities.d.ts +25 -0
  110. package/lib/hooks/useAuthentication/common/entities.js +11 -0
  111. package/lib/hooks/useAuthentication/common/utils.d.ts +15 -0
  112. package/lib/hooks/useAuthentication/common/utils.js +25 -0
  113. package/lib/hooks/useAuthentication/useAuthentication.d.ts +6 -0
  114. package/lib/hooks/useAuthentication/useAuthentication.js +9 -0
  115. package/lib/hooks/useAuthentication/useAuthenticationComplete.d.ts +6 -0
  116. package/lib/hooks/useAuthentication/useAuthenticationComplete.js +55 -0
  117. package/lib/hooks/useAuthentication/useAuthenticationForm.d.ts +20 -0
  118. package/lib/hooks/useAuthentication/useAuthenticationForm.js +88 -0
  119. package/lib/hooks/useAuthentication/useAuthenticationNIHExpiry.d.ts +18 -0
  120. package/lib/hooks/useAuthentication/useAuthenticationNIHExpiry.js +50 -0
  121. package/lib/hooks/useAuthentication/useAuthenticationStatus.d.ts +20 -0
  122. package/lib/hooks/useAuthentication/useAuthenticationStatus.js +32 -0
  123. package/lib/hooks/useAuthentication/useFetchGoogleProfile.d.ts +21 -0
  124. package/lib/hooks/useAuthentication/useFetchGoogleProfile.js +41 -0
  125. package/lib/hooks/useAuthentication/useFetchTerraNIHProfile.d.ts +18 -0
  126. package/lib/hooks/useAuthentication/useFetchTerraNIHProfile.js +62 -0
  127. package/lib/hooks/useAuthentication/useFetchTerraProfile.d.ts +24 -0
  128. package/lib/hooks/useAuthentication/useFetchTerraProfile.js +62 -0
  129. package/lib/hooks/useAuthentication/useFetchTerraTermsOfService.d.ts +15 -0
  130. package/lib/hooks/useAuthentication/useFetchTerraTermsOfService.js +62 -0
  131. package/lib/hooks/useAuthentication/useTokenClient.d.ts +11 -0
  132. package/lib/hooks/useAuthentication/useTokenClient.js +29 -0
  133. package/lib/hooks/useAuthenticationConfig.d.ts +6 -0
  134. package/lib/hooks/useAuthenticationConfig.js +14 -0
  135. package/lib/hooks/useCategoryConfigs.d.ts +6 -0
  136. package/lib/hooks/useCategoryConfigs.js +17 -0
  137. package/lib/hooks/useEntityListRelatedView.d.ts +15 -0
  138. package/lib/hooks/useEntityListRelatedView.js +62 -0
  139. package/lib/hooks/useExploreMode.d.ts +14 -0
  140. package/lib/hooks/useExploreMode.js +20 -0
  141. package/lib/hooks/useFileManifest/common/buildFileManifestRequestURL.d.ts +15 -0
  142. package/lib/hooks/useFileManifest/common/buildFileManifestRequestURL.js +27 -0
  143. package/lib/hooks/useFileManifest/useFileManifestURL.d.ts +5 -0
  144. package/lib/hooks/useFileManifest/useFileManifestURL.js +11 -0
  145. package/lib/hooks/useFileManifest/useRequestFileManifest.d.ts +9 -0
  146. package/lib/hooks/useFileManifest/useRequestFileManifest.js +37 -0
  147. package/lib/hooks/useLayoutState.d.ts +6 -0
  148. package/lib/hooks/useLayoutState.js +9 -0
  149. package/lib/hooks/useMenu.d.ts +10 -0
  150. package/lib/hooks/useMenu.js +17 -0
  151. package/lib/hooks/useMenuWithPosition.d.ts +14 -0
  152. package/lib/hooks/useMenuWithPosition.js +33 -0
  153. package/lib/hooks/useSessionTimeout.d.ts +11 -0
  154. package/lib/hooks/useSessionTimeout.js +28 -0
  155. package/lib/providers/authentication.d.ts +51 -0
  156. package/lib/providers/authentication.js +110 -0
  157. package/lib/providers/exploreState/actions/stateToUrl/action.d.ts +9 -0
  158. package/lib/providers/exploreState/actions/stateToUrl/action.js +14 -0
  159. package/lib/providers/exploreState/actions/stateToUrl/dispatch.d.ts +7 -0
  160. package/lib/providers/exploreState/actions/{syncStateFromUrl → stateToUrl}/dispatch.js +3 -3
  161. package/lib/providers/exploreState/actions/stateToUrl/types.d.ts +12 -0
  162. package/lib/providers/exploreState/actions/stateToUrl/types.js +5 -0
  163. package/lib/providers/exploreState/actions/urlToState/action.d.ts +9 -0
  164. package/lib/providers/exploreState/actions/urlToState/action.js +38 -0
  165. package/lib/providers/exploreState/actions/urlToState/dispatch.d.ts +7 -0
  166. package/lib/providers/exploreState/actions/urlToState/dispatch.js +12 -0
  167. package/lib/providers/exploreState/actions/urlToState/types.d.ts +9 -0
  168. package/lib/providers/exploreState/actions/urlToState/types.js +1 -0
  169. package/lib/providers/exploreState/actions/urlToState/utils.d.ts +7 -0
  170. package/lib/providers/exploreState/actions/urlToState/utils.js +13 -0
  171. package/lib/providers/{exploreStateSync/hooks/UseMetaCommands/utils.d.ts → exploreState/entities/query/buildQuery.d.ts} +10 -14
  172. package/lib/providers/{exploreStateSync/hooks/UseMetaCommands/utils.js → exploreState/entities/query/buildQuery.js} +13 -21
  173. package/lib/providers/exploreState/entities/state.d.ts +10 -0
  174. package/lib/providers/exploreState/entities/state.js +36 -0
  175. package/lib/providers/exploreState/entities/types.d.ts +16 -0
  176. package/lib/providers/exploreState/entities/types.js +1 -0
  177. package/lib/providers/exploreState/entities.d.ts +1 -1
  178. package/lib/providers/exploreState/initializer/constants.js +1 -0
  179. package/lib/providers/exploreState/initializer/utils.js +26 -1
  180. package/lib/providers/exploreState.d.ts +8 -4
  181. package/lib/providers/exploreState.js +20 -15
  182. package/lib/providers/layoutState.d.ts +40 -0
  183. package/lib/providers/layoutState.js +47 -0
  184. package/lib/providers/services/provider.d.ts +14 -0
  185. package/lib/providers/services/provider.js +18 -0
  186. package/lib/providers/services/wasPop/context.d.ts +2 -0
  187. package/lib/providers/services/wasPop/context.js +5 -0
  188. package/lib/providers/services/wasPop/hook.d.ts +2 -0
  189. package/lib/providers/services/wasPop/hook.js +5 -0
  190. package/lib/providers/services/wasPop/provider.d.ts +16 -0
  191. package/lib/providers/services/wasPop/provider.js +30 -0
  192. package/lib/providers/services/wasPop/types.d.ts +6 -0
  193. package/lib/providers/services/wasPop/types.js +1 -0
  194. package/lib/services/beforePopState/popStateBus.d.ts +20 -0
  195. package/lib/services/beforePopState/popStateBus.js +58 -0
  196. package/lib/services/beforePopState/types.d.ts +11 -0
  197. package/lib/services/beforePopState/types.js +1 -0
  198. package/lib/services/beforePopState/useOnPopState.d.ts +2 -0
  199. package/lib/services/beforePopState/useOnPopState.js +10 -0
  200. package/lib/services/beforePopState/usePopStateBus.d.ts +6 -0
  201. package/lib/services/beforePopState/usePopStateBus.js +12 -0
  202. package/lib/styles/common/mixins/shadows.d.ts +3 -0
  203. package/lib/styles/common/mixins/shadows.js +4 -0
  204. package/lib/styles/common/mui/palette.d.ts +47 -0
  205. package/lib/styles/common/mui/palette.js +47 -0
  206. package/lib/theme/common/entities.d.ts +6 -0
  207. package/lib/theme/common/entities.js +1 -0
  208. package/lib/views/ExploreView/exploreView.js +15 -2
  209. package/lib/views/ExploreView/utils.d.ts +14 -0
  210. package/lib/views/ExploreView/utils.js +17 -0
  211. package/package.json +1 -1
  212. package/src/hooks/stateSyncManager/hook.ts +47 -0
  213. package/src/hooks/stateSyncManager/hooks/UseBeforePopState/hook.ts +30 -0
  214. package/src/hooks/stateSyncManager/hooks/UseMetaCommands/hook.ts +30 -0
  215. package/src/hooks/stateSyncManager/hooks/UseMetaCommands/types.ts +4 -0
  216. package/src/hooks/stateSyncManager/hooks/UseStateSync/hook.ts +52 -0
  217. package/src/hooks/stateSyncManager/hooks/UseStateSync/utils.ts +58 -0
  218. package/src/hooks/stateSyncManager/types.ts +34 -0
  219. package/src/providers/exploreState/actions/stateToUrl/action.ts +20 -0
  220. package/src/providers/exploreState/actions/stateToUrl/dispatch.ts +14 -0
  221. package/src/providers/exploreState/actions/stateToUrl/types.ts +15 -0
  222. package/src/providers/exploreState/actions/urlToState/action.ts +74 -0
  223. package/src/providers/exploreState/actions/urlToState/dispatch.ts +14 -0
  224. package/src/providers/exploreState/actions/urlToState/types.ts +11 -0
  225. package/src/providers/exploreState/actions/urlToState/utils.ts +18 -0
  226. package/src/providers/{exploreStateSync/hooks/UseMetaCommands/utils.ts → exploreState/entities/query/buildQuery.ts} +23 -26
  227. package/src/providers/exploreState/entities/state.ts +50 -0
  228. package/src/providers/exploreState/entities/types.ts +19 -0
  229. package/src/providers/exploreState/entities.ts +1 -1
  230. package/src/providers/exploreState/initializer/constants.ts +1 -0
  231. package/src/providers/exploreState/initializer/utils.ts +30 -1
  232. package/src/providers/exploreState.tsx +27 -20
  233. package/src/providers/services/provider.tsx +25 -0
  234. package/src/providers/services/wasPop/context.ts +7 -0
  235. package/src/providers/services/wasPop/hook.ts +7 -0
  236. package/src/providers/services/wasPop/provider.tsx +45 -0
  237. package/src/providers/services/wasPop/types.ts +7 -0
  238. package/src/services/beforePopState/popStateBus.ts +64 -0
  239. package/src/services/beforePopState/types.ts +15 -0
  240. package/src/services/beforePopState/useOnPopState.ts +15 -0
  241. package/src/services/beforePopState/usePopStateBus.ts +14 -0
  242. package/src/views/ExploreView/exploreView.tsx +17 -3
  243. package/src/views/ExploreView/utils.ts +25 -0
  244. package/lib/providers/exploreState/actions/syncStateFromUrl/action.d.ts +0 -10
  245. package/lib/providers/exploreState/actions/syncStateFromUrl/action.js +0 -25
  246. package/lib/providers/exploreState/actions/syncStateFromUrl/dispatch.d.ts +0 -7
  247. package/lib/providers/exploreState/actions/syncStateFromUrl/types.d.ts +0 -8
  248. package/lib/providers/exploreState/actions/syncStateFromUrl/utils.d.ts +0 -9
  249. package/lib/providers/exploreState/actions/syncStateFromUrl/utils.js +0 -23
  250. package/lib/providers/exploreState/hooks/UseBeforePopState/useBeforePopState.d.ts +0 -18
  251. package/lib/providers/exploreState/hooks/UseBeforePopState/useBeforePopState.js +0 -41
  252. package/lib/providers/exploreState/hooks/UseBeforePopState/utils.d.ts +0 -23
  253. package/lib/providers/exploreState/hooks/UseBeforePopState/utils.js +0 -68
  254. package/lib/providers/exploreStateSync/hooks/UseMetaCommands/handlers.d.ts +0 -10
  255. package/lib/providers/exploreStateSync/hooks/UseMetaCommands/handlers.js +0 -18
  256. package/lib/providers/exploreStateSync/hooks/UseMetaCommands/hook.d.ts +0 -1
  257. package/lib/providers/exploreStateSync/hooks/UseMetaCommands/hook.js +0 -33
  258. package/lib/providers/exploreStateSync/hooks/UseMetaCommands/types.d.ts +0 -11
  259. package/lib/providers/exploreStateSync/provider.d.ts +0 -19
  260. package/lib/providers/exploreStateSync/provider.js +0 -22
  261. package/src/providers/exploreState/actions/syncStateFromUrl/action.ts +0 -44
  262. package/src/providers/exploreState/actions/syncStateFromUrl/dispatch.ts +0 -16
  263. package/src/providers/exploreState/actions/syncStateFromUrl/types.ts +0 -13
  264. package/src/providers/exploreState/actions/syncStateFromUrl/utils.ts +0 -31
  265. package/src/providers/exploreState/hooks/UseBeforePopState/useBeforePopState.ts +0 -46
  266. package/src/providers/exploreState/hooks/UseBeforePopState/utils.ts +0 -93
  267. package/src/providers/exploreStateSync/hooks/UseMetaCommands/handlers.ts +0 -25
  268. package/src/providers/exploreStateSync/hooks/UseMetaCommands/hook.ts +0 -38
  269. package/src/providers/exploreStateSync/hooks/UseMetaCommands/types.ts +0 -13
  270. package/src/providers/exploreStateSync/provider.tsx +0 -29
  271. /package/lib/{providers/exploreState/actions/syncStateFromUrl → components/DataDictionary/components/Table/columns}/types.js +0 -0
  272. /package/lib/{providers/exploreStateSync → hooks/stateSyncManager}/hooks/UseMetaCommands/types.js +0 -0
@@ -0,0 +1,30 @@
1
+ import React, { useCallback, useRef } from "react";
2
+ import { useOnPopState } from "../../../services/beforePopState/useOnPopState";
3
+ import { WasPopContext } from "./context";
4
+ /**
5
+ * WasPopProvider tracks browser navigation events to determine if the current route change
6
+ * was triggered by a popstate event (browser back/forward navigation).
7
+ *
8
+ * This provider:
9
+ * 1. Registers callbacks for route change events.
10
+ * 2. Tracks when navigation occurs via browser back/forward buttons
11
+ * 3. Provides this state via context to child components
12
+ *
13
+ * This allows components to respond differently to user-initiated navigation versus
14
+ * browser history navigation.
15
+ */
16
+ export function WasPopProvider({ children, }) {
17
+ const popRef = useRef();
18
+ // Pop callback.
19
+ const onBeforePopState = useCallback((state) => {
20
+ popRef.current = state;
21
+ return true;
22
+ }, []);
23
+ // Clear pop ref.
24
+ const onClearPopRef = useCallback(() => {
25
+ popRef.current = undefined;
26
+ }, []);
27
+ // Register the callback to be invoked before pop.
28
+ useOnPopState(onBeforePopState);
29
+ return (React.createElement(WasPopContext.Provider, { value: { onClearPopRef, popRef } }, children));
30
+ }
@@ -0,0 +1,6 @@
1
+ import { MutableRefObject } from "react";
2
+ import { NextHistoryState } from "../../../services/beforePopState/types";
3
+ export interface WasPopContextProps {
4
+ onClearPopRef: () => void;
5
+ popRef: MutableRefObject<NextHistoryState | undefined>;
6
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,20 @@
1
+ import { BeforePopStateCallback } from "./types";
2
+ /**
3
+ * Register a callback to be invoked immediately before any Next.js pop navigation.
4
+ * Return `false` from your callback to prevent the pop; otherwise return `true`.
5
+ * @param cb - The callback function to register.
6
+ */
7
+ export declare function registerBeforePopCallback(cb: BeforePopStateCallback): void;
8
+ /**
9
+ * Unregister a previously registered “before pop” callback.
10
+ * @param cb - The callback function to unregister.
11
+ */
12
+ export declare function unregisterBeforePopCallback(cb: BeforePopStateCallback): void;
13
+ /**
14
+ * Install the global “before pop” interceptor into Next.js’s router.
15
+ * Subsequent calls to this function will be no‐ops.
16
+ *
17
+ * This method must be called once (e.g. in your app’s top‐level code)
18
+ * to enable the pop‐state bus.
19
+ */
20
+ export declare function registerPopStateHandler(): void;
@@ -0,0 +1,58 @@
1
+ import Router from "next/router";
2
+ /**
3
+ * Pop‐State Event Bus
4
+ *
5
+ * Provides a centralized mechanism for components to intercept
6
+ * and optionally prevent Back/Forward navigation (Next.js beforePopState).
7
+ */
8
+ /**
9
+ * A set of callback functions that will run before Next.js performs a pop.
10
+ * Each callback returns `true` to allow navigation or `false` to block.
11
+ */
12
+ const beforePopCallbacks = new Set();
13
+ /**
14
+ * Register a callback to be invoked immediately before any Next.js pop navigation.
15
+ * Return `false` from your callback to prevent the pop; otherwise return `true`.
16
+ * @param cb - The callback function to register.
17
+ */
18
+ export function registerBeforePopCallback(cb) {
19
+ beforePopCallbacks.add(cb);
20
+ }
21
+ /**
22
+ * Unregister a previously registered “before pop” callback.
23
+ * @param cb - The callback function to unregister.
24
+ */
25
+ export function unregisterBeforePopCallback(cb) {
26
+ beforePopCallbacks.delete(cb);
27
+ }
28
+ /**
29
+ * Ensures that we only hook into Next.js once. After install, any pop event
30
+ * will first invoke all registered callbacks and only proceed if all return true.
31
+ */
32
+ let hasInstalledInterceptor = false;
33
+ /**
34
+ * Install the global “before pop” interceptor into Next.js’s router.
35
+ * Subsequent calls to this function will be no‐ops.
36
+ *
37
+ * This method must be called once (e.g. in your app’s top‐level code)
38
+ * to enable the pop‐state bus.
39
+ */
40
+ export function registerPopStateHandler() {
41
+ if (hasInstalledInterceptor)
42
+ return;
43
+ hasInstalledInterceptor = true;
44
+ Router.beforePopState((state) => {
45
+ // Iteratively call every callback. If any returns false, block navigation.
46
+ let allAllow = true;
47
+ beforePopCallbacks.forEach((cb) => {
48
+ try {
49
+ if (cb(state) === false)
50
+ allAllow = false;
51
+ }
52
+ catch (e) {
53
+ console.error("Pop listener failed:", e);
54
+ }
55
+ });
56
+ return allAllow;
57
+ });
58
+ }
@@ -0,0 +1,11 @@
1
+ import Router from "next/router";
2
+ /**
3
+ * Type representing the callback function passed to beforePopState.
4
+ * Extracted from the Next.js Router.beforePopState API.
5
+ */
6
+ export type BeforePopStateCallback = Parameters<typeof Router.beforePopState>[0];
7
+ /**
8
+ * Type representing the state passed to beforePopState.
9
+ * Extracted from the Next.js Router.beforePopState API.
10
+ */
11
+ export type NextHistoryState = Parameters<BeforePopStateCallback>[0];
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,2 @@
1
+ import { BeforePopStateCallback } from "./types";
2
+ export declare const useOnPopState: (cb: BeforePopStateCallback) => void;
@@ -0,0 +1,10 @@
1
+ import { useEffect } from "react";
2
+ import { registerBeforePopCallback, unregisterBeforePopCallback, } from "./popStateBus";
3
+ export const useOnPopState = (cb) => {
4
+ useEffect(() => {
5
+ registerBeforePopCallback(cb);
6
+ return () => {
7
+ unregisterBeforePopCallback(cb);
8
+ };
9
+ }, [cb]);
10
+ };
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Registers the single global `router.beforePopState` handler that
3
+ * fans out to all feature listeners.
4
+ * Safe to call multiple times - only the first invocation does the real install.
5
+ */
6
+ export declare const usePopStateBus: () => void;
@@ -0,0 +1,12 @@
1
+ import { useEffect } from "react";
2
+ import { registerPopStateHandler } from "./popStateBus";
3
+ /**
4
+ * Registers the single global `router.beforePopState` handler that
5
+ * fans out to all feature listeners.
6
+ * Safe to call multiple times - only the first invocation does the real install.
7
+ */
8
+ export const usePopStateBus = () => {
9
+ useEffect(() => {
10
+ registerPopStateHandler();
11
+ }, []);
12
+ };
@@ -0,0 +1,3 @@
1
+ import { ThemeProps } from "../../../theme/theme";
2
+ export declare const shadows01: ({ theme }: ThemeProps) => string;
3
+ export declare const shadows02: ({ theme }: ThemeProps) => string;
@@ -0,0 +1,4 @@
1
+ // Elevation 01
2
+ export const shadows01 = ({ theme }) => theme.shadows[1];
3
+ // Elevation 02
4
+ export const shadows02 = ({ theme }) => theme.shadows[2];
@@ -0,0 +1,47 @@
1
+ export declare const PALETTE: {
2
+ readonly ALERT_LIGHT: "var(--mui-palette-alert-light)";
3
+ readonly ALERT_LIGHTEST: "var(--mui-palette-alert-lightest)";
4
+ readonly ALERT_MAIN: "var(--mui-palette-alert-main)";
5
+ readonly BACKGROUND_DEFAULT: "var(--mui-palette-background-default)";
6
+ readonly BACKGROUND_PAPER: "var(--mui-palette-background-paper)";
7
+ readonly COMMON_BACKGROUND: "var(--mui-palette-common-background)";
8
+ readonly COMMON_BLACK: "var(--mui-palette-common-black)";
9
+ readonly COMMON_WHITE: "var(--mui-palette-common-white)";
10
+ readonly ERROR_CONTRAST_TEXT: "var(--mui-palette-error-contrastText)";
11
+ readonly ERROR_DARK: "var(--mui-palette-error-dark)";
12
+ readonly ERROR_LIGHT: "var(--mui-palette-error-light)";
13
+ readonly ERROR_MAIN: "var(--mui-palette-error-main)";
14
+ readonly INFO_CONTRAST_TEXT: "var(--mui-palette-info-contrastText)";
15
+ readonly INFO_DARK: "var(--mui-palette-info-dark)";
16
+ readonly INFO_LIGHT: "var(--mui-palette-info-light)";
17
+ readonly INFO_LIGHTEST: "var(--mui-palette-info-lightest)";
18
+ readonly INFO_MAIN: "var(--mui-palette-info-main)";
19
+ readonly INK_LIGHT: "var(--mui-palette-ink-light)";
20
+ readonly INK_MAIN: "var(--mui-palette-ink-main)";
21
+ readonly PRIMARY_CONTRAST_TEXT: "var(--mui-palette-primary-contrastText)";
22
+ readonly PRIMARY_DARK: "var(--mui-palette-primary-dark)";
23
+ readonly PRIMARY_LIGHT: "var(--mui-palette-primary-light)";
24
+ readonly PRIMARY_LIGHTEST: "var(--mui-palette-primary-lightest)";
25
+ readonly PRIMARY_MAIN: "var(--mui-palette-primary-main)";
26
+ readonly SECONDARY_CONTRAST_TEXT: "var(--mui-palette-secondary-contrastText)";
27
+ readonly SECONDARY_DARK: "var(--mui-palette-secondary-dark)";
28
+ readonly SECONDARY_LIGHT: "var(--mui-palette-secondary-light)";
29
+ readonly SECONDARY_MAIN: "var(--mui-palette-secondary-main)";
30
+ readonly SMOKE_DARK: "var(--mui-palette-smoke-dark)";
31
+ readonly SMOKE_LIGHT: "var(--mui-palette-smoke-light)";
32
+ readonly SMOKE_LIGHTEST: "var(--mui-palette-smoke-lightest)";
33
+ readonly SMOKE_MAIN: "var(--mui-palette-smoke-main)";
34
+ readonly SUCCESS_CONTRAST_TEXT: "var(--mui-palette-success-contrastText)";
35
+ readonly SUCCESS_DARK: "var(--mui-palette-success-dark)";
36
+ readonly SUCCESS_LIGHT: "var(--mui-palette-success-light)";
37
+ readonly SUCCESS_LIGHTEST: "var(--mui-palette-success-lightest)";
38
+ readonly SUCCESS_MAIN: "var(--mui-palette-success-main)";
39
+ readonly TEXT_DISABLED: "var(--mui-palette-text-disabled)";
40
+ readonly TEXT_PRIMARY: "var(--mui-palette-text-primary)";
41
+ readonly TEXT_SECONDARY: "var(--mui-palette-text-secondary)";
42
+ readonly WARNING_CONTRAST_TEXT: "var(--mui-palette-warning-contrastText)";
43
+ readonly WARNING_DARK: "var(--mui-palette-warning-dark)";
44
+ readonly WARNING_LIGHT: "var(--mui-palette-warning-light)";
45
+ readonly WARNING_LIGHTEST: "var(--mui-palette-warning-lightest)";
46
+ readonly WARNING_MAIN: "var(--mui-palette-warning-main)";
47
+ };
@@ -0,0 +1,47 @@
1
+ export const PALETTE = {
2
+ ALERT_LIGHT: `var(--mui-palette-alert-light)`,
3
+ ALERT_LIGHTEST: `var(--mui-palette-alert-lightest)`,
4
+ ALERT_MAIN: `var(--mui-palette-alert-main)`,
5
+ BACKGROUND_DEFAULT: `var(--mui-palette-background-default)`,
6
+ BACKGROUND_PAPER: `var(--mui-palette-background-paper)`,
7
+ COMMON_BACKGROUND: `var(--mui-palette-common-background)`,
8
+ COMMON_BLACK: `var(--mui-palette-common-black)`,
9
+ COMMON_WHITE: `var(--mui-palette-common-white)`,
10
+ ERROR_CONTRAST_TEXT: `var(--mui-palette-error-contrastText)`,
11
+ ERROR_DARK: `var(--mui-palette-error-dark)`,
12
+ ERROR_LIGHT: `var(--mui-palette-error-light)`,
13
+ ERROR_MAIN: `var(--mui-palette-error-main)`,
14
+ INFO_CONTRAST_TEXT: `var(--mui-palette-info-contrastText)`,
15
+ INFO_DARK: `var(--mui-palette-info-dark)`,
16
+ INFO_LIGHT: `var(--mui-palette-info-light)`,
17
+ INFO_LIGHTEST: `var(--mui-palette-info-lightest)`,
18
+ INFO_MAIN: `var(--mui-palette-info-main)`,
19
+ INK_LIGHT: `var(--mui-palette-ink-light)`,
20
+ INK_MAIN: `var(--mui-palette-ink-main)`,
21
+ PRIMARY_CONTRAST_TEXT: `var(--mui-palette-primary-contrastText)`,
22
+ PRIMARY_DARK: `var(--mui-palette-primary-dark)`,
23
+ PRIMARY_LIGHT: `var(--mui-palette-primary-light)`,
24
+ PRIMARY_LIGHTEST: `var(--mui-palette-primary-lightest)`,
25
+ PRIMARY_MAIN: `var(--mui-palette-primary-main)`,
26
+ SECONDARY_CONTRAST_TEXT: `var(--mui-palette-secondary-contrastText)`,
27
+ SECONDARY_DARK: `var(--mui-palette-secondary-dark)`,
28
+ SECONDARY_LIGHT: `var(--mui-palette-secondary-light)`,
29
+ SECONDARY_MAIN: `var(--mui-palette-secondary-main)`,
30
+ SMOKE_DARK: `var(--mui-palette-smoke-dark)`,
31
+ SMOKE_LIGHT: `var(--mui-palette-smoke-light)`,
32
+ SMOKE_LIGHTEST: `var(--mui-palette-smoke-lightest)`,
33
+ SMOKE_MAIN: `var(--mui-palette-smoke-main)`,
34
+ SUCCESS_CONTRAST_TEXT: `var(--mui-palette-success-contrastText)`,
35
+ SUCCESS_DARK: `var(--mui-palette-success-dark)`,
36
+ SUCCESS_LIGHT: `var(--mui-palette-success-light)`,
37
+ SUCCESS_LIGHTEST: `var(--mui-palette-success-lightest)`,
38
+ SUCCESS_MAIN: `var(--mui-palette-success-main)`,
39
+ TEXT_DISABLED: `var(--mui-palette-text-disabled)`,
40
+ TEXT_PRIMARY: `var(--mui-palette-text-primary)`,
41
+ TEXT_SECONDARY: `var(--mui-palette-text-secondary)`,
42
+ WARNING_CONTRAST_TEXT: `var(--mui-palette-warning-contrastText)`,
43
+ WARNING_DARK: `var(--mui-palette-warning-dark)`,
44
+ WARNING_LIGHT: `var(--mui-palette-warning-light)`,
45
+ WARNING_LIGHTEST: `var(--mui-palette-warning-lightest)`,
46
+ WARNING_MAIN: `var(--mui-palette-warning-main)`,
47
+ };
@@ -0,0 +1,6 @@
1
+ export interface BaseComponentProps {
2
+ className?: string;
3
+ }
4
+ export interface TrackingComponentProps {
5
+ trackingId?: string;
6
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -11,16 +11,20 @@ import { SidebarButton } from "../../components/Layout/components/Sidebar/compon
11
11
  import { SidebarLabel } from "../../components/Layout/components/Sidebar/components/SidebarLabel/sidebarLabel";
12
12
  import { SidebarTools } from "../../components/Layout/components/Sidebar/components/SidebarTools/sidebarTools.styles";
13
13
  import { Sidebar } from "../../components/Layout/components/Sidebar/sidebar";
14
+ import { useStateSyncManager } from "../../hooks/stateSyncManager/hook";
14
15
  import { BREAKPOINT_FN_NAME, useBreakpointHelper, } from "../../hooks/useBreakpointHelper";
15
16
  import { useConfig } from "../../hooks/useConfig";
16
17
  import { useEntityList } from "../../hooks/useEntityList";
17
18
  import { useExploreState } from "../../hooks/useExploreState";
18
19
  import { useSummary } from "../../hooks/useSummary";
19
20
  import { ExploreActionKind } from "../../providers/exploreState";
21
+ import { clearMeta } from "../../providers/exploreState/actions/clearMeta/dispatch";
22
+ import { stateToUrl } from "../../providers/exploreState/actions/stateToUrl/dispatch";
23
+ import { urlToState } from "../../providers/exploreState/actions/urlToState/dispatch";
20
24
  import { SELECT_CATEGORY_KEY } from "../../providers/exploreState/constants";
21
- import { ExploreStateSyncProvider } from "../../providers/exploreStateSync/provider";
22
25
  import { TEST_IDS } from "../../tests/testIds";
23
26
  import { DESKTOP_SM } from "../../theme/common/breakpoints";
27
+ import { buildStateSyncManagerContext } from "./utils";
24
28
  export const ExploreView = (props) => {
25
29
  const [isDrawerOpen, setIsDrawerOpen] = useState(false);
26
30
  const tabletDown = useBreakpointHelper(BREAKPOINT_FN_NAME.DOWN, DESKTOP_SM);
@@ -34,6 +38,15 @@ export const ExploreView = (props) => {
34
38
  useEntityList(props); // Fetch entities.
35
39
  const { entityListType } = props;
36
40
  const categoryFilters = useMemo(() => buildCategoryFilters(categoryViews, categoryGroups), [categoryGroups, categoryViews]);
41
+ /**
42
+ * State sync manager.
43
+ * Handles state synchronization between the explore state and the URL.
44
+ */
45
+ useStateSyncManager({
46
+ actions: { clearMeta, stateToUrl, urlToState },
47
+ dispatch: exploreDispatch,
48
+ state: buildStateSyncManagerContext(exploreState, props),
49
+ });
37
50
  /**
38
51
  * Closes filter drawer.
39
52
  */
@@ -99,7 +112,7 @@ export const ExploreView = (props) => {
99
112
  });
100
113
  }
101
114
  }, [entityListType, exploreDispatch]);
102
- return (React.createElement(ExploreStateSyncProvider, null,
115
+ return (React.createElement(React.Fragment, null,
103
116
  categoryViews && !!categoryViews.length && (React.createElement(Sidebar, { drawerOpen: isDrawerOpen, onDrawerClose: onCloseDrawer },
104
117
  React.createElement(SidebarTools, { "data-testid": TEST_IDS.FILTER_CONTROLS },
105
118
  React.createElement(SidebarLabel, { label: "Filters" }),
@@ -0,0 +1,14 @@
1
+ import { ComponentProps } from "react";
2
+ import { StateSyncManagerContext } from "../../hooks/stateSyncManager/types";
3
+ import { ExploreState } from "../../providers/exploreState";
4
+ import { ExploreView } from "./exploreView";
5
+ /**
6
+ *Builds the state sync manager context object for URL-state synchronization.
7
+ * - Command: The meta command that signals a URL update operation is needed e.g. "STATE_TO_URL_PUSH" or "STATE_TO_URL_REPLACE".
8
+ * - ParamKeys: Expected list of URL parameter keys that should be synchronized with state e.g. "filter", "catalog".
9
+ * - Query: Entity related query object from state that should be synchronized with the URL.
10
+ * @param exploreState - Explore state.
11
+ * @param pageProps - Page props.
12
+ * @returns The state sync manager context.
13
+ */
14
+ export declare function buildStateSyncManagerContext(exploreState: ExploreState, pageProps: ComponentProps<typeof ExploreView>): StateSyncManagerContext;
@@ -0,0 +1,17 @@
1
+ import { EXPLORE_URL_PARAMS } from "../../providers/exploreState/constants";
2
+ /**
3
+ *Builds the state sync manager context object for URL-state synchronization.
4
+ * - Command: The meta command that signals a URL update operation is needed e.g. "STATE_TO_URL_PUSH" or "STATE_TO_URL_REPLACE".
5
+ * - ParamKeys: Expected list of URL parameter keys that should be synchronized with state e.g. "filter", "catalog".
6
+ * - Query: Entity related query object from state that should be synchronized with the URL.
7
+ * @param exploreState - Explore state.
8
+ * @param pageProps - Page props.
9
+ * @returns The state sync manager context.
10
+ */
11
+ export function buildStateSyncManagerContext(exploreState, pageProps) {
12
+ return {
13
+ command: exploreState.meta?.command,
14
+ paramKeys: Object.values(EXPLORE_URL_PARAMS),
15
+ query: exploreState.entities[pageProps.entityListType].query,
16
+ };
17
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@databiosphere/findable-ui",
3
- "version": "34.1.0",
3
+ "version": "35.0.2",
4
4
  "description": "",
5
5
  "scripts": {
6
6
  "test": "node --experimental-vm-modules node_modules/jest/bin/jest.js",
@@ -0,0 +1,47 @@
1
+ import { useState } from "react";
2
+ import { useBeforePopState } from "./hooks/UseBeforePopState/hook";
3
+ import { useMetaCommands } from "./hooks/UseMetaCommands/hook";
4
+ import { useStateSync } from "./hooks/UseStateSync/hook";
5
+ import { StateSyncManagerActions, UseStateSyncManagerProps } from "./types";
6
+
7
+ /**
8
+ * Synchronizes state (e.g. explore state, data dictionary state) with browser URL query parameters.
9
+ *
10
+ * Keeps state and URL in sync by:
11
+ * - Updating URL when state changes (push/replace).
12
+ * - Updating state when URL changes (navigation).
13
+ * - Handling browser history events (back/forward buttons).
14
+ *
15
+ * Internal components:
16
+ * 1. Pop-State Manager - Manages browser navigation events.
17
+ * 2. Meta-Command Handler - Processes URL update commands from state.
18
+ * 3. State-URL Sync - Coordinates bidirectional synchronization.
19
+ *
20
+ * Required setup:
21
+ * - Action creators for state/URL operations.
22
+ * - State containing command flags, URL parameters, and sync configuration.
23
+ */
24
+
25
+ export const useStateSyncManager = <Action>({
26
+ actions: actionCreators,
27
+ dispatch,
28
+ state,
29
+ }: UseStateSyncManagerProps<Action>): void => {
30
+ /**
31
+ * Store action creators in state to maintain referential stability across renders.
32
+ * This prevents unnecessary re-renders in hooks that depend on these actions
33
+ * and ensures effect dependencies remain stable.
34
+ */
35
+ const [actions] = useState<StateSyncManagerActions<Action>>(
36
+ () => actionCreators
37
+ );
38
+
39
+ // Register pop state related side effects.
40
+ useBeforePopState();
41
+
42
+ // Set up Meta-commands related side effects.
43
+ useMetaCommands({ actions, dispatch, state });
44
+
45
+ // Dispatch state <-> URL sync side effects.
46
+ useStateSync({ actions, dispatch, state });
47
+ };
@@ -0,0 +1,30 @@
1
+ import Router from "next/router";
2
+ import { useCallback } from "react";
3
+ import {
4
+ BeforePopStateCallback,
5
+ NextHistoryState,
6
+ } from "../../../../services/beforePopState/types";
7
+ import { useOnPopState } from "../../../../services/beforePopState/useOnPopState";
8
+
9
+ export const useBeforePopState = (): void => {
10
+ // Callback to handle beforePopState.
11
+ const onBeforePopState = useCallback<BeforePopStateCallback>(
12
+ (state: NextHistoryState) => {
13
+ const basePath = Router.basePath ?? "";
14
+ // Use regex to remove the base path from the beginning of the URL.
15
+ const as = basePath
16
+ ? state.as.replace(new RegExp(`^${basePath}`), "") || "/"
17
+ : state.as;
18
+ // Force a full route transition to ensure page props are refreshed.
19
+ Router.replace(as, undefined, {
20
+ ...state.options,
21
+ shallow: false,
22
+ });
23
+ return false;
24
+ },
25
+ []
26
+ );
27
+
28
+ // Pop state related side effects.
29
+ useOnPopState(onBeforePopState);
30
+ };
@@ -0,0 +1,30 @@
1
+ import Router, { useRouter } from "next/router";
2
+ import { useEffect } from "react";
3
+ import { UseStateSyncManagerProps } from "../../types";
4
+ import { META_COMMAND } from "./types";
5
+
6
+ export const useMetaCommands = <Action>({
7
+ actions,
8
+ dispatch,
9
+ state,
10
+ }: UseStateSyncManagerProps<Action>): void => {
11
+ const { isReady } = useRouter();
12
+
13
+ useEffect(() => {
14
+ if (!isReady) return;
15
+ const { command, query } = state;
16
+
17
+ switch (command) {
18
+ case META_COMMAND.STATE_TO_URL_PUSH:
19
+ Router.push({ query }, undefined, { shallow: true });
20
+ dispatch(actions.clearMeta());
21
+ break;
22
+ case META_COMMAND.STATE_TO_URL_REPLACE:
23
+ Router.replace({ query }, undefined, { shallow: true });
24
+ dispatch(actions.clearMeta());
25
+ break;
26
+ default:
27
+ break;
28
+ }
29
+ }, [actions, dispatch, isReady, state]);
30
+ };
@@ -0,0 +1,4 @@
1
+ export enum META_COMMAND {
2
+ STATE_TO_URL_PUSH = "STATE_TO_URL_PUSH",
3
+ STATE_TO_URL_REPLACE = "STATE_TO_URL_REPLACE",
4
+ }
@@ -0,0 +1,52 @@
1
+ import { NextRouter, useRouter } from "next/router";
2
+ import { useEffect } from "react";
3
+ import { ROUTER_METHOD } from "../../../../providers/exploreState/actions/stateToUrl/types";
4
+ import { useWasPop } from "../../../../providers/services/wasPop/hook";
5
+ import { UseStateSyncManagerProps } from "../../types";
6
+ import { hasParams, isSynced, wasPop } from "./utils";
7
+
8
+ export const useStateSync = <Action>({
9
+ actions,
10
+ dispatch,
11
+ state,
12
+ }: UseStateSyncManagerProps<Action>): void => {
13
+ const { basePath, isReady, pathname, query } = useRouter();
14
+ const { onClearPopRef, popRef } = useWasPop();
15
+
16
+ // Extract the query from the state.
17
+ const stateQuery = state.query as NextRouter["query"];
18
+
19
+ // Extract the param keys from the state.
20
+ const paramKeys = state.paramKeys;
21
+
22
+ // Dispatch action to sync state <-> URL.
23
+ useEffect(() => {
24
+ // Do nothing if the router is not ready.
25
+ if (!isReady) return;
26
+
27
+ // Do nothing; URL and state are in sync.
28
+ if (isSynced(stateQuery, query)) {
29
+ onClearPopRef();
30
+ return;
31
+ }
32
+
33
+ // Dispatch action sync URL >> state.
34
+ if (wasPop(basePath, pathname, popRef.current)) {
35
+ // When the user navigates with the back/forward buttons.
36
+ dispatch(actions.urlToState({ query }));
37
+ onClearPopRef();
38
+ return;
39
+ }
40
+
41
+ // Dispatch action sync URL >> state.
42
+ if (hasParams(query, paramKeys)) {
43
+ // When the URL has parameters.
44
+ dispatch(actions.urlToState({ query }));
45
+ return;
46
+ }
47
+
48
+ // Otherwise, dispatch action sync URL << state.
49
+ dispatch(actions.stateToUrl({ method: ROUTER_METHOD.REPLACE }));
50
+ // eslint-disable-next-line react-hooks/exhaustive-deps -- deliberate omission of `state.query`, `state.paramKeys`, `actions` and `dispatch` from dependencies, hook is only expected to run when the Next.js router changes.
51
+ }, [basePath, isReady, pathname, query]);
52
+ };
@@ -0,0 +1,58 @@
1
+ import { NextRouter } from "next/router";
2
+ import { NextHistoryState } from "../../../../services/beforePopState/types";
3
+
4
+ /**
5
+ * Returns true if the query object has any of the specified keys.
6
+ * @param query - Query object.
7
+ * @param paramKeys - Array of parameter keys to check.
8
+ * @returns True if the query object has any of the specified keys.
9
+ */
10
+ export function hasParams(
11
+ query: NextRouter["query"],
12
+ paramKeys: string[]
13
+ ): boolean {
14
+ return paramKeys.some((key) => query[key] !== undefined);
15
+ }
16
+
17
+ /**
18
+ * Returns true if the two query objects are equal.
19
+ * @param queryA - Query object.
20
+ * @param queryB - Query object.
21
+ * @returns True if the two query objects are equal.
22
+ */
23
+ export function isSynced(
24
+ queryA: NextRouter["query"],
25
+ queryB: NextRouter["query"]
26
+ ): boolean {
27
+ return stringifyQuery(queryA) === stringifyQuery(queryB);
28
+ }
29
+
30
+ /**
31
+ * Returns a sorted string representation of a query object.
32
+ * @param query - Query object.
33
+ * @returns Sorted string representation of the query object.
34
+ */
35
+ export function stringifyQuery(query: NextRouter["query"]): string {
36
+ return JSON.stringify(
37
+ Object.keys(query)
38
+ .sort()
39
+ .reduce((acc, key) => ({ ...acc, [key]: query[key] }), {})
40
+ );
41
+ }
42
+
43
+ /**
44
+ * Returns true if the URL was navigated to using the back/forward buttons.
45
+ * @param basePath - Base path.
46
+ * @param pathname - Pathname.
47
+ * @param nextHistoryState - Next history state.
48
+ * @returns True if the URL was navigated to using the back/forward buttons.
49
+ */
50
+ export function wasPop(
51
+ basePath = "",
52
+ pathname: string,
53
+ nextHistoryState: NextHistoryState | undefined
54
+ ): boolean {
55
+ if (!nextHistoryState) return false;
56
+ const [path] = nextHistoryState.url.split("?");
57
+ return path === `${basePath}${pathname}`;
58
+ }
@@ -0,0 +1,34 @@
1
+ import { NextRouter } from "next/router";
2
+ import { Dispatch } from "react";
3
+ import { ROUTER_METHOD } from "../../providers/exploreState/actions/stateToUrl/types";
4
+ import { META_COMMAND } from "./hooks/UseMetaCommands/types";
5
+
6
+ export type ActionCreator<P, DispatchType> = [P] extends [void]
7
+ ? () => DispatchType
8
+ : (payload: P) => DispatchType;
9
+
10
+ export interface StateToUrlPayload {
11
+ method: ROUTER_METHOD;
12
+ }
13
+
14
+ export interface UrlToStatePayload {
15
+ query: NextRouter["query"];
16
+ }
17
+
18
+ export interface StateSyncManagerActions<Action> {
19
+ clearMeta: ActionCreator<void, Action>;
20
+ stateToUrl: ActionCreator<StateToUrlPayload, Action>;
21
+ urlToState: ActionCreator<UrlToStatePayload, Action>;
22
+ }
23
+
24
+ export interface StateSyncManagerContext {
25
+ command: META_COMMAND | undefined;
26
+ paramKeys: string[];
27
+ query: NextRouter["query"];
28
+ }
29
+
30
+ export interface UseStateSyncManagerProps<Action> {
31
+ actions: StateSyncManagerActions<Action>;
32
+ dispatch: Dispatch<Action>;
33
+ state: StateSyncManagerContext;
34
+ }