@descope/web-components-ui 1.0.70 → 1.0.72
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 +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'
|