@descope/web-components-ui 1.0.70 → 1.0.72
Sign up to get free protection for your applications and to get access to all the features.
- package/dist/index.esm.js +420 -464
- 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-image-index-js.js +1 -0
- 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-image/Image.js +53 -0
- package/src/components/descope-image/index.js +5 -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/index.js +1 -0
- 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/src/theme/components/container.js +1 -1
- package/src/theme/components/image.js +7 -0
- package/src/theme/components/index.js +3 -1
- package/dist/umd/775.js +0 -1
- package/src/baseClasses/BaseInputClass.js +0 -4
@@ -1,26 +1,30 @@
|
|
1
|
-
import
|
1
|
+
import { createBaseInputClass } from '../../../baseClasses/createBaseInputClass';
|
2
2
|
import { getComponentName } from '../../../helpers/componentHelpers';
|
3
|
+
import { createDispatchEvent, createEventListener } from '../../../helpers/mixinsHelpers';
|
3
4
|
import { getSanitizedCharacters, focusElement } from './helpers';
|
4
5
|
|
5
6
|
export const componentName = getComponentName('passcode-internal');
|
6
7
|
|
8
|
+
const observedAttributes = [
|
9
|
+
'disabled',
|
10
|
+
'bordered',
|
11
|
+
'size',
|
12
|
+
'invalid'
|
13
|
+
]
|
14
|
+
|
15
|
+
const BaseInputClass = createBaseInputClass({ componentName, baseSelector: ':host > div' })
|
16
|
+
|
7
17
|
class PasscodeInternal extends BaseInputClass {
|
8
18
|
static get observedAttributes() {
|
9
|
-
return [
|
10
|
-
...(BaseInputClass.observedAttributes || []),
|
11
|
-
'disabled',
|
12
|
-
'bordered',
|
13
|
-
'size',
|
14
|
-
];
|
19
|
+
return observedAttributes.concat(BaseInputClass.observedAttributes || []);
|
15
20
|
}
|
16
21
|
|
17
22
|
static get componentName() {
|
18
23
|
return componentName;
|
19
24
|
}
|
20
25
|
|
21
|
-
#
|
22
|
-
#
|
23
|
-
#boundHandleBlur = this.#handleBlur.bind(this)
|
26
|
+
#dispatchBlur = createDispatchEvent.bind(this, 'blur')
|
27
|
+
#dispatchFocus = createDispatchEvent.bind(this, 'focus')
|
24
28
|
|
25
29
|
constructor() {
|
26
30
|
super();
|
@@ -29,7 +33,7 @@ class PasscodeInternal extends BaseInputClass {
|
|
29
33
|
st-width="35px"
|
30
34
|
data-id=${idx}
|
31
35
|
type="tel"
|
32
|
-
autocomplete="
|
36
|
+
autocomplete="off"
|
33
37
|
></descope-text-field>
|
34
38
|
`)
|
35
39
|
|
@@ -39,8 +43,6 @@ class PasscodeInternal extends BaseInputClass {
|
|
39
43
|
</div>
|
40
44
|
`;
|
41
45
|
|
42
|
-
this.baseSelector = ':host > div';
|
43
|
-
|
44
46
|
this.inputs = Array.from(this.querySelectorAll('descope-text-field'))
|
45
47
|
}
|
46
48
|
|
@@ -66,16 +68,6 @@ class PasscodeInternal extends BaseInputClass {
|
|
66
68
|
return `^$|^\\d{${this.digits},}$`
|
67
69
|
}
|
68
70
|
|
69
|
-
#handleInvalid() {
|
70
|
-
if (this.hasAttribute('invalid')) {
|
71
|
-
this.inputs.forEach(input => input.setAttribute('invalid', 'true'))
|
72
|
-
}
|
73
|
-
}
|
74
|
-
|
75
|
-
#handleValid() {
|
76
|
-
this.inputs.forEach(input => input.removeAttribute('invalid'))
|
77
|
-
}
|
78
|
-
|
79
71
|
getValidity() {
|
80
72
|
if (this.isRequired && !this.value) {
|
81
73
|
return { valueMissing: true };
|
@@ -88,25 +80,17 @@ class PasscodeInternal extends BaseInputClass {
|
|
88
80
|
}
|
89
81
|
};
|
90
82
|
|
91
|
-
onFocus(){
|
92
|
-
this.inputs[0].focus()
|
93
|
-
}
|
94
|
-
|
95
83
|
connectedCallback() {
|
84
|
+
// we are adding listeners before calling to super because it's stopping the events
|
85
|
+
createEventListener.call(this, 'focus', (e) => {
|
86
|
+
// we want to ignore focus events we are dispatching
|
87
|
+
if (e.isTrusted)
|
88
|
+
this.inputs[0].focus()
|
89
|
+
})
|
90
|
+
|
96
91
|
super.connectedCallback?.();
|
97
92
|
|
98
93
|
this.initInputs()
|
99
|
-
|
100
|
-
this.addEventListener('invalid', this.#boundHandleInvalid)
|
101
|
-
this.addEventListener('valid', this.#boundHandleValid)
|
102
|
-
this.addEventListener('blur', this.#boundHandleBlur)
|
103
|
-
}
|
104
|
-
|
105
|
-
disconnectedCallback() {
|
106
|
-
super.connectedCallback?.();
|
107
|
-
this.removeEventListener('invalid', this.#boundHandleInvalid)
|
108
|
-
this.removeEventListener('valid', this.#boundHandleValid)
|
109
|
-
this.removeEventListener('blur', this.#boundHandleBlur)
|
110
94
|
}
|
111
95
|
|
112
96
|
getInputIdx(inputEle) {
|
@@ -138,33 +122,60 @@ class PasscodeInternal extends BaseInputClass {
|
|
138
122
|
focusElement(currentInput);
|
139
123
|
};
|
140
124
|
|
141
|
-
#handleBlur() {
|
142
|
-
this.#handleInvalid()
|
143
|
-
}
|
144
|
-
|
145
125
|
initInputs() {
|
126
|
+
let prevVal = this.value
|
127
|
+
let blurTimerId
|
128
|
+
|
146
129
|
this.inputs.forEach((input) => {
|
147
|
-
|
130
|
+
// in order to simulate blur on the input
|
131
|
+
// we are checking if focus on one of the digits happened immediately after blur on another digit
|
132
|
+
// if not, the component is no longer focused and we should simulate blur
|
133
|
+
createEventListener.call(this, 'blur', (e) => {
|
134
|
+
e.stopImmediatePropagation();
|
135
|
+
|
136
|
+
blurTimerId = setTimeout(() => {
|
137
|
+
blurTimerId = null
|
138
|
+
this.#dispatchBlur()
|
139
|
+
});
|
140
|
+
}, { element: input })
|
141
|
+
|
142
|
+
createEventListener.call(this, 'focus', (e) => {
|
143
|
+
e.stopImmediatePropagation();
|
144
|
+
|
145
|
+
clearTimeout(blurTimerId)
|
146
|
+
if (!blurTimerId) {
|
147
|
+
this.#dispatchFocus()
|
148
|
+
}
|
149
|
+
}, { element: input })
|
150
|
+
|
151
|
+
createEventListener.call(this, 'input', (e) => {
|
148
152
|
const charArr = getSanitizedCharacters(input.value);
|
149
153
|
|
150
154
|
if (!charArr.length) {
|
151
155
|
// if we got an invalid value we want to clear the input
|
152
156
|
input.value = '';
|
153
|
-
if (e.data === null) {
|
154
|
-
// if the user deleted the char, we want to focus the prev digit
|
155
|
-
focusElement(this.getPrevInput(input));
|
156
|
-
}
|
157
157
|
}
|
158
158
|
else this.fillDigits(charArr, input);
|
159
|
-
|
159
|
+
|
160
|
+
// we want to stop input events if the value wasn't change
|
161
|
+
if (prevVal === this.value) {
|
162
|
+
e.stopImmediatePropagation();
|
163
|
+
}
|
164
|
+
}, { element: input })
|
160
165
|
|
161
166
|
input.onkeydown = ({ key }) => {
|
167
|
+
prevVal = this.value
|
168
|
+
|
162
169
|
// when user deletes a digit, we want to focus the previous digit
|
163
170
|
if (key === 'Backspace') {
|
171
|
+
// if the cursor is at 0, we want to move it to 1, so the value will be deleted
|
172
|
+
if (!input.selectionStart) {
|
173
|
+
input.setSelectionRange(1, 1);
|
174
|
+
}
|
164
175
|
setTimeout(() => {
|
165
176
|
focusElement(this.getPrevInput(input));
|
166
177
|
});
|
167
|
-
} else if (key.
|
178
|
+
} else if (key.length === 1) { // we want only characters and not command keys
|
168
179
|
input.value = ''; // we are clearing the previous value so we can override it with the new value
|
169
180
|
}
|
170
181
|
};
|
@@ -174,9 +185,14 @@ class PasscodeInternal extends BaseInputClass {
|
|
174
185
|
attributeChangedCallback(attrName, oldValue, newValue) {
|
175
186
|
super.attributeChangedCallback?.(attrName, oldValue, newValue)
|
176
187
|
|
188
|
+
// sync attributes to inputs
|
177
189
|
if (oldValue !== newValue) {
|
178
|
-
if (
|
179
|
-
this.inputs.forEach(
|
190
|
+
if (observedAttributes.includes(attrName)) {
|
191
|
+
this.inputs.forEach(
|
192
|
+
(input) => newValue === null ?
|
193
|
+
input.removeAttribute(attrName) :
|
194
|
+
input.setAttribute(attrName, newValue)
|
195
|
+
)
|
180
196
|
}
|
181
197
|
}
|
182
198
|
}
|
@@ -1,18 +1,35 @@
|
|
1
|
-
// move create event to here
|
2
1
|
|
2
|
+
// create a dispatch event function that also calls to the onevent function in case it's set
|
3
3
|
// usage example:
|
4
|
-
// #dispatchSomething = createDispatchEvent.bind(this, 'something')
|
5
|
-
|
6
|
-
|
7
|
-
|
4
|
+
// #dispatchSomething = createDispatchEvent.bind(this, 'something' { ...options })
|
5
|
+
// this will dispatch a new event when called, but also call to "onsomething"
|
6
|
+
export function createDispatchEvent(eventName, options = {}) {
|
7
|
+
const event = new Event(eventName, options)
|
8
|
+
|
9
|
+
this[`on${eventName}`]?.(event); // in case we got an event callback as property
|
10
|
+
this.dispatchEvent(event);
|
8
11
|
}
|
9
12
|
|
13
|
+
// add an event listener that is automatically removed on disconnect
|
10
14
|
// usage example:
|
11
|
-
//
|
12
|
-
export function createEventListener(event, callback) {
|
15
|
+
// createEventListener.call(this,'change', this.onChange, { element? , ...options })
|
16
|
+
export function createEventListener(event, callback, { element, ...options } = {}) {
|
17
|
+
const timerId = setTimeout(() => console.warn(this.localName, 'is not using "createBaseClass", events will not be removed automatically on disconnect'), 2000)
|
18
|
+
|
19
|
+
this.addEventListener('connected', () => {
|
20
|
+
clearTimeout(timerId);
|
21
|
+
}, { once: true })
|
22
|
+
|
23
|
+
const targetEle = element || this;
|
13
24
|
const boundCallback = callback.bind(this);
|
14
25
|
|
15
|
-
|
26
|
+
const onDisconnect = () => {
|
27
|
+
targetEle.removeEventListener(event, boundCallback);
|
28
|
+
}
|
29
|
+
|
30
|
+
this.addEventListener('disconnected', onDisconnect, { once: true })
|
31
|
+
|
32
|
+
targetEle.addEventListener(event, boundCallback, options);
|
16
33
|
|
17
|
-
return
|
34
|
+
return onDisconnect
|
18
35
|
}
|
package/src/index.js
CHANGED
@@ -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'
|