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
package/lib/index.d.ts CHANGED
@@ -11,11 +11,7 @@ export type InputLocationQuery = Record<
11
11
  string | number | boolean | null | undefined
12
12
  >;
13
13
 
14
- export interface Location<TState = any> {
15
- /**
16
- * See the README on the `action` property of `location`.
17
- */
18
- action: 'PUSH' | 'REPLACE' | 'SHIFT' | 'INIT';
14
+ export interface LocationBase {
19
15
  /**
20
16
  * the path name; as on window.location e.g. '/foo'
21
17
  */
@@ -32,61 +28,53 @@ export interface Location<TState = any> {
32
28
  * the location hash; as on window.location e.g. '#qux'
33
29
  */
34
30
  hash: string;
31
+ }
32
+
33
+ export interface Location extends LocationBase {
35
34
  /**
36
35
  * a unique key identifying the current history entry
37
36
  */
38
37
  key: string;
38
+
39
39
  /**
40
40
  * the current index of the history entry, starting at 0 for the initial
41
- * entry; this increments on FarceActions.push but not on
42
- * FarceActions.replace
41
+ * entry; this increments on `.push()` but not on `.replace()`
43
42
  */
44
43
  index: number;
44
+ }
45
+
46
+ type PushOrReplaceOperation = 'push' | 'replace';
47
+
48
+ export interface LocationInternal extends Location {
45
49
  /**
46
- * the difference between the current index and the index of the previous location
50
+ * `navigation-stack` operation.
47
51
  */
48
- delta: number;
52
+ operation: PushOrReplaceOperation | 'shift' | 'init';
49
53
  /**
50
- * any additional location state that the application might explicitly define and store
54
+ * the difference between the index of the current location and the index of the previous location.
51
55
  */
52
- state: TState;
56
+ delta: number;
53
57
  }
54
58
 
55
59
  /**
56
60
  * Location descriptor object used in #push and #replace.
57
61
  */
58
62
  export interface InputLocationObject {
59
- pathname: Location['pathname'];
60
- search?: Location['search'];
63
+ pathname: LocationBase['pathname'];
64
+ search?: LocationBase['search'];
61
65
  query?: InputLocationQuery;
62
- hash?: Location['hash'];
63
- state?: Location['state'];
64
- }
65
-
66
- export interface LocationBase {
67
- pathname: Location['pathname'];
68
- search: Location['search'];
69
- query: Query;
70
- hash: Location['hash'];
71
- state?: Location['state'];
72
- }
73
-
74
- export interface NavigationLocation extends LocationBase {
75
- action: 'PUSH' | 'REPLACE';
66
+ hash?: LocationBase['hash'];
76
67
  }
77
68
 
78
69
  /**
79
- * Location descriptor string:
80
- * store.dispatch(FarceActions.push('/foo?bar=baz#qux'));
70
+ * Location input string: "/foo?bar=baz#qux"
81
71
  *
82
- * Equivalent location descriptor object:
83
- * store.dispatch(FarceActions.push({
84
- * pathname: '/foo',
85
- * search: '?bar=baz',
86
- * hash: '#qux',
87
- * }));
88
- *
89
- * https://github.com/4Catalyzer/farce#locations-and-location-descriptors
72
+ * Equivalent location input object:
73
+ * {
74
+ * pathname: '/foo',
75
+ * search: '?bar=baz',
76
+ * hash: '#qux'
77
+ * }
90
78
  */
91
79
  export type InputLocationString = string;
92
80
 
@@ -99,10 +87,6 @@ export interface InputLocationTypes {
99
87
 
100
88
  export type InputLocation = InputLocationTypes[keyof InputLocationTypes];
101
89
 
102
- export interface CreateMiddlewaresOptions {
103
- basePath?: string;
104
- }
105
-
106
90
  export type NavigationBlockerSyncResult = boolean | undefined;
107
91
  export type NavigationBlockerResult =
108
92
  | NavigationBlockerSyncResult
@@ -112,13 +96,13 @@ export type NavigationBlockerResult =
112
96
  * Navigation blocker function receives a `location` to which the application (or the user) is attempting to navigate.
113
97
  *
114
98
  * * The `location` argument is `null` when the web browser tab is about to be closed.
115
- * * The `location` argument is of type `NavigationLocation` when a `.push()` or `.replace()` action is blocked.
99
+ * * The `location` argument is of type `LocationBase` when a `.push()` or `.replace()` navigation is blocked.
116
100
  * * The `location` argument is of type `Location` when blocking a navigation that was initiated outside of the application code.
117
101
  * For example, when the user clicks "Back" or "Forward" button in a web browser.
118
102
  */
119
- export interface NavigationBlocker {
120
- (location: Location | NavigationLocation | null): NavigationBlockerResult;
121
- }
103
+ export type NavigationBlocker = (
104
+ location: Location | LocationBase | null,
105
+ ) => NavigationBlockerResult;
122
106
 
123
107
  // I dunno why did they use an `interface` here.
124
108
  export interface BeforeLocationChangeListener {
@@ -137,166 +121,204 @@ export function removeBasePath<L extends InputLocation>(
137
121
  export function getLocationUrl(location: InputLocationObject): string;
138
122
  export function parseLocationUrl(locationUrl: string): LocationBase;
139
123
 
140
- export function createMiddlewares(
141
- session: SessionBase,
142
- options?: CreateMiddlewaresOptions,
143
- ): Middleware[];
124
+ export function parseInputLocation(location: InputLocation): LocationBase;
144
125
 
145
126
  export function addNavigationBlocker(
146
- session: SessionBase,
127
+ session: Session,
147
128
  blocker: NavigationBlocker,
148
129
  ): () => void;
149
130
 
150
- export function addBeforeLocationChangeListener(
151
- session: SessionBase,
152
- listener: BeforeLocationChangeListener,
153
- ): () => void;
154
-
155
- export const ActionTypes: {
156
- INIT: '@@navigation-stack/INIT';
157
- PUSH: '@@navigation-stack/PUSH';
158
- REPLACE: '@@navigation-stack/REPLACE';
159
- NAVIGATE: '@@navigation-stack/NAVIGATE';
160
- SHIFT: '@@navigation-stack/SHIFT';
161
- UPDATE: '@@navigation-stack/UPDATE';
162
- DISPOSE: '@@navigation-stack/DISPOSE';
163
- };
164
-
165
- export interface InitAction {
166
- type: (typeof ActionTypes)['INIT'];
131
+ export interface NavigationStackOptions {
132
+ basePath?: string;
133
+ maintainScrollPosition?: boolean;
167
134
  }
168
135
 
169
- export interface PushAction {
170
- type: (typeof ActionTypes)['PUSH'];
171
- payload: InputLocation;
172
- }
136
+ export class NavigationStack<ScrollableContainer = any, Anchor = any> {
137
+ constructor(
138
+ session: Session<ScrollableContainer, Anchor>,
139
+ options?: NavigationStackOptions,
140
+ );
173
141
 
174
- export interface ReplaceAction {
175
- type: (typeof ActionTypes)['REPLACE'];
176
- payload: InputLocation;
177
- }
142
+ addScrollableContainer(
143
+ scrollableContainerKey: string,
144
+ scrollableContainer: ScrollableContainer,
145
+ ): () => void;
146
+
147
+ subscribe(listener: (location: Location) => void): () => void;
148
+
149
+ current(): Location;
178
150
 
179
- export interface RewindAction {
180
- type: (typeof ActionTypes)['SHIFT'];
181
- payload: number;
151
+ init(initialLocation?: InputLocation): void;
152
+
153
+ push(location: InputLocation): void;
154
+
155
+ replace(location: InputLocation): void;
156
+
157
+ shift(delta: number): void;
158
+
159
+ locationRendered(): Promise<void>;
160
+
161
+ stop(): void;
182
162
  }
183
163
 
184
- export interface DisposeAction {
185
- type: (typeof ActionTypes)['DISPOSE'];
164
+ export type SessionTerminationBlocker = () => boolean | undefined;
165
+
166
+ interface SessionExecutionStatusListenerParameters {
167
+ running: boolean;
186
168
  }
187
169
 
188
- export type Action =
189
- | InitAction
190
- | PushAction
191
- | ReplaceAction
192
- | RewindAction
193
- | DisposeAction;
170
+ export type SessionExecutionStatusListener = (
171
+ parameters: SessionExecutionStatusListenerParameters,
172
+ ) => void;
194
173
 
195
- export const Actions: {
196
- init(): InitAction;
197
- push(location: InputLocation): PushAction;
198
- replace(location: InputLocation): ReplaceAction;
199
- shift(delta: number): RewindAction;
200
- dispose(): DisposeAction;
201
- };
174
+ export type ScrollListener = () => void;
202
175
 
203
- type BeforeDestroyListener = () => boolean | undefined;
176
+ export class ServerSideNavigationError extends Error {
177
+ constructor(location: LocationBase);
204
178
 
205
- interface SessionNavigation {
206
- init(): void;
179
+ location: LocationBase;
180
+ }
207
181
 
208
- // Subscribes to changes in location,
209
- // excluding ones that happened as a result of calling `.navigate()`.
210
- subscribe(listener: (location: Location) => void): () => void;
182
+ export class NavigationOutOfBoundsError extends Error {
183
+ constructor(index: number);
211
184
 
212
- navigate(location: NavigationLocation): Location;
185
+ index: number;
186
+ }
213
187
 
214
- shift(delta: number): void;
188
+ export class Navigation {
189
+ // Subscribes to "location change" events.
190
+ subscribe(listener: (location: LocationInternal) => void): () => void;
191
+
192
+ init(
193
+ initialLocation: LocationBase,
194
+ parameters: Pick<
195
+ LocationInternal,
196
+ 'operation' | 'key' | 'index' | 'delta'
197
+ >,
198
+ ): LocationInternal | undefined;
199
+
200
+ navigate(
201
+ location: LocationBase,
202
+ parameters: Pick<
203
+ LocationInternal,
204
+ 'operation' | 'key' | 'index' | 'delta'
205
+ >,
206
+ ): LocationInternal | undefined;
207
+
208
+ shift(
209
+ parameters: Pick<LocationInternal, 'operation' | 'index' | 'delta'>,
210
+ ): LocationInternal | undefined;
211
+
212
+ getInitialLocation(): InputLocation | undefined;
215
213
  }
216
214
 
217
- interface SessionDataStorage {
215
+ export interface EnvironmentDataStorage {
218
216
  get(key: string): string | null;
219
217
  remove(key: string): void;
220
218
  set(key: string, value: string): void;
221
219
  }
222
220
 
223
- export interface Session {
224
- navigation: SessionNavigation;
225
- dataStorage: SessionDataStorage;
226
- addBeforeDestroyListener(listener: BeforeDestroyListener): void;
221
+ export interface SessionLifecycle {
222
+ addTerminationBlocker(blocker: SessionTerminationBlocker): () => void;
223
+ addExecutionStatusListener(
224
+ listener: SessionExecutionStatusListener,
225
+ ): () => void;
226
+ }
227
+
228
+ // Manages scroll position in an environment such as a web browser.
229
+ export interface EnvironmentScrollPosition<ScrollableContainer, Anchor> {
230
+ // Gets numeric scroll position of a page.
231
+ getPageScrollPosition(): [number, number];
232
+ // Sets numeric scroll position of a page.
233
+ setPageScrollPosition(scrollPosition: [number, number]): void;
234
+ // Sets scroll position of a page to be at an "anchor".
235
+ setPageScrollPositionAtAnchor(anchor: Anchor): void;
236
+ // Gets numeric scroll position of a scrollable element.
237
+ getScrollableContainerScrollPosition(
238
+ scrollableContainer: ScrollableContainer,
239
+ ): [number, number];
240
+ // Sets numeric scroll position of a scrollable element.
241
+ setScrollableContainerScrollPosition(
242
+ scrollableContainer: ScrollableContainer,
243
+ scrollPosition: [number, number],
244
+ ): void;
245
+ // Adds "on scroll" listeners.
246
+ addPageScrollListener(listener: ScrollListener): () => void;
247
+ addScrollableContainerScrollListener(listener: ScrollListener): () => void;
248
+ // These methods could be used to disable environment's automatic scroll position restoration feature,
249
+ // such as the one present in web browsers, so that the code could control it manually.
250
+ enableAutomaticScrollRestoration(): void;
251
+ disableAutomaticScrollRestoration(): void;
252
+ // `init()` is called every time when a new page is rendered.
253
+ init(): void;
254
+ }
227
255
 
228
- // These're internal variables that're manually set under the hood.
229
- // _beforeLocationChangeListenersList?: Array<BeforeLocationChangeListener>;
230
- // _navigationBlockersList?: Array<NavigationBlocker>;
231
- // _removeBeforeDestroyListener?: () => void;
232
- // _navigationBlockersEvaluationStatus?: { cancelled?: boolean };
256
+ export interface Environment<ScrollableContainer, Anchor> {
257
+ dataStorage: EnvironmentDataStorage;
258
+ scrollPosition: EnvironmentScrollPosition<ScrollableContainer, Anchor>;
233
259
  }
234
260
 
235
- // This is just a copy-paste of the `session` interface above.
236
- declare abstract class SessionBase implements Session {
237
- navigation: SessionNavigation;
261
+ export interface Session<ScrollableContainer = any, Anchor = any> {
262
+ // `key` should be unique within `environment.dataStorage`.
263
+ // For example, `BrowserEnvironment` uses `window.sessionStorage`
264
+ // that is shared across different sessions within a given web browser tab,
265
+ // hence the uniqueness requirement.
266
+ key: string;
238
267
 
239
- dataStorage: SessionDataStorage;
268
+ // Private varibles. Not public API.
269
+ environment: Environment<ScrollableContainer, Anchor>;
240
270
 
241
- addBeforeDestroyListener(listener: BeforeDestroyListener): void;
271
+ lifecycle: SessionLifecycle;
242
272
 
243
- // These're internal variables that're manually set under the hood.
244
- // _beforeLocationChangeListenersList?: Array<BeforeLocationChangeListener>;
245
- // _navigationBlockersList?: Array<NavigationBlocker>;
246
- // _removeBeforeDestroyListener?: () => void;
247
- // _navigationBlockersEvaluationStatus?: { cancelled?: boolean };
248
- }
273
+ subscribe(listener: (location: LocationInternal) => void): () => void;
249
274
 
250
- export class BrowserSession extends SessionBase {}
275
+ start(initialLocation?: LocationBase): void;
251
276
 
252
- export interface MemorySessionOptions {
253
- save?: (data: string) => void;
254
- load?: () => string | undefined | null;
255
- }
277
+ stop(): void;
256
278
 
257
- export class ServerSession extends SessionBase {
258
- constructor(initialLocation: InputLocation);
259
- }
279
+ navigate(operation: PushOrReplaceOperation, location: LocationBase): void;
260
280
 
261
- export class MemorySession extends SessionBase {
262
- constructor(initialLocation: InputLocation, options?: MemorySessionOptions);
281
+ shift(delta: number): void;
263
282
  }
264
283
 
265
- export const locationReducer: Reducer<Location, Action>;
284
+ // This is just a copy-paste of the `session` interface above.
285
+ declare abstract class SessionBaseClass<
286
+ ScrollableContainer = any,
287
+ Anchor = any,
288
+ > implements Session<ScrollableContainer, Anchor>
289
+ {
290
+ constructor(parameters: { navigation: Navigation });
291
+
292
+ // `key` should be unique within `environment.dataStorage`.
293
+ // For example, `BrowserEnvironment` uses `window.sessionStorage`
294
+ // that is shared across different sessions within a given web browser tab,
295
+ // hence the uniqueness requirement.
296
+ key: string;
266
297
 
267
- export class LocationDataStorage {
268
- constructor(session: Session, options: { namespace: string });
298
+ // Private varibles. Not public API.
299
+ environment: Environment<ScrollableContainer, Anchor>;
269
300
 
270
- get(location: Location, key: string): any;
301
+ lifecycle: SessionLifecycle;
271
302
 
272
- set(location: Location, key: string, value: any): void;
273
- }
303
+ subscribe(listener: (location: LocationInternal) => void): () => void;
274
304
 
275
- // The following types are copy-pasted from `redux`.
305
+ start(initialLocation?: LocationBase): void;
276
306
 
277
- interface ReduxAction<T = any> {
278
- type: T;
279
- }
307
+ stop(): void;
280
308
 
281
- interface AnyAction extends ReduxAction {
282
- // Allows any extra properties to be defined in an action.
283
- [extraProps: string]: any;
284
- }
309
+ navigate(operation: PushOrReplaceOperation, location: LocationBase): void;
285
310
 
286
- interface Dispatch<A extends Action = AnyAction> {
287
- <T extends A>(action: T): T;
311
+ shift(delta: number): void;
288
312
  }
289
313
 
290
- interface MiddlewareAPI<D extends Dispatch = Dispatch, S = any> {
291
- dispatch: D;
292
- getState(): S;
314
+ export class WebBrowserSession extends SessionBaseClass<HTMLElement, string> {
315
+ constructor();
293
316
  }
294
317
 
295
- interface Middleware<S = any, D extends Dispatch = Dispatch> {
296
- (api: MiddlewareAPI<D, S>): (next: Dispatch) => (action: any) => any;
318
+ export class ServerSideRenderSession extends SessionBaseClass<string, string> {
319
+ constructor();
297
320
  }
298
321
 
299
- type Reducer<S = any, A extends Action = AnyAction> = (
300
- state: S | undefined,
301
- action: A,
302
- ) => S;
322
+ export class InMemorySession extends SessionBaseClass<string, string> {
323
+ constructor();
324
+ }
@@ -0,0 +1,90 @@
1
+ import { InputLocation, Session } from '../index.d.js';
2
+
3
+ export interface CreateMiddlewaresOptions {
4
+ basePath?: string;
5
+ }
6
+
7
+ export function createMiddlewares(
8
+ session: Session,
9
+ options?: CreateMiddlewaresOptions,
10
+ ): Middleware[];
11
+
12
+ export const ActionTypes: {
13
+ INIT: '@@navigation-stack/INIT';
14
+ PUSH: '@@navigation-stack/PUSH';
15
+ REPLACE: '@@navigation-stack/REPLACE';
16
+ NAVIGATE: '@@navigation-stack/NAVIGATE';
17
+ SHIFT: '@@navigation-stack/SHIFT';
18
+ UPDATE: '@@navigation-stack/UPDATE';
19
+ STOP: '@@navigation-stack/STOP';
20
+ };
21
+
22
+ export interface InitAction {
23
+ type: (typeof ActionTypes)['INIT'];
24
+ payload?: InputLocation;
25
+ }
26
+
27
+ export interface PushAction {
28
+ type: (typeof ActionTypes)['PUSH'];
29
+ payload: InputLocation;
30
+ }
31
+
32
+ export interface ReplaceAction {
33
+ type: (typeof ActionTypes)['REPLACE'];
34
+ payload: InputLocation;
35
+ }
36
+
37
+ export interface ShiftAction {
38
+ type: (typeof ActionTypes)['SHIFT'];
39
+ payload: number;
40
+ }
41
+
42
+ export interface DisposeAction {
43
+ type: (typeof ActionTypes)['STOP'];
44
+ }
45
+
46
+ export type Action =
47
+ | InitAction
48
+ | PushAction
49
+ | ReplaceAction
50
+ | ShiftAction
51
+ | DisposeAction;
52
+
53
+ export const Actions: {
54
+ init(initialLocation?: InputLocation): InitAction;
55
+ push(location: InputLocation): PushAction;
56
+ replace(location: InputLocation): ReplaceAction;
57
+ shift(delta: number): ShiftAction;
58
+ stop(): DisposeAction;
59
+ };
60
+
61
+ export const locationReducer: Reducer<Location, Action>;
62
+
63
+ // The following types are copy-pasted from `redux`.
64
+
65
+ interface ReduxAction<T = any> {
66
+ type: T;
67
+ }
68
+
69
+ interface AnyAction extends ReduxAction {
70
+ // Allows any extra properties to be defined in an action.
71
+ [extraProps: string]: any;
72
+ }
73
+
74
+ interface Dispatch<A extends Action = AnyAction> {
75
+ <T extends A>(action: T): T;
76
+ }
77
+
78
+ interface MiddlewareAPI<D extends Dispatch = Dispatch, S = any> {
79
+ dispatch: D;
80
+ getState(): S;
81
+ }
82
+
83
+ interface Middleware<S = any, D extends Dispatch = Dispatch> {
84
+ (api: MiddlewareAPI<D, S>): (next: Dispatch) => (action: any) => any;
85
+ }
86
+
87
+ type Reducer<S = any, A extends Action = AnyAction> = (
88
+ state: S | undefined,
89
+ action: A,
90
+ ) => S;
@@ -0,0 +1,107 @@
1
+ // TypeScript Version: 3.0
2
+
3
+ import { EnvironmentScrollPosition, Location, Session } from '../index.d.js';
4
+
5
+ export { EnvironmentScrollPosition, Location, Session } from '../index.d.js';
6
+
7
+ export {};
8
+
9
+ export class ScrollPositionRestoration<
10
+ ScrollableContainer = any,
11
+ Anchor = any,
12
+ > {
13
+ constructor(
14
+ session: Session<ScrollableContainer, Anchor>,
15
+
16
+ // `_options` are currently only used in tests.
17
+ _options?: {
18
+ // `_options._shouldUpdatePageScrollPositionForLocation`
19
+ // isn't used in real life and is not part of the public API.
20
+ // It's only used in tests.
21
+ _shouldUpdatePageScrollPositionForLocation?: (
22
+ location: Location,
23
+ prevLocation: Location | undefined,
24
+ ) => boolean;
25
+
26
+ // `_options._getPageScrollPositionForLocation`
27
+ // isn't used in real life and is not part of the public API.
28
+ // It's only used in tests.
29
+ _getPageScrollPositionForLocation?: (
30
+ location: Location,
31
+ prevLocation: Location | undefined,
32
+ ) => [number, number] | undefined;
33
+
34
+ // Using this option, a developer could theoretically provide their own implementation
35
+ // of setting a scroll position. For example, it could use "smooth" (animated) scrolling, etc.
36
+ // This could be part of the public API if anyone provided a sensible real-world use case for it.
37
+ _pageScrollPositionSetter: ScrollPositionSetter<
38
+ ScrollableContainer,
39
+ Anchor
40
+ >;
41
+ },
42
+ );
43
+
44
+ addScrollableContainer(
45
+ scrollableContainerKey: string,
46
+ scrollableContainer: ScrollableContainer,
47
+
48
+ // `_options` are currently only used in tests.
49
+ _options?: {
50
+ // `_options._shouldUpdateScrollPositionForLocation`
51
+ // isn't used in real life and is not part of the public API.
52
+ // It's only used in tests.
53
+ _shouldUpdateScrollPositionForLocation?: (
54
+ location: Location,
55
+ prevLocation: Location | undefined,
56
+ ) => boolean;
57
+
58
+ // `_options._getScrollPositionForLocation`
59
+ // isn't used in real life and is not part of the public API.
60
+ // It's only used in tests.
61
+ _getScrollPositionForLocation?: (
62
+ location: Location,
63
+ prevLocation: Location | undefined,
64
+ ) => [number, number] | undefined;
65
+
66
+ // Using this option, a developer could theoretically provide their own implementation
67
+ // of setting a scroll position. For example, it could use "smooth" (animated) scrolling, etc.
68
+ // This could be part of the public API if anyone provided a sensible real-world use case for it.
69
+ _scrollPositionSetter: ScrollPositionSetter<ScrollableContainer, Anchor>;
70
+ },
71
+ ): () => void;
72
+
73
+ locationRendered: (location: Location) => Promise<void>;
74
+
75
+ stop(): void;
76
+
77
+ // `_enableSavingScrollPosition()` and `_disableSavingScrollPosition()`
78
+ // aren't used in real life and are not part of the public API.
79
+ // They're only used in tests.
80
+ _enableSavingScrollPosition(): void;
81
+
82
+ // `_enableSavingScrollPosition()` and `_disableSavingScrollPosition()`
83
+ // aren't used in real life and are not part of the public API.
84
+ // They're only used in tests.
85
+ _disableSavingScrollPosition(): void;
86
+ }
87
+
88
+ // Theoretically, a developer could pass their own `ScrollPositionSetter` implementation
89
+ // when calling `.addScrollableContainer()` or
90
+ export interface ScrollPositionSetter<ScrollableContainer, Anchor> {
91
+ // Sets scroll position of a page or a scrollable element.
92
+ // Returns a `Promise` that resolves when it has finished setting the scroll position.
93
+ set(
94
+ // When setting page scroll position, `scrollableContainer` is `undefined`.
95
+ // When setting scrollable element scroll position, `scrollableContainer` is the scrollable element.
96
+ scrollableContainer: ScrollableContainer,
97
+ // When setting page scroll position, it could be either an anchor or numeric coordinates.
98
+ // When setting scrollable element scroll position, it could only be numeric coordinates.
99
+ scrollPositionOrAnchor: Anchor | [number, number],
100
+ // `scrollPosition` provides the API for setting scroll position according to the environment.
101
+ // For example, `WebBrowserScrollPosition` provides the methods for setting scroll position in a web browser.
102
+ scrollPosition: EnvironmentScrollPosition<ScrollableContainer, Anchor>,
103
+ ): Promise<void>;
104
+
105
+ // Cancels any pending (or in-progress) setting of scroll position.
106
+ cancel(): void;
107
+ }