@relax.js/core 1.0.3 → 1.0.5

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 (90) hide show
  1. package/README.md +194 -188
  2. package/dist/DependencyInjection.d.ts +45 -27
  3. package/dist/collections/LinkedList.d.ts +9 -8
  4. package/dist/collections/index.js +1 -1
  5. package/dist/collections/index.js.map +3 -3
  6. package/dist/collections/index.mjs +1 -1
  7. package/dist/collections/index.mjs.map +3 -3
  8. package/dist/di/index.js +1 -1
  9. package/dist/di/index.js.map +3 -3
  10. package/dist/di/index.mjs +1 -1
  11. package/dist/di/index.mjs.map +3 -3
  12. package/dist/elements/index.js +1 -1
  13. package/dist/elements/index.js.map +1 -1
  14. package/dist/errors.d.ts +20 -0
  15. package/dist/forms/FormValidator.d.ts +3 -22
  16. package/dist/forms/ValidationRules.d.ts +4 -6
  17. package/dist/forms/index.js +1 -1
  18. package/dist/forms/index.js.map +4 -4
  19. package/dist/forms/index.mjs +1 -1
  20. package/dist/forms/index.mjs.map +4 -4
  21. package/dist/forms/setFormData.d.ts +39 -1
  22. package/dist/html/TableRenderer.d.ts +1 -0
  23. package/dist/html/index.js +1 -1
  24. package/dist/html/index.js.map +3 -3
  25. package/dist/html/index.mjs +1 -1
  26. package/dist/html/index.mjs.map +3 -3
  27. package/dist/html/template.d.ts +4 -0
  28. package/dist/http/ServerSentEvents.d.ts +1 -1
  29. package/dist/http/SimpleWebSocket.d.ts +1 -1
  30. package/dist/http/http.d.ts +1 -0
  31. package/dist/http/index.js +1 -1
  32. package/dist/http/index.js.map +3 -3
  33. package/dist/http/index.mjs +1 -1
  34. package/dist/http/index.mjs.map +3 -3
  35. package/dist/i18n/icu.d.ts +1 -1
  36. package/dist/i18n/index.js +1 -1
  37. package/dist/i18n/index.js.map +2 -2
  38. package/dist/i18n/index.mjs +1 -1
  39. package/dist/i18n/index.mjs.map +2 -2
  40. package/dist/index.js +3 -3
  41. package/dist/index.js.map +3 -3
  42. package/dist/index.mjs +3 -3
  43. package/dist/index.mjs.map +3 -3
  44. package/dist/routing/NavigateRouteEvent.d.ts +4 -4
  45. package/dist/routing/index.js +3 -3
  46. package/dist/routing/index.js.map +3 -3
  47. package/dist/routing/index.mjs +3 -3
  48. package/dist/routing/index.mjs.map +3 -3
  49. package/dist/routing/navigation.d.ts +1 -1
  50. package/dist/routing/routeTargetRegistry.d.ts +1 -0
  51. package/dist/routing/types.d.ts +2 -1
  52. package/dist/templates/NodeTemplate.d.ts +3 -1
  53. package/dist/utils/index.d.ts +1 -1
  54. package/dist/utils/index.js +1 -1
  55. package/dist/utils/index.js.map +3 -3
  56. package/dist/utils/index.mjs +1 -1
  57. package/dist/utils/index.mjs.map +3 -3
  58. package/docs/Architecture.md +333 -333
  59. package/docs/DependencyInjection.md +277 -237
  60. package/docs/Errors.md +87 -87
  61. package/docs/GettingStarted.md +238 -231
  62. package/docs/Pipes.md +5 -5
  63. package/docs/Translations.md +167 -312
  64. package/docs/WhyRelaxjs.md +336 -336
  65. package/docs/api.json +93193 -0
  66. package/docs/elements/dom.md +102 -102
  67. package/docs/forms/creating-form-components.md +924 -924
  68. package/docs/forms/form-api.md +94 -94
  69. package/docs/forms/forms.md +99 -99
  70. package/docs/forms/patterns.md +311 -311
  71. package/docs/forms/reading-writing.md +465 -365
  72. package/docs/forms/validation.md +351 -351
  73. package/docs/html/TableRenderer.md +291 -291
  74. package/docs/html/html.md +175 -175
  75. package/docs/html/index.md +54 -54
  76. package/docs/html/template.md +422 -422
  77. package/docs/http/HttpClient.md +459 -459
  78. package/docs/http/ServerSentEvents.md +184 -184
  79. package/docs/http/index.md +109 -109
  80. package/docs/i18n/i18n.md +49 -4
  81. package/docs/i18n/intl-standard.md +178 -178
  82. package/docs/routing/RouteLink.md +98 -98
  83. package/docs/routing/Routing.md +332 -332
  84. package/docs/routing/layouts.md +207 -207
  85. package/docs/setup/bootstrapping.md +154 -0
  86. package/docs/setup/build-and-deploy.md +183 -0
  87. package/docs/setup/project-structure.md +170 -0
  88. package/docs/setup/vite.md +175 -0
  89. package/docs/utilities.md +143 -143
  90. package/package.json +4 -2
@@ -1,365 +1,465 @@
1
- # Reading & Writing Form Data
2
-
3
- Functions for reading and writing form data with automatic type conversion.
4
-
5
- ## mapFormToClass
6
-
7
- Maps form field values to a class instance's properties with type conversion. Provides full type safety by populating an existing typed object.
8
-
9
- ```typescript
10
- import { mapFormToClass } from 'relaxjs/forms';
11
-
12
- class UserDTO {
13
- name: string = '';
14
- email: string = '';
15
- age: number = 0;
16
- newsletter: boolean = false;
17
- }
18
-
19
- const form = document.querySelector('form');
20
- const user = mapFormToClass(form, new UserDTO());
21
- console.log(user.name, user.age, user.newsletter);
22
- ```
23
-
24
- ### Options
25
-
26
- ```typescript
27
- // Options object (inline, not a named interface)
28
- {
29
- throwOnMissingProperty?: boolean; // Throw if form field has no matching property
30
- throwOnMissingField?: boolean; // Throw if class property has no matching field
31
- }
32
- ```
33
-
34
- ### Strict Validation Mode
35
-
36
- Catch mismatches between form fields and your data model:
37
-
38
- ```typescript
39
- const user = mapFormToClass(form, new UserDTO(), {
40
- throwOnMissingProperty: true, // Catch typos in form field names
41
- throwOnMissingField: true // Ensure all DTO fields are in form
42
- });
43
- ```
44
-
45
- ### Type Conversion
46
-
47
- Automatic conversion based on input types:
48
-
49
- | Input Type | Converted To |
50
- |------------|--------------|
51
- | `checkbox` | `boolean` |
52
- | `number` | `number` |
53
- | `date` | `Date` |
54
-
55
- ```typescript
56
- class ProductDTO {
57
- name: string = '';
58
- price: number = 0;
59
- inStock: boolean = false;
60
- releaseDate: Date | null = null;
61
- }
62
-
63
- const product = mapFormToClass(form, new ProductDTO());
64
- // product.price is number, product.inStock is boolean
65
- ```
66
-
67
- ## readData
68
-
69
- Reads all form data into a plain object with automatic type conversion. Use when you don't need strict typing.
70
-
71
- ```typescript
72
- import { readData } from 'relaxjs/forms';
73
-
74
- const form = document.querySelector('form');
75
- const data = readData(form);
76
- ```
77
-
78
- ### Type Conversion
79
-
80
- Type conversion is determined by:
81
- 1. `data-type` attribute if present (`number`, `boolean`, `string`, `Date`)
82
- 2. Input type (`checkbox`, `number`, `date`, etc.)
83
- 3. Falls back to string
84
-
85
- ```html
86
- <form>
87
- <input name="username" value="john" />
88
- <input name="age" type="number" value="25" />
89
- <input name="active" type="checkbox" checked />
90
- <input name="salary" data-type="number" value="50000" />
91
- <select name="colors" multiple>
92
- <option value="red" selected>Red</option>
93
- <option value="blue" selected>Blue</option>
94
- </select>
95
- </form>
96
- ```
97
-
98
- ```typescript
99
- const data = readData(form);
100
- // Returns: {
101
- // username: 'john',
102
- // age: 25,
103
- // active: true,
104
- // salary: 50000,
105
- // colors: ['red', 'blue']
106
- // }
107
- ```
108
-
109
- ### Checkbox Handling
110
-
111
- Unchecked checkboxes are included as `false` in the result. Checked checkboxes with no explicit `value` attribute (which submit as `'on'` in FormData) are correctly converted to `true`.
112
-
113
- ```html
114
- <input name="active" type="checkbox" checked />
115
- <input name="terms" type="checkbox" />
116
- ```
117
-
118
- ```typescript
119
- const data = readData(form);
120
- // Returns: { active: true, terms: false }
121
- ```
122
-
123
- ### Custom Form Components
124
-
125
- Works with form-associated custom elements:
126
-
127
- ```html
128
- <form>
129
- <r-input name="email" value="test@example.com"></r-input>
130
- <r-checkbox name="terms" checked></r-checkbox>
131
- </form>
132
- ```
133
-
134
- ```typescript
135
- const data = readData(form);
136
- // Returns: { email: 'test@example.com', terms: true }
137
- ```
138
-
139
- ## setFormData
140
-
141
- Populates form fields from a data object using the `name` attribute.
142
-
143
- ```typescript
144
- import { setFormData } from 'relaxjs/forms';
145
-
146
- const form = document.querySelector('form');
147
- const data = { name: 'John', email: 'john@example.com' };
148
- setFormData(form, data);
149
- ```
150
-
151
- ### Nested Objects (Dot Notation)
152
-
153
- ```html
154
- <form>
155
- <input name="user.name" />
156
- <input name="user.contact.email" />
157
- </form>
158
- ```
159
-
160
- ```typescript
161
- const data = {
162
- user: {
163
- name: 'John',
164
- contact: {
165
- email: 'john@example.com'
166
- }
167
- }
168
- };
169
- setFormData(form, data);
170
- ```
171
-
172
- ### Date and DateTime
173
-
174
- `setFormData` formats `Date` objects to the correct string format for each input type:
175
-
176
- ```html
177
- <input type="date" name="birthday" />
178
- <input type="datetime-local" name="meeting" />
179
- ```
180
-
181
- ```typescript
182
- setFormData(form, {
183
- birthday: new Date('2000-01-15'), // Sets "2000-01-15"
184
- meeting: new Date('2024-06-15T14:30') // Sets "2024-06-15T14:30"
185
- });
186
- ```
187
-
188
- ### Select Multiple
189
-
190
- Array values are set directly on `<select multiple>` elements:
191
-
192
- ```html
193
- <select name="colors" multiple>
194
- <option value="red">Red</option>
195
- <option value="green">Green</option>
196
- <option value="blue">Blue</option>
197
- </select>
198
- ```
199
-
200
- ```typescript
201
- setFormData(form, { colors: ['red', 'blue'] });
202
- // Selects "red" and "blue", deselects "green"
203
- ```
204
-
205
- ### Simple Arrays ([] Notation)
206
-
207
- For checkboxes, multi-select, and text inputs:
208
-
209
- ```html
210
- <form>
211
- <input name="hobbies[]" type="checkbox" value="Reading" />
212
- <input name="hobbies[]" type="checkbox" value="Cycling" />
213
- <input name="hobbies[]" type="checkbox" value="Cooking" />
214
- </form>
215
- ```
216
-
217
- ```typescript
218
- const data = {
219
- hobbies: ['Reading', 'Cooking']
220
- };
221
- setFormData(form, data);
222
- // Checks "Reading" and "Cooking" checkboxes
223
- ```
224
-
225
- ### Array of Objects (Indexed Notation)
226
-
227
- ```html
228
- <form>
229
- <input name="users[0].name" />
230
- <input name="users[0].email" />
231
- <input name="users[1].name" />
232
- <input name="users[1].email" />
233
- </form>
234
- ```
235
-
236
- ```typescript
237
- const data = {
238
- users: [
239
- { name: 'John', email: 'john@example.com' },
240
- { name: 'Jane', email: 'jane@example.com' }
241
- ]
242
- };
243
- setFormData(form, data);
244
- ```
245
-
246
- ### Complex Data Structures
247
-
248
- ```html
249
- <form id="product-form">
250
- <input name="product.name" placeholder="Product Name">
251
- <input name="product.price" type="number" placeholder="Price">
252
-
253
- <input name="categories[]" type="checkbox" value="electronics">
254
- <input name="categories[]" type="checkbox" value="gadgets">
255
-
256
- <input name="variants[0].size" placeholder="Size">
257
- <input name="variants[0].color" placeholder="Color">
258
- <input name="variants[1].size" placeholder="Size">
259
- <input name="variants[1].color" placeholder="Color">
260
-
261
- <input name="supplier.company" placeholder="Company">
262
- <input name="supplier.contact.email" placeholder="Email">
263
- </form>
264
- ```
265
-
266
- ```typescript
267
- const productData = {
268
- product: {
269
- name: 'Wireless Headphones',
270
- price: 99.99
271
- },
272
- categories: ['electronics', 'gadgets'],
273
- variants: [
274
- { size: 'M', color: 'black' },
275
- { size: 'L', color: 'white' }
276
- ],
277
- supplier: {
278
- company: 'TechCorp',
279
- contact: {
280
- email: 'orders@techcorp.com'
281
- }
282
- }
283
- };
284
-
285
- setFormData(form, productData);
286
-
287
- // Later, extract the data
288
- const extractedData = readData(form);
289
- // Result matches the original structure
290
- ```
291
-
292
- ## Type Converters
293
-
294
- ### Built-in Converters
295
-
296
- | Converter | Input | Output |
297
- |-----------|-------|--------|
298
- | `BooleanConverter` | `'true'`, `'on'`, `'1'`, any positive number string -> `true`; `'false'`, `'off'`, `'0'` -> `false` | `boolean` |
299
- | `NumberConverter` | Numeric string | `number` |
300
- | `DateConverter` | ISO or locale-formatted date string | `Date` |
301
-
302
- #### Locale-Aware Date Parsing
303
-
304
- `DateConverter` detects the current locale and parses dates in the local format:
305
-
306
- | Locale | Format | Example |
307
- |--------|--------|---------|
308
- | `en` (US) | `MM/DD/YYYY` | `01/15/2024` |
309
- | `sv` (Swedish) | `YYYY-MM-DD` | `2024-01-15` |
310
- | `de` (German) | `DD.MM.YYYY` | `15.01.2024` |
311
-
312
- ISO format (`2024-01-15`) is always accepted regardless of locale.
313
-
314
- ### Custom Type via `data-type` Attribute
315
-
316
- Use the `data-type` attribute to override conversion for any field:
317
-
318
- ```html
319
- <input name="salary" data-type="number" value="50000" />
320
- <input name="active" data-type="boolean" value="true" />
321
- <input name="birthday" data-type="Date" value="2000-01-15" />
322
- ```
323
-
324
- ```typescript
325
- const data = readData(form);
326
- console.log(data.salary); // number: 50000
327
- console.log(data.active); // boolean: true
328
- console.log(data.birthday); // Date object
329
- ```
330
-
331
- Supported `data-type` values: `number`, `boolean`, `string`, `Date`.
332
-
333
- ### Date and Time Handling
334
-
335
- ```html
336
- <input name="startDate" type="date">
337
- <input name="startTime" type="time">
338
- <input name="duration" type="time" step="900"> <!-- 15 min steps -->
339
- ```
340
-
341
- ```typescript
342
- // Custom handling for combining date and time
343
- document.querySelector('[name="startTime"]').getData = function() {
344
- const dateInput = form.querySelector('[name="startDate"]') as HTMLInputElement;
345
- const timeInput = this as HTMLInputElement;
346
-
347
- if (dateInput.value && timeInput.value) {
348
- return new Date(`${dateInput.value}T${timeInput.value}`);
349
- }
350
- return null;
351
- };
352
- ```
353
-
354
- ### Input Type Conversion Table
355
-
356
- | Input Type | Converted To |
357
- |------------|--------------|
358
- | `checkbox` | `boolean` |
359
- | `number` | `number` |
360
- | `date` | `Date` |
361
- | `datetime-local` | `Date` |
362
- | `month` | `Date` (first of month) |
363
- | `week` | `{ year, week }` |
364
- | `time` | `{ hours, minutes, seconds }` |
365
- | Default | `string` |
1
+ # Reading & Writing Form Data
2
+
3
+ Functions for reading and writing form data with automatic type conversion.
4
+
5
+ ## mapFormToClass
6
+
7
+ Maps form field values to a class instance's properties with type conversion. Provides full type safety by populating an existing typed object.
8
+
9
+ ```typescript
10
+ import { mapFormToClass } from '@relax.js/core/forms';
11
+
12
+ class UserDTO {
13
+ name: string = '';
14
+ email: string = '';
15
+ age: number = 0;
16
+ newsletter: boolean = false;
17
+ }
18
+
19
+ const form = document.querySelector('form');
20
+ const user = mapFormToClass(form, new UserDTO());
21
+ console.log(user.name, user.age, user.newsletter);
22
+ ```
23
+
24
+ ### Options
25
+
26
+ ```typescript
27
+ // Options object (inline, not a named interface)
28
+ {
29
+ throwOnMissingProperty?: boolean; // Throw if form field has no matching property
30
+ throwOnMissingField?: boolean; // Throw if class property has no matching field
31
+ }
32
+ ```
33
+
34
+ ### Strict Validation Mode
35
+
36
+ Catch mismatches between form fields and your data model:
37
+
38
+ ```typescript
39
+ const user = mapFormToClass(form, new UserDTO(), {
40
+ throwOnMissingProperty: true, // Catch typos in form field names
41
+ throwOnMissingField: true // Ensure all DTO fields are in form
42
+ });
43
+ ```
44
+
45
+ ### Type Conversion
46
+
47
+ Automatic conversion based on input types:
48
+
49
+ | Input Type | Converted To |
50
+ |------------|--------------|
51
+ | `checkbox` | `boolean` |
52
+ | `number` | `number` |
53
+ | `date` | `Date` |
54
+
55
+ ```typescript
56
+ class ProductDTO {
57
+ name: string = '';
58
+ price: number = 0;
59
+ inStock: boolean = false;
60
+ releaseDate: Date | null = null;
61
+ }
62
+
63
+ const product = mapFormToClass(form, new ProductDTO());
64
+ // product.price is number, product.inStock is boolean
65
+ ```
66
+
67
+ ## readData
68
+
69
+ Reads all form data into a plain object with automatic type conversion. Use when you don't need strict typing.
70
+
71
+ ```typescript
72
+ import { readData } from '@relax.js/core/forms';
73
+
74
+ const form = document.querySelector('form');
75
+ const data = readData(form);
76
+ ```
77
+
78
+ ### Type Conversion
79
+
80
+ Type conversion is determined by:
81
+ 1. `data-type` attribute if present (`number`, `boolean`, `string`, `Date`)
82
+ 2. Input type (`checkbox`, `number`, `date`, etc.)
83
+ 3. Falls back to string
84
+
85
+ ```html
86
+ <form>
87
+ <input name="username" value="john" />
88
+ <input name="age" type="number" value="25" />
89
+ <input name="active" type="checkbox" checked />
90
+ <input name="salary" data-type="number" value="50000" />
91
+ <select name="colors" multiple>
92
+ <option value="red" selected>Red</option>
93
+ <option value="blue" selected>Blue</option>
94
+ </select>
95
+ </form>
96
+ ```
97
+
98
+ ```typescript
99
+ const data = readData(form);
100
+ // Returns: {
101
+ // username: 'john',
102
+ // age: 25,
103
+ // active: true,
104
+ // salary: 50000,
105
+ // colors: ['red', 'blue']
106
+ // }
107
+ ```
108
+
109
+ ### Checkbox Handling
110
+
111
+ Unchecked checkboxes are included as `false` in the result. Checked checkboxes with no explicit `value` attribute (which submit as `'on'` in FormData) are correctly converted to `true`.
112
+
113
+ ```html
114
+ <input name="active" type="checkbox" checked />
115
+ <input name="terms" type="checkbox" />
116
+ ```
117
+
118
+ ```typescript
119
+ const data = readData(form);
120
+ // Returns: { active: true, terms: false }
121
+ ```
122
+
123
+ ### Custom Form Components
124
+
125
+ Works with form-associated custom elements:
126
+
127
+ ```html
128
+ <form>
129
+ <r-input name="email" value="test@example.com"></r-input>
130
+ <r-checkbox name="terms" checked></r-checkbox>
131
+ </form>
132
+ ```
133
+
134
+ ```typescript
135
+ const data = readData(form);
136
+ // Returns: { email: 'test@example.com', terms: true }
137
+ ```
138
+
139
+ ## setFormData
140
+
141
+ Populates form fields from a data object using the `name` attribute.
142
+
143
+ ```typescript
144
+ import { setFormData } from '@relax.js/core/forms';
145
+
146
+ const form = document.querySelector('form');
147
+ const data = { name: 'John', email: 'john@example.com' };
148
+ setFormData(form, data);
149
+ ```
150
+
151
+ ### Nested Objects (Dot Notation)
152
+
153
+ ```html
154
+ <form>
155
+ <input name="user.name" />
156
+ <input name="user.contact.email" />
157
+ </form>
158
+ ```
159
+
160
+ ```typescript
161
+ const data = {
162
+ user: {
163
+ name: 'John',
164
+ contact: {
165
+ email: 'john@example.com'
166
+ }
167
+ }
168
+ };
169
+ setFormData(form, data);
170
+ ```
171
+
172
+ ### Date and DateTime
173
+
174
+ `setFormData` formats `Date` objects to the correct string format for each input type:
175
+
176
+ ```html
177
+ <input type="date" name="birthday" />
178
+ <input type="datetime-local" name="meeting" />
179
+ ```
180
+
181
+ ```typescript
182
+ setFormData(form, {
183
+ birthday: new Date('2000-01-15'), // Sets "2000-01-15"
184
+ meeting: new Date('2024-06-15T14:30') // Sets "2024-06-15T14:30"
185
+ });
186
+ ```
187
+
188
+ ### Select (Single)
189
+
190
+ `setFormData` can both **populate options** and **select a value** on a `<select>`.
191
+
192
+ - The selected value comes from `data` (second argument).
193
+ - The options come from `context` (optional third argument). If `context` is not provided, options must already exist in the DOM.
194
+ - The data value is compared as a string against each option's `value` attribute. If no option matches, nothing is selected.
195
+
196
+ #### Existing options in the DOM
197
+
198
+ ```html
199
+ <select name="country">
200
+ <option value="se">Sweden</option>
201
+ <option value="us">United States</option>
202
+ </select>
203
+ ```
204
+
205
+ ```typescript
206
+ setFormData(form, { country: 'se' });
207
+ // Selects the "Sweden" option
208
+ ```
209
+
210
+ #### Populating options from `context`
211
+
212
+ Pass the collection on a third argument. By convention, the property name in `context` matches the select's `name` attribute. Items use a `{ value, text }` shape.
213
+
214
+ ```html
215
+ <select name="country"></select>
216
+ ```
217
+
218
+ ```typescript
219
+ setFormData(form, { country: 'se' }, {
220
+ country: [
221
+ { value: 'se', text: 'Sweden' },
222
+ { value: 'us', text: 'United States' }
223
+ ]
224
+ });
225
+ ```
226
+
227
+ A plain string array also works — each string becomes both the value and the text:
228
+
229
+ ```typescript
230
+ setFormData(form, { country: 'Sweden' }, {
231
+ country: ['Sweden', 'United States']
232
+ });
233
+ ```
234
+
235
+ #### Using `data-source` for custom property names
236
+
237
+ When your collection has different property names (like `id` and `name`), declare them on the select:
238
+
239
+ ```html
240
+ <select name="country" data-source="countries(id, name)"></select>
241
+ ```
242
+
243
+ ```typescript
244
+ setFormData(form, { country: 2 }, {
245
+ countries: [
246
+ { id: 1, name: 'Sweden' },
247
+ { id: 2, name: 'United States' }
248
+ ]
249
+ });
250
+ ```
251
+
252
+ The `data-source` attribute has two parts:
253
+
254
+ - **Source name** (required): the property on `context` to read from — `countries` above.
255
+ - **Value and text properties** (optional): inside parentheses, separated by a comma — `(id, name)` means `item.id` is the option value and `item.name` is the option text. Without parentheses, the defaults `value` and `text` are used.
256
+
257
+ #### `data-source` as a method
258
+
259
+ The property on `context` can be a method. It is called with no arguments and must return an array:
260
+
261
+ ```html
262
+ <select name="country" data-source="getCountries"></select>
263
+ ```
264
+
265
+ ```typescript
266
+ setFormData(form, { country: 'se' }, {
267
+ getCountries: () => [
268
+ { value: 'se', text: 'Sweden' },
269
+ { value: 'us', text: 'United States' }
270
+ ]
271
+ });
272
+ ```
273
+
274
+ #### Placeholder options are preserved
275
+
276
+ Options whose `value` is empty (for example a leading "Select one…") are kept when `setFormData` repopulates the list:
277
+
278
+ ```html
279
+ <select name="country">
280
+ <option value="">Select one…</option>
281
+ </select>
282
+ ```
283
+
284
+ After calling `setFormData` with a `context`, the placeholder stays at the top and the new options are appended after it.
285
+
286
+ ### Select Multiple
287
+
288
+ Works the same way as single selects. The `data` value must be an array. Options can be populated from `context` using either the name convention or `data-source`.
289
+
290
+ ```html
291
+ <select name="colors" multiple>
292
+ <option value="red">Red</option>
293
+ <option value="green">Green</option>
294
+ <option value="blue">Blue</option>
295
+ </select>
296
+ ```
297
+
298
+ ```typescript
299
+ setFormData(form, { colors: ['red', 'blue'] });
300
+ // Selects "red" and "blue", deselects "green"
301
+ ```
302
+
303
+ Each array item is compared as a string against each option's `value` attribute. Options that don't match any array item are deselected.
304
+
305
+ ### Simple Arrays ([] Notation)
306
+
307
+ For checkboxes, multi-select, and text inputs:
308
+
309
+ ```html
310
+ <form>
311
+ <input name="hobbies[]" type="checkbox" value="Reading" />
312
+ <input name="hobbies[]" type="checkbox" value="Cycling" />
313
+ <input name="hobbies[]" type="checkbox" value="Cooking" />
314
+ </form>
315
+ ```
316
+
317
+ ```typescript
318
+ const data = {
319
+ hobbies: ['Reading', 'Cooking']
320
+ };
321
+ setFormData(form, data);
322
+ // Checks "Reading" and "Cooking" checkboxes
323
+ ```
324
+
325
+ ### Array of Objects (Indexed Notation)
326
+
327
+ ```html
328
+ <form>
329
+ <input name="users[0].name" />
330
+ <input name="users[0].email" />
331
+ <input name="users[1].name" />
332
+ <input name="users[1].email" />
333
+ </form>
334
+ ```
335
+
336
+ ```typescript
337
+ const data = {
338
+ users: [
339
+ { name: 'John', email: 'john@example.com' },
340
+ { name: 'Jane', email: 'jane@example.com' }
341
+ ]
342
+ };
343
+ setFormData(form, data);
344
+ ```
345
+
346
+ ### Complex Data Structures
347
+
348
+ ```html
349
+ <form id="product-form">
350
+ <input name="product.name" placeholder="Product Name">
351
+ <input name="product.price" type="number" placeholder="Price">
352
+
353
+ <input name="categories[]" type="checkbox" value="electronics">
354
+ <input name="categories[]" type="checkbox" value="gadgets">
355
+
356
+ <input name="variants[0].size" placeholder="Size">
357
+ <input name="variants[0].color" placeholder="Color">
358
+ <input name="variants[1].size" placeholder="Size">
359
+ <input name="variants[1].color" placeholder="Color">
360
+
361
+ <input name="supplier.company" placeholder="Company">
362
+ <input name="supplier.contact.email" placeholder="Email">
363
+ </form>
364
+ ```
365
+
366
+ ```typescript
367
+ const productData = {
368
+ product: {
369
+ name: 'Wireless Headphones',
370
+ price: 99.99
371
+ },
372
+ categories: ['electronics', 'gadgets'],
373
+ variants: [
374
+ { size: 'M', color: 'black' },
375
+ { size: 'L', color: 'white' }
376
+ ],
377
+ supplier: {
378
+ company: 'TechCorp',
379
+ contact: {
380
+ email: 'orders@techcorp.com'
381
+ }
382
+ }
383
+ };
384
+
385
+ setFormData(form, productData);
386
+
387
+ // Later, extract the data
388
+ const extractedData = readData(form);
389
+ // Result matches the original structure
390
+ ```
391
+
392
+ ## Type Converters
393
+
394
+ ### Built-in Converters
395
+
396
+ | Converter | Input | Output |
397
+ |-----------|-------|--------|
398
+ | `BooleanConverter` | `'true'`, `'on'`, `'1'`, any positive number string -> `true`; `'false'`, `'off'`, `'0'` -> `false` | `boolean` |
399
+ | `NumberConverter` | Numeric string | `number` |
400
+ | `DateConverter` | ISO or locale-formatted date string | `Date` |
401
+
402
+ #### Locale-Aware Date Parsing
403
+
404
+ `DateConverter` detects the current locale and parses dates in the local format:
405
+
406
+ | Locale | Format | Example |
407
+ |--------|--------|---------|
408
+ | `en` (US) | `MM/DD/YYYY` | `01/15/2024` |
409
+ | `sv` (Swedish) | `YYYY-MM-DD` | `2024-01-15` |
410
+ | `de` (German) | `DD.MM.YYYY` | `15.01.2024` |
411
+
412
+ ISO format (`2024-01-15`) is always accepted regardless of locale.
413
+
414
+ ### Custom Type via `data-type` Attribute
415
+
416
+ Use the `data-type` attribute to override conversion for any field:
417
+
418
+ ```html
419
+ <input name="salary" data-type="number" value="50000" />
420
+ <input name="active" data-type="boolean" value="true" />
421
+ <input name="birthday" data-type="Date" value="2000-01-15" />
422
+ ```
423
+
424
+ ```typescript
425
+ const data = readData(form);
426
+ console.log(data.salary); // number: 50000
427
+ console.log(data.active); // boolean: true
428
+ console.log(data.birthday); // Date object
429
+ ```
430
+
431
+ Supported `data-type` values: `number`, `boolean`, `string`, `Date`.
432
+
433
+ ### Date and Time Handling
434
+
435
+ ```html
436
+ <input name="startDate" type="date">
437
+ <input name="startTime" type="time">
438
+ <input name="duration" type="time" step="900"> <!-- 15 min steps -->
439
+ ```
440
+
441
+ ```typescript
442
+ // Custom handling for combining date and time
443
+ document.querySelector('[name="startTime"]').getData = function() {
444
+ const dateInput = form.querySelector('[name="startDate"]') as HTMLInputElement;
445
+ const timeInput = this as HTMLInputElement;
446
+
447
+ if (dateInput.value && timeInput.value) {
448
+ return new Date(`${dateInput.value}T${timeInput.value}`);
449
+ }
450
+ return null;
451
+ };
452
+ ```
453
+
454
+ ### Input Type Conversion Table
455
+
456
+ | Input Type | Converted To |
457
+ |------------|--------------|
458
+ | `checkbox` | `boolean` |
459
+ | `number` | `number` |
460
+ | `date` | `Date` |
461
+ | `datetime-local` | `Date` |
462
+ | `month` | `Date` (first of month) |
463
+ | `week` | `{ year, week }` |
464
+ | `time` | `{ hours, minutes, seconds }` |
465
+ | Default | `string` |