@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,292 +1,292 @@
1
- # TableRenderer for Web Components
2
-
3
- `TableRenderer` is a small helper class that renders table rows inside Web Components using a `<template>` and an array of data. It supports data binding, row updates, and declarative button handlers that call methods on your Web Component.
4
-
5
- ## 1. Getting Started
6
-
7
- ### Minimal Web Component Setup
8
-
9
- ```ts
10
- import { TableRenderer } from 'relaxjs/html';
11
-
12
- class UserTable extends HTMLElement {
13
- private renderer!: TableRenderer;
14
-
15
- constructor() {
16
- super();
17
- this.attachShadow({ mode: 'open' });
18
- }
19
-
20
- connectedCallback() {
21
- this.shadowRoot!.innerHTML = `
22
- <table>
23
- <tbody></tbody>
24
- </table>
25
- <template id="row-template">
26
- <tr>
27
- <td data-field="name"></td>
28
- <td data-field="role"></td>
29
- </tr>
30
- </template>
31
- `;
32
-
33
- const table = this.shadowRoot!.querySelector('table')!;
34
- const template = this.shadowRoot!.querySelector('#row-template') as HTMLTemplateElement;
35
-
36
- this.renderer = new TableRenderer(table, template, 'id', this);
37
- this.renderer.render([
38
- { id: 1, name: 'Alice', role: 'Admin' },
39
- { id: 2, name: 'Bob', role: 'User' }
40
- ]);
41
- }
42
- }
43
-
44
- customElements.define('user-table', UserTable);
45
- ```
46
-
47
- **What this does:**
48
- - Clones the `<template>` row for each item
49
- - Fills in `<td data-field="name">` using data values
50
- - Associates each row with its data via the `id` field
51
-
52
- ## 🛠 2. Updating a Row
53
-
54
- Use `update()` to refresh a single row without re-rendering everything:
55
-
56
- ```ts
57
- this.renderer.update({ id: 2, name: 'Bob Smith', role: 'Moderator' });
58
- ```
59
-
60
- If a matching row exists (`idColumn` = `"id"`), it will update in-place. If not, a new row is added.
61
-
62
- ## 🎯 3. Button Event Binding
63
-
64
- Add buttons in the template using `onclick="methodName"`.
65
-
66
- ### Updated Template
67
-
68
- ```html
69
- <template id="row-template">
70
- <tr>
71
- <td data-field="name"></td>
72
- <td data-field="role"></td>
73
- <td>
74
- <button onclick="edit">Edit</button>
75
- </td>
76
- </tr>
77
- </template>
78
- ```
79
-
80
- ### Component Method
81
-
82
- ```ts
83
- edit(data: any, event: MouseEvent) {
84
- console.log('Editing user:', data);
85
- }
86
- ```
87
-
88
- **Behavior:**
89
- - `edit` is automatically bound for each row
90
- - It receives the row's `data` as the first argument
91
- - The second argument is the `MouseEvent`
92
-
93
- ## 📦 4. Using Arguments in Handlers
94
-
95
- You can call methods with literal arguments. The remaining arguments are always `data` and `event`.
96
-
97
- ### Template Example
98
-
99
- ```html
100
- <button onclick="remove('soft')">Delete</button>
101
- ```
102
-
103
- ### Component Method
104
-
105
- ```ts
106
- remove(mode: string, data: any, event: MouseEvent) {
107
- console.log(`Removing (${mode})`, data);
108
- }
109
- ```
110
-
111
- **How It Works:**
112
- - The string `'soft'` is passed as the first argument
113
- - `data` and `event` are appended automatically
114
-
115
- ## 🧼 5. Re-rendering Safely
116
-
117
- The `.render()` method replaces all rows and cleans up memory:
118
-
119
- ```ts
120
- this.renderer.render([
121
- { id: 3, name: 'Charlie', role: 'User' }
122
- ]);
123
- ```
124
-
125
- - Clears old rows and their event listeners
126
- - Rebuilds new rows from the template and data
127
-
128
- ## 6. Column Sorting with TableSorter
129
-
130
- `TableSorter` adds click-to-sort behavior to table headers. Click a `<th>` to cycle through ascending, descending, and unsorted.
131
-
132
- ### Setup
133
-
134
- ```ts
135
- import { TableSorter } from 'relaxjs/html';
136
-
137
- class UserTable extends HTMLElement {
138
- connectedCallback() {
139
- this.innerHTML = `
140
- <table>
141
- <thead>
142
- <tr>
143
- <th name="name">Name</th>
144
- <th name="role">Role</th>
145
- <th name="age">Age</th>
146
- </tr>
147
- </thead>
148
- <tbody></tbody>
149
- </table>
150
- `;
151
-
152
- const table = this.querySelector('table')!;
153
- const sorter = new TableSorter(table, this);
154
- }
155
- }
156
- ```
157
-
158
- **How it works:**
159
- - Headers must have a `name` attribute matching the data field
160
- - Click cycles: none -> ascending (arrow up) -> descending (arrow down) -> none
161
- - Sort indicators (arrows) are added/removed automatically
162
-
163
- ### Handling Sort Events
164
-
165
- `TableSorter` dispatches a `SortChangeEvent` on the component:
166
-
167
- ```ts
168
- import { SortChangeEvent, SortColumn } from 'relaxjs/html';
169
-
170
- this.addEventListener('sortchange', (e: SortChangeEvent) => {
171
- const sortColumns: SortColumn[] = e.detail;
172
- // sortColumns = [{ column: 'name', direction: 'asc' }]
173
- this.fetchSortedData(sortColumns);
174
- });
175
- ```
176
-
177
- ### API
178
-
179
- | Method | Description |
180
- |--------|-------------|
181
- | `getSortColumns()` | Returns current sort state as `SortColumn[]` |
182
- | `clear()` | Resets all sorting and emits a `sortchange` event |
183
-
184
- ```typescript
185
- type SortColumn = { column: string; direction: 'asc' | 'desc' };
186
- ```
187
-
188
- ## 7. Pagination with Pager
189
-
190
- `Pager` renders page navigation buttons (Previous, page numbers, Next) and dispatches events on page change.
191
-
192
- ### Setup
193
-
194
- ```ts
195
- import { Pager, PageSelectedEvent } from 'relaxjs/html';
196
-
197
- class UserTable extends HTMLElement {
198
- private pager!: Pager;
199
-
200
- connectedCallback() {
201
- this.innerHTML = `
202
- <table><!-- ... --></table>
203
- <div id="pager"></div>
204
- `;
205
-
206
- const pagerContainer = this.querySelector('#pager')!;
207
- this.pager = new Pager(pagerContainer, 100, 10); // 100 items, 10 per page
208
-
209
- pagerContainer.addEventListener('pageselected', (e: PageSelectedEvent) => {
210
- this.loadPage(e.detail);
211
- });
212
- }
213
-
214
- async loadPage(page: number) {
215
- const data = await fetchUsers(page);
216
- this.renderer.render(data);
217
- }
218
- }
219
- ```
220
-
221
- ### Updating Total Count
222
-
223
- When the total number of items changes (e.g. after filtering):
224
-
225
- ```ts
226
- this.pager.update(newTotalCount);
227
- ```
228
-
229
- ### API
230
-
231
- | Method | Description |
232
- |--------|-------------|
233
- | `update(totalCount)` | Updates total count and re-renders page buttons |
234
- | `getCurrentPage()` | Returns the current page number |
235
-
236
- ### Combined Example: TableRenderer + TableSorter + Pager
237
-
238
- ```ts
239
- import { TableRenderer, TableSorter, Pager } from 'relaxjs/html';
240
-
241
- class ProductList extends HTMLElement {
242
- private renderer!: TableRenderer;
243
- private sorter!: TableSorter;
244
- private pager!: Pager;
245
-
246
- connectedCallback() {
247
- this.innerHTML = `
248
- <table>
249
- <thead>
250
- <tr>
251
- <th name="name">Name</th>
252
- <th name="price">Price</th>
253
- </tr>
254
- </thead>
255
- <tbody></tbody>
256
- </table>
257
- <template id="row-tpl">
258
- <tr>
259
- <td data-field="name"></td>
260
- <td data-field="price"></td>
261
- <td><button onclick="edit">Edit</button></td>
262
- </tr>
263
- </template>
264
- <div id="pager"></div>
265
- `;
266
-
267
- const table = this.querySelector('table')!;
268
- const template = this.querySelector('#row-tpl') as HTMLTemplateElement;
269
-
270
- this.renderer = new TableRenderer(table, template, 'id', this);
271
- this.sorter = new TableSorter(table, this);
272
- this.pager = new Pager(this.querySelector('#pager')!, 0, 20);
273
-
274
- this.addEventListener('sortchange', () => this.reload());
275
- this.querySelector('#pager')!.addEventListener('pageselected', () => this.reload());
276
-
277
- this.reload();
278
- }
279
-
280
- async reload() {
281
- const page = this.pager.getCurrentPage();
282
- const sort = this.sorter.getSortColumns();
283
- const { items, total } = await fetchProducts(page, sort);
284
- this.renderer.render(items);
285
- this.pager.update(total);
286
- }
287
-
288
- edit(data: any) {
289
- console.log('Edit product:', data);
290
- }
291
- }
1
+ # TableRenderer for Web Components
2
+
3
+ `TableRenderer` is a small helper class that renders table rows inside Web Components using a `<template>` and an array of data. It supports data binding, row updates, and declarative button handlers that call methods on your Web Component.
4
+
5
+ ## 1. Getting Started
6
+
7
+ ### Minimal Web Component Setup
8
+
9
+ ```ts
10
+ import { TableRenderer } from '@relax.js/core/html';
11
+
12
+ class UserTable extends HTMLElement {
13
+ private renderer!: TableRenderer;
14
+
15
+ constructor() {
16
+ super();
17
+ this.attachShadow({ mode: 'open' });
18
+ }
19
+
20
+ connectedCallback() {
21
+ this.shadowRoot!.innerHTML = `
22
+ <table>
23
+ <tbody></tbody>
24
+ </table>
25
+ <template id="row-template">
26
+ <tr>
27
+ <td data-field="name"></td>
28
+ <td data-field="role"></td>
29
+ </tr>
30
+ </template>
31
+ `;
32
+
33
+ const table = this.shadowRoot!.querySelector('table')!;
34
+ const template = this.shadowRoot!.querySelector('#row-template') as HTMLTemplateElement;
35
+
36
+ this.renderer = new TableRenderer(table, template, 'id', this);
37
+ this.renderer.render([
38
+ { id: 1, name: 'Alice', role: 'Admin' },
39
+ { id: 2, name: 'Bob', role: 'User' }
40
+ ]);
41
+ }
42
+ }
43
+
44
+ customElements.define('user-table', UserTable);
45
+ ```
46
+
47
+ **What this does:**
48
+ - Clones the `<template>` row for each item
49
+ - Fills in `<td data-field="name">` using data values
50
+ - Associates each row with its data via the `id` field
51
+
52
+ ## 2. Updating a Row
53
+
54
+ Use `update()` to refresh a single row without re-rendering everything:
55
+
56
+ ```ts
57
+ this.renderer.update({ id: 2, name: 'Bob Smith', role: 'Moderator' });
58
+ ```
59
+
60
+ If a matching row exists (`idColumn` = `"id"`), it will update in-place. If not, a new row is added.
61
+
62
+ ## 3. Button Event Binding
63
+
64
+ Add buttons in the template using `onclick="methodName"`.
65
+
66
+ ### Updated Template
67
+
68
+ ```html
69
+ <template id="row-template">
70
+ <tr>
71
+ <td data-field="name"></td>
72
+ <td data-field="role"></td>
73
+ <td>
74
+ <button onclick="edit">Edit</button>
75
+ </td>
76
+ </tr>
77
+ </template>
78
+ ```
79
+
80
+ ### Component Method
81
+
82
+ ```ts
83
+ edit(data: any, event: MouseEvent) {
84
+ console.log('Editing user:', data);
85
+ }
86
+ ```
87
+
88
+ **Behavior:**
89
+ - `edit` is automatically bound for each row
90
+ - It receives the row's `data` as the first argument
91
+ - The second argument is the `MouseEvent`
92
+
93
+ ## 4. Using Arguments in Handlers
94
+
95
+ You can call methods with literal arguments. The remaining arguments are always `data` and `event`.
96
+
97
+ ### Template Example
98
+
99
+ ```html
100
+ <button onclick="remove('soft')">Delete</button>
101
+ ```
102
+
103
+ ### Component Method
104
+
105
+ ```ts
106
+ remove(mode: string, data: any, event: MouseEvent) {
107
+ console.log(`Removing (${mode})`, data);
108
+ }
109
+ ```
110
+
111
+ **How It Works:**
112
+ - The string `'soft'` is passed as the first argument
113
+ - `data` and `event` are appended automatically
114
+
115
+ ## 5. Re-rendering Safely
116
+
117
+ The `.render()` method replaces all rows and cleans up memory:
118
+
119
+ ```ts
120
+ this.renderer.render([
121
+ { id: 3, name: 'Charlie', role: 'User' }
122
+ ]);
123
+ ```
124
+
125
+ - Clears old rows and their event listeners
126
+ - Rebuilds new rows from the template and data
127
+
128
+ ## 6. Column Sorting with TableSorter
129
+
130
+ `TableSorter` adds click-to-sort behavior to table headers. Click a `<th>` to cycle through ascending, descending, and unsorted.
131
+
132
+ ### Setup
133
+
134
+ ```ts
135
+ import { TableSorter } from '@relax.js/core/html';
136
+
137
+ class UserTable extends HTMLElement {
138
+ connectedCallback() {
139
+ this.innerHTML = `
140
+ <table>
141
+ <thead>
142
+ <tr>
143
+ <th name="name">Name</th>
144
+ <th name="role">Role</th>
145
+ <th name="age">Age</th>
146
+ </tr>
147
+ </thead>
148
+ <tbody></tbody>
149
+ </table>
150
+ `;
151
+
152
+ const table = this.querySelector('table')!;
153
+ const sorter = new TableSorter(table, this);
154
+ }
155
+ }
156
+ ```
157
+
158
+ **How it works:**
159
+ - Headers must have a `name` attribute matching the data field
160
+ - Click cycles: none -> ascending (arrow up) -> descending (arrow down) -> none
161
+ - Sort indicators (arrows) are added/removed automatically
162
+
163
+ ### Handling Sort Events
164
+
165
+ `TableSorter` dispatches a `SortChangeEvent` on the component:
166
+
167
+ ```ts
168
+ import { SortChangeEvent, SortColumn } from '@relax.js/core/html';
169
+
170
+ this.addEventListener('sortchange', (e: SortChangeEvent) => {
171
+ const sortColumns: SortColumn[] = e.detail;
172
+ // sortColumns = [{ column: 'name', direction: 'asc' }]
173
+ this.fetchSortedData(sortColumns);
174
+ });
175
+ ```
176
+
177
+ ### API
178
+
179
+ | Method | Description |
180
+ |--------|-------------|
181
+ | `getSortColumns()` | Returns current sort state as `SortColumn[]` |
182
+ | `clear()` | Resets all sorting and emits a `sortchange` event |
183
+
184
+ ```typescript
185
+ type SortColumn = { column: string; direction: 'asc' | 'desc' };
186
+ ```
187
+
188
+ ## 7. Pagination with Pager
189
+
190
+ `Pager` renders page navigation buttons (Previous, page numbers, Next) and dispatches events on page change.
191
+
192
+ ### Setup
193
+
194
+ ```ts
195
+ import { Pager, PageSelectedEvent } from '@relax.js/core/html';
196
+
197
+ class UserTable extends HTMLElement {
198
+ private pager!: Pager;
199
+
200
+ connectedCallback() {
201
+ this.innerHTML = `
202
+ <table><!-- ... --></table>
203
+ <div id="pager"></div>
204
+ `;
205
+
206
+ const pagerContainer = this.querySelector('#pager')!;
207
+ this.pager = new Pager(pagerContainer, 100, 10); // 100 items, 10 per page
208
+
209
+ pagerContainer.addEventListener('pageselected', (e: PageSelectedEvent) => {
210
+ this.loadPage(e.detail);
211
+ });
212
+ }
213
+
214
+ async loadPage(page: number) {
215
+ const data = await fetchUsers(page);
216
+ this.renderer.render(data);
217
+ }
218
+ }
219
+ ```
220
+
221
+ ### Updating Total Count
222
+
223
+ When the total number of items changes (e.g. after filtering):
224
+
225
+ ```ts
226
+ this.pager.update(newTotalCount);
227
+ ```
228
+
229
+ ### API
230
+
231
+ | Method | Description |
232
+ |--------|-------------|
233
+ | `update(totalCount)` | Updates total count and re-renders page buttons |
234
+ | `getCurrentPage()` | Returns the current page number |
235
+
236
+ ### Combined Example: TableRenderer + TableSorter + Pager
237
+
238
+ ```ts
239
+ import { TableRenderer, TableSorter, Pager } from '@relax.js/core/html';
240
+
241
+ class ProductList extends HTMLElement {
242
+ private renderer!: TableRenderer;
243
+ private sorter!: TableSorter;
244
+ private pager!: Pager;
245
+
246
+ connectedCallback() {
247
+ this.innerHTML = `
248
+ <table>
249
+ <thead>
250
+ <tr>
251
+ <th name="name">Name</th>
252
+ <th name="price">Price</th>
253
+ </tr>
254
+ </thead>
255
+ <tbody></tbody>
256
+ </table>
257
+ <template id="row-tpl">
258
+ <tr>
259
+ <td data-field="name"></td>
260
+ <td data-field="price"></td>
261
+ <td><button onclick="edit">Edit</button></td>
262
+ </tr>
263
+ </template>
264
+ <div id="pager"></div>
265
+ `;
266
+
267
+ const table = this.querySelector('table')!;
268
+ const template = this.querySelector('#row-tpl') as HTMLTemplateElement;
269
+
270
+ this.renderer = new TableRenderer(table, template, 'id', this);
271
+ this.sorter = new TableSorter(table, this);
272
+ this.pager = new Pager(this.querySelector('#pager')!, 0, 20);
273
+
274
+ this.addEventListener('sortchange', () => this.reload());
275
+ this.querySelector('#pager')!.addEventListener('pageselected', () => this.reload());
276
+
277
+ this.reload();
278
+ }
279
+
280
+ async reload() {
281
+ const page = this.pager.getCurrentPage();
282
+ const sort = this.sorter.getSortColumns();
283
+ const { items, total } = await fetchProducts(page, sort);
284
+ this.renderer.render(items);
285
+ this.pager.update(total);
286
+ }
287
+
288
+ edit(data: any) {
289
+ console.log('Edit product:', data);
290
+ }
291
+ }
292
292
  ```