@mmlogic/components 0.5.2 → 0.5.3

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 (105) hide show
  1. package/dist/cjs/{cell-renderer-CmaO0CZT.js → cell-renderer-7KX9Qbml.js} +122 -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} +5618 -5225
  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-view/mrd-document-view.js +7 -8
  11. package/dist/collection/components/email/mrd-email-view/mrd-email-view.css +126 -0
  12. package/dist/collection/components/email/mrd-email-view/mrd-email-view.js +247 -0
  13. package/dist/collection/components/fields/file-upload-common.js +23 -7
  14. package/dist/collection/components/fields/mrd-boolean-field/mrd-boolean-field.js +1 -1
  15. package/dist/collection/components/fields/mrd-currency-field/mrd-currency-field.js +1 -1
  16. package/dist/collection/components/fields/mrd-date-field/mrd-date-field.js +1 -1
  17. package/dist/collection/components/fields/mrd-datetime-field/mrd-datetime-field.js +1 -1
  18. package/dist/collection/components/fields/mrd-email-field/mrd-email-field.js +1 -1
  19. package/dist/collection/components/fields/mrd-file-field/mrd-file-field.css +20 -1
  20. package/dist/collection/components/fields/mrd-file-field/mrd-file-field.js +108 -47
  21. package/dist/collection/components/fields/mrd-image-field/mrd-image-field.css +44 -0
  22. package/dist/collection/components/fields/mrd-image-field/mrd-image-field.js +126 -58
  23. package/dist/collection/components/fields/mrd-longtext-field/mrd-longtext-field.js +1 -1
  24. package/dist/collection/components/fields/mrd-number-field/mrd-number-field.js +1 -1
  25. package/dist/collection/components/fields/mrd-secret-field/mrd-secret-field.js +2 -2
  26. package/dist/collection/components/fields/mrd-text-field/mrd-text-field.js +1 -1
  27. package/dist/collection/components/fields/mrd-textarea-field/mrd-textarea-field.js +1 -1
  28. package/dist/collection/components/fields/mrd-time-field/mrd-time-field.js +1 -1
  29. package/dist/collection/components/form/mrd-field/mrd-field.js +5 -5
  30. package/dist/collection/components/form/mrd-form/mrd-form.js +55 -6
  31. package/dist/collection/components/invoice/mrd-invoice-view/mrd-invoice-view.css +145 -0
  32. package/dist/collection/components/invoice/mrd-invoice-view/mrd-invoice-view.js +281 -0
  33. package/dist/collection/components/layout/mrd-layout-section/field-value.js +5 -1
  34. package/dist/collection/components/layout/mrd-layout-section/mrd-layout-section.css +10 -0
  35. package/dist/collection/components/layout/mrd-layout-section/mrd-layout-section.js +38 -8
  36. package/dist/collection/components/merge/mrd-merge-view/mrd-merge-view.js +2 -2
  37. package/dist/collection/dev/api.js +20 -5
  38. package/dist/collection/dev/app.js +51 -13
  39. package/dist/collection/dev/example-data.js +126 -2
  40. package/dist/collection/types/client-layout.js +6 -0
  41. package/dist/collection/utils/document-attachments.js +27 -14
  42. package/dist/collection/utils/i18n.js +120 -0
  43. package/dist/components/cell-renderer.js +1 -1
  44. package/dist/components/client-layout.js +1 -1
  45. package/dist/components/document-attachments.js +1 -1
  46. package/dist/components/file-upload-common.js +1 -1
  47. package/dist/components/format.js +1 -1
  48. package/dist/components/i18n.js +1 -1
  49. package/dist/components/index.js +1 -1
  50. package/dist/components/mrd-boolean-field2.js +1 -1
  51. package/dist/components/mrd-currency-field2.js +1 -1
  52. package/dist/components/mrd-date-field2.js +1 -1
  53. package/dist/components/mrd-datetime-field2.js +1 -1
  54. package/dist/components/mrd-document-view2.js +1 -1
  55. package/dist/components/mrd-email-field2.js +1 -1
  56. package/dist/components/mrd-email-view.d.ts +11 -0
  57. package/dist/components/mrd-email-view.js +1 -0
  58. package/dist/components/mrd-email-view2.js +1 -0
  59. package/dist/components/mrd-field2.js +1 -1
  60. package/dist/components/mrd-file-field2.js +1 -1
  61. package/dist/components/mrd-form.js +1 -1
  62. package/dist/components/mrd-image-field2.js +1 -1
  63. package/dist/components/mrd-invoice-view.d.ts +11 -0
  64. package/dist/components/mrd-invoice-view.js +1 -0
  65. package/dist/components/mrd-invoice-view2.js +1 -0
  66. package/dist/components/mrd-layout-section.js +1 -1
  67. package/dist/components/mrd-longtext-field2.js +1 -1
  68. package/dist/components/mrd-merge-view.js +1 -1
  69. package/dist/components/mrd-number-field2.js +1 -1
  70. package/dist/components/mrd-secret-field2.js +1 -1
  71. package/dist/components/mrd-table2.js +1 -1
  72. package/dist/components/mrd-text-field2.js +1 -1
  73. package/dist/components/mrd-textarea-field2.js +1 -2
  74. package/dist/components/mrd-time-field2.js +1 -1
  75. package/dist/components/purify.es.js +2 -0
  76. package/dist/esm/{cell-renderer-D63AUZxa.js → cell-renderer-DIsNX9D7.js} +122 -2
  77. package/dist/esm/{client-layout-DCOAAH92.js → client-layout-Bc42CfJB.js} +7 -1
  78. package/dist/esm/{index-DiSe6vjv.js → index-CDiBvvJU.js} +468 -6
  79. package/dist/esm/index.js +1 -1
  80. package/dist/esm/loader.js +3 -3
  81. package/dist/esm/mosterdcomponents.js +3 -3
  82. package/dist/esm/{mrd-boolean-field_23.entry.js → mrd-boolean-field_25.entry.js} +5617 -5226
  83. package/dist/esm/mrd-merge-view.entry.js +5 -5
  84. package/dist/mosterdcomponents/index.esm.js +1 -1
  85. package/dist/mosterdcomponents/mosterdcomponents.esm.js +1 -1
  86. package/dist/mosterdcomponents/{p-2e16a7d0.entry.js → p-4e061416.entry.js} +1 -1
  87. package/dist/mosterdcomponents/p-910ebfee.entry.js +3 -0
  88. package/dist/mosterdcomponents/{p-DCOAAH92.js → p-Bc42CfJB.js} +1 -1
  89. package/dist/mosterdcomponents/p-CDiBvvJU.js +2 -0
  90. package/dist/mosterdcomponents/p-Ce2QDq0t.js +1 -0
  91. package/dist/types/components/documents/mrd-document-view/mrd-document-view.d.ts +2 -2
  92. package/dist/types/components/email/mrd-email-view/mrd-email-view.d.ts +52 -0
  93. package/dist/types/components/fields/file-upload-common.d.ts +19 -1
  94. package/dist/types/components/fields/mrd-file-field/mrd-file-field.d.ts +24 -10
  95. package/dist/types/components/fields/mrd-image-field/mrd-image-field.d.ts +27 -11
  96. package/dist/types/components/form/mrd-form/mrd-form.d.ts +16 -1
  97. package/dist/types/components/invoice/mrd-invoice-view/mrd-invoice-view.d.ts +60 -0
  98. package/dist/types/components/layout/mrd-layout-section/mrd-layout-section.d.ts +13 -0
  99. package/dist/types/components.d.ts +294 -19
  100. package/dist/types/types/client-layout.d.ts +6 -0
  101. package/dist/types/utils/document-attachments.d.ts +12 -5
  102. package/package.json +1 -1
  103. package/dist/mosterdcomponents/p-CnqDM0Gb.js +0 -1
  104. package/dist/mosterdcomponents/p-DiSe6vjv.js +0 -2
  105. package/dist/mosterdcomponents/p-b5f38707.entry.js +0 -3
@@ -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]: '' });
@@ -269,6 +293,20 @@ export class MrdForm {
269
293
  normalizeFieldValue(value) {
270
294
  return (value === '' || value == null) ? null : value;
271
295
  }
296
+ /** FILE/IMAGE entries render as `{ href, fileName, contentType?, size? }` — the same
297
+ * shape the API returns for an already-persisted file — or a bare href string for
298
+ * freshly-resolved uploads. Only `href` is meaningful to the API, so unwrap before
299
+ * submit (mirrors normalizeRelationValue). Do NOT fall back to any other key (e.g.
300
+ * `id`) as a stand-in for `href` — a bare id is not a valid/complete reference on its
301
+ * own; if `href` is ever missing that's an API bug to fix upstream, not to paper over here. */
302
+ normalizeFileFieldValue(value) {
303
+ const toHref = (v) => v && typeof v === 'object' && 'href' in v ? v.href : v;
304
+ if (Array.isArray(value)) {
305
+ const hrefs = value.map(toHref).filter(v => v != null && v !== '');
306
+ return hrefs.length ? hrefs : null;
307
+ }
308
+ return this.normalizeFieldValue(toHref(value));
309
+ }
272
310
  normalizeRelationValue(value) {
273
311
  if (value == null || value === '')
274
312
  return null;
@@ -315,8 +353,11 @@ export class MrdForm {
315
353
  const rawValue = this.formValues[name];
316
354
  if (rawValue instanceof File)
317
355
  continue;
318
- const current = this.normalizeFieldValue(rawValue);
319
- const initial = this.normalizeFieldValue(this.initialValues[name]);
356
+ if (Array.isArray(rawValue) && rawValue.some(v => v instanceof File))
357
+ continue;
358
+ const isFileField = item.dataType === ClientLayoutItemFieldDataType.FILE || item.dataType === ClientLayoutItemFieldDataType.IMAGE;
359
+ const current = isFileField ? this.normalizeFileFieldValue(rawValue) : this.normalizeFieldValue(rawValue);
360
+ const initial = isFileField ? this.normalizeFileFieldValue(this.initialValues[name]) : this.normalizeFieldValue(this.initialValues[name]);
320
361
  if (this.deepEqual(current, initial))
321
362
  continue;
322
363
  payload[name] = current;
@@ -619,7 +660,7 @@ export class MrdForm {
619
660
  return {
620
661
  "setFieldValue": {
621
662
  "complexType": {
622
- "signature": "(name: string, value: unknown) => Promise<void>",
663
+ "signature": "(name: string, value: unknown, file?: File) => Promise<void>",
623
664
  "parameters": [{
624
665
  "name": "name",
625
666
  "type": "string",
@@ -628,17 +669,25 @@ export class MrdForm {
628
669
  "name": "value",
629
670
  "type": "unknown",
630
671
  "docs": ""
672
+ }, {
673
+ "name": "file",
674
+ "type": "File | undefined",
675
+ "docs": ""
631
676
  }],
632
677
  "references": {
633
678
  "Promise": {
634
679
  "location": "global",
635
680
  "id": "global::Promise"
681
+ },
682
+ "File": {
683
+ "location": "global",
684
+ "id": "global::File"
636
685
  }
637
686
  },
638
687
  "return": "Promise<void>"
639
688
  },
640
689
  "docs": {
641
- "text": "",
690
+ "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
691
  "tags": []
643
692
  }
644
693
  }
@@ -0,0 +1,145 @@
1
+ :host {
2
+ display: block;
3
+ }
4
+
5
+ .mrd-invoice-view {
6
+ max-width: 720px;
7
+ display: flex;
8
+ flex-direction: column;
9
+ gap: var(--mrd-space-6);
10
+ }
11
+ .mrd-invoice-view__title-row {
12
+ display: flex;
13
+ align-items: center;
14
+ gap: var(--mrd-space-3);
15
+ flex-wrap: wrap;
16
+ }
17
+ .mrd-invoice-view__title {
18
+ font-size: var(--mrd-font-size-2xl);
19
+ font-weight: var(--mrd-font-weight-bold);
20
+ color: var(--mrd-color-neutral-900);
21
+ letter-spacing: -0.02em;
22
+ margin: 0;
23
+ }
24
+ .mrd-invoice-view__badge {
25
+ display: inline-block;
26
+ font-size: var(--mrd-font-size-xs);
27
+ font-weight: var(--mrd-font-weight-medium);
28
+ padding: 2px var(--mrd-space-3);
29
+ border-radius: var(--mrd-border-radius-full);
30
+ }
31
+ .mrd-invoice-view__badge--plain {
32
+ color: var(--mrd-color-neutral-600);
33
+ background: var(--mrd-color-neutral-100);
34
+ }
35
+ .mrd-invoice-view__dates {
36
+ display: flex;
37
+ gap: var(--mrd-space-4);
38
+ font-size: var(--mrd-font-size-sm);
39
+ color: var(--mrd-color-neutral-400);
40
+ font-variant-numeric: tabular-nums;
41
+ margin-top: var(--mrd-space-1);
42
+ }
43
+ .mrd-invoice-view__container-link {
44
+ display: inline-block;
45
+ margin-top: var(--mrd-space-3);
46
+ background: none;
47
+ border: none;
48
+ padding: 0;
49
+ font-family: var(--mrd-font-family);
50
+ font-size: var(--mrd-font-size-sm);
51
+ font-weight: var(--mrd-font-weight-semibold);
52
+ color: var(--mrd-color-primary);
53
+ cursor: pointer;
54
+ text-align: left;
55
+ }
56
+ .mrd-invoice-view__container-link:hover {
57
+ text-decoration: underline;
58
+ }
59
+ .mrd-invoice-view__parties {
60
+ display: flex;
61
+ gap: var(--mrd-space-8);
62
+ flex-wrap: wrap;
63
+ }
64
+ .mrd-invoice-view__party {
65
+ display: flex;
66
+ flex-direction: column;
67
+ gap: var(--mrd-space-1);
68
+ }
69
+ .mrd-invoice-view__party-label {
70
+ font-size: var(--mrd-font-size-xs);
71
+ font-weight: var(--mrd-font-weight-medium);
72
+ color: var(--mrd-color-neutral-400);
73
+ text-transform: uppercase;
74
+ letter-spacing: 0.04em;
75
+ }
76
+ .mrd-invoice-view__link {
77
+ background: none;
78
+ border: none;
79
+ padding: 0;
80
+ font-family: var(--mrd-font-family);
81
+ font-size: var(--mrd-font-size-sm);
82
+ font-weight: var(--mrd-font-weight-semibold);
83
+ color: var(--mrd-color-primary);
84
+ cursor: pointer;
85
+ text-align: left;
86
+ }
87
+ .mrd-invoice-view__link:hover {
88
+ text-decoration: underline;
89
+ }
90
+ .mrd-invoice-view__lines {
91
+ border-top: var(--mrd-border-width) solid var(--mrd-color-neutral-200);
92
+ border-bottom: var(--mrd-border-width) solid var(--mrd-color-neutral-200);
93
+ padding: var(--mrd-space-4) 0;
94
+ }
95
+ .mrd-invoice-view__amounts {
96
+ align-self: flex-end;
97
+ display: flex;
98
+ flex-direction: column;
99
+ gap: var(--mrd-space-2);
100
+ min-width: 16rem;
101
+ }
102
+ .mrd-invoice-view__amount-row {
103
+ display: flex;
104
+ justify-content: space-between;
105
+ gap: var(--mrd-space-6);
106
+ font-size: var(--mrd-font-size-sm);
107
+ color: var(--mrd-color-neutral-700);
108
+ }
109
+ .mrd-invoice-view__amount-row--total {
110
+ padding-top: var(--mrd-space-2);
111
+ border-top: var(--mrd-border-width) solid var(--mrd-color-neutral-200);
112
+ font-size: var(--mrd-font-size-base);
113
+ font-weight: var(--mrd-font-weight-bold);
114
+ color: var(--mrd-color-neutral-900);
115
+ }
116
+ .mrd-invoice-view__attachments {
117
+ display: flex;
118
+ flex-direction: column;
119
+ gap: var(--mrd-space-2);
120
+ }
121
+ .mrd-invoice-view__attachment {
122
+ display: inline-flex;
123
+ align-items: center;
124
+ gap: var(--mrd-space-2);
125
+ align-self: flex-start;
126
+ height: 2rem;
127
+ padding: 0 var(--mrd-space-3);
128
+ background: var(--mrd-color-white);
129
+ border: 1px solid var(--mrd-color-neutral-300);
130
+ border-radius: var(--mrd-border-radius);
131
+ font-family: var(--mrd-font-family);
132
+ font-size: var(--mrd-font-size-sm);
133
+ font-weight: var(--mrd-font-weight-medium);
134
+ color: var(--mrd-color-neutral-700);
135
+ cursor: pointer;
136
+ }
137
+ .mrd-invoice-view__attachment:hover {
138
+ background: var(--mrd-color-neutral-100);
139
+ color: var(--mrd-color-neutral-900);
140
+ }
141
+ .mrd-invoice-view__attachment-icon {
142
+ width: 0.9375rem;
143
+ height: 0.9375rem;
144
+ flex-shrink: 0;
145
+ }
@@ -0,0 +1,281 @@
1
+ import { Host, h } from "@stencil/core";
2
+ import { t } from "../../../utils/i18n";
3
+ import { formatCurrency, formatDate } from "../../../utils/format";
4
+ import { attachmentsFromRecord, attachmentsKey } from "../../../utils/document-attachments";
5
+ /**
6
+ * Archetype-driven single-invoice view. Given a `commons.invoice` binding and a data
7
+ * record, renders a clear, recognisable invoice summary — number, status, dates,
8
+ * customer/supplier, totals and attachments — instead of a generic field form.
9
+ *
10
+ * The line items (`lines` slot, the `commons.invoiceLine` sub-archetype) are NOT
11
+ * rendered here: this component only reserves a `slot="lines"` at the right spot in
12
+ * the layout. The host (`mrd-layout-section`) still owns all the actual RELATED_VIEW
13
+ * table data-loading/event wiring — via its existing `renderRelatedView()` — and
14
+ * projects the resulting `<mrd-table>` into that slot, so line editing keeps using
15
+ * the same generic create/edit-navigation mechanism every other relation already has.
16
+ *
17
+ * Like `mrd-document-view`, this component never resolves file hrefs itself — it only
18
+ * exposes the attachment references via `mrdAttachments` (now possibly more than one)
19
+ * and a plain download button; the host decides how/whether to render them.
20
+ */
21
+ export class MrdInvoiceView {
22
+ constructor() {
23
+ /** The data record from the /data endpoint. Fields keyed by name; relations under `_links`. */
24
+ this.data = {};
25
+ this.locale = navigator.language;
26
+ /** listItems of the FIELD bound to the `status` slot, resolved by mrd-layout-section
27
+ * from its own items array (archetype metadata does not publish listItems), so the
28
+ * status badge can get a real label/color instead of the raw stored key. */
29
+ this.statusListItems = null;
30
+ /** Tracks the key attachments were last emitted for, so componentDidLoad and the
31
+ * data watcher don't emit duplicate events for the same set of files. */
32
+ this.lastAttachmentKey = null;
33
+ }
34
+ componentDidLoad() {
35
+ this.emitAttachmentsIfNeeded();
36
+ }
37
+ dataChanged() {
38
+ this.emitAttachmentsIfNeeded();
39
+ }
40
+ emitAttachmentsIfNeeded() {
41
+ const attachments = attachmentsFromRecord(this.archetype, this.data, 'attachments');
42
+ const key = attachmentsKey(attachments);
43
+ if (key === this.lastAttachmentKey)
44
+ return;
45
+ this.lastAttachmentKey = key;
46
+ this.mrdAttachments.emit({ attachments });
47
+ }
48
+ /** Value of a value-slot: reads data[field] via the slot→field map. */
49
+ slotValue(slot) {
50
+ var _a, _b;
51
+ const field = (_b = (_a = this.archetype) === null || _a === void 0 ? void 0 : _a.slots) === null || _b === void 0 ? void 0 : _b[slot];
52
+ if (!field)
53
+ return undefined;
54
+ return this.data[field];
55
+ }
56
+ /** Relation link for a relation-slot: reads data._links[field]. */
57
+ slotLink(slot) {
58
+ var _a, _b, _c, _d;
59
+ const field = (_b = (_a = this.archetype) === null || _a === void 0 ? void 0 : _a.slots) === null || _b === void 0 ? void 0 : _b[slot];
60
+ if (!field)
61
+ return undefined;
62
+ const links = ((_d = (_c = this.data) === null || _c === void 0 ? void 0 : _c._links) !== null && _d !== void 0 ? _d : {});
63
+ return links[field];
64
+ }
65
+ renderPartyLink(slot, label) {
66
+ const link = this.slotLink(slot);
67
+ if (!(link === null || link === void 0 ? void 0 : link.href) || !link.name)
68
+ return null;
69
+ return (h("div", { class: "mrd-invoice-view__party" }, h("span", { class: "mrd-invoice-view__party-label" }, label), h("button", { type: "button", class: "mrd-invoice-view__link", onClick: () => this.mrdNavigate.emit({ href: link.href, label: link.name }) }, link.name)));
70
+ }
71
+ renderContainer() {
72
+ const link = this.slotLink('container');
73
+ if (!(link === null || link === void 0 ? void 0 : link.href) || !link.name)
74
+ return null;
75
+ return (h("button", { type: "button", class: "mrd-invoice-view__container-link", onClick: () => this.mrdNavigate.emit({ href: link.href, label: link.name }) }, link.name));
76
+ }
77
+ renderStatus() {
78
+ var _a, _b;
79
+ const statusRaw = this.slotValue('status');
80
+ if (statusRaw == null || statusRaw === '')
81
+ return null;
82
+ const listItem = ((_a = this.statusListItems) !== null && _a !== void 0 ? _a : []).find(li => li.key === String(statusRaw));
83
+ const label = (_b = listItem === null || listItem === void 0 ? void 0 : listItem.label) !== null && _b !== void 0 ? _b : String(statusRaw);
84
+ if ((listItem === null || listItem === void 0 ? void 0 : listItem.color) && (listItem === null || listItem === void 0 ? void 0 : listItem.backgroundColor)) {
85
+ return (h("span", { class: "mrd-invoice-view__badge", style: { color: listItem.color, backgroundColor: listItem.backgroundColor } }, label));
86
+ }
87
+ return h("span", { class: "mrd-invoice-view__badge mrd-invoice-view__badge--plain" }, label);
88
+ }
89
+ renderAmounts() {
90
+ const net = this.slotValue('netAmount');
91
+ const tax = this.slotValue('taxAmount');
92
+ const total = this.slotValue('amount');
93
+ if ((net === null || net === void 0 ? void 0 : net.amount) == null && (tax === null || tax === void 0 ? void 0 : tax.amount) == null && (total === null || total === void 0 ? void 0 : total.amount) == null)
94
+ return null;
95
+ return (h("div", { class: "mrd-invoice-view__amounts" }, (net === null || net === void 0 ? void 0 : net.amount) != null && (h("div", { class: "mrd-invoice-view__amount-row" }, h("span", null, t('invoice_subtotal', this.locale)), h("span", null, formatCurrency(net.amount, net.currency, this.locale)))), (tax === null || tax === void 0 ? void 0 : tax.amount) != null && (h("div", { class: "mrd-invoice-view__amount-row" }, h("span", null, t('invoice_tax', this.locale)), h("span", null, formatCurrency(tax.amount, tax.currency, this.locale)))), (total === null || total === void 0 ? void 0 : total.amount) != null && (h("div", { class: "mrd-invoice-view__amount-row mrd-invoice-view__amount-row--total" }, h("span", null, t('invoice_total', this.locale)), h("span", null, formatCurrency(total.amount, total.currency, this.locale))))));
96
+ }
97
+ renderAttachments() {
98
+ const attachments = attachmentsFromRecord(this.archetype, this.data, 'attachments');
99
+ if (!attachments.length)
100
+ return null;
101
+ return (h("div", { class: "mrd-invoice-view__attachments" }, attachments.map(a => (h("button", { type: "button", key: a.href, class: "mrd-invoice-view__attachment", onClick: () => this.mrdDownload.emit({ href: a.href, fileName: a.fileName }) }, h("svg", { class: "mrd-invoice-view__attachment-icon", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", "stroke-width": "2", "stroke-linecap": "round", "stroke-linejoin": "round", "aria-hidden": "true" }, h("path", { d: "M21.44 11.05l-9.19 9.19a5 5 0 01-7.07-7.07l9.19-9.19a3 3 0 014.24 4.24l-9.2 9.19a1 1 0 01-1.41-1.41l8.49-8.48" })), a.fileName)))));
102
+ }
103
+ render() {
104
+ var _a, _b;
105
+ const invoiceNumberRaw = this.slotValue('invoiceNumber');
106
+ const invoiceNumber = invoiceNumberRaw != null && invoiceNumberRaw !== '' ? String(invoiceNumberRaw) : null;
107
+ const invoiceDateRaw = this.slotValue('invoiceDate');
108
+ const dueDateRaw = this.slotValue('dueDate');
109
+ const hasLines = !!((_b = (_a = this.archetype) === null || _a === void 0 ? void 0 : _a.slots) === null || _b === void 0 ? void 0 : _b['lines']);
110
+ return (h(Host, { key: 'a97c51e53e39ace913c902ad2ba6813eb85e4561' }, h("div", { key: '6938267848bceed5b28b56c3cdd6d6693e57cdbf', class: "mrd-invoice-view" }, h("div", { key: '7a0b3346232abfc1e35805e4e92c19d35698f881', class: "mrd-invoice-view__header" }, h("div", { key: '5c0471dd8ebd94dd9b374897268c7c03bec66ec7', class: "mrd-invoice-view__title-row" }, invoiceNumber && h("h1", { key: '86b4c0ebd0a5b83ae03b731b7e41394645aae954', class: "mrd-invoice-view__title" }, t('invoice_title', this.locale), " ", invoiceNumber), this.renderStatus()), h("div", { key: 'd209bc34d989146b5f510f2bcdc4c484d723378c', class: "mrd-invoice-view__dates" }, invoiceDateRaw != null && invoiceDateRaw !== '' && (h("span", { key: '8fffcd1ab10e511c9d939c351ea93e4d8a902688' }, t('invoice_date', this.locale), ": ", formatDate(invoiceDateRaw, this.locale))), dueDateRaw != null && dueDateRaw !== '' && (h("span", { key: '8d13c6660798108cc2f54137c8415173326d5bf2' }, t('invoice_due_date', this.locale), ": ", formatDate(dueDateRaw, this.locale)))), this.renderContainer()), h("div", { key: 'cb08c6fd7e6b46a46a69f11aafa960ab5cb35544', class: "mrd-invoice-view__parties" }, this.renderPartyLink('customer', t('invoice_customer', this.locale)), this.renderPartyLink('supplier', t('invoice_supplier', this.locale))), hasLines && (h("div", { key: '8321e74171fe933a4e10fc202b66cd3720ba959e', class: "mrd-invoice-view__lines" }, h("slot", { key: 'eb31d97080173413e74e5b0024984a4b06aa5bdd', name: "lines" }))), this.renderAmounts(), this.renderAttachments())));
111
+ }
112
+ static get is() { return "mrd-invoice-view"; }
113
+ static get encapsulation() { return "scoped"; }
114
+ static get originalStyleUrls() {
115
+ return {
116
+ "$": ["mrd-invoice-view.scss"]
117
+ };
118
+ }
119
+ static get styleUrls() {
120
+ return {
121
+ "$": ["mrd-invoice-view.css"]
122
+ };
123
+ }
124
+ static get properties() {
125
+ return {
126
+ "archetype": {
127
+ "type": "unknown",
128
+ "mutable": false,
129
+ "complexType": {
130
+ "original": "ClientArchetype",
131
+ "resolved": "ClientArchetype",
132
+ "references": {
133
+ "ClientArchetype": {
134
+ "location": "import",
135
+ "path": "../../../types/client-layout",
136
+ "id": "src/types/client-layout.ts::ClientArchetype",
137
+ "referenceLocation": "ClientArchetype"
138
+ }
139
+ }
140
+ },
141
+ "required": true,
142
+ "optional": false,
143
+ "docs": {
144
+ "tags": [],
145
+ "text": "Resolved `commons.invoice` binding: slot name \u2192 concrete field/relation name."
146
+ },
147
+ "getter": false,
148
+ "setter": false
149
+ },
150
+ "data": {
151
+ "type": "unknown",
152
+ "mutable": false,
153
+ "complexType": {
154
+ "original": "Record<string, unknown>",
155
+ "resolved": "string | unknown",
156
+ "references": {
157
+ "Record": {
158
+ "location": "global",
159
+ "id": "global::Record"
160
+ }
161
+ }
162
+ },
163
+ "required": false,
164
+ "optional": false,
165
+ "docs": {
166
+ "tags": [],
167
+ "text": "The data record from the /data endpoint. Fields keyed by name; relations under `_links`."
168
+ },
169
+ "getter": false,
170
+ "setter": false,
171
+ "defaultValue": "{}"
172
+ },
173
+ "locale": {
174
+ "type": "string",
175
+ "mutable": false,
176
+ "complexType": {
177
+ "original": "string",
178
+ "resolved": "string",
179
+ "references": {}
180
+ },
181
+ "required": false,
182
+ "optional": false,
183
+ "docs": {
184
+ "tags": [],
185
+ "text": ""
186
+ },
187
+ "getter": false,
188
+ "setter": false,
189
+ "reflect": false,
190
+ "attribute": "locale",
191
+ "defaultValue": "navigator.language"
192
+ },
193
+ "statusListItems": {
194
+ "type": "unknown",
195
+ "mutable": false,
196
+ "complexType": {
197
+ "original": "ClientListValue[] | null",
198
+ "resolved": "ClientListValue[] | null",
199
+ "references": {
200
+ "ClientListValue": {
201
+ "location": "import",
202
+ "path": "../../../types/client-layout",
203
+ "id": "src/types/client-layout.ts::ClientListValue",
204
+ "referenceLocation": "ClientListValue"
205
+ }
206
+ }
207
+ },
208
+ "required": false,
209
+ "optional": false,
210
+ "docs": {
211
+ "tags": [],
212
+ "text": "listItems of the FIELD bound to the `status` slot, resolved by mrd-layout-section\nfrom its own items array (archetype metadata does not publish listItems), so the\nstatus badge can get a real label/color instead of the raw stored key."
213
+ },
214
+ "getter": false,
215
+ "setter": false,
216
+ "defaultValue": "null"
217
+ }
218
+ };
219
+ }
220
+ static get events() {
221
+ return [{
222
+ "method": "mrdNavigate",
223
+ "name": "mrdNavigate",
224
+ "bubbles": true,
225
+ "cancelable": true,
226
+ "composed": true,
227
+ "docs": {
228
+ "tags": [],
229
+ "text": "Navigate to the customer/supplier/container. Host resolves the href and routes."
230
+ },
231
+ "complexType": {
232
+ "original": "{ href?: string; label: string }",
233
+ "resolved": "{ href?: string | undefined; label: string; }",
234
+ "references": {}
235
+ }
236
+ }, {
237
+ "method": "mrdDownload",
238
+ "name": "mrdDownload",
239
+ "bubbles": true,
240
+ "cancelable": true,
241
+ "composed": true,
242
+ "docs": {
243
+ "tags": [],
244
+ "text": "Download an attachment. Host resolves the href to a signed URL."
245
+ },
246
+ "complexType": {
247
+ "original": "{ href: string; fileName: string }",
248
+ "resolved": "{ href: string; fileName: string; }",
249
+ "references": {}
250
+ }
251
+ }, {
252
+ "method": "mrdAttachments",
253
+ "name": "mrdAttachments",
254
+ "bubbles": true,
255
+ "cancelable": true,
256
+ "composed": true,
257
+ "docs": {
258
+ "tags": [],
259
+ "text": "The file(s) belonging to this invoice, fired once on load and again whenever the\nattachments slot's value changes. The host resolves the href(s) itself."
260
+ },
261
+ "complexType": {
262
+ "original": "{ attachments: DocumentAttachment[] }",
263
+ "resolved": "{ attachments: DocumentAttachment[]; }",
264
+ "references": {
265
+ "DocumentAttachment": {
266
+ "location": "import",
267
+ "path": "../../../types/client-layout",
268
+ "id": "src/types/client-layout.ts::DocumentAttachment",
269
+ "referenceLocation": "DocumentAttachment"
270
+ }
271
+ }
272
+ }
273
+ }];
274
+ }
275
+ static get watchers() {
276
+ return [{
277
+ "propName": "data",
278
+ "methodName": "dataChanged"
279
+ }];
280
+ }
281
+ }
@@ -27,7 +27,7 @@ function renderSingleFieldValue(ctx, item, value) {
27
27
  const v = value;
28
28
  const fileName = (_e = v === null || v === void 0 ? void 0 : v.fileName) !== null && _e !== void 0 ? _e : String(value);
29
29
  const href = (_f = v === null || v === void 0 ? void 0 : v.href) !== null && _f !== void 0 ? _f : '';
30
- return downloadLink(ctx, href, fileName, t('download', ctx.locale));
30
+ return downloadLink(ctx, href, fileName, fileName || t('download', ctx.locale));
31
31
  }
32
32
  case ClientLayoutItemFieldDataType.IMAGE: {
33
33
  const v = value;
@@ -66,6 +66,10 @@ export function renderFieldValue(ctx, item, rawValue) {
66
66
  if (rawValue == null || rawValue === '')
67
67
  return null;
68
68
  if (item.multiple && Array.isArray(rawValue)) {
69
+ const isFileType = item.dataType === ClientLayoutItemFieldDataType.FILE || item.dataType === ClientLayoutItemFieldDataType.IMAGE;
70
+ if (isFileType) {
71
+ return (h("div", { class: "mrd-layout-section__file-list" }, rawValue.map((v, i) => (h("div", { class: "mrd-layout-section__file-list-item", key: String(i) }, renderSingleFieldValue(ctx, item, v))))));
72
+ }
69
73
  const rendered = rawValue.map(v => renderSingleFieldValue(ctx, item, v));
70
74
  if (rendered.every(r => typeof r === 'string' || r == null)) {
71
75
  return rendered.filter(Boolean).join(', ') || null;
@@ -170,6 +170,16 @@
170
170
  width: 1rem;
171
171
  height: 1rem;
172
172
  }
173
+ .mrd-layout-section__file-list {
174
+ display: flex;
175
+ flex-direction: column;
176
+ align-items: flex-start;
177
+ gap: var(--mrd-space-1);
178
+ }
179
+ .mrd-layout-section__file-list-item {
180
+ min-width: 0;
181
+ max-width: 100%;
182
+ }
173
183
  .mrd-layout-section__boolean--true {
174
184
  color: var(--mrd-color-success);
175
185
  font-weight: var(--mrd-font-weight-semibold);