native-document 1.0.166 → 1.0.168

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 (108) hide show
  1. package/.vitepress/config.js +166 -0
  2. package/CHANGELOG.md +153 -0
  3. package/components.js +2 -1
  4. package/dist/native-document.components.min.js +495 -228
  5. package/dist/native-document.dev.js +7 -0
  6. package/dist/native-document.dev.js.map +1 -1
  7. package/dist/native-document.min.js +1 -1
  8. package/docs/advanced-components.md +213 -608
  9. package/docs/anchor.md +173 -312
  10. package/docs/cache.md +95 -803
  11. package/docs/cli.md +179 -0
  12. package/docs/components/accordion.md +172 -0
  13. package/docs/components/alert.md +99 -0
  14. package/docs/components/avatar.md +160 -0
  15. package/docs/components/badge.md +102 -0
  16. package/docs/components/breadcrumb.md +89 -0
  17. package/docs/components/button.md +183 -0
  18. package/docs/components/card.md +69 -0
  19. package/docs/components/context-menu.md +118 -0
  20. package/docs/components/data-table.md +345 -0
  21. package/docs/components/dropdown.md +214 -0
  22. package/docs/components/form/autocomplete-field.md +81 -0
  23. package/docs/components/form/checkbox-field.md +41 -0
  24. package/docs/components/form/checkbox-group-field.md +54 -0
  25. package/docs/components/form/color-field.md +64 -0
  26. package/docs/components/form/date-field.md +92 -0
  27. package/docs/components/form/field-collection.md +63 -0
  28. package/docs/components/form/file-field.md +203 -0
  29. package/docs/components/form/form-control.md +87 -0
  30. package/docs/components/form/image-field.md +90 -0
  31. package/docs/components/form/index.md +115 -0
  32. package/docs/components/form/number-field.md +65 -0
  33. package/docs/components/form/radio-field.md +51 -0
  34. package/docs/components/form/select-field.md +123 -0
  35. package/docs/components/form/slider.md +136 -0
  36. package/docs/components/form/string-field.md +134 -0
  37. package/docs/components/form/textarea-field.md +65 -0
  38. package/docs/components/form-fields.md +372 -0
  39. package/docs/components/getting-started.md +264 -0
  40. package/docs/components/index.md +337 -0
  41. package/docs/components/layout.md +279 -0
  42. package/docs/components/list.md +73 -0
  43. package/docs/components/menu.md +215 -0
  44. package/docs/components/modal.md +156 -0
  45. package/docs/components/pagination.md +95 -0
  46. package/docs/components/popover.md +131 -0
  47. package/docs/components/progress.md +111 -0
  48. package/docs/components/shortcut-manager.md +221 -0
  49. package/docs/components/simple-table.md +107 -0
  50. package/docs/components/skeleton.md +155 -0
  51. package/docs/components/spinner.md +100 -0
  52. package/docs/components/splitter.md +133 -0
  53. package/docs/components/stepper.md +163 -0
  54. package/docs/components/switch.md +113 -0
  55. package/docs/components/tabs.md +153 -0
  56. package/docs/components/toast.md +119 -0
  57. package/docs/components/tooltip.md +151 -0
  58. package/docs/components/traits.md +261 -0
  59. package/docs/conditional-rendering.md +170 -588
  60. package/docs/contributing.md +300 -25
  61. package/docs/core-concepts.md +205 -374
  62. package/docs/elements.md +251 -367
  63. package/docs/extending-native-document-element.md +192 -207
  64. package/docs/filters.md +153 -1122
  65. package/docs/getting-started.md +193 -267
  66. package/docs/i18n.md +241 -0
  67. package/docs/index.md +76 -0
  68. package/docs/lifecycle-events.md +143 -75
  69. package/docs/list-rendering.md +227 -852
  70. package/docs/memory-management.md +134 -47
  71. package/docs/native-document-element.md +337 -186
  72. package/docs/native-fetch.md +99 -630
  73. package/docs/observable-resource.md +364 -0
  74. package/docs/observables.md +592 -526
  75. package/docs/routing.md +244 -653
  76. package/docs/state-management.md +134 -241
  77. package/docs/svg-elements.md +231 -0
  78. package/docs/theming.md +409 -0
  79. package/docs/tutorials/.gitkeep +0 -0
  80. package/docs/validation.md +95 -97
  81. package/docs/vitepress-conventions.md +219 -0
  82. package/package.json +34 -13
  83. package/readme.md +269 -89
  84. package/src/components/card/Card.js +93 -39
  85. package/src/components/card/index.js +1 -1
  86. package/src/components/list/HasListItem.js +171 -0
  87. package/src/components/list/List.js +41 -107
  88. package/src/components/list/ListDivider.js +39 -0
  89. package/src/components/list/ListGroup.js +76 -59
  90. package/src/components/list/ListItem.js +117 -69
  91. package/src/components/list/index.js +3 -1
  92. package/src/components/list/types/ListItem.d.ts +45 -34
  93. package/src/components/spacer/Spacer.js +1 -1
  94. package/src/core/data/ObservableResource.js +5 -0
  95. package/src/core/data/observable-helpers/observable.prototypes.js +2 -0
  96. package/src/ui/components/card/CardRender.js +133 -0
  97. package/src/ui/components/card/card.css +169 -0
  98. package/src/ui/components/contextmenu/ContextmenuRender.js +1 -1
  99. package/src/ui/components/list/ListRender.js +18 -0
  100. package/src/ui/components/list/divider/ListDividerRender.js +10 -0
  101. package/src/ui/components/list/divider/list-divider.css +12 -0
  102. package/src/ui/components/list/group/ListGroupRender.js +61 -0
  103. package/src/ui/components/list/group/list-group.css +62 -0
  104. package/src/ui/components/list/item/ListItemRender.js +238 -0
  105. package/src/ui/components/list/item/list-item.css +191 -0
  106. package/src/ui/components/list/list.css +24 -0
  107. package/src/ui/components/spacer/SpacerRender.js +10 -0
  108. package/src/ui/index.js +8 -0
@@ -0,0 +1,54 @@
1
+ ---
2
+ title: CheckboxGroupField
3
+ description: Multiple checkboxes where the value is an array of selected values
4
+ ---
5
+
6
+ # CheckboxGroupField
7
+
8
+ ```javascript
9
+ import { CheckboxGroupField } from 'native-document/components';
10
+
11
+ CheckboxGroupField(name, props?)
12
+ ```
13
+
14
+ ## Default Renderer
15
+
16
+ ```javascript
17
+ import { CheckboxGroupFieldRender } from 'native-document/ui';
18
+
19
+ CheckboxGroupField.use(CheckboxGroupFieldRender);
20
+ ```
21
+
22
+ ## Methods
23
+
24
+ | Method | Parameters | Description |
25
+ |---|---|---|
26
+ | `.model(observable)` | `observable: Observable<*[]>` | Two-way binding - array of selected values |
27
+ | `.label(text)` | `text: NdChild` | Label for the field group |
28
+ | `.disabled(val)` | `val: boolean \| Observable<boolean>` | Disable the field |
29
+ | `.option(value, label, props?)` | `value: *`, `label: NdChild`, `props?: object` | Add a checkbox option |
30
+ | `.options(items)` | `items: { value, label }[]` | Add multiple options at once |
31
+ | `.layout(type)` | `'vertical' \| 'horizontal' \| 'grid'` | Layout of the checkboxes. Default `'vertical'` |
32
+ | `.vertical()` | - | Shorthand for `.layout('vertical')` |
33
+ | `.horizontal()` | - | Shorthand for `.layout('horizontal')` |
34
+ | `.grid()` | - | Shorthand for `.layout('grid')` |
35
+ | `.required(message?)` | `message?: string` | Validation - at least one must be selected |
36
+ | `.minChecked(n, message?)` | `n: number`, `message?: string` | Minimum number of selections |
37
+ | `.maxChecked(n, message?)` | `n: number`, `message?: string` | Maximum number of selections |
38
+ | `.exactChecked(n, message?)` | `n: number`, `message?: string` | Exact number of selections required |
39
+
40
+ ## Example
41
+
42
+ ```javascript
43
+ const interests = Observable([]);
44
+
45
+ CheckboxGroupField('interests')
46
+ .label('Select your interests')
47
+ .model(interests)
48
+ .option('frontend', 'Frontend')
49
+ .option('backend', 'Backend')
50
+ .option('devops', 'DevOps')
51
+ .option('mobile', 'Mobile')
52
+ .grid()
53
+ .minChecked(1, 'Select at least one interest')
54
+ ```
@@ -0,0 +1,64 @@
1
+ ---
2
+ title: ColorField
3
+ description: Color picker field with format options and presets
4
+ ---
5
+
6
+ # ColorField
7
+
8
+ ```javascript
9
+ import { ColorField } from 'native-document/components';
10
+
11
+ ColorField(name, props?)
12
+ ```
13
+
14
+ ## Default Renderer
15
+
16
+ ```javascript
17
+ import { ColorFieldRender } from 'native-document/ui';
18
+
19
+ ColorField.use(ColorFieldRender);
20
+ ```
21
+
22
+ ## Methods
23
+
24
+ All shared Field methods apply, plus:
25
+
26
+ | Method | Parameters | Description |
27
+ |---|---|---|
28
+ | `.format(type)` | `'hex' \| 'rgb'` | Output format |
29
+ | `.presets(colors)` | `colors: string[]` | Array of preset color strings shown as swatches |
30
+ | `.hex(message?)` | `message?: string` | Validate that the value is a valid hex color |
31
+ | `.rgb(message?)` | `message?: string` | Validate that the value is a valid RGB color |
32
+
33
+ ## Example
34
+
35
+ ```javascript
36
+ ColorField('brandColor')
37
+ .label('Brand Color')
38
+ .model(color)
39
+ .format('hex')
40
+ .presets(['#3b82f6', '#10b981', '#f59e0b', '#ef4444'])
41
+ ```
42
+
43
+ ## Reactive theme example
44
+
45
+ ```javascript
46
+ import { Store } from 'native-document';
47
+
48
+ const ThemeStore = Store.group('theme', (g) => {
49
+ g.createPersistent('primary', '#3b82f6');
50
+ g.createPersistent('accent', '#10b981');
51
+ });
52
+
53
+ ColorField('primary')
54
+ .label('Primary color')
55
+ .model(ThemeStore.use('primary'))
56
+ .format('hex')
57
+ .presets(['#3b82f6', '#6366f1', '#8b5cf6', '#ec4899'])
58
+
59
+ ColorField('accent')
60
+ .label('Accent color')
61
+ .model(ThemeStore.use('accent'))
62
+ .format('hex')
63
+ .presets(['#10b981', '#14b8a6', '#22c55e', '#84cc16'])
64
+ ```
@@ -0,0 +1,92 @@
1
+ ---
2
+ title: Date & Time Fields
3
+ description: DateField and TimeField for date and time input
4
+ ---
5
+
6
+ # Date & Time Fields
7
+
8
+ ```javascript
9
+ import { DateField, TimeField } from 'native-document/components';
10
+ ```
11
+
12
+ ## Default Renderers
13
+
14
+ ```javascript
15
+ import { DateFieldRender, TimeFieldRender } from 'native-document/ui';
16
+
17
+ DateField.use(DateFieldRender);
18
+ TimeField.use(TimeFieldRender);
19
+ ```
20
+
21
+ ---
22
+
23
+ ## `DateField`
24
+
25
+ ```javascript
26
+ DateField('birthdate')
27
+ .label('Date of birth')
28
+ .model(birthdate)
29
+ .minDate(new Date('1900-01-01'))
30
+ .maxDate(new Date())
31
+ .format('DD/MM/YYYY')
32
+ .required()
33
+ ```
34
+
35
+ ### Additional methods
36
+
37
+ ```javascript
38
+ .format(formatString)
39
+ .minDate(date)
40
+ .maxDate(date)
41
+ .min(date, message?)
42
+ .max(date, message?)
43
+ .between(start, end, message?)
44
+ .disabledDates(dates)
45
+ .withTime()
46
+ .range()
47
+ .modelStart(observable)
48
+ .modelEnd(observable)
49
+ .rangeSeparator(string)
50
+ .mondayAsFirstDay()
51
+ .sundayAsFirstDay()
52
+ .locale(locale)
53
+ .timezone(tz)
54
+ .useLocalTimezone()
55
+ .timeStep(seconds)
56
+ .fromToday()
57
+ .untilToday()
58
+ .onChange(handler)
59
+ .onClear(handler)
60
+ ```
61
+
62
+ ---
63
+
64
+ ## `TimeField`
65
+
66
+ ```javascript
67
+ TimeField('appointment')
68
+ .label('Appointment time')
69
+ .model(time)
70
+ .min('09:00', 'Too early')
71
+ .max('18:00', 'Too late')
72
+ .clearable()
73
+ ```
74
+
75
+ ### Additional methods
76
+
77
+ ```javascript
78
+ .format(formatString)
79
+ .step(seconds)
80
+ .clearable()
81
+ .range()
82
+ .modelStart(observable)
83
+ .modelEnd(observable)
84
+ .rangeSeparator(string)
85
+ .min(time, message?)
86
+ .max(time, message?)
87
+ .between(start, end, message?)
88
+ .after(time, message?)
89
+ .before(time, message?)
90
+ .onChange(handler)
91
+ .onClear(handler)
92
+ ```
@@ -0,0 +1,63 @@
1
+ ---
2
+ title: FieldCollection
3
+ description: Dynamic list of repeatable field groups with add, remove, and validation
4
+ ---
5
+
6
+ # FieldCollection
7
+
8
+ ```javascript
9
+ import { FieldCollection } from 'native-document/components';
10
+ ```
11
+
12
+ ## Default Renderer
13
+
14
+ ```javascript
15
+ import { FieldCollectionRender } from 'native-document/ui';
16
+
17
+ FieldCollection.use(FieldCollectionRender);
18
+ ```
19
+
20
+ ## Methods
21
+
22
+ ```javascript
23
+ // Fields definition
24
+ .fields((group) => {
25
+ return {
26
+ name: StringField('name').label('Name').required(),
27
+ email: EmailField('email').label('Email')
28
+ }
29
+ })
30
+
31
+ // Default item value
32
+ .data({ name: '', email: '' })
33
+
34
+ // Binding
35
+ .model(contacts)
36
+
37
+ // Render
38
+ .renderItem((fields, index, remove) => element)
39
+ .renderAdd(() => element)
40
+
41
+ // Animation
42
+ .transition('fade')
43
+
44
+ // Programmatic
45
+ .add()
46
+ .remove(item)
47
+ .clear()
48
+ .reset()
49
+ .value() // get all values
50
+ .count()
51
+ .isEmpty()
52
+
53
+ // Validation
54
+ .min(count, message?)
55
+ .max(count, message?)
56
+ .validate(allValues?)
57
+
58
+ // Events
59
+ .onChange((items) => console.log(items))
60
+ .onAdd((item) => console.log('Added'))
61
+ .onRemove((item) => console.log('Removed'))
62
+ ```
63
+
@@ -0,0 +1,203 @@
1
+ ---
2
+ title: FileField
3
+ description: File upload component with multiple modes - native, dropzone, button, wall, and avatar
4
+ ---
5
+
6
+ # FileField
7
+
8
+ ```javascript
9
+ import {
10
+ FileField,
11
+ FileNativeMode, FileDropzoneMode,
12
+ FileUploadButtonMode, FileWallMode, FileAvatarMode
13
+ } from 'native-document/components';
14
+ ```
15
+
16
+ ## Default Renderers
17
+
18
+ ```javascript
19
+ import {
20
+ FileFieldRender,
21
+ FileNativeModeRender, FileDropzoneModeRender,
22
+ FileUploadButtonModeRender, FileWallModeRender, FileAvatarModeRender
23
+ } from 'native-document/ui';
24
+
25
+ FileField.use(FileFieldRender);
26
+ FileNativeMode.use(FileNativeModeRender);
27
+ FileDropzoneMode.use(FileDropzoneModeRender);
28
+ FileUploadButtonMode.use(FileUploadButtonModeRender);
29
+ FileWallMode.use(FileWallModeRender);
30
+ FileAvatarMode.use(FileAvatarModeRender);
31
+ ```
32
+
33
+ ## Methods
34
+
35
+ ### Mode & Accept
36
+
37
+ | Method | Parameters | Description |
38
+ |---|---|---|
39
+ | `.mode(modeInstance)` | `modeInstance: FileMode` | Set the upload mode |
40
+ | `.accept(mimeTypes)` | `mimeTypes: string[]` | Accepted MIME types |
41
+ | `.mimeTypes(types, message?)` | `types: string[]`, `message?: string` | Validate MIME types |
42
+ | `.extensions(exts, message?)` | `exts: string[]`, `message?: string` | Validate file extensions |
43
+
44
+ ### Limits
45
+
46
+ | Method | Parameters | Description |
47
+ |---|---|---|
48
+ | `.multiple(enabled?)` | `enabled?: boolean` | Allow multiple files |
49
+ | `.maxFiles(n, message?)` | `n: number`, `message?: string` | Maximum number of files |
50
+ | `.minFiles(n, message?)` | `n: number`, `message?: string` | Minimum number of files |
51
+ | `.maxSize(bytes, message?)` | `bytes: number`, `message?: string` | Maximum file size in bytes |
52
+ | `.minSize(bytes, message?)` | `bytes: number`, `message?: string` | Minimum file size in bytes |
53
+
54
+ ### File management
55
+
56
+ | Method | Parameters | Description |
57
+ |---|---|---|
58
+ | `.addFile(file)` | `file: File` | Add a single file programmatically |
59
+ | `.addFiles(files)` | `files: File[]` | Add multiple files |
60
+ | `.setFiles(files)` | `files: File[]` | Replace all files |
61
+ | `.removeFile(file)` | `file: File` | Remove a specific file |
62
+ | `.getFiles()` | - | Returns the current file list |
63
+ | `.reset()` | - | Clear all files |
64
+ | `.fileIcon(desc)` | `desc: { type: string, icon: element }` | Icon for a specific MIME type |
65
+ | `.fileIcons(descs)` | `descs: { type, icon }[]` | Icons for multiple MIME types |
66
+
67
+ ### Events
68
+
69
+ | Method | Parameters | Description |
70
+ |---|---|---|
71
+ | `.onFileAdd(handler)` | `handler: (file, files) => void` | Fires when a file is added |
72
+ | `.onFileRemove(handler)` | `handler: (file, files) => void` | Fires when a file is removed |
73
+ | `.onReset(handler)` | `handler: () => void` | Fires when files are cleared |
74
+
75
+ ## Modes
76
+
77
+ ### `FileNativeMode`
78
+
79
+ Standard browser file picker. No extra methods.
80
+
81
+ ```javascript
82
+ FileField('document')
83
+ .accept(['application/pdf'])
84
+ .mode(FileNativeMode())
85
+ ```
86
+
87
+ ### `FileDropzoneMode`
88
+
89
+ Drag-and-drop area.
90
+
91
+ | Method | Parameters | Description |
92
+ |---|---|---|
93
+ | `.icon(element)` | `element: NdChild` | Icon displayed in the zone |
94
+ | `.text(text)` | `text: string` | Main label |
95
+ | `.hint(text)` | `text: string` | Secondary hint text |
96
+ | `.height(px)` | `px: number` | Zone height in px |
97
+ | `.removeIcon(element)` | `element: NdChild` | Icon for the remove button |
98
+ | `.renderZone(fn)` | `fn: ($description) => NdChild` | Custom zone renderer |
99
+
100
+ ```javascript
101
+ FileField('files')
102
+ .multiple()
103
+ .mode(
104
+ FileDropzoneMode()
105
+ .text('Drop files here or click to browse')
106
+ .hint('PDF, PNG, JPG up to 10MB')
107
+ .icon(UploadIcon)
108
+ .height(200)
109
+ )
110
+ ```
111
+
112
+ ### `FileUploadButtonMode`
113
+
114
+ A button that opens the file picker, with a list of selected files below it.
115
+
116
+ | Method | Parameters | Description |
117
+ |---|---|---|
118
+ | `.buttonLabel(text)` | `text: string` | Button label |
119
+ | `.buttonIcon(element)` | `element: NdChild` | Button icon |
120
+ | `.showProgress(enabled?)` | `enabled?: boolean` | Show upload progress |
121
+ | `.renderItem(fn)` | `fn: ($file) => NdChild` | Custom file item renderer |
122
+ | `.renderButton(fn)` | `fn: ($description) => NdChild` | Custom button renderer |
123
+ | `.renderList(fn)` | `fn: ($files) => NdChild` | Custom file list renderer |
124
+
125
+ ```javascript
126
+ FileField('attachment')
127
+ .mode(
128
+ FileUploadButtonMode()
129
+ .buttonLabel('Upload File')
130
+ .buttonIcon(UploadIcon)
131
+ .showProgress()
132
+ )
133
+ ```
134
+
135
+ ### `FileWallMode`
136
+
137
+ Grid of file previews with remove buttons.
138
+
139
+ | Method | Parameters | Description |
140
+ |---|---|---|
141
+ | `.cellSize(px)` | `px: number` | Cell size in px |
142
+ | `.addLabel(text)` | `text: string` | Label on the add button |
143
+ | `.addIcon(element)` | `element: NdChild` | Icon on the add button |
144
+ | `.renderCell(fn)` | `fn: ($file) => NdChild` | Custom file cell renderer |
145
+ | `.renderAdd(fn)` | `fn: () => NdChild` | Custom add button renderer |
146
+
147
+ ```javascript
148
+ FileField('gallery')
149
+ .multiple()
150
+ .accept(['image/*'])
151
+ .mode(
152
+ FileWallMode()
153
+ .cellSize(120)
154
+ .addLabel('Add image')
155
+ .addIcon(PlusIcon)
156
+ )
157
+ ```
158
+
159
+ ### `FileAvatarMode`
160
+
161
+ Single image upload styled as a circular avatar.
162
+
163
+ | Method | Parameters | Description |
164
+ |---|---|---|
165
+ | `.hoverOverlay()` | - | Show edit overlay on hover |
166
+ | `.cornerBadge()` | - | Show a badge in the corner |
167
+ | `.actionButtons()` | - | Show action buttons |
168
+ | `.circle()` | - | Circular shape (default) |
169
+ | `.square()` | - | Square shape |
170
+ | `.size(size)` | `size: string \| number` | Avatar size |
171
+ | `.placeholderIcon(element)` | `element: NdChild` | Icon shown when no image |
172
+ | `.overlayIcon(element)` | `element: NdChild` | Icon shown on hover overlay |
173
+ | `.editImageIcon(element)` | `element: NdChild` | Edit icon |
174
+ | `.changeLabel(text)` | `text: string` | Label for the change action |
175
+
176
+ ```javascript
177
+ FileField('avatar')
178
+ .accept(['image/*'])
179
+ .mode(
180
+ FileAvatarMode()
181
+ .hoverOverlay()
182
+ .circle()
183
+ )
184
+ ```
185
+
186
+ ## Example
187
+
188
+ ```javascript
189
+ FileField('documents')
190
+ .multiple()
191
+ .maxFiles(10)
192
+ .accept(['application/pdf'])
193
+ .maxSize(5 * 1024 * 1024, 'Max 5MB per file')
194
+ .mode(
195
+ FileDropzoneMode()
196
+ .text('Drop PDFs here')
197
+ .icon(PdfIcon)
198
+ )
199
+ .onFileAdd(async (file) => {
200
+ const url = await uploadFile(file);
201
+ console.log('Uploaded:', url);
202
+ })
203
+ ```
@@ -0,0 +1,87 @@
1
+ ---
2
+ title: FormControl
3
+ description: Form container with group validation, error handling, and submit management
4
+ ---
5
+
6
+ # FormControl
7
+
8
+ ```javascript
9
+ import { FormControl } from 'native-document/components';
10
+ ```
11
+
12
+ ## Default Renderer
13
+
14
+ ```javascript
15
+ import { FormControlRender } from 'native-document/ui';
16
+
17
+ FormControl.use(FormControlRender);
18
+ ```
19
+
20
+ ## Methods
21
+
22
+ ```javascript
23
+ // Fields
24
+ .fields((group) => {
25
+ group.add(StringField('name').label('Name').required())
26
+ group.add(EmailField('email').label('Email').required())
27
+ })
28
+ .get(fieldName) // get a field instance by name
29
+
30
+ // Layout
31
+ .layout(($description) => Div($description.fields))
32
+
33
+ // Validation
34
+ .validate(allValues?) // returns Promise<boolean>
35
+ .trigger(...fieldNames) // trigger specific fields
36
+ .errorsMode('inline') // 'inline' | 'summary' | 'both'
37
+ .errorsAtTop()
38
+ .errorsAtBottom()
39
+ .errorsPosition(position)
40
+ .renderErrors(($description) => element)
41
+ .dispatchErrors(mapper?)
42
+ .summarizeErrors()
43
+ .dispatchAndSummarize(mapper?)
44
+ .dispatchServerErrors(error)
45
+
46
+ // State
47
+ .disable(fieldName?) // disable all or specific field
48
+ .enable(fieldName?)
49
+ .reset()
50
+ .resetField(name)
51
+
52
+ // Values
53
+ .values() // returns { name: value } object
54
+
55
+ // Watch
56
+ .watch(fieldName, (value) => console.log(value))
57
+
58
+ // Submit
59
+ .onSubmit((values) => {})
60
+ .onPreventSubmit((values) => {})
61
+ .onDebouncedSubmit((values) => {}, delay?)
62
+ .onSuccess((values) => {})
63
+ .onError((error) => {})
64
+ .onChange((values) => {})
65
+ .onReset(() => {})
66
+ .submit(event?)
67
+ ```
68
+
69
+ ## Example
70
+
71
+ ```javascript
72
+ const control = FormControl()
73
+ .fields((group) => {
74
+ group.add(StringField('name').label('Name').required())
75
+ group.add(EmailField('email').label('Email').required())
76
+ group.add(PasswordField('password').label('Password').required().minLength(8))
77
+ })
78
+ .onSubmit(async (values) => {
79
+ const valid = await control.validate();
80
+ if (valid) {
81
+ await createUser(values);
82
+ }
83
+ })
84
+ .onError((error) => {
85
+ control.dispatchErrors(error);
86
+ })
87
+ ```
@@ -0,0 +1,90 @@
1
+ ---
2
+ title: ImageField
3
+ description: Image upload field with preview, crop, and dimension validation
4
+ ---
5
+
6
+ # ImageField
7
+
8
+ ```javascript
9
+ import { ImageField } from 'native-document/components';
10
+
11
+ ImageField(name, props?)
12
+ ```
13
+
14
+ `ImageField` extends `FileField` and is pre-configured for single image uploads. It adds image-specific validation (dimensions, aspect ratio) on top of the full `FileField` API.
15
+
16
+ ## Default Renderer
17
+
18
+ ```javascript
19
+ import { FileFieldRender, FileNativeModeRender } from 'native-document/ui';
20
+
21
+ ImageField.use(FileFieldRender);
22
+ ```
23
+
24
+ ## Methods
25
+
26
+ All `FileField` methods apply (`accept`, `maxSize`, `mode`, `onFileAdd`…). See **[FileField](./file-field.md)** for the full reference. Image-specific additions:
27
+
28
+ | Method | Parameters | Description |
29
+ |---|---|---|
30
+ | `.maxWidth(px)` | `px: number` | Maximum image width in px |
31
+ | `.maxHeight(px)` | `px: number` | Maximum image height in px |
32
+ | `.crop(enabled?)` | `enabled?: boolean` | Enable image cropping before upload |
33
+ | `.dimensions(w, h, message?)` | `w: number`, `h: number`, `message?: string` | Validate exact dimensions |
34
+ | `.maxDimensions(w, h, message?)` | `w: number`, `h: number`, `message?: string` | Validate maximum dimensions |
35
+ | `.minDimensions(w, h, message?)` | `w: number`, `h: number`, `message?: string` | Validate minimum dimensions |
36
+ | `.aspectRatio(ratio, message?)` | `ratio: string`, `message?: string` | Validate aspect ratio (e.g. `'16:9'`, `'1:1'`) |
37
+
38
+ ## ImageField vs FileField
39
+
40
+ Use `ImageField` when you need image-specific validation (dimensions, aspect ratio, crop). Use `FileField` when you need multiple file types or multi-file upload.
41
+
42
+ | | ImageField | FileField |
43
+ |---|---|---|
44
+ | Single image | Yes (default) | Yes (with `.accept`) |
45
+ | Multiple files | No | Yes (`.multiple()`) |
46
+ | Dimension validation | Yes | No |
47
+ | Aspect ratio validation | Yes | No |
48
+ | Crop | Yes | No |
49
+ | All upload modes | Yes (via inheritance) | Yes |
50
+
51
+ ## Example
52
+
53
+ ```javascript
54
+ ImageField('cover')
55
+ .label('Cover Image')
56
+ .model(imageUrl)
57
+ .accept(['image/jpeg', 'image/png'])
58
+ .maxSize(5 * 1024 * 1024, 'Max 5MB')
59
+ .maxDimensions(2000, 2000, 'Image too large')
60
+ .aspectRatio('16:9', 'Must be 16:9')
61
+ .crop()
62
+ ```
63
+
64
+ ## Avatar example
65
+
66
+ ```javascript
67
+ import { FileAvatarMode } from 'native-document/components';
68
+ import { FileAvatarModeRender } from 'native-document/ui';
69
+
70
+ FileAvatarMode.use(FileAvatarModeRender);
71
+
72
+ ImageField('avatar')
73
+ .label('Profile picture')
74
+ .model(avatarUrl)
75
+ .accept(['image/jpeg', 'image/png', 'image/webp'])
76
+ .maxSize(2 * 1024 * 1024, 'Max 2MB')
77
+ .aspectRatio('1:1', 'Must be square')
78
+ .crop()
79
+ .mode(
80
+ FileAvatarMode()
81
+ .hoverOverlay()
82
+ .circle()
83
+ )
84
+ ```
85
+
86
+ ---
87
+
88
+ ## Next Steps
89
+
90
+ - **[FileField](./file-field.md)** - Full file upload reference