@relax.js/core 1.0.3 → 1.0.4

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.
Files changed (234) hide show
  1. package/README.md +194 -188
  2. package/dist/DependencyInjection.d.ts +42 -24
  3. package/dist/di/index.js +1 -1
  4. package/dist/di/index.js.map +3 -3
  5. package/dist/di/index.mjs +1 -1
  6. package/dist/di/index.mjs.map +3 -3
  7. package/dist/errors.d.ts +20 -0
  8. package/dist/forms/FormValidator.d.ts +1 -20
  9. package/dist/forms/ValidationRules.d.ts +2 -0
  10. package/dist/forms/index.js +1 -1
  11. package/dist/forms/index.js.map +4 -4
  12. package/dist/forms/index.mjs +1 -1
  13. package/dist/forms/index.mjs.map +4 -4
  14. package/dist/html/TableRenderer.d.ts +1 -0
  15. package/dist/html/index.js.map +2 -2
  16. package/dist/html/index.mjs.map +2 -2
  17. package/dist/html/template.d.ts +4 -0
  18. package/dist/http/http.d.ts +1 -0
  19. package/dist/http/index.js.map +2 -2
  20. package/dist/http/index.mjs.map +2 -2
  21. package/dist/index.js +3 -3
  22. package/dist/index.js.map +3 -3
  23. package/dist/index.mjs +3 -3
  24. package/dist/index.mjs.map +3 -3
  25. package/dist/routing/index.js +3 -3
  26. package/dist/routing/index.js.map +3 -3
  27. package/dist/routing/index.mjs +3 -3
  28. package/dist/routing/index.mjs.map +3 -3
  29. package/dist/routing/routeTargetRegistry.d.ts +1 -0
  30. package/dist/routing/types.d.ts +2 -1
  31. package/dist/templates/NodeTemplate.d.ts +2 -0
  32. package/dist/utils/index.js +1 -1
  33. package/dist/utils/index.js.map +2 -2
  34. package/dist/utils/index.mjs +1 -1
  35. package/dist/utils/index.mjs.map +2 -2
  36. package/docs/Architecture.md +333 -333
  37. package/docs/DependencyInjection.md +277 -237
  38. package/docs/Errors.md +87 -87
  39. package/docs/GettingStarted.md +231 -231
  40. package/docs/Pipes.md +5 -5
  41. package/docs/Translations.md +167 -312
  42. package/docs/WhyRelaxjs.md +336 -336
  43. package/docs/api/.nojekyll +1 -0
  44. package/docs/api/assets/hierarchy.js +1 -0
  45. package/docs/api/assets/highlight.css +120 -0
  46. package/docs/api/assets/icons.js +18 -0
  47. package/docs/api/assets/icons.svg +1 -0
  48. package/docs/api/assets/main.js +60 -0
  49. package/docs/api/assets/navigation.js +1 -0
  50. package/docs/api/assets/search.js +1 -0
  51. package/docs/api/assets/style.css +1633 -0
  52. package/docs/api/classes/http.WebSocketClient.html +26 -0
  53. package/docs/api/classes/i18n.LocaleChangeEvent.html +66 -0
  54. package/docs/api/classes/index.Blueprint.html +3 -0
  55. package/docs/api/classes/index.BoundNode.html +3 -0
  56. package/docs/api/classes/index.DigitsValidation.html +10 -0
  57. package/docs/api/classes/index.FormValidator.html +32 -0
  58. package/docs/api/classes/index.HttpError.html +13 -0
  59. package/docs/api/classes/index.LinkedList.html +26 -0
  60. package/docs/api/classes/index.NavigateRouteEvent.html +76 -0
  61. package/docs/api/classes/index.Node.html +15 -0
  62. package/docs/api/classes/index.PageSelectedEvent.html +61 -0
  63. package/docs/api/classes/index.Pager.html +4 -0
  64. package/docs/api/classes/index.RangeValidation.html +15 -0
  65. package/docs/api/classes/index.RelaxError.html +17 -0
  66. package/docs/api/classes/index.RequiredValidation.html +10 -0
  67. package/docs/api/classes/index.RouteError.html +11 -0
  68. package/docs/api/classes/index.RouteGuardError.html +12 -0
  69. package/docs/api/classes/index.RouteLink.html +779 -0
  70. package/docs/api/classes/index.RouteTarget.html +788 -0
  71. package/docs/api/classes/index.SSEClient.html +13 -0
  72. package/docs/api/classes/index.SSEDataEvent.html +63 -0
  73. package/docs/api/classes/index.ServiceCollection.html +28 -0
  74. package/docs/api/classes/index.ServiceContainer.html +24 -0
  75. package/docs/api/classes/index.SortChangeEvent.html +61 -0
  76. package/docs/api/classes/index.TableRenderer.html +5 -0
  77. package/docs/api/classes/index.TableSorter.html +4 -0
  78. package/docs/api/enums/index.GuardResult.html +9 -0
  79. package/docs/api/functions/elements.formError.html +6 -0
  80. package/docs/api/functions/elements.selectOne.html +6 -0
  81. package/docs/api/functions/i18n.getCurrentLocale.html +3 -0
  82. package/docs/api/functions/i18n.loadNamespace.html +7 -0
  83. package/docs/api/functions/i18n.loadNamespaces.html +6 -0
  84. package/docs/api/functions/i18n.onMissingTranslation.html +7 -0
  85. package/docs/api/functions/i18n.setLocale.html +7 -0
  86. package/docs/api/functions/i18n.setMessageFormatter.html +7 -0
  87. package/docs/api/functions/i18n.t.html +9 -0
  88. package/docs/api/functions/index.BooleanConverter.html +6 -0
  89. package/docs/api/functions/index.ContainerService.html +13 -0
  90. package/docs/api/functions/index.DateConverter.html +11 -0
  91. package/docs/api/functions/index.Inject.html +16 -0
  92. package/docs/api/functions/index.NumberConverter.html +5 -0
  93. package/docs/api/functions/index.RegisterValidator.html +7 -0
  94. package/docs/api/functions/index.applyPipes.html +17 -0
  95. package/docs/api/functions/index.asyncHandler.html +11 -0
  96. package/docs/api/functions/index.capitalizePipe.html +4 -0
  97. package/docs/api/functions/index.clearPendingNavigations.html +1 -0
  98. package/docs/api/functions/index.compileTemplate.html +26 -0
  99. package/docs/api/functions/index.configure.html +5 -0
  100. package/docs/api/functions/index.createBluePrint.html +1 -0
  101. package/docs/api/functions/index.createConverterFromDataType.html +4 -0
  102. package/docs/api/functions/index.createConverterFromInputType.html +5 -0
  103. package/docs/api/functions/index.createPipeRegistry.html +12 -0
  104. package/docs/api/functions/index.currencyPipe.html +9 -0
  105. package/docs/api/functions/index.datePipe.html +9 -0
  106. package/docs/api/functions/index.daysAgoPipe.html +8 -0
  107. package/docs/api/functions/index.defaultPipe.html +5 -0
  108. package/docs/api/functions/index.defineRoutes.html +8 -0
  109. package/docs/api/functions/index.del.html +8 -0
  110. package/docs/api/functions/index.findRouteByName.html +5 -0
  111. package/docs/api/functions/index.findRouteByUrl.html +4 -0
  112. package/docs/api/functions/index.firstPipe.html +4 -0
  113. package/docs/api/functions/index.generateSequentialId.html +21 -0
  114. package/docs/api/functions/index.get.html +9 -0
  115. package/docs/api/functions/index.getDataConverter.html +11 -0
  116. package/docs/api/functions/index.getParentComponent.html +18 -0
  117. package/docs/api/functions/index.getValidator.html +4 -0
  118. package/docs/api/functions/index.html.html +19 -0
  119. package/docs/api/functions/index.joinPipe.html +5 -0
  120. package/docs/api/functions/index.keysPipe.html +4 -0
  121. package/docs/api/functions/index.lastPipe.html +4 -0
  122. package/docs/api/functions/index.lowercasePipe.html +4 -0
  123. package/docs/api/functions/index.mapFormToClass.html +17 -0
  124. package/docs/api/functions/index.matchRoute.html +5 -0
  125. package/docs/api/functions/index.navigate.html +8 -0
  126. package/docs/api/functions/index.onError.html +8 -0
  127. package/docs/api/functions/index.piecesPipe.html +8 -0
  128. package/docs/api/functions/index.post.html +9 -0
  129. package/docs/api/functions/index.printRoutes.html +2 -0
  130. package/docs/api/functions/index.put.html +9 -0
  131. package/docs/api/functions/index.readData.html +17 -0
  132. package/docs/api/functions/index.registerRouteTarget.html +9 -0
  133. package/docs/api/functions/index.reportError.html +10 -0
  134. package/docs/api/functions/index.request.html +8 -0
  135. package/docs/api/functions/index.resolveValue.html +18 -0
  136. package/docs/api/functions/index.setFetch.html +6 -0
  137. package/docs/api/functions/index.setFormData.html +17 -0
  138. package/docs/api/functions/index.shortenPipe.html +5 -0
  139. package/docs/api/functions/index.startRouting.html +6 -0
  140. package/docs/api/functions/index.ternaryPipe.html +6 -0
  141. package/docs/api/functions/index.trimPipe.html +4 -0
  142. package/docs/api/functions/index.unregisterRouteTarget.html +3 -0
  143. package/docs/api/functions/index.uppercasePipe.html +4 -0
  144. package/docs/api/hierarchy.html +1 -0
  145. package/docs/api/index.html +323 -0
  146. package/docs/api/interfaces/http.SimpleDataEvent.html +3 -0
  147. package/docs/api/interfaces/http.WebSocketAbstraction.html +9 -0
  148. package/docs/api/interfaces/http.WebSocketCodec.html +4 -0
  149. package/docs/api/interfaces/http.WebSocketOptions.html +20 -0
  150. package/docs/api/interfaces/index.CompiledTemplate.html +10 -0
  151. package/docs/api/interfaces/index.DataLoader.html +19 -0
  152. package/docs/api/interfaces/index.EngineConfig.html +11 -0
  153. package/docs/api/interfaces/index.ErrorContext.html +4 -0
  154. package/docs/api/interfaces/index.FormReaderOptions.html +8 -0
  155. package/docs/api/interfaces/index.HttpOptions.html +16 -0
  156. package/docs/api/interfaces/index.HttpResponse.html +17 -0
  157. package/docs/api/interfaces/index.LoadRoute.html +7 -0
  158. package/docs/api/interfaces/index.NavigateOptions.html +7 -0
  159. package/docs/api/interfaces/index.PipeRegistry.html +12 -0
  160. package/docs/api/interfaces/index.RegistrationOptions.html +22 -0
  161. package/docs/api/interfaces/index.RenderTemplate.html +7 -0
  162. package/docs/api/interfaces/index.RequestOptions.html +11 -0
  163. package/docs/api/interfaces/index.Routable.html +10 -0
  164. package/docs/api/interfaces/index.Route.html +13 -0
  165. package/docs/api/interfaces/index.RouteGuard.html +2 -0
  166. package/docs/api/interfaces/index.RouteValue.html +6 -0
  167. package/docs/api/interfaces/index.SSEOptions.html +24 -0
  168. package/docs/api/interfaces/index.ValidationContext.html +8 -0
  169. package/docs/api/interfaces/index.ValidatorOptions.html +14 -0
  170. package/docs/api/media/Architecture.md +333 -0
  171. package/docs/api/media/DependencyInjection.md +277 -0
  172. package/docs/api/media/GettingStarted.md +231 -0
  173. package/docs/api/media/HttpClient.md +459 -0
  174. package/docs/api/media/Pipes.md +211 -0
  175. package/docs/api/media/Routing.md +332 -0
  176. package/docs/api/media/WhyRelaxjs.md +336 -0
  177. package/docs/api/media/forms.md +99 -0
  178. package/docs/api/media/html.md +175 -0
  179. package/docs/api/media/i18n.md +354 -0
  180. package/docs/api/media/utilities.md +143 -0
  181. package/docs/api/media/validation.md +351 -0
  182. package/docs/api/modules/collections_Index.html +1 -0
  183. package/docs/api/modules/di.html +1 -0
  184. package/docs/api/modules/elements.html +1 -0
  185. package/docs/api/modules/forms.html +1 -0
  186. package/docs/api/modules/html.html +1 -0
  187. package/docs/api/modules/http.html +1 -0
  188. package/docs/api/modules/i18n.html +1 -0
  189. package/docs/api/modules/index.html +1 -0
  190. package/docs/api/modules/routing.html +1 -0
  191. package/docs/api/modules/utils.html +1 -0
  192. package/docs/api/modules.html +1 -0
  193. package/docs/api/types/http.WebSocketFactory.html +2 -0
  194. package/docs/api/types/i18n.MessageFormatter.html +3 -0
  195. package/docs/api/types/i18n.MissingTranslationHandler.html +1 -0
  196. package/docs/api/types/index.Constructor.html +7 -0
  197. package/docs/api/types/index.ConverterFunc.html +2 -0
  198. package/docs/api/types/index.DataType.html +2 -0
  199. package/docs/api/types/index.InputType.html +2 -0
  200. package/docs/api/types/index.PipeFunction.html +6 -0
  201. package/docs/api/types/index.RouteData.html +1 -0
  202. package/docs/api/types/index.RouteMatchResult.html +9 -0
  203. package/docs/api/types/index.RouteParamType.html +1 -0
  204. package/docs/api/types/index.RouteSegmentType.html +2 -0
  205. package/docs/api/types/index.SSEEventFactory.html +5 -0
  206. package/docs/api/types/index.ServiceScope.html +10 -0
  207. package/docs/api/types/index.SortColumn.html +3 -0
  208. package/docs/api/variables/i18n.formatICU.html +3 -0
  209. package/docs/api/variables/index.container.html +6 -0
  210. package/docs/api/variables/index.defaultPipes.html +6 -0
  211. package/docs/api/variables/index.internalRoutes.html +1 -0
  212. package/docs/api/variables/index.serviceCollection.html +6 -0
  213. package/docs/api.json +93171 -0
  214. package/docs/elements/dom.md +102 -102
  215. package/docs/forms/creating-form-components.md +924 -924
  216. package/docs/forms/form-api.md +94 -94
  217. package/docs/forms/forms.md +99 -99
  218. package/docs/forms/patterns.md +311 -311
  219. package/docs/forms/reading-writing.md +365 -365
  220. package/docs/forms/validation.md +351 -351
  221. package/docs/html/TableRenderer.md +291 -291
  222. package/docs/html/html.md +175 -175
  223. package/docs/html/index.md +54 -54
  224. package/docs/html/template.md +422 -422
  225. package/docs/http/HttpClient.md +459 -459
  226. package/docs/http/ServerSentEvents.md +184 -184
  227. package/docs/http/index.md +109 -109
  228. package/docs/i18n/i18n.md +49 -4
  229. package/docs/i18n/intl-standard.md +178 -178
  230. package/docs/routing/RouteLink.md +98 -98
  231. package/docs/routing/Routing.md +332 -332
  232. package/docs/routing/layouts.md +207 -207
  233. package/docs/utilities.md +143 -143
  234. package/package.json +4 -3
@@ -1,332 +1,332 @@
1
- # Routing
2
-
3
- ## Overview
4
-
5
- A client-side routing system for single-page applications. The router:
6
-
7
- - Matches URLs to route configurations
8
- - Extracts typed parameters from URL segments
9
- - Manages browser history (back/forward navigation)
10
- - Dispatches `NavigateRouteEvent` to render components
11
- - Supports multiple rendering targets (main content, sidebars, modals)
12
- - Handles layout switching between different HTML shells
13
-
14
- The router itself only handles matching and navigation. Rendering is handled by [`<r-route-target>`](RoutingTarget.md) components that listen for navigation events.
15
-
16
- ## Quick Start
17
-
18
- ```typescript
19
- import { Route, defineRoutes, startRouting } from 'relaxjs/routing';
20
-
21
- const routes: Route[] = [
22
- { name: 'home', path: '/', componentTagName: 'home-page' },
23
- { name: 'users', path: '/users', componentTagName: 'user-list' },
24
- { name: 'user', path: '/users/:id', componentTagName: 'user-profile' },
25
- ];
26
-
27
- defineRoutes(routes);
28
- startRouting();
29
- ```
30
-
31
- ```html
32
- <body>
33
- <nav>
34
- <r-link name="home">Home</r-link>
35
- <r-link name="users">Users</r-link>
36
- </nav>
37
- <main>
38
- <r-route-target></r-route-target>
39
- </main>
40
- </body>
41
- ```
42
-
43
- ## Route Definition
44
-
45
- Each route specifies how a URL maps to a component:
46
-
47
- ```typescript
48
- interface Route {
49
- name?: string; // Identifier for programmatic navigation
50
- path: string; // URL pattern with parameters
51
- componentTagName?: string; // Custom element tag name
52
- component?: WebComponentConstructor; // Or class reference
53
- target?: string; // Named r-route-target (default: unnamed)
54
- layout?: string; // HTML file for different shells
55
- guards?: RouteGuard[]; // Access control
56
- }
57
- ```
58
-
59
- ### URL Pattern Syntax
60
-
61
- | Syntax | Type | Example | Matches |
62
- |--------|------|---------|---------|
63
- | `text` | Static segment | `/users` | Exactly "users" |
64
- | `:name` | String parameter | `/users/:id` | `/users/john` → `{ id: 'john' }` |
65
- | `;name` | Number parameter | `/orders/;orderId` | `/orders/123` → `{ orderId: 123 }` |
66
-
67
- Number parameters (`;`) validate that the segment contains only digits and convert to `number` type. String parameters (`:`) accept any value and remain as `string`.
68
-
69
- ```typescript
70
- const routes: Route[] = [
71
- { name: 'home', path: '/' },
72
- { name: 'user', path: '/users/:userName' }, // String: userName
73
- { name: 'order', path: '/orders/;orderId' }, // Number: orderId
74
- { name: 'category', path: '/shop/:category/items' }, // Mixed static + param
75
- ];
76
- ```
77
-
78
- ## Navigation
79
-
80
- ### Using RouteLink Component
81
-
82
- The simplest way to navigate is with [`<r-link>`](RouteLink.md):
83
-
84
- ```html
85
- <r-link name="home">Home</r-link>
86
- <r-link name="user" param-userName="john">View Profile</r-link>
87
- <r-link name="order" param-orderId="123">Order Details</r-link>
88
- ```
89
-
90
- ### Programmatic Navigation
91
-
92
- Use `navigate()` for navigation from code:
93
-
94
- ```typescript
95
- import { navigate } from 'relaxjs/routing';
96
-
97
- // By route name with parameters
98
- navigate('user', { params: { userName: 'john' } });
99
- navigate('order', { params: { orderId: 123 } });
100
-
101
- // By URL directly
102
- navigate('/users/john');
103
- navigate('/orders/123');
104
-
105
- // To a specific target
106
- navigate('preview', { params: { id: '42' }, target: 'modal' });
107
- ```
108
-
109
- ## Multiple Targets
110
-
111
- Routes can render in different [`<r-route-target>`](RoutingTarget.md) elements:
112
-
113
- ```html
114
- <div class="layout">
115
- <aside>
116
- <r-route-target name="sidebar"></r-route-target>
117
- </aside>
118
- <main>
119
- <r-route-target></r-route-target>
120
- </main>
121
- </div>
122
- ```
123
-
124
- ```typescript
125
- const routes: Route[] = [
126
- { name: 'home', path: '/', componentTagName: 'home-page' },
127
- { name: 'menu', path: '/menu', target: 'sidebar', componentTagName: 'nav-menu' },
128
- ];
129
- ```
130
-
131
- Routes without a `target` property render in the default (unnamed) target.
132
-
133
- ## Browser History
134
-
135
- The router integrates with the browser's History API:
136
-
137
- - `navigate()` calls `history.pushState()` to add entries
138
- - Back/forward buttons trigger navigation to previous routes (components receive `loadRoute` again)
139
- - `startRouting()` reads the current URL and navigates on page load
140
-
141
- URLs display the route path (e.g., `/users/john`), not the HTML file.
142
-
143
- ## Layouts
144
-
145
- Different parts of your application may need different HTML shells (navigation, sidebar presence, etc.). See [Layouts](layouts.md) for details.
146
-
147
- ```typescript
148
- const routes: Route[] = [
149
- { name: 'login', path: '/login', componentTagName: 'login-page', layout: 'public' },
150
- { name: 'dashboard', path: '/dashboard', componentTagName: 'dashboard-page' }, // default layout
151
- ];
152
- ```
153
-
154
- When navigating between layouts, the router redirects to the appropriate HTML file and resumes navigation.
155
-
156
- ## Route Guards
157
-
158
- Guards control access to routes:
159
-
160
- ```typescript
161
- import { RouteGuard, GuardResult, RouteMatchResult } from 'relaxjs/routing';
162
-
163
- class AuthGuard implements RouteGuard {
164
- check(route: RouteMatchResult): GuardResult {
165
- if (isAuthenticated()) {
166
- return GuardResult.Continue; // Check other guards
167
- }
168
- navigate('login');
169
- return GuardResult.Stop; // Prevent navigation
170
- }
171
- }
172
-
173
- const routes: Route[] = [
174
- { name: 'dashboard', path: '/dashboard', componentTagName: 'dashboard-page', guards: [new AuthGuard()] },
175
- ];
176
- ```
177
-
178
- ### Guard Results
179
-
180
- | Result | Behavior |
181
- |--------|----------|
182
- | `Allow` | Proceed immediately, skip remaining guards |
183
- | `Continue` | Check next guard (or proceed if none left) |
184
- | `Stop` | Cancel navigation silently |
185
- | `Deny` | Cancel navigation and throw `RouteGuardError` |
186
-
187
- ## Events
188
-
189
- Navigation dispatches `NavigateRouteEvent` on `document`:
190
-
191
- ```typescript
192
- import { NavigateRouteEvent } from 'relaxjs/routing';
193
-
194
- document.addEventListener('rlx.navigateRoute', (e: NavigateRouteEvent) => {
195
- console.log('Route:', e.route.name);
196
- console.log('Params:', e.routeData);
197
- console.log('Target:', e.routeTarget ?? 'default');
198
- });
199
- ```
200
-
201
- This event is typed in `HTMLElementEventMap` for full TypeScript support.
202
-
203
- ## Route Matching
204
-
205
- For advanced use cases, match routes without navigating:
206
-
207
- ```typescript
208
- import { matchRoute, findRouteByUrl, findRouteByName } from 'relaxjs/routing';
209
-
210
- // Match by URL
211
- const result = matchRoute(routes, '/users/john');
212
- // { route: {...}, params: { userName: 'john' }, urlSegments: ['users', 'john'] }
213
-
214
- // Match by name
215
- const result = matchRoute(routes, 'user', { userName: 'john' });
216
-
217
- // Direct functions
218
- findRouteByUrl(routes, '/users/john');
219
- findRouteByName(routes, 'user', { userName: 'john' });
220
- ```
221
-
222
- ## Receiving Route Parameters
223
-
224
- Components rendered by `<r-route-target>` can receive route parameters in two ways.
225
-
226
- ### loadRoute (async initialization)
227
-
228
- Implement `loadRoute()` to run async setup before the component is added to the DOM. The component is not visible until `loadRoute()` completes.
229
-
230
- ```typescript
231
- import { LoadRoute, RouteData } from 'relaxjs/routing';
232
-
233
- class OrderDetail extends HTMLElement implements LoadRoute<{ orderId: number }> {
234
- private order: Order;
235
-
236
- async loadRoute(data: { orderId: number }) {
237
- this.order = await fetchOrder(data.orderId);
238
- }
239
-
240
- connectedCallback() {
241
- this.render(this.order);
242
- }
243
- }
244
- ```
245
-
246
- ### routeData (typed property)
247
-
248
- Implement `Routable` to receive parameters as a typed property. The property is optional since it's set by the router after construction.
249
-
250
- ```typescript
251
- import { Routable } from 'relaxjs/routing';
252
-
253
- class UserProfile extends HTMLElement implements Routable<{ userName: string }> {
254
- routeData?: { userName: string };
255
- }
256
- ```
257
-
258
- For convention-based usage without the interface, declare `routeData` directly on your component. The router always assigns it regardless.
259
-
260
- Both can be combined. `loadRoute()` runs first, then `routeData` is assigned.
261
-
262
- ## Error Handling
263
-
264
- ```typescript
265
- import { RouteError, RouteGuardError } from 'relaxjs/routing';
266
-
267
- try {
268
- navigate('unknown-route');
269
- } catch (e) {
270
- if (e instanceof RouteGuardError) {
271
- console.log('Access denied');
272
- } else if (e instanceof RouteError) {
273
- console.log('Route not found');
274
- }
275
- }
276
- ```
277
-
278
- When no route matches, the error message lists all available routes for debugging.
279
-
280
- Routing errors are reported through the global [error handler](../Errors.md). The error context contains:
281
-
282
- | Field | Description |
283
- |-------|-------------|
284
- | `route` | Route name |
285
- | `componentTagName` | Custom element tag name |
286
- | `component` | Component class name (if using class reference) |
287
- | `routeData` | Parameters extracted from the URL |
288
-
289
- ## API Reference
290
-
291
- ### Functions
292
-
293
- | Function | Description |
294
- |----------|-------------|
295
- | `defineRoutes(routes)` | Register routes at startup |
296
- | `startRouting()` | Initialize router and navigate to current URL |
297
- | `navigate(nameOrUrl, options?)` | Navigate to a route |
298
- | `matchRoute(routes, nameOrUrl, params?)` | Match without navigating |
299
- | `findRouteByUrl(routes, path)` | Match by URL pattern |
300
- | `findRouteByName(routes, name, params)` | Match by route name |
301
-
302
- ### Types
303
-
304
- ```typescript
305
- type RouteParamType = string | number;
306
- type RouteData = Record<string, RouteParamType>;
307
-
308
- interface NavigateOptions {
309
- params?: Record<string, string | number>;
310
- target?: string;
311
- routes?: Route[]; // Override registered routes
312
- }
313
-
314
- interface RouteMatchResult {
315
- route: Route;
316
- params: RouteData;
317
- urlSegments: string[];
318
- }
319
-
320
- enum GuardResult {
321
- Allow, // Proceed, skip remaining guards
322
- Deny, // Throw RouteGuardError
323
- Continue, // Check next guard
324
- Stop // Cancel silently
325
- }
326
- ```
327
-
328
- ## Related
329
-
330
- - [`<r-route-target>`](RoutingTarget.md) - Renders routed components
331
- - [`<r-link>`](RouteLink.md) - Declarative navigation links
332
- - [Layouts](layouts.md) - Multiple HTML shells
1
+ # Routing
2
+
3
+ ## Overview
4
+
5
+ A client-side routing system for single-page applications. The router:
6
+
7
+ - Matches URLs to route configurations
8
+ - Extracts typed parameters from URL segments
9
+ - Manages browser history (back/forward navigation)
10
+ - Dispatches `NavigateRouteEvent` to render components
11
+ - Supports multiple rendering targets (main content, sidebars, modals)
12
+ - Handles layout switching between different HTML shells
13
+
14
+ The router itself only handles matching and navigation. Rendering is handled by [`<r-route-target>`](RoutingTarget.md) components that listen for navigation events.
15
+
16
+ ## Quick Start
17
+
18
+ ```typescript
19
+ import { Route, defineRoutes, startRouting } from '@relax.js/core/routing';
20
+
21
+ const routes: Route[] = [
22
+ { name: 'home', path: '/', componentTagName: 'home-page' },
23
+ { name: 'users', path: '/users', componentTagName: 'user-list' },
24
+ { name: 'user', path: '/users/:id', componentTagName: 'user-profile' },
25
+ ];
26
+
27
+ defineRoutes(routes);
28
+ startRouting();
29
+ ```
30
+
31
+ ```html
32
+ <body>
33
+ <nav>
34
+ <r-link name="home">Home</r-link>
35
+ <r-link name="users">Users</r-link>
36
+ </nav>
37
+ <main>
38
+ <r-route-target></r-route-target>
39
+ </main>
40
+ </body>
41
+ ```
42
+
43
+ ## Route Definition
44
+
45
+ Each route specifies how a URL maps to a component:
46
+
47
+ ```typescript
48
+ interface Route {
49
+ name?: string; // Identifier for programmatic navigation
50
+ path: string; // URL pattern with parameters
51
+ componentTagName?: string; // Custom element tag name
52
+ component?: WebComponentConstructor; // Or class reference
53
+ target?: string; // Named r-route-target (default: unnamed)
54
+ layout?: string; // HTML file for different shells
55
+ guards?: RouteGuard[]; // Access control
56
+ }
57
+ ```
58
+
59
+ ### URL Pattern Syntax
60
+
61
+ | Syntax | Type | Example | Matches |
62
+ |--------|------|---------|---------|
63
+ | `text` | Static segment | `/users` | Exactly "users" |
64
+ | `:name` | String parameter | `/users/:id` | `/users/john` → `{ id: 'john' }` |
65
+ | `;name` | Number parameter | `/orders/;orderId` | `/orders/123` → `{ orderId: 123 }` |
66
+
67
+ Number parameters (`;`) validate that the segment contains only digits and convert to `number` type. String parameters (`:`) accept any value and remain as `string`.
68
+
69
+ ```typescript
70
+ const routes: Route[] = [
71
+ { name: 'home', path: '/' },
72
+ { name: 'user', path: '/users/:userName' }, // String: userName
73
+ { name: 'order', path: '/orders/;orderId' }, // Number: orderId
74
+ { name: 'category', path: '/shop/:category/items' }, // Mixed static + param
75
+ ];
76
+ ```
77
+
78
+ ## Navigation
79
+
80
+ ### Using RouteLink Component
81
+
82
+ The simplest way to navigate is with [`<r-link>`](RouteLink.md):
83
+
84
+ ```html
85
+ <r-link name="home">Home</r-link>
86
+ <r-link name="user" param-userName="john">View Profile</r-link>
87
+ <r-link name="order" param-orderId="123">Order Details</r-link>
88
+ ```
89
+
90
+ ### Programmatic Navigation
91
+
92
+ Use `navigate()` for navigation from code:
93
+
94
+ ```typescript
95
+ import { navigate } from '@relax.js/core/routing';
96
+
97
+ // By route name with parameters
98
+ navigate('user', { params: { userName: 'john' } });
99
+ navigate('order', { params: { orderId: 123 } });
100
+
101
+ // By URL directly
102
+ navigate('/users/john');
103
+ navigate('/orders/123');
104
+
105
+ // To a specific target
106
+ navigate('preview', { params: { id: '42' }, target: 'modal' });
107
+ ```
108
+
109
+ ## Multiple Targets
110
+
111
+ Routes can render in different [`<r-route-target>`](RoutingTarget.md) elements:
112
+
113
+ ```html
114
+ <div class="layout">
115
+ <aside>
116
+ <r-route-target name="sidebar"></r-route-target>
117
+ </aside>
118
+ <main>
119
+ <r-route-target></r-route-target>
120
+ </main>
121
+ </div>
122
+ ```
123
+
124
+ ```typescript
125
+ const routes: Route[] = [
126
+ { name: 'home', path: '/', componentTagName: 'home-page' },
127
+ { name: 'menu', path: '/menu', target: 'sidebar', componentTagName: 'nav-menu' },
128
+ ];
129
+ ```
130
+
131
+ Routes without a `target` property render in the default (unnamed) target.
132
+
133
+ ## Browser History
134
+
135
+ The router integrates with the browser's History API:
136
+
137
+ - `navigate()` calls `history.pushState()` to add entries
138
+ - Back/forward buttons trigger navigation to previous routes (components receive `loadRoute` again)
139
+ - `startRouting()` reads the current URL and navigates on page load
140
+
141
+ URLs display the route path (e.g., `/users/john`), not the HTML file.
142
+
143
+ ## Layouts
144
+
145
+ Different parts of your application may need different HTML shells (navigation, sidebar presence, etc.). See [Layouts](layouts.md) for details.
146
+
147
+ ```typescript
148
+ const routes: Route[] = [
149
+ { name: 'login', path: '/login', componentTagName: 'login-page', layout: 'public' },
150
+ { name: 'dashboard', path: '/dashboard', componentTagName: 'dashboard-page' }, // default layout
151
+ ];
152
+ ```
153
+
154
+ When navigating between layouts, the router redirects to the appropriate HTML file and resumes navigation.
155
+
156
+ ## Route Guards
157
+
158
+ Guards control access to routes:
159
+
160
+ ```typescript
161
+ import { RouteGuard, GuardResult, RouteMatchResult } from '@relax.js/core/routing';
162
+
163
+ class AuthGuard implements RouteGuard {
164
+ check(route: RouteMatchResult): GuardResult {
165
+ if (isAuthenticated()) {
166
+ return GuardResult.Continue; // Check other guards
167
+ }
168
+ navigate('login');
169
+ return GuardResult.Stop; // Prevent navigation
170
+ }
171
+ }
172
+
173
+ const routes: Route[] = [
174
+ { name: 'dashboard', path: '/dashboard', componentTagName: 'dashboard-page', guards: [new AuthGuard()] },
175
+ ];
176
+ ```
177
+
178
+ ### Guard Results
179
+
180
+ | Result | Behavior |
181
+ |--------|----------|
182
+ | `Allow` | Proceed immediately, skip remaining guards |
183
+ | `Continue` | Check next guard (or proceed if none left) |
184
+ | `Stop` | Cancel navigation silently |
185
+ | `Deny` | Cancel navigation and throw `RouteGuardError` |
186
+
187
+ ## Events
188
+
189
+ Navigation dispatches `NavigateRouteEvent` on `document`:
190
+
191
+ ```typescript
192
+ import { NavigateRouteEvent } from '@relax.js/core/routing';
193
+
194
+ document.addEventListener('rlx.navigateRoute', (e: NavigateRouteEvent) => {
195
+ console.log('Route:', e.route.name);
196
+ console.log('Params:', e.routeData);
197
+ console.log('Target:', e.routeTarget ?? 'default');
198
+ });
199
+ ```
200
+
201
+ This event is typed in `HTMLElementEventMap` for full TypeScript support.
202
+
203
+ ## Route Matching
204
+
205
+ For advanced use cases, match routes without navigating:
206
+
207
+ ```typescript
208
+ import { matchRoute, findRouteByUrl, findRouteByName } from '@relax.js/core/routing';
209
+
210
+ // Match by URL
211
+ const result = matchRoute(routes, '/users/john');
212
+ // { route: {...}, params: { userName: 'john' }, urlSegments: ['users', 'john'] }
213
+
214
+ // Match by name
215
+ const result = matchRoute(routes, 'user', { userName: 'john' });
216
+
217
+ // Direct functions
218
+ findRouteByUrl(routes, '/users/john');
219
+ findRouteByName(routes, 'user', { userName: 'john' });
220
+ ```
221
+
222
+ ## Receiving Route Parameters
223
+
224
+ Components rendered by `<r-route-target>` can receive route parameters in two ways.
225
+
226
+ ### loadRoute (async initialization)
227
+
228
+ Implement `loadRoute()` to run async setup before the component is added to the DOM. The component is not visible until `loadRoute()` completes.
229
+
230
+ ```typescript
231
+ import { LoadRoute, RouteData } from '@relax.js/core/routing';
232
+
233
+ class OrderDetail extends HTMLElement implements LoadRoute<{ orderId: number }> {
234
+ private order: Order;
235
+
236
+ async loadRoute(data: { orderId: number }) {
237
+ this.order = await fetchOrder(data.orderId);
238
+ }
239
+
240
+ connectedCallback() {
241
+ this.render(this.order);
242
+ }
243
+ }
244
+ ```
245
+
246
+ ### routeData (typed property)
247
+
248
+ Implement `Routable` to receive parameters as a typed property. The property is optional since it's set by the router after construction.
249
+
250
+ ```typescript
251
+ import { Routable } from '@relax.js/core/routing';
252
+
253
+ class UserProfile extends HTMLElement implements Routable<{ userName: string }> {
254
+ routeData?: { userName: string };
255
+ }
256
+ ```
257
+
258
+ For convention-based usage without the interface, declare `routeData` directly on your component. The router always assigns it regardless.
259
+
260
+ Both can be combined. `loadRoute()` runs first, then `routeData` is assigned.
261
+
262
+ ## Error Handling
263
+
264
+ ```typescript
265
+ import { RouteError, RouteGuardError } from '@relax.js/core/routing';
266
+
267
+ try {
268
+ navigate('unknown-route');
269
+ } catch (e) {
270
+ if (e instanceof RouteGuardError) {
271
+ console.log('Access denied');
272
+ } else if (e instanceof RouteError) {
273
+ console.log('Route not found');
274
+ }
275
+ }
276
+ ```
277
+
278
+ When no route matches, the error message lists all available routes for debugging.
279
+
280
+ Routing errors are reported through the global [error handler](../Errors.md). The error context contains:
281
+
282
+ | Field | Description |
283
+ |-------|-------------|
284
+ | `route` | Route name |
285
+ | `componentTagName` | Custom element tag name |
286
+ | `component` | Component class name (if using class reference) |
287
+ | `routeData` | Parameters extracted from the URL |
288
+
289
+ ## API Reference
290
+
291
+ ### Functions
292
+
293
+ | Function | Description |
294
+ |----------|-------------|
295
+ | `defineRoutes(routes)` | Register routes at startup |
296
+ | `startRouting()` | Initialize router and navigate to current URL |
297
+ | `navigate(nameOrUrl, options?)` | Navigate to a route |
298
+ | `matchRoute(routes, nameOrUrl, params?)` | Match without navigating |
299
+ | `findRouteByUrl(routes, path)` | Match by URL pattern |
300
+ | `findRouteByName(routes, name, params)` | Match by route name |
301
+
302
+ ### Types
303
+
304
+ ```typescript
305
+ type RouteParamType = string | number;
306
+ type RouteData = Record<string, RouteParamType>;
307
+
308
+ interface NavigateOptions {
309
+ params?: Record<string, string | number>;
310
+ target?: string;
311
+ routes?: Route[]; // Override registered routes
312
+ }
313
+
314
+ interface RouteMatchResult {
315
+ route: Route;
316
+ params: RouteData;
317
+ urlSegments: string[];
318
+ }
319
+
320
+ enum GuardResult {
321
+ Allow, // Proceed, skip remaining guards
322
+ Deny, // Throw RouteGuardError
323
+ Continue, // Check next guard
324
+ Stop // Cancel silently
325
+ }
326
+ ```
327
+
328
+ ## Related
329
+
330
+ - [`<r-route-target>`](RoutingTarget.md) - Renders routed components
331
+ - [`<r-link>`](RouteLink.md) - Declarative navigation links
332
+ - [Layouts](layouts.md) - Multiple HTML shells