native-document 1.0.190 → 1.0.191
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/CHANGELOG.md +1 -1
- package/docs/components/form/file-field.md +11 -11
- package/docs/components/form/image-field.md +5 -5
- package/docs/components/form-fields.md +1 -1
- package/package.json +1 -1
- package/src/components/form/field/types/FileField.js +18 -8
- package/src/components/form/field/types/file-field-mode/FileImagePreviewMode.js +274 -0
- package/src/components/form/index.js +2 -2
- package/src/components/form/types/fields/FileField.d.ts +3 -3
- package/src/components/form/types/fields/FileImagePreviewMode.d.ts +57 -0
- package/src/components/index.d.ts +1 -1
- package/src/core/data/ObservableObject.js +13 -0
- package/src/fetch/NativeFetch.js +5 -2
- package/src/ui/components/form/fields/NumberFieldRender.js +3 -5
- package/src/ui/components/form/file-upload-mode/FileImagePreviewModeRender.js +162 -0
- package/src/ui/components/form/file-upload-mode/{file-avatar-mode.css → file-image-preview-mode.css} +54 -23
- package/src/ui/components/form/index.js +1 -1
- package/types/observable.d.ts +1 -1
- package/src/components/form/field/types/file-field-mode/FileAvatarMode.js +0 -183
- package/src/components/form/types/fields/FileAvatarMode.d.ts +0 -46
- package/src/ui/components/form/file-upload-mode/FileAvatarModeRender.js +0 -143
package/CHANGELOG.md
CHANGED
|
@@ -247,7 +247,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
247
247
|
- **Data**: `DataTable` + `Column` + `ColumnGroup`, `SimpleTable`, `Accordion` + `AccordionItem`, `List`
|
|
248
248
|
- **Interaction**: `Button`, `Switch`, `Slider`
|
|
249
249
|
- **Media**: `Avatar`, `AvatarGroup`, `Card`
|
|
250
|
-
- **Forms**: `FormControl`, `FieldCollection`, `StringField`, `EmailField`, `PasswordField`, `TelField`, `UrlField`, `HiddenField`, `NumberField`, `RangeField`, `TextAreaField`, `CheckboxField`, `CheckboxGroupField`, `RadioField`, `SelectField`, `AutocompleteField`, `DateField`, `TimeField`, `ColorField`, `SliderField`, `ImageField`, `FileField` (with `FileNativeMode`, `FileDropzoneMode`, `FileUploadButtonMode`, `FileWallMode`, `
|
|
250
|
+
- **Forms**: `FormControl`, `FieldCollection`, `StringField`, `EmailField`, `PasswordField`, `TelField`, `UrlField`, `HiddenField`, `NumberField`, `RangeField`, `TextAreaField`, `CheckboxField`, `CheckboxGroupField`, `RadioField`, `SelectField`, `AutocompleteField`, `DateField`, `TimeField`, `ColorField`, `SliderField`, `ImageField`, `FileField` (with `FileNativeMode`, `FileDropzoneMode`, `FileUploadButtonMode`, `FileWallMode`, `FileImagePreviewMode`)
|
|
251
251
|
- **Traits**: `HasEventEmitter`, `HasDraggable`, `HasResizable`, `HasItems`, `HasPosition`, `HasFullPosition`, `HasValidation`
|
|
252
252
|
|
|
253
253
|
#### CLI (`@native-document/cli`)
|
|
@@ -9,7 +9,7 @@ description: File upload component with multiple modes - native, dropzone, butto
|
|
|
9
9
|
import {
|
|
10
10
|
FileField,
|
|
11
11
|
FileNativeMode, FileDropzoneMode,
|
|
12
|
-
FileUploadButtonMode, FileWallMode,
|
|
12
|
+
FileUploadButtonMode, FileWallMode, FileImagePreviewMode
|
|
13
13
|
} from 'native-document/components';
|
|
14
14
|
```
|
|
15
15
|
|
|
@@ -19,7 +19,7 @@ import {
|
|
|
19
19
|
import {
|
|
20
20
|
FileFieldRender,
|
|
21
21
|
FileNativeModeRender, FileDropzoneModeRender,
|
|
22
|
-
FileUploadButtonModeRender, FileWallModeRender,
|
|
22
|
+
FileUploadButtonModeRender, FileWallModeRender, FileImagePreviewModeRender
|
|
23
23
|
} from 'native-document/ui';
|
|
24
24
|
|
|
25
25
|
FileField.use(FileFieldRender);
|
|
@@ -27,7 +27,7 @@ FileNativeMode.use(FileNativeModeRender);
|
|
|
27
27
|
FileDropzoneMode.use(FileDropzoneModeRender);
|
|
28
28
|
FileUploadButtonMode.use(FileUploadButtonModeRender);
|
|
29
29
|
FileWallMode.use(FileWallModeRender);
|
|
30
|
-
|
|
30
|
+
FileImagePreviewMode.use(FileImagePreviewModeRender);
|
|
31
31
|
```
|
|
32
32
|
|
|
33
33
|
## Methods
|
|
@@ -66,11 +66,11 @@ FileAvatarMode.use(FileAvatarModeRender);
|
|
|
66
66
|
|
|
67
67
|
### Events
|
|
68
68
|
|
|
69
|
-
| Method
|
|
70
|
-
|
|
71
|
-
| `.
|
|
72
|
-
| `.
|
|
73
|
-
| `.onReset(handler)`
|
|
69
|
+
| Method | Parameters | Description |
|
|
70
|
+
|--------------------------|---|---|
|
|
71
|
+
| `.onAddFile(handler)` | `handler: (file, files) => void` | Fires when a file is added |
|
|
72
|
+
| `.onRemoveFile(handler)` | `handler: (file, files) => void` | Fires when a file is removed |
|
|
73
|
+
| `.onReset(handler)` | `handler: () => void` | Fires when files are cleared |
|
|
74
74
|
|
|
75
75
|
## Modes
|
|
76
76
|
|
|
@@ -156,7 +156,7 @@ FileField('gallery')
|
|
|
156
156
|
)
|
|
157
157
|
```
|
|
158
158
|
|
|
159
|
-
### `
|
|
159
|
+
### `FileImagePreviewMode`
|
|
160
160
|
|
|
161
161
|
Single image upload styled as a circular avatar.
|
|
162
162
|
|
|
@@ -177,7 +177,7 @@ Single image upload styled as a circular avatar.
|
|
|
177
177
|
FileField('avatar')
|
|
178
178
|
.accept(['image/*'])
|
|
179
179
|
.mode(
|
|
180
|
-
|
|
180
|
+
FileImagePreviewMode()
|
|
181
181
|
.hoverOverlay()
|
|
182
182
|
.circle()
|
|
183
183
|
)
|
|
@@ -196,7 +196,7 @@ FileField('documents')
|
|
|
196
196
|
.text('Drop PDFs here')
|
|
197
197
|
.icon(PdfIcon)
|
|
198
198
|
)
|
|
199
|
-
.
|
|
199
|
+
.onAddFile(async (file) => {
|
|
200
200
|
const url = await uploadFile(file);
|
|
201
201
|
console.log('Uploaded:', url);
|
|
202
202
|
})
|
|
@@ -23,7 +23,7 @@ ImageField.use(FileFieldRender);
|
|
|
23
23
|
|
|
24
24
|
## Methods
|
|
25
25
|
|
|
26
|
-
All `FileField` methods apply (`accept`, `maxSize`, `mode`, `
|
|
26
|
+
All `FileField` methods apply (`accept`, `maxSize`, `mode`, `onAddFile`…). See **[FileField](./file-field.md)** for the full reference. Image-specific additions:
|
|
27
27
|
|
|
28
28
|
| Method | Parameters | Description |
|
|
29
29
|
|---|---|---|
|
|
@@ -64,10 +64,10 @@ ImageField('cover')
|
|
|
64
64
|
## Avatar example
|
|
65
65
|
|
|
66
66
|
```javascript
|
|
67
|
-
import {
|
|
68
|
-
import {
|
|
67
|
+
import { FileImagePreviewMode } from 'native-document/components';
|
|
68
|
+
import { FileImagePreviewModeRender } from 'native-document/ui';
|
|
69
69
|
|
|
70
|
-
|
|
70
|
+
FileImagePreviewMode.use(FileImagePreviewModeRender);
|
|
71
71
|
|
|
72
72
|
ImageField('avatar')
|
|
73
73
|
.label('Profile picture')
|
|
@@ -77,7 +77,7 @@ ImageField('avatar')
|
|
|
77
77
|
.aspectRatio('1:1', 'Must be square')
|
|
78
78
|
.crop()
|
|
79
79
|
.mode(
|
|
80
|
-
|
|
80
|
+
FileImagePreviewMode()
|
|
81
81
|
.hoverOverlay()
|
|
82
82
|
.circle()
|
|
83
83
|
)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "native-document",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.191",
|
|
4
4
|
"description": "A reactive JavaScript framework that preserves native DOM simplicity without sacrificing modern features",
|
|
5
5
|
"author": "AfroCodeur <https://github.com/afrocodeur>",
|
|
6
6
|
"license": "MIT",
|
|
@@ -14,8 +14,8 @@ import FileItemPreview from '../../field/types/file-field-mode/FileItemPreview';
|
|
|
14
14
|
* .maxSize(5 * 1024 * 1024, 'Max 5MB per file')
|
|
15
15
|
* .maxFiles(3, 'Max 3 files')
|
|
16
16
|
* .mode(FileDropzoneMode())
|
|
17
|
-
* .
|
|
18
|
-
* .
|
|
17
|
+
* .onAddFile((file) => console.log('added', file.name))
|
|
18
|
+
* .onRemoveFile((file) => console.log('removed', file.name));
|
|
19
19
|
*
|
|
20
20
|
* FileField.use((description, instance) => {
|
|
21
21
|
* // description.accept, description.multiple, description.mode,
|
|
@@ -54,7 +54,7 @@ FileField.defaultTemplate = null;
|
|
|
54
54
|
* label: NdChild|null,
|
|
55
55
|
* accept: string|null,
|
|
56
56
|
* multiple: boolean,
|
|
57
|
-
* mode: FileNativeMode|
|
|
57
|
+
* mode: FileNativeMode|FileImagePreviewMode|FileDropzoneMode|FileUploadButtonMode|FileWallMode|null,
|
|
58
58
|
* files: Observable<FileItemPreview[]>,
|
|
59
59
|
* fileIcons: Array<(file: File) => NdChild>,
|
|
60
60
|
* disabled: boolean|Observable<boolean>,
|
|
@@ -91,7 +91,7 @@ FileField.prototype.multiple = function(enabled = true) {
|
|
|
91
91
|
};
|
|
92
92
|
|
|
93
93
|
/**
|
|
94
|
-
* @param {FileNativeMode|
|
|
94
|
+
* @param {FileNativeMode|FileImagePreviewMode|FileDropzoneMode|FileUploadButtonMode|FileWallMode|null} mode
|
|
95
95
|
* @returns {this}
|
|
96
96
|
*/
|
|
97
97
|
FileField.prototype.mode = function(mode) {
|
|
@@ -154,14 +154,23 @@ FileField.prototype.minFiles = function(min, message) {
|
|
|
154
154
|
};
|
|
155
155
|
|
|
156
156
|
/**
|
|
157
|
-
* @param {(file:
|
|
157
|
+
* @param {(file: FileItemPreview) => void} handler
|
|
158
158
|
* @returns {this}
|
|
159
159
|
*/
|
|
160
|
-
FileField.prototype.
|
|
160
|
+
FileField.prototype.onAddFile = function(handler) {
|
|
161
161
|
this.on('fileAdd', handler);
|
|
162
162
|
return this;
|
|
163
163
|
};
|
|
164
164
|
|
|
165
|
+
/**
|
|
166
|
+
* @param {(file: File) => void} handler
|
|
167
|
+
* @returns {this}
|
|
168
|
+
*/
|
|
169
|
+
FileField.prototype.onSelectFile = function(handler) {
|
|
170
|
+
this.on('selectFile', handler);
|
|
171
|
+
return this;
|
|
172
|
+
};
|
|
173
|
+
|
|
165
174
|
/**
|
|
166
175
|
* @param {() => void} handler
|
|
167
176
|
* @returns {this}
|
|
@@ -175,7 +184,7 @@ FileField.prototype.onReset = function(handler) {
|
|
|
175
184
|
* @param {(file: File) => void} handler
|
|
176
185
|
* @returns {this}
|
|
177
186
|
*/
|
|
178
|
-
FileField.prototype.
|
|
187
|
+
FileField.prototype.onRemoveFile = function(handler) {
|
|
179
188
|
this.on('fileRemove', handler);
|
|
180
189
|
return this;
|
|
181
190
|
};
|
|
@@ -189,6 +198,7 @@ FileField.prototype.onFileRemove = function(handler) {
|
|
|
189
198
|
FileField.prototype.addFile = function(file, update = true) {
|
|
190
199
|
const item = new FileItemPreview(file);
|
|
191
200
|
this.$description.files.push(item);
|
|
201
|
+
this.emit('selectFile', file);
|
|
192
202
|
this.emit('fileAdd', item);
|
|
193
203
|
update && this.$update();
|
|
194
204
|
return this;
|
|
@@ -222,7 +232,7 @@ FileField.prototype.addFiles = function(files) {
|
|
|
222
232
|
|
|
223
233
|
FileField.prototype.$update = function() {
|
|
224
234
|
const files = this.$description.files.val();
|
|
225
|
-
this.$description.value
|
|
235
|
+
this.$description.value?.set(this.$description.multiple ? files.map(i => i.file()) : files[0]?.file() || null);
|
|
226
236
|
this.validate();
|
|
227
237
|
};
|
|
228
238
|
|
|
@@ -0,0 +1,274 @@
|
|
|
1
|
+
import BaseComponent from '../../../../BaseComponent';
|
|
2
|
+
import HasEventEmitter from '../../../../../core/utils/HasEventEmitter';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Avatar-style single file upload mode for FileField.
|
|
6
|
+
* Displays a circular or square avatar with hover/badge/button interaction variants.
|
|
7
|
+
* Ideal for profile picture uploads.
|
|
8
|
+
* @example
|
|
9
|
+
* new FileField('avatar')
|
|
10
|
+
* .mode('avatar');
|
|
11
|
+
*
|
|
12
|
+
* FileImagePreviewMode.use((description, instance) => {
|
|
13
|
+
* // description.variant — 'hover-overlay' | 'corner-badge' | 'action-buttons'
|
|
14
|
+
* // description.shape — 'circle' | 'square'
|
|
15
|
+
* // description.size, description.placeholderIcon, description.overlayIcon,
|
|
16
|
+
* // description.editImageIcon, description.changeLabel, description.removeLabel,
|
|
17
|
+
* // description.renderAvatar, description.renderOverlay, description.renderActions
|
|
18
|
+
* return Div({ class: \`avatar-upload avatar-upload--\${description.variant}\`,
|
|
19
|
+
* style: { width: description.size, height: description.size } });
|
|
20
|
+
* });
|
|
21
|
+
*
|
|
22
|
+
* @constructor
|
|
23
|
+
* @param {GlobalAttributes} [props={}]
|
|
24
|
+
*/
|
|
25
|
+
export default function FileImagePreviewMode(props = {}) {
|
|
26
|
+
if(!(this instanceof FileImagePreviewMode)) {
|
|
27
|
+
return new FileImagePreviewMode(props);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
BaseComponent.call(this, props);
|
|
31
|
+
|
|
32
|
+
this.$description = {
|
|
33
|
+
variant: 'hover-overlay',
|
|
34
|
+
shape: 'circle',
|
|
35
|
+
size: null,
|
|
36
|
+
placeholderIcon: null,
|
|
37
|
+
overlayIcon: null,
|
|
38
|
+
editImageIcon: null,
|
|
39
|
+
changeLabel: 'Change photo',
|
|
40
|
+
removeLabel: 'Remove',
|
|
41
|
+
renderAvatar: null,
|
|
42
|
+
renderOverlay: null,
|
|
43
|
+
renderActions: null,
|
|
44
|
+
mode: 'avatar',
|
|
45
|
+
ratio: null,
|
|
46
|
+
props,
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
BaseComponent.extends(FileImagePreviewMode);
|
|
51
|
+
BaseComponent.use(FileImagePreviewMode, HasEventEmitter);
|
|
52
|
+
|
|
53
|
+
FileImagePreviewMode.defaultTemplate = null;
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Registers the render template for FileImagePreviewMode.
|
|
57
|
+
* @param {(description: {
|
|
58
|
+
* variant: 'hover-overlay'|'corner-badge'|'action-buttons',
|
|
59
|
+
* shape: 'circle'|'square',
|
|
60
|
+
* size: number|string,
|
|
61
|
+
* placeholderIcon: NdChild|null,
|
|
62
|
+
* overlayIcon: NdChild|null,
|
|
63
|
+
* editImageIcon: NdChild|null,
|
|
64
|
+
* changeLabel: NdChild,
|
|
65
|
+
* removeLabel: NdChild,
|
|
66
|
+
* renderAvatar: ((desc: *, instance: FileImagePreviewMode) => NdChild)|null,
|
|
67
|
+
* renderOverlay: ((desc: *, instance: FileImagePreviewMode) => NdChild)|null,
|
|
68
|
+
* renderActions: ((desc: *, instance: FileImagePreviewMode) => NdChild)|null,
|
|
69
|
+
* props: GlobalAttributes
|
|
70
|
+
* }, instance: FileImagePreviewMode) => NdChild} template
|
|
71
|
+
*/
|
|
72
|
+
FileImagePreviewMode.use = function(template) {
|
|
73
|
+
FileImagePreviewMode.defaultTemplate = template;
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
/** @returns {this} */
|
|
77
|
+
FileImagePreviewMode.prototype.hoverOverlay = function() {
|
|
78
|
+
this.$description.variant = 'hover-overlay';
|
|
79
|
+
return this;
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
/** @returns {this} */
|
|
83
|
+
FileImagePreviewMode.prototype.cornerBadge = function() {
|
|
84
|
+
this.$description.variant = 'corner-badge';
|
|
85
|
+
return this;
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
/** @returns {this} */
|
|
89
|
+
FileImagePreviewMode.prototype.actionButtons = function() {
|
|
90
|
+
this.$description.variant = 'action-buttons';
|
|
91
|
+
return this;
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
/** @returns {this} */
|
|
95
|
+
FileImagePreviewMode.prototype.circle = function() {
|
|
96
|
+
this.$description.shape = 'circle';
|
|
97
|
+
return this;
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
/** @returns {this} */
|
|
101
|
+
FileImagePreviewMode.prototype.square = function() {
|
|
102
|
+
this.$description.shape = 'square';
|
|
103
|
+
return this;
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* @param {number|string} size - Size in pixels or CSS value
|
|
108
|
+
* @returns {this}
|
|
109
|
+
*/
|
|
110
|
+
FileImagePreviewMode.prototype.size = function(size) {
|
|
111
|
+
this.$description.size = size;
|
|
112
|
+
return this;
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* @param {NdChild} icon
|
|
117
|
+
* @returns {this}
|
|
118
|
+
*/
|
|
119
|
+
FileImagePreviewMode.prototype.placeholderIcon = function(icon) {
|
|
120
|
+
this.$description.placeholderIcon = icon;
|
|
121
|
+
return this;
|
|
122
|
+
};
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* @param {NdChild} icon
|
|
126
|
+
* @returns {this}
|
|
127
|
+
*/
|
|
128
|
+
FileImagePreviewMode.prototype.overlayIcon = function(icon) {
|
|
129
|
+
this.$description.overlayIcon = icon;
|
|
130
|
+
return this;
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* @param {NdChild} icon
|
|
135
|
+
* @returns {this}
|
|
136
|
+
*/
|
|
137
|
+
FileImagePreviewMode.prototype.editImageIcon = function(icon) {
|
|
138
|
+
this.$description.editImageIcon = icon;
|
|
139
|
+
return this;
|
|
140
|
+
};
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* @param {NdChild} label
|
|
144
|
+
* @returns {this}
|
|
145
|
+
*/
|
|
146
|
+
FileImagePreviewMode.prototype.changeLabel = function(label) {
|
|
147
|
+
this.$description.changeLabel = label;
|
|
148
|
+
return this;
|
|
149
|
+
};
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* @param {NdChild} label
|
|
153
|
+
* @returns {this}
|
|
154
|
+
*/
|
|
155
|
+
FileImagePreviewMode.prototype.removeLabel = function(label) {
|
|
156
|
+
this.$description.removeLabel = label;
|
|
157
|
+
return this;
|
|
158
|
+
};
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* @param {(desc: *, instance: FileImagePreviewMode) => NdChild} fn
|
|
162
|
+
* @returns {this}
|
|
163
|
+
*/
|
|
164
|
+
FileImagePreviewMode.prototype.renderAvatar = function(fn) {
|
|
165
|
+
this.$description.renderAvatar = fn;
|
|
166
|
+
return this;
|
|
167
|
+
};
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* @param {(desc: *, instance: FileImagePreviewMode) => NdChild} fn
|
|
171
|
+
* @returns {this}
|
|
172
|
+
*/
|
|
173
|
+
FileImagePreviewMode.prototype.renderOverlay = function(fn) {
|
|
174
|
+
this.$description.renderOverlay = fn;
|
|
175
|
+
return this;
|
|
176
|
+
};
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* @param {(desc: *, instance: FileImagePreviewMode) => NdChild} fn
|
|
180
|
+
* @returns {this}
|
|
181
|
+
*/
|
|
182
|
+
FileImagePreviewMode.prototype.renderActions = function(fn) {
|
|
183
|
+
this.$description.renderActions = fn;
|
|
184
|
+
return this;
|
|
185
|
+
};
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
* Configures the component specifically for avatar preset (circular default).
|
|
189
|
+
* @returns {this}
|
|
190
|
+
*/
|
|
191
|
+
FileImagePreviewMode.prototype.asAvatar = function() {
|
|
192
|
+
this.$description.mode = 'avatar';
|
|
193
|
+
return this;
|
|
194
|
+
};
|
|
195
|
+
/**
|
|
196
|
+
* Configures the component specifically for banner/cover preset (square default).
|
|
197
|
+
* @returns {this}
|
|
198
|
+
*/
|
|
199
|
+
FileImagePreviewMode.prototype.asCover = function() {
|
|
200
|
+
this.$description.mode = 'cover';
|
|
201
|
+
this.square();
|
|
202
|
+
return this;
|
|
203
|
+
};
|
|
204
|
+
|
|
205
|
+
/**
|
|
206
|
+
* Sets CSS aspect-ratio or ratio string (e.g. '16/9', '3/1', 1.77)
|
|
207
|
+
* @param {string|number} ratio
|
|
208
|
+
* @returns {this}
|
|
209
|
+
*/
|
|
210
|
+
FileImagePreviewMode.prototype.ratio = function(ratio) {
|
|
211
|
+
this.$description.ratio = ratio;
|
|
212
|
+
return this;
|
|
213
|
+
};
|
|
214
|
+
|
|
215
|
+
/**
|
|
216
|
+
* Sets aspect ratio to 1:1 (Square).
|
|
217
|
+
* Ideal for avatars, product thumbnails, or Instagram feed items.
|
|
218
|
+
* @returns {this}
|
|
219
|
+
*/
|
|
220
|
+
FileImagePreviewMode.prototype.squareRatio = function() {
|
|
221
|
+
this.$description.ratio = '1/1';
|
|
222
|
+
return this;
|
|
223
|
+
};
|
|
224
|
+
|
|
225
|
+
/**
|
|
226
|
+
* Sets aspect ratio to 16:9 (Widescreen).
|
|
227
|
+
* Ideal for video previews, event covers, and standard web banners.
|
|
228
|
+
* @returns {this}
|
|
229
|
+
*/
|
|
230
|
+
FileImagePreviewMode.prototype.widescreenRatio = function() {
|
|
231
|
+
this.$description.ratio = '16/9';
|
|
232
|
+
return this;
|
|
233
|
+
};
|
|
234
|
+
|
|
235
|
+
/**
|
|
236
|
+
* Sets aspect ratio to an ultra-wide banner format (defaults to 3:1).
|
|
237
|
+
* Ideal for profile headers (e.g., Twitter, LinkedIn) or hero banners.
|
|
238
|
+
* @param {string} [customRatio='3/1'] - Custom ratio string if different from '3/1' (e.g., '4/1')
|
|
239
|
+
* @returns {this}
|
|
240
|
+
*/
|
|
241
|
+
FileImagePreviewMode.prototype.bannerRatio = function(customRatio = '3/1') {
|
|
242
|
+
this.$description.ratio = customRatio;
|
|
243
|
+
return this;
|
|
244
|
+
};
|
|
245
|
+
|
|
246
|
+
/**
|
|
247
|
+
* Sets aspect ratio to 4:3 (Landscape).
|
|
248
|
+
* Ideal for traditional photography, blog thumbnails, or product cards.
|
|
249
|
+
* @returns {this}
|
|
250
|
+
*/
|
|
251
|
+
FileImagePreviewMode.prototype.landscapeRatio = function() {
|
|
252
|
+
this.$description.ratio = '4/3';
|
|
253
|
+
return this;
|
|
254
|
+
};
|
|
255
|
+
|
|
256
|
+
/**
|
|
257
|
+
* Sets aspect ratio to 9:16 (Story / Full Mobile).
|
|
258
|
+
* Ideal for mobile-first vertical media (Instagram Stories, Reels, TikTok).
|
|
259
|
+
* @returns {this}
|
|
260
|
+
*/
|
|
261
|
+
FileImagePreviewMode.prototype.storyRatio = function() {
|
|
262
|
+
this.$description.ratio = '9/16';
|
|
263
|
+
return this;
|
|
264
|
+
};
|
|
265
|
+
|
|
266
|
+
/**
|
|
267
|
+
* Sets aspect ratio to 4:5 (Social Portrait).
|
|
268
|
+
* Ideal for Instagram or Facebook feed posts optimized for mobile screen height.
|
|
269
|
+
* @returns {this}
|
|
270
|
+
*/
|
|
271
|
+
FileImagePreviewMode.prototype.portraitRatio = function() {
|
|
272
|
+
this.$description.ratio = '4/5';
|
|
273
|
+
return this;
|
|
274
|
+
};
|
|
@@ -24,7 +24,7 @@ import FileNativeMode from './field/types/file-field-mode/FileNativeMode';
|
|
|
24
24
|
import FileDropzoneMode from './field/types/file-field-mode/FileDropzoneMode';
|
|
25
25
|
import FileUploadButtonMode from './field/types/file-field-mode/FileUploadButtonMode';
|
|
26
26
|
import FileWallMode from './field/types/file-field-mode/FileWallMode';
|
|
27
|
-
import
|
|
27
|
+
import FileImagePreviewMode from './field/types/file-field-mode/FileImagePreviewMode';
|
|
28
28
|
|
|
29
29
|
// import './field/FieldFactory';
|
|
30
30
|
|
|
@@ -53,7 +53,7 @@ export {
|
|
|
53
53
|
FieldCollection,
|
|
54
54
|
|
|
55
55
|
FileNativeMode,
|
|
56
|
-
|
|
56
|
+
FileImagePreviewMode,
|
|
57
57
|
FileWallMode,
|
|
58
58
|
FileUploadButtonMode,
|
|
59
59
|
FileDropzoneMode,
|
|
@@ -31,10 +31,10 @@ export interface FileFieldInterface extends Omit<FieldInterface, 'render' | 'res
|
|
|
31
31
|
extensions(extensions: string[], message?: string): this;
|
|
32
32
|
maxFiles(max: number, message?: string): this;
|
|
33
33
|
minFiles(min: number, message?: string): this;
|
|
34
|
-
|
|
34
|
+
onAddFile(handler: (file: File) => void): this;
|
|
35
35
|
onReset(handler: () => void): this;
|
|
36
|
-
|
|
37
|
-
addFile(file:
|
|
36
|
+
onRemoveFile(handler: (file: File) => void): this;
|
|
37
|
+
addFile(file: FileItemPreviewInterface, update?: boolean): this;
|
|
38
38
|
setFiles(files: File[]): this;
|
|
39
39
|
addFiles(files: File[]): this;
|
|
40
40
|
removeFile(file: File): this;
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import type { ValidChild } from '../../../../../types/elements';
|
|
2
|
+
import type { GlobalAttributes } from '../../../../../types/globals';
|
|
3
|
+
import type { BaseComponent } from '../../../BaseComponent';
|
|
4
|
+
|
|
5
|
+
export type FileImagePreviewModeDescription = {
|
|
6
|
+
variant: 'hover-overlay' | 'corner-badge' | 'action-buttons';
|
|
7
|
+
shape: 'circle' | 'square';
|
|
8
|
+
size: number | string;
|
|
9
|
+
placeholderIcon: ValidChild | null;
|
|
10
|
+
overlayIcon: ValidChild | null;
|
|
11
|
+
editImageIcon: ValidChild | null;
|
|
12
|
+
changeLabel: ValidChild;
|
|
13
|
+
removeLabel: ValidChild;
|
|
14
|
+
renderAvatar: ((desc: unknown, instance: FileImagePreviewModeInterface) => ValidChild) | null;
|
|
15
|
+
renderOverlay: ((desc: unknown, instance: FileImagePreviewModeInterface) => ValidChild) | null;
|
|
16
|
+
renderActions: ((desc: unknown, instance: FileImagePreviewModeInterface) => ValidChild) | null;
|
|
17
|
+
props: GlobalAttributes;
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
export interface FileImagePreviewModeInterface extends BaseComponent {
|
|
21
|
+
render(renderFn: (description: FileImagePreviewModeDescription, instance: FileImagePreviewModeInterface) => ValidChild): this;
|
|
22
|
+
hoverOverlay(): this;
|
|
23
|
+
cornerBadge(): this;
|
|
24
|
+
actionButtons(): this;
|
|
25
|
+
circle(): this;
|
|
26
|
+
square(): this;
|
|
27
|
+
asAvatar(): this;
|
|
28
|
+
asCover(): this;
|
|
29
|
+
ratio(ratio: string): this;
|
|
30
|
+
|
|
31
|
+
ratio(ratio: string | number): this;
|
|
32
|
+
|
|
33
|
+
squareRatio(): this;
|
|
34
|
+
widescreenRatio(): this;
|
|
35
|
+
bannerRatio(customRatio?: string): this;
|
|
36
|
+
landscapeRatio(): this;
|
|
37
|
+
storyRatio(): this;
|
|
38
|
+
portraitRatio(): this;
|
|
39
|
+
|
|
40
|
+
size(size: number | string): this;
|
|
41
|
+
placeholderIcon(icon: ValidChild): this;
|
|
42
|
+
overlayIcon(icon: ValidChild): this;
|
|
43
|
+
editImageIcon(icon: ValidChild): this;
|
|
44
|
+
changeLabel(label: ValidChild): this;
|
|
45
|
+
removeLabel(label: ValidChild): this;
|
|
46
|
+
renderAvatar(fn: (desc: unknown, instance: FileImagePreviewModeInterface) => ValidChild): this;
|
|
47
|
+
renderOverlay(fn: (desc: unknown, instance: FileImagePreviewModeInterface) => ValidChild): this;
|
|
48
|
+
renderActions(fn: (desc: unknown, instance: FileImagePreviewModeInterface) => ValidChild): this;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
export declare function FileImagePreviewMode(props?: GlobalAttributes): FileImagePreviewModeInterface;
|
|
53
|
+
export declare namespace FileImagePreviewMode {
|
|
54
|
+
|
|
55
|
+
function use(template: (description: FileImagePreviewModeDescription, instance: FileImagePreviewModeInterface) => ValidChild): void;
|
|
56
|
+
|
|
57
|
+
}
|
|
@@ -78,7 +78,7 @@ export type { RangeField, RangeFieldInterface } from './form/types/fields/RangeF
|
|
|
78
78
|
export type { SearchField, SearchFieldInterface } from './form/types/fields/SearchField';
|
|
79
79
|
export type { FileField, FileFieldInterface } from './form/types/fields/FileField';
|
|
80
80
|
export type { ImageField, ImageFieldInterface } from './form/types/fields/ImageField';
|
|
81
|
-
export type {
|
|
81
|
+
export type { FileImagePreviewMode, FileImagePreviewModeInterface, FileImagePreviewModeDescription } from './form/types/fields/FileImagePreviewMode';
|
|
82
82
|
export type { FileDropzoneMode, FileDropzoneModeInterface, FileDropzoneModeDescription } from './form/types/fields/FileDropzoneMode';
|
|
83
83
|
export type { FileItemPreview, FileItemPreviewInterface, FileItemPreviewDescription } from './form/types/fields/FileItemPreview';
|
|
84
84
|
export type { FileNativeMode, FileNativeModeInterface, FileNativeModeDescription } from './form/types/fields/FileNativeMode';
|
|
@@ -64,6 +64,19 @@ ObservableObject.prototype.$addKey = function(key) {
|
|
|
64
64
|
}
|
|
65
65
|
};
|
|
66
66
|
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
*
|
|
70
|
+
* @param {String[]} properties
|
|
71
|
+
*/
|
|
72
|
+
ObservableObject.prototype.only = function(properties) {
|
|
73
|
+
const result = {};
|
|
74
|
+
for(const key in properties) {
|
|
75
|
+
result[key] = this.$observables[key]?.val();
|
|
76
|
+
}
|
|
77
|
+
return result;
|
|
78
|
+
};
|
|
79
|
+
|
|
67
80
|
/**
|
|
68
81
|
* Initialises (or reinitialize) the internal observables map from a plain object.
|
|
69
82
|
* Called automatically in the constructor.
|
package/src/fetch/NativeFetch.js
CHANGED
|
@@ -95,16 +95,19 @@ export default function NativeFetch($baseUrl) {
|
|
|
95
95
|
};
|
|
96
96
|
|
|
97
97
|
export const resolveData = (data) => {
|
|
98
|
-
if(data
|
|
98
|
+
if(data?.__$Observable) {
|
|
99
99
|
return data.resolve();
|
|
100
100
|
}
|
|
101
101
|
for(const key in data) {
|
|
102
102
|
const value = data[key];
|
|
103
|
+
if(value == null) {
|
|
104
|
+
continue;
|
|
105
|
+
}
|
|
103
106
|
if(value.__$Observable) {
|
|
104
107
|
data[key] = value.resolve();
|
|
105
108
|
continue;
|
|
106
109
|
}
|
|
107
|
-
if(typeof
|
|
110
|
+
if(typeof value === 'object') {
|
|
108
111
|
data[key] = resolveData(data[key]);
|
|
109
112
|
}
|
|
110
113
|
}
|
|
@@ -26,9 +26,7 @@ export default function NumberFieldRender($desc, instance) {
|
|
|
26
26
|
|
|
27
27
|
const el = FieldRender($desc, instance);
|
|
28
28
|
|
|
29
|
-
|
|
30
|
-
setupDecimalsHandling(instance, $desc);
|
|
31
|
-
}
|
|
29
|
+
setupDecimalsHandling(instance, $desc);
|
|
32
30
|
|
|
33
31
|
return el;
|
|
34
32
|
}
|
|
@@ -40,9 +38,9 @@ const setupDecimalsHandling = (instance, $desc) => {
|
|
|
40
38
|
}
|
|
41
39
|
|
|
42
40
|
input.nd.onBlur(() => {
|
|
43
|
-
const raw =
|
|
41
|
+
const raw = Number(input.value);
|
|
44
42
|
if(!isNaN(raw)) {
|
|
45
|
-
const formatted = raw.toFixed($desc.decimals);
|
|
43
|
+
const formatted = $desc.decimals ? raw.toFixed($desc.decimals) : raw;
|
|
46
44
|
input.value = formatted;
|
|
47
45
|
if($desc.value?.__$isObservable) {
|
|
48
46
|
$desc.value.set(parseFloat(formatted));
|