@mmlogic/components 0.5.4 → 0.5.5

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 (67) hide show
  1. package/dist/cjs/{cell-renderer-Coy-aD3k.js → cell-renderer-Mx3pwONr.js} +237 -0
  2. package/dist/cjs/loader.cjs.js +1 -1
  3. package/dist/cjs/mosterdcomponents.cjs.js +1 -1
  4. package/dist/cjs/mrd-boolean-field_25.cjs.entry.js +296 -56
  5. package/dist/cjs/mrd-merge-view.cjs.entry.js +3 -3
  6. package/dist/collection/components/fields/mrd-currency-field/mrd-currency-field.js +56 -5
  7. package/dist/collection/components/fields/mrd-date-field/mrd-date-field.css +55 -4
  8. package/dist/collection/components/fields/mrd-date-field/mrd-date-field.js +118 -8
  9. package/dist/collection/components/fields/mrd-datetime-field/mrd-datetime-field.css +55 -4
  10. package/dist/collection/components/fields/mrd-datetime-field/mrd-datetime-field.js +117 -15
  11. package/dist/collection/components/fields/mrd-email-field/mrd-email-field.js +1 -1
  12. package/dist/collection/components/fields/mrd-file-field/mrd-file-field.js +1 -1
  13. package/dist/collection/components/fields/mrd-image-field/mrd-image-field.js +1 -1
  14. package/dist/collection/components/fields/mrd-longtext-field/mrd-longtext-field.js +1 -1
  15. package/dist/collection/components/fields/mrd-number-field/mrd-number-field.js +33 -8
  16. package/dist/collection/components/fields/mrd-secret-field/mrd-secret-field.js +2 -2
  17. package/dist/collection/components/fields/mrd-text-field/mrd-text-field.js +1 -1
  18. package/dist/collection/components/fields/mrd-textarea-field/mrd-textarea-field.js +1 -1
  19. package/dist/collection/components/fields/mrd-time-field/mrd-time-field.js +1 -1
  20. package/dist/collection/components/form/mrd-field/mrd-field.js +11 -11
  21. package/dist/collection/components/invoice/mrd-invoice-view/mrd-invoice-view.js +1 -1
  22. package/dist/collection/components/layout/mrd-layout-section/mrd-layout-section.js +1 -1
  23. package/dist/collection/components/merge/mrd-merge-view/mrd-merge-view.js +2 -2
  24. package/dist/collection/dev/app.js +58 -13
  25. package/dist/collection/utils/format.js +160 -0
  26. package/dist/collection/utils/i18n.js +72 -0
  27. package/dist/components/cell-renderer.js +1 -1
  28. package/dist/components/format.js +1 -1
  29. package/dist/components/i18n.js +1 -1
  30. package/dist/components/mrd-currency-field2.js +1 -1
  31. package/dist/components/mrd-date-field2.js +1 -1
  32. package/dist/components/mrd-datetime-field2.js +1 -1
  33. package/dist/components/mrd-document-list2.js +1 -1
  34. package/dist/components/mrd-document-view2.js +1 -1
  35. package/dist/components/mrd-email-field2.js +1 -1
  36. package/dist/components/mrd-email-view2.js +1 -1
  37. package/dist/components/mrd-field2.js +1 -1
  38. package/dist/components/mrd-file-field2.js +1 -1
  39. package/dist/components/mrd-image-field2.js +1 -1
  40. package/dist/components/mrd-invoice-view2.js +1 -1
  41. package/dist/components/mrd-layout-section.js +1 -1
  42. package/dist/components/mrd-longtext-field2.js +1 -1
  43. package/dist/components/mrd-merge-view.js +1 -1
  44. package/dist/components/mrd-number-field2.js +1 -1
  45. package/dist/components/mrd-secret-field2.js +1 -1
  46. package/dist/components/mrd-table2.js +1 -1
  47. package/dist/components/mrd-text-field2.js +1 -1
  48. package/dist/components/mrd-textarea-field2.js +1 -1
  49. package/dist/components/mrd-time-field2.js +1 -1
  50. package/dist/esm/{cell-renderer-C2FLUrMN.js → cell-renderer-UQOYlDv7.js} +233 -1
  51. package/dist/esm/loader.js +1 -1
  52. package/dist/esm/mosterdcomponents.js +1 -1
  53. package/dist/esm/mrd-boolean-field_25.entry.js +296 -56
  54. package/dist/esm/mrd-merge-view.entry.js +3 -3
  55. package/dist/mosterdcomponents/mosterdcomponents.esm.js +1 -1
  56. package/dist/mosterdcomponents/p-8cc374e0.entry.js +3 -0
  57. package/dist/mosterdcomponents/p-CF--ScWr.js +1 -0
  58. package/dist/mosterdcomponents/{p-4ea5b97c.entry.js → p-d31b71d9.entry.js} +1 -1
  59. package/dist/types/components/fields/mrd-currency-field/mrd-currency-field.d.ts +13 -0
  60. package/dist/types/components/fields/mrd-date-field/mrd-date-field.d.ts +22 -1
  61. package/dist/types/components/fields/mrd-datetime-field/mrd-datetime-field.d.ts +21 -2
  62. package/dist/types/components/fields/mrd-number-field/mrd-number-field.d.ts +6 -0
  63. package/dist/types/components.d.ts +26 -0
  64. package/dist/types/utils/format.d.ts +34 -0
  65. package/package.json +1 -1
  66. package/dist/mosterdcomponents/p-8736313b.entry.js +0 -3
  67. package/dist/mosterdcomponents/p-BgsPVesg.js +0 -1
@@ -112,7 +112,7 @@ export class MrdFileField {
112
112
  error: hasError,
113
113
  disabled: this.disabled || this.zoneBusy,
114
114
  });
115
- return (h(Host, { key: '2e740a0857b069740a6a13616d0a0dfee2b945be' }, h("div", { key: '069b25ced3b2f1ce547819086dadbd53ade2378c', class: "mrd-file-field" }, h(FieldLabel, { key: 'd13f3cb0af766d6328772dab865b2079c85ec075', base: "mrd-file-field", label: this.label, required: this.required }), h("div", { key: '9ffdbb508e3934854034c46e6f719a88d4175920', class: zoneClass, onClick: this.handleZoneClick, onDragOver: this.drag.onDragOver, onDragLeave: this.drag.onDragLeave, onDrop: this.drag.onDrop }, h("input", { key: 'c5d6954b8fa050889b5b65e15fde229dd64d7cc7', ref: el => (this.fileInputRef = el), class: "mrd-file-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 ? (h("div", { class: "mrd-file-field__selected" }, this.renderEntry(this.entries[0], 0))) : (h("div", { class: "mrd-file-field__prompt" }, h("svg", { class: "mrd-file-field__upload-icon", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", "stroke-width": "2" }, h("polyline", { points: "16 16 12 12 8 16" }), h("line", { x1: "12", y1: "12", x2: "12", y2: "21" }), h("path", { d: "M20.39 18.39A5 5 0 0 0 18 9h-1.26A8 8 0 1 0 3 16.3" })), h("span", null, t('drop_file_here', this.locale), ' ', h("span", { class: "mrd-file-field__browse" }, t('browse', this.locale)))))), this.multiple && hasEntries && (h("ul", { key: '910a3e89f74d93ab16fb9f677a4276d5ad88897d', class: "mrd-file-field__list" }, this.entries.map((entry, index) => (h("li", { class: "mrd-file-field__item", key: `${index}-${entryLabel(entry)}` }, this.renderEntry(entry, index)))))), h(FieldError, { key: '4f0f071b0a1460ea1d6067a8fa4f588ebc620f2a', base: "mrd-file-field", error: this.error }))));
115
+ return (h(Host, { key: '36f9660fd297e7b2e7b2ac1816f4dcbc70d5cfcb' }, h("div", { key: 'a33534d3fedae04bad0eefc9d2d297ea148c5784', class: "mrd-file-field" }, h(FieldLabel, { key: '833f8ff178ec259c8066ddf542bdd1930bac486f', base: "mrd-file-field", label: this.label, required: this.required }), h("div", { key: 'd3bd00b25329b8cc716c5fcc526919d8f2c14bce', class: zoneClass, onClick: this.handleZoneClick, onDragOver: this.drag.onDragOver, onDragLeave: this.drag.onDragLeave, onDrop: this.drag.onDrop }, h("input", { key: '4b98ee338e8a5e4e6f94e5b5614ca88c90f802ee', ref: el => (this.fileInputRef = el), class: "mrd-file-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 ? (h("div", { class: "mrd-file-field__selected" }, this.renderEntry(this.entries[0], 0))) : (h("div", { class: "mrd-file-field__prompt" }, h("svg", { class: "mrd-file-field__upload-icon", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", "stroke-width": "2" }, h("polyline", { points: "16 16 12 12 8 16" }), h("line", { x1: "12", y1: "12", x2: "12", y2: "21" }), h("path", { d: "M20.39 18.39A5 5 0 0 0 18 9h-1.26A8 8 0 1 0 3 16.3" })), h("span", null, t('drop_file_here', this.locale), ' ', h("span", { class: "mrd-file-field__browse" }, t('browse', this.locale)))))), this.multiple && hasEntries && (h("ul", { key: '6e6e1d62738d0a824eb86e0dbb01b63a0227580d', class: "mrd-file-field__list" }, this.entries.map((entry, index) => (h("li", { class: "mrd-file-field__item", key: `${index}-${entryLabel(entry)}` }, this.renderEntry(entry, index)))))), h(FieldError, { key: 'df6427015d3f813a72e8998f9fa6152f6caef90e', base: "mrd-file-field", error: this.error }))));
116
116
  }
117
117
  static get is() { return "mrd-file-field"; }
118
118
  static get encapsulation() { return "scoped"; }
@@ -130,7 +130,7 @@ export class MrdImageField {
130
130
  error: hasError,
131
131
  disabled: this.disabled || this.zoneBusy,
132
132
  });
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 }))));
133
+ return (h(Host, { key: '5009b2db845c8f85f395c7230e2d5c464c56ea14' }, h("div", { key: 'fcae7821876ee3d71aac576cdea44c3c4f169890', class: "mrd-image-field" }, h(FieldLabel, { key: '834638e3f7b2073508d98b6dd5ecb437b0bb52ad', base: "mrd-image-field", label: this.label, required: this.required }), h("div", { key: '50e9bdaa1ebb3ec87f36e0d37dc6c2f8dc79848d', class: zoneClass, onClick: this.handleZoneClick, onDragOver: this.drag.onDragOver, onDragLeave: this.drag.onDragLeave, onDrop: this.drag.onDrop }, h("input", { key: '38e115fae5b3e2c3371857d284fbd54ea23812df', 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: '67bad2f2142f78d7f25b8f35819b6adba48ea597', class: "mrd-image-field__grid" }, this.entries.map((entry, index) => this.renderGridItem(entry, index)))), h(FieldError, { key: 'ef07863946f0b4c94aa30b97d0ecb9616303e793', base: "mrd-image-field", error: this.error }))));
134
134
  }
135
135
  static get is() { return "mrd-image-field"; }
136
136
  static get encapsulation() { return "scoped"; }
@@ -21,7 +21,7 @@ export class MrdLongtextField {
21
21
  };
22
22
  }
23
23
  render() {
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 }))));
24
+ return (h(Host, { key: 'a43ce67cb05e96853193e56c175d99e51d07cbab' }, h("div", { key: 'eb7d09bcae010736dd17b5f17bcb1cb32a6b1c4e', class: "mrd-longtext-field" }, h(FieldLabel, { key: 'd3a47993cc977c06b845eda4575b93d238eaf15a', 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: '89f801af61fc0b874db44dbfca6eb33ffe285a84', 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"; }
@@ -17,10 +17,13 @@ export class MrdNumberField {
17
17
  this.decimalPrecision = 2;
18
18
  this.displayValue = '';
19
19
  this.error = '';
20
+ /** While focused the user owns the text; prop updates must not overwrite it. */
21
+ this.focused = false;
20
22
  this.handleInput = (e) => {
21
23
  this.displayValue = e.target.value;
22
24
  };
23
25
  this.handleBlur = (e) => {
26
+ this.focused = false;
24
27
  const raw = e.target.value;
25
28
  const parsed = parseLocalizedNumber(raw, this.locale);
26
29
  if (this.required && (parsed === null || raw === '')) {
@@ -39,17 +42,30 @@ export class MrdNumberField {
39
42
  this.mrdBlur.emit({ name: this.name, value: parsed });
40
43
  };
41
44
  this.handleFocus = () => {
42
- // Show raw number on focus
43
- if (this.value !== null && this.value !== undefined) {
44
- const str = String(this.value);
45
- this.displayValue = str;
45
+ this.focused = true;
46
+ // Drop the grouping separators while editing, but keep the locale's decimal
47
+ // separator: String(1234.56) would be re-read as 123456 where '.' groups.
48
+ const parsed = parseLocalizedNumber(this.displayValue, this.locale);
49
+ if (parsed !== null) {
50
+ this.displayValue = formatNumber(parsed, this.locale, { useGrouping: false, maximumFractionDigits: 20 });
46
51
  }
47
52
  };
48
53
  }
49
54
  componentWillLoad() {
50
- if (this.value !== null && this.value !== undefined) {
51
- this.displayValue = this.formatForDisplay(this.value);
52
- }
55
+ this.syncFromValue();
56
+ }
57
+ /** Edit mode loads the record after mount, so the incoming value must re-sync. */
58
+ valueChanged() {
59
+ if (!this.focused)
60
+ this.syncFromValue();
61
+ }
62
+ localeChanged() {
63
+ if (!this.focused)
64
+ this.syncFromValue();
65
+ }
66
+ syncFromValue() {
67
+ this.displayValue =
68
+ this.value !== null && this.value !== undefined ? this.formatForDisplay(this.value) : '';
53
69
  }
54
70
  formatForDisplay(val) {
55
71
  if (this.dataType === ClientLayoutItemFieldDataType.PERCENTAGE) {
@@ -70,7 +86,7 @@ export class MrdNumberField {
70
86
  const hasError = !!this.error;
71
87
  const suffix = this.dataType === ClientLayoutItemFieldDataType.PERCENTAGE ? '%' :
72
88
  this.dataType === ClientLayoutItemFieldDataType.DECIMAL ? '' : '';
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 }))));
89
+ return (h(Host, { key: 'bd5eb25cf7b97ef2ddb67a6bc6d6d96c9bff7510' }, h("div", { key: '68b15887a045f743a7f177e7a178146a9f4e0400', class: "mrd-number-field" }, h(FieldLabel, { key: '72e88a34a390e36ee11f5840d87dedd9d4d7fb12', base: "mrd-number-field", label: this.label, required: this.required }), h("div", { key: 'd97dae69eedf4c0ed1c872065754327dbffb5ba7', class: "mrd-number-field__input-wrapper" }, h("input", { key: '5767f6c34a08f503a1a157390c679382c857ff01', 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: '1d4f8ffd3fcdf2ae2e8eb1eb68c051619d66c954', base: "mrd-number-field", error: this.error }))));
74
90
  }
75
91
  static get is() { return "mrd-number-field"; }
76
92
  static get encapsulation() { return "scoped"; }
@@ -314,4 +330,13 @@ export class MrdNumberField {
314
330
  }
315
331
  }];
316
332
  }
333
+ static get watchers() {
334
+ return [{
335
+ "propName": "value",
336
+ "methodName": "valueChanged"
337
+ }, {
338
+ "propName": "locale",
339
+ "methodName": "localeChanged"
340
+ }];
341
+ }
317
342
  }
@@ -21,9 +21,9 @@ export class MrdSecretField {
21
21
  };
22
22
  }
23
23
  render() {
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
24
+ return (h(Host, { key: 'd3d204242898c1f258b088e7b5428421cde8d7a7' }, h("div", { key: '11d284625cdd1e0def58cc31eab9bbe59346649d', class: "mrd-secret-field" }, h(FieldLabel, { key: '333e5b23d2379125a393f555b936be7bc521fdb4', 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: '6353e4b40566852dbc1994c7c9c409d0d0aa1b37', 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: 'cc202197fdab62da7b5083e078e69efd86e673ca', 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: '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 }))));
24
+ return (h(Host, { key: '145aa80d06459fe85650702ced893e102762afa8' }, h("div", { key: '8f08af9acff56e704d216dc185a5d344b5b9f671', class: "mrd-text-field" }, h(FieldLabel, { key: '4e0204ffe7e8460eaf9a4be1535252cc11d0bab7', base: "mrd-text-field", label: this.label, required: this.required }), h("input", { key: '4d519077ad96c121ede51e188a2151efe72811db', 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: '3c5af47037f6d6851503d769429d6c0f786d389d', 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: '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 }))));
67
+ return (h(Host, { key: 'e7c53e9cf54bd4138b42ca3bc7bb5381aad3e4ac' }, h("div", { key: 'bf580a74d2233704852c7d3083ec91c9bbe868ad', class: "mrd-textarea-field" }, h(FieldLabel, { key: '7de50cb2865e83c7fe1ef9b6b099b639e165909c', base: "mrd-textarea-field", label: this.label, required: this.required }), h("div", { key: 'c78e914091cd70b52c1e9cd0046e7c97bcc7e799', class: controlClass('mrd-textarea-field', !!this.error, 'container') }, h("div", { key: '604a157e82ca8a4d98bbd1c928e9f8b41d031227', class: "mrd-textarea-field__editor" })), h(FieldError, { key: 'd0263a7285b928be6ed377e7e0fa3f53a555dadd', 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: '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 }))));
23
+ return (h(Host, { key: 'ef3876dd5c1d72bdbdb65cba00ea0992ba2ab2b6' }, h("div", { key: '6b217f6c6ec0dbb8b3249dfab6443b8f6140891c', class: "mrd-time-field" }, h(FieldLabel, { key: '87adbcc4bf1ccad07d899a315cb14a00650af09c', base: "mrd-time-field", label: this.label, required: this.required }), h("input", { key: '870fcf7e0df2ee2804f95fdc79c890b3e27bba3f', 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: '03c21d23fbc039ffd79856134d181dffcb9e3621', 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, _5, _6;
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, _7, _8;
115
115
  const { item, locale } = this;
116
116
  const commonProps = {
117
117
  name: item.name,
@@ -136,25 +136,25 @@ export class MrdField {
136
136
  case ClientLayoutItemFieldDataType.BOOLEAN:
137
137
  return (h("mrd-boolean-field", Object.assign({}, commonProps, { value: (_l = displayValue) !== null && _l !== void 0 ? _l : false })));
138
138
  case ClientLayoutItemFieldDataType.DATE:
139
- return (h("mrd-date-field", Object.assign({}, commonProps, { value: (_m = displayValue) !== null && _m !== void 0 ? _m : '' })));
139
+ return (h("mrd-date-field", Object.assign({}, commonProps, { value: (_m = displayValue) !== null && _m !== void 0 ? _m : '', placeholder: (_o = item.placeholder) !== null && _o !== void 0 ? _o : '' })));
140
140
  case ClientLayoutItemFieldDataType.DATETIME:
141
- return (h("mrd-datetime-field", Object.assign({}, commonProps, { value: (_o = displayValue) !== null && _o !== void 0 ? _o : '' })));
141
+ return (h("mrd-datetime-field", Object.assign({}, commonProps, { value: (_p = displayValue) !== null && _p !== void 0 ? _p : '', placeholder: (_q = item.placeholder) !== null && _q !== void 0 ? _q : '' })));
142
142
  case ClientLayoutItemFieldDataType.TIME:
143
- return (h("mrd-time-field", Object.assign({}, commonProps, { value: (_p = displayValue) !== null && _p !== void 0 ? _p : '' })));
143
+ return (h("mrd-time-field", Object.assign({}, commonProps, { value: (_r = displayValue) !== null && _r !== void 0 ? _r : '' })));
144
144
  case ClientLayoutItemFieldDataType.EMAIL:
145
- 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 : '' })));
145
+ 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 : '' })));
146
146
  case ClientLayoutItemFieldDataType.HYPERLINK:
147
- 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 : '' })));
147
+ 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 : '' })));
148
148
  case ClientLayoutItemFieldDataType.LIST:
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 : [] })));
149
+ 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 : [] })));
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, multiple: (_z = item.multiple) !== null && _z !== void 0 ? _z : false, onMrdUpload: this.handleUpload })));
151
+ 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 })));
152
152
  case ClientLayoutItemFieldDataType.IMAGE:
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 })));
153
+ 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 })));
154
154
  case ClientLayoutItemFieldDataType.LONGTEXT:
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 : '' })));
155
+ 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 : '' })));
156
156
  case ClientLayoutItemFieldDataType.SECRET:
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 : '' })));
157
+ 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 : '' })));
158
158
  default:
159
159
  return null;
160
160
  }
@@ -107,7 +107,7 @@ export class MrdInvoiceView {
107
107
  const invoiceDateRaw = this.slotValue('invoiceDate');
108
108
  const dueDateRaw = this.slotValue('dueDate');
109
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())));
110
+ return (h(Host, { key: '8005640e6d13004ae2fdf5f48129214fbc8c55fc' }, h("div", { key: '992f925691e2455c8888df76f93176cec5866210', class: "mrd-invoice-view" }, h("div", { key: 'aa418658cfe830be4e0f8bf77af42a898c9170db', class: "mrd-invoice-view__header" }, h("div", { key: 'ce10fef2072c26a0eb8f7ead620cb460af6646f0', class: "mrd-invoice-view__title-row" }, invoiceNumber && h("h1", { key: '992486cd5dc0bfb9761bdc94bcd237e6bd6e3d24', class: "mrd-invoice-view__title" }, t('invoice_title', this.locale), " ", invoiceNumber), this.renderStatus()), h("div", { key: 'a8c5d2516b737d4dcd5d0f966aadcda4d47a63ad', class: "mrd-invoice-view__dates" }, invoiceDateRaw != null && invoiceDateRaw !== '' && (h("span", { key: 'dd728929fc408c280ad81e212bdfac5905cda02d' }, t('invoice_date', this.locale), ": ", formatDate(invoiceDateRaw, this.locale))), dueDateRaw != null && dueDateRaw !== '' && (h("span", { key: 'd4226d5cc37f0152950f44a3b63a88f0454919bd' }, t('invoice_due_date', this.locale), ": ", formatDate(dueDateRaw, this.locale)))), this.renderContainer()), h("div", { key: 'c40b00c852d005963be7b5006e13804e72ba032a', 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: '000e991c64daa582194631b2eac6b1acba418e97', class: "mrd-invoice-view__lines" }, h("slot", { key: '97ba2acb750137eeb5ed6b5e9e5d277f638d4f0a', name: "lines" }))), this.renderAmounts(), this.renderAttachments())));
111
111
  }
112
112
  static get is() { return "mrd-invoice-view"; }
113
113
  static get encapsulation() { return "scoped"; }
@@ -359,7 +359,7 @@ export class MrdLayoutSection {
359
359
  const docArchetype = resolveArchetype(this.archetypes, ARCHETYPE_DOCUMENT);
360
360
  const invoiceArchetype = !docArchetype ? resolveArchetype(this.archetypes, ARCHETYPE_INVOICE) : undefined;
361
361
  const emailArchetype = !docArchetype && !invoiceArchetype ? resolveArchetype(this.archetypes, ARCHETYPE_EMAIL) : undefined;
362
- return (h(Host, { key: 'ccae8e82b4dd549ab24239fe685a6830a508061d' }, h("div", { key: '4072cc4ecdd2b087c54a5515e653f7f713b2150f', class: "mrd-layout-section" }, docArchetype && this.renderDocumentObject(docArchetype), invoiceArchetype && this.renderInvoiceObject(invoiceArchetype), emailArchetype && this.renderEmailObject(emailArchetype), !docArchetype && !invoiceArchetype && !emailArchetype && this.items.map(item => this.renderItem(item))), this.renderImageModal()));
362
+ return (h(Host, { key: '8c4e0a0e71d777295010702584b4aa8f7817cafb' }, h("div", { key: 'fe021021eba48acad3d52ee4849ebe355e83c348', class: "mrd-layout-section" }, docArchetype && this.renderDocumentObject(docArchetype), invoiceArchetype && this.renderInvoiceObject(invoiceArchetype), emailArchetype && this.renderEmailObject(emailArchetype), !docArchetype && !invoiceArchetype && !emailArchetype && this.items.map(item => this.renderItem(item))), this.renderImageModal()));
363
363
  }
364
364
  static get is() { return "mrd-layout-section"; }
365
365
  static get encapsulation() { return "scoped"; }
@@ -121,14 +121,14 @@ export class MrdMergeView {
121
121
  var _a, _b;
122
122
  let stepContent;
123
123
  if (this.step === 'search') {
124
- stepContent = (h(MergeSearchStep, { key: '3adb36fb46aa7260641a46a2cf27cc0ef320876f', locale: this.locale, searchQuery: this.searchQuery, searchResults: this.searchResults, isLoading: this.isLoading, onSearchInput: this.handleSearchInput, onSelect: this.handleSelect }));
124
+ stepContent = (h(MergeSearchStep, { key: 'b965c21176faf7844e58cb156958fa1305d07cdc', locale: this.locale, searchQuery: this.searchQuery, searchResults: this.searchResults, isLoading: this.isLoading, onSearchInput: this.handleSearchInput, onSelect: this.handleSelect }));
125
125
  }
126
126
  else {
127
127
  const diff = diffFields(this.fields, this.currentRecord, this.candidate);
128
128
  const bLabel = candidateLabel(this.candidate);
129
129
  stepContent = this.step === 'compare' ? (h(MergeCompareStep, { locale: this.locale, currentLabel: this.currentLabel, candidateLabel: bLabel, conflicts: diff.conflicts, autoFilled: diff.autoFilled, recordA: this.currentRecord, recordB: (_a = this.candidate) !== null && _a !== void 0 ? _a : {}, resolutions: this.resolutions, openHistoryKey: this.openHistoryKey, onResolve: this.handleResolve, onToggleHistory: this.handleToggleHistory, onChangeCandidate: this.handleChangeCandidate, onContinue: this.handleContinue })) : (h(MergeConfirmStep, { locale: this.locale, candidateLabel: bLabel, conflicts: diff.conflicts, autoFilled: diff.autoFilled, resolutions: this.resolutions, recordB: (_b = this.candidate) !== null && _b !== void 0 ? _b : {}, openHistoryKey: this.openHistoryKey, onToggleHistory: this.handleToggleHistory, onBack: this.handleBack, onConfirm: this.handleConfirm }));
130
130
  }
131
- return (h(Host, { key: 'e33a6d66a220b28cc9533da78dc274e2d7ec5c4a' }, h("div", { key: 'b5fb1390d86192f9f6f15e3ccb89e2d21d9c7c8b', class: "mrd-merge-view" }, stepContent)));
131
+ return (h(Host, { key: 'e9cffda6c5cff51f802b044f3d8e417df83b7846' }, h("div", { key: '26690ef2db7489cbf7994191ad19491a0fdd7002', class: "mrd-merge-view" }, stepContent)));
132
132
  }
133
133
  static get is() { return "mrd-merge-view"; }
134
134
  static get encapsulation() { return "scoped"; }
@@ -134,7 +134,7 @@ async function initDetailViewTab() {
134
134
  table.item = window.EXAMPLE_COMPANY_VIEW_ITEM;
135
135
  table.totalElements = window.EXAMPLE_COMPANIES.length;
136
136
  table.pageSize = 20;
137
- table.locale = 'nl-NL';
137
+ table.locale = _locale;
138
138
 
139
139
  // Register before init(): init() emits mrdLoadPage for page 0 itself.
140
140
  table.addEventListener('mrdLoadPage', async (e) => {
@@ -174,7 +174,7 @@ function showCompanyDetail(row) {
174
174
  const section = document.createElement('mrd-layout-section');
175
175
  section.items = layout.items;
176
176
  section.data = company;
177
- section.locale = 'nl-NL';
177
+ section.locale = _locale;
178
178
 
179
179
  section.addEventListener('mrdNavigate', (e) => {
180
180
  logEvent('mrdNavigate', e.detail);
@@ -1136,16 +1136,57 @@ customElements.whenDefined('mrd-form').then(() => {
1136
1136
  form.addEventListener('mrdFetchAll', (e) => { console.log('[mrdFetchAll]', e.detail); logEvent('mrdFetchAll', e.detail); });
1137
1137
  });
1138
1138
 
1139
- document.getElementById('locale-select').addEventListener('change', (e) => {
1140
- _locale = e.target.value;
1141
- const demoForm = document.getElementById('demo-form');
1142
- if (demoForm) demoForm.locale = _locale;
1143
- const liveForm = document.getElementById('live-form');
1144
- if (liveForm) liveForm.locale = _locale;
1145
- // Update actieve section als aanwezig
1146
- const section = document.querySelector('#sections-container mrd-layout-section');
1147
- if (section) section.locale = _locale;
1148
- });
1139
+ /* =====================================================================
1140
+ LOCALE CONTROL
1141
+ ===================================================================== */
1142
+
1143
+ /** Every component that takes a `locale` prop — the control drives all of them. */
1144
+ const LOCALE_HOSTS = [
1145
+ 'mrd-form', 'mrd-layout-section', 'mrd-table', 'mrd-merge-view',
1146
+ 'mrd-document-container', 'mrd-document-list', 'mrd-document-view',
1147
+ 'mrd-invoice-view', 'mrd-email-view',
1148
+ ].join(', ');
1149
+
1150
+ /** Single source of truth: push the selected locale into everything on the page. */
1151
+ function applyLocale(locale) {
1152
+ _locale = locale;
1153
+ document.querySelectorAll(LOCALE_HOSTS).forEach((el) => { el.locale = locale; });
1154
+ renderLocaleHint();
1155
+ }
1156
+
1157
+ /**
1158
+ * Shows what the selected locale does next to what the browser would do on its
1159
+ * own. Those two differ exactly where the `locale` prop earns its keep — the
1160
+ * date fields format by prop, a native date input would follow the browser.
1161
+ */
1162
+ function renderLocaleHint() {
1163
+ const hint = document.getElementById('locale-hint');
1164
+ if (!hint) return;
1165
+ const sample = new Date(2026, 6, 6, 14, 30); // 6 July — day > 12 keeps d/m order readable
1166
+ const format = (loc) => new Intl.DateTimeFormat(loc, {
1167
+ year: 'numeric', month: '2-digit', day: '2-digit', hour: '2-digit', minute: '2-digit',
1168
+ }).format(sample);
1169
+
1170
+ hint.innerHTML =
1171
+ `<span>prop <code>${escHtml(_locale)}</code> → <b>${escHtml(format(_locale))}</b></span>` +
1172
+ `<span>browser <code>${escHtml(navigator.language)}</code> → <b>${escHtml(format(navigator.language))}</b></span>`;
1173
+ }
1174
+
1175
+ function initLocaleControl() {
1176
+ const select = document.getElementById('locale-select');
1177
+ // The browser locale is worth testing too, even when it is not in the list.
1178
+ if (![...select.options].some((o) => o.value === _locale)) {
1179
+ const option = document.createElement('option');
1180
+ option.value = _locale;
1181
+ option.textContent = `🖥️ Browser (${_locale})`;
1182
+ select.insertBefore(option, select.firstChild);
1183
+ }
1184
+ select.value = _locale;
1185
+ select.addEventListener('change', (e) => applyLocale(e.target.value));
1186
+ applyLocale(_locale);
1187
+ }
1188
+
1189
+ initLocaleControl();
1149
1190
 
1150
1191
  document.getElementById('btn-inject-results').addEventListener('click', () => {
1151
1192
  const relationField = document.querySelector('mrd-relation-field');
@@ -1170,7 +1211,7 @@ document.addEventListener('DOMContentLoaded', async () => {
1170
1211
  const ftSection = document.getElementById('field-types-section');
1171
1212
  ftSection.items = window.EXAMPLE_FIELD_TYPES_ITEMS;
1172
1213
  ftSection.data = window.EXAMPLE_FIELD_TYPES_DATA;
1173
- ftSection.locale = 'nl-NL';
1214
+ ftSection.locale = _locale;
1174
1215
 
1175
1216
  // Simulate host resolving a signed URL for IMAGE fields (thumbnail on load).
1176
1217
  ftSection.addEventListener('mrdLoadImage', (e) => {
@@ -1197,6 +1238,10 @@ document.addEventListener('DOMContentLoaded', async () => {
1197
1238
  ftSection.setSearchResults(mock, dataClass);
1198
1239
  });
1199
1240
 
1241
+ // Re-apply once the whole page is parsed, so components mounted in markup
1242
+ // (and any upgraded after the control ran) all agree on the locale.
1243
+ applyLocale(_locale);
1244
+
1200
1245
  authRestoreSession();
1201
1246
  await authHandleCallback();
1202
1247
 
@@ -53,6 +53,166 @@ export function formatTime(value, locale) {
53
53
  date.setHours(hours, minutes, 0, 0);
54
54
  return new Intl.DateTimeFormat(locale, { hour: '2-digit', minute: '2-digit' }).format(date);
55
55
  }
56
+ /**
57
+ * Locale-aware date input support.
58
+ *
59
+ * A native <input type="date"> always displays in the browser/OS locale, so the
60
+ * date fields render a plain text input instead and use these helpers to format
61
+ * and parse the value in the locale the host passed down — the same approach
62
+ * mrd-number-field takes with formatNumber / parseLocalizedNumber.
63
+ */
64
+ /** 31 December 2026, 13:45 — day > 12 so day/month order is unambiguous. */
65
+ const PATTERN_SAMPLE = new Date(2026, 11, 31, 13, 45, 0);
66
+ function dateTimeParts(locale, withTime) {
67
+ const options = { year: 'numeric', month: '2-digit', day: '2-digit' };
68
+ if (withTime) {
69
+ options.hour = '2-digit';
70
+ options.minute = '2-digit';
71
+ }
72
+ return new Intl.DateTimeFormat(locale, options).formatToParts(PATTERN_SAMPLE);
73
+ }
74
+ /** Order in which day/month/year appear for a locale, e.g. ['day','month','year'] for nl-NL. */
75
+ function datePartOrder(locale) {
76
+ const order = dateTimeParts(locale, false)
77
+ .filter(p => p.type === 'day' || p.type === 'month' || p.type === 'year')
78
+ .map(p => p.type);
79
+ return order.length === 3 ? order : ['day', 'month', 'year'];
80
+ }
81
+ /** The locale's am/pm markers, so 12-hour input can be parsed back. */
82
+ function dayPeriodMarkers(locale) {
83
+ const marker = (hour) => {
84
+ var _a, _b;
85
+ return (_b = (_a = new Intl.DateTimeFormat(locale, { hour: 'numeric', hour12: true })
86
+ .formatToParts(new Date(2026, 0, 1, hour))
87
+ .find(p => p.type === 'dayPeriod')) === null || _a === void 0 ? void 0 : _a.value) !== null && _b !== void 0 ? _b : '';
88
+ };
89
+ return { am: marker(9), pm: marker(21) };
90
+ }
91
+ /** Arabic-Indic and extended Arabic-Indic digits → ASCII, so typed input parses everywhere. */
92
+ function toLatinDigits(str) {
93
+ return str.replace(/[٠-٩۰-۹]/g, d => {
94
+ const code = d.charCodeAt(0);
95
+ return String(code >= 0x06f0 ? code - 0x06f0 : code - 0x0660);
96
+ });
97
+ }
98
+ function pad(n, size = 2) {
99
+ return String(n).padStart(size, '0');
100
+ }
101
+ /** Two-digit years: < 70 → 20xx, otherwise 19xx. */
102
+ function expandYear(value, digits) {
103
+ if (digits > 2)
104
+ return value;
105
+ return value < 70 ? 2000 + value : 1900 + value;
106
+ }
107
+ function isValidYmd(year, month, day) {
108
+ if (year < 1 || month < 1 || month > 12 || day < 1)
109
+ return false;
110
+ return day <= new Date(year, month, 0).getDate();
111
+ }
112
+ /**
113
+ * Placeholder showing the locale's date order, e.g. 'dd-mm-jjjj' (nl-NL) or
114
+ * 'mm/dd/yyyy' (en-US). Pass `hour`/`minute` tokens to include the time part.
115
+ */
116
+ export function localeDatePattern(locale, tokens) {
117
+ const withTime = !!(tokens.hour && tokens.minute);
118
+ return dateTimeParts(locale, withTime)
119
+ .map(part => {
120
+ var _a, _b;
121
+ switch (part.type) {
122
+ case 'day': return tokens.day;
123
+ case 'month': return tokens.month;
124
+ case 'year': return tokens.year;
125
+ case 'hour': return (_a = tokens.hour) !== null && _a !== void 0 ? _a : '';
126
+ case 'minute': return (_b = tokens.minute) !== null && _b !== void 0 ? _b : '';
127
+ case 'dayPeriod': return 'AM';
128
+ case 'literal': return part.value;
129
+ default: return '';
130
+ }
131
+ })
132
+ .join('')
133
+ .trim();
134
+ }
135
+ /** Pulls day/month/year out of the number groups a user typed, per locale order. */
136
+ function ymdFromGroups(groups, locale) {
137
+ const order = datePartOrder(locale);
138
+ const values = {};
139
+ order.forEach((type, i) => (values[type] = groups[i]));
140
+ const year = expandYear(Number(values.year), values.year.length);
141
+ const month = Number(values.month);
142
+ const day = Number(values.day);
143
+ return isValidYmd(year, month, day) ? { year, month, day } : null;
144
+ }
145
+ /**
146
+ * Parses a date the user typed in their own locale format → 'YYYY-MM-DD'.
147
+ * Accepts any separator, one- or two-digit day/month, two-digit years, and
148
+ * plain ISO input (pasted values). Returns null when it is not a valid date.
149
+ */
150
+ export function parseLocalizedDate(input, locale) {
151
+ const raw = toLatinDigits(String(input !== null && input !== void 0 ? input : '')).trim();
152
+ if (!raw)
153
+ return null;
154
+ const groups = raw.match(/\d+/g);
155
+ if (!groups || groups.length < 3)
156
+ return null;
157
+ const iso = /^(\d{4})-(\d{1,2})-(\d{1,2})$/.exec(raw);
158
+ const parsed = iso
159
+ ? (isValidYmd(+iso[1], +iso[2], +iso[3]) ? { year: +iso[1], month: +iso[2], day: +iso[3] } : null)
160
+ : ymdFromGroups(groups.slice(0, 3), locale);
161
+ if (!parsed)
162
+ return null;
163
+ return `${pad(parsed.year, 4)}-${pad(parsed.month)}-${pad(parsed.day)}`;
164
+ }
165
+ /**
166
+ * Parses a date+time the user typed in their own locale format →
167
+ * 'YYYY-MM-DDTHH:mm' in local time (the shape mrd-datetime-field converts to
168
+ * UTC before emitting). A missing time defaults to 00:00; am/pm is recognised
169
+ * both in English and in the locale's own markers.
170
+ */
171
+ export function parseLocalizedDateTime(input, locale) {
172
+ const raw = toLatinDigits(String(input !== null && input !== void 0 ? input : '')).trim();
173
+ if (!raw)
174
+ return null;
175
+ const groups = raw.match(/\d+/g);
176
+ if (!groups || groups.length < 3)
177
+ return null;
178
+ const iso = /^(\d{4})-(\d{1,2})-(\d{1,2})(?:[T ]|$)/.exec(raw);
179
+ const date = iso
180
+ ? (isValidYmd(+iso[1], +iso[2], +iso[3]) ? { year: +iso[1], month: +iso[2], day: +iso[3] } : null)
181
+ : ymdFromGroups(groups.slice(0, 3), locale);
182
+ if (!date)
183
+ return null;
184
+ let hour = groups.length > 3 ? Number(groups[3]) : 0;
185
+ const minute = groups.length > 4 ? Number(groups[4]) : 0;
186
+ const lower = raw.toLowerCase();
187
+ const { am, pm } = dayPeriodMarkers(locale);
188
+ const isPm = /p\.?\s?m\.?/.test(lower) || (!!pm && lower.includes(pm.toLowerCase()));
189
+ const isAm = /a\.?\s?m\.?/.test(lower) || (!!am && lower.includes(am.toLowerCase()));
190
+ if (isPm && hour < 12)
191
+ hour += 12;
192
+ if (isAm && hour === 12)
193
+ hour = 0;
194
+ if (hour > 23 || minute > 59)
195
+ return null;
196
+ return `${pad(date.year, 4)}-${pad(date.month)}-${pad(date.day)}T${pad(hour)}:${pad(minute)}`;
197
+ }
198
+ /**
199
+ * 'YYYY-MM-DD' → localized display string. Parsed as a *local* date, unlike
200
+ * formatDate(), which would read a date-only string as UTC midnight and shift
201
+ * the day in negative-offset timezones.
202
+ */
203
+ export function formatIsoDate(iso, locale) {
204
+ const m = /^(\d{4})-(\d{2})-(\d{2})/.exec(String(iso !== null && iso !== void 0 ? iso : ''));
205
+ if (!m)
206
+ return '';
207
+ return formatDate(new Date(+m[1], +m[2] - 1, +m[3]), locale);
208
+ }
209
+ /** 'YYYY-MM-DDTHH:mm' in local time → localized display string. */
210
+ export function formatLocalDateTime(local, locale) {
211
+ const m = /^(\d{4})-(\d{2})-(\d{2})[T ](\d{2}):(\d{2})/.exec(String(local !== null && local !== void 0 ? local : ''));
212
+ if (!m)
213
+ return '';
214
+ return formatDateTime(new Date(+m[1], +m[2] - 1, +m[3], +m[4], +m[5]), locale);
215
+ }
56
216
  export function parseLocalizedNumber(str, locale) {
57
217
  var _a, _b, _c, _d;
58
218
  if (!str || !str.trim())