@descope/web-components-ui 1.0.70 → 1.0.71
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
};
|