oneuptime/Common/UI/Utils/Navigation.ts

244 lines
6.3 KiB
TypeScript

import { AgnosticRouteMatch } from "@remix-run/router";
import Hostname from "Common/Types/API/Hostname";
import Route from "Common/Types/API/Route";
import URL from "Common/Types/API/URL";
import Dictionary from "Common/Types/Dictionary";
import BadDataException from "Common/Types/Exception/BadDataException";
import ObjectID from "Common/Types/ObjectID";
import {
Location,
NavigateFunction,
Params,
matchRoutes,
} from "react-router-dom";
abstract class Navigation {
private static navigateHook: NavigateFunction;
private static location: Location;
private static params: Params;
public static setNavigateHook(navigateHook: NavigateFunction): void {
this.navigateHook = navigateHook;
}
public static setLocation(location: Location): void {
this.location = location;
}
public static setParams(params: Params): void {
this.params = params;
}
public static getParams(): Params {
return this.params;
}
public static getCurrentPath(): Route {
return new Route(window.location.pathname);
}
public static getBreadcrumbRoute(level: number): Route {
const paths: Array<string> = this.location.pathname.split("/");
// +2 because we want to include the first 2 paths which are empty, dashboard and project id
const indexToSplice: number = level + 2;
return new Route(paths.splice(0, indexToSplice).join("/"));
}
public static getRoutePath(routes: Array<{ path: string }>): string {
const pathes: AgnosticRouteMatch[] | null = matchRoutes(
routes,
this.location.pathname,
);
return pathes?.[0]?.route.path || "";
}
public static getQueryStringByName(paramName: string): string | null {
const urlSearchParams: URLSearchParams = new URLSearchParams(
window.location.search,
);
const params: Dictionary<string> = Object.fromEntries(
urlSearchParams.entries(),
);
if (params && params[paramName]) {
return params[paramName] as string;
}
return null;
}
public static getParamByName(
paramName: string,
routeTemplate: Route,
): string | null {
const currentPath: Array<string> = this.location.pathname.split("/");
if (!paramName.startsWith(":")) {
paramName = ":" + paramName;
}
const routeParamTemplateIndex: number = routeTemplate
.toString()
.split("/")
.indexOf(paramName);
if (routeParamTemplateIndex === -1) {
throw new BadDataException(
`Param ${paramName} not found in template ${routeTemplate.toString()}`,
);
}
if (currentPath[routeParamTemplateIndex]) {
return currentPath[routeParamTemplateIndex] as string;
}
return null;
}
public static getLastParam(getFromLastRoute?: number): Route | null {
return URL.fromString(window.location.href).getLastRoute(getFromLastRoute);
}
public static getFirstParam(getFromFirstRoute?: number): string | undefined {
const pathname: string = window.location.pathname;
return pathname.split("/")[getFromFirstRoute || 1];
}
public static getLastParamAsString(getFromLastRoute?: number): string {
const param: Route | null = URL.fromString(
window.location.href,
).getLastRoute(getFromLastRoute);
return param?.toString().replace("/", "") || "";
}
public static getLastParamAsObjectID(getFromLastRoute?: number): ObjectID {
return new ObjectID(this.getLastParamAsString(getFromLastRoute));
}
public static getCurrentRoute(): Route {
return new Route(this.location.pathname);
}
public static getHostname(): Hostname {
return new Hostname(window.location.hostname);
}
public static getCurrentURL(): URL {
return URL.fromString(window.location.href);
}
public static reload(): void {
window.location.reload();
}
public static containsInPath(text: string): boolean {
return window.location.pathname.includes(text);
}
public static isStartWith(route: Route): boolean {
const current: Route = this.getCurrentRoute();
const routeItems: Array<string> = route.toString().split("/");
let anotherRouteItems: Array<string> = current.toString().split("/");
if (routeItems.length > anotherRouteItems.length) {
return false;
}
anotherRouteItems = anotherRouteItems.splice(0, routeItems.length);
let start: number = 0;
let startsWith: boolean = true;
for (const item of anotherRouteItems) {
if (routeItems[start]?.startsWith(":") && item) {
start++;
continue;
}
if (routeItems[start]?.toString() !== item.toString()) {
startsWith = false;
break;
}
start++;
}
return startsWith;
}
public static isOnThisPage(route: Route | URL): boolean {
if (route instanceof Route) {
const current: Route = this.getCurrentRoute();
let isOnThisPage: boolean = true;
const routeItems: Array<string> = route.toString().split("/");
const currentPathItems: Array<string> = current.toString().split("/");
if (routeItems.length !== currentPathItems.length) {
return false;
}
let start: number = 0;
for (const item of currentPathItems) {
if (routeItems[start]?.startsWith(":") && item) {
start++;
continue;
}
if (routeItems[start]?.toString() !== item.toString()) {
isOnThisPage = false;
break;
}
start++;
}
return isOnThisPage;
}
if (route instanceof URL) {
const current: URL = this.getCurrentURL();
if (current.toString() === route.toString()) {
return true;
}
return false;
}
return false;
}
public static goBack(): void {
this.navigateHook(-1);
}
public static navigate(
to: Route | URL,
options?: {
openInNewTab?: boolean | undefined;
forceNavigate?: boolean | undefined;
},
): void {
if (options?.openInNewTab) {
// open in new tab
window.open(to.toString(), "_blank");
return;
}
if (options?.forceNavigate && to instanceof Route) {
window.location.href = to.toString();
}
if (this.navigateHook && to instanceof Route && !this.isOnThisPage(to)) {
this.navigateHook(to.toString());
}
// if its an external link outside of react.
if (to instanceof URL) {
window.location.href = to.toString();
}
}
}
export default Navigation;