@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.
- package/dist/cjs/{cell-renderer-Coy-aD3k.js → cell-renderer-Mx3pwONr.js} +237 -0
- package/dist/cjs/loader.cjs.js +1 -1
- package/dist/cjs/mosterdcomponents.cjs.js +1 -1
- package/dist/cjs/mrd-boolean-field_25.cjs.entry.js +296 -56
- package/dist/cjs/mrd-merge-view.cjs.entry.js +3 -3
- package/dist/collection/components/fields/mrd-currency-field/mrd-currency-field.js +56 -5
- package/dist/collection/components/fields/mrd-date-field/mrd-date-field.css +55 -4
- package/dist/collection/components/fields/mrd-date-field/mrd-date-field.js +118 -8
- package/dist/collection/components/fields/mrd-datetime-field/mrd-datetime-field.css +55 -4
- package/dist/collection/components/fields/mrd-datetime-field/mrd-datetime-field.js +117 -15
- package/dist/collection/components/fields/mrd-email-field/mrd-email-field.js +1 -1
- package/dist/collection/components/fields/mrd-file-field/mrd-file-field.js +1 -1
- package/dist/collection/components/fields/mrd-image-field/mrd-image-field.js +1 -1
- package/dist/collection/components/fields/mrd-longtext-field/mrd-longtext-field.js +1 -1
- package/dist/collection/components/fields/mrd-number-field/mrd-number-field.js +33 -8
- package/dist/collection/components/fields/mrd-secret-field/mrd-secret-field.js +2 -2
- package/dist/collection/components/fields/mrd-text-field/mrd-text-field.js +1 -1
- package/dist/collection/components/fields/mrd-textarea-field/mrd-textarea-field.js +1 -1
- package/dist/collection/components/fields/mrd-time-field/mrd-time-field.js +1 -1
- package/dist/collection/components/form/mrd-field/mrd-field.js +11 -11
- package/dist/collection/components/invoice/mrd-invoice-view/mrd-invoice-view.js +1 -1
- package/dist/collection/components/layout/mrd-layout-section/mrd-layout-section.js +1 -1
- package/dist/collection/components/merge/mrd-merge-view/mrd-merge-view.js +2 -2
- package/dist/collection/dev/app.js +58 -13
- package/dist/collection/utils/format.js +160 -0
- package/dist/collection/utils/i18n.js +72 -0
- package/dist/components/cell-renderer.js +1 -1
- package/dist/components/format.js +1 -1
- package/dist/components/i18n.js +1 -1
- package/dist/components/mrd-currency-field2.js +1 -1
- package/dist/components/mrd-date-field2.js +1 -1
- package/dist/components/mrd-datetime-field2.js +1 -1
- package/dist/components/mrd-document-list2.js +1 -1
- package/dist/components/mrd-document-view2.js +1 -1
- package/dist/components/mrd-email-field2.js +1 -1
- package/dist/components/mrd-email-view2.js +1 -1
- package/dist/components/mrd-field2.js +1 -1
- package/dist/components/mrd-file-field2.js +1 -1
- package/dist/components/mrd-image-field2.js +1 -1
- package/dist/components/mrd-invoice-view2.js +1 -1
- package/dist/components/mrd-layout-section.js +1 -1
- package/dist/components/mrd-longtext-field2.js +1 -1
- package/dist/components/mrd-merge-view.js +1 -1
- package/dist/components/mrd-number-field2.js +1 -1
- package/dist/components/mrd-secret-field2.js +1 -1
- package/dist/components/mrd-table2.js +1 -1
- package/dist/components/mrd-text-field2.js +1 -1
- package/dist/components/mrd-textarea-field2.js +1 -1
- package/dist/components/mrd-time-field2.js +1 -1
- package/dist/esm/{cell-renderer-C2FLUrMN.js → cell-renderer-UQOYlDv7.js} +233 -1
- package/dist/esm/loader.js +1 -1
- package/dist/esm/mosterdcomponents.js +1 -1
- package/dist/esm/mrd-boolean-field_25.entry.js +296 -56
- package/dist/esm/mrd-merge-view.entry.js +3 -3
- package/dist/mosterdcomponents/mosterdcomponents.esm.js +1 -1
- package/dist/mosterdcomponents/p-8cc374e0.entry.js +3 -0
- package/dist/mosterdcomponents/p-CF--ScWr.js +1 -0
- package/dist/mosterdcomponents/{p-4ea5b97c.entry.js → p-d31b71d9.entry.js} +1 -1
- package/dist/types/components/fields/mrd-currency-field/mrd-currency-field.d.ts +13 -0
- package/dist/types/components/fields/mrd-date-field/mrd-date-field.d.ts +22 -1
- package/dist/types/components/fields/mrd-datetime-field/mrd-datetime-field.d.ts +21 -2
- package/dist/types/components/fields/mrd-number-field/mrd-number-field.d.ts +6 -0
- package/dist/types/components.d.ts +26 -0
- package/dist/types/utils/format.d.ts +34 -0
- package/package.json +1 -1
- package/dist/mosterdcomponents/p-8736313b.entry.js +0 -3
- package/dist/mosterdcomponents/p-BgsPVesg.js +0 -1
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
var index = require('./index-DVbdkYO3.js');
|
|
4
4
|
var clientLayout = require('./client-layout-CqVuJumR.js');
|
|
5
|
-
var cellRenderer = require('./cell-renderer-
|
|
5
|
+
var cellRenderer = require('./cell-renderer-Mx3pwONr.js');
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
8
|
* Whether a layout item can appear in the merge diff at all. m-n relations
|
|
@@ -257,14 +257,14 @@ const MrdMergeView = class {
|
|
|
257
257
|
var _a, _b;
|
|
258
258
|
let stepContent;
|
|
259
259
|
if (this.step === 'search') {
|
|
260
|
-
stepContent = (index.h(MergeSearchStep, { key: '
|
|
260
|
+
stepContent = (index.h(MergeSearchStep, { key: 'b965c21176faf7844e58cb156958fa1305d07cdc', locale: this.locale, searchQuery: this.searchQuery, searchResults: this.searchResults, isLoading: this.isLoading, onSearchInput: this.handleSearchInput, onSelect: this.handleSelect }));
|
|
261
261
|
}
|
|
262
262
|
else {
|
|
263
263
|
const diff = diffFields(this.fields, this.currentRecord, this.candidate);
|
|
264
264
|
const bLabel = candidateLabel(this.candidate);
|
|
265
265
|
stepContent = this.step === 'compare' ? (index.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 })) : (index.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 }));
|
|
266
266
|
}
|
|
267
|
-
return (index.h(index.Host, { key: '
|
|
267
|
+
return (index.h(index.Host, { key: 'e9cffda6c5cff51f802b044f3d8e417df83b7846' }, index.h("div", { key: '26690ef2db7489cbf7994191ad19491a0fdd7002', class: "mrd-merge-view" }, stepContent)));
|
|
268
268
|
}
|
|
269
269
|
get el() { return index.getElement(this); }
|
|
270
270
|
};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Host, h } from "@stencil/core";
|
|
2
|
-
import { parseLocalizedNumber } from "../../../utils/format";
|
|
2
|
+
import { formatNumber, parseLocalizedNumber } from "../../../utils/format";
|
|
3
3
|
import { FieldLabel, FieldError, controlClass, requiredError } from "../field-helpers";
|
|
4
4
|
const COMMON_CURRENCIES = [
|
|
5
5
|
'EUR', 'USD', 'GBP', 'CHF', 'JPY', 'CNY', 'AUD', 'CAD',
|
|
@@ -17,13 +17,26 @@ export class MrdCurrencyField {
|
|
|
17
17
|
this.amountDisplay = '';
|
|
18
18
|
this.currency = 'EUR';
|
|
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.handleAmountInput = (e) => {
|
|
21
23
|
this.amountDisplay = e.target.value;
|
|
22
24
|
};
|
|
25
|
+
this.handleAmountFocus = () => {
|
|
26
|
+
this.focused = true;
|
|
27
|
+
// Drop the grouping separators while editing, but keep the locale's decimal
|
|
28
|
+
// separator: String(1234.56) would be re-read as 123456 where '.' groups.
|
|
29
|
+
const parsed = parseLocalizedNumber(this.amountDisplay, this.locale);
|
|
30
|
+
if (parsed !== null) {
|
|
31
|
+
this.amountDisplay = formatNumber(parsed, this.locale, { useGrouping: false, maximumFractionDigits: 20 });
|
|
32
|
+
}
|
|
33
|
+
};
|
|
23
34
|
this.handleAmountBlur = (e) => {
|
|
35
|
+
this.focused = false;
|
|
24
36
|
const raw = e.target.value;
|
|
25
37
|
const parsed = parseLocalizedNumber(raw, this.locale);
|
|
26
38
|
this.error = requiredError(raw, this.required, this.locale);
|
|
39
|
+
this.amountDisplay = parsed !== null ? this.formatAmount(parsed) : '';
|
|
27
40
|
const val = { amount: parsed, currency: this.currency };
|
|
28
41
|
this.mrdChange.emit({ name: this.name, value: val });
|
|
29
42
|
this.mrdBlur.emit({ name: this.name, value: val });
|
|
@@ -35,14 +48,43 @@ export class MrdCurrencyField {
|
|
|
35
48
|
};
|
|
36
49
|
}
|
|
37
50
|
componentWillLoad() {
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
51
|
+
this.syncFromValue();
|
|
52
|
+
}
|
|
53
|
+
componentDidRender() {
|
|
54
|
+
// A rendered `selected` attribute is ignored once the select has an established
|
|
55
|
+
// value, so the currency is applied imperatively — same reason as the DROPDOWN
|
|
56
|
+
// preselect in mrd-relation-field. Without this an edit-mode record that loads
|
|
57
|
+
// after mount keeps showing EUR and would be saved back with the wrong currency.
|
|
58
|
+
if (this.selectEl && this.selectEl.value !== this.currency) {
|
|
59
|
+
this.selectEl.value = this.currency;
|
|
41
60
|
}
|
|
42
61
|
}
|
|
62
|
+
/** The record's own currency may sit outside the common list; never drop it. */
|
|
63
|
+
currencyOptions() {
|
|
64
|
+
return COMMON_CURRENCIES.includes(this.currency) ? COMMON_CURRENCIES : [this.currency, ...COMMON_CURRENCIES];
|
|
65
|
+
}
|
|
66
|
+
/** Edit mode loads the record after mount, so the incoming value must re-sync. */
|
|
67
|
+
valueChanged() {
|
|
68
|
+
if (!this.focused)
|
|
69
|
+
this.syncFromValue();
|
|
70
|
+
}
|
|
71
|
+
localeChanged() {
|
|
72
|
+
if (!this.focused)
|
|
73
|
+
this.syncFromValue();
|
|
74
|
+
}
|
|
75
|
+
syncFromValue() {
|
|
76
|
+
const value = this.value;
|
|
77
|
+
this.currency = (value === null || value === void 0 ? void 0 : value.currency) || 'EUR';
|
|
78
|
+
this.amountDisplay =
|
|
79
|
+
(value === null || value === void 0 ? void 0 : value.amount) !== null && (value === null || value === void 0 ? void 0 : value.amount) !== undefined ? this.formatAmount(value.amount) : '';
|
|
80
|
+
}
|
|
81
|
+
/** Display form: locale separators, always two decimals. The ISO code sits in the select. */
|
|
82
|
+
formatAmount(amount) {
|
|
83
|
+
return formatNumber(amount, this.locale, { minimumFractionDigits: 2, maximumFractionDigits: 2 });
|
|
84
|
+
}
|
|
43
85
|
render() {
|
|
44
86
|
const hasError = !!this.error;
|
|
45
|
-
return (h(Host, { key: '
|
|
87
|
+
return (h(Host, { key: '43ef1fe660c4eaef025203544916b077975b6aa3' }, h("div", { key: '00b8e79182c8e2a353d57ad5c341024caf00d4ea', class: "mrd-currency-field" }, h(FieldLabel, { key: '96835c311e0d490864c484b818f1dcc623f488b2', base: "mrd-currency-field", label: this.label, required: this.required }), h("div", { key: '80317cfe562af00cc4cbf9aabf4bac2126cf6fe5', class: "mrd-currency-field__row" }, h("input", { key: '1983ef1e4d19e0fc529cf5e8be7d287fea36620a', class: controlClass('mrd-currency-field', hasError, 'amount'), type: "text", inputMode: "decimal", name: `${this.name}_amount`, value: this.amountDisplay, placeholder: this.formatAmount(0), required: this.required, disabled: this.disabled, onInput: this.handleAmountInput, onFocus: this.handleAmountFocus, onBlur: this.handleAmountBlur }), h("select", { key: 'b5fe3f03590bebaa564d76bc5fcb47c327eacece', class: "mrd-currency-field__currency", ref: el => (this.selectEl = el), disabled: this.disabled, onChange: this.handleCurrencyChange }, this.currencyOptions().map(c => (h("option", { key: c, value: c, selected: c === this.currency }, c))))), h(FieldError, { key: '1a128b26293170d4137f787622d4e89a23e62c0b', base: "mrd-currency-field", error: this.error }))));
|
|
46
88
|
}
|
|
47
89
|
static get is() { return "mrd-currency-field"; }
|
|
48
90
|
static get encapsulation() { return "scoped"; }
|
|
@@ -239,4 +281,13 @@ export class MrdCurrencyField {
|
|
|
239
281
|
}
|
|
240
282
|
}];
|
|
241
283
|
}
|
|
284
|
+
static get watchers() {
|
|
285
|
+
return [{
|
|
286
|
+
"propName": "value",
|
|
287
|
+
"methodName": "valueChanged"
|
|
288
|
+
}, {
|
|
289
|
+
"propName": "locale",
|
|
290
|
+
"methodName": "localeChanged"
|
|
291
|
+
}];
|
|
292
|
+
}
|
|
242
293
|
}
|
|
@@ -19,6 +19,10 @@
|
|
|
19
19
|
content: " *";
|
|
20
20
|
color: var(--mrd-color-danger);
|
|
21
21
|
}
|
|
22
|
+
.mrd-date-field__control {
|
|
23
|
+
position: relative;
|
|
24
|
+
display: block;
|
|
25
|
+
}
|
|
22
26
|
.mrd-date-field__input {
|
|
23
27
|
display: block;
|
|
24
28
|
width: 100%;
|
|
@@ -53,12 +57,59 @@
|
|
|
53
57
|
.mrd-date-field__input--error:focus {
|
|
54
58
|
box-shadow: var(--mrd-shadow-focus-error);
|
|
55
59
|
}
|
|
60
|
+
.mrd-date-field__input {
|
|
61
|
+
padding-inline-end: calc(var(--mrd-input-padding-x) + 1.75rem);
|
|
62
|
+
}
|
|
63
|
+
.mrd-date-field__picker {
|
|
64
|
+
position: absolute;
|
|
65
|
+
inset-inline-end: var(--mrd-space-1);
|
|
66
|
+
top: 50%;
|
|
67
|
+
transform: translateY(-50%);
|
|
68
|
+
width: 1.75rem;
|
|
69
|
+
height: 1.75rem;
|
|
70
|
+
}
|
|
71
|
+
.mrd-date-field__picker-button {
|
|
72
|
+
display: inline-flex;
|
|
73
|
+
align-items: center;
|
|
74
|
+
justify-content: center;
|
|
75
|
+
width: 100%;
|
|
76
|
+
height: 100%;
|
|
77
|
+
padding: 0;
|
|
78
|
+
border: 0;
|
|
79
|
+
border-radius: var(--mrd-border-radius-sm);
|
|
80
|
+
background: transparent;
|
|
81
|
+
color: var(--mrd-color-neutral-500);
|
|
82
|
+
cursor: pointer;
|
|
83
|
+
transition: color var(--mrd-transition), background-color var(--mrd-transition);
|
|
84
|
+
}
|
|
85
|
+
.mrd-date-field__picker-button svg {
|
|
86
|
+
width: 1.125rem;
|
|
87
|
+
height: 1.125rem;
|
|
88
|
+
}
|
|
89
|
+
.mrd-date-field__picker-button:hover:not(:disabled) {
|
|
90
|
+
color: var(--mrd-color-primary);
|
|
91
|
+
background-color: var(--mrd-color-neutral-100);
|
|
92
|
+
}
|
|
93
|
+
.mrd-date-field__picker-button:focus-visible {
|
|
94
|
+
outline: 2px solid var(--mrd-border-color-focus);
|
|
95
|
+
outline-offset: 1px;
|
|
96
|
+
}
|
|
97
|
+
.mrd-date-field__picker-button:disabled {
|
|
98
|
+
cursor: not-allowed;
|
|
99
|
+
opacity: 0.5;
|
|
100
|
+
}
|
|
101
|
+
.mrd-date-field__picker-native {
|
|
102
|
+
position: absolute;
|
|
103
|
+
inset: 0;
|
|
104
|
+
width: 100%;
|
|
105
|
+
height: 100%;
|
|
106
|
+
padding: 0;
|
|
107
|
+
border: 0;
|
|
108
|
+
opacity: 0;
|
|
109
|
+
pointer-events: none;
|
|
110
|
+
}
|
|
56
111
|
.mrd-date-field__error {
|
|
57
112
|
font-family: var(--mrd-font-family);
|
|
58
113
|
font-size: var(--mrd-error-font-size);
|
|
59
114
|
color: var(--mrd-error-color);
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
.mrd-date-field__input {
|
|
63
|
-
cursor: pointer;
|
|
64
115
|
}
|
|
@@ -1,26 +1,106 @@
|
|
|
1
1
|
import { Host, h } from "@stencil/core";
|
|
2
|
+
import { t } from "../../../utils/i18n";
|
|
3
|
+
import { formatIsoDate, localeDatePattern, parseLocalizedDate } from "../../../utils/format";
|
|
2
4
|
import { FieldLabel, FieldError, controlClass, requiredError } from "../field-helpers";
|
|
3
5
|
export class MrdDateField {
|
|
4
6
|
constructor() {
|
|
5
7
|
this.name = '';
|
|
6
8
|
this.label = '';
|
|
9
|
+
/** ISO date, 'YYYY-MM-DD'. */
|
|
7
10
|
this.value = '';
|
|
11
|
+
/** Overrides the locale-derived pattern placeholder (e.g. 'dd-mm-jjjj'). */
|
|
12
|
+
this.placeholder = '';
|
|
8
13
|
this.required = false;
|
|
9
14
|
this.disabled = false;
|
|
10
15
|
this.locale = navigator.language;
|
|
16
|
+
/** What the user sees: the value formatted for `locale`, or their raw typing. */
|
|
17
|
+
this.displayValue = '';
|
|
11
18
|
this.error = '';
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
this.
|
|
19
|
+
/** While focused the user owns the text; prop updates must not overwrite it. */
|
|
20
|
+
this.focused = false;
|
|
21
|
+
this.handleFocus = () => {
|
|
22
|
+
this.focused = true;
|
|
23
|
+
};
|
|
24
|
+
this.handleInput = (e) => {
|
|
25
|
+
this.displayValue = e.target.value;
|
|
16
26
|
};
|
|
17
27
|
this.handleBlur = (e) => {
|
|
18
|
-
|
|
19
|
-
|
|
28
|
+
this.focused = false;
|
|
29
|
+
const value = this.commit(e.target.value);
|
|
30
|
+
this.mrdChange.emit({ name: this.name, value });
|
|
31
|
+
this.mrdBlur.emit({ name: this.name, value });
|
|
32
|
+
};
|
|
33
|
+
this.handleKeyDown = (e) => {
|
|
34
|
+
// Submitting with Enter never fires blur, so commit the typed text here too.
|
|
35
|
+
if (e.key !== 'Enter')
|
|
36
|
+
return;
|
|
37
|
+
const value = this.commit(e.target.value);
|
|
38
|
+
this.mrdChange.emit({ name: this.name, value });
|
|
39
|
+
};
|
|
40
|
+
this.openPicker = () => {
|
|
41
|
+
const native = this.nativeInput;
|
|
42
|
+
if (!native || this.disabled)
|
|
43
|
+
return;
|
|
44
|
+
// The blur commit that precedes this click re-renders asynchronously, so sync by hand first.
|
|
45
|
+
native.value = this.currentIso();
|
|
46
|
+
try {
|
|
47
|
+
native.showPicker();
|
|
48
|
+
}
|
|
49
|
+
catch (_a) {
|
|
50
|
+
// Browsers without showPicker(): focusing at least surfaces the platform control.
|
|
51
|
+
native.focus();
|
|
52
|
+
}
|
|
20
53
|
};
|
|
54
|
+
this.handlePickerChange = (e) => {
|
|
55
|
+
const iso = e.target.value; // native inputs always yield 'YYYY-MM-DD'
|
|
56
|
+
this.displayValue = formatIsoDate(iso, this.locale);
|
|
57
|
+
this.error = requiredError(iso, this.required, this.locale);
|
|
58
|
+
this.mrdChange.emit({ name: this.name, value: iso });
|
|
59
|
+
this.mrdBlur.emit({ name: this.name, value: iso });
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
componentWillLoad() {
|
|
63
|
+
this.displayValue = formatIsoDate(this.value, this.locale);
|
|
64
|
+
}
|
|
65
|
+
valueChanged(newValue) {
|
|
66
|
+
if (!this.focused)
|
|
67
|
+
this.displayValue = formatIsoDate(newValue, this.locale);
|
|
68
|
+
}
|
|
69
|
+
localeChanged() {
|
|
70
|
+
if (!this.focused)
|
|
71
|
+
this.displayValue = formatIsoDate(this.value, this.locale);
|
|
72
|
+
}
|
|
73
|
+
patternPlaceholder() {
|
|
74
|
+
if (this.placeholder)
|
|
75
|
+
return this.placeholder;
|
|
76
|
+
return localeDatePattern(this.locale, {
|
|
77
|
+
day: t('date_token_day', this.locale),
|
|
78
|
+
month: t('date_token_month', this.locale),
|
|
79
|
+
year: t('date_token_year', this.locale),
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
/** Parse the typed text, reformat it, and emit the ISO value. */
|
|
83
|
+
commit(raw) {
|
|
84
|
+
const trimmed = raw.trim();
|
|
85
|
+
const iso = trimmed ? parseLocalizedDate(trimmed, this.locale) : '';
|
|
86
|
+
if (trimmed && iso === null) {
|
|
87
|
+
// Keep the raw text so the user can correct it instead of retyping.
|
|
88
|
+
this.error = t('invalid_date', this.locale);
|
|
89
|
+
this.displayValue = trimmed;
|
|
90
|
+
}
|
|
91
|
+
else {
|
|
92
|
+
this.error = requiredError(iso, this.required, this.locale);
|
|
93
|
+
this.displayValue = iso ? formatIsoDate(iso, this.locale) : '';
|
|
94
|
+
}
|
|
95
|
+
return iso !== null && iso !== void 0 ? iso : '';
|
|
96
|
+
}
|
|
97
|
+
/** The ISO value currently shown, so the picker opens on the right date. */
|
|
98
|
+
currentIso() {
|
|
99
|
+
var _a;
|
|
100
|
+
return (_a = (this.displayValue ? parseLocalizedDate(this.displayValue, this.locale) : '')) !== null && _a !== void 0 ? _a : '';
|
|
21
101
|
}
|
|
22
102
|
render() {
|
|
23
|
-
return (h(Host, { key: '
|
|
103
|
+
return (h(Host, { key: '06ba48bd538ee4c8de4e071285535451f783c259' }, h("div", { key: '73ea6ea23c85fe3325bb151afb755cc11a13cf58', class: "mrd-date-field" }, h(FieldLabel, { key: '527848cf72323aef176fc7d5bcd47dcf2f7e46f6', base: "mrd-date-field", label: this.label, required: this.required }), h("div", { key: '2ff7586f4e6298d1f9b9b131106044533de82c6d', class: "mrd-date-field__control" }, h("input", { key: '68fdf2f1f7961ca049ab6493494c8c0707e6a4af', class: controlClass('mrd-date-field', !!this.error), type: "text", name: this.name, value: this.displayValue, placeholder: this.patternPlaceholder(), autocomplete: "off", required: this.required, disabled: this.disabled, onFocus: this.handleFocus, onInput: this.handleInput, onBlur: this.handleBlur, onKeyDown: this.handleKeyDown }), h("span", { key: '9557b7a3f6780b8a05030ab8e7c941c44df9f4a5', class: "mrd-date-field__picker" }, h("button", { key: '586973e0440ed0b74e1fa0affae9e462257024c3', type: "button", class: "mrd-date-field__picker-button", title: t('open_calendar', this.locale), "aria-label": t('open_calendar', this.locale), disabled: this.disabled, onClick: this.openPicker }, h("svg", { key: '144319d3a50d0d38c75c59dd3321d4b9097322a6', viewBox: "0 0 20 20", fill: "none", stroke: "currentColor", "stroke-width": "1.5", "aria-hidden": "true" }, h("rect", { key: 'aec01ca5c620bf3116705b7a28052eb3bc210cfd', x: "3", y: "4.5", width: "14", height: "12", rx: "2" }), h("path", { key: 'a0e13dd3a2005bfbee40f5a071b6760270d0d58d', d: "M3 8.5h14M7 2.5v3M13 2.5v3", "stroke-linecap": "round" }))), h("input", { key: 'be02df44f31a59a26ce166ab9a9eab9db2635d89', class: "mrd-date-field__picker-native", type: "date", ref: el => (this.nativeInput = el), value: this.currentIso(), disabled: this.disabled, tabIndex: -1, "aria-hidden": "true", onChange: this.handlePickerChange }))), h(FieldError, { key: '2f8b2072bee32170a35f98ec5c962018774c7e8c', base: "mrd-date-field", error: this.error }))));
|
|
24
104
|
}
|
|
25
105
|
static get is() { return "mrd-date-field"; }
|
|
26
106
|
static get encapsulation() { return "scoped"; }
|
|
@@ -88,7 +168,7 @@ export class MrdDateField {
|
|
|
88
168
|
"optional": false,
|
|
89
169
|
"docs": {
|
|
90
170
|
"tags": [],
|
|
91
|
-
"text": ""
|
|
171
|
+
"text": "ISO date, 'YYYY-MM-DD'."
|
|
92
172
|
},
|
|
93
173
|
"getter": false,
|
|
94
174
|
"setter": false,
|
|
@@ -96,6 +176,26 @@ export class MrdDateField {
|
|
|
96
176
|
"attribute": "value",
|
|
97
177
|
"defaultValue": "''"
|
|
98
178
|
},
|
|
179
|
+
"placeholder": {
|
|
180
|
+
"type": "string",
|
|
181
|
+
"mutable": false,
|
|
182
|
+
"complexType": {
|
|
183
|
+
"original": "string",
|
|
184
|
+
"resolved": "string",
|
|
185
|
+
"references": {}
|
|
186
|
+
},
|
|
187
|
+
"required": false,
|
|
188
|
+
"optional": false,
|
|
189
|
+
"docs": {
|
|
190
|
+
"tags": [],
|
|
191
|
+
"text": "Overrides the locale-derived pattern placeholder (e.g. 'dd-mm-jjjj')."
|
|
192
|
+
},
|
|
193
|
+
"getter": false,
|
|
194
|
+
"setter": false,
|
|
195
|
+
"reflect": false,
|
|
196
|
+
"attribute": "placeholder",
|
|
197
|
+
"defaultValue": "''"
|
|
198
|
+
},
|
|
99
199
|
"required": {
|
|
100
200
|
"type": "boolean",
|
|
101
201
|
"mutable": false,
|
|
@@ -160,6 +260,7 @@ export class MrdDateField {
|
|
|
160
260
|
}
|
|
161
261
|
static get states() {
|
|
162
262
|
return {
|
|
263
|
+
"displayValue": {},
|
|
163
264
|
"error": {}
|
|
164
265
|
};
|
|
165
266
|
}
|
|
@@ -196,4 +297,13 @@ export class MrdDateField {
|
|
|
196
297
|
}
|
|
197
298
|
}];
|
|
198
299
|
}
|
|
300
|
+
static get watchers() {
|
|
301
|
+
return [{
|
|
302
|
+
"propName": "value",
|
|
303
|
+
"methodName": "valueChanged"
|
|
304
|
+
}, {
|
|
305
|
+
"propName": "locale",
|
|
306
|
+
"methodName": "localeChanged"
|
|
307
|
+
}];
|
|
308
|
+
}
|
|
199
309
|
}
|
|
@@ -19,6 +19,10 @@
|
|
|
19
19
|
content: " *";
|
|
20
20
|
color: var(--mrd-color-danger);
|
|
21
21
|
}
|
|
22
|
+
.mrd-datetime-field__control {
|
|
23
|
+
position: relative;
|
|
24
|
+
display: block;
|
|
25
|
+
}
|
|
22
26
|
.mrd-datetime-field__input {
|
|
23
27
|
display: block;
|
|
24
28
|
width: 100%;
|
|
@@ -53,12 +57,59 @@
|
|
|
53
57
|
.mrd-datetime-field__input--error:focus {
|
|
54
58
|
box-shadow: var(--mrd-shadow-focus-error);
|
|
55
59
|
}
|
|
60
|
+
.mrd-datetime-field__input {
|
|
61
|
+
padding-inline-end: calc(var(--mrd-input-padding-x) + 1.75rem);
|
|
62
|
+
}
|
|
63
|
+
.mrd-datetime-field__picker {
|
|
64
|
+
position: absolute;
|
|
65
|
+
inset-inline-end: var(--mrd-space-1);
|
|
66
|
+
top: 50%;
|
|
67
|
+
transform: translateY(-50%);
|
|
68
|
+
width: 1.75rem;
|
|
69
|
+
height: 1.75rem;
|
|
70
|
+
}
|
|
71
|
+
.mrd-datetime-field__picker-button {
|
|
72
|
+
display: inline-flex;
|
|
73
|
+
align-items: center;
|
|
74
|
+
justify-content: center;
|
|
75
|
+
width: 100%;
|
|
76
|
+
height: 100%;
|
|
77
|
+
padding: 0;
|
|
78
|
+
border: 0;
|
|
79
|
+
border-radius: var(--mrd-border-radius-sm);
|
|
80
|
+
background: transparent;
|
|
81
|
+
color: var(--mrd-color-neutral-500);
|
|
82
|
+
cursor: pointer;
|
|
83
|
+
transition: color var(--mrd-transition), background-color var(--mrd-transition);
|
|
84
|
+
}
|
|
85
|
+
.mrd-datetime-field__picker-button svg {
|
|
86
|
+
width: 1.125rem;
|
|
87
|
+
height: 1.125rem;
|
|
88
|
+
}
|
|
89
|
+
.mrd-datetime-field__picker-button:hover:not(:disabled) {
|
|
90
|
+
color: var(--mrd-color-primary);
|
|
91
|
+
background-color: var(--mrd-color-neutral-100);
|
|
92
|
+
}
|
|
93
|
+
.mrd-datetime-field__picker-button:focus-visible {
|
|
94
|
+
outline: 2px solid var(--mrd-border-color-focus);
|
|
95
|
+
outline-offset: 1px;
|
|
96
|
+
}
|
|
97
|
+
.mrd-datetime-field__picker-button:disabled {
|
|
98
|
+
cursor: not-allowed;
|
|
99
|
+
opacity: 0.5;
|
|
100
|
+
}
|
|
101
|
+
.mrd-datetime-field__picker-native {
|
|
102
|
+
position: absolute;
|
|
103
|
+
inset: 0;
|
|
104
|
+
width: 100%;
|
|
105
|
+
height: 100%;
|
|
106
|
+
padding: 0;
|
|
107
|
+
border: 0;
|
|
108
|
+
opacity: 0;
|
|
109
|
+
pointer-events: none;
|
|
110
|
+
}
|
|
56
111
|
.mrd-datetime-field__error {
|
|
57
112
|
font-family: var(--mrd-font-family);
|
|
58
113
|
font-size: var(--mrd-error-font-size);
|
|
59
114
|
color: var(--mrd-error-color);
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
.mrd-datetime-field__input {
|
|
63
|
-
cursor: pointer;
|
|
64
115
|
}
|
|
@@ -1,33 +1,77 @@
|
|
|
1
1
|
import { Host, h } from "@stencil/core";
|
|
2
|
+
import { t } from "../../../utils/i18n";
|
|
3
|
+
import { formatLocalDateTime, localeDatePattern, parseLocalizedDateTime } from "../../../utils/format";
|
|
2
4
|
import { FieldLabel, FieldError, controlClass, requiredError } from "../field-helpers";
|
|
3
5
|
export class MrdDatetimeField {
|
|
4
6
|
constructor() {
|
|
5
7
|
this.name = '';
|
|
6
8
|
this.label = '';
|
|
9
|
+
/** UTC ISO string, e.g. '2026-07-06T12:30:00Z'. */
|
|
7
10
|
this.value = '';
|
|
11
|
+
/** Overrides the locale-derived pattern placeholder (e.g. 'dd-mm-jjjj uu:mm'). */
|
|
12
|
+
this.placeholder = '';
|
|
8
13
|
this.required = false;
|
|
9
14
|
this.disabled = false;
|
|
10
15
|
this.locale = navigator.language;
|
|
16
|
+
/** What the user sees: the value formatted for `locale`, or their raw typing. */
|
|
17
|
+
this.displayValue = '';
|
|
11
18
|
this.error = '';
|
|
12
|
-
|
|
13
|
-
this.
|
|
14
|
-
|
|
15
|
-
this.
|
|
16
|
-
|
|
17
|
-
|
|
19
|
+
/** While focused the user owns the text; prop updates must not overwrite it. */
|
|
20
|
+
this.focused = false;
|
|
21
|
+
this.handleFocus = () => {
|
|
22
|
+
this.focused = true;
|
|
23
|
+
};
|
|
24
|
+
this.handleInput = (e) => {
|
|
25
|
+
this.displayValue = e.target.value;
|
|
18
26
|
};
|
|
19
27
|
this.handleBlur = (e) => {
|
|
20
|
-
|
|
21
|
-
|
|
28
|
+
this.focused = false;
|
|
29
|
+
const value = this.commit(e.target.value);
|
|
30
|
+
this.mrdChange.emit({ name: this.name, value });
|
|
31
|
+
this.mrdBlur.emit({ name: this.name, value });
|
|
32
|
+
};
|
|
33
|
+
this.handleKeyDown = (e) => {
|
|
34
|
+
// Submitting with Enter never fires blur, so commit the typed text here too.
|
|
35
|
+
if (e.key !== 'Enter')
|
|
36
|
+
return;
|
|
37
|
+
const value = this.commit(e.target.value);
|
|
38
|
+
this.mrdChange.emit({ name: this.name, value });
|
|
39
|
+
};
|
|
40
|
+
this.openPicker = () => {
|
|
41
|
+
const native = this.nativeInput;
|
|
42
|
+
if (!native || this.disabled)
|
|
43
|
+
return;
|
|
44
|
+
// The blur commit that precedes this click re-renders asynchronously, so sync by hand first.
|
|
45
|
+
native.value = this.currentLocal();
|
|
46
|
+
try {
|
|
47
|
+
native.showPicker();
|
|
48
|
+
}
|
|
49
|
+
catch (_a) {
|
|
50
|
+
// Browsers without showPicker(): focusing at least surfaces the platform control.
|
|
51
|
+
native.focus();
|
|
52
|
+
}
|
|
53
|
+
};
|
|
54
|
+
this.handlePickerChange = (e) => {
|
|
55
|
+
const local = e.target.value; // native inputs always yield 'YYYY-MM-DDTHH:mm'
|
|
56
|
+
this.displayValue = formatLocalDateTime(local, this.locale);
|
|
57
|
+
this.error = requiredError(local, this.required, this.locale);
|
|
58
|
+
const value = this.localToUtc(local);
|
|
59
|
+
this.mrdChange.emit({ name: this.name, value });
|
|
60
|
+
this.mrdBlur.emit({ name: this.name, value });
|
|
22
61
|
};
|
|
23
62
|
}
|
|
24
63
|
componentWillLoad() {
|
|
25
|
-
this.
|
|
64
|
+
this.displayValue = this.toDisplay(this.value);
|
|
26
65
|
}
|
|
27
66
|
valueChanged(newVal) {
|
|
28
|
-
|
|
67
|
+
if (!this.focused)
|
|
68
|
+
this.displayValue = this.toDisplay(newVal);
|
|
29
69
|
}
|
|
30
|
-
|
|
70
|
+
localeChanged() {
|
|
71
|
+
if (!this.focused)
|
|
72
|
+
this.displayValue = this.toDisplay(this.value);
|
|
73
|
+
}
|
|
74
|
+
// UTC ISO string → "YYYY-MM-DDTHH:mm" in local timezone
|
|
31
75
|
utcToLocal(utcStr) {
|
|
32
76
|
if (!utcStr)
|
|
33
77
|
return '';
|
|
@@ -46,8 +90,43 @@ export class MrdDatetimeField {
|
|
|
46
90
|
return '';
|
|
47
91
|
return d.toISOString().replace(/\.\d{3}Z$/, 'Z');
|
|
48
92
|
}
|
|
93
|
+
// UTC ISO string → locale-formatted text for the input
|
|
94
|
+
toDisplay(utcStr) {
|
|
95
|
+
return formatLocalDateTime(this.utcToLocal(utcStr), this.locale);
|
|
96
|
+
}
|
|
97
|
+
patternPlaceholder() {
|
|
98
|
+
if (this.placeholder)
|
|
99
|
+
return this.placeholder;
|
|
100
|
+
return localeDatePattern(this.locale, {
|
|
101
|
+
day: t('date_token_day', this.locale),
|
|
102
|
+
month: t('date_token_month', this.locale),
|
|
103
|
+
year: t('date_token_year', this.locale),
|
|
104
|
+
hour: t('time_token_hour', this.locale),
|
|
105
|
+
minute: t('time_token_minute', this.locale),
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
/** Parse the typed text, reformat it, and return the UTC value to emit. */
|
|
109
|
+
commit(raw) {
|
|
110
|
+
const trimmed = raw.trim();
|
|
111
|
+
const local = trimmed ? parseLocalizedDateTime(trimmed, this.locale) : '';
|
|
112
|
+
if (trimmed && local === null) {
|
|
113
|
+
// Keep the raw text so the user can correct it instead of retyping.
|
|
114
|
+
this.error = t('invalid_datetime', this.locale);
|
|
115
|
+
this.displayValue = trimmed;
|
|
116
|
+
return '';
|
|
117
|
+
}
|
|
118
|
+
const localValue = local !== null && local !== void 0 ? local : '';
|
|
119
|
+
this.error = requiredError(localValue, this.required, this.locale);
|
|
120
|
+
this.displayValue = localValue ? formatLocalDateTime(localValue, this.locale) : '';
|
|
121
|
+
return this.localToUtc(localValue);
|
|
122
|
+
}
|
|
123
|
+
/** The local 'YYYY-MM-DDTHH:mm' currently shown, so the picker opens on the right moment. */
|
|
124
|
+
currentLocal() {
|
|
125
|
+
var _a;
|
|
126
|
+
return (_a = (this.displayValue ? parseLocalizedDateTime(this.displayValue, this.locale) : '')) !== null && _a !== void 0 ? _a : '';
|
|
127
|
+
}
|
|
49
128
|
render() {
|
|
50
|
-
return (h(Host, { key: '
|
|
129
|
+
return (h(Host, { key: 'e075e65c76a627a8f19db488c07704e2b1a7270b' }, h("div", { key: 'a6b5a5df3168272f1f775933dfc372e1b2ccbd95', class: "mrd-datetime-field" }, h(FieldLabel, { key: '39e232c066b8c354eeda7df9bcba4e3f95c18ee5', base: "mrd-datetime-field", label: this.label, required: this.required }), h("div", { key: '4ffac4089cbb9e5506aa0854c8b1f15862bc8456', class: "mrd-datetime-field__control" }, h("input", { key: 'd33400133173c7f3e8b045339f44fbeba7f0b35a', class: controlClass('mrd-datetime-field', !!this.error), type: "text", name: this.name, value: this.displayValue, placeholder: this.patternPlaceholder(), autocomplete: "off", required: this.required, disabled: this.disabled, onFocus: this.handleFocus, onInput: this.handleInput, onBlur: this.handleBlur, onKeyDown: this.handleKeyDown }), h("span", { key: '54d8d3b1f508d6226d1341d87c1fec19d5914a10', class: "mrd-datetime-field__picker" }, h("button", { key: '27cd3977984eedd9e3471dd039fc73798f1c7abc', type: "button", class: "mrd-datetime-field__picker-button", title: t('open_calendar', this.locale), "aria-label": t('open_calendar', this.locale), disabled: this.disabled, onClick: this.openPicker }, h("svg", { key: '634ca817fa001263933795e35e1a215a06da7a77', viewBox: "0 0 20 20", fill: "none", stroke: "currentColor", "stroke-width": "1.5", "aria-hidden": "true" }, h("rect", { key: '66eadf6b2cfee53376f6ea0f50dbd2ec04fc5663', x: "3", y: "4.5", width: "14", height: "12", rx: "2" }), h("path", { key: '4bb61793e0501bd18bb16c4e203b20f509692970', d: "M3 8.5h14M7 2.5v3M13 2.5v3", "stroke-linecap": "round" }))), h("input", { key: '8877da44e4dc527e7c05244359a0caf9d2e81fdd', class: "mrd-datetime-field__picker-native", type: "datetime-local", ref: el => (this.nativeInput = el), value: this.currentLocal(), disabled: this.disabled, tabIndex: -1, "aria-hidden": "true", onChange: this.handlePickerChange }))), h(FieldError, { key: '4ee97180614972c4dd334d68873a9fb7f91e48f5', base: "mrd-datetime-field", error: this.error }))));
|
|
51
130
|
}
|
|
52
131
|
static get is() { return "mrd-datetime-field"; }
|
|
53
132
|
static get encapsulation() { return "scoped"; }
|
|
@@ -115,7 +194,7 @@ export class MrdDatetimeField {
|
|
|
115
194
|
"optional": false,
|
|
116
195
|
"docs": {
|
|
117
196
|
"tags": [],
|
|
118
|
-
"text": ""
|
|
197
|
+
"text": "UTC ISO string, e.g. '2026-07-06T12:30:00Z'."
|
|
119
198
|
},
|
|
120
199
|
"getter": false,
|
|
121
200
|
"setter": false,
|
|
@@ -123,6 +202,26 @@ export class MrdDatetimeField {
|
|
|
123
202
|
"attribute": "value",
|
|
124
203
|
"defaultValue": "''"
|
|
125
204
|
},
|
|
205
|
+
"placeholder": {
|
|
206
|
+
"type": "string",
|
|
207
|
+
"mutable": false,
|
|
208
|
+
"complexType": {
|
|
209
|
+
"original": "string",
|
|
210
|
+
"resolved": "string",
|
|
211
|
+
"references": {}
|
|
212
|
+
},
|
|
213
|
+
"required": false,
|
|
214
|
+
"optional": false,
|
|
215
|
+
"docs": {
|
|
216
|
+
"tags": [],
|
|
217
|
+
"text": "Overrides the locale-derived pattern placeholder (e.g. 'dd-mm-jjjj uu:mm')."
|
|
218
|
+
},
|
|
219
|
+
"getter": false,
|
|
220
|
+
"setter": false,
|
|
221
|
+
"reflect": false,
|
|
222
|
+
"attribute": "placeholder",
|
|
223
|
+
"defaultValue": "''"
|
|
224
|
+
},
|
|
126
225
|
"required": {
|
|
127
226
|
"type": "boolean",
|
|
128
227
|
"mutable": false,
|
|
@@ -187,8 +286,8 @@ export class MrdDatetimeField {
|
|
|
187
286
|
}
|
|
188
287
|
static get states() {
|
|
189
288
|
return {
|
|
190
|
-
"
|
|
191
|
-
"
|
|
289
|
+
"displayValue": {},
|
|
290
|
+
"error": {}
|
|
192
291
|
};
|
|
193
292
|
}
|
|
194
293
|
static get events() {
|
|
@@ -228,6 +327,9 @@ export class MrdDatetimeField {
|
|
|
228
327
|
return [{
|
|
229
328
|
"propName": "value",
|
|
230
329
|
"methodName": "valueChanged"
|
|
330
|
+
}, {
|
|
331
|
+
"propName": "locale",
|
|
332
|
+
"methodName": "localeChanged"
|
|
231
333
|
}];
|
|
232
334
|
}
|
|
233
335
|
}
|
|
@@ -24,7 +24,7 @@ export class MrdEmailField {
|
|
|
24
24
|
};
|
|
25
25
|
}
|
|
26
26
|
render() {
|
|
27
|
-
return (h(Host, { key: '
|
|
27
|
+
return (h(Host, { key: 'e185145e628e3a23dd338d93dd8e42bf11569728' }, h("div", { key: '7d77dd982543a8add5fa1f47aa7985cc15079080', class: "mrd-email-field" }, h(FieldLabel, { key: '14eb00a535a052ebe3cb2793cc7dfc05168c402b', base: "mrd-email-field", label: this.label, required: this.required }), h("input", { key: '11fb9856144f7da30a054b4433350d80d316de64', class: controlClass('mrd-email-field', !!this.error), type: "email", name: this.name, value: this.value, placeholder: this.placeholder || 'name@example.com', required: this.required, disabled: this.disabled, onInput: this.handleInput, onBlur: this.handleBlur }), h(FieldError, { key: 'be33c0a43cd99a7152c1d10b7067ea5e52834742', base: "mrd-email-field", error: this.error }))));
|
|
28
28
|
}
|
|
29
29
|
static get is() { return "mrd-email-field"; }
|
|
30
30
|
static get encapsulation() { return "scoped"; }
|