@descope/web-components-ui 1.0.76 → 1.0.78

Sign up to get free protection for your applications and to get access to all the features.
Files changed (38) hide show
  1. package/dist/index.esm.js +137 -133
  2. package/dist/index.esm.js.map +1 -1
  3. package/dist/umd/878.js +1 -1
  4. package/dist/umd/933.js +1 -0
  5. package/dist/umd/descope-button-index-js.js +1 -1
  6. package/dist/umd/descope-checkbox-index-js.js +1 -1
  7. package/dist/umd/descope-combo-box-index-js.js +1 -1
  8. package/dist/umd/descope-container-index-js.js +1 -1
  9. package/dist/umd/descope-date-picker-index-js.js +1 -1
  10. package/dist/umd/descope-divider-index-js.js +1 -1
  11. package/dist/umd/descope-email-field-index-js.js +1 -1
  12. package/dist/umd/descope-image-index-js.js +1 -1
  13. package/dist/umd/descope-link-index-js.js +1 -1
  14. package/dist/umd/descope-loader-linear-index-js.js +1 -1
  15. package/dist/umd/descope-loader-radial-index-js.js +1 -1
  16. package/dist/umd/descope-logo-index-js.js +1 -1
  17. package/dist/umd/descope-number-field-index-js.js +1 -1
  18. package/dist/umd/descope-passcode-descope-passcode-internal-index-js.js +1 -1
  19. package/dist/umd/descope-passcode-index-js.js +1 -1
  20. package/dist/umd/descope-password-field-index-js.js +1 -1
  21. package/dist/umd/descope-phone-field-descope-phone-field-internal-index-js.js +1 -1
  22. package/dist/umd/descope-phone-field-index-js.js +1 -1
  23. package/dist/umd/descope-switch-toggle-index-js.js +1 -1
  24. package/dist/umd/descope-text-area-index-js.js +1 -1
  25. package/dist/umd/descope-text-field-index-js.js +1 -1
  26. package/dist/umd/descope-text-index-js.js +1 -1
  27. package/dist/umd/index.js +1 -1
  28. package/package.json +1 -1
  29. package/src/baseClasses/createBaseClass.js +3 -1
  30. package/src/baseClasses/createBaseInputClass.js +4 -2
  31. package/src/components/descope-combo-box/ComboBox.js +2 -2
  32. package/src/components/descope-passcode/descope-passcode-internal/PasscodeInternal.js +5 -31
  33. package/src/components/descope-phone-field/PhoneField.js +0 -7
  34. package/src/components/descope-phone-field/descope-phone-field-internal/PhoneFieldInternal.js +10 -31
  35. package/src/mixins/index.js +1 -0
  36. package/src/mixins/inputEventsDispatchingMixin.js +54 -0
  37. package/src/mixins/proxyInputMixin.js +7 -5
  38. package/dist/umd/744.js +0 -1
package/dist/index.esm.js CHANGED
@@ -421,6 +421,65 @@ const draggableMixin = (superclass) =>
421
421
  }
422
422
  };
423
423
 
424
+ const componentNameValidationMixin = (superclass) =>
425
+ class ComponentNameValidationMixinClass extends superclass {
426
+ #checkComponentName() {
427
+ const currentComponentName = this.localName;
428
+
429
+ if (!superclass.componentName) {
430
+ throw Error(
431
+ `component name is not defined on super class, make sure you have a static get for "componentName"`
432
+ );
433
+ }
434
+
435
+ if (currentComponentName !== superclass.componentName) {
436
+ throw Error(
437
+ `component name mismatch, expected "${superclass.componentName}", current "${currentComponentName}"`
438
+ );
439
+ }
440
+ }
441
+
442
+ init() {
443
+ super.init?.();
444
+ this.#checkComponentName();
445
+ }
446
+ };
447
+
448
+ const hoverableMixin = (superclass) =>
449
+ class HoverableMixinClass extends superclass {
450
+ init() {
451
+ super.init?.();
452
+
453
+ this.baseElement.addEventListener('mouseover', (e) => {
454
+ this.setAttribute('hover', 'true');
455
+ e.target.addEventListener(
456
+ 'mouseleave',
457
+ () => this.removeAttribute('hover'),
458
+ { once: true }
459
+ );
460
+ });
461
+ }
462
+ };
463
+
464
+ // we want all the valueless attributes to have "true" value
465
+ // and all the falsy attribute to be removed
466
+ const normalizeBooleanAttributesMixin = (superclass) => class NormalizeBooleanAttributesMixinClass extends superclass {
467
+ init() {
468
+ super.init?.();
469
+
470
+ observeAttributes(this, (attrs) =>
471
+ attrs.forEach(attr => {
472
+ const attrVal = this.getAttribute(attr);
473
+
474
+ if (attrVal === '') {
475
+ this.setAttribute(attr, 'true');
476
+ } else if (attrVal === 'false') {
477
+ this.removeAttribute(attr);
478
+ }
479
+ }), {});
480
+ }
481
+ };
482
+
424
483
  const createBaseClass = ({ componentName, baseSelector = '' }) => {
425
484
  class DescopeBaseClass extends HTMLElement {
426
485
  static get componentName() {
@@ -757,13 +816,14 @@ const proxyInputMixin = (superclass) =>
757
816
 
758
817
  reportValidityOnInternalInput() {
759
818
  setTimeout(() => {
819
+ this.inputElement.focus();
760
820
  this.inputElement.reportValidity();
761
821
  });
762
822
  }
763
823
 
764
824
  // we want reportValidity to behave like form submission
765
825
  reportValidity() {
766
- if (!isValid) {
826
+ if (!this.checkValidity()) {
767
827
  this.setAttribute('invalid', 'true');
768
828
  this.reportValidityOnInternalInput();
769
829
  }
@@ -811,9 +871,10 @@ const proxyInputMixin = (superclass) =>
811
871
  });
812
872
 
813
873
  this.addEventListener('focus', (e) => {
814
- // when clicking on the form submit button and the input is invalid
815
- // we want it to appear as invalid
816
- if (e.relatedTarget?.form === this.form) {
874
+ // when clicking on the form submit button and the input is invalid, we want it to appear as invalid
875
+ // this is a best effort, we cannot identify it for sure without listening to the form submission event
876
+ // this will also be triggered when the focus is moving from the submit button to the input by pressing TAB key
877
+ if (e.relatedTarget?.form && e.relatedTarget?.form === this.form && e.relatedTarget?.nodeName === 'BUTTON') {
817
878
  if (!this.checkValidity()) {
818
879
  this.setAttribute('invalid', 'true');
819
880
  }
@@ -841,46 +902,6 @@ const proxyInputMixin = (superclass) =>
841
902
  }
842
903
  };
843
904
 
844
- const componentNameValidationMixin = (superclass) =>
845
- class ComponentNameValidationMixinClass extends superclass {
846
- #checkComponentName() {
847
- const currentComponentName = this.localName;
848
-
849
- if (!superclass.componentName) {
850
- throw Error(
851
- `component name is not defined on super class, make sure you have a static get for "componentName"`
852
- );
853
- }
854
-
855
- if (currentComponentName !== superclass.componentName) {
856
- throw Error(
857
- `component name mismatch, expected "${superclass.componentName}", current "${currentComponentName}"`
858
- );
859
- }
860
- }
861
-
862
- init() {
863
- super.init?.();
864
- this.#checkComponentName();
865
- }
866
- };
867
-
868
- const hoverableMixin = (superclass) =>
869
- class HoverableMixinClass extends superclass {
870
- init() {
871
- super.init?.();
872
-
873
- this.baseElement.addEventListener('mouseover', (e) => {
874
- this.setAttribute('hover', 'true');
875
- e.target.addEventListener(
876
- 'mouseleave',
877
- () => this.removeAttribute('hover'),
878
- { once: true }
879
- );
880
- });
881
- }
882
- };
883
-
884
905
  const focusMixin = (superclass) => class FocusMixinClass extends superclass {
885
906
  // we want to block all native events,
886
907
  // so the input can control when to dispatch it based on its internal behavior
@@ -991,25 +1012,6 @@ const changeMixin = (superclass) => class ChangeMixinClass extends superclass {
991
1012
  }
992
1013
  };
993
1014
 
994
- // we want all the valueless attributes to have "true" value
995
- // and all the falsy attribute to be removed
996
- const normalizeBooleanAttributesMixin = (superclass) => class NormalizeBooleanAttributesMixinClass extends superclass {
997
- init() {
998
- super.init?.();
999
-
1000
- observeAttributes(this, (attrs) =>
1001
- attrs.forEach(attr => {
1002
- const attrVal = this.getAttribute(attr);
1003
-
1004
- if (attrVal === '') {
1005
- this.setAttribute(attr, 'true');
1006
- } else if (attrVal === 'false') {
1007
- this.removeAttribute(attr);
1008
- }
1009
- }), {});
1010
- }
1011
- };
1012
-
1013
1015
  const readOnlyMixin = (superclass) => class ReadOnlyMixinClass extends superclass {
1014
1016
  get isReadOnly() {
1015
1017
  return this.hasAttribute('readonly') && this.getAttribute('readonly') !== 'false'
@@ -1029,6 +1031,59 @@ const readOnlyMixin = (superclass) => class ReadOnlyMixinClass extends superclas
1029
1031
  }
1030
1032
  };
1031
1033
 
1034
+ const inputEventsDispatchingMixin = (superclass) => class InputEventsDispatchingMixinClass extends superclass {
1035
+ handleFocusEventsDispatching(focusableElements = []) {
1036
+ let blurTimerId;
1037
+
1038
+ // in order to simulate blur on the input
1039
+ // we are checking if focus on one of the other elements happened immediately after blur
1040
+ // if not, the component is no longer focused and we should dispatch blur
1041
+ focusableElements.forEach(ele => {
1042
+ ele?.addEventListener('blur', (e) => {
1043
+ e.stopImmediatePropagation();
1044
+ blurTimerId = setTimeout(() => {
1045
+ blurTimerId = null;
1046
+ createDispatchEvent.call(this, 'blur');
1047
+ createDispatchEvent.call(this, 'focusout', { bubbles: true });
1048
+ });
1049
+ });
1050
+
1051
+ // in order to simulate focus on the input
1052
+ // we are holding a blur timer id and clearing it on blur
1053
+ // if there is a timer id, it means that there was no blur on the input, so it's still focused
1054
+ // otherwise, it was not focused before, and we need to dispatch a focus event
1055
+ ele?.addEventListener('focus', (e) => {
1056
+ e.stopImmediatePropagation();
1057
+ clearTimeout(blurTimerId);
1058
+ if (!blurTimerId) {
1059
+ createDispatchEvent.call(this, 'focus');
1060
+ createDispatchEvent.call(this, 'focusin', { bubbles: true });
1061
+ }
1062
+ });
1063
+ });
1064
+ }
1065
+
1066
+ // we want to block the input events from propagating in case the value of the root input wasn't change
1067
+ // this can happen if we are sanitizing characters on the internal inputs and do not want it to affect the root input element value
1068
+ // 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
1069
+ handleInputEventDispatching(inputElements) {
1070
+ let previousRootComponentValue = this.value;
1071
+
1072
+ inputElements.forEach(input => {
1073
+ input.addEventListener('input', (e) => {
1074
+ // we are comparing the previous value to the new one,
1075
+ // and if they have the same value, we are blocking the input event
1076
+ if (previousRootComponentValue === this.value) {
1077
+ e.stopImmediatePropagation();
1078
+ } else {
1079
+ previousRootComponentValue = this.value;
1080
+ }
1081
+ });
1082
+ });
1083
+
1084
+ }
1085
+ };
1086
+
1032
1087
  const componentName$l = getComponentName('button');
1033
1088
 
1034
1089
  const editorOverrides = `vaadin-button::part(label) { pointer-events: none; }`;
@@ -1757,7 +1812,9 @@ const createBaseInputClass = (...args) => compose(
1757
1812
  focusMixin,
1758
1813
  inputValidationMixin,
1759
1814
  changeMixin,
1760
- readOnlyMixin
1815
+ readOnlyMixin,
1816
+ normalizeBooleanAttributesMixin,
1817
+ inputEventsDispatchingMixin
1761
1818
  )(createBaseClass(...args));
1762
1819
 
1763
1820
  const focusElement = (ele) => {
@@ -1913,31 +1970,8 @@ class PasscodeInternal extends BaseInputClass$1 {
1913
1970
  };
1914
1971
 
1915
1972
  initInputs() {
1916
- let prevVal = this.value;
1917
- let blurTimerId;
1918
-
1919
1973
  this.inputs.forEach((input) => {
1920
- // in order to simulate blur on the input
1921
- // we are checking if focus on one of the digits happened immediately after blur on another digit
1922
- // if not, the component is no longer focused and we should simulate blur
1923
- input.addEventListener('blur', (e) => {
1924
- e.stopImmediatePropagation();
1925
-
1926
- blurTimerId = setTimeout(() => {
1927
- blurTimerId = null;
1928
- this.#dispatchBlur();
1929
- });
1930
- });
1931
-
1932
- input.addEventListener('focus', (e) => {
1933
- e.stopImmediatePropagation();
1934
-
1935
- clearTimeout(blurTimerId);
1936
- if (!blurTimerId) {
1937
- this.#dispatchFocus();
1938
- }
1939
- });
1940
-
1974
+ // sanitize the input
1941
1975
  input.addEventListener('input', (e) => {
1942
1976
  const charArr = getSanitizedCharacters(input.value);
1943
1977
 
@@ -1946,16 +1980,10 @@ class PasscodeInternal extends BaseInputClass$1 {
1946
1980
  input.value = '';
1947
1981
  }
1948
1982
  else this.fillDigits(charArr, input);
1949
-
1950
- // we want to stop input events if the value wasn't change
1951
- if (prevVal === this.value) {
1952
- e.stopImmediatePropagation();
1953
- }
1954
1983
  });
1955
1984
 
1985
+ // we want backspace to focus on the previous digit
1956
1986
  input.onkeydown = ({ key }) => {
1957
- prevVal = this.value;
1958
-
1959
1987
  // when user deletes a digit, we want to focus the previous digit
1960
1988
  if (key === 'Backspace') {
1961
1989
  // if the cursor is at 0, we want to move it to 1, so the value will be deleted
@@ -1972,6 +2000,9 @@ class PasscodeInternal extends BaseInputClass$1 {
1972
2000
 
1973
2001
  forwardAttrs(this, input, { includeAttrs: forwardAttributes });
1974
2002
  });
2003
+
2004
+ this.handleFocusEventsDispatching(this.inputs);
2005
+ this.handleInputEventDispatching(this.inputs);
1975
2006
  }
1976
2007
 
1977
2008
  attributeChangedCallback(attrName, oldValue, newValue) {
@@ -2536,8 +2567,8 @@ const ComboBoxMixin = (superclass) => class ComboBoxMixinClass extends superclas
2536
2567
  overlay._enterModalState = function () { };
2537
2568
  }
2538
2569
 
2539
- connectedCallback() {
2540
- super.connectedCallback?.();
2570
+ init() {
2571
+ super.init?.();
2541
2572
 
2542
2573
  this.#overrideOverlaySettings();
2543
2574
  observeChildren(this, this.#onChildrenChange.bind(this));
@@ -3905,9 +3936,6 @@ class PhoneFieldInternal extends BaseInputClass {
3905
3936
  );
3906
3937
  }
3907
3938
 
3908
- #dispatchBlur = createDispatchEvent.bind(this, 'blur');
3909
- #dispatchFocus = createDispatchEvent.bind(this, 'focus');
3910
-
3911
3939
  constructor() {
3912
3940
  super();
3913
3941
 
@@ -3972,6 +4000,13 @@ class PhoneFieldInternal extends BaseInputClass {
3972
4000
  };
3973
4001
 
3974
4002
  init() {
4003
+
4004
+ this.addEventListener('focus', (e) => {
4005
+ // we want to ignore focus events we are dispatching
4006
+ if (e.isTrusted)
4007
+ this.inputs[0].focus();
4008
+ });
4009
+
3975
4010
  super.init();
3976
4011
  this.initInputs();
3977
4012
  this.setComboBoxDescriptor();
@@ -4022,9 +4057,6 @@ class PhoneFieldInternal extends BaseInputClass {
4022
4057
  }
4023
4058
 
4024
4059
  initInputs() {
4025
- let prevVal = this.value;
4026
- let blurTimerId;
4027
-
4028
4060
  // Sanitize phone input value to filter everything but digits
4029
4061
  this.phoneNumberInput.addEventListener('input', (e) => {
4030
4062
  const telDigitsRegExp = /^\d$/;
@@ -4035,29 +4067,8 @@ class PhoneFieldInternal extends BaseInputClass {
4035
4067
  e.target.value = sanitizedInput;
4036
4068
  });
4037
4069
 
4038
- this.inputs.forEach(input => {
4039
- input.addEventListener('blur', (e) => {
4040
- e.stopImmediatePropagation();
4041
- blurTimerId = setTimeout(() => {
4042
- blurTimerId = null;
4043
- this.#dispatchBlur();
4044
- });
4045
- });
4046
-
4047
- input.addEventListener('focus', (e) => {
4048
- e.stopImmediatePropagation();
4049
- clearTimeout(blurTimerId);
4050
- if (!blurTimerId) {
4051
- this.#dispatchFocus();
4052
- }
4053
- });
4054
-
4055
- input.addEventListener('input', (e) => {
4056
- if (prevVal === this.value) {
4057
- e.stopImmediatePropagation();
4058
- }
4059
- });
4060
- });
4070
+ this.handleFocusEventsDispatching(this.inputs);
4071
+ this.handleInputEventDispatching(this.inputs);
4061
4072
  }
4062
4073
 
4063
4074
  attributeChangedCallback(attrName, oldValue, newValue) {
@@ -4251,13 +4262,6 @@ const PhoneField = compose(
4251
4262
  })
4252
4263
  );
4253
4264
 
4254
- /*
4255
- Bugs:
4256
- - default code value, open the dropdown and click outside, the value is gone
4257
- - make invalid by blur, enter a 6 digit number, component is valid but the divider is red
4258
- - missing handling of outline when focused, hiding the divider when focusing the phone
4259
- */
4260
-
4261
4265
  customElements.define(componentName, PhoneField);
4262
4266
 
4263
4267
  const getVarName = (path) => getCssVarName(DESCOPE_PREFIX, ...path);