@equinor/fusion-framework-module-navigation 7.0.4 → 7.0.6
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 +16 -0
- package/dist/esm/NavigateEvent.js +24 -0
- package/dist/esm/NavigateEvent.js.map +1 -0
- package/dist/esm/NavigatedEvent.js +20 -0
- package/dist/esm/NavigatedEvent.js.map +1 -0
- package/dist/esm/NavigationConfigurator.js +13 -1
- package/dist/esm/NavigationConfigurator.js.map +1 -1
- package/dist/esm/NavigationProvider.js +8 -2
- package/dist/esm/NavigationProvider.js.map +1 -1
- package/dist/esm/enable-navigation.js +1 -0
- package/dist/esm/enable-navigation.js.map +1 -1
- package/dist/esm/index.js +2 -1
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/lib/BaseHistory.js +9 -0
- package/dist/esm/lib/BaseHistory.js.map +1 -1
- package/dist/esm/lib/BrowserHistory.js +1 -0
- package/dist/esm/lib/BrowserHistory.js.map +1 -1
- package/dist/esm/lib/BrowserHistoryHashStack.js +1 -0
- package/dist/esm/lib/BrowserHistoryHashStack.js.map +1 -1
- package/dist/esm/lib/BrowserHistoryStack.js +15 -0
- package/dist/esm/lib/BrowserHistoryStack.js.map +1 -1
- package/dist/esm/lib/MemoryHistory.js +4 -1
- package/dist/esm/lib/MemoryHistory.js.map +1 -1
- package/dist/esm/lib/{MemoryStack.js → MemoryHistoryStack.js} +12 -1
- package/dist/esm/lib/MemoryHistoryStack.js.map +1 -0
- package/dist/esm/lib/ProxyHistory.js +1 -0
- package/dist/esm/lib/ProxyHistory.js.map +1 -1
- package/dist/esm/lib/create-history.js +1 -0
- package/dist/esm/lib/create-history.js.map +1 -1
- package/dist/esm/lib/index.js +1 -1
- package/dist/esm/lib/index.js.map +1 -1
- package/dist/esm/lib/state/{history.actions.js → actions.js} +1 -1
- package/dist/esm/lib/state/actions.js.map +1 -0
- package/dist/esm/lib/state/check-blockers.js +36 -0
- package/dist/esm/lib/state/check-blockers.js.map +1 -0
- package/dist/esm/lib/state/create-flow.js +27 -0
- package/dist/esm/lib/state/create-flow.js.map +1 -0
- package/dist/esm/lib/state/{history.reducer.js → create-history-reducer.js} +5 -2
- package/dist/esm/lib/state/create-history-reducer.js.map +1 -0
- package/dist/esm/lib/state/create-store.js +26 -0
- package/dist/esm/lib/state/create-store.js.map +1 -0
- package/dist/esm/lib/state/flow-creators.js +7 -0
- package/dist/esm/lib/state/flow-creators.js.map +1 -0
- package/dist/esm/lib/state/go.js +12 -0
- package/dist/esm/lib/state/go.js.map +1 -0
- package/dist/esm/lib/state/history.state.js +1 -39
- package/dist/esm/lib/state/history.state.js.map +1 -1
- package/dist/esm/lib/state/index.js +3 -3
- package/dist/esm/lib/state/index.js.map +1 -1
- package/dist/esm/lib/state/navigate.js +43 -0
- package/dist/esm/lib/state/navigate.js.map +1 -0
- package/dist/esm/lib/state/pop.js +14 -0
- package/dist/esm/lib/state/pop.js.map +1 -0
- package/dist/esm/lib/state/validate-current-location.js +26 -0
- package/dist/esm/lib/state/validate-current-location.js.map +1 -0
- package/dist/esm/lib/utils/index.js +2 -2
- package/dist/esm/lib/utils/index.js.map +1 -1
- package/dist/esm/lib/utils/resolve-browser-location.js +1 -52
- package/dist/esm/lib/utils/resolve-browser-location.js.map +1 -1
- package/dist/esm/lib/utils/resolve-hash-location.js +20 -0
- package/dist/esm/lib/utils/resolve-hash-location.js.map +1 -0
- package/dist/esm/lib/utils/resolve-window-location.js +18 -0
- package/dist/esm/lib/utils/resolve-window-location.js.map +1 -0
- package/dist/esm/version.js +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/types/NavigateEvent.d.ts +36 -0
- package/dist/types/NavigatedEvent.d.ts +28 -0
- package/dist/types/NavigationConfigurator.d.ts +3 -0
- package/dist/types/NavigationProvider.d.ts +3 -0
- package/dist/types/index.d.ts +2 -1
- package/dist/types/lib/BaseHistory.d.ts +8 -0
- package/dist/types/lib/BrowserHistoryHashStack.d.ts +1 -0
- package/dist/types/lib/BrowserHistoryStack.d.ts +14 -0
- package/dist/types/lib/MemoryHistory.d.ts +3 -0
- package/dist/types/lib/{MemoryStack.d.ts → MemoryHistoryStack.d.ts} +6 -0
- package/dist/types/lib/ProxyHistory.d.ts +1 -1
- package/dist/types/lib/index.d.ts +1 -1
- package/dist/types/lib/state/check-blockers.d.ts +3 -0
- package/dist/types/lib/state/create-flow.d.ts +10 -0
- package/dist/types/lib/state/{history.reducer.d.ts → create-history-reducer.d.ts} +32 -32
- package/dist/types/lib/state/create-store.d.ts +17 -0
- package/dist/types/lib/state/flow-creators.d.ts +7 -0
- package/dist/types/lib/state/go.d.ts +3 -0
- package/dist/types/lib/state/history.state.d.ts +2 -23
- package/dist/types/lib/state/index.d.ts +4 -3
- package/dist/types/lib/state/navigate.d.ts +9 -0
- package/dist/types/lib/state/pop.d.ts +3 -0
- package/dist/types/lib/state/validate-current-location.d.ts +3 -0
- package/dist/types/lib/types.d.ts +1 -1
- package/dist/types/lib/utils/index.d.ts +2 -2
- package/dist/types/lib/utils/resolve-browser-location.d.ts +1 -42
- package/dist/types/lib/utils/resolve-hash-location.d.ts +10 -0
- package/dist/types/lib/utils/resolve-window-location.d.ts +10 -0
- package/dist/types/version.d.ts +1 -1
- package/package.json +9 -9
- package/src/NavigateEvent.ts +47 -0
- package/src/NavigatedEvent.ts +35 -0
- package/src/NavigationConfigurator.ts +13 -3
- package/src/NavigationProvider.interface.ts +1 -1
- package/src/NavigationProvider.ts +8 -2
- package/src/enable-navigation.ts +1 -0
- package/src/index.ts +2 -6
- package/src/lib/BaseHistory.ts +9 -0
- package/src/lib/BrowserHistory.ts +1 -0
- package/src/lib/BrowserHistoryHashStack.ts +1 -0
- package/src/lib/BrowserHistoryStack.ts +15 -0
- package/src/lib/MemoryHistory.ts +4 -1
- package/src/lib/{MemoryStack.ts → MemoryHistoryStack.ts} +11 -0
- package/src/lib/ProxyHistory.ts +2 -1
- package/src/lib/create-history.ts +1 -0
- package/src/lib/index.ts +1 -1
- package/src/lib/state/check-blockers.ts +52 -0
- package/src/lib/state/create-flow.ts +36 -0
- package/src/lib/state/{history.reducer.ts → create-history-reducer.ts} +4 -1
- package/src/lib/state/create-store.ts +39 -0
- package/src/lib/state/flow-creators.ts +7 -0
- package/src/lib/state/go.ts +20 -0
- package/src/lib/state/history.state.ts +2 -56
- package/src/lib/state/index.ts +4 -3
- package/src/lib/state/navigate.ts +58 -0
- package/src/lib/state/pop.ts +24 -0
- package/src/lib/state/validate-current-location.ts +33 -0
- package/src/lib/types.ts +1 -1
- package/src/lib/utils/index.ts +2 -2
- package/src/lib/utils/resolve-browser-location.ts +1 -60
- package/src/lib/utils/resolve-hash-location.ts +26 -0
- package/src/lib/utils/resolve-window-location.ts +24 -0
- package/src/version.ts +1 -1
- package/dist/esm/events.js +0 -40
- package/dist/esm/events.js.map +0 -1
- package/dist/esm/lib/MemoryStack.js.map +0 -1
- package/dist/esm/lib/state/history.actions.js.map +0 -1
- package/dist/esm/lib/state/history.flows.js +0 -207
- package/dist/esm/lib/state/history.flows.js.map +0 -1
- package/dist/esm/lib/state/history.reducer.js.map +0 -1
- package/dist/types/events.d.ts +0 -65
- package/dist/types/lib/state/history.flows.d.ts +0 -97
- package/src/events.ts +0 -82
- package/src/lib/state/history.flows.ts +0 -274
- /package/dist/types/lib/state/{history.actions.d.ts → actions.d.ts} +0 -0
- /package/src/lib/state/{history.actions.ts → actions.ts} +0 -0
package/dist/types/events.d.ts
DELETED
|
@@ -1,65 +0,0 @@
|
|
|
1
|
-
import { FrameworkEvent, type FrameworkEventInit } from '@equinor/fusion-framework-module-event';
|
|
2
|
-
import type { INavigationProvider } from './NavigationProvider.interface';
|
|
3
|
-
import type { Action, NavigationUpdate, Path } from './lib/types';
|
|
4
|
-
/**
|
|
5
|
-
* Event detail for navigation events (before navigation).
|
|
6
|
-
*/
|
|
7
|
-
export interface NavigateEventDetail {
|
|
8
|
-
/** The target path or location to navigate to */
|
|
9
|
-
location: NavigationUpdate;
|
|
10
|
-
/** Optional current location before navigation */
|
|
11
|
-
currentLocation?: Path;
|
|
12
|
-
}
|
|
13
|
-
/**
|
|
14
|
-
* Event emitted before navigation occurs.
|
|
15
|
-
* Can be canceled by calling `preventDefault()` to block the navigation.
|
|
16
|
-
*
|
|
17
|
-
* @example
|
|
18
|
-
* ```ts
|
|
19
|
-
* eventProvider.addEventListener('onNavigate', (event) => {
|
|
20
|
-
* if (hasUnsavedChanges) {
|
|
21
|
-
* event.preventDefault();
|
|
22
|
-
* }
|
|
23
|
-
* });
|
|
24
|
-
* ```
|
|
25
|
-
*/
|
|
26
|
-
export declare class NavigateEvent extends FrameworkEvent<FrameworkEventInit<NavigateEventDetail, INavigationProvider>> {
|
|
27
|
-
constructor(detail: NavigateEventDetail, source: INavigationProvider);
|
|
28
|
-
}
|
|
29
|
-
/**
|
|
30
|
-
* Event detail for navigated events (after navigation).
|
|
31
|
-
*/
|
|
32
|
-
export interface NavigatedEventDetail {
|
|
33
|
-
/** The navigation action that occurred (PUSH, POP, REPLACE) */
|
|
34
|
-
action: Action;
|
|
35
|
-
/** The new location after navigation */
|
|
36
|
-
current: NavigationUpdate;
|
|
37
|
-
/** The previous location before navigation */
|
|
38
|
-
previous: NavigationUpdate;
|
|
39
|
-
}
|
|
40
|
-
/**
|
|
41
|
-
* Event emitted after navigation completes.
|
|
42
|
-
* Contains the navigation action type and both current and previous locations.
|
|
43
|
-
*
|
|
44
|
-
* @example
|
|
45
|
-
* ```ts
|
|
46
|
-
* eventProvider.addEventListener('onNavigated', (event) => {
|
|
47
|
-
* const { action, current, previous } = event.detail;
|
|
48
|
-
* console.log(`${action}: ${previous.location.pathname} → ${current.location.pathname}`);
|
|
49
|
-
* });
|
|
50
|
-
* ```
|
|
51
|
-
*/
|
|
52
|
-
export declare class NavigatedEvent extends FrameworkEvent<FrameworkEventInit<NavigatedEventDetail, INavigationProvider>> {
|
|
53
|
-
constructor(detail: NavigatedEventDetail, source: INavigationProvider);
|
|
54
|
-
}
|
|
55
|
-
/**
|
|
56
|
-
* Declares navigation events in the FrameworkEventMap for type safety.
|
|
57
|
-
*/
|
|
58
|
-
declare module '@equinor/fusion-framework-module-event' {
|
|
59
|
-
interface FrameworkEventMap {
|
|
60
|
-
/** Event emitted before navigation occurs, can be canceled to prevent navigation */
|
|
61
|
-
onNavigate: NavigateEvent;
|
|
62
|
-
/** Event emitted after navigation occurs */
|
|
63
|
-
onNavigated: NavigatedEvent;
|
|
64
|
-
}
|
|
65
|
-
}
|
|
@@ -1,97 +0,0 @@
|
|
|
1
|
-
import type { Flow } from '@equinor/fusion-observable';
|
|
2
|
-
import { type Actions } from './history.actions';
|
|
3
|
-
import { type LocationState, type HistoryStack } from '../types';
|
|
4
|
-
/**
|
|
5
|
-
* History flow for processing navigation actions.
|
|
6
|
-
*/
|
|
7
|
-
export type HistoryFlow = Flow<Actions, LocationState>;
|
|
8
|
-
/**
|
|
9
|
-
* Factory function that creates a history flow from a stack.
|
|
10
|
-
*/
|
|
11
|
-
export type HistoryFlowCreator = (stack: HistoryStack) => HistoryFlow;
|
|
12
|
-
/**
|
|
13
|
-
* Flow creator for handling navigate actions.
|
|
14
|
-
*
|
|
15
|
-
* Execution flow:
|
|
16
|
-
* 1. Filters for navigate actions
|
|
17
|
-
* 2. Resolves the target path from the action payload
|
|
18
|
-
* 3. Creates a location object with the resolved path, key (from action meta), and optional state
|
|
19
|
-
* 4. Checks if the new location is the same as current to prevent navigation loops
|
|
20
|
-
* 5. Updates the stack based on replace option (replace updates current, otherwise pushes new entry)
|
|
21
|
-
* 6. Returns success action with the new location and appropriate action type (PUSH/REPLACE)
|
|
22
|
-
*/
|
|
23
|
-
export declare const navigate: HistoryFlowCreator;
|
|
24
|
-
/**
|
|
25
|
-
* Flow creator for handling go (back/forward) actions.
|
|
26
|
-
*
|
|
27
|
-
* Execution flow:
|
|
28
|
-
* 1. Filters for go actions
|
|
29
|
-
* 2. Combines with current state to get history entries
|
|
30
|
-
* 3. Moves the stack index by the specified delta (negative for back, positive for forward)
|
|
31
|
-
* 4. Returns success action with POP action type and the new current location
|
|
32
|
-
*/
|
|
33
|
-
export declare const go: HistoryFlowCreator;
|
|
34
|
-
/**
|
|
35
|
-
* Flow creator for handling pop (browser back/forward) actions.
|
|
36
|
-
*
|
|
37
|
-
* Execution flow:
|
|
38
|
-
* 1. Filters for pop actions
|
|
39
|
-
* 2. Uses the location from the action payload or falls back to stack.current
|
|
40
|
-
* 3. Emits a success action with POP action type and the current location
|
|
41
|
-
* 4. Completes the observable immediately (synchronous operation)
|
|
42
|
-
*/
|
|
43
|
-
export declare const pop: HistoryFlowCreator;
|
|
44
|
-
/**
|
|
45
|
-
* Flow creator for checking navigation blockers before processing actions.
|
|
46
|
-
*
|
|
47
|
-
* Execution flow:
|
|
48
|
-
* 1. Filters for navigation actions (navigate, go, pop, validateLocation)
|
|
49
|
-
* 2. Determines the action type (PUSH/REPLACE for navigate, POP for others)
|
|
50
|
-
* 3. Combines with current state to check for active blockers
|
|
51
|
-
* 4. If no blockers exist, passes the action through immediately
|
|
52
|
-
* 5. If blockers exist:
|
|
53
|
-
* - Creates a transition object with action, location, and retry callback
|
|
54
|
-
* - Calls each blocker asynchronously (wrapped in Promise to prevent blocking)
|
|
55
|
-
* - Blockers can call retry() to allow navigation or prevent it by not calling retry()
|
|
56
|
-
* - Waits for all blockers to complete before continuing
|
|
57
|
-
* 6. Returns the original action if navigation is allowed, or completes without emitting if blocked
|
|
58
|
-
*/
|
|
59
|
-
export declare const checkBlockers: HistoryFlowCreator;
|
|
60
|
-
/**
|
|
61
|
-
* Flow creator for validating the current location against history state.
|
|
62
|
-
*
|
|
63
|
-
* Execution flow:
|
|
64
|
-
* 1. Filters for validateLocation actions
|
|
65
|
-
* 2. Combines with current state to access history entries
|
|
66
|
-
* 3. Gets the current location from the stack
|
|
67
|
-
* 4. Searches history for an entry matching the current location's key
|
|
68
|
-
* 5. Validates that:
|
|
69
|
-
* - The location key exists in history (returns failure if not found)
|
|
70
|
-
* - The location state matches the history entry state (returns failure if mismatch)
|
|
71
|
-
* 6. Returns success action with current location if validation passes, failure otherwise
|
|
72
|
-
*/
|
|
73
|
-
export declare const validateCurrentLocation: HistoryFlowCreator;
|
|
74
|
-
/**
|
|
75
|
-
* Creates a combined history flow from multiple flow creators.
|
|
76
|
-
*
|
|
77
|
-
* Merges all provided flows so they process actions in parallel.
|
|
78
|
-
* Optionally prepends a blocker-checking step that gates navigation
|
|
79
|
-
* actions through registered {@link NavigationBlocker | blockers}.
|
|
80
|
-
*
|
|
81
|
-
* @param flowCreators - Array of flow creators to combine
|
|
82
|
-
* @param options - Optional configuration
|
|
83
|
-
* @param options.skipBlockCheck - If `true`, skip blocker checking
|
|
84
|
-
* @returns A combined {@link HistoryFlowCreator} that merges all provided flows
|
|
85
|
-
*/
|
|
86
|
-
export declare const createFlow: (flowCreators: HistoryFlowCreator[], options?: {
|
|
87
|
-
skipBlockCheck?: boolean;
|
|
88
|
-
}) => HistoryFlowCreator;
|
|
89
|
-
/**
|
|
90
|
-
* Collection of flow creators for history state management.
|
|
91
|
-
*/
|
|
92
|
-
export declare const flowCreators: {
|
|
93
|
-
navigate: HistoryFlowCreator;
|
|
94
|
-
go: HistoryFlowCreator;
|
|
95
|
-
pop: HistoryFlowCreator;
|
|
96
|
-
validateCurrentLocation: HistoryFlowCreator;
|
|
97
|
-
};
|
package/src/events.ts
DELETED
|
@@ -1,82 +0,0 @@
|
|
|
1
|
-
import { FrameworkEvent, type FrameworkEventInit } from '@equinor/fusion-framework-module-event';
|
|
2
|
-
|
|
3
|
-
import type { INavigationProvider } from './NavigationProvider.interface';
|
|
4
|
-
import type { Action, NavigationUpdate, Path } from './lib/types';
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* Event detail for navigation events (before navigation).
|
|
8
|
-
*/
|
|
9
|
-
export interface NavigateEventDetail {
|
|
10
|
-
/** The target path or location to navigate to */
|
|
11
|
-
location: NavigationUpdate;
|
|
12
|
-
/** Optional current location before navigation */
|
|
13
|
-
currentLocation?: Path;
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
/**
|
|
17
|
-
* Event emitted before navigation occurs.
|
|
18
|
-
* Can be canceled by calling `preventDefault()` to block the navigation.
|
|
19
|
-
*
|
|
20
|
-
* @example
|
|
21
|
-
* ```ts
|
|
22
|
-
* eventProvider.addEventListener('onNavigate', (event) => {
|
|
23
|
-
* if (hasUnsavedChanges) {
|
|
24
|
-
* event.preventDefault();
|
|
25
|
-
* }
|
|
26
|
-
* });
|
|
27
|
-
* ```
|
|
28
|
-
*/
|
|
29
|
-
export class NavigateEvent extends FrameworkEvent<
|
|
30
|
-
FrameworkEventInit<NavigateEventDetail, INavigationProvider>
|
|
31
|
-
> {
|
|
32
|
-
constructor(detail: NavigateEventDetail, source: INavigationProvider) {
|
|
33
|
-
super('onNavigate', {
|
|
34
|
-
detail,
|
|
35
|
-
source,
|
|
36
|
-
});
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
/**
|
|
41
|
-
* Event detail for navigated events (after navigation).
|
|
42
|
-
*/
|
|
43
|
-
export interface NavigatedEventDetail {
|
|
44
|
-
/** The navigation action that occurred (PUSH, POP, REPLACE) */
|
|
45
|
-
action: Action;
|
|
46
|
-
/** The new location after navigation */
|
|
47
|
-
current: NavigationUpdate;
|
|
48
|
-
/** The previous location before navigation */
|
|
49
|
-
previous: NavigationUpdate;
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
/**
|
|
53
|
-
* Event emitted after navigation completes.
|
|
54
|
-
* Contains the navigation action type and both current and previous locations.
|
|
55
|
-
*
|
|
56
|
-
* @example
|
|
57
|
-
* ```ts
|
|
58
|
-
* eventProvider.addEventListener('onNavigated', (event) => {
|
|
59
|
-
* const { action, current, previous } = event.detail;
|
|
60
|
-
* console.log(`${action}: ${previous.location.pathname} → ${current.location.pathname}`);
|
|
61
|
-
* });
|
|
62
|
-
* ```
|
|
63
|
-
*/
|
|
64
|
-
export class NavigatedEvent extends FrameworkEvent<
|
|
65
|
-
FrameworkEventInit<NavigatedEventDetail, INavigationProvider>
|
|
66
|
-
> {
|
|
67
|
-
constructor(detail: NavigatedEventDetail, source: INavigationProvider) {
|
|
68
|
-
super('onNavigated', { detail, source });
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
/**
|
|
73
|
-
* Declares navigation events in the FrameworkEventMap for type safety.
|
|
74
|
-
*/
|
|
75
|
-
declare module '@equinor/fusion-framework-module-event' {
|
|
76
|
-
interface FrameworkEventMap {
|
|
77
|
-
/** Event emitted before navigation occurs, can be canceled to prevent navigation */
|
|
78
|
-
onNavigate: NavigateEvent;
|
|
79
|
-
/** Event emitted after navigation occurs */
|
|
80
|
-
onNavigated: NavigatedEvent;
|
|
81
|
-
}
|
|
82
|
-
}
|
|
@@ -1,274 +0,0 @@
|
|
|
1
|
-
import { Observable, of, merge } from 'rxjs';
|
|
2
|
-
import { concatMap, map, withLatestFrom } from 'rxjs/operators';
|
|
3
|
-
import type { Flow } from '@equinor/fusion-observable';
|
|
4
|
-
import { filterAction } from '@equinor/fusion-observable/operators';
|
|
5
|
-
import { actions, type Actions } from './history.actions';
|
|
6
|
-
import { resolvePath } from '../utils';
|
|
7
|
-
import { type Location, type LocationState, type HistoryStack, Action } from '../types';
|
|
8
|
-
|
|
9
|
-
/**
|
|
10
|
-
* History flow for processing navigation actions.
|
|
11
|
-
*/
|
|
12
|
-
export type HistoryFlow = Flow<Actions, LocationState>;
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
* Factory function that creates a history flow from a stack.
|
|
16
|
-
*/
|
|
17
|
-
export type HistoryFlowCreator = (stack: HistoryStack) => HistoryFlow;
|
|
18
|
-
|
|
19
|
-
const compareLocation = (a: Location, b: Location) => {
|
|
20
|
-
return (
|
|
21
|
-
a.hash === b.hash &&
|
|
22
|
-
a.search === b.search &&
|
|
23
|
-
a.pathname === b.pathname &&
|
|
24
|
-
JSON.stringify(a.state) === JSON.stringify(b.state)
|
|
25
|
-
);
|
|
26
|
-
};
|
|
27
|
-
|
|
28
|
-
/**
|
|
29
|
-
* Flow creator for handling navigate actions.
|
|
30
|
-
*
|
|
31
|
-
* Execution flow:
|
|
32
|
-
* 1. Filters for navigate actions
|
|
33
|
-
* 2. Resolves the target path from the action payload
|
|
34
|
-
* 3. Creates a location object with the resolved path, key (from action meta), and optional state
|
|
35
|
-
* 4. Checks if the new location is the same as current to prevent navigation loops
|
|
36
|
-
* 5. Updates the stack based on replace option (replace updates current, otherwise pushes new entry)
|
|
37
|
-
* 6. Returns success action with the new location and appropriate action type (PUSH/REPLACE)
|
|
38
|
-
*/
|
|
39
|
-
export const navigate: HistoryFlowCreator =
|
|
40
|
-
(stack: HistoryStack): HistoryFlow =>
|
|
41
|
-
(action$: Observable<Actions>) => {
|
|
42
|
-
const { navigate } = actions;
|
|
43
|
-
return action$.pipe(
|
|
44
|
-
filterAction(navigate.type),
|
|
45
|
-
map((action) => {
|
|
46
|
-
const { payload, meta } = action;
|
|
47
|
-
const path = resolvePath(payload.to);
|
|
48
|
-
const key = meta.key;
|
|
49
|
-
const { replace, state } = payload.options;
|
|
50
|
-
const nextLocation = { ...path, key, state, unstable_mask: undefined } satisfies Location;
|
|
51
|
-
// prevent death loop navigation
|
|
52
|
-
if (compareLocation(nextLocation, stack.current)) {
|
|
53
|
-
return actions.abortNavigate('Location is the same as the current location');
|
|
54
|
-
}
|
|
55
|
-
if (replace) {
|
|
56
|
-
stack.replace(nextLocation);
|
|
57
|
-
} else {
|
|
58
|
-
stack.push(nextLocation);
|
|
59
|
-
}
|
|
60
|
-
return navigate.success({
|
|
61
|
-
delta: 0,
|
|
62
|
-
action: replace ? Action.Replace : Action.Push,
|
|
63
|
-
location: nextLocation,
|
|
64
|
-
});
|
|
65
|
-
}),
|
|
66
|
-
);
|
|
67
|
-
};
|
|
68
|
-
|
|
69
|
-
/**
|
|
70
|
-
* Flow creator for handling go (back/forward) actions.
|
|
71
|
-
*
|
|
72
|
-
* Execution flow:
|
|
73
|
-
* 1. Filters for go actions
|
|
74
|
-
* 2. Combines with current state to get history entries
|
|
75
|
-
* 3. Moves the stack index by the specified delta (negative for back, positive for forward)
|
|
76
|
-
* 4. Returns success action with POP action type and the new current location
|
|
77
|
-
*/
|
|
78
|
-
export const go: HistoryFlowCreator =
|
|
79
|
-
(stack: HistoryStack): HistoryFlow =>
|
|
80
|
-
(action$: Observable<Actions>, state$: Observable<LocationState>) => {
|
|
81
|
-
const { go } = actions;
|
|
82
|
-
return action$.pipe(
|
|
83
|
-
filterAction(go.type),
|
|
84
|
-
withLatestFrom(state$),
|
|
85
|
-
map(([action, state]) => {
|
|
86
|
-
const { delta } = action.payload;
|
|
87
|
-
stack.go(delta, state);
|
|
88
|
-
return go.success({
|
|
89
|
-
delta: 0,
|
|
90
|
-
action: Action.Pop,
|
|
91
|
-
location: stack.current,
|
|
92
|
-
});
|
|
93
|
-
}),
|
|
94
|
-
);
|
|
95
|
-
};
|
|
96
|
-
|
|
97
|
-
/**
|
|
98
|
-
* Flow creator for handling pop (browser back/forward) actions.
|
|
99
|
-
*
|
|
100
|
-
* Execution flow:
|
|
101
|
-
* 1. Filters for pop actions
|
|
102
|
-
* 2. Uses the location from the action payload or falls back to stack.current
|
|
103
|
-
* 3. Emits a success action with POP action type and the current location
|
|
104
|
-
* 4. Completes the observable immediately (synchronous operation)
|
|
105
|
-
*/
|
|
106
|
-
export const pop: HistoryFlowCreator =
|
|
107
|
-
(stack: HistoryStack): HistoryFlow =>
|
|
108
|
-
(action$: Observable<Actions>) => {
|
|
109
|
-
const { pop } = actions;
|
|
110
|
-
return action$.pipe(
|
|
111
|
-
filterAction(pop.type),
|
|
112
|
-
concatMap(
|
|
113
|
-
(action) =>
|
|
114
|
-
new Observable<Actions>((subscriber) => {
|
|
115
|
-
const currentLocation = action.payload.update?.location ?? stack.current;
|
|
116
|
-
// todo - we might need to check if the current location is in the history stack
|
|
117
|
-
// for now we will just use the current location
|
|
118
|
-
subscriber.next(
|
|
119
|
-
pop.success({
|
|
120
|
-
delta: 0,
|
|
121
|
-
action: Action.Pop,
|
|
122
|
-
location: currentLocation,
|
|
123
|
-
}),
|
|
124
|
-
);
|
|
125
|
-
subscriber.complete();
|
|
126
|
-
}),
|
|
127
|
-
),
|
|
128
|
-
);
|
|
129
|
-
};
|
|
130
|
-
|
|
131
|
-
/**
|
|
132
|
-
* Flow creator for checking navigation blockers before processing actions.
|
|
133
|
-
*
|
|
134
|
-
* Execution flow:
|
|
135
|
-
* 1. Filters for navigation actions (navigate, go, pop, validateLocation)
|
|
136
|
-
* 2. Determines the action type (PUSH/REPLACE for navigate, POP for others)
|
|
137
|
-
* 3. Combines with current state to check for active blockers
|
|
138
|
-
* 4. If no blockers exist, passes the action through immediately
|
|
139
|
-
* 5. If blockers exist:
|
|
140
|
-
* - Creates a transition object with action, location, and retry callback
|
|
141
|
-
* - Calls each blocker asynchronously (wrapped in Promise to prevent blocking)
|
|
142
|
-
* - Blockers can call retry() to allow navigation or prevent it by not calling retry()
|
|
143
|
-
* - Waits for all blockers to complete before continuing
|
|
144
|
-
* 6. Returns the original action if navigation is allowed, or completes without emitting if blocked
|
|
145
|
-
*/
|
|
146
|
-
export const checkBlockers: HistoryFlowCreator =
|
|
147
|
-
(stack: HistoryStack): HistoryFlow =>
|
|
148
|
-
(action$: Observable<Actions>, state$: Observable<LocationState>) => {
|
|
149
|
-
const { pop, navigate, go, validateLocation } = actions;
|
|
150
|
-
return action$.pipe(
|
|
151
|
-
filterAction(navigate.type, pop.type, go.type, validateLocation.type),
|
|
152
|
-
map((action) => {
|
|
153
|
-
switch (action.type) {
|
|
154
|
-
case navigate.type:
|
|
155
|
-
return {
|
|
156
|
-
replace: action.payload.options.replace,
|
|
157
|
-
action: action,
|
|
158
|
-
};
|
|
159
|
-
case go.type:
|
|
160
|
-
case pop.type:
|
|
161
|
-
case validateLocation.type:
|
|
162
|
-
return {
|
|
163
|
-
type: 'POP' as Action,
|
|
164
|
-
action: action,
|
|
165
|
-
};
|
|
166
|
-
}
|
|
167
|
-
}),
|
|
168
|
-
withLatestFrom(state$),
|
|
169
|
-
concatMap(([{ action, replace }, state]) => {
|
|
170
|
-
if (!state.blockers.length) {
|
|
171
|
-
return of(action);
|
|
172
|
-
}
|
|
173
|
-
return new Observable<Actions>((subscriber) => {
|
|
174
|
-
const location = stack.current;
|
|
175
|
-
const blockers = state.blockers.map((blocker) =>
|
|
176
|
-
Promise.resolve(
|
|
177
|
-
blocker({
|
|
178
|
-
delta: 0,
|
|
179
|
-
action: replace ? Action.Replace : Action.Push,
|
|
180
|
-
location,
|
|
181
|
-
retry: () => {
|
|
182
|
-
subscriber.next(action);
|
|
183
|
-
},
|
|
184
|
-
}),
|
|
185
|
-
),
|
|
186
|
-
);
|
|
187
|
-
Promise.allSettled(blockers).then(() => {
|
|
188
|
-
subscriber.complete();
|
|
189
|
-
});
|
|
190
|
-
});
|
|
191
|
-
}),
|
|
192
|
-
);
|
|
193
|
-
};
|
|
194
|
-
|
|
195
|
-
/**
|
|
196
|
-
* Flow creator for validating the current location against history state.
|
|
197
|
-
*
|
|
198
|
-
* Execution flow:
|
|
199
|
-
* 1. Filters for validateLocation actions
|
|
200
|
-
* 2. Combines with current state to access history entries
|
|
201
|
-
* 3. Gets the current location from the stack
|
|
202
|
-
* 4. Searches history for an entry matching the current location's key
|
|
203
|
-
* 5. Validates that:
|
|
204
|
-
* - The location key exists in history (returns failure if not found)
|
|
205
|
-
* - The location state matches the history entry state (returns failure if mismatch)
|
|
206
|
-
* 6. Returns success action with current location if validation passes, failure otherwise
|
|
207
|
-
*/
|
|
208
|
-
export const validateCurrentLocation: HistoryFlowCreator =
|
|
209
|
-
(stack: HistoryStack): HistoryFlow =>
|
|
210
|
-
(action$: Observable<Actions>, state$: Observable<LocationState>) => {
|
|
211
|
-
const { validateLocation } = actions;
|
|
212
|
-
return action$.pipe(
|
|
213
|
-
filterAction(validateLocation.type),
|
|
214
|
-
withLatestFrom(state$),
|
|
215
|
-
map(([, state]) => {
|
|
216
|
-
const currentLocation = stack.current;
|
|
217
|
-
const record = state.history.find(({ location }) => location?.key === currentLocation?.key);
|
|
218
|
-
if (!record) {
|
|
219
|
-
return validateLocation.failure(new Error('Stack state not found'));
|
|
220
|
-
}
|
|
221
|
-
if (record?.location?.state !== currentLocation?.state) {
|
|
222
|
-
return validateLocation.failure(new Error('Stack state mismatch'));
|
|
223
|
-
}
|
|
224
|
-
return validateLocation.success({
|
|
225
|
-
delta: 0,
|
|
226
|
-
action: Action.Pop,
|
|
227
|
-
location: currentLocation,
|
|
228
|
-
});
|
|
229
|
-
}),
|
|
230
|
-
);
|
|
231
|
-
};
|
|
232
|
-
|
|
233
|
-
/**
|
|
234
|
-
* Creates a combined history flow from multiple flow creators.
|
|
235
|
-
*
|
|
236
|
-
* Merges all provided flows so they process actions in parallel.
|
|
237
|
-
* Optionally prepends a blocker-checking step that gates navigation
|
|
238
|
-
* actions through registered {@link NavigationBlocker | blockers}.
|
|
239
|
-
*
|
|
240
|
-
* @param flowCreators - Array of flow creators to combine
|
|
241
|
-
* @param options - Optional configuration
|
|
242
|
-
* @param options.skipBlockCheck - If `true`, skip blocker checking
|
|
243
|
-
* @returns A combined {@link HistoryFlowCreator} that merges all provided flows
|
|
244
|
-
*/
|
|
245
|
-
export const createFlow = (
|
|
246
|
-
flowCreators: HistoryFlowCreator[],
|
|
247
|
-
options?: { skipBlockCheck?: boolean },
|
|
248
|
-
): HistoryFlowCreator => {
|
|
249
|
-
return (stack: HistoryStack): HistoryFlow => {
|
|
250
|
-
const preProcessActions = options?.skipBlockCheck
|
|
251
|
-
? (action$: Observable<Actions>) => action$
|
|
252
|
-
: checkBlockers(stack);
|
|
253
|
-
|
|
254
|
-
const flows: HistoryFlow = (
|
|
255
|
-
action$: Observable<Actions>,
|
|
256
|
-
state$: Observable<LocationState>,
|
|
257
|
-
) => {
|
|
258
|
-
return merge(
|
|
259
|
-
...flowCreators
|
|
260
|
-
.map((initializer) => initializer(stack))
|
|
261
|
-
.map((flow) => flow(action$, state$)),
|
|
262
|
-
);
|
|
263
|
-
};
|
|
264
|
-
|
|
265
|
-
return (action$: Observable<Actions>, state$: Observable<LocationState>) => {
|
|
266
|
-
return preProcessActions(action$, state$).pipe((source$) => flows(source$, state$));
|
|
267
|
-
};
|
|
268
|
-
};
|
|
269
|
-
};
|
|
270
|
-
|
|
271
|
-
/**
|
|
272
|
-
* Collection of flow creators for history state management.
|
|
273
|
-
*/
|
|
274
|
-
export const flowCreators = { navigate, go, pop, validateCurrentLocation };
|
|
File without changes
|
|
File without changes
|