@descope/web-components-ui 1.0.77 → 1.0.79

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 +149 -152
  2. package/dist/index.esm.js.map +1 -1
  3. package/dist/umd/878.js +1 -1
  4. package/dist/umd/988.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 -3
  31. package/src/components/descope-combo-box/ComboBox.js +2 -2
  32. package/src/components/descope-passcode/descope-passcode-internal/PasscodeInternal.js +2 -31
  33. package/src/components/descope-phone-field/descope-phone-field-internal/PhoneFieldInternal.js +8 -32
  34. package/src/mixins/index.js +1 -1
  35. package/src/mixins/inputEventsDispatchingMixin.js +71 -0
  36. package/src/mixins/proxyInputMixin.js +7 -5
  37. package/dist/umd/744.js +0 -1
  38. package/src/mixins/focusMixin.js +0 -23
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,70 +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
- const focusMixin = (superclass) => class FocusMixinClass extends superclass {
885
- // we want to block all native events,
886
- // so the input can control when to dispatch it based on its internal behavior
887
- init() {
888
- super.init?.();
889
-
890
- this.addEventListener('blur', (e) => {
891
- e.isTrusted && e.stopImmediatePropagation();
892
- });
893
-
894
- this.addEventListener('focus', (e) => {
895
- e.isTrusted && e.stopImmediatePropagation();
896
- });
897
-
898
- this.addEventListener('focusout', (e) => {
899
- e.isTrusted && e.stopImmediatePropagation();
900
- });
901
-
902
- this.addEventListener('focusin', (e) => {
903
- e.isTrusted && e.stopImmediatePropagation();
904
- });
905
- }
906
- };
907
-
908
905
  // this is needed because we might generate the same css vars names
909
906
  // e.g. "overlayColor" attribute in style mixin's mapping,
910
907
  // will generate the same var as "color" attribute in portals's mapping
@@ -991,25 +988,6 @@ const changeMixin = (superclass) => class ChangeMixinClass extends superclass {
991
988
  }
992
989
  };
993
990
 
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
991
  const readOnlyMixin = (superclass) => class ReadOnlyMixinClass extends superclass {
1014
992
  get isReadOnly() {
1015
993
  return this.hasAttribute('readonly') && this.getAttribute('readonly') !== 'false'
@@ -1029,6 +1007,76 @@ const readOnlyMixin = (superclass) => class ReadOnlyMixinClass extends superclas
1029
1007
  }
1030
1008
  };
1031
1009
 
1010
+ const inputEventsDispatchingMixin = (superclass) => class InputEventsDispatchingMixinClass extends superclass {
1011
+ init() {
1012
+ this.#blockNativeEvents();
1013
+ this.#handleFocusEventsDispatching();
1014
+ this.#handleInputEventDispatching();
1015
+
1016
+ super.init?.();
1017
+ }
1018
+
1019
+ // we want to block the native (trusted) events and control when these events are being dispatched
1020
+ #blockNativeEvents() {
1021
+ ['blur', 'focus', 'focusin', 'focusout'].forEach(event => {
1022
+ this.addEventListener(event, (e) => {
1023
+ e.isTrusted && e.target === this && e.stopImmediatePropagation();
1024
+ });
1025
+ });
1026
+ }
1027
+
1028
+ #handleFocusEventsDispatching() {
1029
+ let timerId;
1030
+
1031
+ // in order to simulate blur & focusout on root input element
1032
+ // we are checking if focus on one of the inner elements happened immediately after blur
1033
+ // if not, the component is no longer focused and we should dispatch blur & focusout
1034
+ this.addEventListener('focusout', (e) => {
1035
+ if (e.isTrusted) {
1036
+ e.stopImmediatePropagation();
1037
+ timerId = setTimeout(() => {
1038
+ timerId = null;
1039
+ createDispatchEvent.call(this, 'blur');
1040
+ createDispatchEvent.call(this, 'focusout', { bubbles: true });
1041
+ });
1042
+ }
1043
+ });
1044
+
1045
+ // in order to simulate focus & focusin on the root input element
1046
+ // we are holding a timer id and clearing it on focusin
1047
+ // if there is a timer id, it means that the root input is still focused
1048
+ // otherwise, it was not focused before, and we should dispatch focus & focusin
1049
+ this.addEventListener('focusin', (e) => {
1050
+ if (e.isTrusted) {
1051
+ e.stopImmediatePropagation();
1052
+ clearTimeout(timerId);
1053
+ if (!timerId) {
1054
+ createDispatchEvent.call(this, 'focus');
1055
+ createDispatchEvent.call(this, 'focusin', { bubbles: true });
1056
+ }
1057
+ }
1058
+ });
1059
+ }
1060
+
1061
+ // we want to block the input events from propagating in case the value of the root input wasn't change
1062
+ // this can happen if we are sanitizing characters on the internal inputs and do not want it to affect the root input element value
1063
+ // 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
1064
+ #handleInputEventDispatching() {
1065
+ let previousRootComponentValue = this.value;
1066
+
1067
+ this.addEventListener('input', (e) => {
1068
+ // we are comparing the previous value to the new one,
1069
+ // and if they have the same value, we are blocking the input event
1070
+ if (previousRootComponentValue === this.value) {
1071
+ e.stopImmediatePropagation();
1072
+ } else {
1073
+ previousRootComponentValue = this.value;
1074
+ }
1075
+ });
1076
+
1077
+ }
1078
+ };
1079
+
1032
1080
  const componentName$l = getComponentName('button');
1033
1081
 
1034
1082
  const editorOverrides = `vaadin-button::part(label) { pointer-events: none; }`;
@@ -1754,10 +1802,11 @@ overrides$4 = `
1754
1802
  customElements.define(componentName$a, NumberField);
1755
1803
 
1756
1804
  const createBaseInputClass = (...args) => compose(
1757
- focusMixin,
1758
1805
  inputValidationMixin,
1759
1806
  changeMixin,
1760
- readOnlyMixin
1807
+ readOnlyMixin,
1808
+ normalizeBooleanAttributesMixin,
1809
+ inputEventsDispatchingMixin
1761
1810
  )(createBaseClass(...args));
1762
1811
 
1763
1812
  const focusElement = (ele) => {
@@ -1913,31 +1962,8 @@ class PasscodeInternal extends BaseInputClass$1 {
1913
1962
  };
1914
1963
 
1915
1964
  initInputs() {
1916
- let prevVal = this.value;
1917
- let blurTimerId;
1918
-
1919
1965
  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
-
1966
+ // sanitize the input
1941
1967
  input.addEventListener('input', (e) => {
1942
1968
  const charArr = getSanitizedCharacters(input.value);
1943
1969
 
@@ -1946,16 +1972,10 @@ class PasscodeInternal extends BaseInputClass$1 {
1946
1972
  input.value = '';
1947
1973
  }
1948
1974
  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
1975
  });
1955
1976
 
1977
+ // we want backspace to focus on the previous digit
1956
1978
  input.onkeydown = ({ key }) => {
1957
- prevVal = this.value;
1958
-
1959
1979
  // when user deletes a digit, we want to focus the previous digit
1960
1980
  if (key === 'Backspace') {
1961
1981
  // if the cursor is at 0, we want to move it to 1, so the value will be deleted
@@ -2536,8 +2556,8 @@ const ComboBoxMixin = (superclass) => class ComboBoxMixinClass extends superclas
2536
2556
  overlay._enterModalState = function () { };
2537
2557
  }
2538
2558
 
2539
- connectedCallback() {
2540
- super.connectedCallback?.();
2559
+ init() {
2560
+ super.init?.();
2541
2561
 
2542
2562
  this.#overrideOverlaySettings();
2543
2563
  observeChildren(this, this.#onChildrenChange.bind(this));
@@ -3905,9 +3925,6 @@ class PhoneFieldInternal extends BaseInputClass {
3905
3925
  );
3906
3926
  }
3907
3927
 
3908
- #dispatchBlur = createDispatchEvent.bind(this, 'blur');
3909
- #dispatchFocus = createDispatchEvent.bind(this, 'focus');
3910
-
3911
3928
  constructor() {
3912
3929
  super();
3913
3930
 
@@ -3972,6 +3989,13 @@ class PhoneFieldInternal extends BaseInputClass {
3972
3989
  };
3973
3990
 
3974
3991
  init() {
3992
+
3993
+ this.addEventListener('focus', (e) => {
3994
+ // we want to ignore focus events we are dispatching
3995
+ if (e.isTrusted)
3996
+ this.inputs[0].focus();
3997
+ });
3998
+
3975
3999
  super.init();
3976
4000
  this.initInputs();
3977
4001
  this.setComboBoxDescriptor();
@@ -4022,9 +4046,6 @@ class PhoneFieldInternal extends BaseInputClass {
4022
4046
  }
4023
4047
 
4024
4048
  initInputs() {
4025
- let prevVal = this.value;
4026
- let blurTimerId;
4027
-
4028
4049
  // Sanitize phone input value to filter everything but digits
4029
4050
  this.phoneNumberInput.addEventListener('input', (e) => {
4030
4051
  const telDigitsRegExp = /^\d$/;
@@ -4034,30 +4055,6 @@ class PhoneFieldInternal extends BaseInputClass {
4034
4055
  .join('');
4035
4056
  e.target.value = sanitizedInput;
4036
4057
  });
4037
-
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
- });
4061
4058
  }
4062
4059
 
4063
4060
  attributeChangedCallback(attrName, oldValue, newValue) {