mobx-wouter
Version:
<img src="assets/logo.png" align="right" height="156" alt="logo" />
113 lines (112 loc) • 3.91 kB
JavaScript
import { MobxHistory, MobxLocation, QueryParams, buildSearchString, } from 'mobx-location-history';
import { startTransition } from 'react';
export class MobxRouter {
config;
history;
location;
queryParams;
baseUrl;
type;
constructor(config) {
this.config = config;
this.baseUrl = config.baseUrl;
this.type = config.type ?? 'browser';
this.history = config.history ?? new MobxHistory(config.abortSignal);
this.location =
config.location ?? new MobxLocation(this.history, config.abortSignal);
this.queryParams =
config.queryParams ?? new QueryParams(this.location, this.history);
}
createPath(to) {
const baseUrl = !this.baseUrl || this.baseUrl === '/' ? '' : this.baseUrl;
if (typeof to === 'string') {
const [rawPathname, ...searchSegments] = to.split('?');
const [pathname, ...hashSegments] = rawPathname.split('#');
const search = searchSegments.join('?');
const hash = hashSegments.join('#');
return {
baseUrl,
pathname,
search: search ? `?${search}` : '',
hash: hash || '',
};
}
else if ('baseUrl' in to) {
return to;
}
else {
return this.createPath(`${to.pathname}${buildSearchString(to.search || {})}`);
}
}
createUrl(to, type) {
const path = this.createPath(to);
return [
path.baseUrl,
type === 'hash' ? '#' : '',
path.pathname,
path.hash && `#${path.hash}`,
path.search,
].join('');
}
hashNavigate(to, options) {
const path = this.createPath(to);
const url = this.createUrl({
...path,
// This is fixes bug with pathname endings /
// If location.pathname is /test-foo then after navigation to /test-foo#bar
// navigation back will not work
// If location.pathname is /test-foo/ then after navigation to /test-foo/#/bar
// navigation back will not work
baseUrl: this.location.pathname,
}, this.type);
const state = options?.state ?? null;
this.wrapInViewTransition(() => {
this.location.hash = `#${path.pathname || '/'}`;
this.history.replaceState(state, '', url);
}, options?.useStartViewTransition);
}
browserNavigate(to, options) {
const path = this.createPath(to);
const url = this.createUrl(path, this.type);
const state = options?.state ?? null;
this.wrapInViewTransition(() => {
if (options?.replace) {
this.history.replaceState(state, '', url);
}
else {
this.history.pushState(state, '', url);
}
}, options?.useStartViewTransition);
}
lastViewTransition;
wrapInViewTransition(action, useStartViewTransition) {
if ((useStartViewTransition ||
(useStartViewTransition == null &&
this.config.useStartViewTransition)) &&
document.startViewTransition) {
if (this.lastViewTransition) {
this.lastViewTransition.skipTransition();
}
this.lastViewTransition = document.startViewTransition(() => {
startTransition(action);
});
this.lastViewTransition.finished.finally(() => {
delete this.lastViewTransition;
});
}
else {
action();
}
}
navigate(to, options) {
if (this.type === 'hash') {
this.hashNavigate(to, options);
}
else {
this.browserNavigate(to, options);
}
}
back() {
this.history.back();
}
}