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.
- package/CHANGELOG.md +12 -0
- package/README.md +611 -163
- package/data-storage/package.json +6 -0
- package/karma.conf.cjs +21 -4
- package/lib/cjs/NavigationStack.js +88 -0
- package/lib/cjs/data-storage/DataStorage.js +71 -0
- package/lib/cjs/data-storage/LocationDataStorage.js +29 -0
- package/lib/cjs/data-storage/index.js +9 -0
- package/lib/cjs/debug.js +12 -0
- package/lib/cjs/environment/InMemoryEnvironment.js +15 -0
- package/lib/cjs/environment/WebBrowserEnvironment.js +15 -0
- package/lib/cjs/environment/data-storage/InMemoryDataStorage.js +27 -0
- package/lib/cjs/environment/data-storage/WebBrowserDataStorage.js +21 -0
- package/lib/cjs/environment/scroll-position/InMemoryScrollPosition.js +44 -0
- package/lib/cjs/environment/scroll-position/WebBrowserScrollPosition.js +60 -0
- package/lib/cjs/getLocationFromInternalLocation.js +14 -0
- package/lib/cjs/index.js +20 -16
- package/lib/cjs/navigationBlockers.js +28 -23
- package/lib/cjs/{normalizeInputLocation.js → parseInputLocation.js} +25 -9
- package/lib/cjs/{ActionTypes.js → redux/ActionTypes.js} +1 -1
- package/lib/cjs/redux/ActionTypesInternal.js +8 -0
- package/lib/cjs/{Actions.js → redux/Actions.js} +5 -4
- package/lib/cjs/redux/createMiddlewares.js +60 -0
- package/lib/cjs/redux/index.js +13 -0
- package/lib/cjs/redux/internalLocationReducer.js +14 -0
- package/lib/cjs/redux/middleware/createAddInputLocationBasePathMiddleware.js +32 -0
- package/lib/cjs/redux/middleware/createNonProgrammaticNavigationBlockerMiddleware.js +113 -0
- package/lib/cjs/redux/middleware/createProgrammaticNavigationBlockerMiddleware.js +94 -0
- package/lib/cjs/redux/middleware/createRemoveOutputLocationBasePathMiddleware.js +30 -0
- package/lib/cjs/redux/middleware/createUpdateInternalLocationMiddleware.js +73 -0
- package/lib/cjs/{middleware/navigationActionMiddleware.js → redux/middleware/navigationOperationMiddleware.js} +11 -8
- package/lib/cjs/{middleware/normalizeInputLocationMiddleware.js → redux/middleware/parseInputLocationMiddleware.js} +6 -4
- package/lib/cjs/redux/middleware/updateLocationMiddleware.js +34 -0
- package/lib/cjs/scroll-position/PageScrollPositionSetter.js +97 -0
- package/lib/cjs/scroll-position/ScrollPositionAutoSaver.js +141 -0
- package/lib/cjs/scroll-position/ScrollPositionRestoration.js +407 -0
- package/lib/cjs/scroll-position/ScrollPositionSaver.js +87 -0
- package/lib/cjs/scroll-position/ScrollPositionSetter.js +16 -0
- package/lib/cjs/scroll-position/constants.js +5 -0
- package/lib/cjs/scroll-position/index.js +7 -0
- package/lib/cjs/scroll-position/scheduleNextTick.js +11 -0
- package/lib/cjs/session/InMemorySession.js +22 -0
- package/lib/cjs/session/ServerSideRenderSession.js +17 -0
- package/lib/cjs/session/Session.js +202 -0
- package/lib/cjs/session/WebBrowserSession.js +20 -0
- package/lib/cjs/session/key/createSessionKey.js +23 -0
- package/lib/cjs/session/lifecycle/InMemorySessionLifecycle.js +19 -0
- package/lib/cjs/session/lifecycle/WebBrowserSessionLifecycle.js +128 -0
- package/lib/cjs/session/lifecycle/page-lifecycle/PageLifecycle.js +269 -0
- package/lib/cjs/session/lifecycle/page-lifecycle/PageLifecycleInstance.js +8 -0
- package/lib/cjs/session/lifecycle/page-lifecycle/supportsConstructableEventTarget.js +33 -0
- package/lib/cjs/session/navigation/InMemoryNavigation.js +104 -0
- package/lib/cjs/session/navigation/ServerSideNavigation.js +61 -0
- package/lib/cjs/session/navigation/WebBrowserNavigation.js +221 -0
- package/lib/cjs/session/navigation/error/NavigationOutOfBoundsError.js +12 -0
- package/lib/cjs/session/navigation/error/ServerSideNavigationError.js +21 -0
- package/lib/cjs/session/navigation/operation/operations.js +11 -0
- package/lib/cjs/session/subscription/Subscription.js +81 -0
- package/lib/data-storage/index.d.ts +35 -0
- package/lib/esm/NavigationStack.js +81 -0
- package/lib/esm/data-storage/DataStorage.js +65 -0
- package/lib/esm/data-storage/LocationDataStorage.js +22 -0
- package/lib/esm/data-storage/index.js +2 -0
- package/lib/esm/debug.js +7 -0
- package/lib/esm/environment/InMemoryEnvironment.js +8 -0
- package/lib/esm/environment/WebBrowserEnvironment.js +8 -0
- package/lib/esm/environment/data-storage/InMemoryDataStorage.js +21 -0
- package/lib/esm/environment/data-storage/WebBrowserDataStorage.js +15 -0
- package/lib/esm/environment/scroll-position/InMemoryScrollPosition.js +38 -0
- package/lib/esm/environment/scroll-position/WebBrowserScrollPosition.js +54 -0
- package/lib/esm/getLocationFromInternalLocation.js +9 -0
- package/lib/esm/index.js +10 -8
- package/lib/esm/navigationBlockers.js +28 -23
- package/lib/esm/{normalizeInputLocation.js → parseInputLocation.js} +24 -8
- package/lib/esm/{ActionTypes.js → redux/ActionTypes.js} +1 -1
- package/lib/esm/redux/ActionTypesInternal.js +3 -0
- package/lib/esm/{Actions.js → redux/Actions.js} +5 -4
- package/lib/esm/redux/createMiddlewares.js +54 -0
- package/lib/esm/redux/index.js +4 -0
- package/lib/esm/redux/internalLocationReducer.js +8 -0
- package/lib/esm/redux/middleware/createAddInputLocationBasePathMiddleware.js +27 -0
- package/lib/esm/redux/middleware/createNonProgrammaticNavigationBlockerMiddleware.js +108 -0
- package/lib/esm/redux/middleware/createProgrammaticNavigationBlockerMiddleware.js +88 -0
- package/lib/esm/redux/middleware/createRemoveOutputLocationBasePathMiddleware.js +25 -0
- package/lib/esm/redux/middleware/createUpdateInternalLocationMiddleware.js +68 -0
- package/lib/esm/{middleware/navigationActionMiddleware.js → redux/middleware/navigationOperationMiddleware.js} +10 -7
- package/lib/esm/{middleware/normalizeInputLocationMiddleware.js → redux/middleware/parseInputLocationMiddleware.js} +5 -3
- package/lib/esm/redux/middleware/updateLocationMiddleware.js +28 -0
- package/lib/esm/scroll-position/PageScrollPositionSetter.js +91 -0
- package/lib/esm/scroll-position/ScrollPositionAutoSaver.js +134 -0
- package/lib/esm/scroll-position/ScrollPositionRestoration.js +400 -0
- package/lib/esm/scroll-position/ScrollPositionSaver.js +80 -0
- package/lib/esm/scroll-position/ScrollPositionSetter.js +10 -0
- package/lib/esm/scroll-position/constants.js +1 -0
- package/lib/esm/scroll-position/index.js +1 -0
- package/lib/esm/scroll-position/scheduleNextTick.js +6 -0
- package/lib/esm/session/InMemorySession.js +15 -0
- package/lib/esm/session/ServerSideRenderSession.js +11 -0
- package/lib/esm/session/Session.js +195 -0
- package/lib/esm/session/WebBrowserSession.js +13 -0
- package/lib/esm/session/key/createSessionKey.js +18 -0
- package/lib/esm/session/lifecycle/InMemorySessionLifecycle.js +13 -0
- package/lib/esm/session/lifecycle/WebBrowserSessionLifecycle.js +120 -0
- package/lib/esm/session/lifecycle/page-lifecycle/PageLifecycle.js +263 -0
- package/lib/esm/session/lifecycle/page-lifecycle/PageLifecycleInstance.js +2 -0
- package/lib/esm/session/lifecycle/page-lifecycle/supportsConstructableEventTarget.js +30 -0
- package/lib/esm/session/navigation/InMemoryNavigation.js +97 -0
- package/lib/esm/session/navigation/ServerSideNavigation.js +54 -0
- package/lib/esm/session/navigation/WebBrowserNavigation.js +213 -0
- package/lib/esm/session/navigation/error/NavigationOutOfBoundsError.js +6 -0
- package/lib/esm/session/navigation/error/ServerSideNavigationError.js +14 -0
- package/lib/esm/session/navigation/operation/operations.js +6 -0
- package/lib/esm/session/subscription/Subscription.js +75 -0
- package/lib/index.d.ts +179 -157
- package/lib/redux/index.d.ts +90 -0
- package/lib/scroll-position/index.d.ts +107 -0
- package/package.json +9 -5
- package/redux/package.json +6 -0
- package/scroll-position/package.json +6 -0
- package/src/NavigationStack.js +100 -0
- package/src/data-storage/DataStorage.js +69 -0
- package/src/data-storage/LocationDataStorage.js +23 -0
- package/src/data-storage/index.js +2 -0
- package/src/debug.js +8 -0
- package/src/environment/InMemoryEnvironment.js +9 -0
- package/src/environment/WebBrowserEnvironment.js +9 -0
- package/src/environment/data-storage/InMemoryDataStorage.js +23 -0
- package/src/environment/data-storage/WebBrowserDataStorage.js +17 -0
- package/src/environment/scroll-position/InMemoryScrollPosition.js +45 -0
- package/src/environment/scroll-position/WebBrowserScrollPosition.js +72 -0
- package/src/getLocationFromInternalLocation.js +7 -0
- package/src/index.js +10 -8
- package/src/navigationBlockers.js +31 -27
- package/src/{normalizeInputLocation.js → parseInputLocation.js} +23 -8
- package/src/{ActionTypes.js → redux/ActionTypes.js} +1 -1
- package/src/redux/ActionTypesInternal.js +3 -0
- package/src/{Actions.js → redux/Actions.js} +4 -3
- package/src/redux/createMiddlewares.js +65 -0
- package/src/redux/index.js +4 -0
- package/src/redux/internalLocationReducer.js +9 -0
- package/src/redux/middleware/createAddInputLocationBasePathMiddleware.js +27 -0
- package/src/redux/middleware/createNonProgrammaticNavigationBlockerMiddleware.js +119 -0
- package/src/redux/middleware/createProgrammaticNavigationBlockerMiddleware.js +94 -0
- package/src/redux/middleware/createRemoveOutputLocationBasePathMiddleware.js +26 -0
- package/src/redux/middleware/createUpdateInternalLocationMiddleware.js +72 -0
- package/src/{middleware/navigationActionMiddleware.js → redux/middleware/navigationOperationMiddleware.js} +10 -3
- package/src/{middleware/normalizeInputLocationMiddleware.js → redux/middleware/parseInputLocationMiddleware.js} +5 -3
- package/src/redux/middleware/updateLocationMiddleware.js +28 -0
- package/src/scroll-position/PageScrollPositionSetter.js +110 -0
- package/src/scroll-position/ScrollPositionAutoSaver.js +168 -0
- package/src/scroll-position/ScrollPositionRestoration.js +551 -0
- package/src/scroll-position/ScrollPositionSaver.js +120 -0
- package/src/scroll-position/ScrollPositionSetter.js +16 -0
- package/src/scroll-position/constants.js +1 -0
- package/src/scroll-position/index.js +1 -0
- package/src/scroll-position/scheduleNextTick.js +6 -0
- package/src/session/InMemorySession.js +13 -0
- package/src/session/ServerSideRenderSession.js +9 -0
- package/src/session/Session.js +238 -0
- package/src/session/WebBrowserSession.js +13 -0
- package/src/session/key/createSessionKey.js +18 -0
- package/src/session/lifecycle/InMemorySessionLifecycle.js +13 -0
- package/src/session/lifecycle/WebBrowserSessionLifecycle.js +126 -0
- package/src/session/lifecycle/page-lifecycle/PageLifecycle.js +291 -0
- package/src/session/lifecycle/page-lifecycle/PageLifecycleInstance.js +3 -0
- package/src/session/lifecycle/page-lifecycle/supportsConstructableEventTarget.js +32 -0
- package/src/session/navigation/InMemoryNavigation.js +78 -0
- package/src/session/navigation/ServerSideNavigation.js +43 -0
- package/src/session/navigation/WebBrowserNavigation.js +224 -0
- package/src/session/navigation/error/NavigationOutOfBoundsError.js +7 -0
- package/src/session/navigation/error/ServerSideNavigationError.js +18 -0
- package/src/session/navigation/operation/operations.js +6 -0
- package/src/session/subscription/Subscription.js +76 -0
- package/test/NavigationStack.test.js +296 -0
- package/test/{LocationDataStorage.test.js → data-storage/LocationDataStorage.test.js} +3 -3
- package/test/data-storage/index.test.js +8 -0
- package/test/index.js +12 -0
- package/test/index.test.js +8 -7
- package/test/{helpers.js → middlewareTestUtil.js} +9 -12
- package/test/{normalizeInputLocation.test.js → parseInputLocationMiddleware.test.js} +9 -9
- package/test/{Action.test.js → redux/Action.test.js} +7 -6
- package/test/{ActionTypes.test.js → redux/ActionTypes.test.js} +2 -2
- package/test/redux/createMiddlewares.test.js +96 -0
- package/test/redux/index.test.js +10 -0
- package/test/{locationReducer.test.js → redux/locationReducer.test.js} +4 -7
- package/test/redux/middleware/createAddInputLocationBasePathMiddleware.test.js +40 -0
- package/test/redux/middleware/createNonProgrammaticNavigationBlockerMiddleware.test.js +264 -0
- package/test/redux/middleware/createProgrammaticNavigationBlockerMiddleware.test.js +312 -0
- package/test/redux/middleware/createRemoveOutputLocationBasePathMiddleware.test.js +51 -0
- package/test/{middleware/navigationActionMiddleware.test.js → redux/middleware/navigationOperationMiddleware.test.js} +16 -12
- package/test/{middleware/normalizeInputLocationMiddleware.test.js → redux/middleware/parseInputLocationMiddleware.test.js} +4 -4
- package/test/scroll-position/ScrollPositionRestoration.test.js +435 -0
- package/test/scroll-position/addScrollableContainer.js +39 -0
- package/test/scroll-position/addScrollableContainerWithAnchors.js +56 -0
- package/test/scroll-position/createApp.js +132 -0
- package/test/scroll-position/delay.js +9 -0
- package/test/scroll-position/mockPageLifecycle.js +17 -0
- package/test/scroll-position/runApp.js +24 -0
- package/test/scroll-position/withScrollableContainerAtIndexPageWithDisabledAutomaticScrollPositionRestoration.js +72 -0
- package/test/session/InMemorySession.test.js +348 -0
- package/test/session/ServerSession.test.js +17 -9
- package/test/session/WebBrowserSession.test.js +265 -0
- package/test/testUtil.js +3 -0
- package/types/data-storage/index.d.ts +35 -0
- package/types/index.d.ts +179 -157
- package/types/redux/index.d.ts +90 -0
- package/types/scroll-position/index.d.ts +107 -0
- package/types/tsconfig.json +1 -1
- package/lib/cjs/LocationDataStorage.js +0 -61
- package/lib/cjs/addBeforeLocationChangeListener.js +0 -7
- package/lib/cjs/beforeLocationChangeListeners.js +0 -51
- package/lib/cjs/createMiddlewares.js +0 -47
- package/lib/cjs/middleware/createBasePathMiddleware.js +0 -24
- package/lib/cjs/middleware/createBeforeLocationChangeListenerMiddleware.js +0 -39
- package/lib/cjs/middleware/createLocationMiddleware.js +0 -56
- package/lib/cjs/middleware/createNavigationBlockerMiddleware.js +0 -161
- package/lib/cjs/middleware/createTransformLocationMiddleware.js +0 -38
- package/lib/cjs/onlyAllowedOnClientSide.js +0 -10
- package/lib/cjs/session/BrowserSession.js +0 -235
- package/lib/cjs/session/MemorySession.js +0 -223
- package/lib/cjs/session/ServerSession.js +0 -65
- package/lib/esm/LocationDataStorage.js +0 -54
- package/lib/esm/addBeforeLocationChangeListener.js +0 -2
- package/lib/esm/beforeLocationChangeListeners.js +0 -44
- package/lib/esm/createMiddlewares.js +0 -41
- package/lib/esm/middleware/createBasePathMiddleware.js +0 -19
- package/lib/esm/middleware/createBeforeLocationChangeListenerMiddleware.js +0 -34
- package/lib/esm/middleware/createLocationMiddleware.js +0 -50
- package/lib/esm/middleware/createNavigationBlockerMiddleware.js +0 -156
- package/lib/esm/middleware/createTransformLocationMiddleware.js +0 -33
- package/lib/esm/onlyAllowedOnClientSide.js +0 -5
- package/lib/esm/session/BrowserSession.js +0 -229
- package/lib/esm/session/MemorySession.js +0 -217
- package/lib/esm/session/ServerSession.js +0 -58
- package/src/LocationDataStorage.js +0 -60
- package/src/addBeforeLocationChangeListener.js +0 -2
- package/src/beforeLocationChangeListeners.js +0 -54
- package/src/createMiddlewares.js +0 -45
- package/src/middleware/createBasePathMiddleware.js +0 -20
- package/src/middleware/createBeforeLocationChangeListenerMiddleware.js +0 -40
- package/src/middleware/createLocationMiddleware.js +0 -55
- package/src/middleware/createNavigationBlockerMiddleware.js +0 -168
- package/src/middleware/createTransformLocationMiddleware.js +0 -29
- package/src/onlyAllowedOnClientSide.js +0 -5
- package/src/session/BrowserSession.js +0 -235
- package/src/session/MemorySession.js +0 -219
- package/src/session/ServerSession.js +0 -67
- package/test/createMiddlewares.test.js +0 -62
- package/test/middleware/createBasePathMiddleware.test.js +0 -67
- package/test/middleware/createBeforeLocationChangeListenerMiddleware.test.js +0 -141
- package/test/middleware/createNavigationBlockerMiddleware.test.js +0 -471
- package/test/middleware/createTransformLocationMiddleware.test.js +0 -44
- package/test/session/BrowserSession.test.js +0 -182
- package/test/session/MemorySession.test.js +0 -244
- /package/lib/cjs/{locationReducer.js → redux/locationReducer.js} +0 -0
- /package/lib/esm/{locationReducer.js → redux/locationReducer.js} +0 -0
- /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,60 +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}|`;
|
|
8
|
-
// this._stateKeyPrefix = namespace ? `${namespace}|` : '';
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
get(location, key) {
|
|
12
|
-
const stateKey = this._getStateKey(location, key);
|
|
13
|
-
|
|
14
|
-
try {
|
|
15
|
-
const value = this._environment.dataStorage.get(stateKey);
|
|
16
|
-
// === null is probably sufficient.
|
|
17
|
-
if (value === null) {
|
|
18
|
-
return undefined;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
// We want to catch JSON parse errors in case someone separately threw
|
|
22
|
-
// junk into sessionStorage under our namespace.
|
|
23
|
-
return JSON.parse(value);
|
|
24
|
-
} catch (error) {
|
|
25
|
-
// Pretend that the entry doesn't exist.
|
|
26
|
-
return undefined;
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
set(location, key, value) {
|
|
31
|
-
const stateKey = this._getStateKey(location, key);
|
|
32
|
-
|
|
33
|
-
if (value === undefined) {
|
|
34
|
-
try {
|
|
35
|
-
this._environment.dataStorage.remove(stateKey);
|
|
36
|
-
} catch (error) {
|
|
37
|
-
// No need to handle errors here.
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
return;
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
// Unlike with read, we want to fail on invalid values here, since the
|
|
44
|
-
// value here is provided by the caller of this method.
|
|
45
|
-
const valueString = JSON.stringify(value);
|
|
46
|
-
|
|
47
|
-
try {
|
|
48
|
-
this._environment.dataStorage.set(stateKey, valueString);
|
|
49
|
-
} catch (error) {
|
|
50
|
-
// No need to handle errors here either. If it didn't work, it didn't
|
|
51
|
-
// work. We make no guarantees about actually saving the value.
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
_getStateKey(location, key) {
|
|
56
|
-
const locationKey = location.key || this._getFallbackLocationKey(location);
|
|
57
|
-
const keyPrefix = `${this._stateKeyPrefix}${locationKey}`;
|
|
58
|
-
return `${keyPrefix}|${key}`;
|
|
59
|
-
}
|
|
60
|
-
}
|
|
@@ -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
|
-
}
|
package/src/createMiddlewares.js
DELETED
|
@@ -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
|
-
}
|