application.ts 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +358 -0
- package/dist/app/App.d.ts +124 -0
- package/dist/app/App.js +326 -0
- package/dist/app/App.js.map +1 -0
- package/dist/app/AppView.d.ts +143 -0
- package/dist/app/AppView.js +264 -0
- package/dist/app/AppView.js.map +1 -0
- package/dist/app/types.d.ts +67 -0
- package/dist/app/types.js +2 -0
- package/dist/app/types.js.map +1 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.js +10 -0
- package/dist/index.js.map +1 -0
- package/dist/navigation/route.d.ts +34 -0
- package/dist/navigation/route.js +68 -0
- package/dist/navigation/route.js.map +1 -0
- package/dist/navigation/router.d.ts +92 -0
- package/dist/navigation/router.js +246 -0
- package/dist/navigation/router.js.map +1 -0
- package/dist/navigation/types.d.ts +63 -0
- package/dist/navigation/types.js +9 -0
- package/dist/navigation/types.js.map +1 -0
- package/package.json +68 -0
- package/src/app/App.ts +391 -0
- package/src/app/AppView.ts +333 -0
- package/src/app/types.ts +78 -0
- package/src/index.ts +28 -0
- package/src/navigation/route.ts +87 -0
- package/src/navigation/router.ts +284 -0
- package/src/navigation/types.ts +73 -0
|
@@ -0,0 +1,264 @@
|
|
|
1
|
+
import { TemplateBinder } from 'template.ts';
|
|
2
|
+
import { App } from './App';
|
|
3
|
+
/**
|
|
4
|
+
* Abstract base class for creating views with Template.Ts
|
|
5
|
+
* Extends HTMLElement as a Web Component for seamless integration with StackView.Ts
|
|
6
|
+
* Custom elements are automatically registered when views are registered with App
|
|
7
|
+
*
|
|
8
|
+
* @example
|
|
9
|
+
* ```typescript
|
|
10
|
+
* const template: `
|
|
11
|
+
* <div>
|
|
12
|
+
* <h1>Count: {{ count }}</h1>
|
|
13
|
+
* <button @on:click="increment">Increment</button>
|
|
14
|
+
* </div>`;
|
|
15
|
+
*
|
|
16
|
+
* class State {
|
|
17
|
+
* count: number = 0;
|
|
18
|
+
* increment: () => { this.count += 1; };
|
|
19
|
+
* }
|
|
20
|
+
*
|
|
21
|
+
* @Register
|
|
22
|
+
* class HomeView extends AppView<{ count: number }> {
|
|
23
|
+
* template(): string {
|
|
24
|
+
* return template;
|
|
25
|
+
* }
|
|
26
|
+
*
|
|
27
|
+
* state() {
|
|
28
|
+
* return new State();
|
|
29
|
+
* }
|
|
30
|
+
* }
|
|
31
|
+
*
|
|
32
|
+
* // Register with app - automatically registers as <home-view>
|
|
33
|
+
* app.registerView('HomeView', HomeView);
|
|
34
|
+
* ```
|
|
35
|
+
*/
|
|
36
|
+
export class AppView extends HTMLElement {
|
|
37
|
+
constructor(options) {
|
|
38
|
+
super();
|
|
39
|
+
this.binder = null;
|
|
40
|
+
this._state = null;
|
|
41
|
+
this._isInitialized = false;
|
|
42
|
+
this._options = {
|
|
43
|
+
transitionClass: 'transition-fade',
|
|
44
|
+
autoUpdate: true,
|
|
45
|
+
useShadowDOM: false,
|
|
46
|
+
...options
|
|
47
|
+
};
|
|
48
|
+
// Create shadow root if enabled
|
|
49
|
+
if (this._options.useShadowDOM) {
|
|
50
|
+
const shadow = this.attachShadow({ mode: 'open' });
|
|
51
|
+
shadow.innerHTML = this.template();
|
|
52
|
+
if (!shadow.firstElementChild || !(shadow.firstElementChild instanceof HTMLElement)) {
|
|
53
|
+
throw new Error('AppView template must have a single root element');
|
|
54
|
+
}
|
|
55
|
+
this._root = shadow.firstElementChild;
|
|
56
|
+
}
|
|
57
|
+
else {
|
|
58
|
+
this.innerHTML = this.template();
|
|
59
|
+
if (!this.firstElementChild || !(this.firstElementChild instanceof HTMLElement)) {
|
|
60
|
+
throw new Error('AppView template must have a single root element');
|
|
61
|
+
}
|
|
62
|
+
this._root = this.firstElementChild;
|
|
63
|
+
}
|
|
64
|
+
// Eager initialization in constructor
|
|
65
|
+
this.initialize();
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Render the view with route parameters
|
|
69
|
+
* Initializes the template and binds state
|
|
70
|
+
* @param params - Route parameters from the router
|
|
71
|
+
*/
|
|
72
|
+
initialize() {
|
|
73
|
+
if (this._isInitialized) {
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
// Add app-view class
|
|
77
|
+
this.classList.add('app-view');
|
|
78
|
+
// Initialize state
|
|
79
|
+
this._state = this.state();
|
|
80
|
+
// Bind template using container
|
|
81
|
+
this.binder = new TemplateBinder(this._root, this._state, this._options.transitionClass);
|
|
82
|
+
this.binder.autoUpdate = this._options.autoUpdate ?? true;
|
|
83
|
+
this.binder.bind();
|
|
84
|
+
this._isInitialized = true;
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Get the custom element tag name for this class
|
|
88
|
+
* Converts class name to kebab-case (e.g., HomeView -> home-view)
|
|
89
|
+
*/
|
|
90
|
+
static getTagName() {
|
|
91
|
+
return this.name
|
|
92
|
+
.replace(/([a-z0-9])([A-Z])/g, '$1-$2')
|
|
93
|
+
.toLowerCase();
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Register this class as a custom element
|
|
97
|
+
* Should be called before instantiation
|
|
98
|
+
*/
|
|
99
|
+
static register() {
|
|
100
|
+
const tagName = this.getTagName();
|
|
101
|
+
if (!customElements.get(tagName)) {
|
|
102
|
+
customElements.define(tagName, this);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Get the current state
|
|
107
|
+
*/
|
|
108
|
+
get viewState() {
|
|
109
|
+
if (!this._state) {
|
|
110
|
+
throw new Error('State not initialized. Call render() first.');
|
|
111
|
+
}
|
|
112
|
+
return this._state;
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Get the route parameters passed to this view
|
|
116
|
+
*/
|
|
117
|
+
get params() {
|
|
118
|
+
return this.app?.router?.currentParams || {};
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Get the App instance by traversing up the DOM tree
|
|
122
|
+
*/
|
|
123
|
+
get app() {
|
|
124
|
+
return App.fromElement(this);
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Navigate to a specific path using the app's router
|
|
128
|
+
* @param path - The path to navigate to
|
|
129
|
+
*/
|
|
130
|
+
navigate(path) {
|
|
131
|
+
const appInstance = this.app;
|
|
132
|
+
if (appInstance) {
|
|
133
|
+
appInstance.navigate(path);
|
|
134
|
+
}
|
|
135
|
+
else {
|
|
136
|
+
console.warn('Cannot navigate: App instance not found');
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* Update a single state value
|
|
141
|
+
* @param key - The state key to update
|
|
142
|
+
* @param value - The new value
|
|
143
|
+
*/
|
|
144
|
+
setState(key, value) {
|
|
145
|
+
if (!this._state) {
|
|
146
|
+
console.warn('Cannot set state before initialization');
|
|
147
|
+
return;
|
|
148
|
+
}
|
|
149
|
+
this._state[key] = value;
|
|
150
|
+
// Call lifecycle hook
|
|
151
|
+
if (this.onStateChanged) {
|
|
152
|
+
this.onStateChanged(key, value);
|
|
153
|
+
}
|
|
154
|
+
// Auto-update if enabled
|
|
155
|
+
if (this._options.autoUpdate && this.binder) {
|
|
156
|
+
this.update();
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* Update multiple state values at once
|
|
161
|
+
* @param updates - Object with state updates
|
|
162
|
+
*/
|
|
163
|
+
setStates(updates) {
|
|
164
|
+
if (!this._state) {
|
|
165
|
+
console.warn('Cannot set state before initialization');
|
|
166
|
+
return;
|
|
167
|
+
}
|
|
168
|
+
Object.assign(this._state, updates);
|
|
169
|
+
// Call lifecycle hooks for each update
|
|
170
|
+
if (this.onStateChanged) {
|
|
171
|
+
for (const [key, value] of Object.entries(updates)) {
|
|
172
|
+
this.onStateChanged(key, value);
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
// Auto-update if enabled
|
|
176
|
+
if (this._options.autoUpdate && this.binder) {
|
|
177
|
+
this.update();
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
/**
|
|
181
|
+
* Manually trigger a view update
|
|
182
|
+
* @param withAnimation - Whether to apply transition animation
|
|
183
|
+
*/
|
|
184
|
+
update(withAnimation = true) {
|
|
185
|
+
if (this.binder) {
|
|
186
|
+
this.binder.update(withAnimation);
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
/**
|
|
190
|
+
* Update route parameters and re-trigger initialization logic
|
|
191
|
+
* Used when navigating to the same view with different parameters
|
|
192
|
+
* @param params - New route parameters
|
|
193
|
+
*/
|
|
194
|
+
async updateParams(params = {}) {
|
|
195
|
+
// Call the parameter changed hook with old and new params
|
|
196
|
+
if (this.onParamsChanged) {
|
|
197
|
+
await this.onParamsChanged(params, this.params);
|
|
198
|
+
}
|
|
199
|
+
// Update the view if needed
|
|
200
|
+
if (this.binder && this._options.autoUpdate) {
|
|
201
|
+
this.update();
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
/**
|
|
205
|
+
* StackView lifecycle: called when view is about to be shown
|
|
206
|
+
*/
|
|
207
|
+
async stackViewShowing() {
|
|
208
|
+
// Call before mount hook
|
|
209
|
+
if (this.onBeforeMount) {
|
|
210
|
+
await this.onBeforeMount();
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
/**
|
|
214
|
+
* StackView lifecycle: called when view is about to be hidden
|
|
215
|
+
*/
|
|
216
|
+
async stackViewHiding() {
|
|
217
|
+
if (this.onBeforeUnmount) {
|
|
218
|
+
await this.onBeforeUnmount();
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
/**
|
|
222
|
+
* StackView lifecycle: called after view is hidden
|
|
223
|
+
*/
|
|
224
|
+
async stackViewHidden() {
|
|
225
|
+
// Destroy binder
|
|
226
|
+
if (this.binder) {
|
|
227
|
+
this.binder.destroy();
|
|
228
|
+
this.binder = null;
|
|
229
|
+
}
|
|
230
|
+
this._isInitialized = false;
|
|
231
|
+
if (this.onUnmounted) {
|
|
232
|
+
await this.onUnmounted();
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
/**
|
|
236
|
+
* Web Component lifecycle: called when connected to DOM
|
|
237
|
+
*/
|
|
238
|
+
async connectedCallback() {
|
|
239
|
+
// Initialize if not already done
|
|
240
|
+
if (!this._isInitialized) {
|
|
241
|
+
await this.initialize();
|
|
242
|
+
}
|
|
243
|
+
if (this.onMounted) {
|
|
244
|
+
await this.onMounted();
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
/**
|
|
248
|
+
* Web Component lifecycle: called when disconnected from DOM
|
|
249
|
+
*/
|
|
250
|
+
disconnectedCallback() {
|
|
251
|
+
// Cleanup is handled by stackViewHidden
|
|
252
|
+
}
|
|
253
|
+
/**
|
|
254
|
+
* Check if the view is initialized
|
|
255
|
+
*/
|
|
256
|
+
get isInitialized() {
|
|
257
|
+
return this._isInitialized;
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
export function Register(target) {
|
|
261
|
+
target.register();
|
|
262
|
+
return target;
|
|
263
|
+
}
|
|
264
|
+
//# sourceMappingURL=AppView.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"AppView.js","sourceRoot":"","sources":["../../src/app/AppView.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import type { RouteParams } from '../navigation/types';
|
|
2
|
+
/**
|
|
3
|
+
* AppView lifecycle hooks
|
|
4
|
+
*/
|
|
5
|
+
export interface AppViewLifecycle {
|
|
6
|
+
/**
|
|
7
|
+
* Called before the view is shown
|
|
8
|
+
* Use this to initialize data, fetch from APIs, etc.
|
|
9
|
+
*/
|
|
10
|
+
onBeforeMount?(): void | Promise<void>;
|
|
11
|
+
/**
|
|
12
|
+
* Called after the view is mounted to the DOM
|
|
13
|
+
* Use this to set up event listeners, third-party libraries, etc.
|
|
14
|
+
*/
|
|
15
|
+
onMounted?(): void | Promise<void>;
|
|
16
|
+
/**
|
|
17
|
+
* Called before the view is unmounted
|
|
18
|
+
* Use this to clean up resources, save state, etc.
|
|
19
|
+
*/
|
|
20
|
+
onBeforeUnmount?(): void | Promise<void>;
|
|
21
|
+
/**
|
|
22
|
+
* Called after the view is unmounted from the DOM
|
|
23
|
+
* Final cleanup
|
|
24
|
+
*/
|
|
25
|
+
onUnmounted?(): void | Promise<void>;
|
|
26
|
+
/**
|
|
27
|
+
* Called when the state is updated
|
|
28
|
+
* Use this to react to state changes
|
|
29
|
+
*/
|
|
30
|
+
onStateChanged?(key: string, value: any): void;
|
|
31
|
+
/**
|
|
32
|
+
* Called when route parameters change while staying on the same view
|
|
33
|
+
* Use this to reload data based on new parameters (e.g., /user/1 -> /user/2)
|
|
34
|
+
* @param newParams - The new route parameters
|
|
35
|
+
* @param oldParams - The previous route parameters
|
|
36
|
+
*/
|
|
37
|
+
onParamsChanged?(newParams: RouteParams, oldParams: RouteParams): void | Promise<void>;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* AppView configuration options
|
|
41
|
+
*/
|
|
42
|
+
export interface AppViewOptions {
|
|
43
|
+
/**
|
|
44
|
+
* Custom transition class for Template.Ts animations
|
|
45
|
+
* Default: 'transition-fade'
|
|
46
|
+
*/
|
|
47
|
+
transitionClass?: string;
|
|
48
|
+
/**
|
|
49
|
+
* Whether to automatically update the view on state changes
|
|
50
|
+
* Default: true
|
|
51
|
+
*/
|
|
52
|
+
autoUpdate?: boolean;
|
|
53
|
+
/**
|
|
54
|
+
* Whether to use Shadow DOM for the template
|
|
55
|
+
* When true, template is rendered in shadow root (enables <slot> to work properly)
|
|
56
|
+
* Default: false
|
|
57
|
+
*/
|
|
58
|
+
useShadowDOM?: boolean;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* AppView state object - can be any shape defined by the developer
|
|
62
|
+
*/
|
|
63
|
+
export type AppViewState = Record<string, any>;
|
|
64
|
+
/**
|
|
65
|
+
* Template function that returns HTML string
|
|
66
|
+
*/
|
|
67
|
+
export type TemplateFunction<T extends AppViewState = AppViewState> = (this: T) => string;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/app/types.ts"],"names":[],"mappings":""}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Main entry point for the application
|
|
3
|
+
* Export all public APIs
|
|
4
|
+
*/
|
|
5
|
+
export { App } from './app/App';
|
|
6
|
+
export { AppView, Register } from './app/AppView';
|
|
7
|
+
export { Router } from './navigation/router';
|
|
8
|
+
export { Route } from './navigation/route';
|
|
9
|
+
export type { AppViewLifecycle, AppViewOptions, AppViewState, TemplateFunction } from './app/types';
|
|
10
|
+
export type { RouteParams, RouteGuard, RouteOptions, RouteHandler, RouteDefinition, NavigationEventDetail } from './navigation/types';
|
|
11
|
+
export { NavigationEvents } from './navigation/types';
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Main entry point for the application
|
|
3
|
+
* Export all public APIs
|
|
4
|
+
*/
|
|
5
|
+
export { App } from './app/App';
|
|
6
|
+
export { AppView, Register } from './app/AppView';
|
|
7
|
+
export { Router } from './navigation/router';
|
|
8
|
+
export { Route } from './navigation/route';
|
|
9
|
+
export { NavigationEvents } from './navigation/types';
|
|
10
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,GAAG,EAAE,MAAM,WAAW,CAAC;AAChC,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAClD,OAAO,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAC7C,OAAO,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAmB3C,OAAO,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import type { RouteParams, RouteOptions } from './types';
|
|
2
|
+
/**
|
|
3
|
+
* Represents a route with path pattern matching and parameter extraction
|
|
4
|
+
*/
|
|
5
|
+
export declare class Route {
|
|
6
|
+
readonly path: string;
|
|
7
|
+
readonly pattern: RegExp;
|
|
8
|
+
readonly paramNames: string[];
|
|
9
|
+
readonly options: RouteOptions;
|
|
10
|
+
constructor(path: string, options?: RouteOptions);
|
|
11
|
+
/**
|
|
12
|
+
* Convert a path pattern like '/user/:id' to a RegExp
|
|
13
|
+
* Extracts parameter names like 'id'
|
|
14
|
+
*/
|
|
15
|
+
private pathToRegExp;
|
|
16
|
+
/**
|
|
17
|
+
* Check if the given path matches this route
|
|
18
|
+
* @param path - The path to test
|
|
19
|
+
* @returns The extracted parameters if match, null otherwise
|
|
20
|
+
*/
|
|
21
|
+
match(path: string): RouteParams | null;
|
|
22
|
+
/**
|
|
23
|
+
* Check if navigation is allowed via the route guard
|
|
24
|
+
* @param params - Route parameters
|
|
25
|
+
* @returns true if allowed, false if denied, or redirect path
|
|
26
|
+
*/
|
|
27
|
+
canEnter(params: RouteParams): Promise<boolean | string>;
|
|
28
|
+
/**
|
|
29
|
+
* Generate a path from this route pattern with given parameters
|
|
30
|
+
* @param params - Parameters to inject into the path
|
|
31
|
+
* @returns The generated path
|
|
32
|
+
*/
|
|
33
|
+
generate(params?: RouteParams): string;
|
|
34
|
+
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Represents a route with path pattern matching and parameter extraction
|
|
3
|
+
*/
|
|
4
|
+
export class Route {
|
|
5
|
+
constructor(path, options = {}) {
|
|
6
|
+
this.path = path;
|
|
7
|
+
this.options = options;
|
|
8
|
+
this.paramNames = [];
|
|
9
|
+
// Convert path pattern to RegExp and extract parameter names
|
|
10
|
+
this.pattern = this.pathToRegExp(path);
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Convert a path pattern like '/user/:id' to a RegExp
|
|
14
|
+
* Extracts parameter names like 'id'
|
|
15
|
+
*/
|
|
16
|
+
pathToRegExp(path) {
|
|
17
|
+
// Escape special characters except for :param patterns
|
|
18
|
+
const escapedPath = path.replace(/[.+?^${}()|[\]\\]/g, '\\$&');
|
|
19
|
+
// Replace :param with capture groups and extract param names
|
|
20
|
+
const pattern = escapedPath.replace(/:([^/]+)/g, (match, paramName) => {
|
|
21
|
+
this.paramNames.push(paramName);
|
|
22
|
+
return '([^/]+)';
|
|
23
|
+
});
|
|
24
|
+
// Match exact path or with trailing slash
|
|
25
|
+
return new RegExp(`^${pattern}/?$`);
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Check if the given path matches this route
|
|
29
|
+
* @param path - The path to test
|
|
30
|
+
* @returns The extracted parameters if match, null otherwise
|
|
31
|
+
*/
|
|
32
|
+
match(path) {
|
|
33
|
+
const match = this.pattern.exec(path);
|
|
34
|
+
if (!match) {
|
|
35
|
+
return null;
|
|
36
|
+
}
|
|
37
|
+
// Extract parameters from capture groups
|
|
38
|
+
const params = {};
|
|
39
|
+
this.paramNames.forEach((name, index) => {
|
|
40
|
+
params[name] = decodeURIComponent(match[index + 1]);
|
|
41
|
+
});
|
|
42
|
+
return params;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Check if navigation is allowed via the route guard
|
|
46
|
+
* @param params - Route parameters
|
|
47
|
+
* @returns true if allowed, false if denied, or redirect path
|
|
48
|
+
*/
|
|
49
|
+
async canEnter(params) {
|
|
50
|
+
if (!this.options.canEnter) {
|
|
51
|
+
return true;
|
|
52
|
+
}
|
|
53
|
+
return await this.options.canEnter(params);
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Generate a path from this route pattern with given parameters
|
|
57
|
+
* @param params - Parameters to inject into the path
|
|
58
|
+
* @returns The generated path
|
|
59
|
+
*/
|
|
60
|
+
generate(params = {}) {
|
|
61
|
+
let path = this.path;
|
|
62
|
+
for (const [key, value] of Object.entries(params)) {
|
|
63
|
+
path = path.replace(`:${key}`, encodeURIComponent(value));
|
|
64
|
+
}
|
|
65
|
+
return path;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
//# sourceMappingURL=route.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"route.js","sourceRoot":"","sources":["../../src/navigation/route.ts"],"names":[],"mappings":"AAEA;;GAEG;AACH,MAAM,OAAO,KAAK;IAMd,YAAY,IAAY,EAAE,UAAwB,EAAE;QAChD,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;QACjB,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC;QACvB,IAAI,CAAC,UAAU,GAAG,EAAE,CAAC;QAErB,6DAA6D;QAC7D,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC;IAC3C,CAAC;IAED;;;OAGG;IACK,YAAY,CAAC,IAAY;QAC7B,uDAAuD;QACvD,MAAM,WAAW,GAAG,IAAI,CAAC,OAAO,CAAC,oBAAoB,EAAE,MAAM,CAAC,CAAC;QAE/D,6DAA6D;QAC7D,MAAM,OAAO,GAAG,WAAW,CAAC,OAAO,CAAC,WAAW,EAAE,CAAC,KAAK,EAAE,SAAS,EAAE,EAAE;YAClE,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;YAChC,OAAO,SAAS,CAAC;QACrB,CAAC,CAAC,CAAC;QAEH,0CAA0C;QAC1C,OAAO,IAAI,MAAM,CAAC,IAAI,OAAO,KAAK,CAAC,CAAC;IACxC,CAAC;IAED;;;;OAIG;IACH,KAAK,CAAC,IAAY;QACd,MAAM,KAAK,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAEtC,IAAI,CAAC,KAAK,EAAE,CAAC;YACT,OAAO,IAAI,CAAC;QAChB,CAAC;QAED,yCAAyC;QACzC,MAAM,MAAM,GAAgB,EAAE,CAAC;QAC/B,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE;YACpC,MAAM,CAAC,IAAI,CAAC,GAAG,kBAAkB,CAAC,KAAK,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC,CAAC;QACxD,CAAC,CAAC,CAAC;QAEH,OAAO,MAAM,CAAC;IAClB,CAAC;IAED;;;;OAIG;IACH,KAAK,CAAC,QAAQ,CAAC,MAAmB;QAC9B,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,QAAQ,EAAE,CAAC;YACzB,OAAO,IAAI,CAAC;QAChB,CAAC;QAED,OAAO,MAAM,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;IAC/C,CAAC;IAED;;;;OAIG;IACH,QAAQ,CAAC,SAAsB,EAAE;QAC7B,IAAI,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC;QAErB,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC;YAChD,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,GAAG,EAAE,EAAE,kBAAkB,CAAC,KAAK,CAAC,CAAC,CAAC;QAC9D,CAAC;QAED,OAAO,IAAI,CAAC;IAChB,CAAC;CACJ"}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import type { RouteParams, RouteOptions, RouteHandler } from './types';
|
|
2
|
+
/**
|
|
3
|
+
* Router class for managing navigation and route mapping
|
|
4
|
+
* Emits events for Application.ts to handle view rendering via StackView
|
|
5
|
+
*/
|
|
6
|
+
export declare class Router extends EventTarget {
|
|
7
|
+
private routes;
|
|
8
|
+
private notFoundHandler?;
|
|
9
|
+
private isInitialized;
|
|
10
|
+
private basePath;
|
|
11
|
+
private _currentPath;
|
|
12
|
+
private _currentParams;
|
|
13
|
+
private _currentMeta?;
|
|
14
|
+
constructor();
|
|
15
|
+
/**
|
|
16
|
+
* Map a route path to a handler (view identifier)
|
|
17
|
+
* @param path - The route path (e.g., '/', '/user/:id')
|
|
18
|
+
* @param handler - The view identifier/handler
|
|
19
|
+
* @param options - Optional route configuration
|
|
20
|
+
* @returns The router instance for chaining
|
|
21
|
+
*/
|
|
22
|
+
map(path: string, handler: RouteHandler, options?: RouteOptions): this;
|
|
23
|
+
/**
|
|
24
|
+
* Set the 404 not found handler
|
|
25
|
+
* @param handler - The view identifier to use for 404
|
|
26
|
+
* @returns The router instance for chaining
|
|
27
|
+
*/
|
|
28
|
+
notFound(handler: RouteHandler): this;
|
|
29
|
+
/**
|
|
30
|
+
* Set the base path for all routes
|
|
31
|
+
* @param base - Base path (e.g., '/basic', '/app')
|
|
32
|
+
* @returns The router instance for chaining
|
|
33
|
+
*/
|
|
34
|
+
setBasePath(base: string): this;
|
|
35
|
+
/**
|
|
36
|
+
* Get the current base path
|
|
37
|
+
*/
|
|
38
|
+
getBasePath(): string;
|
|
39
|
+
/**
|
|
40
|
+
* Initialize the router and handle the current path
|
|
41
|
+
*/
|
|
42
|
+
start(): void;
|
|
43
|
+
/**
|
|
44
|
+
* Navigate to a specific path
|
|
45
|
+
* @param path - The path to navigate to (relative to basePath)
|
|
46
|
+
* @param replaceState - If true, replaces current history entry instead of pushing
|
|
47
|
+
*/
|
|
48
|
+
navigate(path: string, replaceState?: boolean): Promise<void>;
|
|
49
|
+
/**
|
|
50
|
+
* Get the current path (route pattern)
|
|
51
|
+
*/
|
|
52
|
+
get currentPath(): string;
|
|
53
|
+
/**
|
|
54
|
+
* Get the current route parameters
|
|
55
|
+
*/
|
|
56
|
+
get currentParams(): RouteParams;
|
|
57
|
+
/**
|
|
58
|
+
* Get the current route metadata
|
|
59
|
+
*/
|
|
60
|
+
get currentMeta(): Record<string, any> | undefined;
|
|
61
|
+
/**
|
|
62
|
+
* Find a route that matches the given path
|
|
63
|
+
*/
|
|
64
|
+
private findRoute;
|
|
65
|
+
/**
|
|
66
|
+
* Handle route change (from popstate or initial load)
|
|
67
|
+
*/
|
|
68
|
+
private handleRouteChange;
|
|
69
|
+
/**
|
|
70
|
+
* Strip base path from a full path
|
|
71
|
+
*/
|
|
72
|
+
private stripBasePath;
|
|
73
|
+
/**
|
|
74
|
+
* Add base path to a route path
|
|
75
|
+
*/
|
|
76
|
+
private addBasePath;
|
|
77
|
+
/**
|
|
78
|
+
* Emit navigation event
|
|
79
|
+
*/
|
|
80
|
+
private emitNavigation;
|
|
81
|
+
/**
|
|
82
|
+
* Emit not found event
|
|
83
|
+
*/
|
|
84
|
+
private emitNotFound;
|
|
85
|
+
/**
|
|
86
|
+
* Generate a URL for a route with parameters
|
|
87
|
+
* @param path - The route path pattern
|
|
88
|
+
* @param params - Parameters to inject
|
|
89
|
+
* @returns The generated URL (with basePath) or null if route not found
|
|
90
|
+
*/
|
|
91
|
+
generateUrl(path: string, params?: RouteParams): string | null;
|
|
92
|
+
}
|