@relax.dev/router
Version:
Router for VKUI
198 lines (164 loc) • 4.68 kB
text/typescript
import { generatePath, MatchInterface, matchPath } from '../workWithPathRegexp';
import { Page } from './Page';
import { RouteList } from './Router';
import { InfinityPanelIdTitle, PageParams, RouterGenericBase } from './Types';
/**
* @ignore
*/
export const POPUP_KEY = 'p';
/**
* @ignore
*/
export const MODAL_KEY = 'm';
let routeUniqueId = 1;
function getNextUniqId() {
return routeUniqueId++;
}
function searchParamsToObject(searchParams: URLSearchParams): Record<string, string> {
const obj: { [key: string]: string } = {};
for (const [key, value] of searchParams) {
obj[key] = value;
}
return obj;
}
export class Route<T extends RouterGenericBase = RouterGenericBase> {
/**
* @type {Page}
*/
structure: Page<T>;
pageId: T['pageId'];
params: PageParams = {};
uniqId: number;
constructor(structure: Page<T>, pageId: T['pageId'], params: PageParams) {
this.structure = structure;
this.pageId = pageId;
this.params = params;
this.uniqId = getNextUniqId();
}
static getParamsFromPath<T extends RouterGenericBase = RouterGenericBase>(location: T['pageId']) {
if (location.includes('?')) {
const [, qs] = location.split('?', 2);
return searchParamsToObject(new URLSearchParams(qs));
} else {
return {};
}
}
/**
* @param {RouteList} routeList
* @param location "info?w=about&show=1" то, что лежит в window.location.hash
* @param noSlash
*/
static fromLocation<T extends RouterGenericBase = RouterGenericBase>(
routeList: RouteList<T>,
location: T['pageId'],
noSlash = true
) {
const params = Route.getParamsFromPath(location);
location = location.replace('#', '');
if (noSlash && location.length && !location.startsWith('/')) {
location = `/${location}`;
}
if (noSlash && !location.length) {
location = '/';
}
location = location.split('?', 2).shift() || (noSlash ? '/' : '');
let match: null | MatchInterface = null;
for (let pageId in routeList) {
if (routeList.hasOwnProperty(pageId)) {
match = matchPath(location, pageId);
if (match && match.isExact) {
break;
}
}
}
if (!match) {
throw new Error('ROUTE_NOT_FOUND');
}
const ps = routeList[match.path as T['pageId']];
if (!ps) {
throw new Error(`Router fail: cant find structure in routes for ${location}`);
}
return new Route<T>(ps, match.path, {
...params,
...match.params,
});
}
static fromPageId<T extends RouterGenericBase = RouterGenericBase>(
routeList: RouteList<T>,
pageId: T['pageId'],
params: PageParams,
prevViewId: T['viewId']
) {
const ps = routeList[pageId];
if (!ps) {
throw new Error(`Router fail: cant find structure in routes for ${pageId}`);
}
const { additionalViews } = ps;
const actualViewId = additionalViews.includes(prevViewId) ? prevViewId : ps.viewId;
ps.viewId = actualViewId;
return new Route(ps, pageId, params || {});
}
clone(): Route<T> {
const copy = new Route<T>(this.structure.clone(), this.pageId, { ...this.params });
copy.uniqId = this.uniqId;
return copy;
}
get location() {
const path = generatePath<T>(this.pageId, this.params);
return `#${path.replace(/\//, '')}`;
}
getPageId(): T['pageId'] {
return this.pageId;
}
getPanelId(): T['panelId'] | InfinityPanelIdTitle<T['panelId']> {
if (this.structure.isInfinityPanel) {
return `_${this.structure.panelId}..${this.uniqId}`;
}
return this.structure.panelId;
}
getPanelIdWithoutInfinity(): T['panelId'] {
return this.structure.panelId;
}
getViewId(): T['viewId'] {
return this.structure.viewId;
}
getRootId(): T['rootId'] {
return this.structure.rootId;
}
getParams(): PageParams {
return this.params;
}
setParams(params: PageParams = {}): Route<T> {
this.params = { ...this.params, ...params };
return this;
}
isPopup(): boolean {
return !!this.getPopupId();
}
getPopupId(): T['popupId'] | null {
return this.params[POPUP_KEY]?.toString() || null;
}
setPopupId(popupId: T['popupId']): Route<T> {
this.params[POPUP_KEY] = popupId;
return this;
}
isModal(): boolean {
return !!this.getModalId();
}
hasOverlay() {
return this.isModal() || this.isPopup();
}
getModalId(): T['modalId'] | null {
return this.params[MODAL_KEY]?.toString() || null;
}
setModalId(modalId: T['modalId']): Route<T> {
this.params[MODAL_KEY] = modalId;
return this;
}
out() {
// $TSFixMe
}
in() {
// $TSFixMe
}
}