navigation-stack 0.3.1 → 0.5.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 (257) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/README.md +611 -163
  3. package/data-storage/package.json +6 -0
  4. package/karma.conf.cjs +21 -4
  5. package/lib/cjs/NavigationStack.js +88 -0
  6. package/lib/cjs/data-storage/DataStorage.js +71 -0
  7. package/lib/cjs/data-storage/LocationDataStorage.js +29 -0
  8. package/lib/cjs/data-storage/index.js +9 -0
  9. package/lib/cjs/debug.js +12 -0
  10. package/lib/cjs/environment/InMemoryEnvironment.js +15 -0
  11. package/lib/cjs/environment/WebBrowserEnvironment.js +15 -0
  12. package/lib/cjs/environment/data-storage/InMemoryDataStorage.js +27 -0
  13. package/lib/cjs/environment/data-storage/WebBrowserDataStorage.js +21 -0
  14. package/lib/cjs/environment/scroll-position/InMemoryScrollPosition.js +44 -0
  15. package/lib/cjs/environment/scroll-position/WebBrowserScrollPosition.js +60 -0
  16. package/lib/cjs/getLocationFromInternalLocation.js +14 -0
  17. package/lib/cjs/index.js +20 -16
  18. package/lib/cjs/navigationBlockers.js +28 -23
  19. package/lib/cjs/{normalizeInputLocation.js → parseInputLocation.js} +25 -9
  20. package/lib/cjs/{ActionTypes.js → redux/ActionTypes.js} +1 -1
  21. package/lib/cjs/redux/ActionTypesInternal.js +8 -0
  22. package/lib/cjs/{Actions.js → redux/Actions.js} +5 -4
  23. package/lib/cjs/redux/createMiddlewares.js +60 -0
  24. package/lib/cjs/redux/index.js +13 -0
  25. package/lib/cjs/redux/internalLocationReducer.js +14 -0
  26. package/lib/cjs/redux/middleware/createAddInputLocationBasePathMiddleware.js +32 -0
  27. package/lib/cjs/redux/middleware/createNonProgrammaticNavigationBlockerMiddleware.js +113 -0
  28. package/lib/cjs/redux/middleware/createProgrammaticNavigationBlockerMiddleware.js +94 -0
  29. package/lib/cjs/redux/middleware/createRemoveOutputLocationBasePathMiddleware.js +30 -0
  30. package/lib/cjs/redux/middleware/createUpdateInternalLocationMiddleware.js +73 -0
  31. package/lib/cjs/{middleware/navigationActionMiddleware.js → redux/middleware/navigationOperationMiddleware.js} +11 -8
  32. package/lib/cjs/{middleware/normalizeInputLocationMiddleware.js → redux/middleware/parseInputLocationMiddleware.js} +6 -4
  33. package/lib/cjs/redux/middleware/updateLocationMiddleware.js +34 -0
  34. package/lib/cjs/scroll-position/PageScrollPositionSetter.js +97 -0
  35. package/lib/cjs/scroll-position/ScrollPositionAutoSaver.js +141 -0
  36. package/lib/cjs/scroll-position/ScrollPositionRestoration.js +407 -0
  37. package/lib/cjs/scroll-position/ScrollPositionSaver.js +87 -0
  38. package/lib/cjs/scroll-position/ScrollPositionSetter.js +16 -0
  39. package/lib/cjs/scroll-position/constants.js +5 -0
  40. package/lib/cjs/scroll-position/index.js +7 -0
  41. package/lib/cjs/scroll-position/scheduleNextTick.js +11 -0
  42. package/lib/cjs/session/InMemorySession.js +22 -0
  43. package/lib/cjs/session/ServerSideRenderSession.js +17 -0
  44. package/lib/cjs/session/Session.js +202 -0
  45. package/lib/cjs/session/WebBrowserSession.js +20 -0
  46. package/lib/cjs/session/key/createSessionKey.js +23 -0
  47. package/lib/cjs/session/lifecycle/InMemorySessionLifecycle.js +19 -0
  48. package/lib/cjs/session/lifecycle/WebBrowserSessionLifecycle.js +128 -0
  49. package/lib/cjs/session/lifecycle/page-lifecycle/PageLifecycle.js +269 -0
  50. package/lib/cjs/session/lifecycle/page-lifecycle/PageLifecycleInstance.js +8 -0
  51. package/lib/cjs/session/lifecycle/page-lifecycle/supportsConstructableEventTarget.js +33 -0
  52. package/lib/cjs/session/navigation/InMemoryNavigation.js +104 -0
  53. package/lib/cjs/session/navigation/ServerSideNavigation.js +61 -0
  54. package/lib/cjs/session/navigation/WebBrowserNavigation.js +221 -0
  55. package/lib/cjs/session/navigation/error/NavigationOutOfBoundsError.js +12 -0
  56. package/lib/cjs/session/navigation/error/ServerSideNavigationError.js +21 -0
  57. package/lib/cjs/session/navigation/operation/operations.js +11 -0
  58. package/lib/cjs/session/subscription/Subscription.js +81 -0
  59. package/lib/data-storage/index.d.ts +35 -0
  60. package/lib/esm/NavigationStack.js +81 -0
  61. package/lib/esm/data-storage/DataStorage.js +65 -0
  62. package/lib/esm/data-storage/LocationDataStorage.js +22 -0
  63. package/lib/esm/data-storage/index.js +2 -0
  64. package/lib/esm/debug.js +7 -0
  65. package/lib/esm/environment/InMemoryEnvironment.js +8 -0
  66. package/lib/esm/environment/WebBrowserEnvironment.js +8 -0
  67. package/lib/esm/environment/data-storage/InMemoryDataStorage.js +21 -0
  68. package/lib/esm/environment/data-storage/WebBrowserDataStorage.js +15 -0
  69. package/lib/esm/environment/scroll-position/InMemoryScrollPosition.js +38 -0
  70. package/lib/esm/environment/scroll-position/WebBrowserScrollPosition.js +54 -0
  71. package/lib/esm/getLocationFromInternalLocation.js +9 -0
  72. package/lib/esm/index.js +10 -8
  73. package/lib/esm/navigationBlockers.js +28 -23
  74. package/lib/esm/{normalizeInputLocation.js → parseInputLocation.js} +24 -8
  75. package/lib/esm/{ActionTypes.js → redux/ActionTypes.js} +1 -1
  76. package/lib/esm/redux/ActionTypesInternal.js +3 -0
  77. package/lib/esm/{Actions.js → redux/Actions.js} +5 -4
  78. package/lib/esm/redux/createMiddlewares.js +54 -0
  79. package/lib/esm/redux/index.js +4 -0
  80. package/lib/esm/redux/internalLocationReducer.js +8 -0
  81. package/lib/esm/redux/middleware/createAddInputLocationBasePathMiddleware.js +27 -0
  82. package/lib/esm/redux/middleware/createNonProgrammaticNavigationBlockerMiddleware.js +108 -0
  83. package/lib/esm/redux/middleware/createProgrammaticNavigationBlockerMiddleware.js +88 -0
  84. package/lib/esm/redux/middleware/createRemoveOutputLocationBasePathMiddleware.js +25 -0
  85. package/lib/esm/redux/middleware/createUpdateInternalLocationMiddleware.js +68 -0
  86. package/lib/esm/{middleware/navigationActionMiddleware.js → redux/middleware/navigationOperationMiddleware.js} +10 -7
  87. package/lib/esm/{middleware/normalizeInputLocationMiddleware.js → redux/middleware/parseInputLocationMiddleware.js} +5 -3
  88. package/lib/esm/redux/middleware/updateLocationMiddleware.js +28 -0
  89. package/lib/esm/scroll-position/PageScrollPositionSetter.js +91 -0
  90. package/lib/esm/scroll-position/ScrollPositionAutoSaver.js +134 -0
  91. package/lib/esm/scroll-position/ScrollPositionRestoration.js +400 -0
  92. package/lib/esm/scroll-position/ScrollPositionSaver.js +80 -0
  93. package/lib/esm/scroll-position/ScrollPositionSetter.js +10 -0
  94. package/lib/esm/scroll-position/constants.js +1 -0
  95. package/lib/esm/scroll-position/index.js +1 -0
  96. package/lib/esm/scroll-position/scheduleNextTick.js +6 -0
  97. package/lib/esm/session/InMemorySession.js +15 -0
  98. package/lib/esm/session/ServerSideRenderSession.js +11 -0
  99. package/lib/esm/session/Session.js +195 -0
  100. package/lib/esm/session/WebBrowserSession.js +13 -0
  101. package/lib/esm/session/key/createSessionKey.js +18 -0
  102. package/lib/esm/session/lifecycle/InMemorySessionLifecycle.js +13 -0
  103. package/lib/esm/session/lifecycle/WebBrowserSessionLifecycle.js +120 -0
  104. package/lib/esm/session/lifecycle/page-lifecycle/PageLifecycle.js +263 -0
  105. package/lib/esm/session/lifecycle/page-lifecycle/PageLifecycleInstance.js +2 -0
  106. package/lib/esm/session/lifecycle/page-lifecycle/supportsConstructableEventTarget.js +30 -0
  107. package/lib/esm/session/navigation/InMemoryNavigation.js +97 -0
  108. package/lib/esm/session/navigation/ServerSideNavigation.js +54 -0
  109. package/lib/esm/session/navigation/WebBrowserNavigation.js +213 -0
  110. package/lib/esm/session/navigation/error/NavigationOutOfBoundsError.js +6 -0
  111. package/lib/esm/session/navigation/error/ServerSideNavigationError.js +14 -0
  112. package/lib/esm/session/navigation/operation/operations.js +6 -0
  113. package/lib/esm/session/subscription/Subscription.js +75 -0
  114. package/lib/index.d.ts +179 -157
  115. package/lib/redux/index.d.ts +90 -0
  116. package/lib/scroll-position/index.d.ts +107 -0
  117. package/package.json +9 -5
  118. package/redux/package.json +6 -0
  119. package/scroll-position/package.json +6 -0
  120. package/src/NavigationStack.js +100 -0
  121. package/src/data-storage/DataStorage.js +69 -0
  122. package/src/data-storage/LocationDataStorage.js +23 -0
  123. package/src/data-storage/index.js +2 -0
  124. package/src/debug.js +8 -0
  125. package/src/environment/InMemoryEnvironment.js +9 -0
  126. package/src/environment/WebBrowserEnvironment.js +9 -0
  127. package/src/environment/data-storage/InMemoryDataStorage.js +23 -0
  128. package/src/environment/data-storage/WebBrowserDataStorage.js +17 -0
  129. package/src/environment/scroll-position/InMemoryScrollPosition.js +45 -0
  130. package/src/environment/scroll-position/WebBrowserScrollPosition.js +72 -0
  131. package/src/getLocationFromInternalLocation.js +7 -0
  132. package/src/index.js +10 -8
  133. package/src/navigationBlockers.js +31 -27
  134. package/src/{normalizeInputLocation.js → parseInputLocation.js} +23 -8
  135. package/src/{ActionTypes.js → redux/ActionTypes.js} +1 -1
  136. package/src/redux/ActionTypesInternal.js +3 -0
  137. package/src/{Actions.js → redux/Actions.js} +4 -3
  138. package/src/redux/createMiddlewares.js +65 -0
  139. package/src/redux/index.js +4 -0
  140. package/src/redux/internalLocationReducer.js +9 -0
  141. package/src/redux/middleware/createAddInputLocationBasePathMiddleware.js +27 -0
  142. package/src/redux/middleware/createNonProgrammaticNavigationBlockerMiddleware.js +119 -0
  143. package/src/redux/middleware/createProgrammaticNavigationBlockerMiddleware.js +94 -0
  144. package/src/redux/middleware/createRemoveOutputLocationBasePathMiddleware.js +26 -0
  145. package/src/redux/middleware/createUpdateInternalLocationMiddleware.js +72 -0
  146. package/src/{middleware/navigationActionMiddleware.js → redux/middleware/navigationOperationMiddleware.js} +10 -3
  147. package/src/{middleware/normalizeInputLocationMiddleware.js → redux/middleware/parseInputLocationMiddleware.js} +5 -3
  148. package/src/redux/middleware/updateLocationMiddleware.js +28 -0
  149. package/src/scroll-position/PageScrollPositionSetter.js +110 -0
  150. package/src/scroll-position/ScrollPositionAutoSaver.js +168 -0
  151. package/src/scroll-position/ScrollPositionRestoration.js +551 -0
  152. package/src/scroll-position/ScrollPositionSaver.js +120 -0
  153. package/src/scroll-position/ScrollPositionSetter.js +16 -0
  154. package/src/scroll-position/constants.js +1 -0
  155. package/src/scroll-position/index.js +1 -0
  156. package/src/scroll-position/scheduleNextTick.js +6 -0
  157. package/src/session/InMemorySession.js +13 -0
  158. package/src/session/ServerSideRenderSession.js +9 -0
  159. package/src/session/Session.js +238 -0
  160. package/src/session/WebBrowserSession.js +13 -0
  161. package/src/session/key/createSessionKey.js +18 -0
  162. package/src/session/lifecycle/InMemorySessionLifecycle.js +13 -0
  163. package/src/session/lifecycle/WebBrowserSessionLifecycle.js +126 -0
  164. package/src/session/lifecycle/page-lifecycle/PageLifecycle.js +291 -0
  165. package/src/session/lifecycle/page-lifecycle/PageLifecycleInstance.js +3 -0
  166. package/src/session/lifecycle/page-lifecycle/supportsConstructableEventTarget.js +32 -0
  167. package/src/session/navigation/InMemoryNavigation.js +78 -0
  168. package/src/session/navigation/ServerSideNavigation.js +43 -0
  169. package/src/session/navigation/WebBrowserNavigation.js +224 -0
  170. package/src/session/navigation/error/NavigationOutOfBoundsError.js +7 -0
  171. package/src/session/navigation/error/ServerSideNavigationError.js +18 -0
  172. package/src/session/navigation/operation/operations.js +6 -0
  173. package/src/session/subscription/Subscription.js +76 -0
  174. package/test/NavigationStack.test.js +296 -0
  175. package/test/{LocationDataStorage.test.js → data-storage/LocationDataStorage.test.js} +3 -3
  176. package/test/data-storage/index.test.js +8 -0
  177. package/test/index.js +12 -0
  178. package/test/index.test.js +8 -7
  179. package/test/{helpers.js → middlewareTestUtil.js} +9 -12
  180. package/test/{normalizeInputLocation.test.js → parseInputLocationMiddleware.test.js} +9 -9
  181. package/test/{Action.test.js → redux/Action.test.js} +7 -6
  182. package/test/{ActionTypes.test.js → redux/ActionTypes.test.js} +2 -2
  183. package/test/redux/createMiddlewares.test.js +96 -0
  184. package/test/redux/index.test.js +10 -0
  185. package/test/{locationReducer.test.js → redux/locationReducer.test.js} +4 -7
  186. package/test/redux/middleware/createAddInputLocationBasePathMiddleware.test.js +40 -0
  187. package/test/redux/middleware/createNonProgrammaticNavigationBlockerMiddleware.test.js +264 -0
  188. package/test/redux/middleware/createProgrammaticNavigationBlockerMiddleware.test.js +312 -0
  189. package/test/redux/middleware/createRemoveOutputLocationBasePathMiddleware.test.js +51 -0
  190. package/test/{middleware/navigationActionMiddleware.test.js → redux/middleware/navigationOperationMiddleware.test.js} +16 -12
  191. package/test/{middleware/normalizeInputLocationMiddleware.test.js → redux/middleware/parseInputLocationMiddleware.test.js} +4 -4
  192. package/test/scroll-position/ScrollPositionRestoration.test.js +435 -0
  193. package/test/scroll-position/addScrollableContainer.js +39 -0
  194. package/test/scroll-position/addScrollableContainerWithAnchors.js +56 -0
  195. package/test/scroll-position/createApp.js +132 -0
  196. package/test/scroll-position/delay.js +9 -0
  197. package/test/scroll-position/mockPageLifecycle.js +17 -0
  198. package/test/scroll-position/runApp.js +24 -0
  199. package/test/scroll-position/withScrollableContainerAtIndexPageWithDisabledAutomaticScrollPositionRestoration.js +72 -0
  200. package/test/session/InMemorySession.test.js +348 -0
  201. package/test/session/ServerSession.test.js +17 -9
  202. package/test/session/WebBrowserSession.test.js +265 -0
  203. package/test/testUtil.js +3 -0
  204. package/types/data-storage/index.d.ts +35 -0
  205. package/types/index.d.ts +179 -157
  206. package/types/redux/index.d.ts +90 -0
  207. package/types/scroll-position/index.d.ts +107 -0
  208. package/types/tsconfig.json +1 -1
  209. package/lib/cjs/LocationDataStorage.js +0 -61
  210. package/lib/cjs/addBeforeLocationChangeListener.js +0 -7
  211. package/lib/cjs/beforeLocationChangeListeners.js +0 -51
  212. package/lib/cjs/createMiddlewares.js +0 -47
  213. package/lib/cjs/middleware/createBasePathMiddleware.js +0 -24
  214. package/lib/cjs/middleware/createBeforeLocationChangeListenerMiddleware.js +0 -39
  215. package/lib/cjs/middleware/createLocationMiddleware.js +0 -56
  216. package/lib/cjs/middleware/createNavigationBlockerMiddleware.js +0 -161
  217. package/lib/cjs/middleware/createTransformLocationMiddleware.js +0 -38
  218. package/lib/cjs/onlyAllowedOnClientSide.js +0 -10
  219. package/lib/cjs/session/BrowserSession.js +0 -235
  220. package/lib/cjs/session/MemorySession.js +0 -223
  221. package/lib/cjs/session/ServerSession.js +0 -65
  222. package/lib/esm/LocationDataStorage.js +0 -54
  223. package/lib/esm/addBeforeLocationChangeListener.js +0 -2
  224. package/lib/esm/beforeLocationChangeListeners.js +0 -44
  225. package/lib/esm/createMiddlewares.js +0 -41
  226. package/lib/esm/middleware/createBasePathMiddleware.js +0 -19
  227. package/lib/esm/middleware/createBeforeLocationChangeListenerMiddleware.js +0 -34
  228. package/lib/esm/middleware/createLocationMiddleware.js +0 -50
  229. package/lib/esm/middleware/createNavigationBlockerMiddleware.js +0 -156
  230. package/lib/esm/middleware/createTransformLocationMiddleware.js +0 -33
  231. package/lib/esm/onlyAllowedOnClientSide.js +0 -5
  232. package/lib/esm/session/BrowserSession.js +0 -229
  233. package/lib/esm/session/MemorySession.js +0 -217
  234. package/lib/esm/session/ServerSession.js +0 -58
  235. package/src/LocationDataStorage.js +0 -60
  236. package/src/addBeforeLocationChangeListener.js +0 -2
  237. package/src/beforeLocationChangeListeners.js +0 -54
  238. package/src/createMiddlewares.js +0 -45
  239. package/src/middleware/createBasePathMiddleware.js +0 -20
  240. package/src/middleware/createBeforeLocationChangeListenerMiddleware.js +0 -40
  241. package/src/middleware/createLocationMiddleware.js +0 -55
  242. package/src/middleware/createNavigationBlockerMiddleware.js +0 -168
  243. package/src/middleware/createTransformLocationMiddleware.js +0 -29
  244. package/src/onlyAllowedOnClientSide.js +0 -5
  245. package/src/session/BrowserSession.js +0 -235
  246. package/src/session/MemorySession.js +0 -219
  247. package/src/session/ServerSession.js +0 -67
  248. package/test/createMiddlewares.test.js +0 -62
  249. package/test/middleware/createBasePathMiddleware.test.js +0 -67
  250. package/test/middleware/createBeforeLocationChangeListenerMiddleware.test.js +0 -141
  251. package/test/middleware/createNavigationBlockerMiddleware.test.js +0 -471
  252. package/test/middleware/createTransformLocationMiddleware.test.js +0 -44
  253. package/test/session/BrowserSession.test.js +0 -182
  254. package/test/session/MemorySession.test.js +0 -244
  255. /package/lib/cjs/{locationReducer.js → redux/locationReducer.js} +0 -0
  256. /package/lib/esm/{locationReducer.js → redux/locationReducer.js} +0 -0
  257. /package/src/{locationReducer.js → redux/locationReducer.js} +0 -0
@@ -0,0 +1,110 @@
1
+ /* eslint-disable no-underscore-dangle */
2
+
3
+ import scheduleNextTick from './scheduleNextTick';
4
+
5
+ // The original author of `scroll-behavior` package wrote:
6
+ //
7
+ // "Updating the window scroll position is really flaky.
8
+ // Just trying to scroll it isn't enough.
9
+ // Instead, try to scroll a few times until it works."
10
+ //
11
+ // What it does here is it scrolls two times:
12
+ // * First time at the moment of calling the `.set()` method.
13
+ // * Second time after a momentary delay.
14
+ //
15
+ export default class PageScrollPositionSetter {
16
+ // Sets page scroll position either at an "anchor" or at given coordinates.
17
+ _setPageScrollPositionTo(scrollPositionOrAnchor, environmentScrollPosition) {
18
+ if (typeof scrollPositionOrAnchor === 'string') {
19
+ // Scrolls page to an "ahcnor".
20
+ environmentScrollPosition.setPageScrollPositionAtAnchor(
21
+ scrollPositionOrAnchor,
22
+ );
23
+ } else {
24
+ // Scrolls page to given coordinates.
25
+ environmentScrollPosition.setPageScrollPosition(scrollPositionOrAnchor);
26
+ }
27
+ }
28
+
29
+ _setPageScrollPosition(environmentScrollPosition) {
30
+ const isDelayedCall = Boolean(this._cancelDelayedSetPageScrollPosition);
31
+
32
+ // If this function was triggered in a delayed fashion,
33
+ // clear the reference to the "cancel" function because it's no longer of use.
34
+ if (isDelayedCall) {
35
+ this._cancelDelayedSetPageScrollPosition = null;
36
+ }
37
+
38
+ // It's not really possible for `this._pageScrollPositionOrAnchorToSet` to be `null` or `undefined` at this point.
39
+ // Still, this `if` condition acts as a "foolproof" redundant check.
40
+ /* istanbul ignore if: paranoid guard */
41
+ if (!this._pageScrollPositionOrAnchorToSet) {
42
+ return Promise.resolve();
43
+ }
44
+
45
+ // The original author of `scroll-behavior` package wrote:
46
+ //
47
+ // "Updating the window scroll position is really flaky.
48
+ // Just trying to scroll it isn't enough.
49
+ // Instead, try to scroll a few times until it works."
50
+ //
51
+ this._setPageScrollPositionTo(
52
+ this._pageScrollPositionOrAnchorToSet,
53
+ environmentScrollPosition,
54
+ );
55
+
56
+ // If it was a delayed call, stop.
57
+ if (isDelayedCall) {
58
+ this._resetScrollPositionOrAnchorToSet();
59
+ return Promise.resolve();
60
+ }
61
+
62
+ // Repeat the attempt to set scroll position after a momentary delay.
63
+ return new Promise((resolve) => {
64
+ this._cancelDelayedSetPageScrollPosition = scheduleNextTick(() =>
65
+ resolve(this._setPageScrollPosition(environmentScrollPosition)),
66
+ );
67
+ });
68
+ }
69
+
70
+ // Sets scroll position at an anchor or at given coordinates.
71
+ set(
72
+ scrollableContainer,
73
+ pageScrollPositionOrAnchor,
74
+ environmentScrollPosition,
75
+ ) {
76
+ // Prevents empty string anchor.
77
+ if (!pageScrollPositionOrAnchor) {
78
+ throw new Error('Argument is required');
79
+ }
80
+
81
+ // Validate that no `scrollableContainer` is passed.
82
+ if (scrollableContainer) {
83
+ throw new Error(
84
+ '`scrollableContainer` argument should not be provided because `PageScrollPositionSetter` was only designed to set scroll position of a page',
85
+ );
86
+ }
87
+
88
+ this.cancel();
89
+
90
+ this._pageScrollPositionOrAnchorToSet = pageScrollPositionOrAnchor;
91
+
92
+ return this._setPageScrollPosition(environmentScrollPosition);
93
+ }
94
+
95
+ // This function should be "idempotent", i.e. be able to be called multiple times.
96
+ cancel() {
97
+ if (this._pageScrollPositionOrAnchorToSet) {
98
+ this._resetScrollPositionOrAnchorToSet();
99
+
100
+ if (this._cancelDelayedSetPageScrollPosition) {
101
+ this._cancelDelayedSetPageScrollPosition();
102
+ this._cancelDelayedSetPageScrollPosition = undefined;
103
+ }
104
+ }
105
+ }
106
+
107
+ _resetScrollPositionOrAnchorToSet() {
108
+ this._pageScrollPositionOrAnchorToSet = undefined;
109
+ }
110
+ }
@@ -0,0 +1,168 @@
1
+ /* eslint-disable no-underscore-dangle */
2
+
3
+ import { PAGE_SCROLLABLE_CONTAINER_KEY } from './constants';
4
+ import scheduleNextTick from './scheduleNextTick';
5
+ import debug from '../debug';
6
+
7
+ export default class ScrollPositionAutoSaver {
8
+ constructor({
9
+ scrollPosition,
10
+ scrollPositionSaver,
11
+ getScrollableContainers,
12
+ shouldSaveScrollPosition,
13
+ }) {
14
+ this._scrollPosition = scrollPosition;
15
+ this._scrollPositionSaver = scrollPositionSaver;
16
+ this._shouldSaveScrollPosition = shouldSaveScrollPosition;
17
+
18
+ this._getScrollableContainers = getScrollableContainers;
19
+ }
20
+
21
+ // Starts auto-saving of scroll positions.
22
+ start() {
23
+ // Get scrollable containers.
24
+ const scrollableContainers = this._getScrollableContainers();
25
+
26
+ // Set up scroll listeners on scrollable containers.
27
+ for (const scrollableContainerKey of Object.keys(scrollableContainers)) {
28
+ if (scrollableContainerKey === PAGE_SCROLLABLE_CONTAINER_KEY) {
29
+ this.addPageScrollListener();
30
+ } else {
31
+ this.addScrollableContainerScrollListener(scrollableContainerKey);
32
+ }
33
+ }
34
+ }
35
+
36
+ // Stops auto-saving of scroll positions.
37
+ stop() {
38
+ // Get scrollable containers.
39
+ const scrollableContainers = this._getScrollableContainers();
40
+
41
+ // Remove scroll listeners on scrollable containers.
42
+ for (const scrollableContainerKey of Object.keys(scrollableContainers)) {
43
+ if (scrollableContainerKey === PAGE_SCROLLABLE_CONTAINER_KEY) {
44
+ // If there's any scheduled saving of page scroll position, cancel it.
45
+ this.cancelSavePageScrollPosition();
46
+ // Remove scroll listener on the page.
47
+ this.removePageScrollListener();
48
+ } else {
49
+ this.cancelSaveScrollableContainerScrollPosition(
50
+ scrollableContainerKey,
51
+ );
52
+ this.removeScrollableContainerScrollListener(scrollableContainerKey);
53
+ }
54
+ }
55
+ }
56
+
57
+ cancelScheduledAutoSave() {
58
+ for (const scrollableContainerKey of Object.keys(
59
+ this._getScrollableContainers(),
60
+ )) {
61
+ if (scrollableContainerKey === PAGE_SCROLLABLE_CONTAINER_KEY) {
62
+ this.cancelSavePageScrollPosition();
63
+ } else {
64
+ this.cancelSaveScrollableContainerScrollPosition(
65
+ scrollableContainerKey,
66
+ );
67
+ }
68
+ }
69
+ }
70
+
71
+ cancelSavePageScrollPosition(hasRun) {
72
+ if (this._cancelSavePageScrollPosition) {
73
+ if (!hasRun) {
74
+ debug(
75
+ 'cancel delayed save scroll position',
76
+ PAGE_SCROLLABLE_CONTAINER_KEY,
77
+ );
78
+ }
79
+ this._cancelSavePageScrollPosition();
80
+ this._cancelSavePageScrollPosition = null;
81
+ }
82
+ }
83
+
84
+ cancelSaveScrollableContainerScrollPosition(scrollableContainerKey, hasRun) {
85
+ const scrollableContainerEntry =
86
+ this._getScrollableContainers()[scrollableContainerKey];
87
+ if (scrollableContainerEntry.cancelSaveScrollPosition) {
88
+ if (!hasRun) {
89
+ debug('cancel delayed save scroll position', scrollableContainerKey);
90
+ }
91
+ scrollableContainerEntry.cancelSaveScrollPosition();
92
+ scrollableContainerEntry.cancelSaveScrollPosition = null;
93
+ }
94
+ }
95
+
96
+ removePageScrollListener() {
97
+ // Remove scroll listener on the page.
98
+ if (this._removePageScrollListener) {
99
+ this._removePageScrollListener();
100
+ this._removePageScrollListener = null;
101
+ }
102
+ }
103
+
104
+ removeScrollableContainerScrollListener(scrollableContainerKey) {
105
+ const scrollableContainerEntry =
106
+ this._getScrollableContainers()[scrollableContainerKey];
107
+ if (scrollableContainerEntry.removeScrollListener) {
108
+ scrollableContainerEntry.removeScrollListener();
109
+ scrollableContainerEntry.removeScrollListener = null;
110
+ }
111
+ }
112
+
113
+ addScrollableContainerScrollListener(scrollableContainerKey) {
114
+ const scrollableContainerEntry =
115
+ this._getScrollableContainers()[scrollableContainerKey];
116
+
117
+ scrollableContainerEntry.removeScrollListener =
118
+ this._scrollPosition.addScrollableContainerScrollListener(
119
+ scrollableContainerEntry.scrollableContainer,
120
+ () => {
121
+ // This flag is not used in real life and is only used in tests (for some reason).
122
+ if (!this._shouldSaveScrollPosition()) {
123
+ return;
124
+ }
125
+ // Use `scheduleNextTick()` function to "throttle" incoming scroll events.
126
+ // There would be no use in reacting to every incoming scroll event
127
+ // because there might be too many in a given short period of time
128
+ // which could affect the performance of the application.
129
+ if (!scrollableContainerEntry.cancelSaveScrollPosition) {
130
+ debug('scroll detected', scrollableContainerKey);
131
+ scrollableContainerEntry.cancelSaveScrollPosition =
132
+ scheduleNextTick(() => {
133
+ debug(
134
+ 'auto-save scroll position after scroll',
135
+ scrollableContainerKey,
136
+ );
137
+ this._scrollPositionSaver.saveScrollableContainerScrollPosition(
138
+ scrollableContainerKey,
139
+ scrollableContainerEntry.scrollableContainer,
140
+ );
141
+ });
142
+ }
143
+ },
144
+ );
145
+ }
146
+
147
+ addPageScrollListener() {
148
+ // Set up scroll listener on the page.
149
+ this._removePageScrollListener =
150
+ this._scrollPosition.addPageScrollListener(() => {
151
+ debug('scroll detected', PAGE_SCROLLABLE_CONTAINER_KEY);
152
+
153
+ // This flag is not used in real life and is only used in tests (for some reason).
154
+ if (!this._shouldSaveScrollPosition()) {
155
+ return;
156
+ }
157
+ // Use `scheduleNextTick()` function to "throttle" incoming scroll events.
158
+ // There would be no use in reacting to every incoming scroll event
159
+ // because there might be too many in a given short period of time
160
+ // which could affect the performance of the application.
161
+ if (!this._cancelSavePageScrollPosition) {
162
+ this._cancelSavePageScrollPosition = scheduleNextTick(() => {
163
+ this._scrollPositionSaver.savePageScrollPosition();
164
+ });
165
+ }
166
+ });
167
+ }
168
+ }