-
Notifications
You must be signed in to change notification settings - Fork 11
Expand file tree
/
Copy pathRouterModel.ts
More file actions
110 lines (97 loc) · 3.44 KB
/
Copy pathRouterModel.ts
File metadata and controls
110 lines (97 loc) · 3.44 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
/*
* This file belongs to Hoist, an application development toolkit
* developed by Extremely Heavy Industries (www.xh.io | info@xh.io)
*
* Copyright © 2026 Extremely Heavy Industries Inc.
*/
import {HoistModel} from '../core';
import {action, observable, makeObservable} from '@xh/hoist/mobx';
import {mergeDeep} from '@xh/hoist/utils/js';
import {isOmitted} from '@xh/hoist/utils/impl';
import {createRouter, Router, State} from 'router5';
import browserPlugin from 'router5-plugin-browser';
// Required so downstream consumers pick up Router TS augmentation (buildUrl, etc.).
import 'router5-plugin-browser';
/**
* Top-level model for managing application routing in Hoist.
*
* This observable model uses Router5 (https://router5.js.org/) to manage the
* underlying routes, presenting them to the application as a set of MobX observables.
*/
export class RouterModel extends HoistModel {
/** Router5 state object representing the current state. */
@observable.ref
currentState: State;
/** Underlying Router5 Router object implementing the routing state. */
router: Router = this.createRouter();
/**
* Does the routing system already have a given route?
*/
hasRoute(routeName: string): boolean {
const flatNames = this.getRouteNames(this.router.rootNode);
return flatNames.includes(routeName);
}
/**
* Add routes to the router.
*
* @param routes - collection of router5 route spec.
* This method supports an additional keyword 'omit' on each spec, in order to allow declarative
* exclusion. Otherwise these are Router5 configs to be passed directly to the Router5 API.
*/
addRoutes(routes: object[]) {
this.router.add(this.preprocessRoutes(routes));
}
/**
* Add a routeName to the current route, preserving params
* @param routeName - the routeName to append
* @param newParams - additional params for this routeName to be merged with existing params.
*/
appendRoute(routeName: string, newParams: object = {}) {
const {name, params} = this.currentState;
return this.router.navigate(`${name}.${routeName}`, mergeDeep({}, params, newParams));
}
/**
* Remove last routeName from the current route, preserving params
*/
popRoute() {
const {name, params} = this.currentState,
match = name.match(/.*(?=\.)/);
if (!match) return;
return this.router.navigate(match[0], params);
}
constructor() {
super();
makeObservable(this);
}
//-------------------------
// Implementation
//-------------------------
@action
private setCurrentState(state) {
this.currentState = state;
}
private getRouteNames(node) {
const name = node.name,
ret = [];
node.children.forEach(child => {
this.getRouteNames(child).forEach(it => {
ret.push(name ? name + '.' + it : it);
});
});
if (name) ret.push(name);
return ret;
}
private createRouter() {
const ret = createRouter();
ret.usePlugin(browserPlugin());
ret.subscribe(ev => this.setCurrentState(ev.route));
return ret;
}
private preprocessRoutes(routes) {
const ret = routes.filter(r => !isOmitted(r));
ret.forEach(r => {
if (r.children) r.children = this.preprocessRoutes(r.children);
});
return ret;
}
}