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.
- package/.vitepress/config.js +166 -0
- package/CHANGELOG.md +153 -0
- package/components.js +2 -1
- package/dist/native-document.components.min.js +495 -228
- package/dist/native-document.dev.js +7 -0
- package/dist/native-document.dev.js.map +1 -1
- package/dist/native-document.min.js +1 -1
- package/docs/advanced-components.md +213 -608
- package/docs/anchor.md +173 -312
- package/docs/cache.md +95 -803
- package/docs/cli.md +179 -0
- package/docs/components/accordion.md +172 -0
- package/docs/components/alert.md +99 -0
- package/docs/components/avatar.md +160 -0
- package/docs/components/badge.md +102 -0
- package/docs/components/breadcrumb.md +89 -0
- package/docs/components/button.md +183 -0
- package/docs/components/card.md +69 -0
- package/docs/components/context-menu.md +118 -0
- package/docs/components/data-table.md +345 -0
- package/docs/components/dropdown.md +214 -0
- package/docs/components/form/autocomplete-field.md +81 -0
- package/docs/components/form/checkbox-field.md +41 -0
- package/docs/components/form/checkbox-group-field.md +54 -0
- package/docs/components/form/color-field.md +64 -0
- package/docs/components/form/date-field.md +92 -0
- package/docs/components/form/field-collection.md +63 -0
- package/docs/components/form/file-field.md +203 -0
- package/docs/components/form/form-control.md +87 -0
- package/docs/components/form/image-field.md +90 -0
- package/docs/components/form/index.md +115 -0
- package/docs/components/form/number-field.md +65 -0
- package/docs/components/form/radio-field.md +51 -0
- package/docs/components/form/select-field.md +123 -0
- package/docs/components/form/slider.md +136 -0
- package/docs/components/form/string-field.md +134 -0
- package/docs/components/form/textarea-field.md +65 -0
- package/docs/components/form-fields.md +372 -0
- package/docs/components/getting-started.md +264 -0
- package/docs/components/index.md +337 -0
- package/docs/components/layout.md +279 -0
- package/docs/components/list.md +73 -0
- package/docs/components/menu.md +215 -0
- package/docs/components/modal.md +156 -0
- package/docs/components/pagination.md +95 -0
- package/docs/components/popover.md +131 -0
- package/docs/components/progress.md +111 -0
- package/docs/components/shortcut-manager.md +221 -0
- package/docs/components/simple-table.md +107 -0
- package/docs/components/skeleton.md +155 -0
- package/docs/components/spinner.md +100 -0
- package/docs/components/splitter.md +133 -0
- package/docs/components/stepper.md +163 -0
- package/docs/components/switch.md +113 -0
- package/docs/components/tabs.md +153 -0
- package/docs/components/toast.md +119 -0
- package/docs/components/tooltip.md +151 -0
- package/docs/components/traits.md +261 -0
- package/docs/conditional-rendering.md +170 -588
- package/docs/contributing.md +300 -25
- package/docs/core-concepts.md +205 -374
- package/docs/elements.md +251 -367
- package/docs/extending-native-document-element.md +192 -207
- package/docs/filters.md +153 -1122
- package/docs/getting-started.md +193 -267
- package/docs/i18n.md +241 -0
- package/docs/index.md +76 -0
- package/docs/lifecycle-events.md +143 -75
- package/docs/list-rendering.md +227 -852
- package/docs/memory-management.md +134 -47
- package/docs/native-document-element.md +337 -186
- package/docs/native-fetch.md +99 -630
- package/docs/observable-resource.md +364 -0
- package/docs/observables.md +592 -526
- package/docs/routing.md +244 -653
- package/docs/state-management.md +134 -241
- package/docs/svg-elements.md +231 -0
- package/docs/theming.md +409 -0
- package/docs/tutorials/.gitkeep +0 -0
- package/docs/validation.md +95 -97
- package/docs/vitepress-conventions.md +219 -0
- package/package.json +34 -13
- package/readme.md +269 -89
- package/src/components/card/Card.js +93 -39
- package/src/components/card/index.js +1 -1
- package/src/components/list/HasListItem.js +171 -0
- package/src/components/list/List.js +41 -107
- package/src/components/list/ListDivider.js +39 -0
- package/src/components/list/ListGroup.js +76 -59
- package/src/components/list/ListItem.js +117 -69
- package/src/components/list/index.js +3 -1
- package/src/components/list/types/ListItem.d.ts +45 -34
- package/src/components/spacer/Spacer.js +1 -1
- package/src/core/data/ObservableResource.js +5 -0
- package/src/core/data/observable-helpers/observable.prototypes.js +2 -0
- package/src/ui/components/card/CardRender.js +133 -0
- package/src/ui/components/card/card.css +169 -0
- package/src/ui/components/contextmenu/ContextmenuRender.js +1 -1
- package/src/ui/components/list/ListRender.js +18 -0
- package/src/ui/components/list/divider/ListDividerRender.js +10 -0
- package/src/ui/components/list/divider/list-divider.css +12 -0
- package/src/ui/components/list/group/ListGroupRender.js +61 -0
- package/src/ui/components/list/group/list-group.css +62 -0
- package/src/ui/components/list/item/ListItemRender.js +238 -0
- package/src/ui/components/list/item/list-item.css +191 -0
- package/src/ui/components/list/list.css +24 -0
- package/src/ui/components/spacer/SpacerRender.js +10 -0
- 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
|