112 lines
3.7 KiB
TypeScript
112 lines
3.7 KiB
TypeScript
|
import Observable from "./helper/observable";
|
||
|
import { Page, PageProps } from "./page";
|
||
|
import { h } from "preact";
|
||
|
|
||
|
function serializQuery(obj: any) {
|
||
|
var str = [];
|
||
|
for (var p in obj)
|
||
|
if (obj.hasOwnProperty(p)) {
|
||
|
str.push(encodeURIComponent(p) + "=" + encodeURIComponent(obj[p]));
|
||
|
}
|
||
|
return str.join("&");
|
||
|
}
|
||
|
|
||
|
function parseQuery(query: string) {
|
||
|
let data: any = {};
|
||
|
if (query.startsWith("?")) query = query.slice(1)
|
||
|
query.split("&").forEach(e => {
|
||
|
let [key, value] = e.split("=");
|
||
|
key = decodeURIComponent(key)
|
||
|
value = decodeURIComponent(value)
|
||
|
data[key] = value
|
||
|
})
|
||
|
return data
|
||
|
}
|
||
|
|
||
|
|
||
|
export default class Navigation {
|
||
|
private static _pages: Map<string, typeof Page> = new Map();
|
||
|
private static _page: { route: string, page: JSX.Element };
|
||
|
private static pageObservableServer = new Observable<JSX.Element>(false);
|
||
|
public static pageObservable = Navigation.pageObservableServer.getPublicApi();
|
||
|
private static _state: { [key: string]: any };
|
||
|
private static _hidden_state: { [key: string]: any };
|
||
|
|
||
|
public static addPage(route: string, comp: typeof Page) {
|
||
|
Navigation._pages.set(route, comp);
|
||
|
}
|
||
|
|
||
|
// public static get state() {
|
||
|
// return Navigation._state;
|
||
|
// }
|
||
|
|
||
|
public static set default(comp: typeof Page) {
|
||
|
console.log("Set default");
|
||
|
Navigation._pages.set("/", comp);
|
||
|
}
|
||
|
|
||
|
public static set not_found(comp: typeof Page) {
|
||
|
Navigation._pages.set("/404", comp);
|
||
|
}
|
||
|
|
||
|
public static setPage(route: string, state?: { [key: string]: string }, hidden?: { [key: string]: string }, replace?: boolean) {
|
||
|
let component = Navigation._pages.get(route);
|
||
|
if (!component && route !== "/404") {
|
||
|
Navigation.setPage("/404", { route, key: "404" })
|
||
|
} else {
|
||
|
if (!Navigation.page || Navigation.page.route !== route || JSON.stringify(state) !== JSON.stringify(Navigation._state) || JSON.stringify(Navigation._hidden_state) !== JSON.stringify(hidden)) {
|
||
|
let s = "";
|
||
|
if (state) {
|
||
|
s = "?" + serializQuery(state)
|
||
|
}
|
||
|
let newhash = "#" + route + s;
|
||
|
let newkey = newhash + serializQuery(hidden)
|
||
|
let whash = window.location.hash;
|
||
|
if (!whash || whash === "") whash = "#/"
|
||
|
let oldkey = whash + serializQuery(history.state);
|
||
|
if (newkey !== oldkey) {
|
||
|
if (replace)
|
||
|
window.history.replaceState(hidden, document.title, newhash);
|
||
|
else
|
||
|
window.history.pushState(hidden, document.title, newhash);
|
||
|
}
|
||
|
let page = h(component as any, { state: state, key: newhash + serializQuery(hidden), hidden: hidden });
|
||
|
|
||
|
Navigation._state = state;
|
||
|
Navigation._hidden_state = hidden;
|
||
|
Navigation._page = { page, route };
|
||
|
Navigation.pageObservableServer.send(page);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static get page() {
|
||
|
return Navigation._page;
|
||
|
}
|
||
|
|
||
|
static onHashChange(hidden_state: { [key: string]: string }) {
|
||
|
let hash = window.location.hash.substring(1);
|
||
|
if (hash && hash !== "") {
|
||
|
let [route, state] = hash.split("?");
|
||
|
let s;
|
||
|
if (state) {
|
||
|
try {
|
||
|
s = parseQuery(state)
|
||
|
} catch (err) {
|
||
|
s = undefined;
|
||
|
console.error(err);
|
||
|
}
|
||
|
}
|
||
|
Navigation.setPage(route, s, hidden_state)
|
||
|
} else {
|
||
|
Navigation.setPage("/");
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static start() {
|
||
|
window.addEventListener("popstate", (ev) => {
|
||
|
Navigation.onHashChange(ev.state);
|
||
|
})
|
||
|
Navigation.onHashChange(undefined);
|
||
|
}
|
||
|
}
|