@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,333 +1,333 @@
1
- # Architecture Overview
2
-
3
- Relaxjs is a library for building web applications with Web Components. It emphasizes explicit control, direct DOM manipulation, and predictable behavior.
4
-
5
- ## Philosophy
6
-
7
- Unlike modern SPA frameworks, Relaxjs takes an explicit approach:
8
-
9
- - **No virtual DOM**: The real DOM is the source of truth
10
- - **No hidden reactivity**: Changes happen when you explicitly make them
11
- - **No synthetic lifecycle**: Uses native Web Component lifecycle hooks
12
- - **Full developer control**: You always know what triggered what
13
-
14
- ## Core Modules
15
-
16
- ```
17
- relaxjs/
18
- ├── Routing # SPA navigation with guards and layouts
19
- ├── Forms # Data binding, validation, type conversion
20
- ├── DI # Dependency injection container
21
- ├── HTTP # Fetch wrapper and WebSocket client
22
- ├── i18n # Internationalization with ICU support
23
- ├── Templates # HTML templating with data binding
24
- └── Components # Pre-built UI components
25
- ```
26
-
27
- ## Module Integration
28
-
29
- ### Application Startup
30
-
31
- ```typescript
32
- import { defineRoutes, startRouting } from 'relaxjs/routing';
33
- import { setLocale } from 'relaxjs/i18n';
34
- import { serviceCollection } from 'relaxjs/di';
35
-
36
- class Application extends HTMLElement {
37
- async connectedCallback() {
38
- // 1. Register services
39
- serviceCollection.registerByType(ApiService, { inject: [] });
40
- serviceCollection.registerByType(AuthService, { inject: [ApiService] });
41
-
42
- // 2. Set locale
43
- await setLocale(navigator.language);
44
-
45
- // 3. Define routes
46
- defineRoutes([
47
- { name: 'home', path: '/', componentTagName: 'app-home' },
48
- { name: 'login', path: '/login', componentTagName: 'login-page' }
49
- ]);
50
-
51
- // 4. Start routing
52
- startRouting();
53
- }
54
- }
55
-
56
- customElements.define('app-main', Application);
57
- ```
58
-
59
- ### Component Pattern
60
-
61
- ```typescript
62
- class UserProfile extends HTMLElement {
63
- private form: HTMLFormElement;
64
- private validator: FormValidator;
65
-
66
- connectedCallback() {
67
- this.innerHTML = `
68
- <form>
69
- <input name="name" required>
70
- <input name="email" type="email" required>
71
- <button type="submit">Save</button>
72
- </form>
73
- `;
74
-
75
- this.form = this.querySelector('form')!;
76
- this.validator = new FormValidator(this.form, {
77
- submitCallback: () => this.save()
78
- });
79
-
80
- this.loadUser();
81
- }
82
-
83
- async loadUser() {
84
- const api = container.resolve(ApiService);
85
- const user = await api.get('/user/profile');
86
- setFormData(this.form, user.as<User>());
87
- }
88
-
89
- async save() {
90
- const data = readData(this.form);
91
- const api = container.resolve(ApiService);
92
- await api.put('/user/profile', JSON.stringify(data));
93
- }
94
- }
95
-
96
- customElements.define('user-profile', UserProfile);
97
- ```
98
-
99
- ## Data Flow
100
-
101
- ```
102
- ┌─────────────────────────────────────────────────────────┐
103
- │ User Action │
104
- └─────────────────────────────────────────────────────────┘
105
-
106
-
107
- ┌─────────────────────────────────────────────────────────┐
108
- │ Event Handler │
109
- │ │
110
- │ element.addEventListener('click', (e) => { │
111
- │ // Explicit action │
112
- │ }); │
113
- └─────────────────────────────────────────────────────────┘
114
-
115
-
116
- ┌─────────────────────────────────────────────────────────┐
117
- │ DOM Update │
118
- │ │
119
- │ // Direct manipulation │
120
- │ element.textContent = newValue; │
121
- │ element.setAttribute('data-state', 'active'); │
122
- └─────────────────────────────────────────────────────────┘
123
- ```
124
-
125
- ## Routing Architecture
126
-
127
- ```
128
- ┌──────────────┐ navigate() ┌──────────────────┐
129
- │ User │ ────────────────► │ Router │
130
- │ Action │ │ │
131
- └──────────────┘ │ - Match route │
132
- │ - Check guards │
133
- │ - Update URL │
134
- └────────┬─────────┘
135
-
136
- NavigateRouteEvent
137
-
138
-
139
- ┌──────────────────┐
140
- │ r-route-target│
141
- │ │
142
- │ - Load component│
143
- │ - Pass params │
144
- └──────────────────┘
145
- ```
146
-
147
- ### Multiple Targets
148
-
149
- ```html
150
- <body>
151
- <r-route-target>
152
- <!-- Main content renders here -->
153
- </r-route-target>
154
-
155
- <r-route-target name="modal">
156
- <!-- Modal content renders here -->
157
- </r-route-target>
158
-
159
- <r-route-target name="sidebar">
160
- <!-- Sidebar content renders here -->
161
- </r-route-target>
162
- </body>
163
- ```
164
-
165
- ```typescript
166
- // Navigate to specific target
167
- navigate('userDetails', { params: { id: '123' }, target: 'modal' });
168
- ```
169
-
170
- ## Form Data Flow
171
-
172
- ```
173
- ┌─────────────┐ setFormData() ┌─────────────────────────────────────┐
174
- │ Data │ ──────────────────► │ Form Elements & Custom Components │
175
- │ Object │ │ │
176
- └─────────────┘ │ <input> <select> <textarea> │
177
- │ <r-input> <r-checkbox> (FORM API) │
178
- ▲ └────────┬────────────────────────────┘
179
- │ │
180
- │ readData() User edits
181
- │ │
182
- │ ▼
183
- ┌─────┴───────┐ FormValidator ┌─────────────────┐
184
- │ Updated │ ◄─────────────────── │ Validation │
185
- │ Object │ │ │
186
- └─────────────┘ │ - HTML5 │
187
- │ - Custom rules │
188
- │ - FORM API │
189
- │ components │
190
- └─────────────────┘
191
- ```
192
-
193
- ### Form Component Support
194
-
195
- The form utilities support both native HTML form elements and modern form-associated custom elements (those implementing the FORM API with `ElementInternals`). This means:
196
-
197
- - **Native elements**: `<input>`, `<select>`, `<textarea>` work as expected
198
- - **Custom components**: Web components like `<r-input>`, `<r-checkbox>`, `<r-select>` that use `ElementInternals` and `formAssociated = true` are fully integrated
199
- - **No API differences**: Both types are handled identically by `setFormData()`, `readData()`, `mapFormToClass()`, and `FormValidator`
200
-
201
- ## Template Rendering
202
-
203
- ### Static Templates (html)
204
-
205
- ```typescript
206
- const template = html`
207
- <div class="card">
208
- <h2>${'title'}</h2>
209
- <p>${'description'}</p>
210
- </div>
211
- `;
212
-
213
- // Creates new DOM each time
214
- const fragment = template({ title: 'Hello', description: 'World' });
215
- container.appendChild(fragment);
216
- ```
217
-
218
- ### Updateable Templates (html)
219
-
220
- ```typescript
221
- const template = html`
222
- <div class="user">
223
- <span>{{name}}</span>
224
- <span>{{score|number}}</span>
225
- </div>
226
- `;
227
-
228
- const rendered = template({ name: 'John', score: 42 });
229
- container.appendChild(rendered.fragment);
230
-
231
- // Later, update without recreating DOM
232
- rendered.update({ name: 'Jane', score: 100 });
233
- ```
234
-
235
- ## Service Architecture
236
-
237
- ```
238
- ┌────────────────────────────────────────────────────────┐
239
- │ ServiceCollection │
240
- │ │
241
- │ Register services with metadata: │
242
- │ - Constructor dependencies │
243
- │ - Property injections │
244
- │ - Scope (global/closest) │
245
- └───────────────────────────┬────────────────────────────┘
246
-
247
- │ Configuration
248
-
249
- ┌────────────────────────────────────────────────────────┐
250
- │ ServiceContainer │
251
- │ │
252
- │ Resolve services: │
253
- │ 1. Check cache (for singletons) │
254
- │ 2. Resolve dependencies │
255
- │ 3. Create instance │
256
- │ 4. Inject properties │
257
- │ 5. Cache if global scope │
258
- └────────────────────────────────────────────────────────┘
259
- ```
260
-
261
- ## File Organization
262
-
263
- Recommended project structure:
264
-
265
- ```
266
- src/
267
- ├── components/
268
- │ ├── forms/ # Form-related components
269
- │ ├── lists/ # List/table components
270
- │ └── shared/ # Shared UI components
271
- ├── pages/ # Route components
272
- ├── services/ # Business logic services
273
- ├── guards/ # Route guards
274
- ├── models/ # TypeScript interfaces/types
275
- └── app.ts # Application entry point
276
- ```
277
-
278
- ## Best Practices
279
-
280
- 1. **Use native HTML when possible**: Don't create components for things HTML already does well
281
-
282
- 2. **Keep components small**: Each component should do one thing
283
-
284
- 3. **Form Components Use FORM API**: When building custom form components, use the HTML Form API with `ElementInternals` and `formAssociated = true`. All RelaxJS form utilities automatically support these components without any special handling:
285
-
286
- ```typescript
287
- class CustomCheckbox extends HTMLElement {
288
- static formAssociated = true;
289
-
290
- private internals: ElementInternals;
291
-
292
- constructor() {
293
- super();
294
- this.internals = this.attachInternals();
295
- }
296
-
297
- connectedCallback() {
298
- const checkbox = document.createElement('input');
299
- checkbox.type = 'checkbox';
300
- this.appendChild(checkbox);
301
-
302
- checkbox.addEventListener('change', () => {
303
- this.internals.setFormValue(checkbox.checked ? 'on' : '');
304
- });
305
- }
306
-
307
- get checked() { return (this.querySelector('input') as HTMLInputElement).checked; }
308
- set checked(v) { (this.querySelector('input') as HTMLInputElement).checked = v; }
309
-
310
- get value() { return this.getAttribute('value') || 'on'; }
311
- }
312
-
313
- customElements.define('r-checkbox', CustomCheckbox);
314
- ```
315
-
316
- Once defined, use it seamlessly with form utilities:
317
-
318
- ```typescript
319
- const form = document.querySelector('form');
320
- const data = { termsAccepted: true };
321
- setFormData(form, data); // Works with <r-checkbox name="termsAccepted"></r-checkbox>
322
-
323
- const extracted = readData(form); // Extracts values from custom components
324
- const validator = new FormValidator(form); // Validates custom components
325
- ```
326
-
327
- 3. **Explicit over implicit**: Prefer explicit method calls over magic bindings
328
-
329
- 4. **Type everything**: Use TypeScript interfaces for all data structures
330
-
331
- 5. **CSS variables for theming**: Use semantic variable names
332
-
333
- 6. **Form-associated components**: Use `ElementInternals` for custom form controls
1
+ # Architecture Overview
2
+
3
+ Relaxjs is a library for building web applications with Web Components. It emphasizes explicit control, direct DOM manipulation, and predictable behavior.
4
+
5
+ ## Philosophy
6
+
7
+ Unlike modern SPA frameworks, Relaxjs takes an explicit approach:
8
+
9
+ - **No virtual DOM**: The real DOM is the source of truth
10
+ - **No hidden reactivity**: Changes happen when you explicitly make them
11
+ - **No synthetic lifecycle**: Uses native Web Component lifecycle hooks
12
+ - **Full developer control**: You always know what triggered what
13
+
14
+ ## Core Modules
15
+
16
+ ```
17
+ @relax.js/core
18
+ ├── Routing # SPA navigation with guards and layouts
19
+ ├── Forms # Data binding, validation, type conversion
20
+ ├── DI # Dependency injection container
21
+ ├── HTTP # Fetch wrapper and WebSocket client
22
+ ├── i18n # Internationalization with ICU support
23
+ ├── Templates # HTML templating with data binding
24
+ └── Components # Pre-built UI components
25
+ ```
26
+
27
+ ## Module Integration
28
+
29
+ ### Application Startup
30
+
31
+ ```typescript
32
+ import { defineRoutes, startRouting } from '@relax.js/core/routing';
33
+ import { setLocale } from '@relax.js/core/i18n';
34
+ import { serviceCollection } from '@relax.js/core/di';
35
+
36
+ class Application extends HTMLElement {
37
+ async connectedCallback() {
38
+ // 1. Register services
39
+ serviceCollection.registerByType(ApiService, { inject: [] });
40
+ serviceCollection.registerByType(AuthService, { inject: [ApiService] });
41
+
42
+ // 2. Set locale
43
+ await setLocale(navigator.language);
44
+
45
+ // 3. Define routes
46
+ defineRoutes([
47
+ { name: 'home', path: '/', componentTagName: 'app-home' },
48
+ { name: 'login', path: '/login', componentTagName: 'login-page' }
49
+ ]);
50
+
51
+ // 4. Start routing
52
+ startRouting();
53
+ }
54
+ }
55
+
56
+ customElements.define('app-main', Application);
57
+ ```
58
+
59
+ ### Component Pattern
60
+
61
+ ```typescript
62
+ class UserProfile extends HTMLElement {
63
+ private form: HTMLFormElement;
64
+ private validator: FormValidator;
65
+
66
+ connectedCallback() {
67
+ this.innerHTML = `
68
+ <form>
69
+ <input name="name" required>
70
+ <input name="email" type="email" required>
71
+ <button type="submit">Save</button>
72
+ </form>
73
+ `;
74
+
75
+ this.form = this.querySelector('form')!;
76
+ this.validator = new FormValidator(this.form, {
77
+ submitCallback: () => this.save()
78
+ });
79
+
80
+ this.loadUser();
81
+ }
82
+
83
+ async loadUser() {
84
+ const api = container.resolve(ApiService);
85
+ const user = await api.get('/user/profile');
86
+ setFormData(this.form, user.as<User>());
87
+ }
88
+
89
+ async save() {
90
+ const data = readData(this.form);
91
+ const api = container.resolve(ApiService);
92
+ await api.put('/user/profile', JSON.stringify(data));
93
+ }
94
+ }
95
+
96
+ customElements.define('user-profile', UserProfile);
97
+ ```
98
+
99
+ ## Data Flow
100
+
101
+ ```
102
+ ┌─────────────────────────────────────────────────────────┐
103
+ │ User Action │
104
+ └─────────────────────────────────────────────────────────┘
105
+
106
+
107
+ ┌─────────────────────────────────────────────────────────┐
108
+ │ Event Handler │
109
+ │ │
110
+ │ element.addEventListener('click', (e) => { │
111
+ │ // Explicit action │
112
+ │ }); │
113
+ └─────────────────────────────────────────────────────────┘
114
+
115
+
116
+ ┌─────────────────────────────────────────────────────────┐
117
+ │ DOM Update │
118
+ │ │
119
+ │ // Direct manipulation │
120
+ │ element.textContent = newValue; │
121
+ │ element.setAttribute('data-state', 'active'); │
122
+ └─────────────────────────────────────────────────────────┘
123
+ ```
124
+
125
+ ## Routing Architecture
126
+
127
+ ```
128
+ ┌──────────────┐ navigate() ┌──────────────────┐
129
+ │ User │ ────────────────► │ Router │
130
+ │ Action │ │ │
131
+ └──────────────┘ │ - Match route │
132
+ │ - Check guards │
133
+ │ - Update URL │
134
+ └────────┬─────────┘
135
+
136
+ NavigateRouteEvent
137
+
138
+
139
+ ┌──────────────────┐
140
+ │ r-route-target│
141
+ │ │
142
+ │ - Load component│
143
+ │ - Pass params │
144
+ └──────────────────┘
145
+ ```
146
+
147
+ ### Multiple Targets
148
+
149
+ ```html
150
+ <body>
151
+ <r-route-target>
152
+ <!-- Main content renders here -->
153
+ </r-route-target>
154
+
155
+ <r-route-target name="modal">
156
+ <!-- Modal content renders here -->
157
+ </r-route-target>
158
+
159
+ <r-route-target name="sidebar">
160
+ <!-- Sidebar content renders here -->
161
+ </r-route-target>
162
+ </body>
163
+ ```
164
+
165
+ ```typescript
166
+ // Navigate to specific target
167
+ navigate('userDetails', { params: { id: '123' }, target: 'modal' });
168
+ ```
169
+
170
+ ## Form Data Flow
171
+
172
+ ```
173
+ ┌─────────────┐ setFormData() ┌─────────────────────────────────────┐
174
+ │ Data │ ──────────────────► │ Form Elements & Custom Components │
175
+ │ Object │ │ │
176
+ └─────────────┘ │ <input> <select> <textarea> │
177
+ │ <r-input> <r-checkbox> (FORM API) │
178
+ ▲ └────────┬────────────────────────────┘
179
+ │ │
180
+ │ readData() User edits
181
+ │ │
182
+ │ ▼
183
+ ┌─────┴───────┐ FormValidator ┌─────────────────┐
184
+ │ Updated │ ◄─────────────────── │ Validation │
185
+ │ Object │ │ │
186
+ └─────────────┘ │ - HTML5 │
187
+ │ - Custom rules │
188
+ │ - FORM API │
189
+ │ components │
190
+ └─────────────────┘
191
+ ```
192
+
193
+ ### Form Component Support
194
+
195
+ The form utilities support both native HTML form elements and modern form-associated custom elements (those implementing the FORM API with `ElementInternals`). This means:
196
+
197
+ - **Native elements**: `<input>`, `<select>`, `<textarea>` work as expected
198
+ - **Custom components**: Web components like `<r-input>`, `<r-checkbox>`, `<r-select>` that use `ElementInternals` and `formAssociated = true` are fully integrated
199
+ - **No API differences**: Both types are handled identically by `setFormData()`, `readData()`, `mapFormToClass()`, and `FormValidator`
200
+
201
+ ## Template Rendering
202
+
203
+ ### Static Templates (html)
204
+
205
+ ```typescript
206
+ const template = html`
207
+ <div class="card">
208
+ <h2>${'title'}</h2>
209
+ <p>${'description'}</p>
210
+ </div>
211
+ `;
212
+
213
+ // Creates new DOM each time
214
+ const fragment = template({ title: 'Hello', description: 'World' });
215
+ container.appendChild(fragment);
216
+ ```
217
+
218
+ ### Updateable Templates (html)
219
+
220
+ ```typescript
221
+ const template = html`
222
+ <div class="user">
223
+ <span>{{name}}</span>
224
+ <span>{{score|number}}</span>
225
+ </div>
226
+ `;
227
+
228
+ const rendered = template({ name: 'John', score: 42 });
229
+ container.appendChild(rendered.fragment);
230
+
231
+ // Later, update without recreating DOM
232
+ rendered.update({ name: 'Jane', score: 100 });
233
+ ```
234
+
235
+ ## Service Architecture
236
+
237
+ ```
238
+ ┌────────────────────────────────────────────────────────┐
239
+ │ ServiceCollection │
240
+ │ │
241
+ │ Register services with metadata: │
242
+ │ - Constructor dependencies │
243
+ │ - Property injections │
244
+ │ - Scope (global/closest) │
245
+ └───────────────────────────┬────────────────────────────┘
246
+
247
+ │ Configuration
248
+
249
+ ┌────────────────────────────────────────────────────────┐
250
+ │ ServiceContainer │
251
+ │ │
252
+ │ Resolve services: │
253
+ │ 1. Check cache (for singletons) │
254
+ │ 2. Resolve dependencies │
255
+ │ 3. Create instance │
256
+ │ 4. Inject properties │
257
+ │ 5. Cache if global scope │
258
+ └────────────────────────────────────────────────────────┘
259
+ ```
260
+
261
+ ## File Organization
262
+
263
+ Recommended project structure:
264
+
265
+ ```
266
+ src/
267
+ ├── components/
268
+ │ ├── forms/ # Form-related components
269
+ │ ├── lists/ # List/table components
270
+ │ └── shared/ # Shared UI components
271
+ ├── pages/ # Route components
272
+ ├── services/ # Business logic services
273
+ ├── guards/ # Route guards
274
+ ├── models/ # TypeScript interfaces/types
275
+ └── app.ts # Application entry point
276
+ ```
277
+
278
+ ## Best Practices
279
+
280
+ 1. **Use native HTML when possible**: Don't create components for things HTML already does well
281
+
282
+ 2. **Keep components small**: Each component should do one thing
283
+
284
+ 3. **Form Components Use FORM API**: When building custom form components, use the HTML Form API with `ElementInternals` and `formAssociated = true`. All RelaxJS form utilities automatically support these components without any special handling:
285
+
286
+ ```typescript
287
+ class CustomCheckbox extends HTMLElement {
288
+ static formAssociated = true;
289
+
290
+ private internals: ElementInternals;
291
+
292
+ constructor() {
293
+ super();
294
+ this.internals = this.attachInternals();
295
+ }
296
+
297
+ connectedCallback() {
298
+ const checkbox = document.createElement('input');
299
+ checkbox.type = 'checkbox';
300
+ this.appendChild(checkbox);
301
+
302
+ checkbox.addEventListener('change', () => {
303
+ this.internals.setFormValue(checkbox.checked ? 'on' : '');
304
+ });
305
+ }
306
+
307
+ get checked() { return (this.querySelector('input') as HTMLInputElement).checked; }
308
+ set checked(v) { (this.querySelector('input') as HTMLInputElement).checked = v; }
309
+
310
+ get value() { return this.getAttribute('value') || 'on'; }
311
+ }
312
+
313
+ customElements.define('r-checkbox', CustomCheckbox);
314
+ ```
315
+
316
+ Once defined, use it seamlessly with form utilities:
317
+
318
+ ```typescript
319
+ const form = document.querySelector('form');
320
+ const data = { termsAccepted: true };
321
+ setFormData(form, data); // Works with <r-checkbox name="termsAccepted"></r-checkbox>
322
+
323
+ const extracted = readData(form); // Extracts values from custom components
324
+ const validator = new FormValidator(form); // Validates custom components
325
+ ```
326
+
327
+ 3. **Explicit over implicit**: Prefer explicit method calls over magic bindings
328
+
329
+ 4. **Type everything**: Use TypeScript interfaces for all data structures
330
+
331
+ 5. **CSS variables for theming**: Use semantic variable names
332
+
333
+ 6. **Form-associated components**: Use `ElementInternals` for custom form controls