@mmlogic/components 0.5.2 → 0.5.4

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 (117) hide show
  1. package/dist/cjs/{cell-renderer-CmaO0CZT.js → cell-renderer-Coy-aD3k.js} +146 -1
  2. package/dist/cjs/{client-layout-RZ3Ix-4a.js → client-layout-CqVuJumR.js} +9 -0
  3. package/dist/cjs/{index-DzRfoAPz.js → index-DVbdkYO3.js} +468 -6
  4. package/dist/cjs/index.cjs.js +4 -1
  5. package/dist/cjs/loader.cjs.js +2 -2
  6. package/dist/cjs/mosterdcomponents.cjs.js +2 -2
  7. package/dist/cjs/{mrd-boolean-field_23.cjs.entry.js → mrd-boolean-field_25.cjs.entry.js} +5696 -5229
  8. package/dist/cjs/mrd-merge-view.cjs.entry.js +5 -5
  9. package/dist/collection/collection-manifest.json +2 -0
  10. package/dist/collection/components/documents/mrd-document-container/mrd-document-container.css +3 -0
  11. package/dist/collection/components/documents/mrd-document-container/mrd-document-container.js +23 -3
  12. package/dist/collection/components/documents/mrd-document-list/mrd-document-list.js +76 -2
  13. package/dist/collection/components/documents/mrd-document-view/mrd-document-view.js +7 -8
  14. package/dist/collection/components/email/mrd-email-view/mrd-email-view.css +126 -0
  15. package/dist/collection/components/email/mrd-email-view/mrd-email-view.js +247 -0
  16. package/dist/collection/components/fields/file-upload-common.js +23 -7
  17. package/dist/collection/components/fields/mrd-boolean-field/mrd-boolean-field.js +1 -1
  18. package/dist/collection/components/fields/mrd-currency-field/mrd-currency-field.js +1 -1
  19. package/dist/collection/components/fields/mrd-date-field/mrd-date-field.js +1 -1
  20. package/dist/collection/components/fields/mrd-datetime-field/mrd-datetime-field.js +1 -1
  21. package/dist/collection/components/fields/mrd-email-field/mrd-email-field.js +1 -1
  22. package/dist/collection/components/fields/mrd-file-field/mrd-file-field.css +20 -1
  23. package/dist/collection/components/fields/mrd-file-field/mrd-file-field.js +108 -47
  24. package/dist/collection/components/fields/mrd-image-field/mrd-image-field.css +44 -0
  25. package/dist/collection/components/fields/mrd-image-field/mrd-image-field.js +126 -58
  26. package/dist/collection/components/fields/mrd-longtext-field/mrd-longtext-field.js +1 -1
  27. package/dist/collection/components/fields/mrd-number-field/mrd-number-field.js +1 -1
  28. package/dist/collection/components/fields/mrd-relation-field/mrd-relation-field.js +13 -3
  29. package/dist/collection/components/fields/mrd-secret-field/mrd-secret-field.js +2 -2
  30. package/dist/collection/components/fields/mrd-text-field/mrd-text-field.js +1 -1
  31. package/dist/collection/components/fields/mrd-textarea-field/mrd-textarea-field.js +1 -1
  32. package/dist/collection/components/fields/mrd-time-field/mrd-time-field.js +1 -1
  33. package/dist/collection/components/form/mrd-field/mrd-field.js +5 -5
  34. package/dist/collection/components/form/mrd-form/mrd-form.js +63 -10
  35. package/dist/collection/components/invoice/mrd-invoice-view/mrd-invoice-view.css +145 -0
  36. package/dist/collection/components/invoice/mrd-invoice-view/mrd-invoice-view.js +281 -0
  37. package/dist/collection/components/layout/mrd-layout-section/field-value.js +9 -3
  38. package/dist/collection/components/layout/mrd-layout-section/layout-helpers.js +6 -0
  39. package/dist/collection/components/layout/mrd-layout-section/mrd-layout-section.css +14 -0
  40. package/dist/collection/components/layout/mrd-layout-section/mrd-layout-section.js +45 -15
  41. package/dist/collection/components/merge/mrd-merge-view/mrd-merge-view.js +2 -2
  42. package/dist/collection/dev/api.js +20 -5
  43. package/dist/collection/dev/app.js +51 -13
  44. package/dist/collection/dev/example-data.js +132 -3
  45. package/dist/collection/types/client-layout.js +6 -0
  46. package/dist/collection/utils/document-attachments.js +27 -14
  47. package/dist/collection/utils/i18n.js +144 -0
  48. package/dist/components/cell-renderer.js +1 -1
  49. package/dist/components/client-layout.js +1 -1
  50. package/dist/components/document-attachments.js +1 -1
  51. package/dist/components/file-upload-common.js +1 -1
  52. package/dist/components/format.js +1 -1
  53. package/dist/components/i18n.js +1 -1
  54. package/dist/components/index.js +1 -1
  55. package/dist/components/mrd-boolean-field2.js +1 -1
  56. package/dist/components/mrd-currency-field2.js +1 -1
  57. package/dist/components/mrd-date-field2.js +1 -1
  58. package/dist/components/mrd-datetime-field2.js +1 -1
  59. package/dist/components/mrd-document-container2.js +1 -1
  60. package/dist/components/mrd-document-list2.js +1 -1
  61. package/dist/components/mrd-document-view2.js +1 -1
  62. package/dist/components/mrd-email-field2.js +1 -1
  63. package/dist/components/mrd-email-view.d.ts +11 -0
  64. package/dist/components/mrd-email-view.js +1 -0
  65. package/dist/components/mrd-email-view2.js +1 -0
  66. package/dist/components/mrd-field2.js +1 -1
  67. package/dist/components/mrd-file-field2.js +1 -1
  68. package/dist/components/mrd-form.js +1 -1
  69. package/dist/components/mrd-image-field2.js +1 -1
  70. package/dist/components/mrd-invoice-view.d.ts +11 -0
  71. package/dist/components/mrd-invoice-view.js +1 -0
  72. package/dist/components/mrd-invoice-view2.js +1 -0
  73. package/dist/components/mrd-layout-section.js +1 -1
  74. package/dist/components/mrd-longtext-field2.js +1 -1
  75. package/dist/components/mrd-merge-view.js +1 -1
  76. package/dist/components/mrd-number-field2.js +1 -1
  77. package/dist/components/mrd-relation-field2.js +1 -1
  78. package/dist/components/mrd-secret-field2.js +1 -1
  79. package/dist/components/mrd-table2.js +1 -1
  80. package/dist/components/mrd-text-field2.js +1 -1
  81. package/dist/components/mrd-textarea-field2.js +1 -2
  82. package/dist/components/mrd-time-field2.js +1 -1
  83. package/dist/components/purify.es.js +2 -0
  84. package/dist/esm/{cell-renderer-D63AUZxa.js → cell-renderer-C2FLUrMN.js} +146 -2
  85. package/dist/esm/{client-layout-DCOAAH92.js → client-layout-Bc42CfJB.js} +7 -1
  86. package/dist/esm/{index-DiSe6vjv.js → index-CDiBvvJU.js} +468 -6
  87. package/dist/esm/index.js +1 -1
  88. package/dist/esm/loader.js +3 -3
  89. package/dist/esm/mosterdcomponents.js +3 -3
  90. package/dist/esm/{mrd-boolean-field_23.entry.js → mrd-boolean-field_25.entry.js} +5695 -5230
  91. package/dist/esm/mrd-merge-view.entry.js +5 -5
  92. package/dist/mosterdcomponents/index.esm.js +1 -1
  93. package/dist/mosterdcomponents/mosterdcomponents.esm.js +1 -1
  94. package/dist/mosterdcomponents/{p-2e16a7d0.entry.js → p-4ea5b97c.entry.js} +1 -1
  95. package/dist/mosterdcomponents/p-8736313b.entry.js +3 -0
  96. package/dist/mosterdcomponents/{p-DCOAAH92.js → p-Bc42CfJB.js} +1 -1
  97. package/dist/mosterdcomponents/p-BgsPVesg.js +1 -0
  98. package/dist/mosterdcomponents/p-CDiBvvJU.js +2 -0
  99. package/dist/types/components/documents/mrd-document-container/mrd-document-container.d.ts +7 -0
  100. package/dist/types/components/documents/mrd-document-list/mrd-document-list.d.ts +17 -0
  101. package/dist/types/components/documents/mrd-document-view/mrd-document-view.d.ts +2 -2
  102. package/dist/types/components/email/mrd-email-view/mrd-email-view.d.ts +52 -0
  103. package/dist/types/components/fields/file-upload-common.d.ts +19 -1
  104. package/dist/types/components/fields/mrd-file-field/mrd-file-field.d.ts +24 -10
  105. package/dist/types/components/fields/mrd-image-field/mrd-image-field.d.ts +27 -11
  106. package/dist/types/components/fields/mrd-relation-field/mrd-relation-field.d.ts +7 -0
  107. package/dist/types/components/form/mrd-form/mrd-form.d.ts +19 -3
  108. package/dist/types/components/invoice/mrd-invoice-view/mrd-invoice-view.d.ts +60 -0
  109. package/dist/types/components/layout/mrd-layout-section/layout-helpers.d.ts +3 -0
  110. package/dist/types/components/layout/mrd-layout-section/mrd-layout-section.d.ts +13 -0
  111. package/dist/types/components.d.ts +303 -19
  112. package/dist/types/types/client-layout.d.ts +13 -0
  113. package/dist/types/utils/document-attachments.d.ts +12 -5
  114. package/package.json +1 -1
  115. package/dist/mosterdcomponents/p-CnqDM0Gb.js +0 -1
  116. package/dist/mosterdcomponents/p-DiSe6vjv.js +0 -2
  117. package/dist/mosterdcomponents/p-b5f38707.entry.js +0 -3
@@ -89,6 +89,7 @@
89
89
  width: 100%;
90
90
  }
91
91
  .mrd-image-field__preview-thumb {
92
+ position: relative;
92
93
  flex-shrink: 0;
93
94
  width: 80px;
94
95
  height: 80px;
@@ -159,6 +160,49 @@
159
160
  background-color: var(--mrd-color-danger);
160
161
  color: var(--mrd-color-white);
161
162
  }
163
+ .mrd-image-field__grid {
164
+ list-style: none;
165
+ display: flex;
166
+ flex-wrap: wrap;
167
+ gap: var(--mrd-space-3);
168
+ margin: var(--mrd-space-2) 0 0;
169
+ padding: 0;
170
+ }
171
+ .mrd-image-field__grid-item {
172
+ position: relative;
173
+ display: flex;
174
+ flex-direction: column;
175
+ align-items: center;
176
+ gap: var(--mrd-space-1);
177
+ width: 88px;
178
+ }
179
+ .mrd-image-field__grid-name {
180
+ font-family: var(--mrd-font-family);
181
+ font-size: var(--mrd-font-size-xs);
182
+ color: var(--mrd-color-neutral-600);
183
+ max-width: 100%;
184
+ overflow: hidden;
185
+ text-overflow: ellipsis;
186
+ white-space: nowrap;
187
+ }
188
+ .mrd-image-field__grid-remove {
189
+ position: absolute;
190
+ top: -6px;
191
+ right: -6px;
192
+ width: 20px;
193
+ height: 20px;
194
+ line-height: 1;
195
+ background-color: var(--mrd-color-white);
196
+ color: var(--mrd-color-neutral-500);
197
+ border: var(--mrd-border-width) solid var(--mrd-border-color);
198
+ border-radius: 50%;
199
+ cursor: pointer;
200
+ font-size: var(--mrd-font-size-xs);
201
+ }
202
+ .mrd-image-field__grid-remove:hover {
203
+ color: var(--mrd-color-danger);
204
+ border-color: var(--mrd-color-danger);
205
+ }
162
206
 
163
207
  @keyframes mrd-spin {
164
208
  to {
@@ -1,7 +1,7 @@
1
1
  import { Host, h } from "@stencil/core";
2
2
  import { t } from "../../../utils/i18n";
3
3
  import { FieldLabel, FieldError } from "../field-helpers";
4
- import { validateUploadFile, uploadZoneClass, makeDragHandlers } from "../file-upload-common";
4
+ import { isPending, entryLabel, validateUploadFile, uploadZoneClass, makeDragHandlers } from "../file-upload-common";
5
5
  export class MrdImageField {
6
6
  constructor() {
7
7
  this.name = '';
@@ -12,79 +12,125 @@ export class MrdImageField {
12
12
  this.locale = navigator.language;
13
13
  this.accept = 'image/*';
14
14
  this.maxSize = 0;
15
- this.previewUrl = '';
16
- this.fileName = '';
15
+ this.multiple = false;
16
+ /** Working copy of the field's files, kept in sync with `value`. Entries still
17
+ * a `File` are pending upload; resolved entries are `{ href, fileName }` (or a
18
+ * bare href string for older hosts). */
19
+ this.entries = [];
17
20
  this.isDragging = false;
18
- this.uploading = false;
19
21
  this.error = '';
22
+ /** Local data-URL previews for pending File entries, keyed by File reference. */
23
+ this.previewCache = new Map();
20
24
  this.handleInputChange = (e) => {
21
25
  var _a;
22
- const files = e.target.files;
23
- this.handleFile((_a = files === null || files === void 0 ? void 0 : files[0]) !== null && _a !== void 0 ? _a : null);
26
+ const input = e.target;
27
+ this.addFiles(Array.from((_a = input.files) !== null && _a !== void 0 ? _a : []));
28
+ input.value = '';
24
29
  };
25
- this.drag = makeDragHandlers(dragging => (this.isDragging = dragging), file => this.handleFile(file));
30
+ this.drag = makeDragHandlers(dragging => (this.isDragging = dragging), files => this.addFiles(files));
26
31
  this.handleZoneClick = () => {
27
32
  var _a;
28
- if (!this.disabled && !this.uploading) {
33
+ if (!this.disabled && !this.zoneBusy) {
29
34
  (_a = this.fileInputRef) === null || _a === void 0 ? void 0 : _a.click();
30
35
  }
31
36
  };
32
- this.handleClear = (e) => {
37
+ this.handleRemove = (index) => (e) => {
33
38
  e.stopPropagation();
34
- this.previewUrl = '';
35
- this.fileName = '';
39
+ const removed = this.entries[index];
40
+ if (removed instanceof File)
41
+ this.previewCache.delete(removed);
42
+ const next = this.entries.filter((_, i) => i !== index);
43
+ this.entries = next;
36
44
  this.error = '';
37
- this.uploading = false;
38
45
  if (this.fileInputRef)
39
46
  this.fileInputRef.value = '';
40
- this.mrdChange.emit({ name: this.name, value: null });
47
+ this.emitValue(next);
41
48
  };
42
49
  }
43
- /** When the host provides a URI back via setFieldValue, the upload is done. */
44
- valueChanged(newVal) {
45
- if (typeof newVal === 'string' && newVal) {
46
- this.uploading = false;
47
- }
48
- else if (!newVal) {
49
- this.uploading = false;
50
- this.previewUrl = '';
51
- this.fileName = '';
52
- }
50
+ componentWillLoad() {
51
+ this.syncEntriesFromValue();
53
52
  }
54
- handleFile(file) {
55
- if (!file) {
56
- this.previewUrl = '';
57
- this.fileName = '';
58
- this.uploading = false;
59
- this.mrdChange.emit({ name: this.name, value: null });
60
- return;
53
+ /** The host (via mrd-form.setFieldValue) is the source of truth once a value round-trips back. */
54
+ valueChanged() {
55
+ this.syncEntriesFromValue();
56
+ }
57
+ syncEntriesFromValue() {
58
+ const v = this.value;
59
+ if (this.multiple) {
60
+ this.entries = Array.isArray(v) ? [...v] : v ? [v] : [];
61
61
  }
62
- const uploadError = validateUploadFile(file, this.maxSize, this.locale, true);
63
- if (uploadError) {
64
- this.error = uploadError;
65
- return;
62
+ else {
63
+ this.entries = v ? [v] : [];
66
64
  }
67
- this.error = '';
68
- this.fileName = file.name;
69
- this.uploading = true;
70
- // Show local preview immediately while upload is in progress
65
+ }
66
+ emitValue(entries) {
67
+ var _a;
68
+ const value = this.multiple ? entries : (_a = entries[0]) !== null && _a !== void 0 ? _a : null;
69
+ this.mrdChange.emit({ name: this.name, value: value });
70
+ }
71
+ readPreview(file) {
71
72
  const reader = new FileReader();
72
- reader.onload = (ev) => {
73
+ reader.onload = ev => {
73
74
  var _a;
74
- this.previewUrl = (_a = ev.target) === null || _a === void 0 ? void 0 : _a.result;
75
+ this.previewCache.set(file, (_a = ev.target) === null || _a === void 0 ? void 0 : _a.result);
76
+ this.entries = [...this.entries]; // trigger re-render now that the preview is ready
75
77
  };
76
78
  reader.readAsDataURL(file);
77
- this.mrdChange.emit({ name: this.name, value: file });
78
- this.mrdUpload.emit({ name: this.name, file });
79
+ }
80
+ addFiles(files) {
81
+ if (this.disabled || this.zoneBusy)
82
+ return;
83
+ const candidates = this.multiple ? files : files.slice(0, 1);
84
+ if (!candidates.length)
85
+ return;
86
+ const accepted = [];
87
+ let uploadError = '';
88
+ for (const file of candidates) {
89
+ const err = validateUploadFile(file, this.maxSize, this.locale, true);
90
+ if (err) {
91
+ uploadError = err;
92
+ continue;
93
+ }
94
+ accepted.push(file);
95
+ }
96
+ this.error = uploadError;
97
+ if (!accepted.length)
98
+ return;
99
+ const next = this.multiple ? [...this.entries, ...accepted] : [accepted[0]];
100
+ this.entries = next;
101
+ this.emitValue(next);
102
+ for (const file of accepted) {
103
+ this.readPreview(file);
104
+ this.mrdUpload.emit({ name: this.name, file });
105
+ }
106
+ }
107
+ /** In single mode, block re-selection while the one image is still uploading;
108
+ * in multiple mode, more images can always be added alongside pending uploads. */
109
+ get zoneBusy() {
110
+ return !this.multiple && this.entries.some(isPending);
111
+ }
112
+ renderThumb(entry) {
113
+ const uploading = isPending(entry);
114
+ const previewUrl = uploading ? this.previewCache.get(entry) : null;
115
+ return (h("div", { class: "mrd-image-field__preview-thumb" }, previewUrl && h("img", { class: "mrd-image-field__preview", src: previewUrl, alt: entryLabel(entry) }), uploading && (h("div", { class: "mrd-image-field__preview-overlay" }, h("span", { class: "mrd-image-field__spinner", "aria-label": t('loading', this.locale) })))));
116
+ }
117
+ renderSingle(entry) {
118
+ const uploading = isPending(entry);
119
+ return (h("div", { class: "mrd-image-field__preview-container" }, this.renderThumb(entry), h("div", { class: "mrd-image-field__preview-info" }, h("span", { class: "mrd-image-field__preview-name" }, entryLabel(entry)), uploading && h("span", { class: "mrd-image-field__upload-status" }, t('loading', this.locale))), !uploading && (h("button", { class: "mrd-image-field__clear", type: "button", onClick: this.handleRemove(0), "aria-label": t('remove', this.locale) }, t('remove', this.locale)))));
120
+ }
121
+ renderGridItem(entry, index) {
122
+ const uploading = isPending(entry);
123
+ return (h("li", { class: "mrd-image-field__grid-item", key: `${index}-${entryLabel(entry)}` }, this.renderThumb(entry), h("span", { class: "mrd-image-field__grid-name" }, entryLabel(entry)), !uploading && (h("button", { class: "mrd-image-field__grid-remove", type: "button", onClick: this.handleRemove(index), "aria-label": t('remove', this.locale) }, "\u2715"))));
79
124
  }
80
125
  render() {
81
126
  const hasError = !!this.error;
127
+ const hasEntries = this.entries.length > 0;
82
128
  const zoneClass = uploadZoneClass('mrd-image-field', {
83
129
  dragging: this.isDragging,
84
130
  error: hasError,
85
- disabled: this.disabled || this.uploading,
131
+ disabled: this.disabled || this.zoneBusy,
86
132
  });
87
- return (h(Host, { key: '700310af716dcc167f98bc7ae7024bb44e1285b6' }, h("div", { key: '95d29a82751249e560404488fb279138eee0c1be', class: "mrd-image-field" }, h(FieldLabel, { key: 'f53f040257a4b65d8435c836846adab137b91a5e', base: "mrd-image-field", label: this.label, required: this.required }), h("div", { key: 'a9cc7a0861b1457d8e4abb11b380b6eeb519a863', class: zoneClass, onClick: this.handleZoneClick, onDragOver: this.drag.onDragOver, onDragLeave: this.drag.onDragLeave, onDrop: this.drag.onDrop }, h("input", { key: '7112030bc2b80b4c3b31a4421656da4c926a2fe1', 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)))))), h(FieldError, { key: '0cf5346ecb1a5d110822b087081b4b5f05255381', base: "mrd-image-field", error: this.error }))));
133
+ return (h(Host, { key: '51d39ea80195565a5dc780ff6a7923a976cfb0a7' }, h("div", { key: 'd76edb7ac9f1efea8c7c3d5b04e906ad4ab4c761', class: "mrd-image-field" }, h(FieldLabel, { key: 'b46a67a76188047677c40ce3f406daed7d8de141', base: "mrd-image-field", label: this.label, required: this.required }), h("div", { key: '0156a9722ea48d84e3ea1a829e4266425a594a6a', class: zoneClass, onClick: this.handleZoneClick, onDragOver: this.drag.onDragOver, onDragLeave: this.drag.onDragLeave, onDrop: this.drag.onDrop }, h("input", { key: '1b8e41da71bb57254bfd2e5527b05e1aeb32eb2f', ref: el => (this.fileInputRef = el), class: "mrd-image-field__input", type: "file", name: this.name, accept: this.accept, multiple: this.multiple, disabled: this.disabled || this.zoneBusy, required: this.required && !hasEntries, onChange: this.handleInputChange }), hasEntries && !this.multiple ? (this.renderSingle(this.entries[0])) : (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)))))), this.multiple && hasEntries && (h("ul", { key: '9211b5212eae0629f7c9b2b49d80e2b658a9ddd3', class: "mrd-image-field__grid" }, this.entries.map((entry, index) => this.renderGridItem(entry, index)))), h(FieldError, { key: '5f49c03156ed18e7c8307adff679b149658ca4a6', base: "mrd-image-field", error: this.error }))));
88
134
  }
89
135
  static get is() { return "mrd-image-field"; }
90
136
  static get encapsulation() { return "scoped"; }
@@ -257,15 +303,33 @@ export class MrdImageField {
257
303
  "reflect": false,
258
304
  "attribute": "max-size",
259
305
  "defaultValue": "0"
306
+ },
307
+ "multiple": {
308
+ "type": "boolean",
309
+ "mutable": false,
310
+ "complexType": {
311
+ "original": "boolean",
312
+ "resolved": "boolean",
313
+ "references": {}
314
+ },
315
+ "required": false,
316
+ "optional": false,
317
+ "docs": {
318
+ "tags": [],
319
+ "text": ""
320
+ },
321
+ "getter": false,
322
+ "setter": false,
323
+ "reflect": false,
324
+ "attribute": "multiple",
325
+ "defaultValue": "false"
260
326
  }
261
327
  };
262
328
  }
263
329
  static get states() {
264
330
  return {
265
- "previewUrl": {},
266
- "fileName": {},
331
+ "entries": {},
267
332
  "isDragging": {},
268
- "uploading": {},
269
333
  "error": {}
270
334
  };
271
335
  }
@@ -281,12 +345,14 @@ export class MrdImageField {
281
345
  "text": ""
282
346
  },
283
347
  "complexType": {
284
- "original": "{ name: string; value: File | null }",
285
- "resolved": "{ name: string; value: File | null; }",
348
+ "original": "{ name: string; value: FileEntry | FileEntry[] | null }",
349
+ "resolved": "{ name: string; value: FileEntry | FileEntry[] | null; }",
286
350
  "references": {
287
- "File": {
288
- "location": "global",
289
- "id": "global::File"
351
+ "FileEntry": {
352
+ "location": "import",
353
+ "path": "../file-upload-common",
354
+ "id": "src/components/fields/file-upload-common.ts::FileEntry",
355
+ "referenceLocation": "FileEntry"
290
356
  }
291
357
  }
292
358
  }
@@ -301,12 +367,14 @@ export class MrdImageField {
301
367
  "text": ""
302
368
  },
303
369
  "complexType": {
304
- "original": "{ name: string; value: File | null }",
305
- "resolved": "{ name: string; value: File | null; }",
370
+ "original": "{ name: string; value: FileEntry | FileEntry[] | null }",
371
+ "resolved": "{ name: string; value: FileEntry | FileEntry[] | null; }",
306
372
  "references": {
307
- "File": {
308
- "location": "global",
309
- "id": "global::File"
373
+ "FileEntry": {
374
+ "location": "import",
375
+ "path": "../file-upload-common",
376
+ "id": "src/components/fields/file-upload-common.ts::FileEntry",
377
+ "referenceLocation": "FileEntry"
310
378
  }
311
379
  }
312
380
  }
@@ -318,7 +386,7 @@ export class MrdImageField {
318
386
  "composed": true,
319
387
  "docs": {
320
388
  "tags": [],
321
- "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."
389
+ "text": "Emitted once per selected/dropped image that needs to be uploaded.\nHost should upload the file and call form.setFieldValue(name, { href, fileName: file.name }, file)\nwith the result \u2014 passing `file` back lets mrd-form resolve the right entry in a `multiple` field."
322
390
  },
323
391
  "complexType": {
324
392
  "original": "{ name: string; file: File }",
@@ -21,7 +21,7 @@ export class MrdLongtextField {
21
21
  };
22
22
  }
23
23
  render() {
24
- return (h(Host, { key: 'b27d14772f5184d41c178606629a9945120296ef' }, h("div", { key: '206b1b9ee1d37e05680e31bf8b324455867fd93b', class: "mrd-longtext-field" }, h(FieldLabel, { key: '0b6f4959f32a097907965753ed8708d7580aee53', base: "mrd-longtext-field", label: this.label, required: this.required }), this.disabled ? (h("pre", { class: "mrd-longtext-field__content" }, this.value)) : (h("textarea", { class: controlClass('mrd-longtext-field', !!this.error), name: this.name, placeholder: this.placeholder, required: this.required, rows: 10, onInput: this.handleInput, onBlur: this.handleBlur }, this.value)), h(FieldError, { key: '0803cf0c1b6409e6c8a65121efef97d28d870119', base: "mrd-longtext-field", error: this.error }))));
24
+ return (h(Host, { key: 'e53fec4d4eafbd68d9cffb1f064d8e0c4c37839c' }, h("div", { key: '8843a23e42b0545c269fc6c924e7ae295770064e', class: "mrd-longtext-field" }, h(FieldLabel, { key: '8767676dd35f25f9bf5ebdb26887edc0882582e0', base: "mrd-longtext-field", label: this.label, required: this.required }), this.disabled ? (h("pre", { class: "mrd-longtext-field__content" }, this.value)) : (h("textarea", { class: controlClass('mrd-longtext-field', !!this.error), name: this.name, placeholder: this.placeholder, required: this.required, rows: 10, onInput: this.handleInput, onBlur: this.handleBlur }, this.value)), h(FieldError, { key: '54dc790173575e8a02764e5b14040b900c2eb609', base: "mrd-longtext-field", error: this.error }))));
25
25
  }
26
26
  static get is() { return "mrd-longtext-field"; }
27
27
  static get encapsulation() { return "scoped"; }
@@ -70,7 +70,7 @@ export class MrdNumberField {
70
70
  const hasError = !!this.error;
71
71
  const suffix = this.dataType === ClientLayoutItemFieldDataType.PERCENTAGE ? '%' :
72
72
  this.dataType === ClientLayoutItemFieldDataType.DECIMAL ? '' : '';
73
- return (h(Host, { key: '90cae99b332f9554bc80d4044fce892ae37a48cf' }, h("div", { key: '26a9cfcc662eaeb571d17785f10f74269cf78537', class: "mrd-number-field" }, h(FieldLabel, { key: '0d9e70c1591f309ad2875d9e9fe6dbb8aa910323', base: "mrd-number-field", label: this.label, required: this.required }), h("div", { key: '95e9b53fa73c452491f65355915bf04912900ded', class: "mrd-number-field__input-wrapper" }, h("input", { key: '1d341c270a21b1a10c51757e90257e48e741abd7', class: controlClass('mrd-number-field', hasError), type: "text", inputMode: "decimal", name: this.name, value: this.displayValue, placeholder: this.placeholder || (suffix ? `0${suffix}` : '0'), required: this.required, disabled: this.disabled, onInput: this.handleInput, onBlur: this.handleBlur, onFocus: this.handleFocus })), h(FieldError, { key: '0dba75e0f7355b71bfa71dab16cd1a6fcc36922f', base: "mrd-number-field", error: this.error }))));
73
+ return (h(Host, { key: '5df5c2e16c85cd54944d95b7a8c340ae1a4ddb34' }, h("div", { key: 'e332bd0190d537c002b14090fa96ec6502e34746', class: "mrd-number-field" }, h(FieldLabel, { key: 'e26ae85ef6c6d7935b2d34041a7ee2294da392a4', base: "mrd-number-field", label: this.label, required: this.required }), h("div", { key: 'fb1a44a363a36b69124a0092e80b67d41b538c03', class: "mrd-number-field__input-wrapper" }, h("input", { key: '175c448177bbd46015fc7f47b98b76e16ee3b743', class: controlClass('mrd-number-field', hasError), type: "text", inputMode: "decimal", name: this.name, value: this.displayValue, placeholder: this.placeholder || (suffix ? `0${suffix}` : '0'), required: this.required, disabled: this.disabled, onInput: this.handleInput, onBlur: this.handleBlur, onFocus: this.handleFocus })), h(FieldError, { key: 'ecb16ddf655e563f0569e774ab2f2e419f60b0f1', base: "mrd-number-field", error: this.error }))));
74
74
  }
75
75
  static get is() { return "mrd-number-field"; }
76
76
  static get encapsulation() { return "scoped"; }
@@ -121,16 +121,22 @@ export class MrdRelationField {
121
121
  async setAllRecords(records) {
122
122
  this.allRecords = records;
123
123
  }
124
+ /** Href of the current single value. Accepts both a RelationSearchResult
125
+ * (`id`) and the raw API link shape (`{ href, name }`) a host puts in the
126
+ * form values for edit mode — same tolerance as `toSearchResult()`. */
124
127
  getValueHref() {
125
- var _a, _b, _c;
128
+ var _a, _b, _c, _d;
126
129
  const v = Array.isArray(this.value) ? ((_a = this.value[0]) !== null && _a !== void 0 ? _a : '') : ((_b = this.value) !== null && _b !== void 0 ? _b : '');
127
130
  if (!v)
128
131
  return '';
129
132
  if (typeof v === 'object')
130
- return (_c = v.id) !== null && _c !== void 0 ? _c : '';
133
+ return (_d = (_c = v.id) !== null && _c !== void 0 ? _c : v.href) !== null && _d !== void 0 ? _d : '';
131
134
  return v;
132
135
  }
133
- allRecordsChanged() {
136
+ /** Push the current value onto the rendered <select>. Options and value arrive
137
+ * independently in edit mode (fetched records vs. the async `values` prop), so
138
+ * this runs on both — whichever lands last completes the preselection. */
139
+ syncDropdownSelection() {
134
140
  if (this.editBehavior !== ClientLayoutItemRelationEditBehavior.DROPDOWN)
135
141
  return;
136
142
  const current = this.getValueHref();
@@ -146,6 +152,9 @@ export class MrdRelationField {
146
152
  select.value = current;
147
153
  }, 0);
148
154
  }
155
+ allRecordsChanged() {
156
+ this.syncDropdownSelection();
157
+ }
149
158
  async setSearchResults(results) {
150
159
  this.searchResults = results;
151
160
  this.isLoading = false;
@@ -197,6 +206,7 @@ export class MrdRelationField {
197
206
  // (host pre-fill). Plain href strings from user interaction are ignored.
198
207
  valueChanged(newValue) {
199
208
  this.applyValuePrefill(newValue);
209
+ this.syncDropdownSelection();
200
210
  }
201
211
  componentDidLoad() {
202
212
  // Only emit when there is no commonRelation dependency — the form orchestrates those.
@@ -21,9 +21,9 @@ export class MrdSecretField {
21
21
  };
22
22
  }
23
23
  render() {
24
- return (h(Host, { key: '9883a87b7fcc840aa4bbe0a3cde163cc58bda65d' }, h("div", { key: '3c7ec45d59a1a17863daa39b7c115db5c58815ee', class: "mrd-secret-field" }, h(FieldLabel, { key: '2dda66c7e6d4946661bff5a5a46589a7e17f569a', base: "mrd-secret-field", label: this.label, required: this.required }), this.disabled
24
+ return (h(Host, { key: 'e06d04fdae34b42441976e21605fe94ad935382e' }, h("div", { key: '2889e1eb1b6d0887ae531523a0b9b43c49ef6f99', class: "mrd-secret-field" }, h(FieldLabel, { key: 'c40907905292cdef8bb0b8b9b0c03e05650bef37', base: "mrd-secret-field", label: this.label, required: this.required }), this.disabled
25
25
  ? (h("span", { class: "mrd-secret-field__masked", "aria-label": this.label }, this.value ? '••••••••' : ''))
26
- : (h("input", { class: controlClass('mrd-secret-field', !!this.error), type: "password", name: this.name, value: this.value, placeholder: this.placeholder, required: this.required, autocomplete: "new-password", onInput: this.handleInput, onBlur: this.handleBlur })), h(FieldError, { key: 'ad8f54a7c76c59591fee77e11a12bbd62c392d91', base: "mrd-secret-field", error: this.error }))));
26
+ : (h("input", { class: controlClass('mrd-secret-field', !!this.error), type: "password", name: this.name, value: this.value, placeholder: this.placeholder, required: this.required, autocomplete: "new-password", onInput: this.handleInput, onBlur: this.handleBlur })), h(FieldError, { key: '6353e4b40566852dbc1994c7c9c409d0d0aa1b37', base: "mrd-secret-field", error: this.error }))));
27
27
  }
28
28
  static get is() { return "mrd-secret-field"; }
29
29
  static get encapsulation() { return "scoped"; }
@@ -21,7 +21,7 @@ export class MrdTextField {
21
21
  };
22
22
  }
23
23
  render() {
24
- return (h(Host, { key: 'd2a6fb5735a3a01887cc467274a0ff9d71312124' }, h("div", { key: '7864bf852d43f93ebd0120a3ec1b72e54837d1ba', class: "mrd-text-field" }, h(FieldLabel, { key: 'bdad112197b44a643a58dd79c4648e284311199d', base: "mrd-text-field", label: this.label, required: this.required }), h("input", { key: '1d3e1cf3374ea2e5a83794b5110205964fff86e1', class: controlClass('mrd-text-field', !!this.error), type: "text", name: this.name, value: this.value, placeholder: this.placeholder, required: this.required, disabled: this.disabled, onInput: this.handleInput, onBlur: this.handleBlur }), h(FieldError, { key: 'bb4ec51de7b6d360fdb3d46dd53e85a5d497bcce', base: "mrd-text-field", error: this.error }))));
24
+ return (h(Host, { key: '3153f0c6f8ab4d9dae93306f7507399aeda75eaf' }, h("div", { key: '5d9c28023fdfb5472fc115c66bc3df509506e10f', class: "mrd-text-field" }, h(FieldLabel, { key: '1e21139aae0cc1ae95aa3297271b618a40043e77', base: "mrd-text-field", label: this.label, required: this.required }), h("input", { key: '46f98d5e39bb834075449fac7096b077a1ae7bb3', class: controlClass('mrd-text-field', !!this.error), type: "text", name: this.name, value: this.value, placeholder: this.placeholder, required: this.required, disabled: this.disabled, onInput: this.handleInput, onBlur: this.handleBlur }), h(FieldError, { key: '10b6ddae9cd9f36dc9d7176167d79df8e4102c03', base: "mrd-text-field", error: this.error }))));
25
25
  }
26
26
  static get is() { return "mrd-text-field"; }
27
27
  static get encapsulation() { return "scoped"; }
@@ -64,7 +64,7 @@ export class MrdTextareaField {
64
64
  return DOMPurify.sanitize(html);
65
65
  }
66
66
  render() {
67
- return (h(Host, { key: '708c1f8f14d2de4b5a274155d190fc19a304d7f0' }, h("div", { key: 'cf5c7a571e74cad08e0e320e91db4e22a3b2e080', class: "mrd-textarea-field" }, h(FieldLabel, { key: '4ced05f3914440f89dc5605901fc311e8ad54360', base: "mrd-textarea-field", label: this.label, required: this.required }), h("div", { key: '81147829bc575d05f3521bf3704edfdfe0d9b2cf', class: controlClass('mrd-textarea-field', !!this.error, 'container') }, h("div", { key: 'a9f42d86cab460d5e0cf6de87e0c0a0a1ca7d77a', class: "mrd-textarea-field__editor" })), h(FieldError, { key: '9f4b5271d0901e474ef6c0f1170b3ef718a844a9', base: "mrd-textarea-field", error: this.error }))));
67
+ return (h(Host, { key: 'e132531881f68f373e700617aac8033c8ebccb47' }, h("div", { key: 'fec0cf56cd7868d3fcd5a4e8a382b2dcdb6e5c50', class: "mrd-textarea-field" }, h(FieldLabel, { key: '333aa2ff57cce9e3178f0c4be4a1df1077884248', base: "mrd-textarea-field", label: this.label, required: this.required }), h("div", { key: '40025207580eb5768981b85aaa0a8ff95fce7234', class: controlClass('mrd-textarea-field', !!this.error, 'container') }, h("div", { key: '621987b3b2b1d8b4836bff065075ba527a82db81', class: "mrd-textarea-field__editor" })), h(FieldError, { key: 'ac2d8de22f008cd1c91842137ccd0cd36eb9ce19', base: "mrd-textarea-field", error: this.error }))));
68
68
  }
69
69
  static get is() { return "mrd-textarea-field"; }
70
70
  static get encapsulation() { return "scoped"; }
@@ -20,7 +20,7 @@ export class MrdTimeField {
20
20
  };
21
21
  }
22
22
  render() {
23
- return (h(Host, { key: '588e298c4f52aa4e65d87bfdc6918282ed97b3ae' }, h("div", { key: 'eed7332ca78747d4ff8b6c8d06ad4674e1c37e69', class: "mrd-time-field" }, h(FieldLabel, { key: '6383beb79fd05dabdc408256e3326f9637e4c993', base: "mrd-time-field", label: this.label, required: this.required }), h("input", { key: 'b372d8b5dd523ee6182fe302450ce904ac17cc3c', class: controlClass('mrd-time-field', !!this.error), type: "time", name: this.name, value: this.value, required: this.required, disabled: this.disabled, onChange: this.handleChange, onBlur: this.handleBlur }), h(FieldError, { key: '7d8ce1e557bf583c0409e8f02893a15fadeab634', base: "mrd-time-field", error: this.error }))));
23
+ return (h(Host, { key: 'd521c654bc7beda71a31542a25198edaaf53b838' }, h("div", { key: '084ab16ac8aed952bd0ec50e0430591d4db5bd4b', class: "mrd-time-field" }, h(FieldLabel, { key: '43938ad7c5a9765353f6523d399d4b74cbb9ec6f', base: "mrd-time-field", label: this.label, required: this.required }), h("input", { key: 'e46eda620e7a5f1bb3b433a4c051e913675c33f9', class: controlClass('mrd-time-field', !!this.error), type: "time", name: this.name, value: this.value, required: this.required, disabled: this.disabled, onChange: this.handleChange, onBlur: this.handleBlur }), h(FieldError, { key: '34bc1eaa1b969951dffaaacad73af532b6d290fb', base: "mrd-time-field", error: this.error }))));
24
24
  }
25
25
  static get is() { return "mrd-time-field"; }
26
26
  static get encapsulation() { return "scoped"; }
@@ -111,7 +111,7 @@ export class MrdField {
111
111
  return (h("div", { class: "mrd-field__history-editor" }, this.historyEntries.length > 0 && (h("span", { class: "mrd-field__history-editor-label" }, t('history_badge_tooltip', locale))), this.historyEntries.map((entry, i) => (h("div", { key: String(i), class: "mrd-field__history-editor-row" }, h("input", { class: "mrd-field__history-editor-value", type: valueType, value: entry.value, onInput: (e) => updateEntry(i, 'value', e.target.value) }), h("span", { class: "mrd-field__history-editor-sep" }, t('history_until', locale)), h("input", { class: "mrd-field__history-editor-until", type: "date", value: entry.until, onInput: (e) => updateEntry(i, 'until', e.target.value) }), h("button", { type: "button", class: "mrd-field__history-editor-remove", onClick: () => removeEntry(i), "aria-label": t('remove', locale) }, h("svg", { viewBox: "0 0 20 20", fill: "currentColor", "aria-hidden": "true" }, h("path", { "fill-rule": "evenodd", d: "M8.75 1A2.75 2.75 0 006 3.75v.443c-.795.077-1.584.176-2.365.298a.75.75 0 10.23 1.482l.149-.022.841 10.518A2.75 2.75 0 007.596 19h4.807a2.75 2.75 0 002.742-2.53l.841-10.52.149.023a.75.75 0 00.23-1.482A41.03 41.03 0 0014 4.193V3.75A2.75 2.75 0 0011.25 1h-2.5zM10 4c.84 0 1.673.025 2.5.075V3.75c0-.69-.56-1.25-1.25-1.25h-2.5c-.69 0-1.25.56-1.25 1.25v.325C8.327 4.025 9.16 4 10 4zM8.58 7.72a.75.75 0 00-1.5.06l.3 7.5a.75.75 0 101.5-.06l-.3-7.5zm4.34.06a.75.75 0 10-1.5-.06l-.3 7.5a.75.75 0 101.5.06l.3-7.5z", "clip-rule": "evenodd" })))))), h("button", { type: "button", class: "mrd-field__history-editor-add", onClick: addEntry }, "+ ", t('add', locale))));
112
112
  }
113
113
  renderLeafField(displayValue) {
114
- var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z, _0, _1, _2, _3, _4;
114
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z, _0, _1, _2, _3, _4, _5, _6;
115
115
  const { item, locale } = this;
116
116
  const commonProps = {
117
117
  name: item.name,
@@ -148,13 +148,13 @@ export class MrdField {
148
148
  case ClientLayoutItemFieldDataType.LIST:
149
149
  return (h("mrd-list-field", Object.assign({}, commonProps, { value: (_u = displayValue) !== null && _u !== void 0 ? _u : '', multiple: (_v = item.multiple) !== null && _v !== void 0 ? _v : false, listItems: (_w = item.listItems) !== null && _w !== void 0 ? _w : [] })));
150
150
  case ClientLayoutItemFieldDataType.FILE:
151
- return (h("mrd-file-field", Object.assign({}, commonProps, { value: displayValue, accept: (_x = item.accept) !== null && _x !== void 0 ? _x : '', maxSize: (_y = item.maxSize) !== null && _y !== void 0 ? _y : 0, onMrdUpload: this.handleUpload })));
151
+ return (h("mrd-file-field", Object.assign({}, commonProps, { value: displayValue, accept: (_x = item.accept) !== null && _x !== void 0 ? _x : '', maxSize: (_y = item.maxSize) !== null && _y !== void 0 ? _y : 0, multiple: (_z = item.multiple) !== null && _z !== void 0 ? _z : false, onMrdUpload: this.handleUpload })));
152
152
  case ClientLayoutItemFieldDataType.IMAGE:
153
- return (h("mrd-image-field", Object.assign({}, commonProps, { value: displayValue, accept: (_z = item.accept) !== null && _z !== void 0 ? _z : 'image/*', maxSize: (_0 = item.maxSize) !== null && _0 !== void 0 ? _0 : 0, onMrdUpload: this.handleUpload })));
153
+ return (h("mrd-image-field", Object.assign({}, commonProps, { value: displayValue, accept: (_0 = item.accept) !== null && _0 !== void 0 ? _0 : 'image/*', maxSize: (_1 = item.maxSize) !== null && _1 !== void 0 ? _1 : 0, multiple: (_2 = item.multiple) !== null && _2 !== void 0 ? _2 : false, onMrdUpload: this.handleUpload })));
154
154
  case ClientLayoutItemFieldDataType.LONGTEXT:
155
- return (h("mrd-longtext-field", Object.assign({}, commonProps, { value: (_1 = displayValue) !== null && _1 !== void 0 ? _1 : '', placeholder: (_2 = item.placeholder) !== null && _2 !== void 0 ? _2 : '' })));
155
+ return (h("mrd-longtext-field", Object.assign({}, commonProps, { value: (_3 = displayValue) !== null && _3 !== void 0 ? _3 : '', placeholder: (_4 = item.placeholder) !== null && _4 !== void 0 ? _4 : '' })));
156
156
  case ClientLayoutItemFieldDataType.SECRET:
157
- return (h("mrd-secret-field", Object.assign({}, commonProps, { value: (_3 = displayValue) !== null && _3 !== void 0 ? _3 : '', placeholder: (_4 = item.placeholder) !== null && _4 !== void 0 ? _4 : '' })));
157
+ return (h("mrd-secret-field", Object.assign({}, commonProps, { value: (_5 = displayValue) !== null && _5 !== void 0 ? _5 : '', placeholder: (_6 = item.placeholder) !== null && _6 !== void 0 ? _6 : '' })));
158
158
  default:
159
159
  return null;
160
160
  }
@@ -1,5 +1,5 @@
1
1
  import { Host, h } from "@stencil/core";
2
- import { ClientLayoutItemType, ClientLayoutItemRelationEditBehavior } from "../../../types";
2
+ import { ClientLayoutItemFieldDataType, ClientLayoutItemType, ClientLayoutItemRelationEditBehavior } from "../../../types";
3
3
  import { t } from "../../../utils/i18n";
4
4
  import { validateRequired } from "../../../utils/validation";
5
5
  export class MrdForm {
@@ -199,7 +199,31 @@ export class MrdForm {
199
199
  }
200
200
  return null;
201
201
  }
202
- async setFieldValue(name, value) {
202
+ /**
203
+ * Inject a value from outside (e.g. after a file upload completes).
204
+ * `file` is optional and only meaningful for `multiple` FILE/IMAGE fields: when
205
+ * the field's current value is an array, mrd-form replaces the array entry that
206
+ * `=== file` with `value`, leaving other pending/resolved entries untouched —
207
+ * this is how mrd-file-field/mrd-image-field resolve concurrent per-file uploads.
208
+ * Pass the `file` from the originating mrdUpload event back unchanged.
209
+ */
210
+ async setFieldValue(name, value, file) {
211
+ if (file) {
212
+ const current = this.formValues[name];
213
+ if (Array.isArray(current)) {
214
+ const index = current.indexOf(file);
215
+ if (index === -1)
216
+ return; // the file was removed from the field before its upload resolved
217
+ const next = [...current];
218
+ next[index] = value;
219
+ this.formValues = Object.assign(Object.assign({}, this.formValues), { [name]: next });
220
+ if (this.errors[name])
221
+ this.errors = Object.assign(Object.assign({}, this.errors), { [name]: '' });
222
+ return;
223
+ }
224
+ if (current !== file)
225
+ return; // stale resolution for a value that was since cleared/replaced
226
+ }
203
227
  this.formValues = Object.assign(Object.assign({}, this.formValues), { [name]: value });
204
228
  if (this.errors[name]) {
205
229
  this.errors = Object.assign(Object.assign({}, this.errors), { [name]: '' });
@@ -229,15 +253,19 @@ export class MrdForm {
229
253
  }
230
254
  }
231
255
  }
232
- /** Extract a plain href string from a form value, which can be a string or
233
- * a RelationSearchResult-like object with an `id` field. */
256
+ /** Extract a plain href string from a form value: a string, a
257
+ * RelationSearchResult-like object (`id`), or the raw API link shape
258
+ * (`{ href, name }`) hosts spread into `values` for edit mode. */
234
259
  getHref(value) {
260
+ var _a, _b;
235
261
  if (!value)
236
262
  return '';
237
263
  if (typeof value === 'string')
238
264
  return value;
239
- if (typeof value === 'object' && 'id' in value)
240
- return value.id;
265
+ if (typeof value === 'object') {
266
+ const v = value;
267
+ return (_b = (_a = v.id) !== null && _a !== void 0 ? _a : v.href) !== null && _b !== void 0 ? _b : '';
268
+ }
241
269
  return '';
242
270
  }
243
271
  collectFields(items) {
@@ -269,6 +297,20 @@ export class MrdForm {
269
297
  normalizeFieldValue(value) {
270
298
  return (value === '' || value == null) ? null : value;
271
299
  }
300
+ /** FILE/IMAGE entries render as `{ href, fileName, contentType?, size? }` — the same
301
+ * shape the API returns for an already-persisted file — or a bare href string for
302
+ * freshly-resolved uploads. Only `href` is meaningful to the API, so unwrap before
303
+ * submit (mirrors normalizeRelationValue). Do NOT fall back to any other key (e.g.
304
+ * `id`) as a stand-in for `href` — a bare id is not a valid/complete reference on its
305
+ * own; if `href` is ever missing that's an API bug to fix upstream, not to paper over here. */
306
+ normalizeFileFieldValue(value) {
307
+ const toHref = (v) => v && typeof v === 'object' && 'href' in v ? v.href : v;
308
+ if (Array.isArray(value)) {
309
+ const hrefs = value.map(toHref).filter(v => v != null && v !== '');
310
+ return hrefs.length ? hrefs : null;
311
+ }
312
+ return this.normalizeFieldValue(toHref(value));
313
+ }
272
314
  normalizeRelationValue(value) {
273
315
  if (value == null || value === '')
274
316
  return null;
@@ -315,8 +357,11 @@ export class MrdForm {
315
357
  const rawValue = this.formValues[name];
316
358
  if (rawValue instanceof File)
317
359
  continue;
318
- const current = this.normalizeFieldValue(rawValue);
319
- const initial = this.normalizeFieldValue(this.initialValues[name]);
360
+ if (Array.isArray(rawValue) && rawValue.some(v => v instanceof File))
361
+ continue;
362
+ const isFileField = item.dataType === ClientLayoutItemFieldDataType.FILE || item.dataType === ClientLayoutItemFieldDataType.IMAGE;
363
+ const current = isFileField ? this.normalizeFileFieldValue(rawValue) : this.normalizeFieldValue(rawValue);
364
+ const initial = isFileField ? this.normalizeFileFieldValue(this.initialValues[name]) : this.normalizeFieldValue(this.initialValues[name]);
320
365
  if (this.deepEqual(current, initial))
321
366
  continue;
322
367
  payload[name] = current;
@@ -619,7 +664,7 @@ export class MrdForm {
619
664
  return {
620
665
  "setFieldValue": {
621
666
  "complexType": {
622
- "signature": "(name: string, value: unknown) => Promise<void>",
667
+ "signature": "(name: string, value: unknown, file?: File) => Promise<void>",
623
668
  "parameters": [{
624
669
  "name": "name",
625
670
  "type": "string",
@@ -628,17 +673,25 @@ export class MrdForm {
628
673
  "name": "value",
629
674
  "type": "unknown",
630
675
  "docs": ""
676
+ }, {
677
+ "name": "file",
678
+ "type": "File | undefined",
679
+ "docs": ""
631
680
  }],
632
681
  "references": {
633
682
  "Promise": {
634
683
  "location": "global",
635
684
  "id": "global::Promise"
685
+ },
686
+ "File": {
687
+ "location": "global",
688
+ "id": "global::File"
636
689
  }
637
690
  },
638
691
  "return": "Promise<void>"
639
692
  },
640
693
  "docs": {
641
- "text": "",
694
+ "text": "Inject a value from outside (e.g. after a file upload completes).\n`file` is optional and only meaningful for `multiple` FILE/IMAGE fields: when\nthe field's current value is an array, mrd-form replaces the array entry that\n`=== file` with `value`, leaving other pending/resolved entries untouched \u2014\nthis is how mrd-file-field/mrd-image-field resolve concurrent per-file uploads.\nPass the `file` from the originating mrdUpload event back unchanged.",
642
695
  "tags": []
643
696
  }
644
697
  }