@mgdis/mg-components 6.27.0 → 6.28.0
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/loader.cjs.js +1 -1
- package/dist/cjs/mg-action-more_36.cjs.entry.js +365 -77
- package/dist/cjs/mg-components.cjs.js +1 -1
- package/dist/cjs/mg-input-combobox.cjs.entry.js +36 -2
- package/dist/cjs/mg-input-file.cjs.entry.js +25 -2
- package/dist/cjs/mg-input-rich-text-editor.cjs.entry.js +717 -504
- package/dist/collection/assets/jodit/jodit.css +1 -1
- package/dist/collection/components/atoms/mg-button/mg-button.js +86 -13
- package/dist/collection/components/molecules/inputs/mg-input-checkbox/mg-input-checkbox.js +24 -1
- package/dist/collection/components/molecules/inputs/mg-input-combobox/mg-input-combobox.js +36 -2
- package/dist/collection/components/molecules/inputs/mg-input-date/mg-input-date.js +25 -9
- package/dist/collection/components/molecules/inputs/mg-input-file/mg-input-file.js +25 -2
- package/dist/collection/components/molecules/inputs/mg-input-numeric/mg-input-numeric.js +24 -6
- package/dist/collection/components/molecules/inputs/mg-input-password/mg-input-password.js +24 -5
- package/dist/collection/components/molecules/inputs/mg-input-radio/mg-input-radio.js +64 -16
- package/dist/collection/components/molecules/inputs/mg-input-select/mg-input-select.js +32 -10
- package/dist/collection/components/molecules/inputs/mg-input-text/mg-input-text.js +24 -5
- package/dist/collection/components/molecules/inputs/mg-input-textarea/mg-input-textarea.js +24 -6
- package/dist/collection/components/molecules/mg-form/mg-form.js +74 -8
- package/dist/components/mg-button2.js +1 -1
- package/dist/components/mg-form.js +1 -1
- package/dist/components/mg-input-checkbox.js +1 -1
- package/dist/components/mg-input-combobox.js +1 -1
- package/dist/components/mg-input-date.js +1 -1
- package/dist/components/mg-input-file.js +1 -1
- package/dist/components/mg-input-numeric.js +1 -1
- package/dist/components/mg-input-password.js +1 -1
- package/dist/components/mg-input-radio.js +1 -1
- package/dist/components/mg-input-rich-text-editor.js +262 -262
- package/dist/components/mg-input-select2.js +1 -1
- package/dist/components/mg-input-text2.js +1 -1
- package/dist/components/mg-input-textarea.js +1 -1
- package/dist/components/mg-modal.js +1 -1
- package/dist/custom-elements.json +12 -2
- package/dist/esm/loader.js +1 -1
- package/dist/esm/mg-action-more_36.entry.js +366 -78
- package/dist/esm/mg-components.js +1 -1
- package/dist/esm/mg-input-combobox.entry.js +36 -2
- package/dist/esm/mg-input-file.entry.js +25 -2
- package/dist/esm/mg-input-rich-text-editor.entry.js +717 -504
- package/dist/mg-components/mg-components.esm.js +1 -1
- package/dist/mg-components/p-2198f145.entry.js +1 -0
- package/dist/mg-components/p-d4a4eb78.entry.js +1 -0
- package/dist/mg-components/{p-6f492676.entry.js → p-e8b5e8d8.entry.js} +261 -261
- package/dist/mg-components/p-fdcce8d5.entry.js +1 -0
- package/dist/types/components/atoms/mg-button/mg-button.d.ts +41 -2
- package/dist/types/components/molecules/inputs/mg-input/mg-input.conf.d.ts +1 -0
- package/dist/types/components/molecules/inputs/mg-input-checkbox/mg-input-checkbox.d.ts +9 -0
- package/dist/types/components/molecules/inputs/mg-input-combobox/mg-input-combobox.d.ts +10 -1
- package/dist/types/components/molecules/inputs/mg-input-date/mg-input-date.d.ts +9 -0
- package/dist/types/components/molecules/inputs/mg-input-file/mg-input-file.d.ts +9 -0
- package/dist/types/components/molecules/inputs/mg-input-numeric/mg-input-numeric.d.ts +9 -0
- package/dist/types/components/molecules/inputs/mg-input-password/mg-input-password.d.ts +9 -0
- package/dist/types/components/molecules/inputs/mg-input-radio/mg-input-radio.d.ts +29 -2
- package/dist/types/components/molecules/inputs/mg-input-select/mg-input-select.d.ts +10 -1
- package/dist/types/components/molecules/inputs/mg-input-text/mg-input-text.d.ts +9 -0
- package/dist/types/components/molecules/inputs/mg-input-textarea/mg-input-textarea.d.ts +9 -0
- package/dist/types/components/molecules/mg-form/mg-form.d.ts +18 -0
- package/dist/types/components.d.ts +10 -2
- package/ide/intellij/web-types.json +4 -4
- package/ide/vscode/html-custom-data.json +3 -3
- package/package.json +19 -19
- package/dist/mg-components/p-32a55ddb.entry.js +0 -1
- package/dist/mg-components/p-813d69c7.entry.js +0 -1
- package/dist/mg-components/p-a2445605.entry.js +0 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { h, Host } from "@stencil/core";
|
|
2
2
|
import { variants, sizes } from "./mg-button.conf";
|
|
3
|
-
import { ClassList, isValidString,
|
|
3
|
+
import { ClassList, isValidString, toString } from "@mgdis/core-ui-helpers/dist/utils";
|
|
4
4
|
import { normalizeBooleanAttributes } from "@mgdis/core-ui-helpers/dist/stencil";
|
|
5
5
|
import { Keys } from "../../../utils/events.utils";
|
|
6
6
|
/**
|
|
@@ -95,6 +95,88 @@ export class MgButton {
|
|
|
95
95
|
(_b = this.element.querySelector('mg-icon')) === null || _b === void 0 ? void 0 : _b.setAttribute('size', this.size);
|
|
96
96
|
}
|
|
97
97
|
};
|
|
98
|
+
/**
|
|
99
|
+
* Submit a native form the way a native submit button does
|
|
100
|
+
* @param form - the form to submit
|
|
101
|
+
*/
|
|
102
|
+
this.dispatchSubmit = (form) => {
|
|
103
|
+
form.dispatchEvent(new SubmitEvent('submit', { bubbles: true, cancelable: true }));
|
|
104
|
+
};
|
|
105
|
+
/**
|
|
106
|
+
* Wrap a `mg-form` in the actions its public API offers, so nothing reaches into its shadow DOM
|
|
107
|
+
* @param mgForm - the owning mg-form
|
|
108
|
+
* @returns its submit and reset actions
|
|
109
|
+
*/
|
|
110
|
+
this.mgFormActions = (mgForm) => ({
|
|
111
|
+
submit: () => mgForm.requestSubmit(),
|
|
112
|
+
reset: () => mgForm.reset(),
|
|
113
|
+
});
|
|
114
|
+
/**
|
|
115
|
+
* Wrap a native form in the same actions
|
|
116
|
+
* @param form - the owning form
|
|
117
|
+
* @returns its submit and reset actions
|
|
118
|
+
*/
|
|
119
|
+
this.nativeFormActions = (form) => ({
|
|
120
|
+
submit: () => this.dispatchSubmit(form),
|
|
121
|
+
reset: () => form.reset(),
|
|
122
|
+
});
|
|
123
|
+
/**
|
|
124
|
+
* Resolve the form the `form` id points to. An id reference does not cross a shadow boundary, so
|
|
125
|
+
* it resolves in the tree the button belongs to — the document, or the shadow tree it sits in —
|
|
126
|
+
* the way a native form owner does. The ancestor wins when it is the one pointed at: two forms
|
|
127
|
+
* can share an identifier, and a tree-wide lookup would retain the first one.
|
|
128
|
+
* @returns the actions of the form holding that id, or null when none does
|
|
129
|
+
*/
|
|
130
|
+
this.resolveOwnerById = () => {
|
|
131
|
+
const ancestorMgForm = this.element.closest('mg-form');
|
|
132
|
+
if (ancestorMgForm !== null && ancestorMgForm.identifier === this.form)
|
|
133
|
+
return this.mgFormActions(ancestorMgForm);
|
|
134
|
+
const root = this.element.getRootNode();
|
|
135
|
+
const tree = root instanceof ShadowRoot ? root : this.element.ownerDocument;
|
|
136
|
+
const mgForm = Array.from(tree.querySelectorAll('mg-form')).find((element) => element.identifier === this.form);
|
|
137
|
+
if (mgForm !== undefined)
|
|
138
|
+
return this.mgFormActions(mgForm);
|
|
139
|
+
// Resolved by id, as a native form owner is, rather than through a selector an id could break
|
|
140
|
+
const nativeForm = tree.getElementById(this.form);
|
|
141
|
+
return nativeForm instanceof HTMLFormElement ? this.nativeFormActions(nativeForm) : null;
|
|
142
|
+
};
|
|
143
|
+
/**
|
|
144
|
+
* Resolve the form owning this button: an explicit `form` id overrides the ancestor form, and an
|
|
145
|
+
* id matching nothing logs then falls back on it, so a button never loses an association it had.
|
|
146
|
+
* @returns the owning form actions, or null when the button has no form owner
|
|
147
|
+
*/
|
|
148
|
+
this.resolveFormOwner = () => {
|
|
149
|
+
if (isValidString(this.form)) {
|
|
150
|
+
const ownerById = this.resolveOwnerById();
|
|
151
|
+
if (ownerById !== null)
|
|
152
|
+
return ownerById;
|
|
153
|
+
console.error(`<mg-button> prop "form" matches no form in the button tree. Passed value: ${toString(this.form)}.`);
|
|
154
|
+
}
|
|
155
|
+
const nativeAncestor = this.element.closest('form');
|
|
156
|
+
if (nativeAncestor !== null)
|
|
157
|
+
return this.nativeFormActions(nativeAncestor);
|
|
158
|
+
const mgAncestor = this.element.closest('mg-form');
|
|
159
|
+
return mgAncestor === null ? null : this.mgFormActions(mgAncestor);
|
|
160
|
+
};
|
|
161
|
+
/**
|
|
162
|
+
* Act on the owning form. The owner is resolved here rather than on load because a native form
|
|
163
|
+
* owner is re-resolved on every submission, never captured once: the button follows a `form`
|
|
164
|
+
* changed after render, and no longer races the target form hydration.
|
|
165
|
+
*/
|
|
166
|
+
this.handleFormClick = () => {
|
|
167
|
+
const isSubmit = ['submit', undefined].includes(this.type);
|
|
168
|
+
// A reset only fires from an explicit association: it has never reset a merely ancestor form
|
|
169
|
+
const isReset = this.type === 'reset' && isValidString(this.form);
|
|
170
|
+
if (!isSubmit && !isReset)
|
|
171
|
+
return;
|
|
172
|
+
const owner = this.resolveFormOwner();
|
|
173
|
+
if (owner === null)
|
|
174
|
+
return;
|
|
175
|
+
if (isSubmit)
|
|
176
|
+
owner.submit();
|
|
177
|
+
else
|
|
178
|
+
owner.reset();
|
|
179
|
+
};
|
|
98
180
|
}
|
|
99
181
|
validateVariant(newValue, oldValue) {
|
|
100
182
|
// validate new value
|
|
@@ -193,16 +275,7 @@ export class MgButton {
|
|
|
193
275
|
* Add listners
|
|
194
276
|
*/
|
|
195
277
|
componentDidLoad() {
|
|
196
|
-
|
|
197
|
-
var _a;
|
|
198
|
-
const closestForm = this.element.closest('form') || ((_a = this.element.closest('mg-form')) === null || _a === void 0 ? void 0 : _a.shadowRoot.querySelector('form'));
|
|
199
|
-
// submit buttons should trigger form submition;
|
|
200
|
-
if (closestForm && ['submit', undefined].includes(this.type)) {
|
|
201
|
-
this.element.addEventListener('click', () => {
|
|
202
|
-
closestForm.dispatchEvent(new SubmitEvent('submit', { bubbles: true, cancelable: true }));
|
|
203
|
-
});
|
|
204
|
-
}
|
|
205
|
-
});
|
|
278
|
+
this.element.addEventListener('click', this.handleFormClick);
|
|
206
279
|
}
|
|
207
280
|
/**
|
|
208
281
|
* Render component
|
|
@@ -210,7 +283,7 @@ export class MgButton {
|
|
|
210
283
|
*/
|
|
211
284
|
render() {
|
|
212
285
|
var _a;
|
|
213
|
-
return (h(Host, { key: '
|
|
286
|
+
return (h(Host, { key: '815bedd3729c652a5faf06c3d451cbc67123ab63', role: "button", tabIndex: this.element.getAttribute('tabindex') || 0, type: this.type, form: this.form, "full-width": this.fullWidth, "aria-label": this.label, "aria-disabled": (_a = this.disabled) === null || _a === void 0 ? void 0 : _a.toString(), onClick: this.handleClick, onKeyup: this.handleKeyup, onKeydown: this.handleKeydown }, h("div", { key: '2a1c20c677294870958f01c4042e8f530bf0f89d', class: this.classCollection.join() }, this.loading && h("mg-icon", { key: 'db3960249deed458f86bcba16b66bbeb9aac69c1', icon: "loader", spin: true }), h("div", { key: '76a3af6c9a105b91b7f6055cc19bbb6f315be1a5', class: "mg-c-button__content" }, h("slot", { key: 'ca87921b6c58fbd055a83ea28f5ce58a618f16fe' })))));
|
|
214
287
|
}
|
|
215
288
|
static get is() { return "mg-button"; }
|
|
216
289
|
static get encapsulation() { return "shadow"; }
|
|
@@ -357,7 +430,7 @@ export class MgButton {
|
|
|
357
430
|
"optional": true,
|
|
358
431
|
"docs": {
|
|
359
432
|
"tags": [],
|
|
360
|
-
"text": "
|
|
433
|
+
"text": "Id of the form to associate the button with, following the native `form` attribute semantics:\nit must match the `identifier` of a `mg-form`, or the `id` of a `<form>`, in the same document,\nand it overrides the ancestor form.\nIf this attribute is not set, the button is associated with its ancestor form."
|
|
361
434
|
},
|
|
362
435
|
"getter": false,
|
|
363
436
|
"setter": false,
|
|
@@ -21,6 +21,8 @@ export class MgInputCheckbox {
|
|
|
21
21
|
constructor() {
|
|
22
22
|
// hasDisplayedError (triggered by blur event)
|
|
23
23
|
this.hasDisplayedError = false;
|
|
24
|
+
// raised when `value` changed, so componentDidUpdate revalidates once the DOM is in sync
|
|
25
|
+
this.validityCheckPending = false;
|
|
24
26
|
this.customErrorMessage = { lock: false };
|
|
25
27
|
this.mode = 'custom';
|
|
26
28
|
// style
|
|
@@ -303,6 +305,7 @@ export class MgInputCheckbox {
|
|
|
303
305
|
this.renderMgInputCheckboxList = () => (h(MgInputCheckboxList, { checkboxes: this.checkboxItems, id: this.checkboxesId, disabled: this.disabled, name: this.name, invalid: this.invalid }));
|
|
304
306
|
}
|
|
305
307
|
validateValue(newValue) {
|
|
308
|
+
this.validityCheckPending = true;
|
|
306
309
|
if (isCheckboxItems(newValue)) {
|
|
307
310
|
this.checkboxItems = newValue.map((item, index) => (Object.assign(Object.assign({}, item), { _id: `${this.identifier}_${index}`, _handleInput: this.handleInput.bind(this), _handleBlur: this.handleBlur.bind(this), _handleKeydown: this.handleKeydown.bind(this) })));
|
|
308
311
|
this.valueChange.emit(newValue);
|
|
@@ -435,6 +438,8 @@ export class MgInputCheckbox {
|
|
|
435
438
|
lock: valid ? false : errorMessageLock,
|
|
436
439
|
message: valid ? undefined : errorMessage,
|
|
437
440
|
};
|
|
441
|
+
// setError() states the validity explicitly, so drop any pending recomputation
|
|
442
|
+
this.validityCheckPending = false;
|
|
438
443
|
this.setValidity(valid);
|
|
439
444
|
this.setErrorMessage(true);
|
|
440
445
|
this.hasDisplayedError = this.invalid;
|
|
@@ -517,8 +522,26 @@ export class MgInputCheckbox {
|
|
|
517
522
|
// https://stenciljs.com/docs/component-lifecycle#componentwillload
|
|
518
523
|
return setTimeout(() => {
|
|
519
524
|
this.checkValidity();
|
|
525
|
+
// the initial check covers the initial value: nothing is pending yet
|
|
526
|
+
this.validityCheckPending = false;
|
|
520
527
|
}, 0);
|
|
521
528
|
}
|
|
529
|
+
/**
|
|
530
|
+
* Recompute validity once the DOM is in sync with `value`.
|
|
531
|
+
* `@Watch('value')` fires before Stencil re-renders, so the rendered control still holds the
|
|
532
|
+
* previous value at that point (visible with a programmatic value change, e.g. a `v-model`
|
|
533
|
+
* default value). The watcher therefore only raises a flag, and the check runs here, after the
|
|
534
|
+
* render. Renders unrelated to `value` leave the flag down and recompute nothing.
|
|
535
|
+
*/
|
|
536
|
+
componentDidUpdate() {
|
|
537
|
+
if (this.validityCheckPending && this.checkboxItems.length > 0) {
|
|
538
|
+
this.validityCheckPending = false;
|
|
539
|
+
this.checkValidity();
|
|
540
|
+
if (this.hasDisplayedError) {
|
|
541
|
+
this.setErrorMessage();
|
|
542
|
+
}
|
|
543
|
+
}
|
|
544
|
+
}
|
|
522
545
|
/**
|
|
523
546
|
* add listeners and DOM attributed
|
|
524
547
|
*/
|
|
@@ -603,7 +626,7 @@ export class MgInputCheckbox {
|
|
|
603
626
|
else {
|
|
604
627
|
inputContent = this.renderCheckboxMulti();
|
|
605
628
|
}
|
|
606
|
-
return (h("mg-input", { key: '
|
|
629
|
+
return (h("mg-input", { key: 'e873b2c1a5e96f5f424f21e3531c6c63c1d9ff39', label: this.label, identifier: this.identifier, class: this.classCollection.join(), labelOnTop: this.labelOnTop, labelHide: this.labelHide, required: !this.readonly ? this.required : undefined, tooltip: this.tooltip, tooltipPosition: this.readonly && readonlyValue.length === 0 ? 'label' : this.tooltipPosition, helpText: !this.readonly ? this.helpText : undefined, errorMessage: !this.readonly ? this.errorMessage : undefined }, inputContent));
|
|
607
630
|
}
|
|
608
631
|
static get is() { return "mg-input-checkbox"; }
|
|
609
632
|
static get encapsulation() { return "shadow"; }
|
|
@@ -48,6 +48,8 @@ export class MgInputCombobox {
|
|
|
48
48
|
constructor() {
|
|
49
49
|
// hasDisplayedError (triggered by blur event)
|
|
50
50
|
this.hasDisplayedError = false;
|
|
51
|
+
// raised when `value` or `items` changed, so componentDidUpdate revalidates once the DOM is in sync
|
|
52
|
+
this.validityCheckPending = false;
|
|
51
53
|
this.customErrorMessage = { lock: false };
|
|
52
54
|
/**
|
|
53
55
|
* Input name
|
|
@@ -521,10 +523,11 @@ export class MgInputCombobox {
|
|
|
521
523
|
};
|
|
522
524
|
}
|
|
523
525
|
watchValue(newValue) {
|
|
526
|
+
this.validityCheckPending = true;
|
|
524
527
|
this.filter = this.getValueTitle();
|
|
525
528
|
this.valueChange.emit(newValue);
|
|
526
529
|
}
|
|
527
|
-
watchItems(newValue) {
|
|
530
|
+
watchItems(newValue, oldValue) {
|
|
528
531
|
if (this.fetchurl !== undefined && newValue === undefined) {
|
|
529
532
|
// skip newValue check
|
|
530
533
|
return;
|
|
@@ -547,6 +550,17 @@ export class MgInputCombobox {
|
|
|
547
550
|
else {
|
|
548
551
|
throw new Error(`<mg-input-combobox> prop "items" values must be the same type, string or Option. Passed value: ${toString(newValue)}.`);
|
|
549
552
|
}
|
|
553
|
+
// `oldValue` is undefined on init
|
|
554
|
+
if (oldValue !== undefined) {
|
|
555
|
+
// the input displays the value's own title, so an unoffered value cannot stay. An empty list is a
|
|
556
|
+
// reload in progress rather than an answer: the value is kept, as `mg-input-select` keeps its own.
|
|
557
|
+
if (this.value !== undefined &&
|
|
558
|
+
this.options.items.length > 0 &&
|
|
559
|
+
!this.options.items.some((option) => this.isCurrentOption(option))) {
|
|
560
|
+
this.value = undefined;
|
|
561
|
+
}
|
|
562
|
+
this.validityCheckPending = true;
|
|
563
|
+
}
|
|
550
564
|
}
|
|
551
565
|
watchIdentifier(newValue) {
|
|
552
566
|
if (isValidString(newValue)) {
|
|
@@ -676,6 +690,8 @@ export class MgInputCombobox {
|
|
|
676
690
|
lock: valid ? false : errorMessageLock,
|
|
677
691
|
message: valid ? undefined : errorMessage,
|
|
678
692
|
};
|
|
693
|
+
// setError() states the validity explicitly, so drop any pending recomputation
|
|
694
|
+
this.validityCheckPending = false;
|
|
679
695
|
this.setValidity(valid);
|
|
680
696
|
this.setErrorMessage(true);
|
|
681
697
|
this.hasDisplayedError = this.invalid;
|
|
@@ -765,8 +781,26 @@ export class MgInputCombobox {
|
|
|
765
781
|
// https://stenciljs.com/docs/component-lifecycle#componentwillload
|
|
766
782
|
return setTimeout(() => {
|
|
767
783
|
this.checkValidity();
|
|
784
|
+
// the initial check covers the initial value: nothing is pending yet
|
|
785
|
+
this.validityCheckPending = false;
|
|
768
786
|
}, 0);
|
|
769
787
|
}
|
|
788
|
+
/**
|
|
789
|
+
* Recompute validity once the DOM is in sync with `value`.
|
|
790
|
+
* `@Watch('value')` fires before Stencil re-renders, so the rendered control still holds the
|
|
791
|
+
* previous value at that point (visible with a programmatic value change, e.g. a `v-model`
|
|
792
|
+
* default value). The watcher therefore only raises a flag, and the check runs here, after the
|
|
793
|
+
* render. Renders unrelated to `value` leave the flag down and recompute nothing.
|
|
794
|
+
*/
|
|
795
|
+
componentDidUpdate() {
|
|
796
|
+
if (this.validityCheckPending && this.input !== undefined) {
|
|
797
|
+
this.validityCheckPending = false;
|
|
798
|
+
this.checkValidity();
|
|
799
|
+
if (this.hasDisplayedError) {
|
|
800
|
+
this.setErrorMessage();
|
|
801
|
+
}
|
|
802
|
+
}
|
|
803
|
+
}
|
|
770
804
|
/**
|
|
771
805
|
* Render
|
|
772
806
|
* @returns HTML Element
|
|
@@ -796,7 +830,7 @@ export class MgInputCombobox {
|
|
|
796
830
|
else {
|
|
797
831
|
activeDescendant = (_e = this.value) === null || _e === void 0 ? void 0 : _e.value;
|
|
798
832
|
}
|
|
799
|
-
return (h("mg-input", { key: '
|
|
833
|
+
return (h("mg-input", { key: '20006f3066f661cd9cf8ec25b4090221d3925b04', label: this.label, identifier: this.identifier, class: this.classCollection.classes.join(' '), ariaDescribedbyIDs: popoverContent && popoverContent !== 'list' ? popoverContent : undefined, labelOnTop: this.labelOnTop, labelHide: this.labelHide, required: this.required, tooltip: this.tooltip, tooltipPosition: this.readonly && this.value === undefined ? 'label' : this.tooltipPosition, helpText: this.helpText, errorMessage: this.errorMessage }, this.readonly ? (readonlyValue && h("b", { class: "mg-c-input__readonly-value" }, readonlyValue)) : (h("mg-popover", { identifier: this.popoverId, placement: this.popoverPlacement, display: this.popoverDisplay, style: {
|
|
800
834
|
'--mg-c-input-list-width': ((_f = this.input) === null || _f === void 0 ? void 0 : _f.offsetWidth) > 0 ? `${this.input.offsetWidth}px` : undefined,
|
|
801
835
|
}, "data-fallback-placement": "bottom-end", "onDisplay-change": this.handlePopoverDisplay }, h("div", { class: "mg-c-input__input-group-container" }, h("mg-icon", { icon: "magnifying-glass" }), h("input", { type: "text", role: "combobox", class: "mg-c-input__box mg-c-input__box--width", value: this.filter, id: this.identifier, name: this.name, placeholder: this.placeholder, title: this.placeholder, disabled: this.disabled, required: this.required, autocomplete: "off", "aria-autocomplete": "list", "aria-expanded": this.popoverDisplay.toString(), "aria-controls": listId, "aria-invalid": (this.invalid === true).toString(), "aria-activedescendant": formatID(activeDescendant), onInput: this.handleFilterInput, onBlur: this.handleFilterBlur, onFocus: this.handleFilterFocus, onKeyDown: this.handleFilterKeydown, ref: (el) => {
|
|
802
836
|
if (el !== null)
|
|
@@ -9,6 +9,8 @@ export class MgInputDate {
|
|
|
9
9
|
constructor() {
|
|
10
10
|
// hasDisplayedError (triggered by blur event)
|
|
11
11
|
this.hasDisplayedError = false;
|
|
12
|
+
// raised when `value` changed, so componentDidUpdate revalidates once the DOM is in sync
|
|
13
|
+
this.validityCheckPending = false;
|
|
12
14
|
this.customErrorMessage = { lock: false };
|
|
13
15
|
/**
|
|
14
16
|
* Input name
|
|
@@ -80,10 +82,6 @@ export class MgInputDate {
|
|
|
80
82
|
* Handle input event
|
|
81
83
|
*/
|
|
82
84
|
this.handleInput = () => {
|
|
83
|
-
this.checkValidity();
|
|
84
|
-
if (this.hasDisplayedError) {
|
|
85
|
-
this.setErrorMessage();
|
|
86
|
-
}
|
|
87
85
|
this.value = this.input.value;
|
|
88
86
|
};
|
|
89
87
|
/**
|
|
@@ -105,11 +103,8 @@ export class MgInputDate {
|
|
|
105
103
|
// when we reset the field we need to test a blank value.
|
|
106
104
|
if (['Delete', 'Backspace'].includes(event.key)) {
|
|
107
105
|
event.preventDefault();
|
|
106
|
+
// validity is recomputed in componentDidUpdate, once the input actually holds the blank value
|
|
108
107
|
this.value = null;
|
|
109
|
-
this.checkValidity();
|
|
110
|
-
if (this.hasDisplayedError) {
|
|
111
|
-
this.setErrorMessage();
|
|
112
|
-
}
|
|
113
108
|
}
|
|
114
109
|
};
|
|
115
110
|
/**
|
|
@@ -196,6 +191,7 @@ export class MgInputDate {
|
|
|
196
191
|
};
|
|
197
192
|
}
|
|
198
193
|
validateValue(newValue) {
|
|
194
|
+
this.validityCheckPending = true;
|
|
199
195
|
// When the input is not fully completed or has been cleared, the value becomes an empty string.
|
|
200
196
|
if (['', undefined].includes(newValue)) {
|
|
201
197
|
// need to force value update to `null` to prevent extra render
|
|
@@ -288,6 +284,8 @@ export class MgInputDate {
|
|
|
288
284
|
lock: valid ? false : errorMessageLock,
|
|
289
285
|
message: valid ? undefined : errorMessage,
|
|
290
286
|
};
|
|
287
|
+
// setError() states the validity explicitly, so drop any pending recomputation
|
|
288
|
+
this.validityCheckPending = false;
|
|
291
289
|
this.setValidity(valid);
|
|
292
290
|
this.setErrorMessage(true);
|
|
293
291
|
this.hasDisplayedError = this.invalid;
|
|
@@ -358,14 +356,32 @@ export class MgInputDate {
|
|
|
358
356
|
// https://stenciljs.com/docs/component-lifecycle#componentwillload
|
|
359
357
|
return setTimeout(() => {
|
|
360
358
|
this.checkValidity();
|
|
359
|
+
// the initial check covers the initial value: nothing is pending yet
|
|
360
|
+
this.validityCheckPending = false;
|
|
361
361
|
}, 0);
|
|
362
362
|
}
|
|
363
|
+
/**
|
|
364
|
+
* Recompute validity once the DOM is in sync with `value`.
|
|
365
|
+
* `@Watch('value')` fires before Stencil re-renders, so the rendered control still holds the
|
|
366
|
+
* previous value at that point (visible with a programmatic value change, e.g. a `v-model`
|
|
367
|
+
* default value). The watcher therefore only raises a flag, and the check runs here, after the
|
|
368
|
+
* render. Renders unrelated to `value` leave the flag down and recompute nothing.
|
|
369
|
+
*/
|
|
370
|
+
componentDidUpdate() {
|
|
371
|
+
if (this.validityCheckPending && this.input !== undefined) {
|
|
372
|
+
this.validityCheckPending = false;
|
|
373
|
+
this.checkValidity();
|
|
374
|
+
if (this.hasDisplayedError) {
|
|
375
|
+
this.setErrorMessage();
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
}
|
|
363
379
|
/**
|
|
364
380
|
* Render
|
|
365
381
|
* @returns HTML Element
|
|
366
382
|
*/
|
|
367
383
|
render() {
|
|
368
|
-
return (h("mg-input", { key: '
|
|
384
|
+
return (h("mg-input", { key: '3d3f6910dde9c32bbf3d0c853e0c778380ed40fc', label: this.label, identifier: this.identifier, class: this.classCollection.join(), labelOnTop: this.labelOnTop, labelHide: this.labelHide, required: this.required, tooltip: this.tooltip, tooltipPosition: this.readonly && this.value === null ? 'label' : this.tooltipPosition, helpText: this.formatHelpText(this.helpText), errorMessage: this.errorMessage }, this.readonly ? (this.value && h("b", { class: "mg-c-input__readonly-value" }, localeDate(this.value, this.locale))) : (h("input", { type: "date", class: "mg-c-input__box", min: this.min, max: this.max, value: this.value, id: this.identifier, name: this.name, disabled: this.disabled, required: this.required, "aria-invalid": (this.invalid === true).toString(), onInput: this.handleInput, onBlur: this.handleBlur, onKeyDown: this.handleKeyDown, ref: (el) => {
|
|
369
385
|
if (el !== null)
|
|
370
386
|
this.input = el;
|
|
371
387
|
} }))));
|
|
@@ -12,6 +12,8 @@ export class MgInputFile {
|
|
|
12
12
|
constructor() {
|
|
13
13
|
// hasDisplayedError (triggered by blur event)
|
|
14
14
|
this.hasDisplayedError = false;
|
|
15
|
+
// raised when `value` changed, so componentDidUpdate revalidates once the DOM is in sync
|
|
16
|
+
this.validityCheckPending = false;
|
|
15
17
|
this.customErrorMessage = { lock: false };
|
|
16
18
|
/**
|
|
17
19
|
* Input name
|
|
@@ -172,6 +174,7 @@ export class MgInputFile {
|
|
|
172
174
|
};
|
|
173
175
|
}
|
|
174
176
|
watchValue(newValue) {
|
|
177
|
+
this.validityCheckPending = true;
|
|
175
178
|
if (allItemsAreString(newValue)) {
|
|
176
179
|
this.files = this.value.map((fileName) => new File([], fileName));
|
|
177
180
|
}
|
|
@@ -277,6 +280,8 @@ export class MgInputFile {
|
|
|
277
280
|
lock: valid ? false : errorMessageLock,
|
|
278
281
|
message: valid ? undefined : errorMessage,
|
|
279
282
|
};
|
|
283
|
+
// setError() states the validity explicitly, so drop any pending recomputation
|
|
284
|
+
this.validityCheckPending = false;
|
|
280
285
|
this.setValidity(valid);
|
|
281
286
|
this.setErrorMessage(true);
|
|
282
287
|
this.hasDisplayedError = this.invalid;
|
|
@@ -328,16 +333,34 @@ export class MgInputFile {
|
|
|
328
333
|
// https://stenciljs.com/docs/component-lifecycle#componentwillload
|
|
329
334
|
return setTimeout(() => {
|
|
330
335
|
this.checkValidity();
|
|
336
|
+
// the initial check covers the initial value: nothing is pending yet
|
|
337
|
+
this.validityCheckPending = false;
|
|
331
338
|
}, 0);
|
|
332
339
|
}
|
|
340
|
+
/**
|
|
341
|
+
* Recompute validity once the DOM is in sync with `value`.
|
|
342
|
+
* `@Watch('value')` fires before Stencil re-renders, so the rendered control still holds the
|
|
343
|
+
* previous value at that point (visible with a programmatic value change, e.g. a `v-model`
|
|
344
|
+
* default value). The watcher therefore only raises a flag, and the check runs here, after the
|
|
345
|
+
* render. Renders unrelated to `value` leave the flag down and recompute nothing.
|
|
346
|
+
*/
|
|
347
|
+
componentDidUpdate() {
|
|
348
|
+
if (this.validityCheckPending && this.inputElement !== undefined) {
|
|
349
|
+
this.validityCheckPending = false;
|
|
350
|
+
this.checkValidity();
|
|
351
|
+
if (this.hasDisplayedError) {
|
|
352
|
+
this.setErrorMessage();
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
}
|
|
333
356
|
/**
|
|
334
357
|
* Render
|
|
335
358
|
* @returns HTML Element
|
|
336
359
|
*/
|
|
337
360
|
render() {
|
|
338
|
-
return (h("mg-input", { key: '
|
|
361
|
+
return (h("mg-input", { key: 'f1613e1105c1eea0c08624ffc270a74889d85ce0', label: this.label, identifier: this.identifier, class: this.classCollection.join(), labelOnTop: this.labelOnTop, labelHide: this.labelHide, required: this.required, tooltip: this.tooltip, tooltipPosition: this.tooltipPosition, helpText: this.helpText, errorMessage: this.errorMessage }, h("div", { key: '23897ae896de9569ea68bf88e26f1349eb5bc909', class: "mg-c-input__file-container" }, h("input", { key: '0601a8af1c3f17331cb671184353b8b63a5f4fa8', name: this.name, id: this.identifier, class: "mg-u-visually-hidden", type: "file", multiple: this.multiple === true, capture: this.capture, accept: this.accept, disabled: this.disabled, required: this.files.length === 0 && this.required, onFocus: this.handleInputFocus, onBlur: this.handleInputBlur, onChange: this.handleInputChange, "aria-invalid": (this.invalid === true).toString(), ref: (el) => {
|
|
339
362
|
this.inputElement = el;
|
|
340
|
-
} }), h("mg-button", { key: '
|
|
363
|
+
} }), h("mg-button", { key: '5ac1076a443cc1a0bb2d7168b1d248d31faeedfd', variant: "secondary", class: "mg-c-input__file-button", type: "button", disabled: this.disabled, onClick: this.handleButtonClick, onBlur: this.handleButtonBlur, tabindex: "-1", ref: (el) => {
|
|
341
364
|
this.fileButtonElement = el;
|
|
342
365
|
} }, this.messages.input.file.browse), this.renderFiles())));
|
|
343
366
|
}
|
|
@@ -12,6 +12,8 @@ export class MgInputNumeric {
|
|
|
12
12
|
constructor() {
|
|
13
13
|
// hasDisplayedError (triggered by blur event)
|
|
14
14
|
this.hasDisplayedError = false;
|
|
15
|
+
// raised when `value` changed, so componentDidUpdate revalidates once the DOM is in sync
|
|
16
|
+
this.validityCheckPending = false;
|
|
15
17
|
this.customErrorMessage = { lock: false };
|
|
16
18
|
/**
|
|
17
19
|
* Input name
|
|
@@ -78,11 +80,6 @@ export class MgInputNumeric {
|
|
|
78
80
|
*/
|
|
79
81
|
this.handleInput = () => {
|
|
80
82
|
this.value = this.input.value;
|
|
81
|
-
// Check validity
|
|
82
|
-
this.checkValidity();
|
|
83
|
-
if (this.hasDisplayedError) {
|
|
84
|
-
this.setErrorMessage();
|
|
85
|
-
}
|
|
86
83
|
};
|
|
87
84
|
/**
|
|
88
85
|
* Handle focus event
|
|
@@ -232,6 +229,7 @@ export class MgInputNumeric {
|
|
|
232
229
|
}
|
|
233
230
|
validateValue(newValue) {
|
|
234
231
|
var _a;
|
|
232
|
+
this.validityCheckPending = true;
|
|
235
233
|
// has input value is always render as string we get a stringified value or a '' for nullish value, so we test string type
|
|
236
234
|
if (typeof newValue === 'string') {
|
|
237
235
|
// Check if integer value starts with '0' (leading zero)
|
|
@@ -352,6 +350,8 @@ export class MgInputNumeric {
|
|
|
352
350
|
lock: valid ? false : errorMessageLock,
|
|
353
351
|
message: valid ? undefined : errorMessage,
|
|
354
352
|
};
|
|
353
|
+
// setError() states the validity explicitly, so drop any pending recomputation
|
|
354
|
+
this.validityCheckPending = false;
|
|
355
355
|
this.setValidity(valid);
|
|
356
356
|
this.setErrorMessage(true);
|
|
357
357
|
this.hasDisplayedError = this.invalid;
|
|
@@ -464,14 +464,32 @@ export class MgInputNumeric {
|
|
|
464
464
|
// https://stenciljs.com/docs/component-lifecycle#componentwillload
|
|
465
465
|
return setTimeout(() => {
|
|
466
466
|
this.checkValidity();
|
|
467
|
+
// the initial check covers the initial value: nothing is pending yet
|
|
468
|
+
this.validityCheckPending = false;
|
|
467
469
|
}, 0);
|
|
468
470
|
}
|
|
471
|
+
/**
|
|
472
|
+
* Recompute validity once the DOM is in sync with `value`.
|
|
473
|
+
* `@Watch('value')` fires before Stencil re-renders, so the rendered control still holds the
|
|
474
|
+
* previous value at that point (visible with a programmatic value change, e.g. a `v-model`
|
|
475
|
+
* default value). The watcher therefore only raises a flag, and the check runs here, after the
|
|
476
|
+
* render. Renders unrelated to `value` leave the flag down and recompute nothing.
|
|
477
|
+
*/
|
|
478
|
+
componentDidUpdate() {
|
|
479
|
+
if (this.validityCheckPending && this.input !== undefined) {
|
|
480
|
+
this.validityCheckPending = false;
|
|
481
|
+
this.checkValidity();
|
|
482
|
+
if (this.hasDisplayedError) {
|
|
483
|
+
this.setErrorMessage();
|
|
484
|
+
}
|
|
485
|
+
}
|
|
486
|
+
}
|
|
469
487
|
/**
|
|
470
488
|
* Render
|
|
471
489
|
* @returns HTML Element
|
|
472
490
|
*/
|
|
473
491
|
render() {
|
|
474
|
-
return (h("mg-input", { key: '
|
|
492
|
+
return (h("mg-input", { key: '8a9a2c269754aa1259e79dd5e68cc0ed6b578fd1', label: this.label, identifier: this.identifier, class: this.classCollection.join(), labelOnTop: this.labelOnTop, labelHide: this.labelHide, required: this.required, tooltip: this.tooltip, tooltipPosition: this.readonly && this.value === undefined ? 'label' : this.tooltipPosition, helpText: this.formatHelpText(this.helpText), errorMessage: this.errorMessage }, this.readonly
|
|
475
493
|
? this.readonlyValue && this.renderReadonly()
|
|
476
494
|
: [
|
|
477
495
|
h("input", { key: "input", type: "text", class: "mg-c-input__box mg-c-input__box--width", value: this.displayValue(), id: this.identifier, name: this.name, placeholder: this.placeholder, title: this.placeholder, disabled: this.disabled, required: this.required, autocomplete: "off", "aria-invalid": (this.invalid === true).toString(), onInput: this.handleInput, onFocus: this.handleFocus, onBlur: this.handleBlur, ref: (el) => {
|
|
@@ -8,6 +8,8 @@ export class MgInputPassword {
|
|
|
8
8
|
// hasDisplayedError (triggered by blur event)
|
|
9
9
|
this.hasDisplayedError = false;
|
|
10
10
|
this.customErrorMessage = { lock: false };
|
|
11
|
+
// raised when `value` changed, so componentDidUpdate revalidates once the DOM is in sync
|
|
12
|
+
this.validityCheckPending = false;
|
|
11
13
|
/**
|
|
12
14
|
* Input name
|
|
13
15
|
* If not set the value equals the identifier
|
|
@@ -53,10 +55,6 @@ export class MgInputPassword {
|
|
|
53
55
|
* Handle input event
|
|
54
56
|
*/
|
|
55
57
|
this.handleInput = () => {
|
|
56
|
-
this.checkValidity();
|
|
57
|
-
if (this.hasDisplayedError) {
|
|
58
|
-
this.setErrorMessage();
|
|
59
|
-
}
|
|
60
58
|
this.value = this.input.value;
|
|
61
59
|
};
|
|
62
60
|
/**
|
|
@@ -101,6 +99,7 @@ export class MgInputPassword {
|
|
|
101
99
|
};
|
|
102
100
|
}
|
|
103
101
|
handleValue(newValue) {
|
|
102
|
+
this.validityCheckPending = true;
|
|
104
103
|
this.valueChange.emit(newValue);
|
|
105
104
|
}
|
|
106
105
|
watchReadonly(newValue) {
|
|
@@ -181,6 +180,8 @@ export class MgInputPassword {
|
|
|
181
180
|
lock: valid ? false : errorMessageLock,
|
|
182
181
|
message: valid ? undefined : errorMessage,
|
|
183
182
|
};
|
|
183
|
+
// setError() states the validity explicitly, so drop any pending recomputation
|
|
184
|
+
this.validityCheckPending = false;
|
|
184
185
|
this.setValidity(valid);
|
|
185
186
|
this.setErrorMessage(true);
|
|
186
187
|
this.hasDisplayedError = this.invalid;
|
|
@@ -243,15 +244,33 @@ export class MgInputPassword {
|
|
|
243
244
|
// https://stenciljs.com/docs/component-lifecycle#componentwillload
|
|
244
245
|
return setTimeout(() => {
|
|
245
246
|
this.checkValidity();
|
|
247
|
+
// the initial check covers the initial value: nothing is pending yet
|
|
248
|
+
this.validityCheckPending = false;
|
|
246
249
|
}, 0);
|
|
247
250
|
}
|
|
251
|
+
/**
|
|
252
|
+
* Recompute validity once the DOM is in sync with `value`.
|
|
253
|
+
* `@Watch('value')` fires before Stencil re-renders, so the rendered control still holds the
|
|
254
|
+
* previous value at that point (visible with a programmatic value change, e.g. a `v-model`
|
|
255
|
+
* default value). The watcher therefore only raises a flag, and the check runs here, after the
|
|
256
|
+
* render. Renders unrelated to `value` leave the flag down and recompute nothing.
|
|
257
|
+
*/
|
|
258
|
+
componentDidUpdate() {
|
|
259
|
+
if (this.validityCheckPending && this.input !== undefined) {
|
|
260
|
+
this.validityCheckPending = false;
|
|
261
|
+
this.checkValidity();
|
|
262
|
+
if (this.hasDisplayedError) {
|
|
263
|
+
this.setErrorMessage();
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
}
|
|
248
267
|
/**
|
|
249
268
|
* Render
|
|
250
269
|
* @returns HTML Element
|
|
251
270
|
*/
|
|
252
271
|
render() {
|
|
253
272
|
const displayPasswordIcon = this.displayPassword ? 'eye-slash' : 'eye';
|
|
254
|
-
return (h("mg-input", { key: '
|
|
273
|
+
return (h("mg-input", { key: '2d9498c380536dc32177d1ac16a513ee43944a72', label: this.label, identifier: this.identifier, class: this.classCollection.join(), labelOnTop: this.labelOnTop, labelHide: this.labelHide, required: this.required, tooltip: this.tooltip, tooltipPosition: this.readonly && this.value === undefined ? 'label' : this.tooltipPosition, helpText: this.helpText, errorMessage: this.errorMessage }, this.readonly ? (this.value !== undefined && h("b", { class: "mg-c-input__readonly-value" }, '•'.repeat(this.value.length))) : (h("span", { class: "mg-c-input__input-group" }, h("input", { type: this.displayPassword ? 'text' : 'password', class: "mg-c-input__box mg-c-input__box--width", value: this.value, id: this.identifier, name: this.name, placeholder: this.placeholder, title: this.placeholder, disabled: this.disabled, required: this.required, maxlength: this.maxlength, "aria-invalid": (this.invalid === true).toString(), onInput: this.handleInput, onBlur: this.handleBlur, ref: (el) => {
|
|
255
274
|
if (el !== null)
|
|
256
275
|
this.input = el;
|
|
257
276
|
} }), h("mg-button", { label: this.messages.input.password[this.displayPassword ? 'hide' : 'display'], disabled: this.disabled, variant: "flat", class: "mg-c-input__append-button", "is-icon": true, onClick: this.toggleDisplayPassword, "aria-controls": this.identifier }, h("mg-icon", { class: "mg-c-input__input-icon", icon: displayPasswordIcon }))))));
|