@wroud/navigation
Version:
A flexible, pattern-matching navigation system for JavaScript applications with built-in routing, browser integration, and navigation state management
200 lines (147 loc) • 5.57 kB
Markdown
# @wroud/navigation
[![ESM-only package][package]][esm-info-url]
[![NPM version][npm]][npm-url]
[package]: https://img.shields.io/badge/package-ESM--only-ffe536.svg
[esm-info-url]: https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c
[npm]: https://img.shields.io/npm/v/@wroud/navigation.svg
[npm-url]: https://npmjs.com/package/@wroud/navigation
@wroud/navigation is a flexible, pattern-matching navigation system for JavaScript applications. It provides a framework-agnostic routing solution with powerful pattern matching capabilities, browser integration, and navigation state management.
## Features
- **Pattern-based Routing**: Built-in support for static routes, parameter segments (`/users/:id`), and wildcard patterns (`/files/:path*`).
- **Framework Agnostic**: Works with any JavaScript framework or vanilla JS.
- **Type Safety**: Written in TypeScript with full type inference for route parameters.
- **Navigation History**: Built-in navigation history management.
- **Browser Integration**: Optional browser URL synchronization.
- **Extensible**: Create custom navigation implementations for any environment.
- [Pure ESM package][esm-info-url]
## Installation
Install via npm:
```sh
npm install @wroud/navigation
```
Install via yarn:
```sh
yarn add @wroud/navigation
```
## Documentation
For detailed usage and API reference, visit the [documentation site](https://wroud.dev).
## Examples
### Basic Usage
```ts
import { Navigation, Router } from "@wroud/navigation";
// Create a router with default settings
const router = new Router();
// Add routes
router.addRoute({ id: "/" });
router.addRoute({ id: "/users" });
router.addRoute({ id: "/users/:id" });
// Create a navigation instance with the router
const navigation = new Navigation(router);
// Navigate to a route
await navigation.navigate({ id: "/users/:id", params: { id: "123" } });
// Get current route state
const currentState = navigation.getState();
console.log(currentState); // { id: "/users/:id", params: { id: "123" } }
// Go back to previous route
await navigation.goBack();
```
### Using Pattern Matching
```ts
import { Navigation, Router, TriePatternMatching } from "@wroud/navigation";
// Create a router with pattern matching
const router = new Router({
matcher: new TriePatternMatching({ trailingSlash: false })
});
// Add routes
router.addRoute({ id: "/" });
router.addRoute({ id: "/app" });
router.addRoute({ id: "/app/users" });
router.addRoute({ id: "/app/users/:id" });
// Create a navigation instance
const navigation = new Navigation(router);
// Match a URL to a route
const match = router.matchUrl("/app/users/123");
console.log(match); // { id: "/app/users/:id", params: { id: "123" } }
// Build a URL from a route and parameters
const url = router.buildUrl("/app/users/:id", { id: "456" });
console.log(url); // "/app/users/456"
// Navigate using the pattern matching
await navigation.navigate(router.matchUrl("/app/users/789"));
```
### Browser Integration
```ts
import { Navigation, Router, TriePatternMatching } from "@wroud/navigation";
import { BrowserNavigation } from "@wroud/navigation/browser";
// Create a router with pattern matching
const router = new Router({
matcher: new TriePatternMatching({
trailingSlash: true,
base: '/app' // Base path for the application
})
});
// Add routes
router.addRoute({ id: "/" });
router.addRoute({ id: "/products" });
router.addRoute({ id: "/products/:id" });
router.addRoute({ id: "/blog/:year/:month/:slug" });
// Create a navigation instance
const navigation = new Navigation(router);
// Add a navigation listener for all navigation events
// including the initial navigation
const unsubscribe = navigation.addListener((type, from, to) => {
if (type === "navigate" && !from) {
console.log("Initial navigation:", to);
} else {
console.log(`Navigation: ${type}`, { from, to });
}
});
// Create a browser navigation instance
const browserNavigation = new BrowserNavigation(navigation);
// Initialize browser navigation by registering routes
// This will sync with the browser's URL and history
await browserNavigation.registerRoutes();
// Navigate - this will update the browser URL
await navigation.navigate({
id: "/products/:id",
params: { id: "123" }
});
// Browser URL is now /app/products/123
// Later, remove the listener
unsubscribe();
```
### Protected Routes with Navigation Guards
```ts
import { Navigation, Router } from "@wroud/navigation";
// Create a router
const router = new Router();
// Add routes with navigation guards
router.addRoute({
id: "/",
});
router.addRoute({
id: "/dashboard",
// Check if user is authenticated before activating this route
canActivate: (to, from) => {
return isAuthenticated(); // Your authentication check
}
});
router.addRoute({
id: "/editor/:documentId",
// Check if user has permission to edit this document
canActivate: async (to, from) => {
if (!to.params.documentId) return false;
return await hasEditPermission(to.params.documentId);
},
// Ask for confirmation before navigating away
canDeactivate: (to, from) => {
return confirm("Discard unsaved changes?");
}
});
const navigation = new Navigation(router);
// This will fail if not authenticated
await navigation.navigate({ id: "/dashboard", params: {} });
```
## Changelog
All notable changes to this project will be documented in the [CHANGELOG](./CHANGELOG.md) file.
## License
This project is licensed under the MIT License. See the [LICENSE](./LICENSE) file for details.