@descope/web-components-ui 1.0.70 → 1.0.71
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/index.esm.js +307 -402
- package/dist/index.esm.js.map +1 -1
- package/dist/umd/809.js +1 -0
- package/dist/umd/descope-button-index-js.js +1 -1
- package/dist/umd/descope-checkbox-index-js.js +1 -1
- package/dist/umd/descope-combo-box-index-js.js +1 -1
- package/dist/umd/descope-container-index-js.js +1 -1
- package/dist/umd/descope-date-picker-index-js.js +1 -1
- package/dist/umd/descope-divider-index-js.js +1 -1
- package/dist/umd/descope-email-field-index-js.js +1 -1
- package/dist/umd/descope-link-index-js.js +1 -1
- package/dist/umd/descope-loader-linear-index-js.js +1 -1
- package/dist/umd/descope-loader-radial-index-js.js +1 -1
- package/dist/umd/descope-logo-index-js.js +1 -1
- package/dist/umd/descope-number-field-index-js.js +1 -1
- package/dist/umd/descope-passcode-descope-passcode-internal-index-js.js +1 -1
- package/dist/umd/descope-passcode-index-js.js +1 -1
- package/dist/umd/descope-password-field-index-js.js +1 -1
- package/dist/umd/descope-switch-toggle-index-js.js +1 -1
- package/dist/umd/descope-text-area-index-js.js +1 -1
- package/dist/umd/descope-text-field-index-js.js +1 -1
- package/dist/umd/descope-text-index-js.js +1 -1
- package/dist/umd/index.js +1 -1
- package/package.json +1 -1
- package/src/baseClasses/createBaseClass.js +29 -3
- package/src/baseClasses/createBaseInputClass.js +9 -0
- package/src/components/descope-passcode/Passcode.js +1 -1
- package/src/components/descope-passcode/descope-passcode-internal/PasscodeInternal.js +67 -51
- package/src/helpers/mixinsHelpers.js +26 -9
- package/src/mixins/changeMixin.js +13 -39
- package/src/mixins/componentNameValidationMixin.js +2 -4
- package/src/mixins/createProxy.js +45 -53
- package/src/mixins/createStyleMixin/index.js +2 -0
- package/src/mixins/focusMixin.js +20 -125
- package/src/mixins/hoverableMixin.js +10 -16
- package/src/mixins/index.js +1 -0
- package/src/mixins/inputValidationMixin.js +10 -30
- package/src/mixins/normalizeBooleanAttributesMixin.js +11 -0
- package/src/mixins/proxyInputMixin.js +51 -58
- package/dist/umd/775.js +0 -1
- package/src/baseClasses/BaseInputClass.js +0 -4
@@ -1,47 +1,21 @@
|
|
1
|
-
import { createDispatchEvent } from "../helpers/mixinsHelpers";
|
1
|
+
import { createDispatchEvent, createEventListener } from "../helpers/mixinsHelpers";
|
2
2
|
|
3
3
|
export const changeMixin = (superclass) => class ChangeMixinClass extends superclass {
|
4
|
-
|
5
|
-
#boundedHandleChange
|
6
|
-
#boundedHandleBlur
|
7
|
-
|
8
|
-
#removeChangeListener
|
9
|
-
#removeBlurListener
|
10
|
-
|
11
4
|
#dispatchChange = createDispatchEvent.bind(this, 'change')
|
12
5
|
|
13
|
-
constructor() {
|
14
|
-
super();
|
15
|
-
|
16
|
-
this.#boundedHandleChange = this.#handleChange.bind(this);
|
17
|
-
this.#boundedHandleBlur = this.#handleBlur.bind(this);
|
18
|
-
}
|
19
|
-
|
20
|
-
#handleChange(e) {
|
21
|
-
// we want to listen only to browser events
|
22
|
-
// and not to events we are dispatching
|
23
|
-
if (e.isTrusted) {
|
24
|
-
// we want to control the change events that dispatched by the component
|
25
|
-
// so we are stopping propagation and handling it in handleBlur
|
26
|
-
e.stopPropagation()
|
27
|
-
}
|
28
|
-
}
|
29
|
-
|
30
|
-
#handleBlur() {
|
31
|
-
// on blur, we want to dispatch a change event
|
32
|
-
this.#dispatchChange()
|
33
|
-
}
|
34
|
-
|
35
6
|
connectedCallback() {
|
36
7
|
super.connectedCallback?.();
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
8
|
+
this.prevValue = this.value
|
9
|
+
|
10
|
+
createEventListener.call(this, 'change', (e) => {
|
11
|
+
e.stopPropagation();
|
12
|
+
})
|
13
|
+
|
14
|
+
createEventListener.call(this, 'blur', () => {
|
15
|
+
if (this.value !== this.prevValue) {
|
16
|
+
this.#dispatchChange()
|
17
|
+
this.prevValue = this.value
|
18
|
+
}
|
19
|
+
})
|
46
20
|
}
|
47
21
|
}
|
@@ -1,7 +1,7 @@
|
|
1
1
|
export const componentNameValidationMixin = (superclass) =>
|
2
2
|
class ComponentNameValidationMixinClass extends superclass {
|
3
3
|
#checkComponentName() {
|
4
|
-
const currentComponentName = this.
|
4
|
+
const currentComponentName = this.localName;
|
5
5
|
|
6
6
|
if (!superclass.componentName) {
|
7
7
|
throw Error(
|
@@ -18,8 +18,6 @@ export const componentNameValidationMixin = (superclass) =>
|
|
18
18
|
|
19
19
|
connectedCallback() {
|
20
20
|
super.connectedCallback?.();
|
21
|
-
|
22
|
-
this.#checkComponentName();
|
23
|
-
}
|
21
|
+
this.#checkComponentName();
|
24
22
|
}
|
25
23
|
};
|
@@ -1,6 +1,7 @@
|
|
1
1
|
import { createBaseClass } from '../baseClasses/createBaseClass';
|
2
2
|
import { isFunction } from '../helpers';
|
3
3
|
import { forwardProps, syncAttrs } from '../helpers/componentHelpers';
|
4
|
+
import { createDispatchEvent, createEventListener } from '../helpers/mixinsHelpers';
|
4
5
|
|
5
6
|
export const createProxy = ({
|
6
7
|
componentName,
|
@@ -11,71 +12,62 @@ export const createProxy = ({
|
|
11
12
|
includeAttrsSync = [],
|
12
13
|
includeForwardProps = []
|
13
14
|
}) => {
|
14
|
-
const template = `
|
15
|
-
<style id="create-proxy"></style>
|
16
|
-
<${wrappedEleName}>
|
17
|
-
<slot></slot>
|
18
|
-
${slots.map((slot) => `<slot name="${slot}" slot="${slot}"></slot>`).join('')}
|
19
|
-
</${wrappedEleName}>
|
20
|
-
`;
|
21
|
-
|
22
15
|
class ProxyClass extends createBaseClass({ componentName, baseSelector: wrappedEleName }) {
|
23
|
-
|
24
|
-
|
25
|
-
this.hostElement = this.shadowRoot.host;
|
26
|
-
this.shadowRoot.getElementById('create-proxy').innerHTML =
|
27
|
-
isFunction(style) ? style() : style;
|
28
|
-
}
|
29
|
-
|
30
|
-
#boundOnFocus = this.#onFocus.bind(this);
|
16
|
+
#dispatchBlur = createDispatchEvent.bind(this, 'blur')
|
17
|
+
#dispatchFocus = createDispatchEvent.bind(this, 'focus')
|
31
18
|
|
32
|
-
|
33
|
-
|
34
|
-
|
19
|
+
constructor() {
|
20
|
+
super().attachShadow({ mode: 'open' }).innerHTML = `
|
21
|
+
<style id="create-proxy">${isFunction(style) ? style() : style
|
22
|
+
}</style>
|
23
|
+
<${wrappedEleName}>
|
24
|
+
<slot></slot>
|
25
|
+
${slots.map((slot) => `<slot name="${slot}" slot="${slot}"></slot>`).join('')}
|
26
|
+
</${wrappedEleName}>
|
27
|
+
`;
|
35
28
|
}
|
36
29
|
|
37
|
-
focus = this
|
30
|
+
focus = () => this.baseElement.focus()
|
38
31
|
|
39
32
|
connectedCallback() {
|
40
|
-
|
41
|
-
this.proxyElement = this.shadowRoot.querySelector(wrappedEleName);
|
42
|
-
|
43
|
-
this.addEventListener('focus', this.#boundOnFocus);
|
33
|
+
super.connectedCallback?.();
|
44
34
|
|
45
|
-
|
46
|
-
|
35
|
+
createEventListener.call(this, 'blur', (e) => {
|
36
|
+
this.#dispatchBlur()
|
37
|
+
}, { element: this.baseElement })
|
47
38
|
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
// we need this to happen only when the proxy component is in the light DOM,
|
52
|
-
// otherwise it will focus the nested proxy element
|
53
|
-
this.proxyElement.onkeydown = (e) => {
|
54
|
-
if (e.shiftKey && e.keyCode === 9 && this.getRootNode() === document) {
|
55
|
-
this.removeAttribute('tabindex');
|
56
|
-
// We want to defer the action of setting the tab index back
|
57
|
-
// so it will happen after focusing the previous element
|
58
|
-
setTimeout(() => this.setAttribute('tabindex', '0'), 0);
|
59
|
-
}
|
60
|
-
};
|
39
|
+
createEventListener.call(this, 'focus', (e) => {
|
40
|
+
this.#dispatchFocus()
|
41
|
+
}, { element: this.baseElement })
|
61
42
|
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
43
|
+
createEventListener.call(this, 'focus', (e) => {
|
44
|
+
// if we got a focus event we want to focus the proxy element
|
45
|
+
if (e.isTrusted) {
|
46
|
+
this.focus();
|
47
|
+
}
|
48
|
+
})
|
68
49
|
|
69
|
-
|
70
|
-
|
71
|
-
return;
|
72
|
-
}
|
73
|
-
}
|
50
|
+
// this is needed for components that uses props, such as combo box
|
51
|
+
forwardProps(this, this.baseElement, includeForwardProps)
|
74
52
|
|
75
|
-
|
76
|
-
|
53
|
+
// `onkeydown` is set on `baseElement` support proper tab-index navigation
|
54
|
+
// this support is needed since both proxy host and element catch `focus`/`blur` event
|
55
|
+
// which causes faulty behavior.
|
56
|
+
// we need this to happen only when the proxy component is in the light DOM,
|
57
|
+
// otherwise it will focus the nested proxy element
|
58
|
+
this.baseElement.onkeydown = (e) => {
|
59
|
+
if (e.shiftKey && e.keyCode === 9 && this.getRootNode() === document) {
|
60
|
+
this.removeAttribute('tabindex');
|
61
|
+
// We want to defer the action of setting the tab index back
|
62
|
+
// so it will happen after focusing the previous element
|
63
|
+
setTimeout(() => this.setAttribute('tabindex', '0'), 0);
|
64
|
+
}
|
65
|
+
};
|
77
66
|
|
78
|
-
this.
|
67
|
+
syncAttrs(this.baseElement, this, {
|
68
|
+
excludeAttrs: excludeAttrsSync,
|
69
|
+
includeAttrs: includeAttrsSync
|
70
|
+
});
|
79
71
|
}
|
80
72
|
}
|
81
73
|
|
package/src/mixins/focusMixin.js
CHANGED
@@ -1,130 +1,25 @@
|
|
1
|
-
import {
|
2
|
-
|
3
|
-
const events = [
|
4
|
-
'blur',
|
5
|
-
'focus',
|
6
|
-
'focusin',
|
7
|
-
'focusout',
|
8
|
-
]
|
1
|
+
import { createEventListener } from "../helpers/mixinsHelpers"
|
9
2
|
|
10
3
|
export const focusMixin = (superclass) => class FocusMixinClass extends superclass {
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
#boundedHandleFocus
|
15
|
-
#boundedHandleBlur
|
16
|
-
#boundedHandleFocusIn
|
17
|
-
#boundedHandleFocusOut
|
18
|
-
|
19
|
-
constructor() {
|
20
|
-
super();
|
21
|
-
|
22
|
-
for (const event of events) {
|
23
|
-
this[`dispatch${upperFirst(event)}`] = function () {
|
24
|
-
this.dispatchInputEvent(event)
|
25
|
-
}
|
26
|
-
}
|
27
|
-
|
28
|
-
this.#boundedHandleFocus = this.#handleFocus.bind(this);
|
29
|
-
this.#boundedHandleBlur = this.#handleBlur.bind(this);
|
30
|
-
this.#boundedHandleFocusIn = this.#handleFocusIn.bind(this);
|
31
|
-
this.#boundedHandleFocusOut = this.#handleFocusOut.bind(this);
|
32
|
-
}
|
33
|
-
|
34
|
-
#handleFocus(e) {
|
35
|
-
if (this.isReadOnly) {
|
36
|
-
e.stopPropagation()
|
37
|
-
return
|
38
|
-
}
|
39
|
-
// we want to listen only to browser events
|
40
|
-
// and not to events we are dispatching
|
41
|
-
if (e.isTrusted) { // TODO: check if this is needed, because Vaadin is also dispatching events
|
42
|
-
// we want to control the focus events that dispatched by the component
|
43
|
-
// so we are stopping propagation and handling it in setFocus
|
44
|
-
e.stopPropagation()
|
45
|
-
this.setFocus(true)
|
46
|
-
|
47
|
-
// if the focus event is on the root component (and not on the inner components)
|
48
|
-
// we want to notify the component and let it decide what to do with it
|
49
|
-
if (e.target === this) {
|
50
|
-
this.onFocus(e)
|
51
|
-
}
|
52
|
-
}
|
53
|
-
}
|
54
|
-
|
55
|
-
#handleFocusOut(e) {
|
56
|
-
// we want to listen only to browser events
|
57
|
-
// and not to events we are dispatching
|
58
|
-
if (e.isTrusted) {
|
59
|
-
// we want to control the focus events that dispatched by the component
|
60
|
-
// so we are stopping propagation and handling it in setFocus
|
61
|
-
e.stopPropagation()
|
62
|
-
}
|
63
|
-
}
|
64
|
-
|
65
|
-
#handleFocusIn(e) {
|
66
|
-
// we want to listen only to browser events
|
67
|
-
// and not to events we are dispatching
|
68
|
-
if (e.isTrusted) {
|
69
|
-
// we want to control the focus events that dispatched by the component
|
70
|
-
// so we are stopping propagation and handling it in setFocus
|
71
|
-
e.stopPropagation()
|
72
|
-
}
|
73
|
-
}
|
74
|
-
|
75
|
-
#handleBlur(e) {
|
76
|
-
if (e.isTrusted) {
|
77
|
-
e.stopPropagation()
|
78
|
-
this.setFocus(false)
|
79
|
-
}
|
80
|
-
}
|
81
|
-
|
82
|
-
get isReadOnly() {
|
83
|
-
return this.hasAttribute('readonly') && this.getAttribute('readonly') !== 'false'
|
84
|
-
}
|
85
|
-
|
86
|
-
// we want to debounce the calls to this fn
|
87
|
-
// so we can support input like components with multiple inputs inside
|
88
|
-
setFocus(isFocused) {
|
89
|
-
clearTimeout(this.#setFocusTimer)
|
90
|
-
|
91
|
-
this.#setFocusTimer = setTimeout(() => {
|
92
|
-
if (this.#isFocused !== isFocused) {
|
93
|
-
this.#isFocused = isFocused
|
94
|
-
if (isFocused) {
|
95
|
-
this.dispatchFocus();
|
96
|
-
this.dispatchFocusin();
|
97
|
-
}
|
98
|
-
else {
|
99
|
-
this.dispatchBlur()
|
100
|
-
this.dispatchFocusout();
|
101
|
-
}
|
102
|
-
}
|
103
|
-
})
|
104
|
-
}
|
105
|
-
|
106
|
-
onFocus() {
|
107
|
-
console.warn('onFocus', 'is not implemented')
|
108
|
-
}
|
109
|
-
|
110
|
-
dispatchInputEvent(eventName) {
|
111
|
-
this[`on${eventName}`]?.(); // in case we got an event callback as property
|
112
|
-
this.dispatchEvent(new InputEvent(eventName));
|
113
|
-
}
|
114
|
-
|
4
|
+
// we want to block all native events,
|
5
|
+
// so the input can control when to dispatch it based on its internal behavior
|
115
6
|
connectedCallback() {
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
7
|
+
super.connectedCallback?.();
|
8
|
+
|
9
|
+
createEventListener.call(this, 'blur', (e) => {
|
10
|
+
e.isTrusted && e.stopImmediatePropagation()
|
11
|
+
})
|
12
|
+
|
13
|
+
createEventListener.call(this, 'focus', (e) => {
|
14
|
+
e.isTrusted && e.stopImmediatePropagation();
|
15
|
+
})
|
16
|
+
|
17
|
+
createEventListener.call(this, 'focusout', (e) => {
|
18
|
+
e.isTrusted && e.stopImmediatePropagation();
|
19
|
+
})
|
20
|
+
|
21
|
+
createEventListener.call(this, 'focusin', (e) => {
|
22
|
+
e.isTrusted && e.stopImmediatePropagation();
|
23
|
+
})
|
129
24
|
}
|
130
25
|
}
|
@@ -1,24 +1,18 @@
|
|
1
|
+
import { createEventListener } from "../helpers/mixinsHelpers";
|
2
|
+
|
1
3
|
export const hoverableMixin =
|
2
4
|
(superclass) =>
|
3
5
|
class HoverableMixinClass extends superclass {
|
4
|
-
#boundOnMouseOver = this.#onMouseOver.bind(this)
|
5
|
-
|
6
|
-
#onMouseOver(e) {
|
7
|
-
this.setAttribute('hover', 'true');
|
8
|
-
e.target.addEventListener(
|
9
|
-
'mouseleave',
|
10
|
-
() => this.shadowRoot.host.removeAttribute('hover'),
|
11
|
-
{ once: true }
|
12
|
-
);
|
13
|
-
}
|
14
|
-
|
15
6
|
connectedCallback() {
|
16
7
|
super.connectedCallback?.();
|
17
8
|
|
18
|
-
|
19
|
-
this.
|
20
|
-
|
21
|
-
|
22
|
-
|
9
|
+
createEventListener.call(this, 'mouseover', (e) => {
|
10
|
+
this.setAttribute('hover', 'true');
|
11
|
+
e.target.addEventListener(
|
12
|
+
'mouseleave',
|
13
|
+
() => this.removeAttribute('hover'),
|
14
|
+
{ once: true }
|
15
|
+
);
|
16
|
+
}, { element: this.baseElement })
|
23
17
|
}
|
24
18
|
};
|
package/src/mixins/index.js
CHANGED
@@ -8,3 +8,4 @@ export { focusMixin } from './focusMixin'
|
|
8
8
|
export { inputValidationMixin } from './inputValidationMixin'
|
9
9
|
export { portalMixin } from './portalMixin'
|
10
10
|
export { changeMixin } from './changeMixin'
|
11
|
+
export { normalizeBooleanAttributesMixin } from './normalizeBooleanAttributesMixin'
|
@@ -1,4 +1,4 @@
|
|
1
|
-
import {
|
1
|
+
import { createEventListener } from "../helpers/mixinsHelpers";
|
2
2
|
|
3
3
|
const observedAttributes = [
|
4
4
|
'required',
|
@@ -21,19 +21,12 @@ export const inputValidationMixin = (superclass) => class InputValidationMixinCl
|
|
21
21
|
return true;
|
22
22
|
}
|
23
23
|
|
24
|
-
#dispatchValid = createDispatchEvent.bind(this, 'valid')
|
25
|
-
#dispatchInvalid = createDispatchEvent.bind(this, 'invalid')
|
26
|
-
|
27
24
|
#internals
|
28
25
|
|
29
|
-
#boundedHandleInput
|
30
|
-
|
31
26
|
constructor() {
|
32
27
|
super();
|
33
28
|
|
34
29
|
this.#internals = this.attachInternals();
|
35
|
-
|
36
|
-
this.#boundedHandleInput = this.#handleInput.bind(this);
|
37
30
|
}
|
38
31
|
|
39
32
|
get defaultErrorMsgValueMissing() {
|
@@ -59,7 +52,7 @@ export const inputValidationMixin = (superclass) => class InputValidationMixinCl
|
|
59
52
|
}
|
60
53
|
}
|
61
54
|
|
62
|
-
setValidity() {
|
55
|
+
#setValidity() {
|
63
56
|
const validity = this.getValidity()
|
64
57
|
this.#internals.setValidity(validity, this.getErrorMessage(validity))
|
65
58
|
}
|
@@ -89,7 +82,7 @@ export const inputValidationMixin = (superclass) => class InputValidationMixinCl
|
|
89
82
|
this.#internals.setValidity({ customError: true }, errorMessage)
|
90
83
|
} else {
|
91
84
|
this.#internals.setValidity({})
|
92
|
-
this
|
85
|
+
this.#setValidity()
|
93
86
|
}
|
94
87
|
}
|
95
88
|
|
@@ -101,37 +94,24 @@ export const inputValidationMixin = (superclass) => class InputValidationMixinCl
|
|
101
94
|
return this.getAttribute('pattern')
|
102
95
|
}
|
103
96
|
|
104
|
-
|
105
|
-
this.
|
106
|
-
this.handleDispatchValidationEvents()
|
97
|
+
get form() {
|
98
|
+
return this.#internals.form
|
107
99
|
}
|
108
100
|
|
109
101
|
attributeChangedCallback(attrName, oldValue, newValue) {
|
110
102
|
super.attributeChangedCallback?.(attrName, oldValue, newValue);
|
111
103
|
|
112
104
|
if (observedAttributes.includes(attrName)) {
|
113
|
-
this
|
114
|
-
}
|
115
|
-
}
|
116
|
-
|
117
|
-
handleDispatchValidationEvents() {
|
118
|
-
if (this.checkValidity()) {
|
119
|
-
this.#dispatchValid()
|
120
|
-
} else {
|
121
|
-
this.#dispatchInvalid()
|
105
|
+
this.#setValidity();
|
122
106
|
}
|
123
107
|
}
|
124
108
|
|
125
109
|
connectedCallback() {
|
126
110
|
super.connectedCallback?.();
|
111
|
+
createEventListener.call(this, 'change', this.#setValidity)
|
112
|
+
createEventListener.call(this, 'invalid', (e) => e.stopPropagation())
|
113
|
+
createEventListener.call(this, 'input', this.#setValidity)
|
127
114
|
|
128
|
-
this
|
129
|
-
|
130
|
-
this.setValidity();
|
131
|
-
this.handleDispatchValidationEvents();
|
132
|
-
}
|
133
|
-
|
134
|
-
disconnectedCallback() {
|
135
|
-
this.removeEventListener('input', this.#boundedHandleInput)
|
115
|
+
this.#setValidity();
|
136
116
|
}
|
137
117
|
}
|
@@ -0,0 +1,11 @@
|
|
1
|
+
// we want all the valueless attributes to have "true" value
|
2
|
+
export const normalizeBooleanAttributesMixin = (superclass) => class NormalizeBooleanAttributesMixinClass extends superclass {
|
3
|
+
attributeChangedCallback(attrName, oldValue, newValue) {
|
4
|
+
if (newValue === '') {
|
5
|
+
this.setAttribute(attrName, 'true')
|
6
|
+
newValue = 'true'
|
7
|
+
}
|
8
|
+
|
9
|
+
super.attributeChangedCallback?.(attrName, oldValue, newValue)
|
10
|
+
}
|
11
|
+
}
|
@@ -1,3 +1,4 @@
|
|
1
|
+
import { createDispatchEvent, createEventListener } from "../helpers/mixinsHelpers";
|
1
2
|
import { inputValidationMixin } from "./inputValidationMixin";
|
2
3
|
|
3
4
|
const errorAttrs = ['invalid', 'has-error-message'];
|
@@ -36,26 +37,17 @@ export const proxyInputMixin = (superclass) =>
|
|
36
37
|
|
37
38
|
#inputElement
|
38
39
|
|
39
|
-
#
|
40
|
-
#boundHandleInvalid
|
41
|
-
#boundHandleValid
|
40
|
+
#dispatchChange = createDispatchEvent.bind(this, 'change')
|
42
41
|
|
43
42
|
constructor() {
|
44
43
|
super();
|
45
44
|
|
46
45
|
this.#inputElement = super.inputElement
|
47
|
-
|
48
|
-
this.#boundHandleFocus = this.#handleFocus.bind(this);
|
49
|
-
this.#boundHandleInvalid = this.#handleInvalid.bind(this);
|
50
|
-
this.#boundHandleValid = this.#handleValid.bind(this);
|
51
|
-
|
52
|
-
this.baseEle = this.shadowRoot.querySelector(this.baseSelector);
|
53
|
-
|
54
46
|
}
|
55
47
|
|
56
48
|
get inputElement() {
|
57
|
-
const inputSlot = this.
|
58
|
-
const textAreaSlot = this.
|
49
|
+
const inputSlot = this.baseElement.shadowRoot.querySelector('slot[name="input"]')
|
50
|
+
const textAreaSlot = this.baseElement.shadowRoot.querySelector('slot[name="textarea"]')
|
59
51
|
|
60
52
|
this.#inputElement ??= getNestedInput(inputSlot) || getNestedInput(textAreaSlot)
|
61
53
|
|
@@ -74,7 +66,6 @@ export const proxyInputMixin = (superclass) =>
|
|
74
66
|
|
75
67
|
reportValidityOnInternalInput() {
|
76
68
|
setTimeout(() => {
|
77
|
-
this.baseEle.focus() //TODO: check if this is needed
|
78
69
|
this.inputElement.reportValidity()
|
79
70
|
})
|
80
71
|
}
|
@@ -87,72 +78,74 @@ export const proxyInputMixin = (superclass) =>
|
|
87
78
|
}
|
88
79
|
}
|
89
80
|
|
90
|
-
|
91
|
-
if (!this.checkValidity()) {
|
81
|
+
handleInternalInputErrorMessage() {
|
82
|
+
if (!this.inputElement.checkValidity()) {
|
92
83
|
this.inputElement.setCustomValidity(this.validationMessage)
|
93
84
|
}
|
94
85
|
}
|
95
86
|
|
96
|
-
|
97
|
-
|
98
|
-
#handleFocus(e) {
|
99
|
-
if (e.relatedTarget?.form) {
|
100
|
-
if (!this.checkValidity()) {
|
101
|
-
this.setAttribute('invalid', 'true');
|
102
|
-
}
|
103
|
-
|
104
|
-
if (this.hasAttribute('invalid')) {
|
105
|
-
this.reportValidityOnInternalInput()
|
106
|
-
}
|
107
|
-
}
|
108
|
-
}
|
109
|
-
|
110
|
-
#handleInvalid() {
|
111
|
-
this.setInternalInputErrorMessage()
|
87
|
+
#handleErrorMessage() {
|
88
|
+
this.handleInternalInputErrorMessage()
|
112
89
|
this.setAttribute('error-message', this.validationMessage)
|
113
90
|
}
|
114
91
|
|
115
|
-
#handleValid() {
|
116
|
-
this.removeAttribute('invalid')
|
117
|
-
}
|
118
|
-
|
119
92
|
connectedCallback() {
|
120
93
|
super.connectedCallback?.();
|
121
94
|
|
122
|
-
|
123
|
-
|
124
|
-
|
95
|
+
createEventListener.call(this, 'input', (e) => {
|
96
|
+
if (!this.inputElement.checkValidity()) {
|
97
|
+
this.inputElement.setCustomValidity('')
|
98
|
+
// after updating the input validity we want to trigger set validity on the wrapping element
|
99
|
+
// so we will get the updated validity
|
100
|
+
this.setCustomValidity('')
|
101
|
+
|
102
|
+
// Vaadin is getting the input event before us,
|
103
|
+
// so in order to make sure they use the updated validity
|
104
|
+
// we calling their fn after updating the input validity
|
105
|
+
this.baseElement.__onInput(e)
|
106
|
+
|
107
|
+
this.#handleErrorMessage()
|
108
|
+
}
|
109
|
+
}, { element: this.inputElement })
|
125
110
|
|
126
|
-
|
127
|
-
|
111
|
+
createEventListener.call(this, 'change', () => {
|
112
|
+
this.#dispatchChange()
|
113
|
+
}, { element: this.baseElement })
|
128
114
|
|
129
|
-
|
130
|
-
this.
|
131
|
-
|
132
|
-
this
|
115
|
+
createEventListener.call(this, 'blur', () => {
|
116
|
+
if (!this.checkValidity()) {
|
117
|
+
this.setAttribute('invalid', 'true')
|
118
|
+
this.#handleErrorMessage()
|
119
|
+
}
|
133
120
|
})
|
134
121
|
|
122
|
+
createEventListener.call(this, 'focus', (e) => {
|
123
|
+
// when clicking on the form submit button and the input is invalid
|
124
|
+
// we want it to appear as invalid
|
125
|
+
if (e.relatedTarget?.form === this.form) {
|
126
|
+
if (!this.checkValidity()) {
|
127
|
+
this.setAttribute('invalid', 'true');
|
128
|
+
}
|
129
|
+
|
130
|
+
if (this.hasAttribute('invalid')) {
|
131
|
+
this.reportValidityOnInternalInput()
|
132
|
+
}
|
133
|
+
}
|
134
|
+
})
|
135
|
+
|
136
|
+
createEventListener.call(this, 'invalid', this.#handleErrorMessage)
|
137
|
+
|
138
|
+
this.handleInternalInputErrorMessage()
|
139
|
+
|
135
140
|
// this is needed in order to make sure the form input validation is working
|
141
|
+
// we do not want it to happen when the component is nested
|
136
142
|
if (!this.hasAttribute('tabindex') && this.getRootNode() === document) {
|
137
143
|
this.setAttribute('tabindex', 0);
|
138
144
|
}
|
139
145
|
|
140
146
|
// sync properties
|
141
147
|
propertyObserver(this, this.inputElement, 'value');
|
148
|
+
propertyObserver(this, this.inputElement, 'selectionStart');
|
142
149
|
this.setSelectionRange = this.inputElement.setSelectionRange?.bind(this.inputElement);
|
143
150
|
}
|
144
|
-
|
145
|
-
disconnectedCallback() {
|
146
|
-
this.removeEventListener('focus', this.#boundHandleFocus)
|
147
|
-
this.removeEventListener('invalid', this.#boundHandleInvalid)
|
148
|
-
this.removeEventListener('valid', this.#boundHandleValid)
|
149
|
-
}
|
150
|
-
|
151
|
-
attributeChangedCallback(attrName, oldValue, newValue) {
|
152
|
-
super.attributeChangedCallback?.(attrName, oldValue, newValue);
|
153
|
-
|
154
|
-
if (attrName === 'invalid' && newValue === '') {
|
155
|
-
this.setAttribute('invalid', 'true')
|
156
|
-
}
|
157
|
-
}
|
158
151
|
};
|