@descope/web-components-ui 1.0.79 → 1.0.81

Sign up to get free protection for your applications and to get access to all the features.
Files changed (41) hide show
  1. package/dist/cjs/index.cjs.js.map +1 -1
  2. package/dist/index.esm.js +1236 -598
  3. package/dist/index.esm.js.map +1 -1
  4. package/dist/umd/387.js +1 -0
  5. package/dist/umd/988.js +1 -1
  6. package/dist/umd/descope-checkbox-descope-checkbox-internal-index-js.js +1 -0
  7. package/dist/umd/descope-checkbox-index-js.js +1 -1
  8. package/dist/umd/descope-new-password-descope-new-password-internal-index-js.js +1 -0
  9. package/dist/umd/descope-new-password-index-js.js +1 -0
  10. package/dist/umd/descope-passcode-descope-passcode-internal-index-js.js +1 -1
  11. package/dist/umd/descope-password-field-index-js.js +1 -1
  12. package/dist/umd/descope-phone-field-descope-phone-field-internal-index-js.js +1 -1
  13. package/dist/umd/descope-phone-field-index-js.js +1 -1
  14. package/dist/umd/index.js +1 -1
  15. package/package.json +1 -1
  16. package/src/components/descope-checkbox/Checkbox.js +161 -13
  17. package/src/components/descope-checkbox/descope-checkbox-internal/CheckboxInternal.js +63 -0
  18. package/src/components/descope-checkbox/descope-checkbox-internal/index.js +3 -0
  19. package/src/components/descope-checkbox/index.js +3 -3
  20. package/src/components/descope-new-password/NewPassword.js +129 -0
  21. package/src/components/descope-new-password/descope-new-password-internal/NewPasswordInternal.js +189 -0
  22. package/src/components/descope-new-password/descope-new-password-internal/componentName.js +3 -0
  23. package/src/components/descope-new-password/descope-new-password-internal/index.js +4 -0
  24. package/src/components/descope-new-password/index.js +6 -0
  25. package/src/components/descope-passcode/descope-passcode-internal/PasscodeInternal.js +3 -0
  26. package/src/components/descope-password-field/PasswordField.js +37 -36
  27. package/src/components/descope-phone-field/PhoneField.js +3 -2
  28. package/src/components/descope-phone-field/descope-phone-field-internal/PhoneFieldInternal.js +4 -1
  29. package/src/components/descope-switch-toggle/SwitchToggle.js +1 -1
  30. package/src/components/descope-switch-toggle/index.js +1 -1
  31. package/src/helpers/componentHelpers.js +3 -3
  32. package/src/index.js +1 -0
  33. package/src/mixins/createProxy.js +2 -25
  34. package/src/mixins/inputEventsDispatchingMixin.js +15 -15
  35. package/src/mixins/inputValidationMixin.js +6 -2
  36. package/src/mixins/proxyInputMixin.js +6 -36
  37. package/src/theme/components/checkbox.js +71 -1
  38. package/src/theme/components/index.js +3 -1
  39. package/src/theme/components/newPassword.js +48 -0
  40. package/src/theme/components/passwordField.js +55 -3
  41. package/src/theme/components/switchToggle.js +4 -4
@@ -0,0 +1,129 @@
1
+ import { forwardAttrs, getComponentName } from '../../helpers/componentHelpers';
2
+ import { compose } from '../../helpers';
3
+ import {
4
+ createStyleMixin,
5
+ proxyInputMixin,
6
+ draggableMixin,
7
+ createProxy,
8
+ } from '../../mixins';
9
+ import { componentName as descopeInternalComponentName } from './descope-new-password-internal/componentName';
10
+ import PasswordField from "../descope-password-field/PasswordField";
11
+
12
+ export const componentName = getComponentName('new-password');
13
+
14
+ const customMixin = (superclass) =>
15
+ class NewPasswordMixinClass extends superclass {
16
+ constructor() {
17
+ super();
18
+ }
19
+
20
+ init() {
21
+ super.init?.();
22
+
23
+ const template = document.createElement('template');
24
+
25
+ template.innerHTML = `
26
+ <${descopeInternalComponentName}
27
+ name="new-password"
28
+ tabindex="-1"
29
+ slot="input"
30
+ ></${descopeInternalComponentName}>
31
+ `;
32
+
33
+ this.baseElement.appendChild(template.content.cloneNode(true));
34
+
35
+ this.inputElement = this.shadowRoot.querySelector(descopeInternalComponentName);
36
+
37
+ forwardAttrs(this, this.inputElement, {
38
+ includeAttrs: [
39
+ 'password-label',
40
+ 'password-placeholder',
41
+ 'confirm-label',
42
+ 'confirm-placeholder',
43
+ 'full-width',
44
+ 'size',
45
+ 'bordered',
46
+ 'label',
47
+ 'has-confirm',
48
+ 'invalid',
49
+ ]
50
+ });
51
+ }
52
+ };
53
+
54
+ const { host, internalInputsWrapper } = {
55
+ host: { selector: () => ':host' },
56
+ internalInputsWrapper: { selector: 'descope-new-password-internal .wrapper' }
57
+ }
58
+ const NewPassword = compose(
59
+ createStyleMixin({
60
+ mappings: {
61
+ componentWidth: { ...host, property: 'width' },
62
+ requiredContent: { ...host, property: 'content' },
63
+ inputLabelTextColor: {
64
+ selector: PasswordField.componentName,
65
+ property: PasswordField.cssVarList.labelTextColor
66
+ },
67
+ inputTextColor: {
68
+ selector: PasswordField.componentName,
69
+ property: PasswordField.cssVarList.inputTextColor
70
+ },
71
+ inputsGap: {...internalInputsWrapper, property: 'gap'}
72
+ }
73
+ }),
74
+ draggableMixin,
75
+ proxyInputMixin,
76
+ customMixin,
77
+ )(
78
+ createProxy({
79
+ slots: [],
80
+ wrappedEleName: 'vaadin-text-field',
81
+ style: () => overrides,
82
+ excludeAttrsSync: ['tabindex'],
83
+ componentName
84
+ })
85
+ );
86
+
87
+ const overrides = `
88
+ :host {
89
+ --vaadin-field-default-width: auto;
90
+ display: inline-block;
91
+ }
92
+ vaadin-text-field {
93
+ padding: 0;
94
+ width: 100%;
95
+ height: 100%;
96
+ }
97
+ vaadin-text-field::part(input-field) {
98
+ min-height: 0;
99
+ background: transparent;
100
+ overflow: hidden;
101
+ box-shadow: none;
102
+ }
103
+ vaadin-text-field::part(input-field)::after {
104
+ background: transparent;
105
+ opacity: 0;
106
+ }
107
+ descope-new-password-internal {
108
+ -webkit-mask-image: none;
109
+ padding: 0;
110
+ min-height: 0;
111
+ width: 100%;
112
+ height: 100%;
113
+ }
114
+ descope-new-password-internal > .wrapper {
115
+ width: 100%;
116
+ height: 100%;
117
+ display: flex;
118
+ flex-direction: column;
119
+ }
120
+ descope-password-field {
121
+ display: block;
122
+ width: 100%;
123
+ }
124
+ descope-new-password-internal vaadin-password-field::before {
125
+ height: initial;
126
+ }
127
+ `;
128
+
129
+ export default NewPassword;
@@ -0,0 +1,189 @@
1
+ import { createBaseInputClass } from '../../../baseClasses/createBaseInputClass';
2
+ import { observeAttributes } from '../../../helpers/componentHelpers';
3
+ import NewPassword from '../NewPassword';
4
+ import { componentName } from './componentName';
5
+
6
+ const passwordAttrPrefixRegex = /^password-/
7
+ const confirmAttrPrefixRegex = /^confirm-/
8
+
9
+ const removeAttrPrefix = (attr, prefix) => attr.replace(prefix, '')
10
+
11
+ const passwordInputAttrs = ['password-label', 'password-placeholder'];
12
+ const confirmInputAttrs = ['confirm-label', 'confirm-placeholder'];
13
+ const commonAttrs = [
14
+ 'disabled',
15
+ 'bordered',
16
+ 'size',
17
+ 'full-width',
18
+ 'maxlength',
19
+ 'invalid',
20
+ ];
21
+
22
+ const inputRelatedAttrs = [].concat(commonAttrs, passwordInputAttrs, confirmInputAttrs);
23
+
24
+ const BaseInputClass = createBaseInputClass({ componentName, baseSelector: 'div' });
25
+
26
+ class NewPasswordInternal extends BaseInputClass {
27
+ static get observedAttributes() {
28
+ return ['has-confirm'].concat(
29
+ BaseInputClass.observedAttributes || [],
30
+ inputRelatedAttrs,
31
+ );
32
+ }
33
+
34
+ constructor() {
35
+ super();
36
+
37
+ this.innerHTML = `
38
+ <div class="wrapper"></div>
39
+ `;
40
+
41
+ this.wrapperEle = this.querySelector('.wrapper')
42
+ }
43
+
44
+ get value() {
45
+ return this.passwordInput?.value || '';
46
+ }
47
+
48
+ set value(val) {
49
+ if (val === this.value) return;
50
+ this.value = val;
51
+ }
52
+
53
+ get hasConfirm() {
54
+ return this.getAttribute('has-confirm') === 'true';
55
+ }
56
+
57
+ getValidity() {
58
+ if (this.isRequired && !this.value) {
59
+ return { valueMissing: true };
60
+ }
61
+ if (this.hasConfirm && this.confirmInput && this.value !== this.confirmInput.value) {
62
+ return { patternMismatch: true };
63
+ }
64
+
65
+ const min = this.getAttribute('minlength');
66
+ const minVal = parseInt(min, 10) || 0;
67
+ const minValid = this.value.length >= minVal;
68
+ if (!minValid) {
69
+ return { tooShort: true }
70
+ }
71
+
72
+ return {}
73
+ };
74
+
75
+ init() {
76
+ this.addEventListener('focus', (e) => {
77
+ // we want to ignore focus events we are dispatching
78
+ if (e.isTrusted) {
79
+ this.passwordInput.focus()
80
+ }
81
+ });
82
+
83
+ super.init();
84
+ this.renderInputs(this.hasConfirm);
85
+ }
86
+
87
+ renderInputs(shouldRenderConfirm) {
88
+ let template = `<descope-password-field data-id="password"></descope-password-field>`;
89
+
90
+ if (shouldRenderConfirm) {
91
+ template += `<descope-password-field data-id="confirm"></descope-password-field>`
92
+ }
93
+
94
+ this.wrapperEle.innerHTML = template;
95
+
96
+ this.passwordInput = this.querySelector('[data-id="password"]');
97
+ this.confirmInput = this.querySelector('[data-id="confirm"]');
98
+
99
+ this.inputs = [this.passwordInput, this.confirmInput];
100
+
101
+ this.initInputs();
102
+
103
+ // we are calling attributeChangedCallback with all the input related attributes
104
+ // in order to set it on the newly generated input
105
+ [...passwordInputAttrs, ...confirmInputAttrs, ...commonAttrs].forEach(attr => {
106
+ this.attributeChangedCallback(attr, null, this.getAttribute(attr))
107
+ })
108
+ }
109
+
110
+ // the inputs are not required but we still want it to have a required
111
+ // indicator in case the root component is required
112
+ handleIndicatorStyle() {
113
+ for (const input of this.inputs) {
114
+ const styleTag = document.createElement('style');
115
+ styleTag.innerHTML = `
116
+ :host::part(required-indicator)::after {
117
+ content: var(${NewPassword.cssVarList.requiredContent});
118
+ }
119
+ `;
120
+ input?.shadowRoot.appendChild(styleTag);
121
+ }
122
+ }
123
+
124
+ get isInvalid() {
125
+ return this.hasAttribute('invalid') && this.getAttribute('invalid') !== 'false'
126
+ }
127
+
128
+ // for some reason, Vaadin is removing the invalid attribute on several events,
129
+ // e.g. focus, input, etc..., so we need to make sure the inputs will stay invalid
130
+ // if the root component is invalid
131
+ handleInputsInvalidAttribute() {
132
+ this.inputs.forEach(input => {
133
+ input && observeAttributes(input, (changedAttributes) => {
134
+ if (changedAttributes.includes('invalid')) {
135
+ const inputInvalidValue = input.getAttribute('invalid')
136
+ const rootInvalidValue = this.getAttribute('invalid')
137
+
138
+ if (this.isInvalid && rootInvalidValue !== inputInvalidValue) {
139
+ input.setAttribute('invalid', 'true')
140
+ }
141
+ }
142
+ }, {});
143
+ })
144
+ }
145
+
146
+ initInputs() {
147
+ this.handleIndicatorStyle()
148
+ this.handleInputsInvalidAttribute();
149
+ this.handleFocusEventsDispatching(this.inputs)
150
+ }
151
+
152
+ toggleBooleanAttribute(ele, name, value) {
153
+ value === null ?
154
+ ele?.removeAttribute(name) :
155
+ ele?.setAttribute(name, value)
156
+ }
157
+
158
+ attributeChangedCallback(attrName, oldValue, newValue) {
159
+ super.attributeChangedCallback?.(attrName, oldValue, newValue);
160
+
161
+ if (oldValue !== newValue) {
162
+ if (attrName === 'has-confirm') {
163
+ this.renderInputs(newValue !== null && newValue !== 'false');
164
+ }
165
+ else if (commonAttrs.includes(attrName)) {
166
+ this.inputs.forEach(
167
+ (input) => this.toggleBooleanAttribute(input, attrName, newValue)
168
+ );
169
+ }
170
+ else if (passwordInputAttrs.includes(attrName)) {
171
+ this.toggleBooleanAttribute(
172
+ this.passwordInput,
173
+ removeAttrPrefix(attrName, passwordAttrPrefixRegex),
174
+ newValue
175
+ );
176
+ }
177
+ else if (confirmInputAttrs.includes(attrName)) {
178
+ this.toggleBooleanAttribute(
179
+ this.confirmInput,
180
+ removeAttrPrefix(attrName, confirmAttrPrefixRegex),
181
+ newValue
182
+ );
183
+
184
+ }
185
+ }
186
+ }
187
+ }
188
+
189
+ export default NewPasswordInternal;
@@ -0,0 +1,3 @@
1
+ import { getComponentName } from "../../../helpers/componentHelpers";
2
+
3
+ export const componentName = getComponentName('new-password-internal');
@@ -0,0 +1,4 @@
1
+ import NewPasswordInternal from "./NewPasswordInternal"
2
+ import { componentName } from "./componentName"
3
+
4
+ customElements.define(componentName, NewPasswordInternal)
@@ -0,0 +1,6 @@
1
+ import NewPassword, { componentName } from './NewPassword';
2
+ import '../descope-text-field'
3
+ import '../descope-password-field'
4
+ import './descope-new-password-internal'
5
+
6
+ customElements.define(componentName, NewPassword);
@@ -172,6 +172,9 @@ class PasscodeInternal extends BaseInputClass {
172
172
 
173
173
  forwardAttrs(this, input, { includeAttrs: forwardAttributes })
174
174
  })
175
+
176
+ this.handleFocusEventsDispatching(this.inputs);
177
+ this.handleInputEventDispatching();
175
178
  }
176
179
 
177
180
  attributeChangedCallback(attrName, oldValue, newValue) {
@@ -5,7 +5,6 @@ import {
5
5
  proxyInputMixin,
6
6
  componentNameValidationMixin
7
7
  } from '../../mixins';
8
- import textFieldMappings from '../descope-text-field/textFieldMappings';
9
8
  import { compose } from '../../helpers';
10
9
  import { getComponentName } from '../../helpers/componentHelpers';
11
10
 
@@ -13,16 +12,37 @@ export const componentName = getComponentName('password-field');
13
12
 
14
13
  let overrides = ``;
15
14
 
15
+ const { host, inputWrapper, inputElement, inputElementPlaceholder, revealButton, label, requiredIndicator } = {
16
+ host: () => ':host',
17
+ inputWrapper: { selector: '::part(input-field)' },
18
+ inputElement: { selector: '> input' },
19
+ inputElementPlaceholder: { selector: '> input:placeholder-shown' },
20
+ revealButton: { selector: 'vaadin-password-field-button' },
21
+ label: { selector: '> label' },
22
+ requiredIndicator: { selector: '::part(required-indicator)::after' },
23
+ }
24
+
16
25
  const PasswordField = compose(
17
26
  createStyleMixin({
18
27
  mappings: {
19
- ...textFieldMappings,
20
- revealCursor: [
21
- {
22
- selector: '::part(reveal-button)::before',
23
- property: 'cursor'
24
- }
25
- ]
28
+ wrapperBorderStyle: { ...inputWrapper, property: 'border-style' },
29
+ wrapperBorderWidth: { ...inputWrapper, property: 'border-width' },
30
+ wrapperBorderColor: { ...inputWrapper, property: 'border-color' },
31
+ wrapperBorderRadius: { ...inputWrapper, property: 'border-radius' },
32
+ labelTextColor: [
33
+ { ...label, property: 'color' },
34
+ { ...requiredIndicator, property: 'color' }
35
+ ],
36
+ inputTextColor: [{ ...inputElement, property: 'color' }],
37
+ placeholderTextColor: { ...inputElementPlaceholder, property: 'color' },
38
+ fontSize: [{}, host],
39
+ height: inputWrapper,
40
+ padding: inputWrapper,
41
+ pointerCursor: [
42
+ { ...revealButton, property: 'cursor' },
43
+ { ...label, property: 'cursor' },
44
+ { ...requiredIndicator, property: 'cursor' }
45
+ ],
26
46
  }
27
47
  }),
28
48
  draggableMixin,
@@ -43,38 +63,19 @@ overrides = `
43
63
  display: inline-block;
44
64
  }
45
65
  vaadin-password-field {
46
- margin: 0;
47
- padding: 0;
48
- }
49
- vaadin-password-field::part(input-field) {
50
- overflow: hidden;
66
+ width: 100%;
51
67
  }
52
- vaadin-password-field[readonly] > input:placeholder-shown {
53
- opacity: 1;
68
+ vaadin-password-field > input {
69
+ min-height: 0;
54
70
  }
55
- vaadin-password-field input:-webkit-autofill,
56
- vaadin-password-field input:-webkit-autofill::first-line,
57
- vaadin-password-field input:-webkit-autofill:hover,
58
- vaadin-password-field input:-webkit-autofill:active,
59
- vaadin-password-field input:-webkit-autofill:focus {
60
- -webkit-text-fill-color: var(${PasswordField.cssVarList.color});
61
- box-shadow: 0 0 0 var(${PasswordField.cssVarList.height}) var(${PasswordField.cssVarList.backgroundColor}) inset;
62
- }
63
- vaadin-password-field > label,
64
71
  vaadin-password-field::part(input-field) {
65
- cursor: pointer;
66
- color: var(${PasswordField.cssVarList.color});
67
- }
68
- vaadin-password-field::part(input-field):focus {
69
- cursor: text;
70
- }
71
- vaadin-password-field[required]::part(required-indicator)::after {
72
- font-size: "12px";
73
- content: "*";
74
- color: var(${PasswordField.cssVarList.color});
72
+ background: transparent;
75
73
  }
76
- vaadin-password-field[readonly]::part(input-field)::after {
77
- border: 0 solid;
74
+ vaadin-password-field::part(input-field)::after {
75
+ opacity: 0;
76
+ }
77
+ vaadin-password-field::before {
78
+ height: initial;
78
79
  }
79
80
  `;
80
81
 
@@ -7,7 +7,6 @@ import {
7
7
  draggableMixin,
8
8
  proxyInputMixin
9
9
  } from '../../mixins';
10
-
11
10
  import TextField from '../descope-text-field/TextField';
12
11
  import ComboBox from '../descope-combo-box/ComboBox';
13
12
 
@@ -54,6 +53,7 @@ const customMixin = (superclass) =>
54
53
  };
55
54
 
56
55
  const {
56
+ host,
57
57
  inputWrapper,
58
58
  countryCodeInput,
59
59
  phoneInput,
@@ -61,6 +61,7 @@ const {
61
61
  requiredIndicator,
62
62
  separator
63
63
  } = {
64
+ host: { selector: () => ':host' },
64
65
  inputWrapper: { selector: '::part(input-field)' },
65
66
  phoneInput: { selector: () => 'descope-text-field' },
66
67
  countryCodeInput: { selector: () => 'descope-combo-box' },
@@ -72,7 +73,7 @@ const {
72
73
  const PhoneField = compose(
73
74
  createStyleMixin({
74
75
  mappings: {
75
- componentWidth: { selector: () => ':host', property: 'width' },
76
+ componentWidth: { ...host, property: 'width' },
76
77
 
77
78
  wrapperBorderStyle: [
78
79
  { ...inputWrapper, property: 'border-style' },
@@ -1,5 +1,5 @@
1
1
  import { createBaseInputClass } from '../../../baseClasses/createBaseInputClass';
2
- import { getComponentName, simulateInputFocusEvents } from '../../../helpers/componentHelpers';
2
+ import { getComponentName } from '../../../helpers/componentHelpers';
3
3
  import CountryCodes from '../CountryCodes';
4
4
  import { comboBoxItem } from '../helpers';
5
5
 
@@ -160,6 +160,9 @@ class PhoneFieldInternal extends BaseInputClass {
160
160
  .join('');
161
161
  e.target.value = sanitizedInput;
162
162
  });
163
+
164
+ this.handleFocusEventsDispatching(this.inputs)
165
+ this.handleInputEventDispatching()
163
166
  }
164
167
 
165
168
  attributeChangedCallback(attrName, oldValue, newValue) {
@@ -15,7 +15,7 @@ let overrides = ``;
15
15
  const SwitchToggle = compose(
16
16
  createStyleMixin({
17
17
  mappings: {
18
- width: {selector: () => ':host'},
18
+ width: { selector: () => ':host' },
19
19
  cursor: [{}, { selector: '> label' }]
20
20
  }
21
21
  }),
@@ -3,4 +3,4 @@ import SwitchToggle, { componentName } from './SwitchToggle';
3
3
 
4
4
  customElements.define(componentName, SwitchToggle);
5
5
 
6
- export { SwitchToggle };
6
+ export { SwitchToggle };
@@ -89,13 +89,13 @@ export const forwardProps = (src, target, props = []) => {
89
89
  const config = props.reduce((acc, prop) => Object.assign(acc, {
90
90
  [prop]: {
91
91
  get() {
92
- return target[prop]
92
+ return src[prop]
93
93
  },
94
94
  set(v) {
95
- target[prop] = v
95
+ src[prop] = v
96
96
  }
97
97
  }
98
98
  }), {})
99
99
 
100
- Object.defineProperties(src, config)
100
+ Object.defineProperties(target, config)
101
101
  }
package/src/index.js CHANGED
@@ -17,6 +17,7 @@ import './components/descope-text-area';
17
17
  import './components/descope-text-field';
18
18
  import './components/descope-image';
19
19
  import './components/descope-phone-field';
20
+ import './components/descope-new-password';
20
21
 
21
22
  export {
22
23
  globalsThemeToStyle,
@@ -17,7 +17,7 @@ export const createProxy = ({
17
17
  #dispatchFocus = createDispatchEvent.bind(this, 'focus')
18
18
 
19
19
  constructor() {
20
- super().attachShadow({ mode: 'open' }).innerHTML = `
20
+ super().attachShadow({ mode: 'open', delegatesFocus: true }).innerHTML = `
21
21
  <style id="create-proxy">${isFunction(style) ? style() : style
22
22
  }</style>
23
23
  <${wrappedEleName}>
@@ -27,8 +27,6 @@ export const createProxy = ({
27
27
  `;
28
28
  }
29
29
 
30
- focus = () => this.baseElement.focus()
31
-
32
30
  init() {
33
31
  super.init?.();
34
32
 
@@ -40,29 +38,8 @@ export const createProxy = ({
40
38
  this.#dispatchFocus()
41
39
  })
42
40
 
43
- this.addEventListener('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
- })
49
-
50
41
  // this is needed for components that uses props, such as combo box
51
- forwardProps(this, this.baseElement, includeForwardProps)
52
-
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
- };
42
+ forwardProps(this.baseElement, this, includeForwardProps);
66
43
 
67
44
  syncAttrs(this.baseElement, this, {
68
45
  excludeAttrs: excludeAttrsSync,
@@ -3,8 +3,6 @@ import { createDispatchEvent } from "../helpers/mixinsHelpers";
3
3
  export const inputEventsDispatchingMixin = (superclass) => class InputEventsDispatchingMixinClass extends superclass {
4
4
  init() {
5
5
  this.#blockNativeEvents();
6
- this.#handleFocusEventsDispatching();
7
- this.#handleInputEventDispatching();
8
6
 
9
7
  super.init?.();
10
8
  }
@@ -18,29 +16,28 @@ export const inputEventsDispatchingMixin = (superclass) => class InputEventsDisp
18
16
  })
19
17
  }
20
18
 
21
- #handleFocusEventsDispatching() {
19
+ handleFocusEventsDispatching(inputs) {
22
20
  let timerId
23
21
 
24
22
  // in order to simulate blur & focusout on root input element
25
23
  // we are checking if focus on one of the inner elements happened immediately after blur
26
24
  // if not, the component is no longer focused and we should dispatch blur & focusout
27
- this.addEventListener('focusout', (e) => {
28
- if (e.isTrusted) {
25
+ inputs?.forEach(input => {
26
+ input?.addEventListener('focusout', (e) => {
29
27
  e.stopImmediatePropagation();
30
28
  timerId = setTimeout(() => {
31
29
  timerId = null
30
+
32
31
  createDispatchEvent.call(this, 'blur');
33
32
  createDispatchEvent.call(this, 'focusout', { bubbles: true });
34
33
  });
35
- }
36
- })
34
+ })
37
35
 
38
- // in order to simulate focus & focusin on the root input element
39
- // we are holding a timer id and clearing it on focusin
40
- // if there is a timer id, it means that the root input is still focused
41
- // otherwise, it was not focused before, and we should dispatch focus & focusin
42
- this.addEventListener('focusin', (e) => {
43
- if (e.isTrusted) {
36
+ // in order to simulate focus & focusin on the root input element
37
+ // we are holding a timer id and clearing it on focusin
38
+ // if there is a timer id, it means that the root input is still focused
39
+ // otherwise, it was not focused before, and we should dispatch focus & focusin
40
+ const onFocus = (e) => {
44
41
  e.stopImmediatePropagation();
45
42
  clearTimeout(timerId)
46
43
  if (!timerId) {
@@ -48,13 +45,17 @@ export const inputEventsDispatchingMixin = (superclass) => class InputEventsDisp
48
45
  createDispatchEvent.call(this, 'focusin', { bubbles: true });
49
46
  }
50
47
  }
48
+
49
+ // some components are not dispatching focusin but only focus
50
+ input?.addEventListener('focusin', onFocus)
51
+ input?.addEventListener('focus', onFocus)
51
52
  })
52
53
  }
53
54
 
54
55
  // we want to block the input events from propagating in case the value of the root input wasn't change
55
56
  // this can happen if we are sanitizing characters on the internal inputs and do not want it to affect the root input element value
56
57
  // in this case, on each input event, we are comparing the root input value to the previous one, and only if it does not match, we are allowing the input event to propagate
57
- #handleInputEventDispatching() {
58
+ handleInputEventDispatching() {
58
59
  let previousRootComponentValue = this.value
59
60
 
60
61
  this.addEventListener('input', (e) => {
@@ -66,6 +67,5 @@ export const inputEventsDispatchingMixin = (superclass) => class InputEventsDisp
66
67
  previousRootComponentValue = this.value
67
68
  }
68
69
  });
69
-
70
70
  }
71
71
  }
@@ -68,7 +68,7 @@ export const inputValidationMixin = (superclass) => class InputValidationMixinCl
68
68
 
69
69
  #setValidity() {
70
70
  const validity = this.getValidity()
71
- this.#internals.setValidity(validity, this.getErrorMessage(validity))
71
+ this.#internals.setValidity(validity, this.getErrorMessage(validity), this.validationTarget)
72
72
  }
73
73
 
74
74
  get validationMessage() {
@@ -91,9 +91,13 @@ export const inputValidationMixin = (superclass) => class InputValidationMixinCl
91
91
  return this.#internals.validity
92
92
  }
93
93
 
94
+ get validationTarget () {
95
+ return this.inputElement
96
+ }
97
+
94
98
  setCustomValidity(errorMessage) {
95
99
  if (errorMessage) {
96
- this.#internals.setValidity({ customError: true }, errorMessage)
100
+ this.#internals.setValidity({ customError: true }, errorMessage, this.validationTarget)
97
101
  } else {
98
102
  this.#internals.setValidity({})
99
103
  this.#setValidity()