@mmlogic/components 0.5.15 → 0.5.17

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 (40) hide show
  1. package/dist/cjs/loader.cjs.js +1 -1
  2. package/dist/cjs/mosterdcomponents.cjs.js +1 -1
  3. package/dist/cjs/mrd-boolean-field_28.cjs.entry.js +361 -56
  4. package/dist/collection/collection-manifest.json +2 -2
  5. package/dist/collection/components/documents/mrd-document-container/mrd-document-container.js +31 -4
  6. package/dist/collection/components/form/mrd-field/mrd-field.css +69 -0
  7. package/dist/collection/components/form/mrd-field/mrd-field.js +95 -25
  8. package/dist/collection/components/layout/mrd-layout-section/field-value.js +19 -2
  9. package/dist/collection/components/layout/mrd-layout-section/layout-helpers.js +10 -0
  10. package/dist/collection/components/layout/mrd-layout-section/mrd-layout-section.css +4 -3
  11. package/dist/collection/components/layout/mrd-layout-section/mrd-layout-section.js +174 -17
  12. package/dist/collection/components/memo/mrd-memo-container/mrd-memo-container.js +105 -6
  13. package/dist/collection/components/memo/mrd-memo-list/mrd-memo-list.css +14 -0
  14. package/dist/collection/components/memo/mrd-memo-list/mrd-memo-list.js +50 -2
  15. package/dist/collection/components/table/mrd-table/mrd-table.js +105 -2
  16. package/dist/collection/dev/api.js +17 -0
  17. package/dist/collection/dev/app.js +49 -15
  18. package/dist/collection/dev/example-data.js +15 -1
  19. package/dist/components/mrd-document-container2.js +1 -1
  20. package/dist/components/mrd-field2.js +1 -1
  21. package/dist/components/mrd-layout-section.js +1 -1
  22. package/dist/components/mrd-memo-container2.js +1 -1
  23. package/dist/components/mrd-memo-list2.js +1 -1
  24. package/dist/components/mrd-table2.js +1 -1
  25. package/dist/esm/loader.js +1 -1
  26. package/dist/esm/mosterdcomponents.js +1 -1
  27. package/dist/esm/mrd-boolean-field_28.entry.js +361 -56
  28. package/dist/mosterdcomponents/mosterdcomponents.esm.js +1 -1
  29. package/dist/mosterdcomponents/p-f72668e9.entry.js +3 -0
  30. package/dist/types/components/documents/mrd-document-container/mrd-document-container.d.ts +7 -0
  31. package/dist/types/components/form/mrd-field/mrd-field.d.ts +16 -0
  32. package/dist/types/components/layout/mrd-layout-section/field-value.d.ts +4 -0
  33. package/dist/types/components/layout/mrd-layout-section/layout-helpers.d.ts +3 -0
  34. package/dist/types/components/layout/mrd-layout-section/mrd-layout-section.d.ts +52 -0
  35. package/dist/types/components/memo/mrd-memo-container/mrd-memo-container.d.ts +26 -1
  36. package/dist/types/components/memo/mrd-memo-list/mrd-memo-list.d.ts +12 -0
  37. package/dist/types/components/table/mrd-table/mrd-table.d.ts +45 -0
  38. package/dist/types/components.d.ts +92 -0
  39. package/package.json +1 -1
  40. package/dist/mosterdcomponents/p-d64ba1f2.entry.js +0 -3
@@ -2,6 +2,7 @@
2
2
  "entries": [
3
3
  "components/documents/mrd-document-container/mrd-document-container.js",
4
4
  "components/documents/mrd-document-list/mrd-document-list.js",
5
+ "components/table/mrd-table/mrd-table.js",
5
6
  "components/documents/mrd-document-view/mrd-document-view.js",
6
7
  "components/email/mrd-email-view/mrd-email-view.js",
7
8
  "components/fields/mrd-boolean-field/mrd-boolean-field.js",
@@ -27,8 +28,7 @@
27
28
  "components/memo/mrd-memo-container/mrd-memo-container.js",
28
29
  "components/memo/mrd-memo-list/mrd-memo-list.js",
29
30
  "components/memo/mrd-memo-view/mrd-memo-view.js",
30
- "components/merge/mrd-merge-view/mrd-merge-view.js",
31
- "components/table/mrd-table/mrd-table.js"
31
+ "components/merge/mrd-merge-view/mrd-merge-view.js"
32
32
  ],
33
33
  "mixins": [],
34
34
  "compiler": {
@@ -32,6 +32,13 @@ export class MrdDocumentContainer {
32
32
  * (with a tooltip explaining why) and the embedded table/list get it passed
33
33
  * through so their own create/upload/drag-drop paths are disabled too. */
34
34
  this.readOnly = false;
35
+ /** Whether the current user may create a document here — set by mrd-layout-section from
36
+ * its generic capability check (TASK-0299/FINDING-0155), the same answer the embedded
37
+ * table gets in list mode. Named `mayCreate` (not `canCreate`) to avoid colliding with
38
+ * the existing `canCreate` state below, which tracks something unrelated: whether the
39
+ * folder tree currently has a valid drop target selected. Defaults to false (hidden)
40
+ * until confirmed, same as mrd-table. */
41
+ this.mayCreate = false;
35
42
  /** Active body: folder tree or flat table. */
36
43
  this.mode = 'tree';
37
44
  /** Whether the folder tree currently has a valid target for "New folder". */
@@ -46,7 +53,7 @@ export class MrdDocumentContainer {
46
53
  };
47
54
  this.pickFile = () => {
48
55
  var _a;
49
- if (this.readOnly)
56
+ if (this.readOnly || !this.mayCreate)
50
57
  return;
51
58
  (_a = this.fileInputEl) === null || _a === void 0 ? void 0 : _a.click();
52
59
  };
@@ -84,7 +91,7 @@ export class MrdDocumentContainer {
84
91
  }
85
92
  // ── Toolbar ────────────────────────────────────────────────────────────────
86
93
  renderToolbar() {
87
- return (h("div", { class: "mrd-document-container__toolbar" }, this.mode === 'tree' && (h("button", { type: "button", class: "mrd-document-container__action", disabled: this.readOnly || !this.canCreate, title: this.readOnly ? t('readonly_access', this.locale) : undefined, onClick: this.createFolder }, h("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", "stroke-width": "2", "stroke-linecap": "round", "stroke-linejoin": "round", "aria-hidden": "true" }, h("path", { d: "M3 7a2 2 0 0 1 2-2h4l2 2h8a2 2 0 0 1 2 2v8a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z" }), h("path", { d: "M12 11v4M10 13h4" })), t('new_folder', this.locale))), this.mode === 'tree' && (h("button", { type: "button", class: "mrd-document-container__action", disabled: this.readOnly || !this.canCreate, title: this.readOnly ? t('readonly_access', this.locale) : (this.targetLabel ? `${t('doc_upload_to', this.locale)} ${this.targetLabel}` : undefined), onClick: this.pickFile }, h("svg", { 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 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4" }), h("path", { d: "M12 3v13M7 8l5-5 5 5" })), t('doc_upload', this.locale))), h("input", { type: "file", class: "mrd-document-container__file-input", ref: el => (this.fileInputEl = el), onChange: this.onFilePicked }), h("div", { class: "mrd-document-container__seg", role: "group", "aria-label": t('doc_view_switch', this.locale) }, h("button", { class: "mrd-document-container__seg-btn", "aria-pressed": String(this.mode === 'tree'), title: t('doc_view_tree', this.locale), "aria-label": t('doc_view_tree', this.locale), onClick: () => this.setMode('tree') }, h("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", "stroke-width": "2", "stroke-linecap": "round", "stroke-linejoin": "round", "aria-hidden": "true" }, h("path", { d: "M3 5h6M3 12h9M3 19h6" }), h("circle", { cx: "18", cy: "5", r: "1.6" }), h("circle", { cx: "18", cy: "19", r: "1.6" }), h("path", { d: "M16 12h4" }))), h("button", { class: "mrd-document-container__seg-btn", "aria-pressed": String(this.mode === 'list'), title: t('doc_view_list', this.locale), "aria-label": t('doc_view_list', this.locale), onClick: () => this.setMode('list') }, h("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", "stroke-width": "2", "stroke-linecap": "round", "stroke-linejoin": "round", "aria-hidden": "true" }, h("path", { d: "M8 6h13M8 12h13M8 18h13M3 6h.01M3 12h.01M3 18h.01" }))))));
94
+ return (h("div", { class: "mrd-document-container__toolbar" }, this.mode === 'tree' && this.mayCreate && (h("button", { type: "button", class: "mrd-document-container__action", disabled: this.readOnly || !this.canCreate, title: this.readOnly ? t('readonly_access', this.locale) : undefined, onClick: this.createFolder }, h("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", "stroke-width": "2", "stroke-linecap": "round", "stroke-linejoin": "round", "aria-hidden": "true" }, h("path", { d: "M3 7a2 2 0 0 1 2-2h4l2 2h8a2 2 0 0 1 2 2v8a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z" }), h("path", { d: "M12 11v4M10 13h4" })), t('new_folder', this.locale))), this.mode === 'tree' && this.mayCreate && (h("button", { type: "button", class: "mrd-document-container__action", disabled: this.readOnly || !this.canCreate, title: this.readOnly ? t('readonly_access', this.locale) : (this.targetLabel ? `${t('doc_upload_to', this.locale)} ${this.targetLabel}` : undefined), onClick: this.pickFile }, h("svg", { 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 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4" }), h("path", { d: "M12 3v13M7 8l5-5 5 5" })), t('doc_upload', this.locale))), h("input", { type: "file", class: "mrd-document-container__file-input", ref: el => (this.fileInputEl = el), onChange: this.onFilePicked }), h("div", { class: "mrd-document-container__seg", role: "group", "aria-label": t('doc_view_switch', this.locale) }, h("button", { class: "mrd-document-container__seg-btn", "aria-pressed": String(this.mode === 'tree'), title: t('doc_view_tree', this.locale), "aria-label": t('doc_view_tree', this.locale), onClick: () => this.setMode('tree') }, h("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", "stroke-width": "2", "stroke-linecap": "round", "stroke-linejoin": "round", "aria-hidden": "true" }, h("path", { d: "M3 5h6M3 12h9M3 19h6" }), h("circle", { cx: "18", cy: "5", r: "1.6" }), h("circle", { cx: "18", cy: "19", r: "1.6" }), h("path", { d: "M16 12h4" }))), h("button", { class: "mrd-document-container__seg-btn", "aria-pressed": String(this.mode === 'list'), title: t('doc_view_list', this.locale), "aria-label": t('doc_view_list', this.locale), onClick: () => this.setMode('list') }, h("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", "stroke-width": "2", "stroke-linecap": "round", "stroke-linejoin": "round", "aria-hidden": "true" }, h("path", { d: "M8 6h13M8 12h13M8 18h13M3 6h.01M3 12h.01M3 18h.01" }))))));
88
95
  }
89
96
  // ── Bodies ───────────────────────────────────────────────────────────────
90
97
  renderTree() {
@@ -115,7 +122,7 @@ export class MrdDocumentContainer {
115
122
  } }));
116
123
  }
117
124
  renderList() {
118
- return (h("mrd-table", { "data-view": this.viewKey, ref: el => (this.tableEl = el), item: this.item, parentId: this.parentId, locale: this.locale, readOnly: this.readOnly, onMrdLoadPage: (e) => {
125
+ return (h("mrd-table", { "data-view": this.viewKey, ref: el => (this.tableEl = el), item: this.item, parentId: this.parentId, locale: this.locale, readOnly: this.readOnly, canCreate: this.mayCreate, onMrdLoadPage: (e) => {
119
126
  e.stopPropagation();
120
127
  this.mrdLoadPage.emit(e.detail);
121
128
  }, onMrdLoadAggregations: (e) => {
@@ -139,7 +146,7 @@ export class MrdDocumentContainer {
139
146
  }
140
147
  render() {
141
148
  const treeMode = this.mode === 'tree';
142
- return (h(Host, { key: '6792d49f08dd19f6c51e4b656df06bbd8bc14f3e' }, this.renderToolbar(), h("div", { key: '702f4c288f8c40bd2c5999beb392b9dfd022b0bc', class: {
149
+ return (h(Host, { key: '4b674a508ac7ddf6af80a40836b72dc65f41c741' }, this.renderToolbar(), h("div", { key: '19968916f685a2e3cd075dabf170f67fb104251c', class: {
143
150
  'mrd-document-container__body': true,
144
151
  'mrd-document-container__body--tree': treeMode,
145
152
  }, style: treeMode ? { height: `${this.height}px`, overflowY: 'auto' } : {} }, treeMode ? this.renderTree() : this.renderList())));
@@ -326,6 +333,26 @@ export class MrdDocumentContainer {
326
333
  "reflect": false,
327
334
  "attribute": "read-only",
328
335
  "defaultValue": "false"
336
+ },
337
+ "mayCreate": {
338
+ "type": "boolean",
339
+ "mutable": false,
340
+ "complexType": {
341
+ "original": "boolean",
342
+ "resolved": "boolean",
343
+ "references": {}
344
+ },
345
+ "required": false,
346
+ "optional": false,
347
+ "docs": {
348
+ "tags": [],
349
+ "text": "Whether the current user may create a document here \u2014 set by mrd-layout-section from\nits generic capability check (TASK-0299/FINDING-0155), the same answer the embedded\ntable gets in list mode. Named `mayCreate` (not `canCreate`) to avoid colliding with\nthe existing `canCreate` state below, which tracks something unrelated: whether the\nfolder tree currently has a valid drop target selected. Defaults to false (hidden)\nuntil confirmed, same as mrd-table."
350
+ },
351
+ "getter": false,
352
+ "setter": false,
353
+ "reflect": false,
354
+ "attribute": "may-create",
355
+ "defaultValue": "false"
329
356
  }
330
357
  };
331
358
  }
@@ -102,4 +102,73 @@
102
102
  }
103
103
  .mrd-field__history-editor-add:hover {
104
104
  background-color: var(--mrd-color-primary-light);
105
+ }
106
+ .mrd-field__multi {
107
+ display: flex;
108
+ flex-direction: column;
109
+ gap: var(--mrd-space-1);
110
+ }
111
+ .mrd-field__multi__label {
112
+ display: block;
113
+ font-family: var(--mrd-font-family);
114
+ font-size: var(--mrd-label-font-size);
115
+ font-weight: var(--mrd-label-font-weight);
116
+ color: var(--mrd-label-color);
117
+ }
118
+ .mrd-field__multi__label--required::after {
119
+ content: " *";
120
+ color: var(--mrd-color-danger);
121
+ }
122
+ .mrd-field__multi__error {
123
+ font-family: var(--mrd-font-family);
124
+ font-size: var(--mrd-error-font-size);
125
+ color: var(--mrd-error-color);
126
+ }
127
+ .mrd-field__multi-row {
128
+ display: flex;
129
+ align-items: flex-start;
130
+ gap: var(--mrd-space-2);
131
+ }
132
+ .mrd-field__multi-row-input {
133
+ flex: 1 1 0;
134
+ min-width: 0;
135
+ }
136
+ .mrd-field__multi-remove {
137
+ display: inline-flex;
138
+ align-items: center;
139
+ justify-content: center;
140
+ flex-shrink: 0;
141
+ width: 2rem;
142
+ height: var(--mrd-input-height);
143
+ padding: 0;
144
+ background: transparent;
145
+ border: none;
146
+ border-radius: var(--mrd-border-radius-sm);
147
+ color: var(--mrd-color-neutral-400);
148
+ cursor: pointer;
149
+ transition: color var(--mrd-transition-fast), background-color var(--mrd-transition-fast);
150
+ }
151
+ .mrd-field__multi-remove:hover {
152
+ color: var(--mrd-color-danger);
153
+ background-color: var(--mrd-color-danger-light);
154
+ }
155
+ .mrd-field__multi-remove svg {
156
+ width: 1rem;
157
+ height: 1rem;
158
+ }
159
+ .mrd-field__multi-add {
160
+ align-self: flex-start;
161
+ padding: var(--mrd-space-1) var(--mrd-space-3);
162
+ font-family: var(--mrd-font-family);
163
+ font-size: var(--mrd-font-size-xs);
164
+ font-weight: var(--mrd-font-weight-medium);
165
+ color: var(--mrd-color-primary);
166
+ background: transparent;
167
+ border: var(--mrd-border-width) solid var(--mrd-color-primary);
168
+ border-radius: var(--mrd-border-radius-md);
169
+ cursor: pointer;
170
+ transition: background-color var(--mrd-transition-fast), color var(--mrd-transition-fast);
171
+ }
172
+ .mrd-field__multi-add:hover {
173
+ background-color: var(--mrd-color-primary-light);
105
174
  }
@@ -1,11 +1,19 @@
1
1
  import { Host, h } from "@stencil/core";
2
2
  import { ClientLayoutItemType, ClientLayoutItemFieldDataType, ClientLayoutItemRelationDisplayType, } from "../../../types";
3
3
  import { t } from "../../../utils/i18n";
4
+ import { FieldLabel, FieldError, requiredError } from "../../fields/field-helpers";
4
5
  export class MrdField {
5
6
  constructor() {
6
7
  this.locale = navigator.language;
7
8
  this.historyEntries = [];
8
9
  this.currentValue = undefined;
10
+ this.multiValues = [];
11
+ this.multiError = '';
12
+ this.handleMultiRowBlur = () => {
13
+ var _a;
14
+ this.multiError = requiredError(this.multiValues, (_a = this.item.required) !== null && _a !== void 0 ? _a : false, this.locale);
15
+ this.mrdBlur.emit({ name: this.item.name, value: this.multiValues });
16
+ };
9
17
  this.handleChange = (e) => {
10
18
  var _a, _b, _c, _d;
11
19
  e.stopPropagation();
@@ -50,9 +58,11 @@ export class MrdField {
50
58
  }
51
59
  componentWillLoad() {
52
60
  this.initHistoryState();
61
+ this.initMultiState();
53
62
  }
54
63
  valueChanged() {
55
64
  this.initHistoryState();
65
+ this.initMultiState();
56
66
  }
57
67
  initHistoryState() {
58
68
  var _a, _b, _c, _d, _e;
@@ -63,6 +73,43 @@ export class MrdField {
63
73
  this.currentValue = (_e = raw === null || raw === void 0 ? void 0 : raw.current) !== null && _e !== void 0 ? _e : raw;
64
74
  this.historyEntries = Array.isArray(raw === null || raw === void 0 ? void 0 : raw.history) ? [...raw.history] : [];
65
75
  }
76
+ /** LIST/FILE/IMAGE already own their multiple-value UI end to end; every
77
+ * other scalar dataType routes through the generic repeater below instead
78
+ * of each leaf field re-implementing add/remove itself (FINDING-0149). */
79
+ isMultiScalar() {
80
+ var _a, _b;
81
+ if (((_a = this.item) === null || _a === void 0 ? void 0 : _a.type) !== ClientLayoutItemType.FIELD || !((_b = this.item) === null || _b === void 0 ? void 0 : _b.multiple))
82
+ return false;
83
+ const dt = this.item.dataType;
84
+ return dt !== ClientLayoutItemFieldDataType.LIST
85
+ && dt !== ClientLayoutItemFieldDataType.FILE
86
+ && dt !== ClientLayoutItemFieldDataType.IMAGE;
87
+ }
88
+ initMultiState() {
89
+ if (!this.isMultiScalar())
90
+ return;
91
+ const v = this.value;
92
+ this.multiValues = Array.isArray(v) ? [...v] : (v != null && v !== '' ? [v] : []);
93
+ }
94
+ emptyRowValue() {
95
+ var _a;
96
+ switch (this.item.dataType) {
97
+ case ClientLayoutItemFieldDataType.INTEGER:
98
+ case ClientLayoutItemFieldDataType.DECIMAL:
99
+ case ClientLayoutItemFieldDataType.PERCENTAGE:
100
+ return null;
101
+ case ClientLayoutItemFieldDataType.CURRENCY:
102
+ return { amount: null, currency: (_a = this.item.currencyCode) !== null && _a !== void 0 ? _a : 'EUR' };
103
+ case ClientLayoutItemFieldDataType.BOOLEAN:
104
+ return false;
105
+ default:
106
+ return '';
107
+ }
108
+ }
109
+ emitMultiChange(values) {
110
+ this.multiValues = values;
111
+ this.mrdChange.emit({ name: this.item.name, value: values });
112
+ }
66
113
  getDisplayValue() {
67
114
  var _a, _b, _c, _d;
68
115
  const hist = (_b = (_a = this.item) === null || _a === void 0 ? void 0 : _a.historyEnabled) !== null && _b !== void 0 ? _b : (_d = (_c = this.item) === null || _c === void 0 ? void 0 : _c.field) === null || _d === void 0 ? void 0 : _d.historyEnabled;
@@ -91,6 +138,9 @@ export class MrdField {
91
138
  value: { current: this.currentValue, history: entries },
92
139
  });
93
140
  }
141
+ renderRemoveIcon() {
142
+ return (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" })));
143
+ }
94
144
  renderHistoryEditor() {
95
145
  var _a, _b, _c, _d;
96
146
  const hist = (_b = (_a = this.item) === null || _a === void 0 ? void 0 : _a.historyEnabled) !== null && _b !== void 0 ? _b : (_d = (_c = this.item) === null || _c === void 0 ? void 0 : _c.field) === null || _d === void 0 ? void 0 : _d.historyEnabled;
@@ -108,62 +158,80 @@ export class MrdField {
108
158
  const addEntry = () => {
109
159
  this.emitHistoryChange([...this.historyEntries, { value: '', until: '' }]);
110
160
  };
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))));
161
+ 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) }, this.renderRemoveIcon())))), h("button", { type: "button", class: "mrd-field__history-editor-add", onClick: addEntry }, "+ ", t('add', locale))));
112
162
  }
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, _5, _6;
163
+ renderLeafField(displayValue, onChange = this.handleChange, onBlur = this.handleBlur, overrides = {}) {
164
+ 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, _7, _8;
115
165
  const { item, locale } = this;
116
166
  const commonProps = {
117
167
  name: item.name,
118
- label: item.label,
119
- required: item.required,
120
- disabled: (_a = item.disabled) !== null && _a !== void 0 ? _a : false,
168
+ label: (_a = overrides.label) !== null && _a !== void 0 ? _a : item.label,
169
+ required: (_b = overrides.required) !== null && _b !== void 0 ? _b : item.required,
170
+ disabled: (_c = item.disabled) !== null && _c !== void 0 ? _c : false,
121
171
  locale,
122
- onMrdChange: this.handleChange,
123
- onMrdBlur: this.handleBlur,
172
+ onMrdChange: onChange,
173
+ onMrdBlur: onBlur,
124
174
  };
125
175
  switch (item.dataType) {
126
176
  case ClientLayoutItemFieldDataType.TEXT:
127
- return (h("mrd-text-field", Object.assign({}, commonProps, { value: (_b = displayValue) !== null && _b !== void 0 ? _b : '', placeholder: (_c = item.placeholder) !== null && _c !== void 0 ? _c : '' })));
177
+ return (h("mrd-text-field", Object.assign({}, commonProps, { value: (_d = displayValue) !== null && _d !== void 0 ? _d : '', placeholder: (_e = item.placeholder) !== null && _e !== void 0 ? _e : '' })));
128
178
  case ClientLayoutItemFieldDataType.TEXTBLOCK:
129
- return (h("mrd-textarea-field", Object.assign({}, commonProps, { value: (_d = displayValue) !== null && _d !== void 0 ? _d : '', placeholder: (_e = item.placeholder) !== null && _e !== void 0 ? _e : '' })));
179
+ return (h("mrd-textarea-field", Object.assign({}, commonProps, { value: (_f = displayValue) !== null && _f !== void 0 ? _f : '', placeholder: (_g = item.placeholder) !== null && _g !== void 0 ? _g : '' })));
130
180
  case ClientLayoutItemFieldDataType.INTEGER:
131
181
  case ClientLayoutItemFieldDataType.DECIMAL:
132
182
  case ClientLayoutItemFieldDataType.PERCENTAGE:
133
- return (h("mrd-number-field", Object.assign({}, commonProps, { value: (_f = displayValue) !== null && _f !== void 0 ? _f : null, dataType: item.dataType, decimalPrecision: (_g = item.decimalPrecision) !== null && _g !== void 0 ? _g : 2, placeholder: (_h = item.placeholder) !== null && _h !== void 0 ? _h : '' })));
183
+ return (h("mrd-number-field", Object.assign({}, commonProps, { value: (_h = displayValue) !== null && _h !== void 0 ? _h : null, dataType: item.dataType, decimalPrecision: (_j = item.decimalPrecision) !== null && _j !== void 0 ? _j : 2, placeholder: (_k = item.placeholder) !== null && _k !== void 0 ? _k : '' })));
134
184
  case ClientLayoutItemFieldDataType.CURRENCY:
135
185
  return (h("mrd-currency-field", Object.assign({}, commonProps, {
136
186
  // Input-only fallback: it preselects the layout's currencyCode in the dropdown
137
187
  // before an amount exists. The field never emits this shape back — an empty
138
188
  // amount emits null (FINDING-0141).
139
- value: (_j = displayValue) !== null && _j !== void 0 ? _j : { amount: null, currency: (_k = item.currencyCode) !== null && _k !== void 0 ? _k : 'EUR' }
189
+ value: (_l = displayValue) !== null && _l !== void 0 ? _l : { amount: null, currency: (_m = item.currencyCode) !== null && _m !== void 0 ? _m : 'EUR' }
140
190
  })));
141
191
  case ClientLayoutItemFieldDataType.BOOLEAN:
142
- return (h("mrd-boolean-field", Object.assign({}, commonProps, { value: (_l = displayValue) !== null && _l !== void 0 ? _l : false })));
192
+ return (h("mrd-boolean-field", Object.assign({}, commonProps, { value: (_o = displayValue) !== null && _o !== void 0 ? _o : false })));
143
193
  case ClientLayoutItemFieldDataType.DATE:
144
- return (h("mrd-date-field", Object.assign({}, commonProps, { value: (_m = displayValue) !== null && _m !== void 0 ? _m : '' })));
194
+ return (h("mrd-date-field", Object.assign({}, commonProps, { value: (_p = displayValue) !== null && _p !== void 0 ? _p : '' })));
145
195
  case ClientLayoutItemFieldDataType.DATETIME:
146
- return (h("mrd-datetime-field", Object.assign({}, commonProps, { value: (_o = displayValue) !== null && _o !== void 0 ? _o : '' })));
196
+ return (h("mrd-datetime-field", Object.assign({}, commonProps, { value: (_q = displayValue) !== null && _q !== void 0 ? _q : '' })));
147
197
  case ClientLayoutItemFieldDataType.TIME:
148
- return (h("mrd-time-field", Object.assign({}, commonProps, { value: (_p = displayValue) !== null && _p !== void 0 ? _p : '' })));
198
+ return (h("mrd-time-field", Object.assign({}, commonProps, { value: (_r = displayValue) !== null && _r !== void 0 ? _r : '' })));
149
199
  case ClientLayoutItemFieldDataType.EMAIL:
150
- return (h("mrd-email-field", Object.assign({}, commonProps, { value: (_q = displayValue) !== null && _q !== void 0 ? _q : '', placeholder: (_r = item.placeholder) !== null && _r !== void 0 ? _r : '' })));
200
+ return (h("mrd-email-field", Object.assign({}, commonProps, { value: (_s = displayValue) !== null && _s !== void 0 ? _s : '', placeholder: (_t = item.placeholder) !== null && _t !== void 0 ? _t : '' })));
151
201
  case ClientLayoutItemFieldDataType.HYPERLINK:
152
- return (h("mrd-hyperlink-field", Object.assign({}, commonProps, { value: (_s = displayValue) !== null && _s !== void 0 ? _s : '', placeholder: (_t = item.placeholder) !== null && _t !== void 0 ? _t : '' })));
202
+ return (h("mrd-hyperlink-field", Object.assign({}, commonProps, { value: (_u = displayValue) !== null && _u !== void 0 ? _u : '', placeholder: (_v = item.placeholder) !== null && _v !== void 0 ? _v : '' })));
153
203
  case ClientLayoutItemFieldDataType.LIST:
154
- 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 : [] })));
204
+ return (h("mrd-list-field", Object.assign({}, commonProps, { value: (_w = displayValue) !== null && _w !== void 0 ? _w : '', multiple: (_x = item.multiple) !== null && _x !== void 0 ? _x : false, listItems: (_y = item.listItems) !== null && _y !== void 0 ? _y : [] })));
155
205
  case ClientLayoutItemFieldDataType.FILE:
156
- 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 })));
206
+ return (h("mrd-file-field", Object.assign({}, commonProps, { value: displayValue, accept: (_z = item.accept) !== null && _z !== void 0 ? _z : '', maxSize: (_0 = item.maxSize) !== null && _0 !== void 0 ? _0 : 0, multiple: (_1 = item.multiple) !== null && _1 !== void 0 ? _1 : false, onMrdUpload: this.handleUpload })));
157
207
  case ClientLayoutItemFieldDataType.IMAGE:
158
- 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 })));
208
+ return (h("mrd-image-field", Object.assign({}, commonProps, { value: displayValue, accept: (_2 = item.accept) !== null && _2 !== void 0 ? _2 : 'image/*', maxSize: (_3 = item.maxSize) !== null && _3 !== void 0 ? _3 : 0, multiple: (_4 = item.multiple) !== null && _4 !== void 0 ? _4 : false, onMrdUpload: this.handleUpload })));
159
209
  case ClientLayoutItemFieldDataType.LONGTEXT:
160
- 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 : '' })));
210
+ return (h("mrd-longtext-field", Object.assign({}, commonProps, { value: (_5 = displayValue) !== null && _5 !== void 0 ? _5 : '', placeholder: (_6 = item.placeholder) !== null && _6 !== void 0 ? _6 : '' })));
161
211
  case ClientLayoutItemFieldDataType.SECRET:
162
- 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 : '' })));
212
+ return (h("mrd-secret-field", Object.assign({}, commonProps, { value: (_7 = displayValue) !== null && _7 !== void 0 ? _7 : '', placeholder: (_8 = item.placeholder) !== null && _8 !== void 0 ? _8 : '' })));
163
213
  default:
164
214
  return null;
165
215
  }
166
216
  }
217
+ /** Repeatable-row UI for a scalar dataType with multiple: true — mirrors the
218
+ * add/remove pattern already established by renderHistoryEditor(), reusing
219
+ * the single-row renderLeafField() per entry instead of duplicating repeat
220
+ * UI into every leaf field component (FINDING-0149). */
221
+ renderMultiScalarField() {
222
+ var _a, _b;
223
+ const { item, locale } = this;
224
+ const updateRow = (i, val) => {
225
+ this.emitMultiChange(this.multiValues.map((v, idx) => (idx === i ? val : v)));
226
+ };
227
+ const removeRow = (i) => {
228
+ this.emitMultiChange(this.multiValues.filter((_, idx) => idx !== i));
229
+ };
230
+ const addRow = () => {
231
+ this.emitMultiChange([...this.multiValues, this.emptyRowValue()]);
232
+ };
233
+ return (h("div", { class: "mrd-field__multi" }, h(FieldLabel, { base: "mrd-field__multi", label: (_a = item.label) !== null && _a !== void 0 ? _a : '', required: (_b = item.required) !== null && _b !== void 0 ? _b : false }), this.multiValues.map((v, i) => (h("div", { key: String(i), class: "mrd-field__multi-row" }, h("div", { class: "mrd-field__multi-row-input" }, this.renderLeafField(v, (e) => { e.stopPropagation(); updateRow(i, e.detail.value); }, (e) => { e.stopPropagation(); this.handleMultiRowBlur(); }, { label: '', required: false })), h("button", { type: "button", class: "mrd-field__multi-remove", onClick: () => removeRow(i), "aria-label": t('remove', locale) }, this.renderRemoveIcon())))), h("button", { type: "button", class: "mrd-field__multi-add", onClick: addRow }, "+ ", t('add', locale)), h(FieldError, { base: "mrd-field__multi", error: this.multiError })));
234
+ }
167
235
  render() {
168
236
  var _a, _b, _c, _d, _e, _f;
169
237
  const { item, locale, value } = this;
@@ -174,7 +242,7 @@ export class MrdField {
174
242
  return h(Host, null);
175
243
  }
176
244
  const displayValue = this.getDisplayValue();
177
- return (h(Host, null, h("div", { class: "mrd-field__inner" }, this.renderLeafField(displayValue), this.renderHistoryEditor())));
245
+ return (h(Host, null, h("div", { class: "mrd-field__inner" }, this.isMultiScalar() ? this.renderMultiScalarField() : this.renderLeafField(displayValue), this.renderHistoryEditor())));
178
246
  }
179
247
  static get is() { return "mrd-field"; }
180
248
  static get encapsulation() { return "scoped"; }
@@ -256,7 +324,9 @@ export class MrdField {
256
324
  static get states() {
257
325
  return {
258
326
  "historyEntries": {},
259
- "currentValue": {}
327
+ "currentValue": {},
328
+ "multiValues": {},
329
+ "multiError": {}
260
330
  };
261
331
  }
262
332
  static get events() {
@@ -94,7 +94,7 @@ function renderHistoryBadge(ctx, item, raw) {
94
94
  ctx.on.toggleHistory(isOpen ? null : item.name);
95
95
  } }, h("svg", { class: "mrd-layout-section__history-icon", viewBox: "0 0 20 20", fill: "currentColor", "aria-hidden": "true" }, h("path", { "fill-rule": "evenodd", d: "M10 18a8 8 0 100-16 8 8 0 000 16zm.75-13a.75.75 0 00-1.5 0v5c0 .207.085.394.22.53l2.5 2.5a.75.75 0 101.06-1.06L10.75 9.69V5z", "clip-rule": "evenodd" }))), isOpen && (h("div", { class: "mrd-layout-section__history-popover", role: "listbox" }, sorted.map((entry, i) => (h("div", { key: String(i), class: "mrd-layout-section__history-entry" }, entry.value, " (", t('history_until', ctx.locale), " ", formatDate(entry.until, ctx.locale), ")")))))));
96
96
  }
97
- export function renderField(ctx, item) {
97
+ function fieldRenderState(ctx, item) {
98
98
  var _a, _b;
99
99
  if (!item.name)
100
100
  return null;
@@ -104,10 +104,27 @@ export function renderField(ctx, item) {
104
104
  ? raw.current
105
105
  : raw;
106
106
  const renderedValue = renderFieldValue(ctx, item, rawValue);
107
+ return { raw, rawValue, renderedValue, isEmpty: renderedValue == null };
108
+ }
109
+ /** Whether a FIELD item would render anything at all (value, or a showEmpty
110
+ * placeholder). A `header` field always renders. Used by mrd-layout-section
111
+ * to decide whether a preceding HEADER item's block is entirely empty. */
112
+ export function isFieldVisible(ctx, item) {
113
+ if (item.header)
114
+ return true;
115
+ const state = fieldRenderState(ctx, item);
116
+ if (!state)
117
+ return false;
118
+ return !state.isEmpty || showsEmpty(item);
119
+ }
120
+ export function renderField(ctx, item) {
121
+ const state = fieldRenderState(ctx, item);
122
+ if (!state)
123
+ return null;
124
+ const { raw, rawValue, renderedValue, isEmpty } = state;
107
125
  if (item.header) {
108
126
  return (h("h1", { class: "mrd-layout-section__field-header", key: item.name }, typeof renderedValue === 'string' ? renderedValue : rawValue != null ? String(rawValue) : item.label));
109
127
  }
110
- const isEmpty = renderedValue == null;
111
128
  if (isEmpty && !showsEmpty(item))
112
129
  return null;
113
130
  const isBlock = item.dataType === ClientLayoutItemFieldDataType.TEXTBLOCK
@@ -12,6 +12,16 @@ export function showsEmpty(item) {
12
12
  var _a, _b, _c, _d, _e;
13
13
  return (_e = (_c = (_a = item.showEmpty) !== null && _a !== void 0 ? _a : (_b = item.field) === null || _b === void 0 ? void 0 : _b.showEmpty) !== null && _c !== void 0 ? _c : (_d = item.relation) === null || _d === void 0 ? void 0 : _d.showEmpty) !== null && _e !== void 0 ? _e : false;
14
14
  }
15
+ /** Whether a RELATION item has a resolvable link value (`_links[item.name]`
16
+ * carries either a multi-value `values[]` or a single `name`). */
17
+ export function hasRelationValue(item, data) {
18
+ var _a, _b;
19
+ if (!item.name)
20
+ return false;
21
+ const links = ((_a = data === null || data === void 0 ? void 0 : data._links) !== null && _a !== void 0 ? _a : {});
22
+ const link = links[item.name];
23
+ return !!(((_b = link === null || link === void 0 ? void 0 : link.values) === null || _b === void 0 ? void 0 : _b.length) || (link === null || link === void 0 ? void 0 : link.name));
24
+ }
15
25
  /** Depth-first flatten of the SECTION/GROUP tree. */
16
26
  export function flattenItems(items) {
17
27
  const result = [];
@@ -101,10 +101,11 @@
101
101
  padding: 0;
102
102
  }
103
103
  .mrd-layout-section__header {
104
- font-size: var(--mrd-font-size-xl);
104
+ font-size: var(--mrd-font-size-sm);
105
105
  font-weight: var(--mrd-font-weight-semibold);
106
- color: var(--mrd-color-neutral-800);
107
- margin: var(--mrd-space-4) 0 var(--mrd-space-2) 0;
106
+ color: var(--mrd-color-neutral-500);
107
+ letter-spacing: 0.01em;
108
+ margin: var(--mrd-space-5) 0 var(--mrd-space-2) 0;
108
109
  padding: 0;
109
110
  }
110
111
  .mrd-layout-section__text {