neo.mjs 10.0.0-beta.1 → 10.0.0-beta.3

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 (149) hide show
  1. package/ServiceWorker.mjs +2 -2
  2. package/apps/colors/view/GridContainer.mjs +1 -1
  3. package/apps/covid/view/AttributionComponent.mjs +1 -1
  4. package/apps/covid/view/HeaderContainer.mjs +6 -6
  5. package/apps/covid/view/MainContainerController.mjs +5 -5
  6. package/apps/covid/view/TableContainerController.mjs +1 -1
  7. package/apps/covid/view/country/Gallery.mjs +13 -13
  8. package/apps/covid/view/country/Helix.mjs +13 -13
  9. package/apps/covid/view/country/HistoricalDataTable.mjs +1 -1
  10. package/apps/email/view/Viewport.mjs +2 -2
  11. package/apps/form/view/FormPageContainer.mjs +2 -3
  12. package/apps/form/view/SideNavList.mjs +1 -1
  13. package/apps/portal/index.html +1 -1
  14. package/apps/portal/resources/data/examples_dist_esm.json +1 -1
  15. package/apps/portal/resources/data/examples_dist_prod.json +2 -2
  16. package/apps/portal/view/HeaderToolbar.mjs +3 -3
  17. package/apps/portal/view/about/Container.mjs +2 -2
  18. package/apps/portal/view/about/MemberContainer.mjs +3 -3
  19. package/apps/portal/view/blog/List.mjs +7 -7
  20. package/apps/portal/view/examples/List.mjs +4 -4
  21. package/apps/portal/view/home/ContentBox.mjs +2 -2
  22. package/apps/portal/view/home/FeatureSection.mjs +3 -3
  23. package/apps/portal/view/home/FooterContainer.mjs +7 -7
  24. package/apps/portal/view/home/parts/AfterMath.mjs +3 -3
  25. package/apps/portal/view/home/parts/MainNeo.mjs +3 -3
  26. package/apps/portal/view/home/parts/References.mjs +6 -6
  27. package/apps/portal/view/learn/ContentComponent.mjs +18 -11
  28. package/apps/portal/view/learn/PageSectionsContainer.mjs +1 -1
  29. package/apps/portal/view/learn/PageSectionsList.mjs +2 -2
  30. package/apps/portal/view/services/Component.mjs +16 -16
  31. package/apps/realworld/view/FooterComponent.mjs +1 -1
  32. package/apps/realworld/view/HeaderComponent.mjs +8 -8
  33. package/apps/realworld/view/HomeComponent.mjs +6 -6
  34. package/apps/realworld/view/article/CommentComponent.mjs +4 -4
  35. package/apps/realworld/view/article/Component.mjs +14 -14
  36. package/apps/realworld/view/article/CreateCommentComponent.mjs +3 -3
  37. package/apps/realworld/view/article/CreateComponent.mjs +3 -3
  38. package/apps/realworld/view/article/PreviewComponent.mjs +1 -1
  39. package/apps/realworld/view/article/TagListComponent.mjs +2 -2
  40. package/apps/realworld/view/user/ProfileComponent.mjs +8 -8
  41. package/apps/realworld/view/user/SettingsComponent.mjs +4 -4
  42. package/apps/realworld/view/user/SignUpComponent.mjs +4 -4
  43. package/apps/realworld2/view/FooterComponent.mjs +1 -1
  44. package/apps/realworld2/view/HomeContainer.mjs +3 -3
  45. package/apps/realworld2/view/article/DetailsContainer.mjs +1 -1
  46. package/apps/realworld2/view/article/PreviewComponent.mjs +7 -7
  47. package/apps/realworld2/view/article/TagListComponent.mjs +2 -2
  48. package/apps/realworld2/view/user/ProfileContainer.mjs +1 -1
  49. package/apps/route/view/center/CardAdministration.mjs +2 -2
  50. package/apps/route/view/center/CardAdministrationDenied.mjs +1 -1
  51. package/apps/route/view/center/CardContact.mjs +2 -2
  52. package/apps/route/view/center/CardHome.mjs +1 -1
  53. package/apps/route/view/center/CardSection1.mjs +1 -1
  54. package/apps/route/view/center/CardSection2.mjs +1 -1
  55. package/apps/sharedcovid/view/AttributionComponent.mjs +1 -1
  56. package/apps/sharedcovid/view/HeaderContainer.mjs +6 -6
  57. package/apps/sharedcovid/view/MainContainerController.mjs +5 -5
  58. package/apps/sharedcovid/view/TableContainerController.mjs +1 -1
  59. package/apps/sharedcovid/view/country/Gallery.mjs +13 -13
  60. package/apps/sharedcovid/view/country/Helix.mjs +13 -13
  61. package/apps/sharedcovid/view/country/HistoricalDataTable.mjs +1 -1
  62. package/apps/shareddialog/childapps/shareddialog2/view/MainContainer.mjs +1 -1
  63. package/apps/shareddialog/view/MainContainer.mjs +1 -1
  64. package/buildScripts/createApp.mjs +2 -2
  65. package/learn/Glossary.md +261 -0
  66. package/learn/README.md +9 -14
  67. package/learn/benefits/ConfigSystem.md +536 -26
  68. package/learn/benefits/Effort.md +47 -2
  69. package/learn/benefits/Features.md +50 -32
  70. package/learn/benefits/FormsEngine.md +54 -24
  71. package/learn/benefits/MultiWindow.md +31 -5
  72. package/learn/benefits/Quick.md +45 -12
  73. package/learn/benefits/RPCLayer.md +75 -0
  74. package/learn/benefits/Speed.md +17 -12
  75. package/learn/guides/Collections.md +436 -0
  76. package/learn/guides/ConfigSystemDeepDive.md +280 -0
  77. package/learn/guides/CustomComponents.md +256 -14
  78. package/learn/guides/DeclarativeComponentTreesVsImperativeVdom.md +17 -17
  79. package/learn/guides/ExtendingNeoClasses.md +331 -0
  80. package/learn/guides/Forms.md +449 -1
  81. package/learn/guides/InstanceLifecycle.md +295 -1
  82. package/learn/guides/Layouts.md +246 -1
  83. package/learn/guides/MainThreadAddons.md +475 -0
  84. package/learn/guides/Records.md +286 -0
  85. package/learn/guides/WorkingWithVDom.md +14 -14
  86. package/learn/guides/form_fields/ComboBox.md +241 -0
  87. package/learn/tree.json +57 -51
  88. package/package.json +2 -2
  89. package/resources/scss/src/apps/portal/learn/ContentComponent.scss +9 -0
  90. package/src/DefaultConfig.mjs +2 -2
  91. package/src/Main.mjs +8 -7
  92. package/src/Neo.mjs +16 -2
  93. package/src/button/Base.mjs +2 -2
  94. package/src/calendar/view/SettingsContainer.mjs +2 -2
  95. package/src/calendar/view/YearComponent.mjs +9 -9
  96. package/src/calendar/view/calendars/ColorsList.mjs +1 -1
  97. package/src/calendar/view/calendars/List.mjs +1 -1
  98. package/src/calendar/view/month/Component.mjs +15 -15
  99. package/src/calendar/view/week/Component.mjs +12 -12
  100. package/src/calendar/view/week/EventDragZone.mjs +4 -4
  101. package/src/calendar/view/week/TimeAxisComponent.mjs +3 -3
  102. package/src/component/Base.mjs +17 -2
  103. package/src/component/Carousel.mjs +2 -2
  104. package/src/component/Chip.mjs +3 -3
  105. package/src/component/Circle.mjs +2 -2
  106. package/src/component/DateSelector.mjs +8 -8
  107. package/src/component/Helix.mjs +1 -1
  108. package/src/component/Label.mjs +3 -18
  109. package/src/component/Legend.mjs +3 -3
  110. package/src/component/MagicMoveText.mjs +6 -14
  111. package/src/component/Process.mjs +3 -3
  112. package/src/component/Progress.mjs +1 -1
  113. package/src/component/StatusBadge.mjs +2 -2
  114. package/src/component/Timer.mjs +2 -2
  115. package/src/component/Toast.mjs +5 -3
  116. package/src/container/AccordionItem.mjs +2 -2
  117. package/src/container/Base.mjs +1 -1
  118. package/src/core/Base.mjs +18 -2
  119. package/src/date/DayViewComponent.mjs +2 -2
  120. package/src/date/SelectorContainer.mjs +1 -1
  121. package/src/form/field/CheckBox.mjs +4 -4
  122. package/src/form/field/ComboBox.mjs +6 -1
  123. package/src/form/field/FileUpload.mjs +25 -39
  124. package/src/form/field/Range.mjs +1 -1
  125. package/src/form/field/Text.mjs +3 -3
  126. package/src/form/field/TextArea.mjs +2 -3
  127. package/src/grid/Body.mjs +6 -2
  128. package/src/list/Color.mjs +2 -2
  129. package/src/main/DeltaUpdates.mjs +157 -98
  130. package/src/main/addon/AmCharts.mjs +53 -73
  131. package/src/main/addon/Base.mjs +11 -0
  132. package/src/main/addon/MonacoEditor.mjs +31 -58
  133. package/src/manager/ClassHierarchy.mjs +114 -0
  134. package/src/menu/List.mjs +1 -1
  135. package/src/plugin/Popover.mjs +2 -2
  136. package/src/sitemap/Component.mjs +1 -1
  137. package/src/table/Body.mjs +6 -2
  138. package/src/tooltip/Base.mjs +1 -6
  139. package/src/tree/Accordion.mjs +3 -3
  140. package/src/vdom/Helper.mjs +21 -19
  141. package/src/worker/App.mjs +1 -2
  142. package/src/worker/Base.mjs +6 -4
  143. package/src/worker/Canvas.mjs +2 -3
  144. package/src/worker/Data.mjs +5 -7
  145. package/src/worker/Task.mjs +2 -3
  146. package/src/worker/VDom.mjs +3 -4
  147. package/src/worker/mixin/RemoteMethodAccess.mjs +4 -1
  148. package/learn/guides/MainThreadAddonExample.md +0 -15
  149. package/learn/guides/MainThreadAddonIntro.md +0 -44
@@ -1 +1,449 @@
1
- ## todo
1
+ The Neo.mjs Forms Engine provides a powerful and flexible way to build
2
+ user interfaces for data input and validation. This guide will walk you
3
+ through the core concepts and practical usage of forms in Neo.mjs,
4
+ from basic field creation to advanced validation and nested structures.
5
+
6
+ ## 1. Basic Form Creation
7
+
8
+ At its core, a form in Neo.mjs is a `Neo.form.Container`. This container
9
+ manages a collection of form fields and provides methods for data
10
+ retrieval, validation, and submission.
11
+
12
+ To create a simple form, you define a `Neo.form.Container` and add
13
+ `Neo.form.field.Text` or other field modules to its `items` config.
14
+
15
+ ```javascript readonly
16
+ import FormContainer from '../../src/form/Container.mjs';
17
+ import TextField from '../../src/form/field/Text.mjs';
18
+
19
+ class MySimpleForm extends FormContainer {
20
+ static config = {
21
+ className: 'MySimpleForm',
22
+ layout : {ntype: 'vbox', align: 'start'},
23
+ items : [{
24
+ module : TextField,
25
+ labelText: 'First Name',
26
+ name : 'firstName',
27
+ required : true
28
+ }, {
29
+ module : TextField,
30
+ labelText: 'Last Name',
31
+ name : 'lastName'
32
+ }]
33
+ }
34
+ }
35
+ ```
36
+
37
+ In this example:
38
+ * `module: TextField` specifies the type of form field.
39
+ * `labelText` defines the visible label for the field.
40
+ * `name` is crucial for data management; it defines the key under
41
+ which the field's value will be stored when retrieving form data.
42
+ * `required: true` enables basic validation, ensuring the field is
43
+ not left empty.
44
+
45
+ ## 2. Field Types
46
+
47
+ Neo.mjs offers a rich set of pre-built form field types, all extending
48
+ `Neo.form.field.Base`. These fields cover a wide range of input needs:
49
+
50
+ * **Text-based Inputs**: `TextField`, `TextArea`, `EmailField`,
51
+ `PasswordField`, `PhoneField`, `UrlField`, `SearchField`,
52
+ `DisplayField` (read-only), `HiddenField`.
53
+ * **Numeric Inputs**: `NumberField`, `CurrencyField`, `RangeField`.
54
+ * **Selection Inputs**: `ComboBox`, `Chip`, `ColorField`, `DateField`,
55
+ `TimeField`, `CountryField`, `ZipCodeField`.
56
+ * **Choice Inputs**: `CheckBox`, `Radio`, `Switch`.
57
+ * **File Upload**: `FileUpload`.
58
+
59
+ You can find the full list of available fields and their configurations
60
+ in the `src/form/field/` directory.
61
+
62
+ ## 3. Data Management
63
+
64
+ A key strength of Neo.mjs forms is their integrated state management. The `Neo.form.Container` automatically manages form data based on field names, eliminating the need for external state management libraries or manual state tree definitions. This significantly simplifies data handling and reduces boilerplate.
65
+
66
+ The `Neo.form.Container` provides powerful methods for managing form data.
67
+
68
+ ### Getting Form Values
69
+
70
+ To retrieve all values from a form, use the asynchronous `getSubmitValues()`
71
+ method. This method returns a plain JavaScript object where keys correspond
72
+ to the `name` attributes of your fields.
73
+
74
+ ```javascript readonly
75
+ // Assuming 'myForm' is an instance of your form container
76
+ const formValues = await myForm.getSubmitValues();
77
+ console.log(formValues);
78
+ // Example output: { firstName: 'John', lastName: 'Doe' }
79
+ ```
80
+
81
+ ### Nested Data Structures
82
+
83
+ The `name` attribute supports dot notation to create nested data structures.
84
+ This is particularly useful for organizing complex form data.
85
+
86
+ ```javascript readonly
87
+ import FormContainer from '../../src/form/Container.mjs';
88
+ import TextField from '../../src/form/field/Text.mjs';
89
+
90
+ class UserForm extends FormContainer {
91
+ static config = {
92
+ className: 'UserForm',
93
+ layout : {ntype: 'vbox', align: 'start'},
94
+ items : [{
95
+ module : TextField,
96
+ labelText: 'User First Name',
97
+ name : 'user.profile.firstName'
98
+ }, {
99
+ module : TextField,
100
+ labelText: 'User Last Name',
101
+ name : 'user.profile.lastName'
102
+ }, {
103
+ module : TextField,
104
+ labelText: 'Address Street',
105
+ name : 'user.address.street'
106
+ }]
107
+ }
108
+ }
109
+
110
+ // ... later, after the form is rendered and values are entered
111
+ const userFormValues = await myUserForm.getSubmitValues();
112
+ console.log(userFormValues);
113
+ /*
114
+ Output:
115
+ {
116
+ user: {
117
+ profile: {
118
+ firstName: 'Jane',
119
+ lastName : 'Doe'
120
+ },
121
+ address: {
122
+ street: '123 Main St'
123
+ }
124
+ }
125
+ }
126
+ */
127
+ ```
128
+
129
+ ### Setting Form Values
130
+
131
+ You can pre-populate a form or update its values programmatically using
132
+ the `setValues(values, suspendEvents)` method. The `values` object should
133
+ mirror the structure of the data returned by `getSubmitValues()`.
134
+
135
+ ```javascript readonly
136
+ // To set values for the UserForm example above:
137
+ await myUserForm.setValues({
138
+ user: {
139
+ profile: {
140
+ firstName: 'Alice',
141
+ lastName : 'Smith'
142
+ },
143
+ address: {
144
+ street: '456 Oak Ave'
145
+ }
146
+ }
147
+ });
148
+ ```
149
+
150
+ The optional `suspendEvents` parameter (default `false`) can be set to
151
+ `true` to prevent `change` events from firing for each field during the
152
+ update, which can be useful for large data sets.
153
+
154
+ ### Resetting Forms
155
+
156
+ The `reset(values)` method allows you to clear or reset form fields.
157
+ If no `values` object is provided, fields will be reset to `null` or their
158
+ `emptyValue` (if configured). If `values` are provided, fields will be
159
+ reset to those specific values.
160
+
161
+ ```javascript readonly
162
+ // Reset all fields to their default empty state
163
+ await myForm.reset();
164
+
165
+ // Reset specific fields to new values
166
+ await myForm.reset({
167
+ firstName: 'Default Name'
168
+ });
169
+ ```
170
+
171
+ ## 4. Validation
172
+
173
+ Neo.mjs forms provide robust validation capabilities, both built-in and
174
+ customizable.
175
+
176
+ ### Built-in Validation
177
+
178
+ Many field types come with built-in validation rules:
179
+
180
+ * **`required`**: Ensures a field is not empty.
181
+ * **`minLength` / `maxLength`**: For text-based fields, validates the
182
+ length of the input.
183
+ * **`minValue` / `maxValue`**: For numeric fields, validates the range
184
+ of the input.
185
+ * **`inputPattern`**: A regular expression to validate the input format.
186
+ (e.g., `EmailField`, `UrlField`, `ZipCodeField` use this internally).
187
+
188
+ You can configure these directly on the field:
189
+
190
+ ```javascript readonly
191
+ import TextField from '../../src/form/field/Text.mjs';
192
+
193
+ // ...
194
+ items: [{
195
+ module : TextField,
196
+ inputPattern: /^[a-zA-Z0-9_]+$/, // Alphanumeric and underscore only
197
+ labelText : 'Username',
198
+ name : 'username',
199
+ required : true,
200
+ minLength : 5,
201
+ maxLength : 20
202
+ }]
203
+ ```
204
+
205
+ Error messages for built-in validations can be customized using `errorText*`
206
+ configs (e.g., `errorTextRequired`, `errorTextMaxLength`).
207
+
208
+ ### Custom Validation (`validator`)
209
+
210
+ For more complex validation logic, you can use the `validator` config on
211
+ any field. This should be a function that receives the field instance as
212
+ its argument and returns `true` if the value is valid, or a string
213
+ (the error message) if it's invalid.
214
+
215
+ ```javascript readonly
216
+ import TextField from '../../src/form/field/Text.mjs';
217
+
218
+ // ...
219
+ items: [{
220
+ module : TextField,
221
+ labelText: 'Password',
222
+ name : 'password',
223
+ reference: 'passwordField' // Add a reference to the password field
224
+ }, {
225
+ module : TextField,
226
+ labelText: 'Confirm Password',
227
+ name : 'confirmPassword',
228
+ validator: function(field) {
229
+ // Access the password field using getClosestForm().getReference()
230
+ const passwordField = field.getClosestForm().getReference('passwordField');
231
+
232
+ if (field.value !== passwordField.value) {
233
+ return 'Passwords do not match'
234
+ }
235
+ return true
236
+ }
237
+ }]
238
+ ```
239
+
240
+ ### Displaying Errors
241
+
242
+ Errors are automatically displayed below the field when validation fails.
243
+ The `useAlertState` config (globally set in `apps/form/Overwrites.mjs`)
244
+ can change the visual styling of required but empty fields from red to orange.
245
+
246
+ The `clean` property on a field determines if errors are shown immediately.
247
+ By default, `clean` is `true` until the user interacts with the field or
248
+ `validate(false)` is called.
249
+
250
+ ### Form Validation State
251
+
252
+ You can check the overall validity of a form using:
253
+
254
+ * **`isValid()`**: An asynchronous method that returns `true` if all
255
+ fields in the form (and its nested forms) are valid, `false` otherwise.
256
+ It also triggers validation for all fields.
257
+
258
+ ```javascript readonly
259
+ const formIsValid = await myForm.isValid();
260
+ if (formIsValid) {
261
+ console.log('Form is valid!');
262
+ } else {
263
+ console.log('Form has errors.');
264
+ }
265
+ ```
266
+
267
+ * **`getFormState()`**: An asynchronous method that returns a string
268
+ indicating the overall state of the form:
269
+ * `'clean'`: All fields are untouched and valid.
270
+ * `'valid'`: All fields are valid.
271
+ * `'invalid'`: At least one field is invalid.
272
+ * `'inProgress'`: Some fields are valid, some are clean.
273
+
274
+ ```javascript readonly
275
+ const state = await myForm.getFormState();
276
+ console.log('Form state:', state);
277
+ ```
278
+
279
+ ## 5. Nested Forms
280
+
281
+ Neo.mjs allows for true nested forms, providing unparalleled structural
282
+ flexibility. This is achieved by nesting `Neo.form.Container` instances
283
+ or using `Neo.form.Fieldset`.
284
+
285
+ ### Using `Neo.form.Fieldset`
286
+
287
+ `Neo.form.Fieldset` extends `Neo.form.Container` and is ideal for
288
+ grouping related fields visually. It can also be collapsed.
289
+
290
+ ```javascript readonly
291
+ import Fieldset from '../../src/form/Fieldset.mjs';
292
+ import FormContainer from '../../src/form/Container.mjs';
293
+ import TextField from '../../src/form/field/Text.mjs';
294
+
295
+ class NestedFieldsetForm extends FormContainer {
296
+ static config = {
297
+ className: 'NestedFieldsetForm',
298
+ layout : {ntype: 'vbox', align: 'start'},
299
+ items : [{
300
+ module : Fieldset,
301
+ title : 'Personal Information',
302
+ formGroup: 'person', // Data will be nested under 'person'
303
+ items : [{
304
+ module : TextField,
305
+ labelText: 'First Name',
306
+ name : 'firstName',
307
+ required : true
308
+ }, {
309
+ module : TextField,
310
+ labelText: 'Last Name',
311
+ name : 'lastName'
312
+ }]
313
+ }, {
314
+ module : Fieldset,
315
+ title : 'Contact Information',
316
+ formGroup: 'contact', // Data will be nested under 'contact'
317
+ items : [{
318
+ module : TextField,
319
+ labelText: 'Email',
320
+ name : 'email',
321
+ required : true
322
+ }, {
323
+ module : TextField,
324
+ labelText: 'Phone',
325
+ name : 'phone'
326
+ }]
327
+ }]
328
+ }
329
+ }
330
+
331
+ // Example getSubmitValues() output:
332
+ /*
333
+ {
334
+ person: {
335
+ firstName: 'John',
336
+ lastName : 'Doe'
337
+ },
338
+ contact: {
339
+ email: 'john.doe@example.com',
340
+ phone: '123-456-7890'
341
+ }
342
+ }
343
+ */
344
+ ```
345
+
346
+ The `formGroup` config on `Fieldset` (or any `Form.Container`) automatically
347
+ nests the data of its child fields under the specified key.
348
+
349
+ ### Nesting `Form.Container` Instances
350
+
351
+ You can directly nest `Form.Container` instances to create more complex
352
+ hierarchies. This is demonstrated in `apps/form/view/FormPageContainer.mjs`,
353
+ which extends `Neo.form.Container` but uses a `div` for its `vdom` tag
354
+ to avoid invalid HTML (`<form>` inside `<form>`).
355
+
356
+ ```javascript readonly
357
+ // Example from apps/form/view/FormPageContainer.mjs
358
+ import BaseFormContainer from '../../../src/form/Container.mjs';
359
+
360
+ class FormPageContainer extends BaseFormContainer {
361
+ static config = {
362
+ className: 'Form.view.FormPageContainer',
363
+ // ... other configs
364
+ tag: 'div' // Using a div instead of a form tag
365
+ }
366
+ }
367
+ ```
368
+
369
+ This allows you to treat each nested container as a sub-form, which can
370
+ be validated or have its values retrieved independently, or as part of
371
+ the top-level form.
372
+
373
+ ## 6. Field Triggers
374
+
375
+ Field triggers are small, interactive icons or buttons that appear within
376
+ or alongside a form field, providing additional functionality. Examples
377
+ include clear buttons, date pickers, or spin buttons for number fields.
378
+
379
+ Triggers are configured via the `triggers` array on a field. You can
380
+ configure multiple triggers for a single field, and control their placement
381
+ (left or right of the input) using the `align` config on the trigger.
382
+
383
+ ```javascript readonly
384
+ import DateField from '../../src/form/field/Date.mjs';
385
+ import ClearTrigger from '../../src/form/field/trigger/Clear.mjs';
386
+
387
+ // ...
388
+ items: [{
389
+ module : DateField,
390
+ labelText: 'Event Date',
391
+ name : 'eventDate',
392
+ triggers : [{
393
+ module: ClearTrigger // Adds a clear button to the date field
394
+ }]
395
+ }]
396
+ ```
397
+
398
+ Many fields automatically include default triggers (e.g., `DateField`
399
+ includes a `DateTrigger`). You can override or add to these defaults.
400
+
401
+ ## 7. Form and Field Events
402
+
403
+ Neo.mjs forms and fields emit various events that you can listen to for
404
+ custom logic:
405
+
406
+ * **`change`**: Fired when a field's `value` config changes.
407
+ * **`userChange`**: Fired when a field's value changes due to direct
408
+ user interaction (e.g., typing in a text field).
409
+ * **`fieldChange`**: Fired on the `Form.Container` when any of its
410
+ child fields' `value` changes.
411
+ * **`fieldUserChange`**: Fired on the `Form.Container` when any of its
412
+ child fields' value changes due to user interaction.
413
+ * **`focusEnter` / `focusLeave`**: Fired when a field gains or loses focus.
414
+
415
+ You can listen to these events using the `on` method:
416
+
417
+ ```javascript readonly
418
+ manyFormField.on('change', (data) => {
419
+ console.log('Field value changed:', data.value);
420
+ });
421
+
422
+ myFormContainer.on('fieldUserChange', (data) => {
423
+ console.log('User changed field:', data.component.name, data.value);
424
+ });
425
+ ```
426
+
427
+ ## 8. Best Practices and Tips
428
+
429
+ * **`itemDefaults`**: Use `itemDefaults` on containers to apply common
430
+ configurations to all child items, reducing boilerplate.
431
+ * **`formGroup`**: Leverage `formGroup` for logical grouping of data,
432
+ especially in complex forms, to create clean nested data structures.
433
+ * **`readOnly` vs. `editable`**:
434
+ * `readOnly: true`: Prevents user interaction from changing the value.
435
+ The field is still part of the form data.
436
+ * `editable: false`: Similar to `readOnly`, but specifically for
437
+ input elements, preventing direct typing. Other interactions (like
438
+ picker selection) might still be possible unless also `readOnly`.
439
+ * **Lazy Loading**: For forms with many pages or complex sections, consider
440
+ lazy loading modules for individual pages or fieldsets to improve initial
441
+ application load times. This is demonstrated in `apps/form/view/FormContainer.mjs`
442
+ where pages are imported dynamically.
443
+ * **Explicit Module Imports**: While the core `Neo` global namespace is always available, it's a best practice to
444
+ explicitly import all Neo.mjs modules you use (e.g., `import FormContainer from '../../src/form/Container.mjs';`).
445
+ Relying on implicit availability of classes within `Neo`'s sub-namespaces can lead to less readable and maintainable code.
446
+ Explicit imports improve code readability, maintainability, and ensure consistent behavior.
447
+ * **`Neo.overwrites`**: Use global overwrites (as seen in `apps/form/Overwrites.mjs`)
448
+ to enforce consistent styling or behavior across all instances of a
449
+ component type.