navigation-stack 0.3.0 → 0.4.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 (253) hide show
  1. package/README.md +603 -163
  2. package/data-storage/package.json +6 -0
  3. package/karma.conf.cjs +21 -4
  4. package/lib/cjs/NavigationStack.js +73 -0
  5. package/lib/cjs/data-storage/DataStorage.js +71 -0
  6. package/lib/cjs/data-storage/LocationDataStorage.js +29 -0
  7. package/lib/cjs/data-storage/index.js +9 -0
  8. package/lib/cjs/environment/InMemoryEnvironment.js +15 -0
  9. package/lib/cjs/environment/WebBrowserEnvironment.js +15 -0
  10. package/lib/cjs/environment/data-storage/InMemoryDataStorage.js +27 -0
  11. package/lib/cjs/environment/data-storage/WebBrowserDataStorage.js +21 -0
  12. package/lib/cjs/environment/scroll-position/InMemoryScrollPosition.js +44 -0
  13. package/lib/cjs/environment/scroll-position/WebBrowserScrollPosition.js +60 -0
  14. package/lib/cjs/getLocationFromInternalLocation.js +14 -0
  15. package/lib/cjs/index.js +20 -16
  16. package/lib/cjs/navigationBlockers.js +25 -23
  17. package/lib/cjs/{normalizeInputLocation.js → parseInputLocation.js} +25 -9
  18. package/lib/cjs/{ActionTypes.js → redux/ActionTypes.js} +1 -1
  19. package/lib/cjs/redux/ActionTypesInternal.js +8 -0
  20. package/lib/cjs/{Actions.js → redux/Actions.js} +5 -4
  21. package/lib/cjs/redux/createMiddlewares.js +60 -0
  22. package/lib/cjs/redux/index.js +13 -0
  23. package/lib/cjs/redux/internalLocationReducer.js +14 -0
  24. package/lib/cjs/redux/middleware/createAddInputLocationBasePathMiddleware.js +32 -0
  25. package/lib/cjs/redux/middleware/createNonProgrammaticNavigationBlockerMiddleware.js +113 -0
  26. package/lib/cjs/redux/middleware/createProgrammaticNavigationBlockerMiddleware.js +94 -0
  27. package/lib/cjs/redux/middleware/createRemoveOutputLocationBasePathMiddleware.js +30 -0
  28. package/lib/cjs/redux/middleware/createUpdateInternalLocationMiddleware.js +73 -0
  29. package/lib/cjs/{middleware/navigationActionMiddleware.js → redux/middleware/navigationOperationMiddleware.js} +11 -8
  30. package/lib/cjs/{middleware/normalizeInputLocationMiddleware.js → redux/middleware/parseInputLocationMiddleware.js} +6 -4
  31. package/lib/cjs/redux/middleware/updateLocationMiddleware.js +34 -0
  32. package/lib/cjs/scroll-position/PageScrollPositionSetter.js +97 -0
  33. package/lib/cjs/scroll-position/ScrollPositionAutoSaver.js +130 -0
  34. package/lib/cjs/scroll-position/ScrollPositionRestoration.js +383 -0
  35. package/lib/cjs/scroll-position/ScrollPositionSaver.js +81 -0
  36. package/lib/cjs/scroll-position/ScrollPositionSetter.js +16 -0
  37. package/lib/cjs/scroll-position/constants.js +5 -0
  38. package/lib/cjs/scroll-position/index.js +7 -0
  39. package/lib/cjs/scroll-position/scheduleNextTick.js +11 -0
  40. package/lib/cjs/session/InMemorySession.js +22 -0
  41. package/lib/cjs/session/ServerSideRenderSession.js +17 -0
  42. package/lib/cjs/session/Session.js +196 -0
  43. package/lib/cjs/session/WebBrowserSession.js +20 -0
  44. package/lib/cjs/session/key/createSessionKey.js +23 -0
  45. package/lib/cjs/session/lifecycle/InMemorySessionLifecycle.js +19 -0
  46. package/lib/cjs/session/lifecycle/WebBrowserSessionLifecycle.js +128 -0
  47. package/lib/cjs/session/lifecycle/page-lifecycle/PageLifecycle.js +269 -0
  48. package/lib/cjs/session/lifecycle/page-lifecycle/PageLifecycleInstance.js +8 -0
  49. package/lib/cjs/session/lifecycle/page-lifecycle/supportsConstructableEventTarget.js +33 -0
  50. package/lib/cjs/session/navigation/InMemoryNavigation.js +104 -0
  51. package/lib/cjs/session/navigation/ServerSideNavigation.js +61 -0
  52. package/lib/cjs/session/navigation/WebBrowserNavigation.js +221 -0
  53. package/lib/cjs/session/navigation/error/NavigationOutOfBoundsError.js +12 -0
  54. package/lib/cjs/session/navigation/error/ServerSideNavigationError.js +21 -0
  55. package/lib/cjs/session/navigation/operation/operations.js +11 -0
  56. package/lib/cjs/session/subscription/Subscription.js +81 -0
  57. package/lib/data-storage/index.d.ts +35 -0
  58. package/lib/esm/NavigationStack.js +66 -0
  59. package/lib/esm/data-storage/DataStorage.js +65 -0
  60. package/lib/esm/data-storage/LocationDataStorage.js +22 -0
  61. package/lib/esm/data-storage/index.js +2 -0
  62. package/lib/esm/environment/InMemoryEnvironment.js +8 -0
  63. package/lib/esm/environment/WebBrowserEnvironment.js +8 -0
  64. package/lib/esm/environment/data-storage/InMemoryDataStorage.js +21 -0
  65. package/lib/esm/environment/data-storage/WebBrowserDataStorage.js +15 -0
  66. package/lib/esm/environment/scroll-position/InMemoryScrollPosition.js +38 -0
  67. package/lib/esm/environment/scroll-position/WebBrowserScrollPosition.js +54 -0
  68. package/lib/esm/getLocationFromInternalLocation.js +9 -0
  69. package/lib/esm/index.js +10 -8
  70. package/lib/esm/navigationBlockers.js +25 -23
  71. package/lib/esm/{normalizeInputLocation.js → parseInputLocation.js} +24 -8
  72. package/lib/esm/{ActionTypes.js → redux/ActionTypes.js} +1 -1
  73. package/lib/esm/redux/ActionTypesInternal.js +3 -0
  74. package/lib/esm/{Actions.js → redux/Actions.js} +5 -4
  75. package/lib/esm/redux/createMiddlewares.js +54 -0
  76. package/lib/esm/redux/index.js +4 -0
  77. package/lib/esm/redux/internalLocationReducer.js +8 -0
  78. package/lib/esm/redux/middleware/createAddInputLocationBasePathMiddleware.js +27 -0
  79. package/lib/esm/redux/middleware/createNonProgrammaticNavigationBlockerMiddleware.js +108 -0
  80. package/lib/esm/redux/middleware/createProgrammaticNavigationBlockerMiddleware.js +88 -0
  81. package/lib/esm/redux/middleware/createRemoveOutputLocationBasePathMiddleware.js +25 -0
  82. package/lib/esm/redux/middleware/createUpdateInternalLocationMiddleware.js +68 -0
  83. package/lib/esm/{middleware/navigationActionMiddleware.js → redux/middleware/navigationOperationMiddleware.js} +10 -7
  84. package/lib/esm/{middleware/normalizeInputLocationMiddleware.js → redux/middleware/parseInputLocationMiddleware.js} +5 -3
  85. package/lib/esm/redux/middleware/updateLocationMiddleware.js +28 -0
  86. package/lib/esm/scroll-position/PageScrollPositionSetter.js +91 -0
  87. package/lib/esm/scroll-position/ScrollPositionAutoSaver.js +123 -0
  88. package/lib/esm/scroll-position/ScrollPositionRestoration.js +376 -0
  89. package/lib/esm/scroll-position/ScrollPositionSaver.js +74 -0
  90. package/lib/esm/scroll-position/ScrollPositionSetter.js +10 -0
  91. package/lib/esm/scroll-position/constants.js +1 -0
  92. package/lib/esm/scroll-position/index.js +1 -0
  93. package/lib/esm/scroll-position/scheduleNextTick.js +6 -0
  94. package/lib/esm/session/InMemorySession.js +15 -0
  95. package/lib/esm/session/ServerSideRenderSession.js +11 -0
  96. package/lib/esm/session/Session.js +189 -0
  97. package/lib/esm/session/WebBrowserSession.js +13 -0
  98. package/lib/esm/session/key/createSessionKey.js +18 -0
  99. package/lib/esm/session/lifecycle/InMemorySessionLifecycle.js +13 -0
  100. package/lib/esm/session/lifecycle/WebBrowserSessionLifecycle.js +120 -0
  101. package/lib/esm/session/lifecycle/page-lifecycle/PageLifecycle.js +263 -0
  102. package/lib/esm/session/lifecycle/page-lifecycle/PageLifecycleInstance.js +2 -0
  103. package/lib/esm/session/lifecycle/page-lifecycle/supportsConstructableEventTarget.js +30 -0
  104. package/lib/esm/session/navigation/InMemoryNavigation.js +97 -0
  105. package/lib/esm/session/navigation/ServerSideNavigation.js +54 -0
  106. package/lib/esm/session/navigation/WebBrowserNavigation.js +213 -0
  107. package/lib/esm/session/navigation/error/NavigationOutOfBoundsError.js +6 -0
  108. package/lib/esm/session/navigation/error/ServerSideNavigationError.js +14 -0
  109. package/lib/esm/session/navigation/operation/operations.js +6 -0
  110. package/lib/esm/session/subscription/Subscription.js +75 -0
  111. package/lib/index.d.ts +178 -157
  112. package/lib/redux/index.d.ts +90 -0
  113. package/lib/scroll-position/index.d.ts +107 -0
  114. package/package.json +9 -5
  115. package/redux/package.json +6 -0
  116. package/scroll-position/package.json +6 -0
  117. package/src/NavigationStack.js +84 -0
  118. package/src/data-storage/DataStorage.js +69 -0
  119. package/src/data-storage/LocationDataStorage.js +23 -0
  120. package/src/data-storage/index.js +2 -0
  121. package/src/environment/InMemoryEnvironment.js +9 -0
  122. package/src/environment/WebBrowserEnvironment.js +9 -0
  123. package/src/environment/data-storage/InMemoryDataStorage.js +23 -0
  124. package/src/environment/data-storage/WebBrowserDataStorage.js +17 -0
  125. package/src/environment/scroll-position/InMemoryScrollPosition.js +45 -0
  126. package/src/environment/scroll-position/WebBrowserScrollPosition.js +72 -0
  127. package/src/getLocationFromInternalLocation.js +7 -0
  128. package/src/index.js +10 -8
  129. package/src/navigationBlockers.js +28 -27
  130. package/src/{normalizeInputLocation.js → parseInputLocation.js} +23 -8
  131. package/src/{ActionTypes.js → redux/ActionTypes.js} +1 -1
  132. package/src/redux/ActionTypesInternal.js +3 -0
  133. package/src/{Actions.js → redux/Actions.js} +4 -3
  134. package/src/redux/createMiddlewares.js +65 -0
  135. package/src/redux/index.js +4 -0
  136. package/src/redux/internalLocationReducer.js +9 -0
  137. package/src/redux/middleware/createAddInputLocationBasePathMiddleware.js +27 -0
  138. package/src/redux/middleware/createNonProgrammaticNavigationBlockerMiddleware.js +119 -0
  139. package/src/redux/middleware/createProgrammaticNavigationBlockerMiddleware.js +94 -0
  140. package/src/redux/middleware/createRemoveOutputLocationBasePathMiddleware.js +26 -0
  141. package/src/redux/middleware/createUpdateInternalLocationMiddleware.js +72 -0
  142. package/src/{middleware/navigationActionMiddleware.js → redux/middleware/navigationOperationMiddleware.js} +10 -3
  143. package/src/{middleware/normalizeInputLocationMiddleware.js → redux/middleware/parseInputLocationMiddleware.js} +5 -3
  144. package/src/redux/middleware/updateLocationMiddleware.js +28 -0
  145. package/src/scroll-position/PageScrollPositionSetter.js +110 -0
  146. package/src/scroll-position/ScrollPositionAutoSaver.js +151 -0
  147. package/src/scroll-position/ScrollPositionRestoration.js +506 -0
  148. package/src/scroll-position/ScrollPositionSaver.js +100 -0
  149. package/src/scroll-position/ScrollPositionSetter.js +16 -0
  150. package/src/scroll-position/constants.js +1 -0
  151. package/src/scroll-position/index.js +1 -0
  152. package/src/scroll-position/scheduleNextTick.js +6 -0
  153. package/src/session/InMemorySession.js +13 -0
  154. package/src/session/ServerSideRenderSession.js +9 -0
  155. package/src/session/Session.js +216 -0
  156. package/src/session/WebBrowserSession.js +13 -0
  157. package/src/session/key/createSessionKey.js +18 -0
  158. package/src/session/lifecycle/InMemorySessionLifecycle.js +13 -0
  159. package/src/session/lifecycle/WebBrowserSessionLifecycle.js +126 -0
  160. package/src/session/lifecycle/page-lifecycle/PageLifecycle.js +291 -0
  161. package/src/session/lifecycle/page-lifecycle/PageLifecycleInstance.js +3 -0
  162. package/src/session/lifecycle/page-lifecycle/supportsConstructableEventTarget.js +32 -0
  163. package/src/session/navigation/InMemoryNavigation.js +78 -0
  164. package/src/session/navigation/ServerSideNavigation.js +43 -0
  165. package/src/session/navigation/WebBrowserNavigation.js +224 -0
  166. package/src/session/navigation/error/NavigationOutOfBoundsError.js +7 -0
  167. package/src/session/navigation/error/ServerSideNavigationError.js +18 -0
  168. package/src/session/navigation/operation/operations.js +6 -0
  169. package/src/session/subscription/Subscription.js +76 -0
  170. package/test/NavigationStack.test.js +296 -0
  171. package/test/{LocationDataStorage.test.js → data-storage/LocationDataStorage.test.js} +3 -3
  172. package/test/data-storage/index.test.js +8 -0
  173. package/test/index.js +12 -0
  174. package/test/index.test.js +8 -7
  175. package/test/{helpers.js → middlewareTestUtil.js} +9 -12
  176. package/test/{normalizeInputLocation.test.js → parseInputLocationMiddleware.test.js} +9 -9
  177. package/test/{Action.test.js → redux/Action.test.js} +7 -6
  178. package/test/{ActionTypes.test.js → redux/ActionTypes.test.js} +2 -2
  179. package/test/redux/createMiddlewares.test.js +96 -0
  180. package/test/redux/index.test.js +10 -0
  181. package/test/{locationReducer.test.js → redux/locationReducer.test.js} +4 -7
  182. package/test/redux/middleware/createAddInputLocationBasePathMiddleware.test.js +40 -0
  183. package/test/redux/middleware/createNonProgrammaticNavigationBlockerMiddleware.test.js +264 -0
  184. package/test/redux/middleware/createProgrammaticNavigationBlockerMiddleware.test.js +312 -0
  185. package/test/redux/middleware/createRemoveOutputLocationBasePathMiddleware.test.js +51 -0
  186. package/test/{middleware/navigationActionMiddleware.test.js → redux/middleware/navigationOperationMiddleware.test.js} +16 -12
  187. package/test/{middleware/normalizeInputLocationMiddleware.test.js → redux/middleware/parseInputLocationMiddleware.test.js} +4 -4
  188. package/test/scroll-position/ScrollPositionRestoration.test.js +418 -0
  189. package/test/scroll-position/addScrollableContainer.js +36 -0
  190. package/test/scroll-position/addScrollableContainerWithHyperlink.js +50 -0
  191. package/test/scroll-position/createApp.js +112 -0
  192. package/test/scroll-position/delay.js +9 -0
  193. package/test/scroll-position/mockPageLifecycle.js +17 -0
  194. package/test/scroll-position/runApp.js +24 -0
  195. package/test/scroll-position/withScrollableContainerAtIndexPage.js +62 -0
  196. package/test/session/InMemorySession.test.js +348 -0
  197. package/test/session/ServerSession.test.js +17 -9
  198. package/test/session/WebBrowserSession.test.js +265 -0
  199. package/test/testUtil.js +3 -0
  200. package/types/data-storage/index.d.ts +35 -0
  201. package/types/index.d.ts +178 -157
  202. package/types/redux/index.d.ts +90 -0
  203. package/types/scroll-position/index.d.ts +107 -0
  204. package/types/tsconfig.json +1 -1
  205. package/lib/cjs/LocationDataStorage.js +0 -60
  206. package/lib/cjs/addBeforeLocationChangeListener.js +0 -7
  207. package/lib/cjs/beforeLocationChangeListeners.js +0 -51
  208. package/lib/cjs/createMiddlewares.js +0 -47
  209. package/lib/cjs/middleware/createBasePathMiddleware.js +0 -24
  210. package/lib/cjs/middleware/createBeforeLocationChangeListenerMiddleware.js +0 -39
  211. package/lib/cjs/middleware/createLocationMiddleware.js +0 -56
  212. package/lib/cjs/middleware/createNavigationBlockerMiddleware.js +0 -161
  213. package/lib/cjs/middleware/createTransformLocationMiddleware.js +0 -38
  214. package/lib/cjs/onlyAllowedOnClientSide.js +0 -10
  215. package/lib/cjs/session/BrowserSession.js +0 -235
  216. package/lib/cjs/session/MemorySession.js +0 -223
  217. package/lib/cjs/session/ServerSession.js +0 -65
  218. package/lib/esm/LocationDataStorage.js +0 -53
  219. package/lib/esm/addBeforeLocationChangeListener.js +0 -2
  220. package/lib/esm/beforeLocationChangeListeners.js +0 -44
  221. package/lib/esm/createMiddlewares.js +0 -41
  222. package/lib/esm/middleware/createBasePathMiddleware.js +0 -19
  223. package/lib/esm/middleware/createBeforeLocationChangeListenerMiddleware.js +0 -34
  224. package/lib/esm/middleware/createLocationMiddleware.js +0 -50
  225. package/lib/esm/middleware/createNavigationBlockerMiddleware.js +0 -156
  226. package/lib/esm/middleware/createTransformLocationMiddleware.js +0 -33
  227. package/lib/esm/onlyAllowedOnClientSide.js +0 -5
  228. package/lib/esm/session/BrowserSession.js +0 -229
  229. package/lib/esm/session/MemorySession.js +0 -217
  230. package/lib/esm/session/ServerSession.js +0 -58
  231. package/src/LocationDataStorage.js +0 -59
  232. package/src/addBeforeLocationChangeListener.js +0 -2
  233. package/src/beforeLocationChangeListeners.js +0 -54
  234. package/src/createMiddlewares.js +0 -45
  235. package/src/middleware/createBasePathMiddleware.js +0 -20
  236. package/src/middleware/createBeforeLocationChangeListenerMiddleware.js +0 -40
  237. package/src/middleware/createLocationMiddleware.js +0 -55
  238. package/src/middleware/createNavigationBlockerMiddleware.js +0 -168
  239. package/src/middleware/createTransformLocationMiddleware.js +0 -29
  240. package/src/onlyAllowedOnClientSide.js +0 -5
  241. package/src/session/BrowserSession.js +0 -235
  242. package/src/session/MemorySession.js +0 -219
  243. package/src/session/ServerSession.js +0 -67
  244. package/test/createMiddlewares.test.js +0 -62
  245. package/test/middleware/createBasePathMiddleware.test.js +0 -67
  246. package/test/middleware/createBeforeLocationChangeListenerMiddleware.test.js +0 -141
  247. package/test/middleware/createNavigationBlockerMiddleware.test.js +0 -471
  248. package/test/middleware/createTransformLocationMiddleware.test.js +0 -44
  249. package/test/session/BrowserSession.test.js +0 -182
  250. package/test/session/MemorySession.test.js +0 -244
  251. /package/lib/cjs/{locationReducer.js → redux/locationReducer.js} +0 -0
  252. /package/lib/esm/{locationReducer.js → redux/locationReducer.js} +0 -0
  253. /package/src/{locationReducer.js → redux/locationReducer.js} +0 -0
@@ -0,0 +1,123 @@
1
+ /* eslint-disable no-underscore-dangle */
2
+
3
+ import { PAGE_SCROLLABLE_CONTAINER_KEY } from './constants';
4
+ import scheduleNextTick from './scheduleNextTick';
5
+ export default class ScrollPositionAutoSaver {
6
+ constructor({
7
+ scrollPosition,
8
+ scrollPositionSaver,
9
+ getScrollableContainers,
10
+ shouldSaveScrollPosition
11
+ }) {
12
+ this._scrollPosition = scrollPosition;
13
+ this._scrollPositionSaver = scrollPositionSaver;
14
+ this._shouldSaveScrollPosition = shouldSaveScrollPosition;
15
+ this._getScrollableContainers = getScrollableContainers;
16
+ }
17
+
18
+ // Starts auto-saving of scroll positions.
19
+ start() {
20
+ // Get scrollable containers.
21
+ const scrollableContainers = this._getScrollableContainers();
22
+
23
+ // Set up scroll listeners on scrollable containers.
24
+ for (const scrollableContainerKey of Object.keys(scrollableContainers)) {
25
+ if (scrollableContainerKey === PAGE_SCROLLABLE_CONTAINER_KEY) {
26
+ this.addPageScrollListener();
27
+ } else {
28
+ this.addScrollableContainerScrollListener(scrollableContainerKey);
29
+ }
30
+ }
31
+ }
32
+
33
+ // Stops auto-saving of scroll positions.
34
+ stop() {
35
+ // Get scrollable containers.
36
+ const scrollableContainers = this._getScrollableContainers();
37
+
38
+ // Remove scroll listeners on scrollable containers.
39
+ for (const scrollableContainerKey of Object.keys(scrollableContainers)) {
40
+ if (scrollableContainerKey === PAGE_SCROLLABLE_CONTAINER_KEY) {
41
+ // If there's any scheduled saving of page scroll position, cancel it.
42
+ this.cancelSavePageScrollPosition();
43
+ // Remove scroll listener on the page.
44
+ this.removePageScrollListener();
45
+ } else {
46
+ this.cancelSaveScrollableContainerScrollPosition(scrollableContainerKey);
47
+ this.removeScrollableContainerScrollListener(scrollableContainerKey);
48
+ }
49
+ }
50
+ }
51
+ cancelScheduledAutoSave() {
52
+ for (const scrollableContainerKey of Object.keys(this._getScrollableContainers())) {
53
+ if (scrollableContainerKey === PAGE_SCROLLABLE_CONTAINER_KEY) {
54
+ this.cancelSavePageScrollPosition();
55
+ } else {
56
+ this.cancelSaveScrollableContainerScrollPosition(scrollableContainerKey);
57
+ }
58
+ }
59
+ }
60
+ cancelSavePageScrollPosition() {
61
+ if (this._cancelSavePageScrollPosition) {
62
+ this._cancelSavePageScrollPosition();
63
+ this._cancelSavePageScrollPosition = null;
64
+ }
65
+ }
66
+ cancelSaveScrollableContainerScrollPosition(scrollableContainerKey) {
67
+ const scrollableContainerEntry = this._getScrollableContainers()[scrollableContainerKey];
68
+ if (scrollableContainerEntry.cancelSaveScrollPosition) {
69
+ scrollableContainerEntry.cancelSaveScrollPosition();
70
+ scrollableContainerEntry.cancelSaveScrollPosition = null;
71
+ }
72
+ }
73
+ removePageScrollListener() {
74
+ // Remove scroll listener on the page.
75
+ if (this._removePageScrollListener) {
76
+ this._removePageScrollListener();
77
+ this._removePageScrollListener = null;
78
+ }
79
+ }
80
+ removeScrollableContainerScrollListener(scrollableContainerKey) {
81
+ const scrollableContainerEntry = this._getScrollableContainers()[scrollableContainerKey];
82
+ if (scrollableContainerEntry.removeScrollListener) {
83
+ scrollableContainerEntry.removeScrollListener();
84
+ scrollableContainerEntry.removeScrollListener = null;
85
+ }
86
+ }
87
+ addScrollableContainerScrollListener(scrollableContainerKey) {
88
+ const scrollableContainerEntry = this._getScrollableContainers()[scrollableContainerKey];
89
+ scrollableContainerEntry.removeScrollListener = this._scrollPosition.addScrollableContainerScrollListener(scrollableContainerEntry.scrollableContainer, () => {
90
+ // This flag is not used in real life and is only used in tests (for some reason).
91
+ if (!this._shouldSaveScrollPosition()) {
92
+ return;
93
+ }
94
+ // Use `scheduleNextTick()` function to "throttle" incoming scroll events.
95
+ // There would be no use in reacting to every incoming scroll event
96
+ // because there might be too many in a given short period of time
97
+ // which could affect the performance of the application.
98
+ if (!scrollableContainerEntry.cancelSaveScrollPosition) {
99
+ scrollableContainerEntry.cancelSaveScrollPosition = scheduleNextTick(() => {
100
+ this._scrollPositionSaver.saveScrollableContainerScrollPosition(scrollableContainerKey, scrollableContainerEntry.scrollableContainer);
101
+ });
102
+ }
103
+ });
104
+ }
105
+ addPageScrollListener() {
106
+ // Set up scroll listener on the page.
107
+ this._removePageScrollListener = this._scrollPosition.addPageScrollListener(() => {
108
+ // This flag is not used in real life and is only used in tests (for some reason).
109
+ if (!this._shouldSaveScrollPosition()) {
110
+ return;
111
+ }
112
+ // Use `scheduleNextTick()` function to "throttle" incoming scroll events.
113
+ // There would be no use in reacting to every incoming scroll event
114
+ // because there might be too many in a given short period of time
115
+ // which could affect the performance of the application.
116
+ if (!this._cancelSavePageScrollPosition) {
117
+ this._cancelSavePageScrollPosition = scheduleNextTick(() => {
118
+ this._scrollPositionSaver.savePageScrollPosition();
119
+ });
120
+ }
121
+ });
122
+ }
123
+ }
@@ -0,0 +1,376 @@
1
+ /* eslint-disable no-underscore-dangle */
2
+
3
+ import PageScrollPositionSetter from './PageScrollPositionSetter';
4
+ import ScrollPositionSaver from './ScrollPositionSaver';
5
+ import ScrollPositionSetter from './ScrollPositionSetter';
6
+ import { PAGE_SCROLLABLE_CONTAINER_KEY } from './constants';
7
+ import LocationDataStorage from '../data-storage/LocationDataStorage';
8
+ function areEqualScrollPositions(scrollPosition1, scrollPosition2) {
9
+ let i = 0;
10
+ while (i < scrollPosition1.length) {
11
+ if (scrollPosition1[i] !== scrollPosition2[i]) {
12
+ return false;
13
+ }
14
+ i++;
15
+ }
16
+ return true;
17
+ }
18
+ export default class ScrollPositionRestoration {
19
+ constructor(session, _options) {
20
+ // Once configured, scroll restoration mode persists across page reloads.
21
+ // I.e. even if a user refreshes the page in a web browser, the custom
22
+ // `window.history.scrollRestoration` value will still remain.
23
+ //
24
+ // And since it's set to a custom value of "manual", the web browser
25
+ // won't attempt to restore the scroll position on page load
26
+ // which it would otherwise normally do.
27
+ //
28
+ // So what happens if the website is fully server-side rendered?
29
+ // It will wait for the javascript code to be downloaded an executed first
30
+ // and only then that javascript code will programmatically restore the
31
+ // previously-saved scroll position.
32
+ //
33
+ // That would work but it also wouldn't be the most efficient way to do that.
34
+ // Instead, `window.history.scrollRestoration` value could be reset to default beforehand
35
+ // so that when the page finishes refreshing, the web browser could automatically
36
+ // restore the scroll position without waiting for the javascript code to download and run.
37
+ //
38
+ // To reset `window.history.scrollRestoration` value to default beforehand,
39
+ // the code should be notified when the browser tab is about to be terminated or suspended.
40
+ // Terminating could happen for various reasons such as not enough memory, code crash, etc.
41
+ // Suspending is treated equally to terminating because once suspended, it could potentially be
42
+ // terminated afterwards without the code being able to do its stuff while it's suspended.
43
+ //
44
+ // One could consider this feature a minor user experience optimization that relies on the web browser
45
+ // to correctly restore the page scroll every time on page refresh, which it normally does.
46
+ //
47
+ this._sessionExecutionStatusListener = ({
48
+ running
49
+ }) => {
50
+ if (running) {
51
+ this._disableAutomaticScrollRestoration();
52
+ } else {
53
+ this._enableAutomaticScrollRestoration();
54
+
55
+ // There might be previous scroll position already saved in the data storage.
56
+ // Overwrite that previously-saved scroll position with the most up-to-date one
57
+ // just so that there's no stale scroll position left over in the data storage.
58
+ // Alternatively, it could just clear any saved scroll position for this page,
59
+ // since the web browser's automatic scroll restoration is now enabled.
60
+ this._scrollPositionSaver.saveScrollPosition();
61
+ }
62
+ };
63
+ // Overrides the default `window.history.scrollRestoration` value.
64
+ // This prevents the web browser from interfering by disabling its
65
+ // automatic scroll position restoration on "Back"/"Forward" navigation.
66
+ // Instead, the application will have to do it manually.
67
+ // The reason is that when the web browser performs "Back" or "Forward" navigation,
68
+ // it updates the URL in the address bar immediately, and it also attempts to
69
+ // automatically restore scroll position immediately, but the thing is that
70
+ // the application might have delayed rendering of the page due to various reasons
71
+ // such as performance considerations or the architecture of the rendering framework.
72
+ // For example, React framework by design renders pages in "asynchronous" fashion.
73
+ // Hence, by the time the web browser attempts to restore the scroll position,
74
+ // the page might not yet be rendered which would result in incorrect scroll position restoration.
75
+ // That's why the application has to take over this functionality from the web browser.
76
+ this._disableAutomaticScrollRestoration = () => {
77
+ try {
78
+ this._scrollPosition.disableAutomaticScrollRestoration();
79
+ } catch (error) {
80
+ // eslint-disable-next-line no-console
81
+ console.error('[navigation-stack] could not disable default scroll restoration mode');
82
+ }
83
+ };
84
+ this._enableAutomaticScrollRestoration = () => {
85
+ try {
86
+ this._scrollPosition.enableAutomaticScrollRestoration();
87
+ } catch (error) {
88
+ // eslint-disable-next-line no-console
89
+ console.error('[navigation-stack] could not enable default scroll restoration mode');
90
+ }
91
+ };
92
+ this._saveScrollPositionForLocation = (location, scrollableContainerKey, scrollPosition) => {
93
+ this._locationDataStorage.set(location, scrollableContainerKey || PAGE_SCROLLABLE_CONTAINER_KEY, scrollPosition);
94
+ };
95
+ this._scrollPosition = session.environment.scrollPosition;
96
+ this._sessionLifecycle = session.lifecycle;
97
+ this._locationDataStorage = new LocationDataStorage(session, {
98
+ namespace: 'navigation-stack/scroll-position'
99
+ });
100
+ this._scrollPositionSaver = new ScrollPositionSaver({
101
+ scrollPosition: this._scrollPosition,
102
+ saveScrollPositionForLocation: this._saveScrollPositionForLocation,
103
+ getScrollableContainers: () => this._scrollableContainers,
104
+ getLocation: () => this._location,
105
+ shouldSaveScrollPosition: () => !this._doNotSaveScrollPosition
106
+ });
107
+
108
+ // Originally, `scrollableContainerKey` was auto-generated,
109
+ // but then it didn't work with the concept of dynamically adding or removing
110
+ // scrollable containers after `ScrollPositionRestoration` has already started.
111
+ //
112
+ // this._scrollableContainerKeyCounter = 0;
113
+
114
+ this._scrollableContainers = {};
115
+
116
+ // Add page scrollable container.
117
+ this._scrollableContainers[PAGE_SCROLLABLE_CONTAINER_KEY] = {
118
+ scrollableContainer: undefined,
119
+ // Using this option, a developer could theoretically provide their own implementation
120
+ // of setting a scroll position. For example, it could use "smooth" (animated) scrolling, etc.
121
+ // This could be part of the public API if anyone provided a sensible real-world use case for it.
122
+ scrollPositionSetter: _options && _options._pageScrollPositionSetter ||
123
+ // The default page scroll position setter.
124
+ new PageScrollPositionSetter(),
125
+ // This function is only used in tests.
126
+ // There seems to be no use of it in real life, hence it's not public API.
127
+ // It's only used in tests.
128
+ _getScrollPositionForLocation: _options && _options._getPageScrollPositionForLocation,
129
+ // This function is only used in tests.
130
+ // There seems to be no use of it in real life, hence it's not public API.
131
+ // It's only used in tests.
132
+ _shouldUpdateScrollPositionForLocation: _options && _options._shouldUpdatePageScrollPositionForLocation
133
+ };
134
+ }
135
+ addScrollableContainer(scrollableContainerKey, scrollableContainer, _options) {
136
+ // Originally, `scrollableContainerKey` was auto-generated,
137
+ // but then it didn't work with the concept of dynamically adding or removing
138
+ // scrollable containers after `ScrollPositionRestoration` has already started.
139
+ //
140
+ // this._scrollableContainerKeyCounter++;
141
+ // const scrollableContainerKey = String(this._scrollableContainerKeyCounter);
142
+
143
+ if (scrollableContainerKey === PAGE_SCROLLABLE_CONTAINER_KEY) {
144
+ throw new Error(`Scrollable container key "${scrollableContainerKey}" is not allowed`);
145
+ }
146
+
147
+ // Add scrollable container entry.
148
+ this._scrollableContainers[scrollableContainerKey] = {
149
+ // Scrollable container element.
150
+ scrollableContainer,
151
+ // Using this option, a developer could theoretically provide their own implementation
152
+ // of setting a scroll position. For example, it could use "smooth" (animated) scrolling, etc.
153
+ // This could be part of the public API if anyone provided a sensible real-world use case for it.
154
+ scrollPositionSetter: _options && _options._scrollPositionSetter ||
155
+ // The default basic "immediate" scroll position setter.
156
+ new ScrollPositionSetter(),
157
+ // This function is only used in tests.
158
+ // There seems to be no use of it in real life, hence it's not public API.
159
+ // It's only used in tests.
160
+ _shouldUpdateScrollPositionForLocation: _options && _options._shouldUpdateScrollPositionForLocation,
161
+ // This function is only used in tests.
162
+ // There seems to be no use of it in real life, hence it's not public API.
163
+ // It's only used in tests.
164
+ _getScrollPositionForLocation: _options && _options._getScrollPositionForLocation
165
+ };
166
+
167
+ // Scrollable containers could be added at any time, including page mount.
168
+ // For example, a user navigates "Back" to a previous page where there's
169
+ // a "unique" scrollable container that's only present on that page.
170
+ // In that case, the previously-saved scroll position inside the scrollable container
171
+ // should be restored, if it pre-exists. Otherwise, if it doesn't pre-exist,
172
+ // the initial scroll position should be saved for the scrollable container.
173
+ if (this._location) {
174
+ const previouslySavedScrollPosition = this._getSavedScrollPositionForLocation(this._location, scrollableContainerKey);
175
+ if (previouslySavedScrollPosition) {
176
+ this._scrollPosition.setScrollableContainerScrollPosition(scrollableContainer, previouslySavedScrollPosition);
177
+ } else {
178
+ this._scrollPositionSaver.saveScrollableContainerScrollPosition(scrollableContainerKey, scrollableContainer);
179
+ }
180
+ }
181
+ if (this._started) {
182
+ this._scrollPositionSaver._scrollPositionAutoSaver.addScrollableContainerScrollListener(scrollableContainerKey);
183
+ }
184
+
185
+ // Removes the scrollable container.
186
+ return () => {
187
+ this._scrollPositionSaver._scrollPositionAutoSaver.cancelSaveScrollableContainerScrollPosition(scrollableContainerKey);
188
+ this._scrollPositionSaver._scrollPositionAutoSaver.removeScrollableContainerScrollListener(scrollableContainerKey);
189
+ delete this._scrollableContainers[scrollableContainerKey];
190
+ };
191
+ }
192
+ start() {
193
+ // "Foolproof" check.
194
+ if (this._started) {
195
+ throw new Error('Already started');
196
+ }
197
+ this._started = true;
198
+ this._disableAutomaticScrollRestoration();
199
+ this._scrollPositionSaver.start();
200
+ this._removePageStatusListener = this._sessionLifecycle.addExecutionStatusListener(this._sessionExecutionStatusListener);
201
+ }
202
+
203
+ // This method is "idempotent", i.e. it can be called multiple times.
204
+ stop() {
205
+ // "Foolproof" check.
206
+ if (!this._started) {
207
+ return;
208
+ // throw new Error('Not started');
209
+ }
210
+ this._started = false;
211
+ this._enableAutomaticScrollRestoration();
212
+
213
+ // If there's any scroll position still scheduled to be set, cancel it.
214
+ this._cancelAnyPendingSettingOfScrollPosition();
215
+ this._scrollPositionSaver.stop();
216
+ this._removePageStatusListener();
217
+ }
218
+ _cancelAnyPendingSettingOfScrollPosition() {
219
+ for (const scrollableContainerEntry of Object.values(this._scrollableContainers)) {
220
+ scrollableContainerEntry.scrollPositionSetter.cancel();
221
+ }
222
+ }
223
+ // willRenderLocation = (location) => {
224
+ // // "Foolproof" check.
225
+ // if (!this._started) {
226
+ // throw new Error('`ScrollPositionRestoration` not started');
227
+ // }
228
+ //
229
+ // // For the initial location, it doesn't do anything.
230
+ // if (location.operation === Operations.INIT) {
231
+ // return;
232
+ // }
233
+ //
234
+ // // Since the current page will no longer be rendered,
235
+ // // cancel any scheduled setting of scroll position on it.
236
+ // this._cancelAnyPendingSettingOfScrollPosition();
237
+ //
238
+ // // The previous page may have scheduled an auto-save of scroll position.
239
+ // // Since the previous page is no longer rendered, its scroll position can no longer be obtained,
240
+ // // so any scheduled scroll position auto-save produres are irrelevant now.
241
+ // this._scrollPositionSaver.cancelPreviouslyScheduledAutoSave();
242
+ //
243
+ // // Save the current scroll position on the current page while it's still rendered.
244
+ // // This saved scroll position could later be restored in case of returing to this page.
245
+ // this._scrollPositionSaver.saveScrollPosition();
246
+ // };
247
+
248
+ locationRendered(location) {
249
+ // Validate that `location` has a `key`.
250
+ if (!location.key) {
251
+ throw new Error('`location` must have a `key`');
252
+ }
253
+ this._prevLocation = this._location;
254
+ this._location = location;
255
+ this._scrollPosition.init();
256
+ if (!this._started) {
257
+ // `this.start()` requires `this._location` to be set.
258
+ this.start();
259
+
260
+ // The initial page might've been server-side rendered which means that
261
+ // by the time this javascript code is downloaded and executed by the web browser,
262
+ // the user might've already scrolled the page to some position,
263
+ // and all those pre-javascript scroll events won't be registered by `ScrollPositionSaver`.
264
+ // If the user doesn't scroll after javascript is loaded and just navigates to a new page,
265
+ // the initial page won't have any saved scroll position to restore on "Back" navigation.
266
+ // Hence, it should explicitly save the current scroll position at the start of operation.
267
+ if (!this._isDefaultScrollPosition()) {
268
+ // `this._scrollPositionSaver.saveScrollPosition()` requires `this._location` to be set.
269
+ this._scrollPositionSaver.saveScrollPosition();
270
+ }
271
+ }
272
+
273
+ // The previous page may have scheduled an auto-save of scroll position.
274
+ // Since the previous page is no longer rendered, its scroll position can no longer be obtained,
275
+ // so any scheduled scroll position auto-save produres are irrelevant now.
276
+ this._scrollPositionSaver.cancelPreviouslyScheduledAutoSave();
277
+
278
+ // If it was in the middle of setting scroll position for a previous location, cancel it.
279
+ this._cancelAnyPendingSettingOfScrollPosition();
280
+
281
+ // Set the scroll position for the new page:
282
+ // either restore a previously-saved one or set it to a default scroll position.
283
+ this._setScrollPosition();
284
+ }
285
+
286
+ // Tells if the current scroll position is the default one.
287
+ _isDefaultScrollPosition() {
288
+ for (const scrollableContainerKey of Object.keys(this._scrollableContainers)) {
289
+ if (scrollableContainerKey === PAGE_SCROLLABLE_CONTAINER_KEY) {
290
+ if (!areEqualScrollPositions(this._scrollPosition.getPageScrollPosition(), this._getDefaultScrollPosition())) {
291
+ return false;
292
+ }
293
+ } else {
294
+ const scrollableContainerEntry = this._scrollableContainers[scrollableContainerKey];
295
+ if (!areEqualScrollPositions(this._scrollPosition.getScrollableContainerScrollPosition(scrollableContainerEntry.scrollableContainer), this._getDefaultScrollPosition())) {
296
+ return false;
297
+ }
298
+ }
299
+ }
300
+ return true;
301
+ }
302
+ _setScrollPosition() {
303
+ for (const scrollableContainerKey of Object.keys(this._scrollableContainers)) {
304
+ const scrollableContainerEntry = this._scrollableContainers[scrollableContainerKey];
305
+
306
+ // This function is only used in tests.
307
+ // There seems to be no use of it in real life, hence it's not public API.
308
+ // It's only used in tests.
309
+ if (scrollableContainerEntry._shouldUpdateScrollPositionForLocation) {
310
+ if (!scrollableContainerEntry._shouldUpdateScrollPositionForLocation(this._location, this._prevLocation)) {
311
+ continue;
312
+ }
313
+ }
314
+
315
+ // Scroll position (or anchor) to set.
316
+ let scrollPositionOrAnchorToSet;
317
+
318
+ // This function is only used in tests.
319
+ // There seems to be no use of it in real life, hence it's not public API.
320
+ // It's only used in tests.
321
+ if (scrollableContainerEntry._getScrollPositionForLocation) {
322
+ scrollPositionOrAnchorToSet = scrollableContainerEntry._getScrollPositionForLocation(this._location, this._prevLocation);
323
+ }
324
+
325
+ // Get scroll position (or anchor) to set.
326
+ if (!scrollPositionOrAnchorToSet) {
327
+ scrollPositionOrAnchorToSet = scrollableContainerKey === PAGE_SCROLLABLE_CONTAINER_KEY ? this._getPageScrollPositionOrAnchorToSet(this._location) : this._getScrollableContainerScrollPositionToSet(this._location, scrollableContainerKey);
328
+ }
329
+
330
+ // Set scroll position of scrollable container.
331
+ scrollableContainerEntry.scrollPositionSetter.set(scrollableContainerEntry.scrollableContainer, scrollPositionOrAnchorToSet, this._scrollPosition);
332
+ }
333
+ }
334
+ _getSavedScrollPositionForLocation(location, scrollableContainerKey = PAGE_SCROLLABLE_CONTAINER_KEY) {
335
+ return this._locationDataStorage.get(location, scrollableContainerKey);
336
+ }
337
+ // Returns scroll position coordinates or an anchor name.
338
+ _getPageScrollPositionOrAnchorToSet(location) {
339
+ // If it's a return to a previously-visited location,
340
+ // read the saved scroll position from session data store.
341
+ return this._getSavedScrollPositionForLocation(location) || this._getAnchor(location) || this._getDefaultScrollPosition();
342
+ }
343
+
344
+ // Returns scroll position coordinates.
345
+ _getScrollableContainerScrollPositionToSet(location, scrollableContainerKey) {
346
+ // If it's a return to a previously-visited location,
347
+ // read the saved scroll position from session data store.
348
+ return this._getSavedScrollPositionForLocation(location, scrollableContainerKey) || this._getDefaultScrollPosition();
349
+ }
350
+ _getAnchor(location) {
351
+ const {
352
+ hash
353
+ } = location;
354
+ if (hash && hash !== '#') {
355
+ return hash.slice('#'.length);
356
+ }
357
+ return undefined;
358
+ }
359
+ _getDefaultScrollPosition() {
360
+ return [0, 0];
361
+ }
362
+
363
+ // `_enableSavingScrollPosition()` and `_disableSavingScrollPosition()`
364
+ // aren't used in real life and are not part of the public API.
365
+ // They're only used in tests.
366
+ _enableSavingScrollPosition() {
367
+ this._doNotSaveScrollPosition = undefined;
368
+ }
369
+
370
+ // `_enableSavingScrollPosition()` and `_disableSavingScrollPosition()`
371
+ // aren't used in real life and are not part of the public API.
372
+ // They're only used in tests.
373
+ _disableSavingScrollPosition() {
374
+ this._doNotSaveScrollPosition = true;
375
+ }
376
+ }
@@ -0,0 +1,74 @@
1
+ /* eslint-disable no-underscore-dangle */
2
+
3
+ import ScrollPositionAutoSaver from './ScrollPositionAutoSaver';
4
+ import { PAGE_SCROLLABLE_CONTAINER_KEY } from './constants';
5
+ export default class ScrollPositionSaver {
6
+ constructor({
7
+ scrollPosition,
8
+ getLocation,
9
+ saveScrollPositionForLocation,
10
+ getScrollableContainers,
11
+ shouldSaveScrollPosition
12
+ }) {
13
+ this._scrollPosition = scrollPosition;
14
+ this._getLocation = getLocation;
15
+ this._saveScrollPositionForLocation = saveScrollPositionForLocation;
16
+ this._getScrollableContainers = getScrollableContainers;
17
+ this._shouldSaveScrollPosition = shouldSaveScrollPosition;
18
+ this._scrollPositionAutoSaver = new ScrollPositionAutoSaver({
19
+ scrollPosition: this._scrollPosition,
20
+ scrollPositionSaver: this,
21
+ getScrollableContainers,
22
+ shouldSaveScrollPosition
23
+ });
24
+ }
25
+ start() {
26
+ this._scrollPositionAutoSaver.start();
27
+ }
28
+ stop() {
29
+ this._scrollPositionAutoSaver.stop();
30
+ }
31
+ cancelPreviouslyScheduledAutoSave() {
32
+ this._scrollPositionAutoSaver.cancelScheduledAutoSave();
33
+ }
34
+ saveScrollPosition() {
35
+ // This flag is not used in real life and is only used in tests (for some reason).
36
+ if (!this._shouldSaveScrollPosition()) {
37
+ return;
38
+ }
39
+
40
+ // Get scrollable containers.
41
+ const scrollableContainers = this._getScrollableContainers();
42
+
43
+ // Save scroll position of each scrollable container.
44
+ for (const scrollableContainerKey of Object.keys(scrollableContainers)) {
45
+ if (scrollableContainerKey === PAGE_SCROLLABLE_CONTAINER_KEY) {
46
+ this.savePageScrollPosition();
47
+ } else {
48
+ this.saveScrollableContainerScrollPosition(scrollableContainerKey, scrollableContainers[scrollableContainerKey].scrollableContainer);
49
+ }
50
+ }
51
+ }
52
+ savePageScrollPosition() {
53
+ // * If this is not a scheduled "auto-save" of scroll position
54
+ // and there already exists any scheduled "auto-save" of scroll position,
55
+ // cancel it and save scroll position right now instead.
56
+ // * If this is a scheduled "auto-save" of scroll position,
57
+ // clear the "cancel" function because it's no longer of use.
58
+ this._scrollPositionAutoSaver.cancelSavePageScrollPosition();
59
+
60
+ // Save scroll position.
61
+ this._saveScrollPositionForLocation(this._getLocation(), undefined, this._scrollPosition.getPageScrollPosition());
62
+ }
63
+ saveScrollableContainerScrollPosition(scrollableContainerKey, scrollableContainer) {
64
+ // * If this is not a scheduled "auto-save" of scroll position
65
+ // and there already exists any scheduled "auto-save" of scroll position,
66
+ // cancel it and save scroll position right now instead.
67
+ // * If this is a scheduled "auto-save" of scroll position,
68
+ // clear the "cancel" function because it's no longer of use.
69
+ this._scrollPositionAutoSaver.cancelSaveScrollableContainerScrollPosition(scrollableContainerKey);
70
+
71
+ // Save scroll position.
72
+ this._saveScrollPositionForLocation(this._getLocation(), scrollableContainerKey, this._scrollPosition.getScrollableContainerScrollPosition(scrollableContainer));
73
+ }
74
+ }
@@ -0,0 +1,10 @@
1
+ export default class ScrollPositionSetter {
2
+ set(scrollableContainer, scrollPositionOrAnchor, environmentScrollPosition) {
3
+ if (typeof scrollPositionOrAnchor === 'string') {
4
+ throw new Error('`ScrollPositionSetter` only allows setting numeric scroll position, not an anchor string');
5
+ }
6
+ environmentScrollPosition.setScrollableContainerScrollPosition(scrollableContainer, scrollPositionOrAnchor);
7
+ return Promise.resolve();
8
+ }
9
+ cancel() {}
10
+ }
@@ -0,0 +1 @@
1
+ export const PAGE_SCROLLABLE_CONTAINER_KEY = 'page';
@@ -0,0 +1 @@
1
+ export { default as ScrollPositionRestoration } from './ScrollPositionRestoration';
@@ -0,0 +1,6 @@
1
+ export default function scheduleNextTick(func) {
2
+ const timerId = window.requestAnimationFrame(func);
3
+ return () => {
4
+ cancelAnimationFrame(timerId);
5
+ };
6
+ }
@@ -0,0 +1,15 @@
1
+ import Session from './Session';
2
+ import InMemoryEnvironment from '../environment/InMemoryEnvironment';
3
+ import InMemorySessionLifecycle from './lifecycle/InMemorySessionLifecycle';
4
+ import InMemoryNavigation from './navigation/InMemoryNavigation';
5
+ export default class InMemorySession extends Session {
6
+ constructor({
7
+ navigation = new InMemoryNavigation()
8
+ } = {}) {
9
+ super({
10
+ navigation
11
+ });
12
+ this.environment = new InMemoryEnvironment();
13
+ this.lifecycle = new InMemorySessionLifecycle();
14
+ }
15
+ }
@@ -0,0 +1,11 @@
1
+ import InMemorySession from './InMemorySession';
2
+ import ServerSideNavigation from './navigation/ServerSideNavigation';
3
+
4
+ // `ServerSideRenderSession` is just a `InMemorySession` that specifically prohibits any navigation.
5
+ export default class ServerSideRenderSession extends InMemorySession {
6
+ constructor() {
7
+ super({
8
+ navigation: new ServerSideNavigation()
9
+ });
10
+ }
11
+ }