13 Commits

8 changed files with 7810 additions and 4446 deletions

View File

@@ -1,5 +1,50 @@
# Changelog
## 2025-07-25 - 1.3.3 - fix(dependencies)
Updated dependencies and improved documentation
- Updated devDependencies: @git.zone/tsbuild, @git.zone/tsbundle, @git.zone/tstest to latest versions
- Removed @push.rocks/tapbundle dependency and updated test imports to use tapbundle from tstest
- Updated dependencies: @push.rocks/lik and @push.rocks/smartrx to latest versions
- Added packageManager field to package.json for pnpm
- Enhanced documentation with new examples for state data, query parameters, sub-routers, and cleanup
- Added verbose flag to test script
## 2024-10-06 - 1.3.2 - fix(core)
Fix TypeScript type definition for route match function
- Updated the type definition for the matchFunction in the SmartRouter class to include a generic parameter.
## 2024-10-06 - 1.3.1 - fix(dependencies)
Updated dependencies to latest versions, resolving compatibility issues and improving performance.
- Updated devDependencies to their latest versions, including tsbuild, tsbundle, tstest, tapbundle, and @types/node
- Updated dependencies to latest versions of lik, smartrx, and path-to-regexp
## 2024-10-06 - 1.3.0 - feat(smartrouter)
Add destroy method to SmartRouter class.
- Introduced a destroy method to the SmartRouter class for cleaning up event listeners and route references.
- Refactored popstate event listener to be removable, improving resource management and preventing memory leaks.
## 2024-10-06 - 1.2.1 - fix(core)
Ensure stability and performance improvements
- Improves platform compatibility for modern web applications.
- Enhances stability of the routing logic within SmartRouter class.
## 2024-10-06 - 1.2.0 - feat(core)
Add support for base paths and sub-routers.
- Added basePath feature to SmartRouter for handling base paths.
- Introduced createSubRouter method to create a sub-router with a specific prefix.
## 2024-10-06 - 1.1.1 - fix(core)
Remove SelectionDimension functionality
- Removed SelectionDimension class and references
- Deleted smartrouter.classes.selectiondimension.ts and related imports
## 2024-10-06 - 1.1.0 - feat(core)
Add selection dimensions and route removal functionality

View File

@@ -1,6 +1,6 @@
{
"name": "@push.rocks/smartrouter",
"version": "1.1.0",
"version": "1.3.3",
"private": false,
"description": "A JavaScript library providing routing capabilities for web applications.",
"main": "dist_ts/index.js",
@@ -8,22 +8,21 @@
"author": "Lossless GmbH",
"license": "MIT",
"scripts": {
"test": "(tstest test/)",
"test": "(tstest test/ --verbose)",
"build": "(tsbuild --web && tsbundle npm --web)",
"format": "(gitzone format)",
"buildDocs": "tsdoc"
},
"devDependencies": {
"@git.zone/tsbuild": "^2.1.70",
"@git.zone/tsbundle": "^2.0.7",
"@git.zone/tstest": "^1.0.81",
"@push.rocks/tapbundle": "^5.0.15",
"@types/node": "^20.6.0"
"@git.zone/tsbuild": "^2.6.4",
"@git.zone/tsbundle": "^2.5.1",
"@git.zone/tstest": "^2.3.2",
"@types/node": "^22.7.4"
},
"dependencies": {
"@push.rocks/lik": "^6.0.5",
"@push.rocks/smartrx": "^3.0.6",
"path-to-regexp": "^6.2.0"
"@push.rocks/lik": "^6.2.2",
"@push.rocks/smartrx": "^3.0.10",
"path-to-regexp": "^8.2.0"
},
"files": [
"ts/**/*",
@@ -54,5 +53,6 @@
"repository": {
"type": "git",
"url": "https://code.foss.global/push.rocks/smartrouter.git"
}
},
"packageManager": "pnpm@10.11.0+sha512.6540583f41cc5f628eb3d9773ecee802f4f9ef9923cc45b69890fb47991d4b092964694ec3a4f738a420c918a333062c8b925d312f42e4f0c263eb603551f977"
}

11996
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,10 +1,10 @@
# @push.rocks/smartrouter
a router for routing on websites
A JavaScript library providing routing capabilities for web applications with support for path parameters, query parameters, and programmatic navigation
## Install
To install `@push.rocks/smartrouter`, run the following command in your project directory:
```sh
npm install @push.rocks/smartrouter --save
pnpm install @push.rocks/smartrouter --save
```
This will add `@push.rocks/smartrouter` to your project's dependencies and enable you to use it within your application.
@@ -73,45 +73,70 @@ router.pushUrl('/about');
// Navigate to a user profile with URL parameters
router.pushUrl('/user/12345');
// Navigate with state data
router.pushUrl('/dashboard', { previousPage: 'home' });
```
### Managing Query Parameters
`@push.rocks/smartrouter` provides methods for managing URL query parameters, enabling dynamic URL manipulation for filter settings, pagination, and other use cases.
```typescript
// Set a query parameter
// Set a query parameter (replace by default)
router.queryParams.setQueryParam('key', 'value');
// Set a query parameter with push (adds to history)
router.queryParams.setQueryParam('key', 'value', 'push');
// Get a query parameter
const value = router.queryParams.getQueryParam('key');
// Get all query parameters as an object
const allParams = router.queryParams.getAllAsObject();
// Delete a query parameter
router.queryParams.deleteQueryParam('key');
// Delete with push to history
router.queryParams.deleteQueryParam('key', 'push');
```
### Selection Dimensions
`@push.rocks/smartrouter` introduces the concept of selection dimensions, allowing you to manage stateful selections across routes. This is especially useful for complex navigation flows that depend on prior selections.
### Sub-Routers
Create sub-routers with a specific base path for modular routing:
```typescript
await router.createSelectionDimension({
routeArg: '/select/:option',
keyArg: 'mySelection',
options: [
{
key: 'option1',
detail: { /* some data */ },
action: async () => { /* action for option1 */ }
},
{
key: 'option2',
detail: { /* some data */ },
action: async () => { /* action for option2 */ }
}
]
// Create a sub-router for admin routes
const adminRouter = router.createSubRouter('/admin');
// Routes will be prefixed with /admin
adminRouter.on('/dashboard', async (routeInfo) => {
// This handles /admin/dashboard
});
// Navigate to a selection option
router.pushUrl('/select/option1');
adminRouter.on('/users', async (routeInfo) => {
// This handles /admin/users
});
```
### Route Management
The `on` method returns a function that can be used to remove the route:
```typescript
// Add a route
const removeRoute = router.on('/temporary', async (routeInfo) => {
console.log('Temporary route accessed');
});
// Later, remove the route
removeRoute();
```
### Cleanup
When you're done with a router instance, clean it up properly:
```typescript
// Destroy the router, removing all event listeners and routes
router.destroy();
```
This module enables complex routing scenarios, simplifying the handling of navigational logic in modern web applications. By leveraging `@push.rocks/smartrouter`, developers can implement detailed routing mechanisms, manipulate browser history thoughtfully, and maintain cleaner URL structures, enhancing the user experience and making web apps more accessible.

View File

@@ -1,4 +1,4 @@
import { expect, expectAsync, tap } from '@push.rocks/tapbundle';
import { expect, tap } from '@git.zone/tstest/tapbundle';
import * as smartrouter from '../ts/index.js';
let testrouter: smartrouter.SmartRouter;

View File

@@ -3,6 +3,6 @@
*/
export const commitinfo = {
name: '@push.rocks/smartrouter',
version: '1.1.0',
version: '1.3.2',
description: 'A JavaScript library providing routing capabilities for web applications.'
}

View File

@@ -1,50 +0,0 @@
import * as plugins from './smartrouter.plugins.js';
export interface ISelectionOption<T = any> {
key: string;
detail: T;
selected?: boolean;
action: () => Promise<T>;
}
export class SelectionDimension<T = any> {
/**
* the key of the selection dimension
*/
public dimensionKey: string;
/**
* the level of the selection dimension
* a higher level is execution later than a lower level
* during catchup phase
*/
public dimensionLevel: number;
public dimensionPeers: SelectionDimension<T>[] = [];
public selectionOptions: ISelectionOption<T>[] = [];
public selectionOptionSubject = new plugins.smartrx.rxjs.Subject<ISelectionOption<T>[]>();
public async emitSelectionOptions () {
const selectionOptions = this.selectionOptions.map((selectionOptionArg): ISelectionOption => {
return {
key: selectionOptionArg.key,
detail: selectionOptionArg.detail,
action: async () => {
this.updateSelection(selectionOptionArg);
return await selectionOptionArg.action();
},
}
});
this.selectionOptionSubject.next(selectionOptions);
}
public async updateSelection (selectionOptionArg: ISelectionOption<T>) {
for (const selectionOption of this.selectionOptions) {
if (selectionOption.key === selectionOptionArg.key) {
selectionOption.selected = true;
} else {
selectionOption.selected = false;
}
}
this.emitSelectionOptions();
}
}

View File

@@ -1,7 +1,6 @@
import * as plugins from './smartrouter.plugins.js';
import { QueryParams } from './smartrouter.classes.queryparams.js';
import { type ISelectionOption, SelectionDimension } from './smartrouter.classes.selectiondimension.js';
const routeLog = (message: string) => {
console.log(`%c[Router]%c ${message}`, 'color: rgb(255, 105, 100);', 'color: inherit');
@@ -33,35 +32,58 @@ export class SmartRouter {
* the routes we are handling
*/
public routes: Array<{
matchFunction: plugins.pathToRegExp.MatchFunction;
matchFunction: plugins.pathToRegExp.MatchFunction<any>;
handler: THandlerFunction;
}> = [];
/**
* base path for this router
*/
private basePath: string;
/**
* Reference to the event listener function for cleanup
*/
private popstateListener: (event: PopStateEvent) => void;
/**
* Creates an instance of Router.
*/
constructor(optionsArg: IRouterOptions) {
constructor(optionsArg: IRouterOptions, basePath: string = '') {
// lets set the router options
this.options = {
...this.options,
...optionsArg,
};
this.basePath = basePath;
// lets subscribe to route changes
window.addEventListener('popstate', (popStateEventArg) => {
this.popstateListener = (popStateEventArg) => {
popStateEventArg.preventDefault();
this._handleRouteState();
});
};
window.addEventListener('popstate', this.popstateListener);
}
/**
* Create a sub-router with a specific prefix
* @param {string} subPath
* @param {IRouterOptions} [options]
*/
public createSubRouter(subPath: string, options?: IRouterOptions): SmartRouter {
const newBasePath = `${this.basePath}${subPath}`;
return new SmartRouter({ ...this.options, ...options }, newBasePath);
}
/**
* Push route state to history stack
*/
public async pushUrl(url: string = '/', state: any = {}) {
if (url !== window.location.pathname) {
window.history.pushState(state, window.document.title, url);
const fullUrl = `${this.basePath}${url}`;
if (fullUrl !== window.location.pathname) {
window.history.pushState(state, window.document.title, fullUrl);
} else {
window.history.replaceState(state, window.document.title, url);
window.history.replaceState(state, window.document.title, fullUrl);
}
await this._handleRouteState();
}
@@ -72,8 +94,9 @@ export class SmartRouter {
* @param {function} handlerArg
*/
public on(routeArg: string, handlerArg: THandlerFunction) {
const fullRoute = `${this.basePath}${routeArg}`;
const routeObject = {
matchFunction: plugins.pathToRegExp.match(routeArg),
matchFunction: plugins.pathToRegExp.match(fullRoute),
handler: handlerArg,
};
this.routes.push(routeObject);
@@ -103,18 +126,13 @@ export class SmartRouter {
}
}
public selectionDimensionsMap = new plugins.lik.ObjectMap<SelectionDimension>();
public async createSelectionDimension(optionsArg: {
routeArg: string,
keyArg: string,
options: ISelectionOption[],
peers?: SelectionDimension[],
}) {
const selectionDimension = new SelectionDimension();
selectionDimension.dimensionKey = optionsArg.keyArg;
selectionDimension.dimensionPeers = optionsArg.peers;
selectionDimension.selectionOptions = optionsArg.options;
await this.selectionDimensionsMap.findOneAndRemove(async dimensionArg => dimensionArg.dimensionKey === optionsArg.keyArg);
this.selectionDimensionsMap.addMappedUnique(optionsArg.keyArg, selectionDimension);
/**
* Destroy the router instance, removing all external references
*/
public destroy() {
// Remove the event listener for popstate
window.removeEventListener('popstate', this.popstateListener);
// Clear all routes
this.routes = [];
}
}