@wroud/navigation
Version:
A flexible, pattern-matching navigation system for JavaScript applications with built-in routing, browser integration, and navigation state management
130 lines • 3.64 kB
JavaScript
import {} from "./IRoute.js";
export class Router {
get routesList() {
return Array.from(this.routes.values());
}
routes;
matcher;
constructor(options = {}) {
this.routes = new Map();
this.matcher = options.matcher || null;
}
/**
* Add a route to the router
*/
addRoute(route) {
if (!route.id) {
throw new Error("Route ID is required");
}
if (this.routes.has(route.id)) {
throw new Error(`Route ${route.id} already exists`);
}
// If a matcher is provided, register the pattern and use it for finding parents
if (this.matcher) {
this.matcher.addPattern(route.id);
}
this.routes.set(route.id, {
...route,
parents: this.getParentsForRoute(route.id),
});
}
/**
* Get a route's parent
*/
getParentRoute(routeId) {
const route = this.routes.get(routeId);
if (!route || route.parents.length === 0) {
return undefined;
}
const parentId = route.parents[route.parents.length - 1];
return parentId ? this.routes.get(parentId) : undefined;
}
/**
* Get the full route hierarchy for a route
*/
getRouteTree(routeId) {
const tree = [];
const route = this.routes.get(routeId);
if (!route) {
throw new Error(`Route ${routeId} not found`);
}
for (const parentId of route.parents) {
const parentRoute = this.routes.get(parentId);
if (parentRoute) {
tree.push(parentRoute);
}
}
tree.push(route);
return tree;
}
/**
* Get a route by ID
*/
getRoute(routeId) {
return this.routes.get(routeId);
}
/**
* Match a URL to a route
*/
matchUrl(url) {
if (!this.matcher) {
return null;
}
return this.matcher.match(url);
}
/**
* Build a URL from a route ID and parameters
*/
buildUrl(routeId, params) {
if (!this.matcher || !this.routes.has(routeId)) {
return null;
}
const state = {
id: routeId,
params: params,
};
// Special case for root path
if (routeId === "/") {
return "/";
}
return this.matcher.stateToUrl(state);
}
/**
* Convert a route state to a URL
*/
stateToUrl(state) {
if (!this.matcher) {
return null;
}
return this.matcher.stateToUrl(state);
}
/**
* Convert a URL to a route state
*/
urlToState(url) {
if (!this.matcher) {
return null;
}
return this.matcher.urlToState(url);
}
/**
* Calculates parent route IDs for a given route ID
*/
getParentsForRoute(id) {
// If a pattern matcher is available, use it for parents
if (this.matcher) {
const ancestors = this.matcher.getPatternAncestors(id);
// Find routes that exist for these ancestors
const existingAncestors = ancestors.filter((ancestor) => this.routes.has(ancestor));
// Always include the root path first if it exists in routes and isn't already included
if (this.routes.has("/") &&
id !== "/" &&
!existingAncestors.includes("/")) {
existingAncestors.unshift("/");
}
return existingAncestors;
}
return [];
}
}
//# sourceMappingURL=Router.js.map