@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.
Files changed (48) hide show
  1. package/dist/index.esm.js +420 -464
  2. package/dist/index.esm.js.map +1 -1
  3. package/dist/umd/809.js +1 -0
  4. package/dist/umd/descope-button-index-js.js +1 -1
  5. package/dist/umd/descope-checkbox-index-js.js +1 -1
  6. package/dist/umd/descope-combo-box-index-js.js +1 -1
  7. package/dist/umd/descope-container-index-js.js +1 -1
  8. package/dist/umd/descope-date-picker-index-js.js +1 -1
  9. package/dist/umd/descope-divider-index-js.js +1 -1
  10. package/dist/umd/descope-email-field-index-js.js +1 -1
  11. package/dist/umd/descope-image-index-js.js +1 -0
  12. package/dist/umd/descope-link-index-js.js +1 -1
  13. package/dist/umd/descope-loader-linear-index-js.js +1 -1
  14. package/dist/umd/descope-loader-radial-index-js.js +1 -1
  15. package/dist/umd/descope-logo-index-js.js +1 -1
  16. package/dist/umd/descope-number-field-index-js.js +1 -1
  17. package/dist/umd/descope-passcode-descope-passcode-internal-index-js.js +1 -1
  18. package/dist/umd/descope-passcode-index-js.js +1 -1
  19. package/dist/umd/descope-password-field-index-js.js +1 -1
  20. package/dist/umd/descope-switch-toggle-index-js.js +1 -1
  21. package/dist/umd/descope-text-area-index-js.js +1 -1
  22. package/dist/umd/descope-text-field-index-js.js +1 -1
  23. package/dist/umd/descope-text-index-js.js +1 -1
  24. package/dist/umd/index.js +1 -1
  25. package/package.json +1 -1
  26. package/src/baseClasses/createBaseClass.js +29 -3
  27. package/src/baseClasses/createBaseInputClass.js +9 -0
  28. package/src/components/descope-image/Image.js +53 -0
  29. package/src/components/descope-image/index.js +5 -0
  30. package/src/components/descope-passcode/Passcode.js +1 -1
  31. package/src/components/descope-passcode/descope-passcode-internal/PasscodeInternal.js +67 -51
  32. package/src/helpers/mixinsHelpers.js +26 -9
  33. package/src/index.js +1 -0
  34. package/src/mixins/changeMixin.js +13 -39
  35. package/src/mixins/componentNameValidationMixin.js +2 -4
  36. package/src/mixins/createProxy.js +45 -53
  37. package/src/mixins/createStyleMixin/index.js +2 -0
  38. package/src/mixins/focusMixin.js +20 -125
  39. package/src/mixins/hoverableMixin.js +10 -16
  40. package/src/mixins/index.js +1 -0
  41. package/src/mixins/inputValidationMixin.js +10 -30
  42. package/src/mixins/normalizeBooleanAttributesMixin.js +11 -0
  43. package/src/mixins/proxyInputMixin.js +51 -58
  44. package/src/theme/components/container.js +1 -1
  45. package/src/theme/components/image.js +7 -0
  46. package/src/theme/components/index.js +3 -1
  47. package/dist/umd/775.js +0 -1
  48. package/src/baseClasses/BaseInputClass.js +0 -4
@@ -1,26 +1,30 @@
1
- import BaseInputClass from '../../../baseClasses/BaseInputClass';
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
- #boundHandleInvalid = this.#handleInvalid.bind(this)
22
- #boundHandleValid = this.#handleValid.bind(this)
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="none"
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
- input.oninput = (e) => {
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.match(/^(\d)$/g)) { // if input is a digit
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 (PasscodeInternal.observedAttributes.includes(attrName) && !BaseInputClass.observedAttributes.includes(attrName)) {
179
- this.inputs.forEach((input) => input.setAttribute(attrName, newValue))
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
- export function createDispatchEvent(eventName) {
6
- this[`on${eventName}`]?.(); // in case we got an event callback as property
7
- this.dispatchEvent(new Event(eventName));
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
- // #removeChangeListener = createEventListener.call(this,'change', this.onChange)
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
- this.addEventListener(event, boundCallback);
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 () => this.removeEventListener(event, boundCallback)
34
+ return onDisconnect
18
35
  }
package/src/index.js CHANGED
@@ -15,6 +15,7 @@ import './components/descope-switch-toggle';
15
15
  import './components/descope-text';
16
16
  import './components/descope-text-area';
17
17
  import './components/descope-text-field';
18
+ import './components/descope-image';
18
19
 
19
20
  export {
20
21
  globalsThemeToStyle,
@@ -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
- this.#removeChangeListener = addEventListener.bind()
39
- this.addEventListener('change', this.#boundedHandleChange, true)
40
- this.addEventListener('blur', this.#boundedHandleBlur)
41
- }
42
-
43
- disconnectedCallback() {
44
- this.removeEventListener('change', this.#boundedHandleChange)
45
- this.removeEventListener('blur', this.#boundedHandleBlur)
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.shadowRoot.host.tagName.toLowerCase();
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
- if (this.shadowRoot.isConnected) {
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
- constructor() {
24
- super().attachShadow({ mode: 'open' }).innerHTML = template;
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
- // we want to focus on the proxy element when focusing our WCP
33
- #onFocus() {
34
- this.proxyElement.focus();
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.#onFocus
30
+ focus = () => this.baseElement.focus()
38
31
 
39
32
  connectedCallback() {
40
- if (this.shadowRoot.isConnected) {
41
- this.proxyElement = this.shadowRoot.querySelector(wrappedEleName);
42
-
43
- this.addEventListener('focus', this.#boundOnFocus);
33
+ super.connectedCallback?.();
44
34
 
45
- // this is needed for components that uses props, such as combo box
46
- forwardProps(this.hostElement, this.proxyElement, includeForwardProps)
35
+ createEventListener.call(this, 'blur', (e) => {
36
+ this.#dispatchBlur()
37
+ }, { element: this.baseElement })
47
38
 
48
- // `onkeydown` is set on `proxyElement` support proper tab-index navigation
49
- // this support is needed since both proxy host and element catch `focus`/`blur` event
50
- // which causes faulty behavior.
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
- syncAttrs(this.proxyElement, this.hostElement, {
63
- excludeAttrs: excludeAttrsSync,
64
- includeAttrs: includeAttrsSync
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
- attributeChangedCallback() {
70
- if (!this.proxyElement) {
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
- disconnectedCallback() {
76
- super.disconnectedCallback?.()
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.removeEventListener('focus', this.#boundOnFocus);
67
+ syncAttrs(this.baseElement, this, {
68
+ excludeAttrs: excludeAttrsSync,
69
+ includeAttrs: includeAttrsSync
70
+ });
79
71
  }
80
72
  }
81
73
 
@@ -131,6 +131,8 @@ export const createStyleMixin =
131
131
  }
132
132
 
133
133
  disconnectedCallback() {
134
+ super.disconnectedCallback?.();
135
+
134
136
  this.#disconnectThemeManager?.();
135
137
  }
136
138
  };
@@ -1,130 +1,25 @@
1
- import { upperFirst } from "../helpers";
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
- #isFocused = false
12
- #setFocusTimer
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
- super.connectedCallback?.();
117
-
118
- this.addEventListener('focus', this.#boundedHandleFocus, true)
119
- this.addEventListener('blur', this.#boundedHandleBlur, true)
120
- this.addEventListener('focusin', this.#boundedHandleFocusIn)
121
- this.addEventListener('focusout', this.#boundedHandleFocusOut)
122
- }
123
-
124
- disconnectedCallback() {
125
- this.removeEventListener('focus', this.#boundedHandleFocus)
126
- this.removeEventListener('blur', this.#boundedHandleBlur)
127
- this.removeEventListener('focusin', this.#boundedHandleFocusIn)
128
- this.removeEventListener('focusout', this.#boundedHandleFocusOut)
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
- const baseElement = this.shadowRoot.querySelector(
19
- this.baseSelector
20
- );
21
-
22
- baseElement.addEventListener('mouseover', this.#boundOnMouseOver);
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
  };
@@ -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'