summaryrefslogtreecommitdiff
path: root/history/modules/createMemoryHistory.js
diff options
context:
space:
mode:
Diffstat (limited to 'history/modules/createMemoryHistory.js')
-rw-r--r--history/modules/createMemoryHistory.js186
1 files changed, 186 insertions, 0 deletions
diff --git a/history/modules/createMemoryHistory.js b/history/modules/createMemoryHistory.js
new file mode 100644
index 0000000..8b541ea
--- /dev/null
+++ b/history/modules/createMemoryHistory.js
@@ -0,0 +1,186 @@
+import { createPath } from './PathUtils.js';
+import { createLocation } from './LocationUtils.js';
+import createTransitionManager from './createTransitionManager.js';
+import warning from './warning.js';
+
+function clamp(n, lowerBound, upperBound) {
+ return Math.min(Math.max(n, lowerBound), upperBound);
+}
+
+/**
+ * Creates a history object that stores locations in memory.
+ */
+function createMemoryHistory(props = {}) {
+ const {
+ getUserConfirmation,
+ initialEntries = ['/'],
+ initialIndex = 0,
+ keyLength = 6
+ } = props;
+
+ const transitionManager = createTransitionManager();
+
+ function setState(nextState) {
+ Object.assign(history, nextState);
+ history.length = history.entries.length;
+ transitionManager.notifyListeners(history.location, history.action);
+ }
+
+ function createKey() {
+ return Math.random()
+ .toString(36)
+ .substr(2, keyLength);
+ }
+
+ const index = clamp(initialIndex, 0, initialEntries.length - 1);
+ const entries = initialEntries.map(entry =>
+ typeof entry === 'string'
+ ? createLocation(entry, undefined, createKey())
+ : createLocation(entry, undefined, entry.key || createKey())
+ );
+
+ // Public interface
+
+ const createHref = createPath;
+
+ function push(path, state) {
+ warning(
+ !(
+ typeof path === 'object' &&
+ path.state !== undefined &&
+ state !== undefined
+ ),
+ 'You should avoid providing a 2nd state argument to push when the 1st ' +
+ 'argument is a location-like object that already has state; it is ignored'
+ );
+
+ const action = 'PUSH';
+ const location = createLocation(path, state, createKey(), history.location);
+
+ transitionManager.confirmTransitionTo(
+ location,
+ action,
+ getUserConfirmation,
+ ok => {
+ if (!ok) return;
+
+ const prevIndex = history.index;
+ const nextIndex = prevIndex + 1;
+
+ const nextEntries = history.entries.slice(0);
+ if (nextEntries.length > nextIndex) {
+ nextEntries.splice(
+ nextIndex,
+ nextEntries.length - nextIndex,
+ location
+ );
+ } else {
+ nextEntries.push(location);
+ }
+
+ setState({
+ action,
+ location,
+ index: nextIndex,
+ entries: nextEntries
+ });
+ }
+ );
+ }
+
+ function replace(path, state) {
+ warning(
+ !(
+ typeof path === 'object' &&
+ path.state !== undefined &&
+ state !== undefined
+ ),
+ 'You should avoid providing a 2nd state argument to replace when the 1st ' +
+ 'argument is a location-like object that already has state; it is ignored'
+ );
+
+ const action = 'REPLACE';
+ const location = createLocation(path, state, createKey(), history.location);
+
+ transitionManager.confirmTransitionTo(
+ location,
+ action,
+ getUserConfirmation,
+ ok => {
+ if (!ok) return;
+
+ history.entries[history.index] = location;
+
+ setState({ action, location });
+ }
+ );
+ }
+
+ function go(n) {
+ const nextIndex = clamp(history.index + n, 0, history.entries.length - 1);
+
+ const action = 'POP';
+ const location = history.entries[nextIndex];
+
+ transitionManager.confirmTransitionTo(
+ location,
+ action,
+ getUserConfirmation,
+ ok => {
+ if (ok) {
+ setState({
+ action,
+ location,
+ index: nextIndex
+ });
+ } else {
+ // Mimic the behavior of DOM histories by
+ // causing a render after a cancelled POP.
+ setState();
+ }
+ }
+ );
+ }
+
+ function goBack() {
+ go(-1);
+ }
+
+ function goForward() {
+ go(1);
+ }
+
+ function canGo(n) {
+ const nextIndex = history.index + n;
+ return nextIndex >= 0 && nextIndex < history.entries.length;
+ }
+
+ function block(prompt = false) {
+ return transitionManager.setPrompt(prompt);
+ }
+
+ function listen(listener) {
+ return transitionManager.appendListener(listener);
+ }
+
+ const history = {
+ length: entries.length,
+ action: 'POP',
+ location: entries[index],
+ index,
+ entries,
+ createHref,
+ push,
+ replace,
+ go,
+ goBack,
+ goForward,
+ canGo,
+ block,
+ listen
+ };
+
+ return history;
+}
+
+export default createMemoryHistory;