@mmlogic/components 0.1.8 → 0.1.10
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/dist/cjs/loader.cjs.js +1 -1
- package/dist/cjs/mosterdcomponents.cjs.js +1 -1
- package/dist/cjs/mrd-boolean-field_16.cjs.entry.js +167 -17
- package/dist/collection/components/mrd-field/mrd-field.js +26 -2
- package/dist/collection/components/mrd-file-field/mrd-file-field.js +70 -2
- package/dist/collection/components/mrd-file-field/mrd-file-field.scss +13 -0
- package/dist/collection/components/mrd-form/mrd-form.js +187 -8
- package/dist/collection/components/mrd-form/mrd-form.scss +32 -0
- package/dist/collection/components/mrd-image-field/mrd-image-field.js +71 -2
- package/dist/collection/components/mrd-image-field/mrd-image-field.scss +26 -2
- package/dist/components/mrd-field2.js +1 -1
- package/dist/components/mrd-file-field2.js +1 -1
- package/dist/components/mrd-form.js +1 -1
- package/dist/components/mrd-image-field2.js +1 -1
- package/dist/esm/loader.js +1 -1
- package/dist/esm/mosterdcomponents.js +1 -1
- package/dist/esm/mrd-boolean-field_16.entry.js +167 -17
- package/dist/mosterdcomponents/mosterdcomponents.esm.js +1 -1
- package/dist/mosterdcomponents/p-bb567c32.entry.js +1 -0
- package/dist/types/components/mrd-field/mrd-field.d.ts +5 -0
- package/dist/types/components/mrd-file-field/mrd-file-field.d.ts +10 -0
- package/dist/types/components/mrd-form/mrd-form.d.ts +40 -0
- package/dist/types/components/mrd-image-field/mrd-image-field.d.ts +10 -0
- package/dist/types/components.d.ts +65 -0
- package/package.json +1 -1
- package/dist/mosterdcomponents/p-5a453e03.entry.js +0 -1
|
@@ -4,6 +4,7 @@ export class MrdFileField {
|
|
|
4
4
|
constructor() {
|
|
5
5
|
this.name = '';
|
|
6
6
|
this.label = '';
|
|
7
|
+
this.value = null;
|
|
7
8
|
this.required = false;
|
|
8
9
|
this.disabled = false;
|
|
9
10
|
this.locale = navigator.language;
|
|
@@ -11,6 +12,7 @@ export class MrdFileField {
|
|
|
11
12
|
this.maxSize = 0; // bytes, 0 = no limit
|
|
12
13
|
this.fileName = '';
|
|
13
14
|
this.isDragging = false;
|
|
15
|
+
this.uploading = false;
|
|
14
16
|
this.error = '';
|
|
15
17
|
this.handleInputChange = (e) => {
|
|
16
18
|
var _a;
|
|
@@ -33,7 +35,7 @@ export class MrdFileField {
|
|
|
33
35
|
};
|
|
34
36
|
this.handleZoneClick = () => {
|
|
35
37
|
var _a;
|
|
36
|
-
if (!this.disabled) {
|
|
38
|
+
if (!this.disabled && !this.uploading) {
|
|
37
39
|
(_a = this.fileInputRef) === null || _a === void 0 ? void 0 : _a.click();
|
|
38
40
|
}
|
|
39
41
|
};
|
|
@@ -41,14 +43,26 @@ export class MrdFileField {
|
|
|
41
43
|
e.stopPropagation();
|
|
42
44
|
this.fileName = '';
|
|
43
45
|
this.error = '';
|
|
46
|
+
this.uploading = false;
|
|
44
47
|
if (this.fileInputRef)
|
|
45
48
|
this.fileInputRef.value = '';
|
|
46
49
|
this.mrdChange.emit({ name: this.name, value: null });
|
|
47
50
|
};
|
|
48
51
|
}
|
|
52
|
+
/** When the host provides a URI back via setFieldValue, the upload is done. */
|
|
53
|
+
valueChanged(newVal) {
|
|
54
|
+
if (typeof newVal === 'string' && newVal) {
|
|
55
|
+
this.uploading = false;
|
|
56
|
+
}
|
|
57
|
+
else if (!newVal) {
|
|
58
|
+
this.uploading = false;
|
|
59
|
+
this.fileName = '';
|
|
60
|
+
}
|
|
61
|
+
}
|
|
49
62
|
handleFile(file) {
|
|
50
63
|
if (!file) {
|
|
51
64
|
this.fileName = '';
|
|
65
|
+
this.uploading = false;
|
|
52
66
|
this.mrdChange.emit({ name: this.name, value: null });
|
|
53
67
|
return;
|
|
54
68
|
}
|
|
@@ -58,11 +72,20 @@ export class MrdFileField {
|
|
|
58
72
|
}
|
|
59
73
|
this.error = '';
|
|
60
74
|
this.fileName = file.name;
|
|
75
|
+
this.uploading = true;
|
|
61
76
|
this.mrdChange.emit({ name: this.name, value: file });
|
|
77
|
+
this.mrdUpload.emit({ name: this.name, file });
|
|
62
78
|
}
|
|
63
79
|
render() {
|
|
80
|
+
const hasFile = this.uploading || (typeof this.value === 'string' && this.value) || this.fileName;
|
|
64
81
|
const hasError = !!this.error;
|
|
65
|
-
|
|
82
|
+
const zoneClass = [
|
|
83
|
+
'mrd-file-field__zone',
|
|
84
|
+
this.isDragging ? 'mrd-file-field__zone--dragging' : '',
|
|
85
|
+
hasError ? 'mrd-file-field__zone--error' : '',
|
|
86
|
+
this.disabled || this.uploading ? 'mrd-file-field__zone--disabled' : '',
|
|
87
|
+
].filter(Boolean).join(' ');
|
|
88
|
+
return (h(Host, { key: '2090102df8169226c1e66f87cbc837296e4b55d4' }, h("div", { key: '7a37a09df1bfa2eaf28ed04c806ad5f7c0337bb0', class: "mrd-file-field" }, this.label && (h("label", { key: '8f1fcb8adbb66b792be902c7c548e50db99b6a53', class: `mrd-file-field__label${this.required ? ' mrd-file-field__label--required' : ''}` }, this.label)), h("div", { key: '66d992298f537b24d69ce3ead3dd229f6d124f3d', class: zoneClass, onClick: this.handleZoneClick, onDragOver: this.handleDragOver, onDragLeave: this.handleDragLeave, onDrop: this.handleDrop }, h("input", { key: '95ba6560e71ee08c6b2526802727b29a18cdc454', ref: el => (this.fileInputRef = el), class: "mrd-file-field__input", type: "file", name: this.name, accept: this.accept, disabled: this.disabled || this.uploading, required: this.required && !hasFile, onChange: this.handleInputChange }), hasFile ? (h("div", { class: "mrd-file-field__selected" }, this.uploading ? (h("span", { class: "mrd-file-field__spinner", "aria-label": t('loading', this.locale) })) : (h("svg", { class: "mrd-file-field__icon", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", "stroke-width": "2" }, h("path", { d: "M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z" }), h("polyline", { points: "14 2 14 8 20 8" }))), h("span", { class: "mrd-file-field__filename" }, this.fileName), !this.uploading && (h("button", { class: "mrd-file-field__clear", type: "button", onClick: this.handleClear, "aria-label": t('clear', this.locale) }, "\u2715")))) : (h("div", { class: "mrd-file-field__prompt" }, h("svg", { class: "mrd-file-field__upload-icon", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", "stroke-width": "2" }, h("polyline", { points: "16 16 12 12 8 16" }), h("line", { x1: "12", y1: "12", x2: "12", y2: "21" }), h("path", { d: "M20.39 18.39A5 5 0 0 0 18 9h-1.26A8 8 0 1 0 3 16.3" })), h("span", null, t('drop_file_here', this.locale), ' ', h("span", { class: "mrd-file-field__browse" }, t('browse', this.locale)))))), hasError && h("span", { key: 'a6020e63bebb01e31a980a903f81badd486585aa', class: "mrd-file-field__error" }, this.error))));
|
|
66
89
|
}
|
|
67
90
|
static get is() { return "mrd-file-field"; }
|
|
68
91
|
static get encapsulation() { return "scoped"; }
|
|
@@ -118,6 +141,24 @@ export class MrdFileField {
|
|
|
118
141
|
"attribute": "label",
|
|
119
142
|
"defaultValue": "''"
|
|
120
143
|
},
|
|
144
|
+
"value": {
|
|
145
|
+
"type": "unknown",
|
|
146
|
+
"mutable": false,
|
|
147
|
+
"complexType": {
|
|
148
|
+
"original": "unknown",
|
|
149
|
+
"resolved": "unknown",
|
|
150
|
+
"references": {}
|
|
151
|
+
},
|
|
152
|
+
"required": false,
|
|
153
|
+
"optional": false,
|
|
154
|
+
"docs": {
|
|
155
|
+
"tags": [],
|
|
156
|
+
"text": ""
|
|
157
|
+
},
|
|
158
|
+
"getter": false,
|
|
159
|
+
"setter": false,
|
|
160
|
+
"defaultValue": "null"
|
|
161
|
+
},
|
|
121
162
|
"required": {
|
|
122
163
|
"type": "boolean",
|
|
123
164
|
"mutable": false,
|
|
@@ -224,6 +265,7 @@ export class MrdFileField {
|
|
|
224
265
|
return {
|
|
225
266
|
"fileName": {},
|
|
226
267
|
"isDragging": {},
|
|
268
|
+
"uploading": {},
|
|
227
269
|
"error": {}
|
|
228
270
|
};
|
|
229
271
|
}
|
|
@@ -268,6 +310,32 @@ export class MrdFileField {
|
|
|
268
310
|
}
|
|
269
311
|
}
|
|
270
312
|
}
|
|
313
|
+
}, {
|
|
314
|
+
"method": "mrdUpload",
|
|
315
|
+
"name": "mrdUpload",
|
|
316
|
+
"bubbles": true,
|
|
317
|
+
"cancelable": true,
|
|
318
|
+
"composed": true,
|
|
319
|
+
"docs": {
|
|
320
|
+
"tags": [],
|
|
321
|
+
"text": "Emitted when a file is selected and needs to be uploaded.\nHost should upload the file and call form.setFieldValue(name, uri) with the result."
|
|
322
|
+
},
|
|
323
|
+
"complexType": {
|
|
324
|
+
"original": "{ name: string; file: File }",
|
|
325
|
+
"resolved": "{ name: string; file: File; }",
|
|
326
|
+
"references": {
|
|
327
|
+
"File": {
|
|
328
|
+
"location": "global",
|
|
329
|
+
"id": "global::File"
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
}];
|
|
334
|
+
}
|
|
335
|
+
static get watchers() {
|
|
336
|
+
return [{
|
|
337
|
+
"propName": "value",
|
|
338
|
+
"methodName": "valueChanged"
|
|
271
339
|
}];
|
|
272
340
|
}
|
|
273
341
|
}
|
|
@@ -133,6 +133,19 @@
|
|
|
133
133
|
background-color: var(--mrd-color-danger-light);
|
|
134
134
|
}
|
|
135
135
|
|
|
136
|
+
.mrd-file-field__spinner {
|
|
137
|
+
display: inline-block;
|
|
138
|
+
flex-shrink: 0;
|
|
139
|
+
width: 18px;
|
|
140
|
+
height: 18px;
|
|
141
|
+
border: 2px solid var(--mrd-color-neutral-300);
|
|
142
|
+
border-top-color: var(--mrd-color-primary);
|
|
143
|
+
border-radius: 50%;
|
|
144
|
+
animation: mrd-file-spin 0.6s linear infinite;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
@keyframes mrd-file-spin { to { transform: rotate(360deg); } }
|
|
148
|
+
|
|
136
149
|
.mrd-file-field__error {
|
|
137
150
|
font-family: var(--mrd-font-family);
|
|
138
151
|
font-size: var(--mrd-error-font-size);
|
|
@@ -6,30 +6,56 @@ export class MrdForm {
|
|
|
6
6
|
constructor() {
|
|
7
7
|
this.locale = navigator.language;
|
|
8
8
|
this.values = {};
|
|
9
|
+
/**
|
|
10
|
+
* Absolute href of the parent/reference object (e.g. the clientAgreement href
|
|
11
|
+
* when creating an invoice from within a client agreement).
|
|
12
|
+
* Combined with `referenceClass`, mrd-form will automatically pre-fill the
|
|
13
|
+
* matching relation field so dependent DROPDOWN fields can be fetched on load
|
|
14
|
+
* — without the host app needing to know anything about the form layout.
|
|
15
|
+
*/
|
|
16
|
+
/**
|
|
17
|
+
* Absolute href of the parent/reference object (e.g. the clientAgreement href
|
|
18
|
+
* when creating an invoice from within a client agreement).
|
|
19
|
+
* Combined with `referenceClass`, mrd-form will automatically pre-fill the
|
|
20
|
+
* matching relation field so dependent DROPDOWN fields can be fetched on load.
|
|
21
|
+
*/
|
|
22
|
+
this.referenceHref = '';
|
|
23
|
+
/**
|
|
24
|
+
* The `mostSignificantClass` of the parent/reference object
|
|
25
|
+
* (e.g. `'clientAgreements'`). Used to locate the matching RELATION field.
|
|
26
|
+
*/
|
|
27
|
+
this.referenceClass = '';
|
|
28
|
+
/** When true, a cancel button is shown next to the submit button. */
|
|
29
|
+
this.showCancel = false;
|
|
9
30
|
this.formValues = {};
|
|
10
31
|
this.errors = {};
|
|
11
32
|
this.submitted = false;
|
|
12
33
|
this.handleFieldChange = (e) => {
|
|
13
34
|
const { name, value } = e.detail;
|
|
35
|
+
const prevHref = this.getHref(this.formValues[name]);
|
|
14
36
|
this.formValues = Object.assign(Object.assign({}, this.formValues), { [name]: value });
|
|
15
37
|
if (this.errors[name]) {
|
|
16
38
|
this.errors = Object.assign(Object.assign({}, this.errors), { [name]: '' });
|
|
17
39
|
}
|
|
18
40
|
// When a field changes, check if it is the commonRelation dependency for any
|
|
19
41
|
// DROPDOWN relation. If so, reset the dependent field and re-fetch its options.
|
|
42
|
+
// Skip when the effective href did not change (e.g. mrdBlur fires after mrdChange
|
|
43
|
+
// with the same value, which would otherwise trigger a duplicate fetch).
|
|
44
|
+
const newHref = this.getHref(value);
|
|
45
|
+
if (newHref === prevHref)
|
|
46
|
+
return;
|
|
20
47
|
for (const rel of this.collectDependentDropdowns()) {
|
|
21
48
|
if (rel.commonRelation !== name)
|
|
22
49
|
continue;
|
|
23
50
|
// Clear the dependent field's current selection (options have changed)
|
|
24
51
|
this.formValues = Object.assign(Object.assign({}, this.formValues), { [rel.name]: null });
|
|
25
|
-
const filterValue = this.getHref(value);
|
|
26
52
|
this.mrdFetchAll.emit({
|
|
27
53
|
name: rel.name,
|
|
28
54
|
relatedClass: rel.relatedClass,
|
|
29
55
|
mostSignificantClass: rel.mostSignificantClass,
|
|
30
56
|
commonRelation: rel.commonRelation,
|
|
31
57
|
filter: rel.commonRelation,
|
|
32
|
-
filterValue, // empty string when dependency was cleared → host should clear the list
|
|
58
|
+
filterValue: newHref, // empty string when dependency was cleared → host should clear the list
|
|
33
59
|
});
|
|
34
60
|
}
|
|
35
61
|
};
|
|
@@ -41,6 +67,10 @@ export class MrdForm {
|
|
|
41
67
|
e.stopPropagation();
|
|
42
68
|
this.mrdFetchAll.emit(e.detail);
|
|
43
69
|
};
|
|
70
|
+
this.handleUpload = (e) => {
|
|
71
|
+
e.stopPropagation();
|
|
72
|
+
this.mrdUpload.emit(e.detail);
|
|
73
|
+
};
|
|
44
74
|
this.handleSubmit = (e) => {
|
|
45
75
|
e.preventDefault();
|
|
46
76
|
this.submitted = true;
|
|
@@ -54,20 +84,71 @@ export class MrdForm {
|
|
|
54
84
|
this.formValues = Object.assign({}, ((_a = this.values) !== null && _a !== void 0 ? _a : {}));
|
|
55
85
|
}
|
|
56
86
|
componentDidLoad() {
|
|
57
|
-
//
|
|
58
|
-
//
|
|
59
|
-
|
|
60
|
-
|
|
87
|
+
// Apply reference pre-fill and emit mrdFetchAll for dependent DROPDOWN fields.
|
|
88
|
+
// Deferred so Angular/host prop bindings are settled before we read them.
|
|
89
|
+
setTimeout(() => {
|
|
90
|
+
this.applyReferenceValue();
|
|
91
|
+
this.emitDependentFetchAll();
|
|
92
|
+
}, 0);
|
|
61
93
|
}
|
|
62
94
|
/** Sync formValues when the values prop is set from outside after mount
|
|
63
95
|
* (e.g. when pre-filling an existing record in edit mode). */
|
|
64
96
|
valuesChanged(newValues) {
|
|
65
97
|
this.formValues = Object.assign({}, (newValues !== null && newValues !== void 0 ? newValues : {}));
|
|
98
|
+
this.applyReferenceValue();
|
|
66
99
|
this.errors = {};
|
|
67
100
|
this.submitted = false;
|
|
68
101
|
// Re-check DROPDOWN dependencies now that formValues are updated
|
|
69
102
|
setTimeout(() => this.emitDependentFetchAll(), 0);
|
|
70
103
|
}
|
|
104
|
+
/**
|
|
105
|
+
* When referenceHref + referenceClass are set, find the matching layout field
|
|
106
|
+
* and inject its value into formValues. This allows dependent DROPDOWN fields
|
|
107
|
+
* (those with commonRelation pointing to that field) to be fetched on load
|
|
108
|
+
* without the host app doing any form-domain logic.
|
|
109
|
+
*
|
|
110
|
+
* Two lookup strategies:
|
|
111
|
+
* 1. Find a RELATION whose mostSignificantClass matches referenceClass.
|
|
112
|
+
* 2. Fallback: find a DROPDOWN whose commonRelation field is absent from the
|
|
113
|
+
* layout (API omitted it because it is implied by the reference context).
|
|
114
|
+
*/
|
|
115
|
+
applyReferenceValue() {
|
|
116
|
+
if (!this.referenceHref || !this.referenceClass)
|
|
117
|
+
return;
|
|
118
|
+
const fieldName = this.resolveReferenceFieldName();
|
|
119
|
+
if (!fieldName)
|
|
120
|
+
return;
|
|
121
|
+
// Only set when not already present (don't overwrite an explicit value)
|
|
122
|
+
if (!this.formValues[fieldName]) {
|
|
123
|
+
this.formValues = Object.assign(Object.assign({}, this.formValues), { [fieldName]: this.referenceHref });
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
resolveReferenceFieldName() {
|
|
127
|
+
var _a, _b;
|
|
128
|
+
const allItems = this.collectFields((_b = (_a = this.layout) === null || _a === void 0 ? void 0 : _a.items) !== null && _b !== void 0 ? _b : []);
|
|
129
|
+
// Strategy 1: direct match on mostSignificantClass
|
|
130
|
+
const direct = allItems.find(item => {
|
|
131
|
+
var _a;
|
|
132
|
+
return item.type === ClientLayoutItemType.RELATION &&
|
|
133
|
+
((_a = item.relation) === null || _a === void 0 ? void 0 : _a.mostSignificantClass) === this.referenceClass;
|
|
134
|
+
});
|
|
135
|
+
if (direct === null || direct === void 0 ? void 0 : direct.relation)
|
|
136
|
+
return direct.relation.name;
|
|
137
|
+
// Strategy 2: a DROPDOWN whose commonRelation field was omitted from the layout
|
|
138
|
+
const layoutRelationNames = new Set(allItems
|
|
139
|
+
.filter(item => item.type === ClientLayoutItemType.RELATION)
|
|
140
|
+
.map(item => item.relation.name));
|
|
141
|
+
for (const item of allItems) {
|
|
142
|
+
const rel = item.relation;
|
|
143
|
+
if (item.type === ClientLayoutItemType.RELATION &&
|
|
144
|
+
(rel === null || rel === void 0 ? void 0 : rel.editBehavior) === ClientLayoutItemRelationEditBehavior.DROPDOWN &&
|
|
145
|
+
rel.commonRelation &&
|
|
146
|
+
!layoutRelationNames.has(rel.commonRelation)) {
|
|
147
|
+
return rel.commonRelation;
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
return null;
|
|
151
|
+
}
|
|
71
152
|
async setFieldValue(name, value) {
|
|
72
153
|
this.formValues = Object.assign(Object.assign({}, this.formValues), { [name]: value });
|
|
73
154
|
if (this.errors[name]) {
|
|
@@ -153,6 +234,9 @@ export class MrdForm {
|
|
|
153
234
|
if (item.type === ClientLayoutItemType.FIELD && item.field) {
|
|
154
235
|
const name = item.field.name;
|
|
155
236
|
const value = this.formValues[name];
|
|
237
|
+
// Skip file/image fields that are still uploading (value is a File object)
|
|
238
|
+
if (value instanceof File)
|
|
239
|
+
continue;
|
|
156
240
|
// Omit empty strings for optional fields
|
|
157
241
|
if (value !== '')
|
|
158
242
|
payload[name] = value !== null && value !== void 0 ? value : null;
|
|
@@ -182,7 +266,7 @@ export class MrdForm {
|
|
|
182
266
|
}
|
|
183
267
|
const fieldName = (_d = (_b = (_a = item.field) === null || _a === void 0 ? void 0 : _a.name) !== null && _b !== void 0 ? _b : (_c = item.relation) === null || _c === void 0 ? void 0 : _c.name) !== null && _d !== void 0 ? _d : '';
|
|
184
268
|
const fieldValue = this.formValues[fieldName];
|
|
185
|
-
return (h("div", { class: "mrd-form__field" }, h("mrd-field", { item: item, locale: this.locale, value: fieldValue, onMrdChange: this.handleFieldChange, onMrdBlur: this.handleFieldChange, onMrdSearch: this.handleSearch, onMrdFetchAll: this.handleFetchAll }), this.errors[fieldName] && (h("span", { class: "mrd-form__field-error" }, this.errors[fieldName]))));
|
|
269
|
+
return (h("div", { class: "mrd-form__field" }, h("mrd-field", { item: item, locale: this.locale, value: fieldValue, onMrdChange: this.handleFieldChange, onMrdBlur: this.handleFieldChange, onMrdSearch: this.handleSearch, onMrdFetchAll: this.handleFetchAll, onMrdUpload: this.handleUpload }), this.errors[fieldName] && (h("span", { class: "mrd-form__field-error" }, this.errors[fieldName]))));
|
|
186
270
|
});
|
|
187
271
|
}
|
|
188
272
|
render() {
|
|
@@ -190,7 +274,7 @@ export class MrdForm {
|
|
|
190
274
|
return h(Host, null);
|
|
191
275
|
}
|
|
192
276
|
const dir = this.locale.startsWith('ar') ? 'rtl' : 'ltr';
|
|
193
|
-
return (h(Host, null, h("form", { class: "mrd-form", dir: dir, onSubmit: this.handleSubmit, noValidate: true }, this.layout.title && h("h2", { class: "mrd-form__title" }, this.layout.title), h("div", { class: "mrd-form__body" }, this.renderItems(this.layout.items)), h("div", { class: "mrd-form__footer" }, h("button", { type: "submit", class: "mrd-form__submit" }, t('submit', this.locale))))));
|
|
277
|
+
return (h(Host, null, h("form", { class: "mrd-form", dir: dir, onSubmit: this.handleSubmit, noValidate: true }, this.layout.title && h("h2", { class: "mrd-form__title" }, this.layout.title), h("div", { class: "mrd-form__body" }, this.renderItems(this.layout.items)), h("div", { class: "mrd-form__footer" }, h("button", { type: "submit", class: "mrd-form__submit" }, t('submit', this.locale)), this.showCancel && (h("button", { type: "button", class: "mrd-form__cancel", onClick: () => this.mrdCancel.emit() }, t('cancel', this.locale)))))));
|
|
194
278
|
}
|
|
195
279
|
static get is() { return "mrd-form"; }
|
|
196
280
|
static get encapsulation() { return "scoped"; }
|
|
@@ -272,6 +356,66 @@ export class MrdForm {
|
|
|
272
356
|
"getter": false,
|
|
273
357
|
"setter": false,
|
|
274
358
|
"defaultValue": "{}"
|
|
359
|
+
},
|
|
360
|
+
"referenceHref": {
|
|
361
|
+
"type": "string",
|
|
362
|
+
"mutable": false,
|
|
363
|
+
"complexType": {
|
|
364
|
+
"original": "string",
|
|
365
|
+
"resolved": "string",
|
|
366
|
+
"references": {}
|
|
367
|
+
},
|
|
368
|
+
"required": false,
|
|
369
|
+
"optional": false,
|
|
370
|
+
"docs": {
|
|
371
|
+
"tags": [],
|
|
372
|
+
"text": "Absolute href of the parent/reference object (e.g. the clientAgreement href\nwhen creating an invoice from within a client agreement).\nCombined with `referenceClass`, mrd-form will automatically pre-fill the\nmatching relation field so dependent DROPDOWN fields can be fetched on load."
|
|
373
|
+
},
|
|
374
|
+
"getter": false,
|
|
375
|
+
"setter": false,
|
|
376
|
+
"reflect": false,
|
|
377
|
+
"attribute": "reference-href",
|
|
378
|
+
"defaultValue": "''"
|
|
379
|
+
},
|
|
380
|
+
"referenceClass": {
|
|
381
|
+
"type": "string",
|
|
382
|
+
"mutable": false,
|
|
383
|
+
"complexType": {
|
|
384
|
+
"original": "string",
|
|
385
|
+
"resolved": "string",
|
|
386
|
+
"references": {}
|
|
387
|
+
},
|
|
388
|
+
"required": false,
|
|
389
|
+
"optional": false,
|
|
390
|
+
"docs": {
|
|
391
|
+
"tags": [],
|
|
392
|
+
"text": "The `mostSignificantClass` of the parent/reference object\n(e.g. `'clientAgreements'`). Used to locate the matching RELATION field."
|
|
393
|
+
},
|
|
394
|
+
"getter": false,
|
|
395
|
+
"setter": false,
|
|
396
|
+
"reflect": false,
|
|
397
|
+
"attribute": "reference-class",
|
|
398
|
+
"defaultValue": "''"
|
|
399
|
+
},
|
|
400
|
+
"showCancel": {
|
|
401
|
+
"type": "boolean",
|
|
402
|
+
"mutable": false,
|
|
403
|
+
"complexType": {
|
|
404
|
+
"original": "boolean",
|
|
405
|
+
"resolved": "boolean",
|
|
406
|
+
"references": {}
|
|
407
|
+
},
|
|
408
|
+
"required": false,
|
|
409
|
+
"optional": false,
|
|
410
|
+
"docs": {
|
|
411
|
+
"tags": [],
|
|
412
|
+
"text": "When true, a cancel button is shown next to the submit button."
|
|
413
|
+
},
|
|
414
|
+
"getter": false,
|
|
415
|
+
"setter": false,
|
|
416
|
+
"reflect": false,
|
|
417
|
+
"attribute": "show-cancel",
|
|
418
|
+
"defaultValue": "false"
|
|
275
419
|
}
|
|
276
420
|
};
|
|
277
421
|
}
|
|
@@ -303,6 +447,21 @@ export class MrdForm {
|
|
|
303
447
|
}
|
|
304
448
|
}
|
|
305
449
|
}
|
|
450
|
+
}, {
|
|
451
|
+
"method": "mrdCancel",
|
|
452
|
+
"name": "mrdCancel",
|
|
453
|
+
"bubbles": true,
|
|
454
|
+
"cancelable": true,
|
|
455
|
+
"composed": true,
|
|
456
|
+
"docs": {
|
|
457
|
+
"tags": [],
|
|
458
|
+
"text": ""
|
|
459
|
+
},
|
|
460
|
+
"complexType": {
|
|
461
|
+
"original": "void",
|
|
462
|
+
"resolved": "void",
|
|
463
|
+
"references": {}
|
|
464
|
+
}
|
|
306
465
|
}, {
|
|
307
466
|
"method": "mrdSearch",
|
|
308
467
|
"name": "mrdSearch",
|
|
@@ -333,6 +492,26 @@ export class MrdForm {
|
|
|
333
492
|
"resolved": "{ name: string; relatedClass: string; mostSignificantClass?: string | undefined; commonRelation?: string | undefined; filter?: string | undefined; filterValue?: string | undefined; }",
|
|
334
493
|
"references": {}
|
|
335
494
|
}
|
|
495
|
+
}, {
|
|
496
|
+
"method": "mrdUpload",
|
|
497
|
+
"name": "mrdUpload",
|
|
498
|
+
"bubbles": true,
|
|
499
|
+
"cancelable": true,
|
|
500
|
+
"composed": true,
|
|
501
|
+
"docs": {
|
|
502
|
+
"tags": [],
|
|
503
|
+
"text": ""
|
|
504
|
+
},
|
|
505
|
+
"complexType": {
|
|
506
|
+
"original": "{ name: string; file: File }",
|
|
507
|
+
"resolved": "{ name: string; file: File; }",
|
|
508
|
+
"references": {
|
|
509
|
+
"File": {
|
|
510
|
+
"location": "global",
|
|
511
|
+
"id": "global::File"
|
|
512
|
+
}
|
|
513
|
+
}
|
|
514
|
+
}
|
|
336
515
|
}];
|
|
337
516
|
}
|
|
338
517
|
static get methods() {
|
|
@@ -83,6 +83,7 @@
|
|
|
83
83
|
border-top: var(--mrd-border-width) solid var(--mrd-border-color);
|
|
84
84
|
display: flex;
|
|
85
85
|
justify-content: flex-end;
|
|
86
|
+
gap: var(--mrd-space-3);
|
|
86
87
|
}
|
|
87
88
|
|
|
88
89
|
.mrd-form__submit {
|
|
@@ -114,3 +115,34 @@
|
|
|
114
115
|
.mrd-form__submit:active {
|
|
115
116
|
background-color: var(--mrd-color-primary-dark);
|
|
116
117
|
}
|
|
118
|
+
|
|
119
|
+
.mrd-form__cancel {
|
|
120
|
+
display: inline-flex;
|
|
121
|
+
align-items: center;
|
|
122
|
+
justify-content: center;
|
|
123
|
+
height: var(--mrd-input-height);
|
|
124
|
+
padding: 0 var(--mrd-space-6);
|
|
125
|
+
background-color: transparent;
|
|
126
|
+
color: var(--mrd-color-neutral-600);
|
|
127
|
+
font-family: var(--mrd-font-family);
|
|
128
|
+
font-size: var(--mrd-font-size-base);
|
|
129
|
+
font-weight: var(--mrd-font-weight-medium);
|
|
130
|
+
border: var(--mrd-border-width) solid var(--mrd-border-color);
|
|
131
|
+
border-radius: var(--mrd-border-radius);
|
|
132
|
+
cursor: pointer;
|
|
133
|
+
transition: background-color var(--mrd-transition), color var(--mrd-transition);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
.mrd-form__cancel:hover {
|
|
137
|
+
background-color: var(--mrd-color-neutral-100);
|
|
138
|
+
color: var(--mrd-color-neutral-800);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
.mrd-form__cancel:focus {
|
|
142
|
+
outline: none;
|
|
143
|
+
box-shadow: var(--mrd-shadow-focus);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
.mrd-form__cancel:active {
|
|
147
|
+
background-color: var(--mrd-color-neutral-200);
|
|
148
|
+
}
|
|
@@ -4,6 +4,7 @@ export class MrdImageField {
|
|
|
4
4
|
constructor() {
|
|
5
5
|
this.name = '';
|
|
6
6
|
this.label = '';
|
|
7
|
+
this.value = null;
|
|
7
8
|
this.required = false;
|
|
8
9
|
this.disabled = false;
|
|
9
10
|
this.locale = navigator.language;
|
|
@@ -12,6 +13,7 @@ export class MrdImageField {
|
|
|
12
13
|
this.previewUrl = '';
|
|
13
14
|
this.fileName = '';
|
|
14
15
|
this.isDragging = false;
|
|
16
|
+
this.uploading = false;
|
|
15
17
|
this.error = '';
|
|
16
18
|
this.handleInputChange = (e) => {
|
|
17
19
|
var _a;
|
|
@@ -34,7 +36,7 @@ export class MrdImageField {
|
|
|
34
36
|
};
|
|
35
37
|
this.handleZoneClick = () => {
|
|
36
38
|
var _a;
|
|
37
|
-
if (!this.disabled) {
|
|
39
|
+
if (!this.disabled && !this.uploading) {
|
|
38
40
|
(_a = this.fileInputRef) === null || _a === void 0 ? void 0 : _a.click();
|
|
39
41
|
}
|
|
40
42
|
};
|
|
@@ -43,15 +45,28 @@ export class MrdImageField {
|
|
|
43
45
|
this.previewUrl = '';
|
|
44
46
|
this.fileName = '';
|
|
45
47
|
this.error = '';
|
|
48
|
+
this.uploading = false;
|
|
46
49
|
if (this.fileInputRef)
|
|
47
50
|
this.fileInputRef.value = '';
|
|
48
51
|
this.mrdChange.emit({ name: this.name, value: null });
|
|
49
52
|
};
|
|
50
53
|
}
|
|
54
|
+
/** When the host provides a URI back via setFieldValue, the upload is done. */
|
|
55
|
+
valueChanged(newVal) {
|
|
56
|
+
if (typeof newVal === 'string' && newVal) {
|
|
57
|
+
this.uploading = false;
|
|
58
|
+
}
|
|
59
|
+
else if (!newVal) {
|
|
60
|
+
this.uploading = false;
|
|
61
|
+
this.previewUrl = '';
|
|
62
|
+
this.fileName = '';
|
|
63
|
+
}
|
|
64
|
+
}
|
|
51
65
|
handleFile(file) {
|
|
52
66
|
if (!file) {
|
|
53
67
|
this.previewUrl = '';
|
|
54
68
|
this.fileName = '';
|
|
69
|
+
this.uploading = false;
|
|
55
70
|
this.mrdChange.emit({ name: this.name, value: null });
|
|
56
71
|
return;
|
|
57
72
|
}
|
|
@@ -65,6 +80,8 @@ export class MrdImageField {
|
|
|
65
80
|
}
|
|
66
81
|
this.error = '';
|
|
67
82
|
this.fileName = file.name;
|
|
83
|
+
this.uploading = true;
|
|
84
|
+
// Show local preview immediately while upload is in progress
|
|
68
85
|
const reader = new FileReader();
|
|
69
86
|
reader.onload = (ev) => {
|
|
70
87
|
var _a;
|
|
@@ -72,10 +89,17 @@ export class MrdImageField {
|
|
|
72
89
|
};
|
|
73
90
|
reader.readAsDataURL(file);
|
|
74
91
|
this.mrdChange.emit({ name: this.name, value: file });
|
|
92
|
+
this.mrdUpload.emit({ name: this.name, file });
|
|
75
93
|
}
|
|
76
94
|
render() {
|
|
77
95
|
const hasError = !!this.error;
|
|
78
|
-
|
|
96
|
+
const zoneClass = [
|
|
97
|
+
'mrd-image-field__zone',
|
|
98
|
+
this.isDragging ? 'mrd-image-field__zone--dragging' : '',
|
|
99
|
+
hasError ? 'mrd-image-field__zone--error' : '',
|
|
100
|
+
this.disabled || this.uploading ? 'mrd-image-field__zone--disabled' : '',
|
|
101
|
+
].filter(Boolean).join(' ');
|
|
102
|
+
return (h(Host, { key: '76b5a36a7f5a420ded3400c8a1481843363d1cbe' }, h("div", { key: '3544df84aaf427a25518bafe13d60cf89ed28537', class: "mrd-image-field" }, this.label && (h("label", { key: 'dca64c1600cb98526eb4b91a908106087f099ad1', class: `mrd-image-field__label${this.required ? ' mrd-image-field__label--required' : ''}` }, this.label)), h("div", { key: 'ce07f32126f0956e47ff6fc41ff231590e0503d3', class: zoneClass, onClick: this.handleZoneClick, onDragOver: this.handleDragOver, onDragLeave: this.handleDragLeave, onDrop: this.handleDrop }, h("input", { key: '660fbae5f730834c465522a11dd674fc8e50a54e', ref: el => (this.fileInputRef = el), class: "mrd-image-field__input", type: "file", name: this.name, accept: this.accept, disabled: this.disabled || this.uploading, required: this.required && !this.previewUrl, onChange: this.handleInputChange }), this.previewUrl ? (h("div", { class: "mrd-image-field__preview-container" }, h("div", { class: "mrd-image-field__preview-thumb" }, h("img", { class: "mrd-image-field__preview", src: this.previewUrl, alt: this.fileName }), this.uploading && h("div", { class: "mrd-image-field__preview-overlay" }, h("span", { class: "mrd-image-field__spinner" }))), h("div", { class: "mrd-image-field__preview-info" }, h("span", { class: "mrd-image-field__preview-name" }, this.fileName), this.uploading && (h("span", { class: "mrd-image-field__upload-status" }, t('loading', this.locale)))), !this.uploading && (h("button", { class: "mrd-image-field__clear", type: "button", onClick: this.handleClear, "aria-label": t('clear', this.locale) }, t('remove', this.locale))))) : (h("div", { class: "mrd-image-field__prompt" }, h("svg", { class: "mrd-image-field__upload-icon", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", "stroke-width": "2" }, h("rect", { x: "3", y: "3", width: "18", height: "18", rx: "2", ry: "2" }), h("circle", { cx: "8.5", cy: "8.5", r: "1.5" }), h("polyline", { points: "21 15 16 10 5 21" })), h("span", null, t('drop_file_here', this.locale), ' ', h("span", { class: "mrd-image-field__browse" }, t('browse', this.locale)))))), hasError && h("span", { key: '9b8f9563fea63bd12c38f5c480e9cbd24106c3af', class: "mrd-image-field__error" }, this.error))));
|
|
79
103
|
}
|
|
80
104
|
static get is() { return "mrd-image-field"; }
|
|
81
105
|
static get encapsulation() { return "scoped"; }
|
|
@@ -131,6 +155,24 @@ export class MrdImageField {
|
|
|
131
155
|
"attribute": "label",
|
|
132
156
|
"defaultValue": "''"
|
|
133
157
|
},
|
|
158
|
+
"value": {
|
|
159
|
+
"type": "unknown",
|
|
160
|
+
"mutable": false,
|
|
161
|
+
"complexType": {
|
|
162
|
+
"original": "unknown",
|
|
163
|
+
"resolved": "unknown",
|
|
164
|
+
"references": {}
|
|
165
|
+
},
|
|
166
|
+
"required": false,
|
|
167
|
+
"optional": false,
|
|
168
|
+
"docs": {
|
|
169
|
+
"tags": [],
|
|
170
|
+
"text": ""
|
|
171
|
+
},
|
|
172
|
+
"getter": false,
|
|
173
|
+
"setter": false,
|
|
174
|
+
"defaultValue": "null"
|
|
175
|
+
},
|
|
134
176
|
"required": {
|
|
135
177
|
"type": "boolean",
|
|
136
178
|
"mutable": false,
|
|
@@ -238,6 +280,7 @@ export class MrdImageField {
|
|
|
238
280
|
"previewUrl": {},
|
|
239
281
|
"fileName": {},
|
|
240
282
|
"isDragging": {},
|
|
283
|
+
"uploading": {},
|
|
241
284
|
"error": {}
|
|
242
285
|
};
|
|
243
286
|
}
|
|
@@ -282,6 +325,32 @@ export class MrdImageField {
|
|
|
282
325
|
}
|
|
283
326
|
}
|
|
284
327
|
}
|
|
328
|
+
}, {
|
|
329
|
+
"method": "mrdUpload",
|
|
330
|
+
"name": "mrdUpload",
|
|
331
|
+
"bubbles": true,
|
|
332
|
+
"cancelable": true,
|
|
333
|
+
"composed": true,
|
|
334
|
+
"docs": {
|
|
335
|
+
"tags": [],
|
|
336
|
+
"text": "Emitted when an image is selected and needs to be uploaded.\nHost should upload the file and call form.setFieldValue(name, uri) with the result."
|
|
337
|
+
},
|
|
338
|
+
"complexType": {
|
|
339
|
+
"original": "{ name: string; file: File }",
|
|
340
|
+
"resolved": "{ name: string; file: File; }",
|
|
341
|
+
"references": {
|
|
342
|
+
"File": {
|
|
343
|
+
"location": "global",
|
|
344
|
+
"id": "global::File"
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
}];
|
|
349
|
+
}
|
|
350
|
+
static get watchers() {
|
|
351
|
+
return [{
|
|
352
|
+
"propName": "value",
|
|
353
|
+
"methodName": "valueChanged"
|
|
285
354
|
}];
|
|
286
355
|
}
|
|
287
356
|
}
|