@equinor/fusion-framework-module-navigation 6.0.1 → 7.0.0-next.2
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 +83 -5
- package/README.md +694 -0
- package/dist/esm/NavigationConfigurator.interface.js +2 -0
- package/dist/esm/NavigationConfigurator.interface.js.map +1 -0
- package/dist/esm/NavigationConfigurator.js +144 -0
- package/dist/esm/NavigationConfigurator.js.map +1 -0
- package/dist/esm/NavigationProvider.interface.js +2 -0
- package/dist/esm/NavigationProvider.interface.js.map +1 -0
- package/dist/esm/NavigationProvider.js +256 -0
- package/dist/esm/NavigationProvider.js.map +1 -0
- package/dist/esm/enable-navigation.js +52 -0
- package/dist/esm/enable-navigation.js.map +1 -0
- package/dist/esm/events.js +23 -0
- package/dist/esm/events.js.map +1 -0
- package/dist/esm/index.js +6 -5
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/lib/BaseHistory.js +189 -0
- package/dist/esm/lib/BaseHistory.js.map +1 -0
- package/dist/esm/lib/BrowserHistory.js +91 -0
- package/dist/esm/lib/BrowserHistory.js.map +1 -0
- package/dist/esm/lib/BrowserHistoryHashStack.js +53 -0
- package/dist/esm/lib/BrowserHistoryHashStack.js.map +1 -0
- package/dist/esm/lib/BrowserHistoryStack.js +77 -0
- package/dist/esm/lib/BrowserHistoryStack.js.map +1 -0
- package/dist/esm/lib/MemoryHistory.js +47 -0
- package/dist/esm/lib/MemoryHistory.js.map +1 -0
- package/dist/esm/lib/MemoryStack.js +98 -0
- package/dist/esm/lib/MemoryStack.js.map +1 -0
- package/dist/esm/lib/create-history.js +48 -0
- package/dist/esm/lib/create-history.js.map +1 -0
- package/dist/esm/lib/index.js +8 -1
- package/dist/esm/lib/index.js.map +1 -1
- package/dist/esm/lib/state/history.actions.js +71 -0
- package/dist/esm/lib/state/history.actions.js.map +1 -0
- package/dist/esm/lib/state/history.flows.js +212 -0
- package/dist/esm/lib/state/history.flows.js.map +1 -0
- package/dist/esm/lib/state/history.reducer.js +59 -0
- package/dist/esm/lib/state/history.reducer.js.map +1 -0
- package/dist/esm/lib/state/history.state.js +37 -0
- package/dist/esm/lib/state/history.state.js.map +1 -0
- package/dist/esm/lib/state/index.js +4 -0
- package/dist/esm/lib/state/index.js.map +1 -0
- package/dist/esm/lib/types.js +25 -0
- package/dist/esm/lib/types.js.map +1 -0
- package/dist/esm/lib/utils/encode-trailing-whitespace.js +19 -0
- package/dist/esm/lib/utils/encode-trailing-whitespace.js.map +1 -0
- package/dist/esm/lib/utils/has-protocol.js +22 -0
- package/dist/esm/lib/utils/has-protocol.js.map +1 -0
- package/dist/esm/lib/utils/index.js +6 -0
- package/dist/esm/lib/utils/index.js.map +1 -0
- package/dist/esm/lib/utils/path-to-string.js +23 -0
- package/dist/esm/lib/utils/path-to-string.js.map +1 -0
- package/dist/esm/lib/utils/path-to-url.js +47 -0
- package/dist/esm/lib/utils/path-to-url.js.map +1 -0
- package/dist/esm/lib/utils/resolve-browser-location.js +53 -0
- package/dist/esm/lib/utils/resolve-browser-location.js.map +1 -0
- package/dist/esm/lib/utils/resolve-path.js +21 -0
- package/dist/esm/lib/utils/resolve-path.js.map +1 -0
- package/dist/esm/module.js +34 -25
- package/dist/esm/module.js.map +1 -1
- package/dist/esm/version.js +1 -1
- package/dist/esm/version.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/types/NavigationConfigurator.d.ts +60 -0
- package/dist/types/NavigationConfigurator.interface.d.ts +17 -0
- package/dist/types/NavigationProvider.d.ts +112 -0
- package/dist/types/NavigationProvider.interface.d.ts +75 -0
- package/dist/types/enable-navigation.d.ts +40 -0
- package/dist/types/events.d.ts +48 -0
- package/dist/types/index.d.ts +13 -6
- package/dist/types/lib/BaseHistory.d.ts +122 -0
- package/dist/types/lib/BrowserHistory.d.ts +53 -0
- package/dist/types/lib/BrowserHistoryHashStack.d.ts +41 -0
- package/dist/types/lib/BrowserHistoryStack.d.ts +51 -0
- package/dist/types/lib/MemoryHistory.d.ts +27 -0
- package/dist/types/lib/MemoryStack.d.ts +57 -0
- package/dist/types/lib/create-history.d.ts +35 -0
- package/dist/types/lib/index.d.ts +7 -1
- package/dist/types/lib/state/history.actions.d.ts +61 -0
- package/dist/types/lib/state/history.flows.d.ts +102 -0
- package/dist/types/lib/state/history.reducer.d.ts +85 -0
- package/dist/types/lib/state/history.state.d.ts +27 -0
- package/dist/types/lib/state/index.d.ts +3 -0
- package/dist/types/lib/types.d.ts +145 -0
- package/dist/types/lib/utils/encode-trailing-whitespace.d.ts +16 -0
- package/dist/types/lib/utils/has-protocol.d.ts +19 -0
- package/dist/types/lib/utils/index.d.ts +5 -0
- package/dist/types/lib/utils/path-to-string.d.ts +17 -0
- package/dist/types/lib/utils/path-to-url.d.ts +32 -0
- package/dist/types/lib/utils/resolve-browser-location.d.ts +42 -0
- package/dist/types/lib/utils/resolve-path.d.ts +17 -0
- package/dist/types/module.d.ts +33 -5
- package/dist/types/version.d.ts +1 -1
- package/package.json +25 -7
- package/src/NavigationConfigurator.interface.ts +18 -0
- package/src/NavigationConfigurator.ts +184 -0
- package/src/NavigationProvider.interface.ts +86 -0
- package/src/NavigationProvider.ts +313 -0
- package/src/__tests__/BrowserHistory.test.ts +151 -0
- package/src/__tests__/HashHistory.test.ts +121 -0
- package/src/__tests__/MemoryHistory.test.ts +185 -0
- package/src/__tests__/NavigationProvider.test.ts +60 -0
- package/src/__tests__/setup.ts +8 -0
- package/src/enable-navigation.ts +59 -0
- package/src/events.ts +65 -0
- package/src/index.ts +27 -6
- package/src/lib/BaseHistory.ts +229 -0
- package/src/lib/BrowserHistory.ts +123 -0
- package/src/lib/BrowserHistoryHashStack.ts +56 -0
- package/src/lib/BrowserHistoryStack.ts +82 -0
- package/src/lib/MemoryHistory.ts +66 -0
- package/src/lib/MemoryStack.ts +106 -0
- package/src/lib/create-history.ts +63 -0
- package/src/lib/index.ts +22 -1
- package/src/lib/state/history.actions.ts +108 -0
- package/src/lib/state/history.flows.ts +279 -0
- package/src/lib/state/history.reducer.ts +70 -0
- package/src/lib/state/history.state.ts +62 -0
- package/src/lib/state/index.ts +3 -0
- package/src/lib/types.ts +159 -0
- package/src/lib/utils/encode-trailing-whitespace.ts +18 -0
- package/src/lib/utils/has-protocol.ts +21 -0
- package/src/lib/utils/index.ts +5 -0
- package/src/lib/utils/path-to-string.ts +24 -0
- package/src/lib/utils/path-to-url.ts +52 -0
- package/src/lib/utils/resolve-browser-location.ts +60 -0
- package/src/lib/utils/resolve-path.ts +22 -0
- package/src/module.ts +53 -37
- package/src/version.ts +1 -1
- package/tsconfig.json +10 -1
- package/vitest.config.ts +14 -0
- package/dist/esm/configurator.js +0 -5
- package/dist/esm/configurator.js.map +0 -1
- package/dist/esm/createHistory.js +0 -18
- package/dist/esm/createHistory.js.map +0 -1
- package/dist/esm/lib/provider/INavigationProvider.js +0 -2
- package/dist/esm/lib/provider/INavigationProvider.js.map +0 -1
- package/dist/esm/lib/provider/NavigationProvider.js +0 -82
- package/dist/esm/lib/provider/NavigationProvider.js.map +0 -1
- package/dist/esm/lib/provider/index.js +0 -2
- package/dist/esm/lib/provider/index.js.map +0 -1
- package/dist/esm/navigator.js +0 -158
- package/dist/esm/navigator.js.map +0 -1
- package/dist/esm/types.js +0 -2
- package/dist/esm/types.js.map +0 -1
- package/dist/types/configurator.d.ts +0 -9
- package/dist/types/createHistory.d.ts +0 -4
- package/dist/types/lib/provider/INavigationProvider.d.ts +0 -38
- package/dist/types/lib/provider/NavigationProvider.d.ts +0 -22
- package/dist/types/lib/provider/index.d.ts +0 -2
- package/dist/types/navigator.d.ts +0 -105
- package/dist/types/types.d.ts +0 -3
- package/src/configurator.ts +0 -11
- package/src/createHistory.ts +0 -21
- package/src/lib/provider/INavigationProvider.ts +0 -46
- package/src/lib/provider/NavigationProvider.ts +0 -117
- package/src/lib/provider/index.ts +0 -2
- package/src/navigator.ts +0 -204
- package/src/types.ts +0 -4
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
import { Subscription } from 'rxjs';
|
|
2
|
+
import { filter } from 'rxjs/operators';
|
|
3
|
+
import { Action } from './types';
|
|
4
|
+
/**
|
|
5
|
+
* Abstract base class for history implementations.
|
|
6
|
+
* Provides common state management and navigation logic that can be shared
|
|
7
|
+
* across different history implementations (browser, memory, etc.).
|
|
8
|
+
*/
|
|
9
|
+
export class BaseHistory {
|
|
10
|
+
// Subscriptions for cleanup
|
|
11
|
+
#teardowns = new Subscription();
|
|
12
|
+
// internal state
|
|
13
|
+
#state;
|
|
14
|
+
/**
|
|
15
|
+
* Gets the current location.
|
|
16
|
+
*/
|
|
17
|
+
get location() {
|
|
18
|
+
return this.#state.subject.value.current.location;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Gets the current action.
|
|
22
|
+
*/
|
|
23
|
+
get action() {
|
|
24
|
+
return this.#state.subject.value.current.action;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Observable stream of navigation state changes.
|
|
28
|
+
* Emits on all navigation events (push, replace, pop).
|
|
29
|
+
*/
|
|
30
|
+
get state$() {
|
|
31
|
+
return this.#state.subject.select((state) => state.current);
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Observable stream of navigation actions.
|
|
35
|
+
*/
|
|
36
|
+
get action$() {
|
|
37
|
+
return this.#state.subject.action$;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Checks if there are any active navigation blockers.
|
|
41
|
+
*/
|
|
42
|
+
get hasBlockers() {
|
|
43
|
+
return this.#state.subject.value.blockers.length > 0;
|
|
44
|
+
}
|
|
45
|
+
constructor(state) {
|
|
46
|
+
this.#state = state;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Creates a valid href string for a given path.
|
|
50
|
+
*/
|
|
51
|
+
createHref(to) {
|
|
52
|
+
return this.createURL(to).href;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Creates a URL object for a given path.
|
|
56
|
+
*/
|
|
57
|
+
createURL(to) {
|
|
58
|
+
return this.#state.stack.createURL(to);
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Encodes a location by properly URL-encoding the pathname.
|
|
62
|
+
*/
|
|
63
|
+
encodeLocation(to) {
|
|
64
|
+
return this.#state.stack.createURL(to);
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Navigate to a location with explicit options.
|
|
68
|
+
*
|
|
69
|
+
* @param to - The target path (string, Path object, or Location object)
|
|
70
|
+
* @param options - Navigation options specifying action (PUSH/REPLACE) and optional state
|
|
71
|
+
*/
|
|
72
|
+
navigate(to, options) {
|
|
73
|
+
const action = this.#state.actions.navigate(to, options);
|
|
74
|
+
this.#state.subject.next(action);
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Pushes a new location onto the history stack.
|
|
78
|
+
*/
|
|
79
|
+
push(to, state) {
|
|
80
|
+
this.navigate(to, { state });
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Replaces the current location in the history stack.
|
|
84
|
+
*/
|
|
85
|
+
replace(to, state) {
|
|
86
|
+
this.navigate(to, { replace: true, state });
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Navigates backward or forward in the history stack.
|
|
90
|
+
*
|
|
91
|
+
* @param delta - The number of steps to move (negative for backward, positive for forward)
|
|
92
|
+
*/
|
|
93
|
+
go(delta) {
|
|
94
|
+
const action = this.#state.actions.go(delta);
|
|
95
|
+
this.#state.subject.next(action);
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Sets up a listener for navigation changes.
|
|
99
|
+
*
|
|
100
|
+
* Only listens for POP actions (browser back/forward navigation). PUSH/REPLACE
|
|
101
|
+
* actions are programmatic and synchronous - the caller already knows about them.
|
|
102
|
+
* POP actions come from browser events and are asynchronous, so we need to listen.
|
|
103
|
+
* This matches industry-standard router behavior (Remix/React Router).
|
|
104
|
+
*
|
|
105
|
+
* @param listener - Function to call on navigation changes
|
|
106
|
+
* @returns Function to unsubscribe the listener
|
|
107
|
+
*/
|
|
108
|
+
listen(listener) {
|
|
109
|
+
// Filter for POP actions only - PUSH/REPLACE are handled synchronously
|
|
110
|
+
const subscription = this.#state.subject
|
|
111
|
+
.select((state) => state.current)
|
|
112
|
+
.pipe(filter((update) => update.action === Action.Pop))
|
|
113
|
+
.subscribe((update) => {
|
|
114
|
+
listener(update);
|
|
115
|
+
});
|
|
116
|
+
// Register subscription for cleanup on dispose
|
|
117
|
+
this._addTeardown(subscription);
|
|
118
|
+
// Return unsubscribe function
|
|
119
|
+
return () => {
|
|
120
|
+
subscription.unsubscribe();
|
|
121
|
+
this._removeTeardown(subscription);
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Registers a blocker to intercept navigation attempts.
|
|
126
|
+
*
|
|
127
|
+
* @param blocker - Navigation blocker function to register
|
|
128
|
+
* @returns Function to unsubscribe the blocker
|
|
129
|
+
*/
|
|
130
|
+
block(blocker) {
|
|
131
|
+
this.#state.subject.next(this.#state.actions.addBlocker(blocker));
|
|
132
|
+
const removeBlocker = () => {
|
|
133
|
+
this.#state.subject.next(this.#state.actions.removeBlocker(blocker));
|
|
134
|
+
};
|
|
135
|
+
return this._addTeardown(removeBlocker, { executeOnRemove: true });
|
|
136
|
+
}
|
|
137
|
+
pop() {
|
|
138
|
+
this.#state.subject.next(this.#state.actions.pop());
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* Disposes of the history instance and cleans up all subscriptions.
|
|
142
|
+
*/
|
|
143
|
+
[Symbol.dispose]() {
|
|
144
|
+
this.#teardowns.unsubscribe();
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* Dispatches an action to trigger state updates.
|
|
148
|
+
*
|
|
149
|
+
* Use this to dispatch actions when external events occur (e.g., browser popstate).
|
|
150
|
+
* The action will be processed by flows and update the history state.
|
|
151
|
+
*
|
|
152
|
+
* @param action - The action to dispatch
|
|
153
|
+
*/
|
|
154
|
+
_dispatch(action) {
|
|
155
|
+
this.#state.subject.next(action);
|
|
156
|
+
}
|
|
157
|
+
/**
|
|
158
|
+
* Registers a cleanup function or subscription for automatic disposal.
|
|
159
|
+
*
|
|
160
|
+
* All teardowns are automatically cleaned up when the history instance is disposed.
|
|
161
|
+
* Use `executeOnRemove: true` if the teardown should run when manually removed.
|
|
162
|
+
*
|
|
163
|
+
* @param teardown - Function or subscription to clean up
|
|
164
|
+
* @param options - Optional configuration
|
|
165
|
+
* @param options.executeOnRemove - Execute teardown when removed (default: false)
|
|
166
|
+
* @returns Function to manually remove the teardown
|
|
167
|
+
*/
|
|
168
|
+
_addTeardown(teardown, options) {
|
|
169
|
+
this.#teardowns.add(teardown);
|
|
170
|
+
return () => {
|
|
171
|
+
if (options?.executeOnRemove) {
|
|
172
|
+
typeof teardown === 'function' ? teardown() : teardown.unsubscribe();
|
|
173
|
+
}
|
|
174
|
+
return this._removeTeardown(teardown);
|
|
175
|
+
};
|
|
176
|
+
}
|
|
177
|
+
/**
|
|
178
|
+
* Removes a teardown from the cleanup collection.
|
|
179
|
+
*
|
|
180
|
+
* Typically called automatically by the function returned from `_addTeardown`.
|
|
181
|
+
* Only call directly if you need to remove a teardown without executing it.
|
|
182
|
+
*
|
|
183
|
+
* @param teardown - The teardown to remove
|
|
184
|
+
*/
|
|
185
|
+
_removeTeardown(teardown) {
|
|
186
|
+
this.#teardowns.remove(teardown);
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
//# sourceMappingURL=BaseHistory.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"BaseHistory.js","sourceRoot":"","sources":["../../../src/lib/BaseHistory.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { fromEvent } from 'rxjs';
|
|
2
|
+
import { map } from 'rxjs/operators';
|
|
3
|
+
import { BaseHistory } from './BaseHistory';
|
|
4
|
+
import { BrowserHistoryStack } from './BrowserHistoryStack';
|
|
5
|
+
import { BrowserHistoryHashStack } from './BrowserHistoryHashStack';
|
|
6
|
+
import { resolveWindowLocation } from './utils';
|
|
7
|
+
import { Action } from './types';
|
|
8
|
+
import { createStore, createHistoryReducer, actions } from './state';
|
|
9
|
+
/**
|
|
10
|
+
* Handler for beforeunload events.
|
|
11
|
+
* Prevents page unload when navigation blockers are active.
|
|
12
|
+
*/
|
|
13
|
+
const onBeforeUnload = (event) => {
|
|
14
|
+
event.preventDefault();
|
|
15
|
+
event.returnValue = '';
|
|
16
|
+
};
|
|
17
|
+
/**
|
|
18
|
+
* Browser history implementation using native browser APIs.
|
|
19
|
+
*
|
|
20
|
+
* Uses the browser's History API (pushState/replaceState) for navigation.
|
|
21
|
+
* Automatically listens for popstate/hashchange events to detect browser back/forward navigation.
|
|
22
|
+
* Compatible with industry-standard routers (Remix/React Router).
|
|
23
|
+
*/
|
|
24
|
+
export class BrowserHistory extends BaseHistory {
|
|
25
|
+
#window;
|
|
26
|
+
/**
|
|
27
|
+
* Creates a browser history instance.
|
|
28
|
+
*
|
|
29
|
+
* Initializes with the current window location and sets up listeners for
|
|
30
|
+
* browser navigation events (popstate for regular routing, hashchange for hash routing).
|
|
31
|
+
*
|
|
32
|
+
* @param options - Configuration options
|
|
33
|
+
* @param options.window - Window object to use (defaults to global window)
|
|
34
|
+
* @param options.stack - Stack implementation (defaults to BrowserHistoryStack, use BrowserHistoryHashStack for hash routing)
|
|
35
|
+
* @throws {Error} If window is not available
|
|
36
|
+
*/
|
|
37
|
+
constructor(options = {}) {
|
|
38
|
+
// Use provided stack or default to BrowserHistoryStack
|
|
39
|
+
const Stack = options.stack ?? BrowserHistoryStack;
|
|
40
|
+
const browserWindow = options.window ?? document.defaultView;
|
|
41
|
+
if (!browserWindow) {
|
|
42
|
+
throw new Error('Window is required');
|
|
43
|
+
}
|
|
44
|
+
// Initialize state with current window location
|
|
45
|
+
const state = createStore(new Stack(browserWindow), createHistoryReducer({
|
|
46
|
+
delta: 0,
|
|
47
|
+
action: Action.Pop,
|
|
48
|
+
location: resolveWindowLocation(browserWindow, browserWindow.history),
|
|
49
|
+
}));
|
|
50
|
+
super(state);
|
|
51
|
+
this.#window = browserWindow;
|
|
52
|
+
// Determine event type based on stack implementation
|
|
53
|
+
// Hash routing uses 'hashchange', regular routing uses 'popstate'
|
|
54
|
+
const isHashHistory = state.stack instanceof BrowserHistoryHashStack;
|
|
55
|
+
const eventName = isHashHistory ? 'hashchange' : 'popstate';
|
|
56
|
+
// Listen for browser navigation events and dispatch POP actions
|
|
57
|
+
// This handles browser back/forward button clicks
|
|
58
|
+
this._addTeardown(fromEvent(browserWindow, eventName)
|
|
59
|
+
.pipe(map(() => {
|
|
60
|
+
const location = state.stack.current;
|
|
61
|
+
return actions.pop({ delta: 0, action: Action.Pop, location });
|
|
62
|
+
}))
|
|
63
|
+
.subscribe(this._dispatch.bind(this)));
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Registers a blocker to intercept navigation attempts.
|
|
67
|
+
*
|
|
68
|
+
* Adds a beforeunload event listener when blockers are active to prevent
|
|
69
|
+
* page unload (e.g., when user tries to close the tab). This provides
|
|
70
|
+
* browser-level protection in addition to in-app navigation blocking.
|
|
71
|
+
*
|
|
72
|
+
* @param blocker - Navigation blocker function to register
|
|
73
|
+
* @returns Function to unsubscribe the blocker
|
|
74
|
+
*/
|
|
75
|
+
block(blocker) {
|
|
76
|
+
const unblock = super.block(blocker);
|
|
77
|
+
// Add beforeunload listener when blockers are active
|
|
78
|
+
// This shows browser's "Leave site?" dialog on page unload
|
|
79
|
+
if (this.hasBlockers) {
|
|
80
|
+
window.addEventListener('beforeunload', onBeforeUnload);
|
|
81
|
+
}
|
|
82
|
+
return () => {
|
|
83
|
+
unblock();
|
|
84
|
+
// Remove beforeunload listener if no blockers remain
|
|
85
|
+
if (this.hasBlockers) {
|
|
86
|
+
window.removeEventListener('beforeunload', onBeforeUnload);
|
|
87
|
+
}
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
//# sourceMappingURL=BrowserHistory.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"BrowserHistory.js","sourceRoot":"","sources":["../../../src/lib/BrowserHistory.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { BrowserHistoryStack } from './BrowserHistoryStack';
|
|
2
|
+
import { resolveHashLocation, resolvePath, pathToString } from './utils';
|
|
3
|
+
/**
|
|
4
|
+
* Browser history hash stack implementation using hash-based routing.
|
|
5
|
+
*
|
|
6
|
+
* Uses URL hash fragment (#) instead of pathname. The hash is not sent to the server,
|
|
7
|
+
* allowing routing without server configuration.
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* ```ts
|
|
11
|
+
* // Regular routing: https://example.com/users
|
|
12
|
+
* // Hash routing: https://example.com/#/users
|
|
13
|
+
* const stack = new BrowserHistoryHashStack(window);
|
|
14
|
+
* ```
|
|
15
|
+
*/
|
|
16
|
+
export class BrowserHistoryHashStack extends BrowserHistoryStack {
|
|
17
|
+
/**
|
|
18
|
+
* Gets the current location from window.location.hash.
|
|
19
|
+
*/
|
|
20
|
+
get current() {
|
|
21
|
+
return resolveHashLocation(this._window);
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Creates a URL object for a given path with hash-based routing.
|
|
25
|
+
*
|
|
26
|
+
* Uses the current window location as the base and only modifies the hash fragment.
|
|
27
|
+
* The path is normalized to ensure it starts with '#' if not already present.
|
|
28
|
+
*
|
|
29
|
+
* @param to - The target path (string, Path object, or Location object)
|
|
30
|
+
* @returns A URL object with the path in the hash fragment
|
|
31
|
+
*
|
|
32
|
+
* @example
|
|
33
|
+
* ```ts
|
|
34
|
+
* // If current URL is 'https://example.com/app'
|
|
35
|
+
* createURL('/users?id=1')
|
|
36
|
+
* // URL { href: 'https://example.com/app#/users?id=1', ... }
|
|
37
|
+
*
|
|
38
|
+
* createURL({ pathname: '/dashboard', search: '?tab=settings' })
|
|
39
|
+
* // URL { href: 'https://example.com/app#/dashboard?tab=settings', ... }
|
|
40
|
+
* ```
|
|
41
|
+
*/
|
|
42
|
+
createURL(to) {
|
|
43
|
+
const path = resolvePath(to);
|
|
44
|
+
const fullPath = pathToString(path);
|
|
45
|
+
// Normalize: ensure path starts with '#' for hash routing
|
|
46
|
+
const hashPath = fullPath.startsWith('#') ? fullPath : `#${fullPath}`;
|
|
47
|
+
// Use current location as base, only modify hash
|
|
48
|
+
const url = new URL(this._window.location.href, this.origin);
|
|
49
|
+
url.hash = hashPath;
|
|
50
|
+
return url;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
//# sourceMappingURL=BrowserHistoryHashStack.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"BrowserHistoryHashStack.js","sourceRoot":"","sources":["../../../src/lib/BrowserHistoryHashStack.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,MAAM,uBAAuB,CAAC;AAE5D,OAAO,EAAE,mBAAmB,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAEzE;;;;;;;;;;;;GAYG;AACH,MAAM,OAAO,uBAAwB,SAAQ,mBAAmB;IAC9D;;OAEG;IACH,IAAW,OAAO;QAChB,OAAO,mBAAmB,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IAC3C,CAAC;IAED;;;;;;;;;;;;;;;;;;OAkBG;IACa,SAAS,CAAC,EAAM;QAC9B,MAAM,IAAI,GAAG,WAAW,CAAC,EAAE,CAAC,CAAC;QAC7B,MAAM,QAAQ,GAAG,YAAY,CAAC,IAAI,CAAC,CAAC;QACpC,0DAA0D;QAC1D,MAAM,QAAQ,GAAG,QAAQ,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,QAAQ,EAAE,CAAC;QACtE,iDAAiD;QACjD,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,IAAI,EAAE,IAAI,CAAC,MAAM,CAAC,CAAC;QAC7D,GAAG,CAAC,IAAI,GAAG,QAAQ,CAAC;QAEpB,OAAO,GAAG,CAAC;IACb,CAAC;CACF"}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { pathToString, resolvePath, resolveWindowLocation } from './utils';
|
|
2
|
+
/**
|
|
3
|
+
* Browser history stack implementation using native browser APIs.
|
|
4
|
+
*
|
|
5
|
+
* Manages navigation state using the browser's History API (pushState/replaceState).
|
|
6
|
+
* Stores location state in history.state and uses relative paths for navigation.
|
|
7
|
+
* This is the base implementation for regular (non-hash) routing.
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* ```ts
|
|
11
|
+
* const stack = new BrowserHistoryStack(window);
|
|
12
|
+
* stack.push({ pathname: '/users', key: 'abc123', state: { userId: 1 } });
|
|
13
|
+
* // Updates URL to /users and adds entry to history
|
|
14
|
+
* ```
|
|
15
|
+
*/
|
|
16
|
+
export class BrowserHistoryStack {
|
|
17
|
+
_window;
|
|
18
|
+
/**
|
|
19
|
+
* @param _window - The window object to use for history operations
|
|
20
|
+
*/
|
|
21
|
+
constructor(_window) {
|
|
22
|
+
this._window = _window;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Gets the origin of the history stack.
|
|
26
|
+
*/
|
|
27
|
+
get origin() {
|
|
28
|
+
return this._window.location.origin;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Gets the current location.
|
|
32
|
+
*/
|
|
33
|
+
get current() {
|
|
34
|
+
return resolveWindowLocation(this._window);
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Pushes a new entry onto the history stack.
|
|
38
|
+
*/
|
|
39
|
+
push(location) {
|
|
40
|
+
this.navigate(location, 'PUSH');
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Replaces the current entry in the history stack.
|
|
44
|
+
*/
|
|
45
|
+
replace(location) {
|
|
46
|
+
this.navigate(location, 'REPLACE');
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Navigates to a location with the specified action.
|
|
50
|
+
*/
|
|
51
|
+
navigate(location, action) {
|
|
52
|
+
const relativePath = this._createRelativePath(location);
|
|
53
|
+
const state = { value: location.state, key: location.key };
|
|
54
|
+
if (action === 'PUSH') {
|
|
55
|
+
this._window.history.pushState(state, '', relativePath);
|
|
56
|
+
}
|
|
57
|
+
else {
|
|
58
|
+
this._window.history.replaceState(state, '', relativePath);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Navigates backward or forward in the history stack.
|
|
63
|
+
*/
|
|
64
|
+
go(delta) {
|
|
65
|
+
this._window.history.go(delta);
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Creates a URL object for a given path.
|
|
69
|
+
*/
|
|
70
|
+
createURL(to) {
|
|
71
|
+
return new URL(pathToString(resolvePath(to)), this.origin);
|
|
72
|
+
}
|
|
73
|
+
_createRelativePath(to) {
|
|
74
|
+
return pathToString(this.createURL(to));
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
//# sourceMappingURL=BrowserHistoryStack.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"BrowserHistoryStack.js","sourceRoot":"","sources":["../../../src/lib/BrowserHistoryStack.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,WAAW,EAAE,qBAAqB,EAAE,MAAM,SAAS,CAAC;AAG3E;;;;;;;;;;;;;GAaG;AACH,MAAM,OAAO,mBAAmB;IAIQ;IAHtC;;OAEG;IACH,YAAsC,OAAe;QAAf,YAAO,GAAP,OAAO,CAAQ;IAAG,CAAC;IAEzD;;OAEG;IACH,IAAW,MAAM;QACf,OAAO,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAC;IACtC,CAAC;IAED;;OAEG;IACH,IAAW,OAAO;QAChB,OAAO,qBAAqB,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IAC7C,CAAC;IAED;;OAEG;IACI,IAAI,CAAC,QAAkB;QAC5B,IAAI,CAAC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;IAClC,CAAC;IAED;;OAEG;IACI,OAAO,CAAC,QAAkB;QAC/B,IAAI,CAAC,QAAQ,CAAC,QAAQ,EAAE,SAAS,CAAC,CAAC;IACrC,CAAC;IAED;;OAEG;IACI,QAAQ,CAAC,QAAkB,EAAE,MAA0B;QAC5D,MAAM,YAAY,GAAG,IAAI,CAAC,mBAAmB,CAAC,QAAQ,CAAC,CAAC;QACxD,MAAM,KAAK,GAAG,EAAE,KAAK,EAAE,QAAQ,CAAC,KAAK,EAAE,GAAG,EAAE,QAAQ,CAAC,GAAG,EAAE,CAAC;QAC3D,IAAI,MAAM,KAAK,MAAM,EAAE,CAAC;YACtB,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,SAAS,CAAC,KAAK,EAAE,EAAE,EAAE,YAAY,CAAC,CAAC;QAC1D,CAAC;aAAM,CAAC;YACN,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,YAAY,CAAC,KAAK,EAAE,EAAE,EAAE,YAAY,CAAC,CAAC;QAC7D,CAAC;IACH,CAAC;IAED;;OAEG;IACI,EAAE,CAAC,KAAa;QACrB,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC;IACjC,CAAC;IAED;;OAEG;IACI,SAAS,CAAC,EAAM;QACrB,OAAO,IAAI,GAAG,CAAC,YAAY,CAAC,WAAW,CAAC,EAAE,CAAC,CAAC,EAAE,IAAI,CAAC,MAAM,CAAC,CAAC;IAC7D,CAAC;IAES,mBAAmB,CAAC,EAAM;QAClC,OAAO,YAAY,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,CAAC,CAAC,CAAC;IAC1C,CAAC;CACF"}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { Action } from './types';
|
|
2
|
+
import { pathToString, resolvePath } from './utils';
|
|
3
|
+
import { MemoryHistoryStack } from './MemoryStack';
|
|
4
|
+
import { BaseHistory } from './BaseHistory';
|
|
5
|
+
import { createHistoryReducer, createStore } from './state';
|
|
6
|
+
/**
|
|
7
|
+
* Default initial location for memory history.
|
|
8
|
+
*/
|
|
9
|
+
const defaultInitialLocation = {
|
|
10
|
+
delta: 0,
|
|
11
|
+
action: Action.Pop,
|
|
12
|
+
location: { pathname: '/', search: '', hash: '', key: 'unknown', state: null },
|
|
13
|
+
};
|
|
14
|
+
/**
|
|
15
|
+
* Memory history implementation using in-memory storage.
|
|
16
|
+
*
|
|
17
|
+
* Useful for:
|
|
18
|
+
* - Testing: Control navigation state in tests without browser APIs
|
|
19
|
+
* - SSR: Server-side rendering where window is not available
|
|
20
|
+
* - Widgets: Serving applications as widgets without affecting the main browser history
|
|
21
|
+
* - Node.js environments: Environments without browser APIs
|
|
22
|
+
*/
|
|
23
|
+
export class MemoryHistory extends BaseHistory {
|
|
24
|
+
constructor(options) {
|
|
25
|
+
const { initialLocation, initialHistory } = options ?? {};
|
|
26
|
+
const initial = initialLocation ?? defaultInitialLocation;
|
|
27
|
+
// create initial state for memory history
|
|
28
|
+
const initialState = {
|
|
29
|
+
current: initial,
|
|
30
|
+
history: initialHistory ?? [initial],
|
|
31
|
+
blockers: [],
|
|
32
|
+
};
|
|
33
|
+
// create stack for memory history
|
|
34
|
+
const stack = new MemoryHistoryStack({ initialLocation: initialState.current.location });
|
|
35
|
+
// initialize state with stack and reducer
|
|
36
|
+
const state = createStore(stack, createHistoryReducer(() => initialState, { maxHistory: 100 }));
|
|
37
|
+
super(state);
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Creates a URL object for a given path using memory:// origin.
|
|
41
|
+
*/
|
|
42
|
+
createURL(to) {
|
|
43
|
+
const path = pathToString(resolvePath(to));
|
|
44
|
+
return new URL(path, 'memory://');
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
//# sourceMappingURL=MemoryHistory.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"MemoryHistory.js","sourceRoot":"","sources":["../../../src/lib/MemoryHistory.ts"],"names":[],"mappings":"AAAA,OAAO,EAAsD,MAAM,EAAE,MAAM,SAAS,CAAC;AACrF,OAAO,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AACpD,OAAO,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAC;AACnD,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAC5C,OAAO,EAAE,oBAAoB,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AAE5D;;GAEG;AACH,MAAM,sBAAsB,GAAqB;IAC/C,KAAK,EAAE,CAAC;IACR,MAAM,EAAE,MAAM,CAAC,GAAG;IAClB,QAAQ,EAAE,EAAE,QAAQ,EAAE,GAAG,EAAE,MAAM,EAAE,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE,GAAG,EAAE,SAAS,EAAE,KAAK,EAAE,IAAI,EAAE;CAC/E,CAAC;AAYF;;;;;;;;GAQG;AACH,MAAM,OAAO,aAAc,SAAQ,WAAW;IAC5C,YAAmB,OAA8B;QAC/C,MAAM,EAAE,eAAe,EAAE,cAAc,EAAE,GAAG,OAAO,IAAI,EAAE,CAAC;QAC1D,MAAM,OAAO,GAAG,eAAe,IAAI,sBAAsB,CAAC;QAE1D,0CAA0C;QAC1C,MAAM,YAAY,GAAkB;YAClC,OAAO,EAAE,OAAO;YAChB,OAAO,EAAE,cAAc,IAAI,CAAC,OAAO,CAAC;YACpC,QAAQ,EAAE,EAAE;SACb,CAAC;QAEF,kCAAkC;QAClC,MAAM,KAAK,GAAG,IAAI,kBAAkB,CAAC,EAAE,eAAe,EAAE,YAAY,CAAC,OAAO,CAAC,QAAQ,EAAE,CAAC,CAAC;QAEzF,0CAA0C;QAC1C,MAAM,KAAK,GAAG,WAAW,CACvB,KAAK,EACL,oBAAoB,CAAC,GAAG,EAAE,CAAC,YAAY,EAAE,EAAE,UAAU,EAAE,GAAG,EAAE,CAAC,CAC9D,CAAC;QAEF,KAAK,CAAC,KAAK,CAAC,CAAC;IACf,CAAC;IAED;;OAEG;IACI,SAAS,CAAC,EAAM;QACrB,MAAM,IAAI,GAAG,YAAY,CAAC,WAAW,CAAC,EAAE,CAAC,CAAC,CAAC;QAC3C,OAAO,IAAI,GAAG,CAAC,IAAI,EAAE,WAAW,CAAC,CAAC;IACpC,CAAC;CACF"}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import { pathToString, resolvePath } from './utils';
|
|
2
|
+
/**
|
|
3
|
+
* Memory-based history stack implementation.
|
|
4
|
+
*
|
|
5
|
+
* Stores navigation state in memory instead of using browser APIs.
|
|
6
|
+
* Unlike BrowserHistoryStack, push/replace only update the current location
|
|
7
|
+
* (history entries are managed by the reducer, not the stack).
|
|
8
|
+
*/
|
|
9
|
+
export class MemoryHistoryStack {
|
|
10
|
+
#current;
|
|
11
|
+
/**
|
|
12
|
+
* Gets the origin of the history stack.
|
|
13
|
+
* Always returns 'memory://' for in-memory storage.
|
|
14
|
+
*/
|
|
15
|
+
get origin() {
|
|
16
|
+
return 'memory://';
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Gets the current location.
|
|
20
|
+
*/
|
|
21
|
+
get current() {
|
|
22
|
+
return this.#current;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Creates a memory history stack instance.
|
|
26
|
+
*
|
|
27
|
+
* @param options - Configuration options
|
|
28
|
+
* @param options.initialLocation - Optional initial location (defaults to '/')
|
|
29
|
+
*/
|
|
30
|
+
constructor(options) {
|
|
31
|
+
this.#current = options?.initialLocation ?? {
|
|
32
|
+
pathname: '/',
|
|
33
|
+
search: '',
|
|
34
|
+
hash: '',
|
|
35
|
+
state: null,
|
|
36
|
+
key: '',
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Pushes a new entry onto the history stack.
|
|
41
|
+
*
|
|
42
|
+
* Only updates the current location. History entries are managed by the reducer.
|
|
43
|
+
*/
|
|
44
|
+
push(location) {
|
|
45
|
+
this.#current = location;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Replaces the current entry in the history stack.
|
|
49
|
+
*
|
|
50
|
+
* Only updates the current location. History entries are managed by the reducer.
|
|
51
|
+
*/
|
|
52
|
+
replace(location) {
|
|
53
|
+
this.#current = location;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Navigates backward or forward in the history stack.
|
|
57
|
+
*
|
|
58
|
+
* Uses the history state to find the target location by index.
|
|
59
|
+
* Clamps to valid range (first or last entry if out of bounds).
|
|
60
|
+
*
|
|
61
|
+
* @param delta - Number of steps to move (negative for back, positive for forward)
|
|
62
|
+
* @param state - Current location state with history entries
|
|
63
|
+
*/
|
|
64
|
+
go(delta, state) {
|
|
65
|
+
const { history, current } = state;
|
|
66
|
+
const currentLocation = current.location ?? this.#current;
|
|
67
|
+
// Find current location in history by key
|
|
68
|
+
const currentIndex = history.findIndex((entry) => entry.location.key === currentLocation.key);
|
|
69
|
+
// If current location not found, use last entry
|
|
70
|
+
if (currentIndex === -1) {
|
|
71
|
+
if (history.length > 0) {
|
|
72
|
+
this.#current = history[history.length - 1].location;
|
|
73
|
+
}
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
// Calculate target index and clamp to valid range
|
|
77
|
+
const newIndex = currentIndex + delta;
|
|
78
|
+
if (newIndex < 0) {
|
|
79
|
+
this.#current = state.history[0].location;
|
|
80
|
+
}
|
|
81
|
+
else if (newIndex >= state.history.length) {
|
|
82
|
+
this.#current = state.history[state.history.length - 1].location;
|
|
83
|
+
}
|
|
84
|
+
else {
|
|
85
|
+
this.#current = state.history[newIndex].location;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Creates a URL object for a given path.
|
|
90
|
+
*
|
|
91
|
+
* All URLs use the 'memory://' origin since this is in-memory storage.
|
|
92
|
+
*/
|
|
93
|
+
createURL(to) {
|
|
94
|
+
const path = resolvePath(to);
|
|
95
|
+
return new URL(pathToString(path), this.origin);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
//# sourceMappingURL=MemoryStack.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"MemoryStack.js","sourceRoot":"","sources":["../../../src/lib/MemoryStack.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AAEpD;;;;;;GAMG;AACH,MAAM,OAAO,kBAAkB;IAC7B,QAAQ,CAAW;IAEnB;;;OAGG;IACH,IAAI,MAAM;QACR,OAAO,WAAW,CAAC;IACrB,CAAC;IAED;;OAEG;IACH,IAAI,OAAO;QACT,OAAO,IAAI,CAAC,QAAQ,CAAC;IACvB,CAAC;IAED;;;;;OAKG;IACH,YAAY,OAAwC;QAClD,IAAI,CAAC,QAAQ,GAAG,OAAO,EAAE,eAAe,IAAI;YAC1C,QAAQ,EAAE,GAAG;YACb,MAAM,EAAE,EAAE;YACV,IAAI,EAAE,EAAE;YACR,KAAK,EAAE,IAAI;YACX,GAAG,EAAE,EAAE;SACR,CAAC;IACJ,CAAC;IAED;;;;OAIG;IACH,IAAI,CAAC,QAAkB;QACrB,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAC;IAC3B,CAAC;IAED;;;;OAIG;IACH,OAAO,CAAC,QAAkB;QACxB,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAC;IAC3B,CAAC;IAED;;;;;;;;OAQG;IACH,EAAE,CAAC,KAAa,EAAE,KAA8B;QAC9C,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,GAAG,KAAK,CAAC;QACnC,MAAM,eAAe,GAAG,OAAO,CAAC,QAAQ,IAAI,IAAI,CAAC,QAAQ,CAAC;QAC1D,0CAA0C;QAC1C,MAAM,YAAY,GAAG,OAAO,CAAC,SAAS,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,QAAQ,CAAC,GAAG,KAAK,eAAe,CAAC,GAAG,CAAC,CAAC;QAE9F,gDAAgD;QAChD,IAAI,YAAY,KAAK,CAAC,CAAC,EAAE,CAAC;YACxB,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;gBACvB,IAAI,CAAC,QAAQ,GAAG,OAAO,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,QAAQ,CAAC;YACvD,CAAC;YACD,OAAO;QACT,CAAC;QAED,kDAAkD;QAClD,MAAM,QAAQ,GAAG,YAAY,GAAG,KAAK,CAAC;QACtC,IAAI,QAAQ,GAAG,CAAC,EAAE,CAAC;YACjB,IAAI,CAAC,QAAQ,GAAG,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC;QAC5C,CAAC;aAAM,IAAI,QAAQ,IAAI,KAAK,CAAC,OAAO,CAAC,MAAM,EAAE,CAAC;YAC5C,IAAI,CAAC,QAAQ,GAAG,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,QAAQ,CAAC;QACnE,CAAC;aAAM,CAAC;YACN,IAAI,CAAC,QAAQ,GAAG,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,QAAQ,CAAC;QACnD,CAAC;IACH,CAAC;IAED;;;;OAIG;IACH,SAAS,CAAC,EAAM;QACd,MAAM,IAAI,GAAG,WAAW,CAAC,EAAE,CAAC,CAAC;QAC7B,OAAO,IAAI,GAAG,CAAC,YAAY,CAAC,IAAI,CAAC,EAAE,IAAI,CAAC,MAAM,CAAC,CAAC;IAClD,CAAC;CACF"}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { MemoryHistory } from './MemoryHistory';
|
|
2
|
+
import { BrowserHistory } from './BrowserHistory';
|
|
3
|
+
import { BrowserHistoryStack } from './BrowserHistoryStack';
|
|
4
|
+
import { BrowserHistoryHashStack } from './BrowserHistoryHashStack';
|
|
5
|
+
/**
|
|
6
|
+
* Creates a history instance based on the specified type.
|
|
7
|
+
*
|
|
8
|
+
* Factory function for creating different history implementations:
|
|
9
|
+
* - `'browser'`: Creates a {@link BrowserHistory} instance using regular routing (pathname-based)
|
|
10
|
+
* - `'hash'`: Creates a {@link BrowserHistory} instance using hash routing (hash-based)
|
|
11
|
+
* - `'memory'`: Creates a {@link MemoryHistory} instance for testing or SSR (in-memory)
|
|
12
|
+
*
|
|
13
|
+
* All returned instances are compatible with industry-standard routers (Remix/React Router).
|
|
14
|
+
*
|
|
15
|
+
* @param type - The type of history to create ('browser', 'hash', or 'memory')
|
|
16
|
+
* @param args - Optional arguments for the history type
|
|
17
|
+
* @returns A History instance compatible with industry-standard routers (Remix/React Router)
|
|
18
|
+
*
|
|
19
|
+
* @example
|
|
20
|
+
* ```ts
|
|
21
|
+
* // Regular browser routing
|
|
22
|
+
* const history = createHistory('browser');
|
|
23
|
+
*
|
|
24
|
+
* // Hash-based routing
|
|
25
|
+
* const hashHistory = createHistory('hash');
|
|
26
|
+
*
|
|
27
|
+
* // In-memory history for testing
|
|
28
|
+
* const memoryHistory = createHistory('memory', { initialLocation: { ... } });
|
|
29
|
+
* ```
|
|
30
|
+
*/
|
|
31
|
+
export const createHistory = (type, ...args) => {
|
|
32
|
+
switch (type) {
|
|
33
|
+
case 'memory':
|
|
34
|
+
return new MemoryHistory(...args);
|
|
35
|
+
case 'browser': {
|
|
36
|
+
const options = args[0];
|
|
37
|
+
return new BrowserHistory({ ...options, stack: BrowserHistoryStack });
|
|
38
|
+
}
|
|
39
|
+
case 'hash': {
|
|
40
|
+
const options = args[0];
|
|
41
|
+
return new BrowserHistory({ ...options, stack: BrowserHistoryHashStack });
|
|
42
|
+
}
|
|
43
|
+
default: {
|
|
44
|
+
throw new Error(`Invalid history type: ${type}`);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
};
|
|
48
|
+
//# sourceMappingURL=create-history.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"create-history.js","sourceRoot":"","sources":["../../../src/lib/create-history.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAA6B,MAAM,iBAAiB,CAAC;AAC3E,OAAO,EAAE,cAAc,EAA8B,MAAM,kBAAkB,CAAC;AAC9E,OAAO,EAAE,mBAAmB,EAAE,MAAM,uBAAuB,CAAC;AAC5D,OAAO,EAAE,uBAAuB,EAAE,MAAM,2BAA2B,CAAC;AAQpE;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,MAAM,CAAC,MAAM,aAAa,GAAG,CAC3B,IAAO,EACP,GAAG,IAAmC,EACP,EAAE;IACjC,QAAQ,IAAI,EAAE,CAAC;QACb,KAAK,QAAQ;YACX,OAAO,IAAI,aAAa,CAAC,GAAI,IAA+B,CAE3D,CAAC;QACJ,KAAK,SAAS,CAAC,CAAC,CAAC;YACf,MAAM,OAAO,GAAG,IAAI,CAAC,CAAC,CAAyC,CAAC;YAChE,OAAO,IAAI,cAAc,CAAC,EAAE,GAAG,OAAO,EAAE,KAAK,EAAE,mBAAmB,EAAE,CAEnE,CAAC;QACJ,CAAC;QACD,KAAK,MAAM,CAAC,CAAC,CAAC;YACZ,MAAM,OAAO,GAAG,IAAI,CAAC,CAAC,CAAyC,CAAC;YAChE,OAAO,IAAI,cAAc,CAAC,EAAE,GAAG,OAAO,EAAE,KAAK,EAAE,uBAAuB,EAAE,CAEvE,CAAC;QACJ,CAAC;QACD,OAAO,CAAC,CAAC,CAAC;YACR,MAAM,IAAI,KAAK,CAAC,yBAAyB,IAAI,EAAE,CAAC,CAAC;QACnD,CAAC;IACH,CAAC;AACH,CAAC,CAAC"}
|
package/dist/esm/lib/index.js
CHANGED
|
@@ -1,2 +1,9 @@
|
|
|
1
|
-
|
|
1
|
+
// History implementations
|
|
2
|
+
export { BaseHistory } from './BaseHistory';
|
|
3
|
+
export { BrowserHistory } from './BrowserHistory';
|
|
4
|
+
export { MemoryHistory } from './MemoryHistory';
|
|
5
|
+
// History stacks
|
|
6
|
+
export { BrowserHistoryStack } from './BrowserHistoryStack';
|
|
7
|
+
export { BrowserHistoryHashStack as HashHistoryStack } from './BrowserHistoryHashStack';
|
|
8
|
+
export { MemoryHistoryStack } from './MemoryStack';
|
|
2
9
|
//# sourceMappingURL=index.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/lib/index.ts"],"names":[],"mappings":"AAAA,cAAc,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/lib/index.ts"],"names":[],"mappings":"AAAA,0BAA0B;AAC1B,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAC5C,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAClD,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAEhD,iBAAiB;AACjB,OAAO,EAAE,mBAAmB,EAAE,MAAM,uBAAuB,CAAC;AAC5D,OAAO,EAAE,uBAAuB,IAAI,gBAAgB,EAAE,MAAM,2BAA2B,CAAC;AACxF,OAAO,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAC"}
|