@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.
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
package/dist/index.esm.js CHANGED
@@ -22,8 +22,6 @@ const compose = (...fns) =>
22
22
  (val) =>
23
23
  fns.reduceRight((res, fn) => fn(res), val);
24
24
 
25
- const upperFirst = (str) => str.charAt(0).toUpperCase() + str.slice(1);
26
-
27
25
  const isFunction = (maybeFunc) => typeof maybeFunc === 'function';
28
26
 
29
27
  const DESCOPE_PREFIX = 'descope';
@@ -384,6 +382,8 @@ const createStyleMixin =
384
382
  }
385
383
 
386
384
  disconnectedCallback() {
385
+ super.disconnectedCallback?.();
386
+
387
387
  this.#disconnectThemeManager?.();
388
388
  }
389
389
  };
@@ -420,56 +420,40 @@ const draggableMixin = (superclass) =>
420
420
  }
421
421
  };
422
422
 
423
- const componentNameValidationMixin = (superclass) =>
424
- class ComponentNameValidationMixinClass extends superclass {
425
- #checkComponentName() {
426
- const currentComponentName = this.shadowRoot.host.tagName.toLowerCase();
423
+ // create a dispatch event function that also calls to the onevent function in case it's set
424
+ // usage example:
425
+ // #dispatchSomething = createDispatchEvent.bind(this, 'something' { ...options })
426
+ // this will dispatch a new event when called, but also call to "onsomething"
427
+ function createDispatchEvent(eventName, options = {}) {
428
+ const event = new Event(eventName, options);
427
429
 
428
- if (!superclass.componentName) {
429
- throw Error(
430
- `component name is not defined on super class, make sure you have a static get for "componentName"`
431
- );
432
- }
430
+ this[`on${eventName}`]?.(event); // in case we got an event callback as property
431
+ this.dispatchEvent(event);
432
+ }
433
433
 
434
- if (currentComponentName !== superclass.componentName) {
435
- throw Error(
436
- `component name mismatch, expected "${superclass.componentName}", current "${currentComponentName}"`
437
- );
438
- }
439
- }
434
+ // add an event listener that is automatically removed on disconnect
435
+ // usage example:
436
+ // createEventListener.call(this,'change', this.onChange, { element? , ...options })
437
+ function createEventListener(event, callback, { element, ...options } = {}) {
438
+ const timerId = setTimeout(() => console.warn(this.localName, 'is not using "createBaseClass", events will not be removed automatically on disconnect'), 2000);
440
439
 
441
- connectedCallback() {
442
- super.connectedCallback?.();
443
- if (this.shadowRoot.isConnected) {
444
- this.#checkComponentName();
445
- }
446
- }
447
- };
440
+ this.addEventListener('connected', () => {
441
+ clearTimeout(timerId);
442
+ }, { once: true });
448
443
 
449
- const hoverableMixin =
450
- (superclass) =>
451
- class HoverableMixinClass extends superclass {
452
- #boundOnMouseOver = this.#onMouseOver.bind(this)
453
-
454
- #onMouseOver(e) {
455
- this.setAttribute('hover', 'true');
456
- e.target.addEventListener(
457
- 'mouseleave',
458
- () => this.shadowRoot.host.removeAttribute('hover'),
459
- { once: true }
460
- );
461
- }
444
+ const targetEle = element || this;
445
+ const boundCallback = callback.bind(this);
462
446
 
463
- connectedCallback() {
464
- super.connectedCallback?.();
447
+ const onDisconnect = () => {
448
+ targetEle.removeEventListener(event, boundCallback);
449
+ };
465
450
 
466
- const baseElement = this.shadowRoot.querySelector(
467
- this.baseSelector
468
- );
451
+ this.addEventListener('disconnected', onDisconnect, { once: true });
469
452
 
470
- baseElement.addEventListener('mouseover', this.#boundOnMouseOver);
471
- }
472
- };
453
+ targetEle.addEventListener(event, boundCallback, options);
454
+
455
+ return onDisconnect
456
+ }
473
457
 
474
458
  const createBaseClass = ({ componentName, baseSelector = '' }) => {
475
459
  class DescopeBaseClass extends HTMLElement {
@@ -478,10 +462,17 @@ const createBaseClass = ({ componentName, baseSelector = '' }) => {
478
462
  }
479
463
 
480
464
  #baseElement;
465
+
466
+ #dispatchConnected = createDispatchEvent.bind(this, 'connected')
467
+ #dispatchDisconnected = createDispatchEvent.bind(this, 'disconnected')
468
+
469
+ // base selector is the selector for the component wrapper,
470
+ // it's the highest element that is relevant for the layout
481
471
  get baseSelector() {
482
472
  return baseSelector
483
473
  }
484
474
 
475
+ // this is the base element, which returned by querying the base selector
485
476
  get baseElement() {
486
477
  this.#baseElement ??= this.baseSelector ?
487
478
  this.rootElement.querySelector(this.baseSelector) :
@@ -490,12 +481,31 @@ const createBaseClass = ({ componentName, baseSelector = '' }) => {
490
481
  return this.#baseElement
491
482
  }
492
483
 
484
+ // this is the component root level element,
485
+ // it can be either a shadow root or the component's node from the light DOM
493
486
  get rootElement() {
494
487
  return this.shadowRoot || this
495
488
  }
489
+
490
+ connectedCallback() {
491
+ super.connectedCallback?.();
492
+
493
+ // we are waiting for all components to listen before dispatching
494
+ setTimeout(this.#dispatchConnected);
495
+ }
496
+
497
+ disconnectedCallback() {
498
+ super.disconnectedCallback?.();
499
+
500
+ this.#dispatchDisconnected();
501
+ }
496
502
  }
497
503
 
498
- return compose(componentNameValidationMixin, hoverableMixin)(DescopeBaseClass)
504
+ return compose(
505
+ componentNameValidationMixin,
506
+ hoverableMixin,
507
+ normalizeBooleanAttributesMixin
508
+ )(DescopeBaseClass)
499
509
  };
500
510
 
501
511
  const createProxy = ({
@@ -507,87 +517,69 @@ const createProxy = ({
507
517
  includeAttrsSync = [],
508
518
  includeForwardProps = []
509
519
  }) => {
510
- const template = `
511
- <style id="create-proxy"></style>
512
- <${wrappedEleName}>
513
- <slot></slot>
514
- ${slots.map((slot) => `<slot name="${slot}" slot="${slot}"></slot>`).join('')}
515
- </${wrappedEleName}>
516
- `;
517
-
518
520
  class ProxyClass extends createBaseClass({ componentName, baseSelector: wrappedEleName }) {
519
- constructor() {
520
- super().attachShadow({ mode: 'open' }).innerHTML = template;
521
- this.hostElement = this.shadowRoot.host;
522
- this.shadowRoot.getElementById('create-proxy').innerHTML =
523
- isFunction(style) ? style() : style;
524
- }
521
+ #dispatchBlur = createDispatchEvent.bind(this, 'blur')
522
+ #dispatchFocus = createDispatchEvent.bind(this, 'focus')
525
523
 
526
- #boundOnFocus = this.#onFocus.bind(this);
527
-
528
- // we want to focus on the proxy element when focusing our WCP
529
- #onFocus() {
530
- this.proxyElement.focus();
524
+ constructor() {
525
+ super().attachShadow({ mode: 'open' }).innerHTML = `
526
+ <style id="create-proxy">${isFunction(style) ? style() : style
527
+ }</style>
528
+ <${wrappedEleName}>
529
+ <slot></slot>
530
+ ${slots.map((slot) => `<slot name="${slot}" slot="${slot}"></slot>`).join('')}
531
+ </${wrappedEleName}>
532
+ `;
531
533
  }
532
534
 
533
- focus = this.#onFocus
535
+ focus = () => this.baseElement.focus()
534
536
 
535
537
  connectedCallback() {
536
- if (this.shadowRoot.isConnected) {
537
- this.proxyElement = this.shadowRoot.querySelector(wrappedEleName);
538
-
539
- this.addEventListener('focus', this.#boundOnFocus);
540
-
541
- // this is needed for components that uses props, such as combo box
542
- forwardProps(this.hostElement, this.proxyElement, includeForwardProps);
543
-
544
- // `onkeydown` is set on `proxyElement` support proper tab-index navigation
545
- // this support is needed since both proxy host and element catch `focus`/`blur` event
546
- // which causes faulty behavior.
547
- // we need this to happen only when the proxy component is in the light DOM,
548
- // otherwise it will focus the nested proxy element
549
- this.proxyElement.onkeydown = (e) => {
550
- if (e.shiftKey && e.keyCode === 9 && this.getRootNode() === document) {
551
- this.removeAttribute('tabindex');
552
- // We want to defer the action of setting the tab index back
553
- // so it will happen after focusing the previous element
554
- setTimeout(() => this.setAttribute('tabindex', '0'), 0);
555
- }
556
- };
538
+ super.connectedCallback?.();
557
539
 
558
- syncAttrs(this.proxyElement, this.hostElement, {
559
- excludeAttrs: excludeAttrsSync,
560
- includeAttrs: includeAttrsSync
561
- });
562
- }
563
- }
540
+ createEventListener.call(this, 'blur', (e) => {
541
+ this.#dispatchBlur();
542
+ }, { element: this.baseElement });
564
543
 
565
- attributeChangedCallback() {
566
- if (!this.proxyElement) {
567
- return;
568
- }
569
- }
544
+ createEventListener.call(this, 'focus', (e) => {
545
+ this.#dispatchFocus();
546
+ }, { element: this.baseElement });
570
547
 
571
- disconnectedCallback() {
572
- super.disconnectedCallback?.();
548
+ createEventListener.call(this, 'focus', (e) => {
549
+ // if we got a focus event we want to focus the proxy element
550
+ if (e.isTrusted) {
551
+ this.focus();
552
+ }
553
+ });
554
+
555
+ // this is needed for components that uses props, such as combo box
556
+ forwardProps(this, this.baseElement, includeForwardProps);
557
+
558
+ // `onkeydown` is set on `baseElement` support proper tab-index navigation
559
+ // this support is needed since both proxy host and element catch `focus`/`blur` event
560
+ // which causes faulty behavior.
561
+ // we need this to happen only when the proxy component is in the light DOM,
562
+ // otherwise it will focus the nested proxy element
563
+ this.baseElement.onkeydown = (e) => {
564
+ if (e.shiftKey && e.keyCode === 9 && this.getRootNode() === document) {
565
+ this.removeAttribute('tabindex');
566
+ // We want to defer the action of setting the tab index back
567
+ // so it will happen after focusing the previous element
568
+ setTimeout(() => this.setAttribute('tabindex', '0'), 0);
569
+ }
570
+ };
573
571
 
574
- this.removeEventListener('focus', this.#boundOnFocus);
572
+ syncAttrs(this.baseElement, this, {
573
+ excludeAttrs: excludeAttrsSync,
574
+ includeAttrs: includeAttrsSync
575
+ });
575
576
  }
576
577
  }
577
578
 
578
579
  return ProxyClass;
579
580
  };
580
581
 
581
- // move create event to here
582
-
583
- // usage example:
584
- // #dispatchSomething = createDispatchEvent.bind(this, 'something')
585
- function createDispatchEvent(eventName) {
586
- this[`on${eventName}`]?.(); // in case we got an event callback as property
587
- this.dispatchEvent(new Event(eventName));
588
- }
589
-
590
- const observedAttributes = [
582
+ const observedAttributes$2 = [
591
583
  'required',
592
584
  'pattern',
593
585
  ];
@@ -600,7 +592,7 @@ const inputValidationMixin = (superclass) => class InputValidationMixinClass ext
600
592
  static get observedAttributes() {
601
593
  return [
602
594
  ...superclass.observedAttributes || [],
603
- ...observedAttributes
595
+ ...observedAttributes$2
604
596
  ];
605
597
  }
606
598
 
@@ -608,19 +600,12 @@ const inputValidationMixin = (superclass) => class InputValidationMixinClass ext
608
600
  return true;
609
601
  }
610
602
 
611
- #dispatchValid = createDispatchEvent.bind(this, 'valid')
612
- #dispatchInvalid = createDispatchEvent.bind(this, 'invalid')
613
-
614
603
  #internals
615
604
 
616
- #boundedHandleInput
617
-
618
605
  constructor() {
619
606
  super();
620
607
 
621
608
  this.#internals = this.attachInternals();
622
-
623
- this.#boundedHandleInput = this.#handleInput.bind(this);
624
609
  }
625
610
 
626
611
  get defaultErrorMsgValueMissing() {
@@ -646,7 +631,7 @@ const inputValidationMixin = (superclass) => class InputValidationMixinClass ext
646
631
  }
647
632
  }
648
633
 
649
- setValidity() {
634
+ #setValidity() {
650
635
  const validity = this.getValidity();
651
636
  this.#internals.setValidity(validity, this.getErrorMessage(validity));
652
637
  }
@@ -676,7 +661,7 @@ const inputValidationMixin = (superclass) => class InputValidationMixinClass ext
676
661
  this.#internals.setValidity({ customError: true }, errorMessage);
677
662
  } else {
678
663
  this.#internals.setValidity({});
679
- this.setValidity();
664
+ this.#setValidity();
680
665
  }
681
666
  }
682
667
 
@@ -688,38 +673,25 @@ const inputValidationMixin = (superclass) => class InputValidationMixinClass ext
688
673
  return this.getAttribute('pattern')
689
674
  }
690
675
 
691
- #handleInput() {
692
- this.setValidity();
693
- this.handleDispatchValidationEvents();
676
+ get form() {
677
+ return this.#internals.form
694
678
  }
695
679
 
696
680
  attributeChangedCallback(attrName, oldValue, newValue) {
697
681
  super.attributeChangedCallback?.(attrName, oldValue, newValue);
698
682
 
699
- if (observedAttributes.includes(attrName)) {
700
- this.setValidity();
701
- }
702
- }
703
-
704
- handleDispatchValidationEvents() {
705
- if (this.checkValidity()) {
706
- this.#dispatchValid();
707
- } else {
708
- this.#dispatchInvalid();
683
+ if (observedAttributes$2.includes(attrName)) {
684
+ this.#setValidity();
709
685
  }
710
686
  }
711
687
 
712
688
  connectedCallback() {
713
689
  super.connectedCallback?.();
690
+ createEventListener.call(this, 'change', this.#setValidity);
691
+ createEventListener.call(this, 'invalid', (e) => e.stopPropagation());
692
+ createEventListener.call(this, 'input', this.#setValidity);
714
693
 
715
- this.addEventListener('input', this.#boundedHandleInput);
716
-
717
- this.setValidity();
718
- this.handleDispatchValidationEvents();
719
- }
720
-
721
- disconnectedCallback() {
722
- this.removeEventListener('input', this.#boundedHandleInput);
694
+ this.#setValidity();
723
695
  }
724
696
  };
725
697
 
@@ -759,26 +731,17 @@ const proxyInputMixin = (superclass) =>
759
731
 
760
732
  #inputElement
761
733
 
762
- #boundHandleFocus
763
- #boundHandleInvalid
764
- #boundHandleValid
734
+ #dispatchChange = createDispatchEvent.bind(this, 'change')
765
735
 
766
736
  constructor() {
767
737
  super();
768
738
 
769
739
  this.#inputElement = super.inputElement;
770
-
771
- this.#boundHandleFocus = this.#handleFocus.bind(this);
772
- this.#boundHandleInvalid = this.#handleInvalid.bind(this);
773
- this.#boundHandleValid = this.#handleValid.bind(this);
774
-
775
- this.baseEle = this.shadowRoot.querySelector(this.baseSelector);
776
-
777
740
  }
778
741
 
779
742
  get inputElement() {
780
- const inputSlot = this.baseEle.shadowRoot.querySelector('slot[name="input"]');
781
- const textAreaSlot = this.baseEle.shadowRoot.querySelector('slot[name="textarea"]');
743
+ const inputSlot = this.baseElement.shadowRoot.querySelector('slot[name="input"]');
744
+ const textAreaSlot = this.baseElement.shadowRoot.querySelector('slot[name="textarea"]');
782
745
 
783
746
  this.#inputElement ??= getNestedInput(inputSlot) || getNestedInput(textAreaSlot);
784
747
 
@@ -797,7 +760,6 @@ const proxyInputMixin = (superclass) =>
797
760
 
798
761
  reportValidityOnInternalInput() {
799
762
  setTimeout(() => {
800
- this.baseEle.focus(); //TODO: check if this is needed
801
763
  this.inputElement.reportValidity();
802
764
  });
803
765
  }
@@ -810,202 +772,140 @@ const proxyInputMixin = (superclass) =>
810
772
  }
811
773
  }
812
774
 
813
- setInternalInputErrorMessage() {
814
- if (!this.checkValidity()) {
775
+ handleInternalInputErrorMessage() {
776
+ if (!this.inputElement.checkValidity()) {
815
777
  this.inputElement.setCustomValidity(this.validationMessage);
816
778
  }
817
779
  }
818
780
 
819
- // when clicking on the form submit button and the input is invalid
820
- // we want it to appear as invalid
821
- #handleFocus(e) {
822
- if (e.relatedTarget?.form) {
823
- if (!this.checkValidity()) {
824
- this.setAttribute('invalid', 'true');
825
- }
826
-
827
- if (this.hasAttribute('invalid')) {
828
- this.reportValidityOnInternalInput();
829
- }
830
- }
831
- }
832
-
833
- #handleInvalid() {
834
- this.setInternalInputErrorMessage();
781
+ #handleErrorMessage() {
782
+ this.handleInternalInputErrorMessage();
835
783
  this.setAttribute('error-message', this.validationMessage);
836
784
  }
837
785
 
838
- #handleValid() {
839
- this.removeAttribute('invalid');
840
- }
841
-
842
786
  connectedCallback() {
843
787
  super.connectedCallback?.();
844
788
 
845
- // this is our way to identify that the form was submitted
846
- // in this case, we want the input to be in error state if it's not valid
847
- this.addEventListener('focus', this.#boundHandleFocus);
789
+ createEventListener.call(this, 'input', (e) => {
790
+ if (!this.inputElement.checkValidity()) {
791
+ this.inputElement.setCustomValidity('');
792
+ // after updating the input validity we want to trigger set validity on the wrapping element
793
+ // so we will get the updated validity
794
+ this.setCustomValidity('');
795
+
796
+ // Vaadin is getting the input event before us,
797
+ // so in order to make sure they use the updated validity
798
+ // we calling their fn after updating the input validity
799
+ this.baseElement.__onInput(e);
800
+
801
+ this.#handleErrorMessage();
802
+ }
803
+ }, { element: this.inputElement });
804
+
805
+ createEventListener.call(this, 'change', () => {
806
+ this.#dispatchChange();
807
+ }, { element: this.baseElement });
848
808
 
849
- this.addEventListener('invalid', this.#boundHandleInvalid);
850
- this.addEventListener('valid', this.#boundHandleValid);
809
+ createEventListener.call(this, 'blur', () => {
810
+ if (!this.checkValidity()) {
811
+ this.setAttribute('invalid', 'true');
812
+ this.#handleErrorMessage();
813
+ }
814
+ });
851
815
 
852
- this.addEventListener('input', () => {
853
- this.inputElement.setCustomValidity('');
854
- if (!this.inputElement.checkValidity())
855
- this.setInternalInputErrorMessage();
816
+ createEventListener.call(this, 'focus', (e) => {
817
+ // when clicking on the form submit button and the input is invalid
818
+ // we want it to appear as invalid
819
+ if (e.relatedTarget?.form === this.form) {
820
+ if (!this.checkValidity()) {
821
+ this.setAttribute('invalid', 'true');
822
+ }
823
+
824
+ if (this.hasAttribute('invalid')) {
825
+ this.reportValidityOnInternalInput();
826
+ }
827
+ }
856
828
  });
857
829
 
830
+ createEventListener.call(this, 'invalid', this.#handleErrorMessage);
831
+
832
+ this.handleInternalInputErrorMessage();
833
+
858
834
  // this is needed in order to make sure the form input validation is working
835
+ // we do not want it to happen when the component is nested
859
836
  if (!this.hasAttribute('tabindex') && this.getRootNode() === document) {
860
837
  this.setAttribute('tabindex', 0);
861
838
  }
862
839
 
863
840
  // sync properties
864
841
  propertyObserver(this, this.inputElement, 'value');
842
+ propertyObserver(this, this.inputElement, 'selectionStart');
865
843
  this.setSelectionRange = this.inputElement.setSelectionRange?.bind(this.inputElement);
866
844
  }
845
+ };
867
846
 
868
- disconnectedCallback() {
869
- this.removeEventListener('focus', this.#boundHandleFocus);
870
- this.removeEventListener('invalid', this.#boundHandleInvalid);
871
- this.removeEventListener('valid', this.#boundHandleValid);
872
- }
847
+ const componentNameValidationMixin = (superclass) =>
848
+ class ComponentNameValidationMixinClass extends superclass {
849
+ #checkComponentName() {
850
+ const currentComponentName = this.localName;
873
851
 
874
- attributeChangedCallback(attrName, oldValue, newValue) {
875
- super.attributeChangedCallback?.(attrName, oldValue, newValue);
852
+ if (!superclass.componentName) {
853
+ throw Error(
854
+ `component name is not defined on super class, make sure you have a static get for "componentName"`
855
+ );
856
+ }
876
857
 
877
- if (attrName === 'invalid' && newValue === '') {
878
- this.setAttribute('invalid', 'true');
858
+ if (currentComponentName !== superclass.componentName) {
859
+ throw Error(
860
+ `component name mismatch, expected "${superclass.componentName}", current "${currentComponentName}"`
861
+ );
879
862
  }
880
863
  }
881
- };
882
-
883
- const events = [
884
- 'blur',
885
- 'focus',
886
- 'focusin',
887
- 'focusout',
888
- ];
889
-
890
- const focusMixin = (superclass) => class FocusMixinClass extends superclass {
891
- #isFocused = false
892
- #setFocusTimer
893
-
894
- #boundedHandleFocus
895
- #boundedHandleBlur
896
- #boundedHandleFocusIn
897
- #boundedHandleFocusOut
898
-
899
- constructor() {
900
- super();
901
-
902
- for (const event of events) {
903
- this[`dispatch${upperFirst(event)}`] = function () {
904
- this.dispatchInputEvent(event);
905
- };
906
- }
907
-
908
- this.#boundedHandleFocus = this.#handleFocus.bind(this);
909
- this.#boundedHandleBlur = this.#handleBlur.bind(this);
910
- this.#boundedHandleFocusIn = this.#handleFocusIn.bind(this);
911
- this.#boundedHandleFocusOut = this.#handleFocusOut.bind(this);
912
- }
913
-
914
- #handleFocus(e) {
915
- if (this.isReadOnly) {
916
- e.stopPropagation();
917
- return
918
- }
919
- // we want to listen only to browser events
920
- // and not to events we are dispatching
921
- if (e.isTrusted) { // TODO: check if this is needed, because Vaadin is also dispatching events
922
- // we want to control the focus events that dispatched by the component
923
- // so we are stopping propagation and handling it in setFocus
924
- e.stopPropagation();
925
- this.setFocus(true);
926
-
927
- // if the focus event is on the root component (and not on the inner components)
928
- // we want to notify the component and let it decide what to do with it
929
- if (e.target === this) {
930
- this.onFocus(e);
931
- }
932
- }
933
- }
934
-
935
- #handleFocusOut(e) {
936
- // we want to listen only to browser events
937
- // and not to events we are dispatching
938
- if (e.isTrusted) {
939
- // we want to control the focus events that dispatched by the component
940
- // so we are stopping propagation and handling it in setFocus
941
- e.stopPropagation();
942
- }
943
- }
944
-
945
- #handleFocusIn(e) {
946
- // we want to listen only to browser events
947
- // and not to events we are dispatching
948
- if (e.isTrusted) {
949
- // we want to control the focus events that dispatched by the component
950
- // so we are stopping propagation and handling it in setFocus
951
- e.stopPropagation();
952
- }
953
- }
954
-
955
- #handleBlur(e) {
956
- if (e.isTrusted) {
957
- e.stopPropagation();
958
- this.setFocus(false);
959
- }
960
- }
961
-
962
- get isReadOnly() {
963
- return this.hasAttribute('readonly') && this.getAttribute('readonly') !== 'false'
964
- }
965
864
 
966
- // we want to debounce the calls to this fn
967
- // so we can support input like components with multiple inputs inside
968
- setFocus(isFocused) {
969
- clearTimeout(this.#setFocusTimer);
970
-
971
- this.#setFocusTimer = setTimeout(() => {
972
- if (this.#isFocused !== isFocused) {
973
- this.#isFocused = isFocused;
974
- if (isFocused) {
975
- this.dispatchFocus();
976
- this.dispatchFocusin();
977
- }
978
- else {
979
- this.dispatchBlur();
980
- this.dispatchFocusout();
981
- }
982
- }
983
- });
984
- }
865
+ connectedCallback() {
866
+ super.connectedCallback?.();
867
+ this.#checkComponentName();
868
+ }
869
+ };
985
870
 
986
- onFocus() {
987
- console.warn('onFocus', 'is not implemented');
988
- }
871
+ const hoverableMixin =
872
+ (superclass) =>
873
+ class HoverableMixinClass extends superclass {
874
+ connectedCallback() {
875
+ super.connectedCallback?.();
989
876
 
990
- dispatchInputEvent(eventName) {
991
- this[`on${eventName}`]?.(); // in case we got an event callback as property
992
- this.dispatchEvent(new InputEvent(eventName));
993
- }
877
+ createEventListener.call(this, 'mouseover', (e) => {
878
+ this.setAttribute('hover', 'true');
879
+ e.target.addEventListener(
880
+ 'mouseleave',
881
+ () => this.removeAttribute('hover'),
882
+ { once: true }
883
+ );
884
+ }, { element: this.baseElement });
885
+ }
886
+ };
994
887
 
888
+ const focusMixin = (superclass) => class FocusMixinClass extends superclass {
889
+ // we want to block all native events,
890
+ // so the input can control when to dispatch it based on its internal behavior
995
891
  connectedCallback() {
996
- super.connectedCallback?.();
892
+ super.connectedCallback?.();
893
+
894
+ createEventListener.call(this, 'blur', (e) => {
895
+ e.isTrusted && e.stopImmediatePropagation();
896
+ });
997
897
 
998
- this.addEventListener('focus', this.#boundedHandleFocus, true);
999
- this.addEventListener('blur', this.#boundedHandleBlur, true);
1000
- this.addEventListener('focusin', this.#boundedHandleFocusIn);
1001
- this.addEventListener('focusout', this.#boundedHandleFocusOut);
1002
- }
898
+ createEventListener.call(this, 'focus', (e) => {
899
+ e.isTrusted && e.stopImmediatePropagation();
900
+ });
901
+
902
+ createEventListener.call(this, 'focusout', (e) => {
903
+ e.isTrusted && e.stopImmediatePropagation();
904
+ });
1003
905
 
1004
- disconnectedCallback() {
1005
- this.removeEventListener('focus', this.#boundedHandleFocus);
1006
- this.removeEventListener('blur', this.#boundedHandleBlur);
1007
- this.removeEventListener('focusin', this.#boundedHandleFocusIn);
1008
- this.removeEventListener('focusout', this.#boundedHandleFocusOut);
906
+ createEventListener.call(this, 'focusin', (e) => {
907
+ e.isTrusted && e.stopImmediatePropagation();
908
+ });
1009
909
  }
1010
910
  };
1011
911
 
@@ -1071,52 +971,38 @@ const portalMixin = ({ name, selector, mappings = {} }) => (superclass) => {
1071
971
  };
1072
972
 
1073
973
  const changeMixin = (superclass) => class ChangeMixinClass extends superclass {
1074
-
1075
- #boundedHandleChange
1076
- #boundedHandleBlur
1077
-
1078
- #removeChangeListener
1079
- #removeBlurListener
1080
-
1081
974
  #dispatchChange = createDispatchEvent.bind(this, 'change')
1082
975
 
1083
- constructor() {
1084
- super();
1085
-
1086
- this.#boundedHandleChange = this.#handleChange.bind(this);
1087
- this.#boundedHandleBlur = this.#handleBlur.bind(this);
1088
- }
976
+ connectedCallback() {
977
+ super.connectedCallback?.();
978
+ this.prevValue = this.value;
1089
979
 
1090
- #handleChange(e) {
1091
- // we want to listen only to browser events
1092
- // and not to events we are dispatching
1093
- if (e.isTrusted) {
1094
- // we want to control the change events that dispatched by the component
1095
- // so we are stopping propagation and handling it in handleBlur
980
+ createEventListener.call(this, 'change', (e) => {
1096
981
  e.stopPropagation();
1097
- }
1098
- }
982
+ });
1099
983
 
1100
- #handleBlur() {
1101
- // on blur, we want to dispatch a change event
1102
- this.#dispatchChange();
984
+ createEventListener.call(this, 'blur', () => {
985
+ if (this.value !== this.prevValue) {
986
+ this.#dispatchChange();
987
+ this.prevValue = this.value;
988
+ }
989
+ });
1103
990
  }
991
+ };
1104
992
 
1105
- connectedCallback() {
1106
- super.connectedCallback?.();
1107
-
1108
- this.#removeChangeListener = addEventListener.bind();
1109
- this.addEventListener('change', this.#boundedHandleChange, true);
1110
- this.addEventListener('blur', this.#boundedHandleBlur);
1111
- }
993
+ // we want all the valueless attributes to have "true" value
994
+ const normalizeBooleanAttributesMixin = (superclass) => class NormalizeBooleanAttributesMixinClass extends superclass {
995
+ attributeChangedCallback(attrName, oldValue, newValue) {
996
+ if (newValue === '') {
997
+ this.setAttribute(attrName, 'true');
998
+ newValue = 'true';
999
+ }
1112
1000
 
1113
- disconnectedCallback() {
1114
- this.removeEventListener('change', this.#boundedHandleChange);
1115
- this.removeEventListener('blur', this.#boundedHandleBlur);
1001
+ super.attributeChangedCallback?.(attrName, oldValue, newValue);
1116
1002
  }
1117
1003
  };
1118
1004
 
1119
- const componentName$i = getComponentName('button');
1005
+ const componentName$j = getComponentName('button');
1120
1006
 
1121
1007
  const editorOverrides = `vaadin-button::part(label) { pointer-events: none; }`;
1122
1008
  const resetStyles = `
@@ -1170,7 +1056,7 @@ const Button = compose(
1170
1056
  style: () =>
1171
1057
  `${resetStyles} ${editorOverrides} ${iconStyles} ${loadingIndicatorStyles}`,
1172
1058
  excludeAttrsSync: ['tabindex'],
1173
- componentName: componentName$i
1059
+ componentName: componentName$j
1174
1060
  })
1175
1061
  );
1176
1062
 
@@ -1207,9 +1093,9 @@ const loadingIndicatorStyles = `
1207
1093
  }
1208
1094
  `;
1209
1095
 
1210
- customElements.define(componentName$i, Button);
1096
+ customElements.define(componentName$j, Button);
1211
1097
 
1212
- const componentName$h = getComponentName('checkbox');
1098
+ const componentName$i = getComponentName('checkbox');
1213
1099
 
1214
1100
  const Checkbox = compose(
1215
1101
  createStyleMixin({
@@ -1235,17 +1121,17 @@ const Checkbox = compose(
1235
1121
  }
1236
1122
  `,
1237
1123
  excludeAttrsSync: ['tabindex'],
1238
- componentName: componentName$h
1124
+ componentName: componentName$i
1239
1125
  })
1240
1126
  );
1241
1127
 
1242
- customElements.define(componentName$h, Checkbox);
1128
+ customElements.define(componentName$i, Checkbox);
1243
1129
 
1244
- const componentName$g = getComponentName('loader-linear');
1130
+ const componentName$h = getComponentName('loader-linear');
1245
1131
 
1246
- class RawLoaderLinear extends createBaseClass({ componentName: componentName$g, baseSelector: ':host > div' }) {
1132
+ class RawLoaderLinear extends createBaseClass({ componentName: componentName$h, baseSelector: ':host > div' }) {
1247
1133
  static get componentName() {
1248
- return componentName$g;
1134
+ return componentName$h;
1249
1135
  }
1250
1136
  constructor() {
1251
1137
  super();
@@ -1303,11 +1189,11 @@ const LoaderLinear = compose(
1303
1189
  componentNameValidationMixin
1304
1190
  )(RawLoaderLinear);
1305
1191
 
1306
- customElements.define(componentName$g, LoaderLinear);
1192
+ customElements.define(componentName$h, LoaderLinear);
1307
1193
 
1308
- const componentName$f = getComponentName('loader-radial');
1194
+ const componentName$g = getComponentName('loader-radial');
1309
1195
 
1310
- class RawLoaderRadial extends createBaseClass({ componentName: componentName$f, baseSelector: ':host > div' }) {
1196
+ class RawLoaderRadial extends createBaseClass({ componentName: componentName$g, baseSelector: ':host > div' }) {
1311
1197
  constructor() {
1312
1198
  super();
1313
1199
 
@@ -1353,11 +1239,11 @@ const LoaderRadial = compose(
1353
1239
  componentNameValidationMixin
1354
1240
  )(RawLoaderRadial);
1355
1241
 
1356
- customElements.define(componentName$f, LoaderRadial);
1242
+ customElements.define(componentName$g, LoaderRadial);
1357
1243
 
1358
- const componentName$e = getComponentName('container');
1244
+ const componentName$f = getComponentName('container');
1359
1245
 
1360
- class RawContainer extends createBaseClass({componentName: componentName$e, baseSelector: ':host > slot'}) {
1246
+ class RawContainer extends createBaseClass({componentName: componentName$f, baseSelector: ':host > slot'}) {
1361
1247
  constructor() {
1362
1248
  super();
1363
1249
 
@@ -1413,26 +1299,26 @@ const Container = compose(
1413
1299
  componentNameValidationMixin
1414
1300
  )(RawContainer);
1415
1301
 
1416
- customElements.define(componentName$e, Container);
1302
+ customElements.define(componentName$f, Container);
1417
1303
 
1418
- const componentName$d = getComponentName('date-picker');
1304
+ const componentName$e = getComponentName('date-picker');
1419
1305
 
1420
1306
  const DatePicker = compose(
1421
1307
  draggableMixin,
1422
1308
  componentNameValidationMixin
1423
1309
  )(
1424
1310
  createProxy({
1425
- componentName: componentName$d,
1311
+ componentName: componentName$e,
1426
1312
  slots: ['prefix', 'suffix'],
1427
1313
  wrappedEleName: 'vaadin-date-picker',
1428
1314
  style: ``
1429
1315
  })
1430
1316
  );
1431
1317
 
1432
- customElements.define(componentName$d, DatePicker);
1318
+ customElements.define(componentName$e, DatePicker);
1433
1319
 
1434
- const componentName$c = getComponentName('divider');
1435
- class RawDivider extends createBaseClass({ componentName: componentName$c, baseSelector: ':host > div' }) {
1320
+ const componentName$d = getComponentName('divider');
1321
+ class RawDivider extends createBaseClass({ componentName: componentName$d, baseSelector: ':host > div' }) {
1436
1322
  constructor() {
1437
1323
  super();
1438
1324
 
@@ -1510,9 +1396,9 @@ const Divider = compose(
1510
1396
  componentNameValidationMixin
1511
1397
  )(RawDivider);
1512
1398
 
1513
- const componentName$b = getComponentName('text');
1399
+ const componentName$c = getComponentName('text');
1514
1400
 
1515
- class RawText extends createBaseClass({ componentName: componentName$b, baseSelector: ':host > slot' }) {
1401
+ class RawText extends createBaseClass({ componentName: componentName$c, baseSelector: ':host > slot' }) {
1516
1402
  constructor() {
1517
1403
  super();
1518
1404
 
@@ -1554,9 +1440,9 @@ const Text = compose(
1554
1440
  componentNameValidationMixin
1555
1441
  )(RawText);
1556
1442
 
1557
- customElements.define(componentName$b, Text);
1443
+ customElements.define(componentName$c, Text);
1558
1444
 
1559
- customElements.define(componentName$c, Divider);
1445
+ customElements.define(componentName$d, Divider);
1560
1446
 
1561
1447
  const selectors$3 = {
1562
1448
  label: '::part(label)',
@@ -1593,7 +1479,7 @@ var textFieldMappings = {
1593
1479
  placeholderColor: { selector: selectors$3.placeholder, property: 'color' }
1594
1480
  };
1595
1481
 
1596
- const componentName$a = getComponentName('email-field');
1482
+ const componentName$b = getComponentName('email-field');
1597
1483
 
1598
1484
  let overrides$5 = ``;
1599
1485
 
@@ -1612,7 +1498,7 @@ const EmailField = compose(
1612
1498
  wrappedEleName: 'vaadin-email-field',
1613
1499
  style: () => overrides$5,
1614
1500
  excludeAttrsSync: ['tabindex'],
1615
- componentName: componentName$a
1501
+ componentName: componentName$b
1616
1502
  })
1617
1503
  );
1618
1504
 
@@ -1656,10 +1542,10 @@ overrides$5 = `
1656
1542
  }
1657
1543
  `;
1658
1544
 
1659
- customElements.define(componentName$a, EmailField);
1545
+ customElements.define(componentName$b, EmailField);
1660
1546
 
1661
- const componentName$9 = getComponentName('link');
1662
- class RawLink extends createBaseClass({ componentName: componentName$9, baseSelector: ':host a' }) {
1547
+ const componentName$a = getComponentName('link');
1548
+ class RawLink extends createBaseClass({ componentName: componentName$a, baseSelector: ':host a' }) {
1663
1549
  constructor() {
1664
1550
  super();
1665
1551
  document.createElement('template');
@@ -1720,14 +1606,14 @@ const Link = compose(
1720
1606
  componentNameValidationMixin
1721
1607
  )(RawLink);
1722
1608
 
1723
- customElements.define(componentName$9, Link);
1609
+ customElements.define(componentName$a, Link);
1724
1610
 
1725
- const componentName$8 = getComponentName('logo');
1611
+ const componentName$9 = getComponentName('logo');
1726
1612
 
1727
1613
  let style;
1728
1614
  const getStyle = () => style;
1729
1615
 
1730
- class RawLogo extends createBaseClass({ componentName: componentName$8, baseSelector: ':host > div' }) {
1616
+ class RawLogo extends createBaseClass({ componentName: componentName$9, baseSelector: ':host > div' }) {
1731
1617
  constructor() {
1732
1618
  super();
1733
1619
 
@@ -1762,9 +1648,9 @@ style = `
1762
1648
  }
1763
1649
  `;
1764
1650
 
1765
- customElements.define(componentName$8, Logo);
1651
+ customElements.define(componentName$9, Logo);
1766
1652
 
1767
- const componentName$7 = getComponentName('number-field');
1653
+ const componentName$8 = getComponentName('number-field');
1768
1654
 
1769
1655
  let overrides$4 = ``;
1770
1656
 
@@ -1783,7 +1669,7 @@ const NumberField = compose(
1783
1669
  wrappedEleName: 'vaadin-number-field',
1784
1670
  style: () => overrides$4,
1785
1671
  excludeAttrsSync: ['tabindex'],
1786
- componentName: componentName$7
1672
+ componentName: componentName$8
1787
1673
  })
1788
1674
  );
1789
1675
 
@@ -1827,9 +1713,13 @@ overrides$4 = `
1827
1713
  }
1828
1714
  `;
1829
1715
 
1830
- customElements.define(componentName$7, NumberField);
1716
+ customElements.define(componentName$8, NumberField);
1831
1717
 
1832
- var BaseInputClass = compose(focusMixin, inputValidationMixin, changeMixin)(HTMLElement); //todo: maybe we should use base class?
1718
+ const createBaseInputClass = (...args) => compose(
1719
+ focusMixin,
1720
+ inputValidationMixin,
1721
+ changeMixin,
1722
+ )(createBaseClass(...args));
1833
1723
 
1834
1724
  const focusElement = (ele) => {
1835
1725
  ele?.focus();
@@ -1845,25 +1735,28 @@ const getSanitizedCharacters = (str) => {
1845
1735
  return [...pin]; // creating array of chars
1846
1736
  };
1847
1737
 
1848
- const componentName$6 = getComponentName('passcode-internal');
1738
+ const componentName$7 = getComponentName('passcode-internal');
1739
+
1740
+ const observedAttributes$1 = [
1741
+ 'disabled',
1742
+ 'bordered',
1743
+ 'size',
1744
+ 'invalid'
1745
+ ];
1746
+
1747
+ const BaseInputClass = createBaseInputClass({ componentName: componentName$7, baseSelector: ':host > div' });
1849
1748
 
1850
1749
  class PasscodeInternal extends BaseInputClass {
1851
1750
  static get observedAttributes() {
1852
- return [
1853
- ...(BaseInputClass.observedAttributes || []),
1854
- 'disabled',
1855
- 'bordered',
1856
- 'size',
1857
- ];
1751
+ return observedAttributes$1.concat(BaseInputClass.observedAttributes || []);
1858
1752
  }
1859
1753
 
1860
1754
  static get componentName() {
1861
- return componentName$6;
1755
+ return componentName$7;
1862
1756
  }
1863
1757
 
1864
- #boundHandleInvalid = this.#handleInvalid.bind(this)
1865
- #boundHandleValid = this.#handleValid.bind(this)
1866
- #boundHandleBlur = this.#handleBlur.bind(this)
1758
+ #dispatchBlur = createDispatchEvent.bind(this, 'blur')
1759
+ #dispatchFocus = createDispatchEvent.bind(this, 'focus')
1867
1760
 
1868
1761
  constructor() {
1869
1762
  super();
@@ -1872,7 +1765,7 @@ class PasscodeInternal extends BaseInputClass {
1872
1765
  st-width="35px"
1873
1766
  data-id=${idx}
1874
1767
  type="tel"
1875
- autocomplete="none"
1768
+ autocomplete="off"
1876
1769
  ></descope-text-field>
1877
1770
  `);
1878
1771
 
@@ -1882,8 +1775,6 @@ class PasscodeInternal extends BaseInputClass {
1882
1775
  </div>
1883
1776
  `;
1884
1777
 
1885
- this.baseSelector = ':host > div';
1886
-
1887
1778
  this.inputs = Array.from(this.querySelectorAll('descope-text-field'));
1888
1779
  }
1889
1780
 
@@ -1909,16 +1800,6 @@ class PasscodeInternal extends BaseInputClass {
1909
1800
  return `^$|^\\d{${this.digits},}$`
1910
1801
  }
1911
1802
 
1912
- #handleInvalid() {
1913
- if (this.hasAttribute('invalid')) {
1914
- this.inputs.forEach(input => input.setAttribute('invalid', 'true'));
1915
- }
1916
- }
1917
-
1918
- #handleValid() {
1919
- this.inputs.forEach(input => input.removeAttribute('invalid'));
1920
- }
1921
-
1922
1803
  getValidity() {
1923
1804
  if (this.isRequired && !this.value) {
1924
1805
  return { valueMissing: true };
@@ -1931,25 +1812,17 @@ class PasscodeInternal extends BaseInputClass {
1931
1812
  }
1932
1813
  };
1933
1814
 
1934
- onFocus(){
1935
- this.inputs[0].focus();
1936
- }
1937
-
1938
1815
  connectedCallback() {
1816
+ // we are adding listeners before calling to super because it's stopping the events
1817
+ createEventListener.call(this, 'focus', (e) => {
1818
+ // we want to ignore focus events we are dispatching
1819
+ if (e.isTrusted)
1820
+ this.inputs[0].focus();
1821
+ });
1822
+
1939
1823
  super.connectedCallback?.();
1940
1824
 
1941
1825
  this.initInputs();
1942
-
1943
- this.addEventListener('invalid', this.#boundHandleInvalid);
1944
- this.addEventListener('valid', this.#boundHandleValid);
1945
- this.addEventListener('blur', this.#boundHandleBlur);
1946
- }
1947
-
1948
- disconnectedCallback() {
1949
- super.connectedCallback?.();
1950
- this.removeEventListener('invalid', this.#boundHandleInvalid);
1951
- this.removeEventListener('valid', this.#boundHandleValid);
1952
- this.removeEventListener('blur', this.#boundHandleBlur);
1953
1826
  }
1954
1827
 
1955
1828
  getInputIdx(inputEle) {
@@ -1981,33 +1854,60 @@ class PasscodeInternal extends BaseInputClass {
1981
1854
  focusElement(currentInput);
1982
1855
  };
1983
1856
 
1984
- #handleBlur() {
1985
- this.#handleInvalid();
1986
- }
1987
-
1988
1857
  initInputs() {
1858
+ let prevVal = this.value;
1859
+ let blurTimerId;
1860
+
1989
1861
  this.inputs.forEach((input) => {
1990
- input.oninput = (e) => {
1862
+ // in order to simulate blur on the input
1863
+ // we are checking if focus on one of the digits happened immediately after blur on another digit
1864
+ // if not, the component is no longer focused and we should simulate blur
1865
+ createEventListener.call(this, 'blur', (e) => {
1866
+ e.stopImmediatePropagation();
1867
+
1868
+ blurTimerId = setTimeout(() => {
1869
+ blurTimerId = null;
1870
+ this.#dispatchBlur();
1871
+ });
1872
+ }, { element: input });
1873
+
1874
+ createEventListener.call(this, 'focus', (e) => {
1875
+ e.stopImmediatePropagation();
1876
+
1877
+ clearTimeout(blurTimerId);
1878
+ if (!blurTimerId) {
1879
+ this.#dispatchFocus();
1880
+ }
1881
+ }, { element: input });
1882
+
1883
+ createEventListener.call(this, 'input', (e) => {
1991
1884
  const charArr = getSanitizedCharacters(input.value);
1992
1885
 
1993
1886
  if (!charArr.length) {
1994
1887
  // if we got an invalid value we want to clear the input
1995
1888
  input.value = '';
1996
- if (e.data === null) {
1997
- // if the user deleted the char, we want to focus the prev digit
1998
- focusElement(this.getPrevInput(input));
1999
- }
2000
1889
  }
2001
1890
  else this.fillDigits(charArr, input);
2002
- };
1891
+
1892
+ // we want to stop input events if the value wasn't change
1893
+ if (prevVal === this.value) {
1894
+ e.stopImmediatePropagation();
1895
+ }
1896
+ }, { element: input });
2003
1897
 
2004
1898
  input.onkeydown = ({ key }) => {
1899
+ prevVal = this.value;
1900
+
2005
1901
  // when user deletes a digit, we want to focus the previous digit
2006
1902
  if (key === 'Backspace') {
1903
+ // if the cursor is at 0, we want to move it to 1, so the value will be deleted
1904
+ if (!input.selectionStart) {
1905
+ input.setSelectionRange(1, 1);
1906
+ }
2007
1907
  setTimeout(() => {
2008
1908
  focusElement(this.getPrevInput(input));
2009
1909
  });
2010
- } else if (key.match(/^(\d)$/g)) { // if input is a digit
1910
+ } else if (key.length === 1) { // we want only characters and not command keys
2011
1911
  input.value = ''; // we are clearing the previous value so we can override it with the new value
2012
1912
  }
2013
1913
  };
@@ -2017,15 +1917,20 @@ class PasscodeInternal extends BaseInputClass {
2017
1917
  attributeChangedCallback(attrName, oldValue, newValue) {
2018
1918
  super.attributeChangedCallback?.(attrName, oldValue, newValue);
2019
1919
 
1920
+ // sync attributes to inputs
2020
1921
  if (oldValue !== newValue) {
2021
- if (PasscodeInternal.observedAttributes.includes(attrName) && !BaseInputClass.observedAttributes.includes(attrName)) {
2022
- this.inputs.forEach((input) => input.setAttribute(attrName, newValue));
1922
+ if (observedAttributes$1.includes(attrName)) {
1923
+ this.inputs.forEach(
1924
+ (input) => newValue === null ?
1925
+ input.removeAttribute(attrName) :
1926
+ input.setAttribute(attrName, newValue)
1927
+ );
2023
1928
  }
2024
1929
  }
2025
1930
  }
2026
1931
  }
2027
1932
 
2028
- const componentName$5 = getComponentName('text-field');
1933
+ const componentName$6 = getComponentName('text-field');
2029
1934
 
2030
1935
  let overrides$3 = ``;
2031
1936
 
@@ -2042,7 +1947,7 @@ const TextField = compose(
2042
1947
  wrappedEleName: 'vaadin-text-field',
2043
1948
  style: () => overrides$3,
2044
1949
  excludeAttrsSync: ['tabindex'],
2045
- componentName: componentName$5
1950
+ componentName: componentName$6
2046
1951
  })
2047
1952
  );
2048
1953
 
@@ -2087,7 +1992,7 @@ overrides$3 = `
2087
1992
  }
2088
1993
  `;
2089
1994
 
2090
- const componentName$4 = getComponentName('passcode');
1995
+ const componentName$5 = getComponentName('passcode');
2091
1996
 
2092
1997
  const customMixin = (superclass) =>
2093
1998
  class DraggableMixinClass extends superclass {
@@ -2104,17 +2009,17 @@ const customMixin = (superclass) =>
2104
2009
  const template = document.createElement('template');
2105
2010
 
2106
2011
  template.innerHTML = `
2107
- <${componentName$6}
2012
+ <${componentName$7}
2108
2013
  bordered="true"
2109
2014
  name="code"
2110
2015
  tabindex="-1"
2111
2016
  slot="input"
2112
- ></${componentName$6}>
2017
+ ></${componentName$7}>
2113
2018
  `;
2114
2019
 
2115
- this.proxyElement.appendChild(template.content.cloneNode(true));
2020
+ this.baseElement.appendChild(template.content.cloneNode(true));
2116
2021
 
2117
- this.inputElement = this.shadowRoot.querySelector(componentName$6);
2022
+ this.inputElement = this.shadowRoot.querySelector(componentName$7);
2118
2023
 
2119
2024
  forwardAttrs(this.shadowRoot.host, this.inputElement, { includeAttrs: ['required', 'pattern'] });
2120
2025
 
@@ -2193,17 +2098,17 @@ const Passcode = compose(
2193
2098
  }
2194
2099
  `,
2195
2100
  excludeAttrsSync: ['tabindex'],
2196
- componentName: componentName$4
2101
+ componentName: componentName$5
2197
2102
  })
2198
2103
  );
2199
2104
 
2200
- customElements.define(componentName$5, TextField);
2105
+ customElements.define(componentName$6, TextField);
2201
2106
 
2202
- customElements.define(componentName$6, PasscodeInternal);
2107
+ customElements.define(componentName$7, PasscodeInternal);
2203
2108
 
2204
- customElements.define(componentName$4, Passcode);
2109
+ customElements.define(componentName$5, Passcode);
2205
2110
 
2206
- const componentName$3 = getComponentName('password-field');
2111
+ const componentName$4 = getComponentName('password-field');
2207
2112
 
2208
2113
  let overrides$2 = ``;
2209
2114
 
@@ -2228,7 +2133,7 @@ const PasswordField = compose(
2228
2133
  wrappedEleName: 'vaadin-password-field',
2229
2134
  style: () => overrides$2,
2230
2135
  excludeAttrsSync: ['tabindex'],
2231
- componentName: componentName$3
2136
+ componentName: componentName$4
2232
2137
  })
2233
2138
  );
2234
2139
 
@@ -2272,9 +2177,9 @@ overrides$2 = `
2272
2177
  }
2273
2178
  `;
2274
2179
 
2275
- customElements.define(componentName$3, PasswordField);
2180
+ customElements.define(componentName$4, PasswordField);
2276
2181
 
2277
- const componentName$2 = getComponentName('switch-toggle');
2182
+ const componentName$3 = getComponentName('switch-toggle');
2278
2183
 
2279
2184
  let overrides$1 = ``;
2280
2185
 
@@ -2294,7 +2199,7 @@ const SwitchToggle = compose(
2294
2199
  wrappedEleName: 'vaadin-checkbox',
2295
2200
  style: () => overrides$1,
2296
2201
  excludeAttrsSync: ['tabindex'],
2297
- componentName: componentName$2
2202
+ componentName: componentName$3
2298
2203
  })
2299
2204
  );
2300
2205
 
@@ -2352,9 +2257,9 @@ overrides$1 = `
2352
2257
  }
2353
2258
  `;
2354
2259
 
2355
- customElements.define(componentName$2, SwitchToggle);
2260
+ customElements.define(componentName$3, SwitchToggle);
2356
2261
 
2357
- const componentName$1 = getComponentName('text-area');
2262
+ const componentName$2 = getComponentName('text-area');
2358
2263
 
2359
2264
  const selectors$1 = {
2360
2265
  label: '::part(label)',
@@ -2390,7 +2295,7 @@ const TextArea = compose(
2390
2295
  wrappedEleName: 'vaadin-text-area',
2391
2296
  style: () => overrides,
2392
2297
  excludeAttrsSync: ['tabindex'],
2393
- componentName: componentName$1
2298
+ componentName: componentName$2
2394
2299
  })
2395
2300
  );
2396
2301
 
@@ -2416,7 +2321,53 @@ overrides = `
2416
2321
  }
2417
2322
  `;
2418
2323
 
2419
- customElements.define(componentName$1, TextArea);
2324
+ customElements.define(componentName$2, TextArea);
2325
+
2326
+ const observedAttributes = ['src', 'alt'];
2327
+
2328
+ const componentName$1 = getComponentName('image');
2329
+
2330
+ const BaseClass = createBaseClass({ componentName: componentName$1, baseSelector: ':host > img' });
2331
+ class RawImage extends BaseClass {
2332
+ static get observedAttributes() {
2333
+ return observedAttributes.concat(BaseClass.observedAttributes || []);
2334
+ }
2335
+
2336
+ constructor() {
2337
+ super();
2338
+
2339
+ this.attachShadow({ mode: 'open' }).innerHTML = `
2340
+ <style>
2341
+ :host > img {
2342
+ width: 100%;
2343
+ height: 100%
2344
+ }
2345
+ :host {
2346
+ display: inline-flex;
2347
+ }
2348
+ </style>
2349
+ <img/>
2350
+ `;
2351
+ }
2352
+
2353
+ connectedCallback(){
2354
+ super.connectedCallback?.();
2355
+
2356
+ forwardAttrs(this, this.baseElement, {includeAttrs: observedAttributes});
2357
+ }
2358
+ }
2359
+
2360
+ const Image = compose(
2361
+ createStyleMixin({
2362
+ mappings: {
2363
+ height: { selector: () => ':host' },
2364
+ width: { selector: () => ':host' },
2365
+ }
2366
+ }),
2367
+ draggableMixin,
2368
+ )(RawImage);
2369
+
2370
+ customElements.define(componentName$1, Image);
2420
2371
 
2421
2372
  const getVarName = (path) => getCssVarName(DESCOPE_PREFIX, ...path);
2422
2373
 
@@ -2700,7 +2651,7 @@ const mode = {
2700
2651
  surface: globalRefs$7.colors.surface
2701
2652
  };
2702
2653
 
2703
- const [helperTheme$2, helperRefs$2] = createHelperVars({ mode }, componentName$i);
2654
+ const [helperTheme$2, helperRefs$2] = createHelperVars({ mode }, componentName$j);
2704
2655
 
2705
2656
  const button = {
2706
2657
  ...helperTheme$2,
@@ -2937,7 +2888,7 @@ const [helperTheme$1, helperRefs$1, helperVars] =
2937
2888
  verticalAlignment,
2938
2889
  horizontalAlignment,
2939
2890
  shadowColor: '#00000020' //if we want to support transparency vars, we should use different color format
2940
- }, 'container');
2891
+ }, Container.componentName);
2941
2892
 
2942
2893
  const container = {
2943
2894
  ...helperTheme$1,
@@ -3158,7 +3109,7 @@ const vars$3 = Divider.cssVarList;
3158
3109
 
3159
3110
  const thickness = '2px';
3160
3111
  const textPaddingSize = '10px';
3161
- const [helperTheme, helperRefs] = createHelperVars({ thickness, textPaddingSize }, componentName$c);
3112
+ const [helperTheme, helperRefs] = createHelperVars({ thickness, textPaddingSize }, componentName$d);
3162
3113
 
3163
3114
 
3164
3115
  const divider = {
@@ -3422,6 +3373,10 @@ const comboBox = {
3422
3373
  // [vars.overlayBorder]: '3px solid red',
3423
3374
  };
3424
3375
 
3376
+ Image.cssVarList;
3377
+
3378
+ const image = {};
3379
+
3425
3380
  var components = {
3426
3381
  button,
3427
3382
  textField: textField$1,
@@ -3439,7 +3394,8 @@ var components = {
3439
3394
  passcode,
3440
3395
  loaderRadial,
3441
3396
  loaderLinear,
3442
- comboBox
3397
+ comboBox,
3398
+ image
3443
3399
  };
3444
3400
 
3445
3401
  var index = { globals, components };