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
@@ -1,217 +0,0 @@
1
- /* eslint-disable max-classes-per-file */
2
-
3
- import normalizeInputLocation from '../normalizeInputLocation';
4
-
5
- // eslint-disable-next-line no-underscore-dangle
6
- function _loadState(load, isValidLoadedData) {
7
- try {
8
- const data = JSON.parse(load());
9
-
10
- // Check that the stack and index at least seem reasonable before using
11
- // them as state. This isn't foolproof, but it might prevent mistakes.
12
- // Also perform a basic validation of `state`.
13
- if (isValidLoadedData(data)) {
14
- return data;
15
- }
16
- } catch (error) {} // eslint-disable-line no-empty
17
-
18
- return null;
19
- }
20
-
21
- // eslint-disable-next-line no-underscore-dangle
22
- function _saveState(save, data) {
23
- try {
24
- save(JSON.stringify(data));
25
- } catch (error) {} // eslint-disable-line no-empty
26
- }
27
- class MemoryNavigation {
28
- constructor(initialLocation, {
29
- save,
30
- load
31
- } = {}) {
32
- this._save = save;
33
- this._keyPrefix = Date.now().toString(36);
34
- this._keyIndex = 0;
35
- this._subscriptionListener = null;
36
- const initialState = load ? _loadState(load, this._isValidLoadedData) : null;
37
- if (initialState) {
38
- this._stack = initialState.stack;
39
- this._index = initialState.index;
40
- } else {
41
- this._stack = [Object.assign({}, normalizeInputLocation(initialLocation), {
42
- key: this._getNextKey()
43
- })];
44
- this._index = 0;
45
- }
46
- }
47
- _isValidLoadedData({
48
- stack,
49
- index
50
- }) {
51
- // Check that the `stack` and `index` at least seem reasonable before using them.
52
- // This isn't foolproof, but it might prevent mistakes.
53
- return Array.isArray(stack) && typeof index === 'number' && stack[index];
54
- }
55
- init() {
56
- return this._createLocationObject({
57
- action: 'INIT',
58
- delta: 0
59
- });
60
- }
61
- subscribe(listener) {
62
- this._subscriptionListener = listener;
63
- return () => {
64
- this._subscriptionListener = null;
65
- };
66
- }
67
- navigate(location) {
68
- const {
69
- action,
70
- pathname,
71
- search,
72
- query,
73
- hash,
74
- state
75
- } = location;
76
- if (action !== 'PUSH' && action !== 'REPLACE') {
77
- throw Error(`Unrecognized browser session action: ${action}`);
78
- }
79
- const delta = action === 'PUSH' ? 1 : 0;
80
- this._index += delta;
81
- const key = this._getNextKey();
82
- this._stack[this._index] = {
83
- pathname,
84
- search,
85
- query,
86
- hash,
87
- state,
88
- key
89
- };
90
- if (action === 'PUSH') {
91
- this._stack.length = this._index + 1;
92
- }
93
- if (this._save) {
94
- _saveState(this._save, {
95
- stack: this._stack,
96
- index: this._index
97
- });
98
- }
99
- return Object.assign({}, location, {
100
- key,
101
- index: this._index,
102
- delta
103
- });
104
- }
105
- shift(delta) {
106
- const prevIndex = this._index;
107
- this._index = Math.min(Math.max(this._index + delta, 0), this._stack.length - 1);
108
- if (this._index === prevIndex) {
109
- return;
110
- }
111
- if (this._save) {
112
- _saveState(this._save, {
113
- stack: this._stack,
114
- index: this._index
115
- });
116
- }
117
- if (this._subscriptionListener) {
118
- this._subscriptionListener(this._createLocationObject({
119
- action: 'SHIFT',
120
- delta: this._index - prevIndex
121
- }));
122
- }
123
- }
124
- _getNextKey() {
125
- const key = `${this._keyPrefix}.${this._keyIndex.toString(36)}`;
126
- this._keyIndex++;
127
- return key;
128
- }
129
- _createLocationObject({
130
- action,
131
- delta
132
- }) {
133
- return Object.assign({}, this._stack[this._index], {
134
- action,
135
- index: this._index,
136
- delta
137
- });
138
- }
139
- }
140
- class MemoryDataStorage {
141
- constructor({
142
- load,
143
- save
144
- } = {}) {
145
- this._save = save;
146
- const initialState = load ? _loadState(load, this._isValidLoadedData) : null;
147
- if (initialState) {
148
- this._state = initialState.state;
149
- } else {
150
- this._state = {};
151
- }
152
- }
153
-
154
- // Returns either a `string` value or `null` if the key doesn't exist.
155
- get(key) {
156
- if (key in this._state) {
157
- return this._state[key];
158
- }
159
- return null;
160
- }
161
- remove(key) {
162
- if (key in this._state) {
163
- delete this._state[key];
164
- }
165
- if (this._save) {
166
- _saveState(this._save, {
167
- state: this._state
168
- });
169
- }
170
- }
171
- set(key, value) {
172
- this._state[key] = value;
173
- if (this._save) {
174
- _saveState(this._save, {
175
- state: this._state
176
- });
177
- }
178
- }
179
- _isValidLoadedData({
180
- state
181
- }) {
182
- // Perform a basic validation of `state`.
183
- return typeof state === 'object' && state !== null;
184
- }
185
- }
186
- function createNestedStateSaveLoadFunctions({
187
- save,
188
- load
189
- }, key) {
190
- return {
191
- save: save ? data => save(key, data) : undefined,
192
- load: load ? () => load(key) : undefined
193
- };
194
- }
195
- export default class MemorySession {
196
- constructor(initialLocation, {
197
- save,
198
- load
199
- } = {}) {
200
- this.navigation = new MemoryNavigation(initialLocation, createNestedStateSaveLoadFunctions({
201
- save,
202
- load
203
- }, 'navigation'));
204
- this.dataStorage = new MemoryDataStorage(createNestedStateSaveLoadFunctions({
205
- save,
206
- load
207
- }, 'dataStorage'));
208
- }
209
-
210
- // "Before destroy" listeners are currently ignored.
211
- // If required, one could implement a `_destroy()` method
212
- // and there check that the listeners actually do get called.
213
- // eslint-disable-next-line no-unused-vars
214
- addBeforeDestroyListener(listener) {
215
- return () => {};
216
- }
217
- }
@@ -1,58 +0,0 @@
1
- /* eslint-disable max-classes-per-file */
2
-
3
- import normalizeInputLocation from '../normalizeInputLocation';
4
- function noop() {}
5
- function serverSideNavigationNotPossible() {
6
- throw new Error('Server-side navigation is not possible');
7
- }
8
- class ServerNavigation {
9
- constructor(initialLocation) {
10
- this._location = normalizeInputLocation(initialLocation);
11
- }
12
- init() {
13
- return Object.assign({
14
- action: 'INIT'
15
- }, this._location, {
16
- index: 0,
17
- key: '0'
18
- });
19
- }
20
- subscribe() {
21
- // Server-side environment emits no location subscription events.
22
- return noop;
23
- }
24
-
25
- // Navigation methods are not implemented, because `ServerSession` instances
26
- // cannot navigate.
27
- navigate() {
28
- serverSideNavigationNotPossible();
29
- }
30
-
31
- // Navigation methods are not implemented, because `ServerSession` instances
32
- // cannot navigate.
33
- shift() {
34
- serverSideNavigationNotPossible();
35
- }
36
- }
37
- class ServerDataStorage {
38
- // It doesn't seem to make any sense to store anything on server side.
39
- // Hence, state management methods are "no op" stubs.
40
- get() {
41
- return null;
42
- }
43
- remove() {}
44
- set() {}
45
- }
46
- export default class ServerSession {
47
- constructor(initialLocation) {
48
- this.navigation = new ServerNavigation(initialLocation);
49
- this.dataStorage = new ServerDataStorage();
50
- }
51
-
52
- // "Before destroy" listeners are currently ignored.
53
- // If required, one could implement a `_destroy()` method
54
- // and there check that the listeners actually do get called.
55
- addBeforeDestroyListener() {
56
- return noop;
57
- }
58
- }
@@ -1,59 +0,0 @@
1
- import getLocationUrl from './getLocationUrl';
2
-
3
- export default class LocationDataStorage {
4
- constructor(environment, { namespace } = {}) {
5
- this._environment = environment;
6
- this._getFallbackLocationKey = getLocationUrl;
7
- this._stateKeyPrefix = namespace ? `${namespace}|` : '';
8
- }
9
-
10
- get(location, key) {
11
- const stateKey = this._getStateKey(location, key);
12
-
13
- try {
14
- const value = this._environment.dataStorage.get(stateKey);
15
- // === null is probably sufficient.
16
- if (value === null) {
17
- return undefined;
18
- }
19
-
20
- // We want to catch JSON parse errors in case someone separately threw
21
- // junk into sessionStorage under our namespace.
22
- return JSON.parse(value);
23
- } catch (error) {
24
- // Pretend that the entry doesn't exist.
25
- return undefined;
26
- }
27
- }
28
-
29
- set(location, key, value) {
30
- const stateKey = this._getStateKey(location, key);
31
-
32
- if (value === undefined) {
33
- try {
34
- this._environment.dataStorage.remove(stateKey);
35
- } catch (error) {
36
- // No need to handle errors here.
37
- }
38
-
39
- return;
40
- }
41
-
42
- // Unlike with read, we want to fail on invalid values here, since the
43
- // value here is provided by the caller of this method.
44
- const valueString = JSON.stringify(value);
45
-
46
- try {
47
- this._environment.dataStorage.set(stateKey, valueString);
48
- } catch (error) {
49
- // No need to handle errors here either. If it didn't work, it didn't
50
- // work. We make no guarantees about actually saving the value.
51
- }
52
- }
53
-
54
- _getStateKey(location, key) {
55
- const locationKey = location.key || this._getFallbackLocationKey(location);
56
- const keyPrefix = `${this._stateKeyPrefix}${locationKey}`;
57
- return `${keyPrefix}|${key}`;
58
- }
59
- }
@@ -1,2 +0,0 @@
1
- // eslint-disable-next-line no-restricted-exports
2
- export { addBeforeLocationChangeListener as default } from './beforeLocationChangeListeners';
@@ -1,54 +0,0 @@
1
- /* eslint-disable no-underscore-dangle */
2
-
3
- export function getBeforeLocationChangeListeners(session) {
4
- return session._beforeLocationChangeListenersList || [];
5
- }
6
-
7
- function addBeforeLocationChangeListenerToTheList(listener, session) {
8
- if (!session._beforeLocationChangeListenersList) {
9
- session._beforeLocationChangeListenersList = [];
10
- }
11
- session._beforeLocationChangeListenersList.push(listener);
12
- }
13
-
14
- function removeBeforeLocationChangeListenerFromTheList(listener, session) {
15
- if (session._beforeLocationChangeListenersList) {
16
- session._beforeLocationChangeListenersList =
17
- session._beforeLocationChangeListenersList.filter((_) => _ !== listener);
18
- }
19
- }
20
-
21
- export function removeAllBeforeLocationChangeListeners(session) {
22
- session._beforeLocationChangeListenersList = [];
23
- }
24
-
25
- // Runs the `listener` while ignoring any errors that might be thrown by it.
26
- function runBeforeLocationChangeListener(listener, location) {
27
- try {
28
- listener(location);
29
- } catch (error) {
30
- // eslint-disable-next-line no-console
31
- console.warn(
32
- `Ignoring before location change listener \`${listener.name}\` that failed with \`${error}\`.`,
33
- );
34
- // eslint-disable-next-line no-console
35
- console.error(error);
36
- }
37
- }
38
-
39
- // Runs all listeners in order.
40
- export function runBeforeLocationChangeListeners(
41
- navigationListeners,
42
- toLocation,
43
- ) {
44
- for (const listener of navigationListeners) {
45
- runBeforeLocationChangeListener(listener, toLocation);
46
- }
47
- }
48
-
49
- export function addBeforeLocationChangeListener(session, listener) {
50
- addBeforeLocationChangeListenerToTheList(listener, session);
51
- return () => {
52
- removeBeforeLocationChangeListenerFromTheList(listener, session);
53
- };
54
- }
@@ -1,45 +0,0 @@
1
- import createBasePathMiddleware from './middleware/createBasePathMiddleware';
2
- import createBeforeLocationChangeListenerMiddleware from './middleware/createBeforeLocationChangeListenerMiddleware';
3
- import createLocationMiddleware from './middleware/createLocationMiddleware';
4
- import createNavigationBlockerMiddleware from './middleware/createNavigationBlockerMiddleware';
5
- import navigationActionMiddleware from './middleware/navigationActionMiddleware';
6
- import normalizeInputLocationMiddleware from './middleware/normalizeInputLocationMiddleware';
7
-
8
- export default function createMiddlewares(session, options) {
9
- // Allows temporarily ignoring location update events.
10
- let shouldIgnoreLocationSubscriptionEvents = false;
11
- const ignoreLocationSubscriptionEvents = (func) => {
12
- shouldIgnoreLocationSubscriptionEvents = true;
13
- func();
14
- shouldIgnoreLocationSubscriptionEvents = false;
15
- };
16
-
17
- return [
18
- // Validates that the action "payload" (input location) is a proper `NormalizedInputLocation`.
19
- normalizeInputLocationMiddleware,
20
- // Transforms a "PUSH" / "REPLACE" action into a "NAVIGATE" action.
21
- navigationActionMiddleware,
22
- // If a website is hosted under a certain path (`basePath`)
23
- // then this middleware will automatically strip that starting segment from the `pathname` of `location`s.
24
- createBasePathMiddleware(options && options.basePath),
25
- // Allows blocking navigation.
26
- // Handles `NAVIGATE` actions dispatched by the application itself.
27
- createNavigationBlockerMiddleware(session, {
28
- ignoreLocationSubscriptionEvents,
29
- }),
30
- // This "middleware" performs the actual navigation according to the `session` being used.
31
- // For example, when `BrowserSession` is used, it calls methods of the `history` object.
32
- createLocationMiddleware(session, {
33
- shouldIgnoreLocationSubscriptionEvents: () =>
34
- shouldIgnoreLocationSubscriptionEvents,
35
- }),
36
- // Allows blocking navigation.
37
- // Handles location `UPDATE` actions dispatched in response to location update events.
38
- createNavigationBlockerMiddleware(session, {
39
- ignoreLocationSubscriptionEvents,
40
- }),
41
- // Allows subscribing to upcoming location changes
42
- // before those changes are applied in the `location` object in the state.
43
- createBeforeLocationChangeListenerMiddleware(session),
44
- ];
45
- }
@@ -1,20 +0,0 @@
1
- import { addBasePath, removeBasePath } from '../basePath';
2
- import createTransformLocationMiddleware from './createTransformLocationMiddleware';
3
-
4
- // Creates a "middleware" that, when a website is hosted under a certain path (`basePath`),
5
- // automatically strips that starting segment from the `pathname` of `location`s.
6
- export default function createBasePathMiddleware(basePath) {
7
- return createTransformLocationMiddleware({
8
- // Transforms input `Location`:
9
- // prepends `basePath` to the URL.
10
- transformInputLocation: (location) => {
11
- return addBasePath(location, basePath);
12
- },
13
-
14
- // Transforms subscription `Location` object:
15
- // removes `basePath` from the URL.
16
- transformSubscriptionLocation: (location) => {
17
- return removeBasePath(location, basePath);
18
- },
19
- });
20
- }
@@ -1,40 +0,0 @@
1
- import ActionTypes from '../ActionTypes';
2
- import {
3
- getBeforeLocationChangeListeners,
4
- removeAllBeforeLocationChangeListeners,
5
- runBeforeLocationChangeListeners,
6
- } from '../beforeLocationChangeListeners';
7
-
8
- // Creates a "middleware" that calls upcoming navigation listeners.
9
- export default function createBeforeLocationChangeListenerMiddleware(session) {
10
- return function navigationListenerMiddleware() {
11
- return (next) => (action) => {
12
- const { type, payload } = action;
13
-
14
- switch (type) {
15
- // Trigger navigation listeners before the `location` has been updated in Redux state.
16
- // It doesn't matter that the new location URL has already been updated in the web browser's
17
- // address bar, or that the web browser's history has already switched to the new locaiton.
18
- // From the application's point of view, all of that doesn't matter and even doesn't exist.
19
- // All that exists from the application's point of view is the `location` object in the Redux state.
20
- // Until the `location` object in the Redux state is updated, the old page is still rendered.
21
- // The appliation is only concerned with the updates of the `location` object in the Redux state
22
- // and completely ignores any updates to the URL in the web browser's address bar.
23
- case ActionTypes.UPDATE:
24
- runBeforeLocationChangeListeners(
25
- getBeforeLocationChangeListeners(session),
26
- payload,
27
- );
28
- return next(action);
29
-
30
- // Remove any navigation listeners on `DISPOSE` event.
31
- case ActionTypes.DISPOSE:
32
- removeAllBeforeLocationChangeListeners(session);
33
- return next(action);
34
-
35
- default:
36
- return next(action);
37
- }
38
- };
39
- };
40
- }
@@ -1,55 +0,0 @@
1
- import ActionTypes from '../ActionTypes';
2
-
3
- function updateLocation(location) {
4
- return {
5
- type: ActionTypes.UPDATE,
6
- payload: location,
7
- };
8
- }
9
-
10
- // Creates a "middleware" that performs the actual navigation according to the `session` being used.
11
- // For example, when `BrowserSession` is used, it calls methods of the `window.history` object.
12
- export default function createLocationMiddleware(
13
- session,
14
- { shouldIgnoreLocationSubscriptionEvents },
15
- ) {
16
- return function locationMiddleware() {
17
- return (next) => {
18
- // Whenever browser location changes,
19
- // perform the same changes with the internal `location` object.
20
- const unsubscribe = session.navigation.subscribe((location) => {
21
- if (!shouldIgnoreLocationSubscriptionEvents()) {
22
- next(updateLocation(location));
23
- }
24
- });
25
-
26
- return (action) => {
27
- const { type, payload } = action;
28
-
29
- switch (type) {
30
- case ActionTypes.INIT:
31
- return next(updateLocation(session.navigation.init()));
32
-
33
- case ActionTypes.NAVIGATE:
34
- // `session.navigate()` doesn't trigger the `subscribe()` listener.
35
- return next(updateLocation(session.navigation.navigate(payload)));
36
-
37
- case ActionTypes.SHIFT:
38
- // `shift()` will trigger the `subscribe()` listener,
39
- // which will call `updateLocation()`.
40
- session.navigation.shift(payload);
41
- // eslint-disable-next-line consistent-return
42
- return;
43
-
44
- case ActionTypes.DISPOSE:
45
- unsubscribe();
46
- // eslint-disable-next-line consistent-return
47
- return;
48
-
49
- default:
50
- return next(action);
51
- }
52
- };
53
- };
54
- };
55
- }