@mgdis/mg-components 6.26.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 +506 -161
- package/dist/cjs/mg-components.cjs.js +1 -1
- package/dist/cjs/mg-illustration.cjs.entry.js +1 -1
- package/dist/cjs/mg-input-combobox.cjs.entry.js +60 -8
- package/dist/cjs/mg-input-file.cjs.entry.js +26 -3
- package/dist/cjs/mg-input-rich-text-editor.cjs.entry.js +2304 -1131
- package/dist/cjs/mg-loader.cjs.entry.js +1 -1
- package/dist/cjs/mg-progress.cjs.entry.js +1 -1
- package/dist/collection/assets/illustrations/index.js +1 -1
- package/dist/collection/assets/jodit/jodit.css +14 -1
- package/dist/collection/components/atoms/mg-button/mg-button.js +86 -13
- package/dist/collection/components/atoms/mg-tooltip/mg-tooltip.js +103 -55
- package/dist/collection/components/molecules/inputs/mg-input-checkbox/mg-input-checkbox.js +25 -4
- package/dist/collection/components/molecules/inputs/mg-input-combobox/mg-input-combobox.js +59 -7
- 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-details/mg-details.js +5 -1
- package/dist/collection/components/molecules/mg-form/mg-form.js +83 -9
- package/dist/collection/components/molecules/mg-illustrated-message/mg-illustrated-message.js +1 -1
- package/dist/collection/components/molecules/mg-loader/mg-loader.js +1 -1
- package/dist/collection/components/molecules/mg-message/mg-message.js +3 -3
- package/dist/collection/components/molecules/mg-modal/mg-modal.js +2 -2
- package/dist/collection/components/molecules/mg-panel/mg-panel.js +1 -1
- package/dist/collection/components/molecules/mg-popover/mg-popover-content/mg-popover-content.js +1 -1
- package/dist/collection/components/molecules/mg-popover/mg-popover.js +1 -1
- package/dist/collection/components/molecules/mg-progress/mg-progress.js +1 -1
- package/dist/collection/components/molecules/mg-skip-links/mg-skip-links.js +1 -1
- package/dist/collection/components/molecules/mg-tabs/mg-tabs.js +1 -1
- package/dist/components/mg-button2.js +1 -1
- package/dist/components/mg-details.js +1 -1
- package/dist/components/mg-form.js +1 -1
- package/dist/components/mg-illustrated-message.js +1 -1
- package/dist/components/mg-illustration.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 +285 -285
- 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-input-toggle.js +1 -1
- package/dist/components/mg-input2.js +1 -1
- package/dist/components/mg-loader2.js +1 -1
- package/dist/components/mg-menu-item2.js +1 -1
- package/dist/components/mg-message2.js +1 -1
- package/dist/components/mg-modal.js +1 -1
- package/dist/components/mg-panel.js +1 -1
- package/dist/components/mg-popover-content2.js +1 -1
- package/dist/components/mg-popover2.js +1 -1
- package/dist/components/mg-progress.js +1 -1
- package/dist/components/mg-skip-links.js +1 -1
- package/dist/components/mg-tabs2.js +1 -1
- package/dist/components/mg-tooltip2.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 +507 -162
- package/dist/esm/mg-components.js +1 -1
- package/dist/esm/mg-illustration.entry.js +1 -1
- package/dist/esm/mg-input-combobox.entry.js +60 -8
- package/dist/esm/mg-input-file.entry.js +26 -3
- package/dist/esm/mg-input-rich-text-editor.entry.js +2304 -1131
- package/dist/esm/mg-loader.entry.js +1 -1
- package/dist/esm/mg-progress.entry.js +1 -1
- 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-85ebacd6.entry.js +1 -0
- package/dist/mg-components/{p-96fc508e.entry.js → p-c2beff2c.entry.js} +1 -1
- package/dist/mg-components/p-d4a4eb78.entry.js +1 -0
- package/dist/mg-components/p-e8b5e8d8.entry.js +1667 -0
- package/dist/mg-components/p-f308e2e4.entry.js +1 -0
- 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/atoms/mg-tooltip/mg-tooltip.d.ts +33 -6
- 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 +21 -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 +22 -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-206505b5.entry.js +0 -1667
- package/dist/mg-components/p-3ad7441c.entry.js +0 -1
- package/dist/mg-components/p-5dff879a.entry.js +0 -1
- package/dist/mg-components/p-b20f3698.entry.js +0 -1
- package/dist/mg-components/p-bf2252c9.entry.js +0 -1
- package/dist/mg-components/p-f8e58181.entry.js +0 -1
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/*!
|
|
2
2
|
* jodit - Jodit is an awesome and useful wysiwyg editor with filebrowser
|
|
3
3
|
* Author: Chupurnov <chupurnov@gmail.com> (https://xdsoft.net/jodit/)
|
|
4
|
-
* Version: v4.
|
|
4
|
+
* Version: v4.14.2
|
|
5
5
|
* Url: https://xdsoft.net/jodit/
|
|
6
6
|
* License(s): MIT
|
|
7
7
|
*/
|
|
@@ -3841,6 +3841,19 @@ a + .jodit-file-browser-tree__source-title {
|
|
|
3841
3841
|
.jodit-color-picker__color-item:active {
|
|
3842
3842
|
border: 2px solid var(--jd-color-border-selected);
|
|
3843
3843
|
}
|
|
3844
|
+
.jodit-color-picker__extra {
|
|
3845
|
+
display: flex;
|
|
3846
|
+
align-items: center;
|
|
3847
|
+
justify-content: space-between;
|
|
3848
|
+
}
|
|
3849
|
+
.jodit-color-picker__hex input {
|
|
3850
|
+
width: 80px;
|
|
3851
|
+
padding: 2px 4px;
|
|
3852
|
+
border: 1px solid var(--jd-color-border);
|
|
3853
|
+
border-radius: var(--jd-border-radius-default);
|
|
3854
|
+
font-family: monospace;
|
|
3855
|
+
font-size: var(--jd-font-size-small, 12px);
|
|
3856
|
+
}
|
|
3844
3857
|
.jodit-color-picker__native svg {
|
|
3845
3858
|
display: inline-block;
|
|
3846
3859
|
width: 16px;
|
|
@@ -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,
|
|
@@ -30,6 +30,13 @@ export class MgTooltip {
|
|
|
30
30
|
* Disable tooltip
|
|
31
31
|
*/
|
|
32
32
|
this.disabled = false;
|
|
33
|
+
/**
|
|
34
|
+
* Toggle the popper scroll and resize listeners, only needed while the tooltip is visible
|
|
35
|
+
* @param enabled - listeners state to apply
|
|
36
|
+
*/
|
|
37
|
+
this.setPopperEventListeners = (enabled) => {
|
|
38
|
+
this.popper.setOptions((options) => (Object.assign(Object.assign({}, options), { modifiers: [...options.modifiers, { name: 'eventListeners', enabled }] })));
|
|
39
|
+
};
|
|
33
40
|
/**
|
|
34
41
|
* Show tooltip
|
|
35
42
|
*/
|
|
@@ -37,7 +44,7 @@ export class MgTooltip {
|
|
|
37
44
|
// Make the tooltip visible
|
|
38
45
|
this.mgTooltipContent.dataset.show = '';
|
|
39
46
|
// Enable the event listeners
|
|
40
|
-
this.
|
|
47
|
+
this.setPopperEventListeners(true);
|
|
41
48
|
// hide when click outside on nextTick to prevent event to trigger after creation
|
|
42
49
|
nextTick(() => {
|
|
43
50
|
this.windows.forEach((localWindow) => {
|
|
@@ -53,7 +60,7 @@ export class MgTooltip {
|
|
|
53
60
|
// Hide the tooltip
|
|
54
61
|
this.mgTooltipContent.removeAttribute('data-show');
|
|
55
62
|
// Disable the event listeners
|
|
56
|
-
this.
|
|
63
|
+
this.setPopperEventListeners(false);
|
|
57
64
|
// Remove event listener
|
|
58
65
|
this.windows.forEach((localWindow) => {
|
|
59
66
|
localWindow.removeEventListener('click', this.handleClickOutside, false);
|
|
@@ -117,6 +124,47 @@ export class MgTooltip {
|
|
|
117
124
|
this.resetGuard = () => {
|
|
118
125
|
this.guard = undefined;
|
|
119
126
|
};
|
|
127
|
+
/**
|
|
128
|
+
* Display tooltip when the tooltiped element takes focus
|
|
129
|
+
*/
|
|
130
|
+
this.handleTooltipedElementFocus = () => {
|
|
131
|
+
this.guard = Guard.FOCUS;
|
|
132
|
+
this.setDisplay(true);
|
|
133
|
+
};
|
|
134
|
+
/**
|
|
135
|
+
* Hide tooltip when the tooltiped element loses focus
|
|
136
|
+
*/
|
|
137
|
+
this.handleTooltipedElementBlur = () => {
|
|
138
|
+
this.resetGuard();
|
|
139
|
+
this.setDisplay(false);
|
|
140
|
+
};
|
|
141
|
+
/**
|
|
142
|
+
* Display tooltip when the tooltiped element is hovered
|
|
143
|
+
*/
|
|
144
|
+
this.handleTooltipedElementMouseenter = () => {
|
|
145
|
+
this.tooltipMouseListenerAction(Guard.HOVER_TOOLTIPED_ELEMENT, 'mouseenter', Guard.HOVER_TOOLTIP_ELEMENT);
|
|
146
|
+
};
|
|
147
|
+
/**
|
|
148
|
+
* Hide tooltip when the tooltiped element is not hovered anymore
|
|
149
|
+
*/
|
|
150
|
+
this.handleTooltipedElementMouseleave = () => {
|
|
151
|
+
this.tooltipMouseListenerAction(Guard.HOVER_TOOLTIPED_ELEMENT, 'mouseleave', Guard.HOVER_TOOLTIP_ELEMENT);
|
|
152
|
+
};
|
|
153
|
+
/**
|
|
154
|
+
* Manage all tooltiped element listeners
|
|
155
|
+
* @param element - element to bind or unbind
|
|
156
|
+
* @param action - process to apply on element listeners
|
|
157
|
+
*/
|
|
158
|
+
this.manageTooltipedElementListeners = (element, action) => {
|
|
159
|
+
element[action]('focus', this.handleTooltipedElementFocus);
|
|
160
|
+
// as `click` event is the composition of `mousedown` and `mouseup` event,
|
|
161
|
+
// and `click` event trigger `focus` event after a success `mousedown` wich set the `guard` to `Guard.FOCUS`
|
|
162
|
+
// we reset the guard on `mouseup` event to unlock tooltip
|
|
163
|
+
element[action]('mouseup', this.resetGuard);
|
|
164
|
+
element[action]('blur', this.handleTooltipedElementBlur);
|
|
165
|
+
element[action]('mouseenter', this.handleTooltipedElementMouseenter);
|
|
166
|
+
element[action]('mouseleave', this.handleTooltipedElementMouseleave);
|
|
167
|
+
};
|
|
120
168
|
/**
|
|
121
169
|
* Update slot content when it is a mg-button
|
|
122
170
|
* @param mgButton - slotted mg-button
|
|
@@ -143,10 +191,11 @@ export class MgTooltip {
|
|
|
143
191
|
}
|
|
144
192
|
};
|
|
145
193
|
/**
|
|
146
|
-
* Set popper instance
|
|
147
|
-
* @param strategy - popper strategy to apply on instance
|
|
194
|
+
* Set popper instance on the current tooltiped element
|
|
148
195
|
*/
|
|
149
|
-
this.setPopper = (
|
|
196
|
+
this.setPopper = () => {
|
|
197
|
+
// mg-popover content is already `fixed`: position a nested tooltip relatively to it
|
|
198
|
+
const strategy = this.element.closest('mg-popover') !== null ? 'absolute' : 'fixed';
|
|
150
199
|
// Create popperjs tooltip
|
|
151
200
|
this.popper = createPopper(this.tooltipedElement, this.mgTooltipContent, {
|
|
152
201
|
placement: this.placement,
|
|
@@ -172,29 +221,20 @@ export class MgTooltip {
|
|
|
172
221
|
* @param element - tooltiped element
|
|
173
222
|
*/
|
|
174
223
|
this.setTooltipedElement = (element) => {
|
|
224
|
+
// unbind the previous element so its listeners do not stack up on every `disabled` toggle
|
|
225
|
+
if (this.tooltipedElement !== undefined) {
|
|
226
|
+
this.manageTooltipedElementListeners(this.tooltipedElement, 'removeEventListener');
|
|
227
|
+
}
|
|
175
228
|
this.tooltipedElement = element;
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
this.resetGuard();
|
|
186
|
-
});
|
|
187
|
-
this.tooltipedElement.addEventListener('blur', () => {
|
|
188
|
-
this.resetGuard();
|
|
189
|
-
this.setDisplay(false);
|
|
190
|
-
});
|
|
191
|
-
// manage tooltipElement & tooltipedElement mouseenter/mouseleave events
|
|
192
|
-
this.tooltipedElement.addEventListener('mouseenter', () => {
|
|
193
|
-
this.tooltipMouseListenerAction(Guard.HOVER_TOOLTIPED_ELEMENT, 'mouseenter', Guard.HOVER_TOOLTIP_ELEMENT);
|
|
194
|
-
});
|
|
195
|
-
this.tooltipedElement.addEventListener('mouseleave', () => {
|
|
196
|
-
this.tooltipMouseListenerAction(Guard.HOVER_TOOLTIPED_ELEMENT, 'mouseleave', Guard.HOVER_TOOLTIP_ELEMENT);
|
|
197
|
-
});
|
|
229
|
+
this.manageTooltipedElementListeners(this.tooltipedElement, 'addEventListener');
|
|
230
|
+
// popper freezes its reference element, so re-create it to follow the new one.
|
|
231
|
+
// The initial instance comes from `componentDidLoad`.
|
|
232
|
+
if (this.popper !== undefined) {
|
|
233
|
+
this.popper.destroy();
|
|
234
|
+
this.setPopper();
|
|
235
|
+
// a fresh instance has its listeners enabled, realign them
|
|
236
|
+
this.setPopperEventListeners(this.display);
|
|
237
|
+
}
|
|
198
238
|
};
|
|
199
239
|
/**
|
|
200
240
|
* Set button mutation observer
|
|
@@ -222,17 +262,36 @@ export class MgTooltip {
|
|
|
222
262
|
}).observe(mgButton, { attributes: true });
|
|
223
263
|
};
|
|
224
264
|
/**
|
|
225
|
-
*
|
|
226
|
-
*
|
|
265
|
+
* Reconcile the tooltiped element a11y: the tooltip is restituted — `aria-describedby` exposed, and a
|
|
266
|
+
* non-interactive element made focusable (`tabindex` + `role`) — unless it is disabled and not displayed.
|
|
267
|
+
* Pre-existing `aria-describedby` ids are preserved.
|
|
227
268
|
*/
|
|
228
|
-
this.
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
269
|
+
this.updateTooltipedElementA11y = () => {
|
|
270
|
+
const restitute = !this.disabled || this.display;
|
|
271
|
+
const currentValue = this.tooltipedElement.getAttribute('aria-describedby');
|
|
272
|
+
const ids = new Set(currentValue !== null ? currentValue.split(' ') : []);
|
|
273
|
+
if (restitute) {
|
|
274
|
+
ids.add(this.identifier);
|
|
275
|
+
}
|
|
276
|
+
else {
|
|
277
|
+
ids.delete(this.identifier);
|
|
278
|
+
}
|
|
279
|
+
if (ids.size > 0) {
|
|
280
|
+
this.tooltipedElement.setAttribute('aria-describedby', [...ids].join(' '));
|
|
232
281
|
}
|
|
233
282
|
else {
|
|
234
|
-
|
|
235
|
-
|
|
283
|
+
this.tooltipedElement.removeAttribute('aria-describedby');
|
|
284
|
+
}
|
|
285
|
+
if (this.hasCustomTabIndex) {
|
|
286
|
+
if (restitute) {
|
|
287
|
+
this.tooltipedElement.setAttribute('role', 'button');
|
|
288
|
+
if (!this.tooltipedElement.hasAttribute('tabindex'))
|
|
289
|
+
this.tooltipedElement.setAttribute('tabindex', '0');
|
|
290
|
+
}
|
|
291
|
+
else {
|
|
292
|
+
this.tooltipedElement.removeAttribute('role');
|
|
293
|
+
this.tooltipedElement.removeAttribute('tabindex');
|
|
294
|
+
}
|
|
236
295
|
}
|
|
237
296
|
};
|
|
238
297
|
}
|
|
@@ -257,14 +316,11 @@ export class MgTooltip {
|
|
|
257
316
|
else {
|
|
258
317
|
this.hide();
|
|
259
318
|
}
|
|
319
|
+
this.updateTooltipedElementA11y();
|
|
260
320
|
}
|
|
261
|
-
watchDisabled(
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
this.tooltipedElement.removeAttribute('tabindex');
|
|
265
|
-
else if (!this.tooltipedElement.hasAttribute('tabindex'))
|
|
266
|
-
this.tooltipedElement.setAttribute('tabindex', '0');
|
|
267
|
-
}
|
|
321
|
+
watchDisabled() {
|
|
322
|
+
// Only fires on runtime changes; the initial reconcile is done in componentDidLoad via watchDisplay.
|
|
323
|
+
this.updateTooltipedElementA11y();
|
|
268
324
|
}
|
|
269
325
|
/**
|
|
270
326
|
* Render tooltip content element and init listeners
|
|
@@ -304,7 +360,6 @@ export class MgTooltip {
|
|
|
304
360
|
// Get tooltip element
|
|
305
361
|
this.renderTooltipContent();
|
|
306
362
|
//validate properties
|
|
307
|
-
this.watchDisabled(this.disabled);
|
|
308
363
|
this.watchMessage(this.message);
|
|
309
364
|
this.watchIdentifier(this.identifier);
|
|
310
365
|
}
|
|
@@ -333,20 +388,13 @@ export class MgTooltip {
|
|
|
333
388
|
if (this.element.querySelector('mg-button[full-width]') !== null) {
|
|
334
389
|
this.element.classList.add('mg-c-tooltip--full-width');
|
|
335
390
|
}
|
|
336
|
-
//
|
|
337
|
-
|
|
338
|
-
this.hasCustomTabIndex = true;
|
|
339
|
-
slotElement.tabIndex = 0;
|
|
340
|
-
// Add role on non-interactive element to work with "aria-describedby" for screen readers
|
|
341
|
-
slotElement.setAttribute('role', 'button');
|
|
342
|
-
}
|
|
343
|
-
// apply a11y aria
|
|
344
|
-
this.setAriaDescribedby(slotElement);
|
|
391
|
+
// Non-interactive slotted element (no focusable element found): needs synthesized focus to be reachable.
|
|
392
|
+
this.hasCustomTabIndex = [undefined, null].includes(interactiveElement);
|
|
345
393
|
// set Tooltip
|
|
346
|
-
this.setPopper(
|
|
394
|
+
this.setPopper();
|
|
347
395
|
// add document keyboard handler
|
|
348
396
|
document.addEventListener('keydown', this.handlePressEscape);
|
|
349
|
-
// check props
|
|
397
|
+
// check props (also does the initial a11y reconcile)
|
|
350
398
|
this.watchDisplay(this.display);
|
|
351
399
|
}
|
|
352
400
|
/**
|
|
@@ -371,7 +419,7 @@ export class MgTooltip {
|
|
|
371
419
|
* @returns HTML Element
|
|
372
420
|
*/
|
|
373
421
|
render() {
|
|
374
|
-
return (h(Host, { key: '
|
|
422
|
+
return (h(Host, { key: '1e9cfc59c335510c21d159253e3f8e1b4256a465' }, h("slot", { key: 'c52d57e3302d4865a2365ac62c931af2948bdf03' }), h("slot", { key: '527b5d8ae562ed1f5faed74356f10be5aca10ac4', name: "content" })));
|
|
375
423
|
}
|
|
376
424
|
static get is() { return "mg-tooltip"; }
|
|
377
425
|
static get encapsulation() { return "shadow"; }
|
|
@@ -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
|
|
@@ -295,9 +297,7 @@ export class MgInputCheckbox {
|
|
|
295
297
|
* Renders the readonly value of the input.
|
|
296
298
|
* @returns The rendered readonly value.
|
|
297
299
|
*/
|
|
298
|
-
this.renderReadonly = (readonlyValue) => {
|
|
299
|
-
return this.inputVerticalList ? (h("ul", { class: "mg-c-input__readonly-value" }, readonlyValue.map((value) => (h("li", { key: value }, h("b", null, value)))))) : (h("b", { class: "mg-c-input__readonly-value" }, readonlyValue.join(', ')));
|
|
300
|
-
};
|
|
300
|
+
this.renderReadonly = (readonlyValue) => (h("b", { class: "mg-c-input__readonly-value" }, readonlyValue.join(', ')));
|
|
301
301
|
/**
|
|
302
302
|
* Renders the MgInputCheckboxList component.
|
|
303
303
|
* @returns The rendered MgInputCheckboxList component.
|
|
@@ -305,6 +305,7 @@ export class MgInputCheckbox {
|
|
|
305
305
|
this.renderMgInputCheckboxList = () => (h(MgInputCheckboxList, { checkboxes: this.checkboxItems, id: this.checkboxesId, disabled: this.disabled, name: this.name, invalid: this.invalid }));
|
|
306
306
|
}
|
|
307
307
|
validateValue(newValue) {
|
|
308
|
+
this.validityCheckPending = true;
|
|
308
309
|
if (isCheckboxItems(newValue)) {
|
|
309
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) })));
|
|
310
311
|
this.valueChange.emit(newValue);
|
|
@@ -437,6 +438,8 @@ export class MgInputCheckbox {
|
|
|
437
438
|
lock: valid ? false : errorMessageLock,
|
|
438
439
|
message: valid ? undefined : errorMessage,
|
|
439
440
|
};
|
|
441
|
+
// setError() states the validity explicitly, so drop any pending recomputation
|
|
442
|
+
this.validityCheckPending = false;
|
|
440
443
|
this.setValidity(valid);
|
|
441
444
|
this.setErrorMessage(true);
|
|
442
445
|
this.hasDisplayedError = this.invalid;
|
|
@@ -519,8 +522,26 @@ export class MgInputCheckbox {
|
|
|
519
522
|
// https://stenciljs.com/docs/component-lifecycle#componentwillload
|
|
520
523
|
return setTimeout(() => {
|
|
521
524
|
this.checkValidity();
|
|
525
|
+
// the initial check covers the initial value: nothing is pending yet
|
|
526
|
+
this.validityCheckPending = false;
|
|
522
527
|
}, 0);
|
|
523
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
|
+
}
|
|
524
545
|
/**
|
|
525
546
|
* add listeners and DOM attributed
|
|
526
547
|
*/
|
|
@@ -605,7 +626,7 @@ export class MgInputCheckbox {
|
|
|
605
626
|
else {
|
|
606
627
|
inputContent = this.renderCheckboxMulti();
|
|
607
628
|
}
|
|
608
|
-
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));
|
|
609
630
|
}
|
|
610
631
|
static get is() { return "mg-input-checkbox"; }
|
|
611
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
|
|
@@ -352,11 +354,29 @@ export class MgInputCombobox {
|
|
|
352
354
|
}
|
|
353
355
|
}
|
|
354
356
|
};
|
|
357
|
+
/**
|
|
358
|
+
* Check if an option matches the current filter
|
|
359
|
+
* @param option - option to match
|
|
360
|
+
* @returns true if the option matches the filter
|
|
361
|
+
*/
|
|
362
|
+
this.matchFilter = (option) => this.filter === '' || option.title.toLocaleLowerCase().includes(this.filter.toLocaleLowerCase());
|
|
363
|
+
/**
|
|
364
|
+
* Get all the options matching the current filter
|
|
365
|
+
* @returns filtered options
|
|
366
|
+
*/
|
|
367
|
+
this.getFilteredOptions = () => this.options.items.filter(this.matchFilter);
|
|
355
368
|
/**
|
|
356
369
|
* Get first page with filter applied
|
|
357
370
|
* @returns first page with filter applied
|
|
358
371
|
*/
|
|
359
|
-
this.getFirstPage = () =>
|
|
372
|
+
this.getFirstPage = () => {
|
|
373
|
+
const page = this.options.getPage(0, this.matchFilter);
|
|
374
|
+
// `Paginate` keeps the unfiltered total: realign it with the filtered options, except in
|
|
375
|
+
// `fetchurl` mode where the total counts the options left to load
|
|
376
|
+
if (this.fetchurl === undefined)
|
|
377
|
+
page.total = this.getFilteredOptions().length;
|
|
378
|
+
return page;
|
|
379
|
+
};
|
|
360
380
|
/**
|
|
361
381
|
* Loading wrapper with a given action
|
|
362
382
|
* @param action - wrapped loading action
|
|
@@ -400,9 +420,7 @@ export class MgInputCombobox {
|
|
|
400
420
|
return promise()
|
|
401
421
|
.then(() => {
|
|
402
422
|
// parse full items list from options
|
|
403
|
-
const options = this.
|
|
404
|
-
? this.options.items
|
|
405
|
-
: this.options.items.filter((option) => option.title.includes(this.filter));
|
|
423
|
+
const options = this.getFilteredOptions();
|
|
406
424
|
// update visual focus
|
|
407
425
|
if (action.name !== 'load-data') {
|
|
408
426
|
index = this.page.getIndexFromCursor(action.cursor, this.loadingAction === 'load-more' && index >= 0 ? options[index] : this.option);
|
|
@@ -439,7 +457,9 @@ export class MgInputCombobox {
|
|
|
439
457
|
return nextTick(() => {
|
|
440
458
|
if (typeof this.page.next === 'function') {
|
|
441
459
|
const nextPage = this.page.next();
|
|
442
|
-
this.page = new Page(Object.assign(Object.assign({}, nextPage), { items: [...this.page.items, ...nextPage.items]
|
|
460
|
+
this.page = new Page(Object.assign(Object.assign({}, nextPage), { items: [...this.page.items, ...nextPage.items],
|
|
461
|
+
// `Paginate` keeps the unfiltered total, realign it as in `getFirstPage`
|
|
462
|
+
total: this.getFilteredOptions().length }));
|
|
443
463
|
}
|
|
444
464
|
});
|
|
445
465
|
}
|
|
@@ -503,10 +523,11 @@ export class MgInputCombobox {
|
|
|
503
523
|
};
|
|
504
524
|
}
|
|
505
525
|
watchValue(newValue) {
|
|
526
|
+
this.validityCheckPending = true;
|
|
506
527
|
this.filter = this.getValueTitle();
|
|
507
528
|
this.valueChange.emit(newValue);
|
|
508
529
|
}
|
|
509
|
-
watchItems(newValue) {
|
|
530
|
+
watchItems(newValue, oldValue) {
|
|
510
531
|
if (this.fetchurl !== undefined && newValue === undefined) {
|
|
511
532
|
// skip newValue check
|
|
512
533
|
return;
|
|
@@ -529,6 +550,17 @@ export class MgInputCombobox {
|
|
|
529
550
|
else {
|
|
530
551
|
throw new Error(`<mg-input-combobox> prop "items" values must be the same type, string or Option. Passed value: ${toString(newValue)}.`);
|
|
531
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
|
+
}
|
|
532
564
|
}
|
|
533
565
|
watchIdentifier(newValue) {
|
|
534
566
|
if (isValidString(newValue)) {
|
|
@@ -658,6 +690,8 @@ export class MgInputCombobox {
|
|
|
658
690
|
lock: valid ? false : errorMessageLock,
|
|
659
691
|
message: valid ? undefined : errorMessage,
|
|
660
692
|
};
|
|
693
|
+
// setError() states the validity explicitly, so drop any pending recomputation
|
|
694
|
+
this.validityCheckPending = false;
|
|
661
695
|
this.setValidity(valid);
|
|
662
696
|
this.setErrorMessage(true);
|
|
663
697
|
this.hasDisplayedError = this.invalid;
|
|
@@ -747,8 +781,26 @@ export class MgInputCombobox {
|
|
|
747
781
|
// https://stenciljs.com/docs/component-lifecycle#componentwillload
|
|
748
782
|
return setTimeout(() => {
|
|
749
783
|
this.checkValidity();
|
|
784
|
+
// the initial check covers the initial value: nothing is pending yet
|
|
785
|
+
this.validityCheckPending = false;
|
|
750
786
|
}, 0);
|
|
751
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
|
+
}
|
|
752
804
|
/**
|
|
753
805
|
* Render
|
|
754
806
|
* @returns HTML Element
|
|
@@ -778,7 +830,7 @@ export class MgInputCombobox {
|
|
|
778
830
|
else {
|
|
779
831
|
activeDescendant = (_e = this.value) === null || _e === void 0 ? void 0 : _e.value;
|
|
780
832
|
}
|
|
781
|
-
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: {
|
|
782
834
|
'--mg-c-input-list-width': ((_f = this.input) === null || _f === void 0 ? void 0 : _f.offsetWidth) > 0 ? `${this.input.offsetWidth}px` : undefined,
|
|
783
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) => {
|
|
784
836
|
if (el !== null)
|