@descope/web-components-ui 1.0.67 → 1.0.69

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 (54) hide show
  1. package/dist/cjs/index.cjs.js.map +1 -1
  2. package/dist/index.esm.js +510 -395
  3. package/dist/index.esm.js.map +1 -1
  4. package/dist/umd/135.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-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/BaseInputClass.js +3 -2
  27. package/src/baseClasses/createBaseClass.js +29 -0
  28. package/src/components/descope-combo-box/ComboBox.js +18 -4
  29. package/src/components/descope-container/Container.js +17 -25
  30. package/src/components/descope-divider/Divider.js +32 -40
  31. package/src/components/descope-link/Link.js +8 -17
  32. package/src/components/descope-loader-linear/LoaderLinear.js +24 -29
  33. package/src/components/descope-loader-radial/LoaderRadial.js +18 -26
  34. package/src/components/descope-logo/Logo.js +4 -12
  35. package/src/components/descope-passcode/Passcode.js +4 -11
  36. package/src/components/descope-passcode/descope-passcode-internal/PasscodeInternal.js +41 -53
  37. package/src/components/descope-text/Text.js +4 -12
  38. package/src/helpers/index.js +2 -0
  39. package/src/helpers/mixinsHelpers.js +18 -0
  40. package/src/mixins/changeMixin.js +47 -0
  41. package/src/mixins/componentNameValidationMixin.js +1 -1
  42. package/src/mixins/createProxy.js +26 -28
  43. package/src/mixins/createStyleMixin/helpers.js +3 -3
  44. package/src/mixins/createStyleMixin/index.js +10 -9
  45. package/src/mixins/draggableMixin.js +1 -1
  46. package/src/mixins/focusMixin.js +130 -0
  47. package/src/mixins/hoverableMixin.js +14 -13
  48. package/src/mixins/index.js +3 -1
  49. package/src/mixins/{inputMixin.js → inputValidationMixin.js} +17 -58
  50. package/src/mixins/portalMixin.js +11 -7
  51. package/src/mixins/proxyInputMixin.js +50 -45
  52. package/src/theme/components/comboBox.js +2 -9
  53. package/dist/umd/860.js +0 -1
  54. package/src/baseClasses/DescopeBaseClass.js +0 -1
package/dist/index.esm.js CHANGED
@@ -24,6 +24,8 @@ const compose = (...fns) =>
24
24
 
25
25
  const upperFirst = (str) => str.charAt(0).toUpperCase() + str.slice(1);
26
26
 
27
+ const isFunction = (maybeFunc) => typeof maybeFunc === 'function';
28
+
27
29
  const DESCOPE_PREFIX = 'descope';
28
30
  const CSS_SELECTOR_SPECIFIER_MULTIPLY = 3;
29
31
  const BASE_THEME_SECTION = 'host';
@@ -175,7 +177,7 @@ const createCssSelector = (
175
177
  baseSelector = '',
176
178
  relativeSelectorOrSelectorFn = ''
177
179
  ) =>
178
- typeof relativeSelectorOrSelectorFn === 'function'
180
+ isFunction(relativeSelectorOrSelectorFn)
179
181
  ? relativeSelectorOrSelectorFn(baseSelector)
180
182
  : `${baseSelector}${/^[A-Za-z]/.test(relativeSelectorOrSelectorFn)
181
183
  ? ` ${relativeSelectorOrSelectorFn}`
@@ -232,7 +234,7 @@ const createStyle = (componentName, baseSelector, mappings) => {
232
234
  ({ selector: relativeSelectorOrSelectorFn, property }) => {
233
235
  style.add(
234
236
  createCssSelector(baseSelector, relativeSelectorOrSelectorFn),
235
- property,
237
+ isFunction(property) ? property() : property,
236
238
  createCssVarFallback(cssVarName)
237
239
  );
238
240
  }
@@ -315,7 +317,7 @@ const createStyleMixin =
315
317
  this.#onComponentThemeChange();
316
318
  }
317
319
 
318
- #createAttrOverrideStyle() {
320
+ #createOverridesStyle() {
319
321
  this.#overrideStyleEle = document.createElement('style');
320
322
  this.#overrideStyleEle.id = 'style-mixin-overrides';
321
323
 
@@ -325,7 +327,7 @@ const createStyleMixin =
325
327
  this.#rootElement.append(this.#overrideStyleEle);
326
328
  }
327
329
 
328
- #setAttrOverrideStyle(attrName, value) {
330
+ #setAttrOverride(attrName, value) {
329
331
  const style = this.#overrideStyleEle?.sheet?.cssRules[0].style;
330
332
  if (!style) return;
331
333
 
@@ -338,10 +340,10 @@ const createStyleMixin =
338
340
  else style?.removeProperty(varName);
339
341
  }
340
342
 
341
- #updateOverrideStyle(attrs = []) {
343
+ #updateOverridesStyle(attrs = []) {
342
344
  for (const attr of attrs) {
343
345
  if (this.#styleAttributes.includes(attr)) {
344
- this.#setAttrOverrideStyle(attr, this.getAttribute(attr));
346
+ this.#setAttrOverride(attr, this.getAttribute(attr));
345
347
  }
346
348
  }
347
349
 
@@ -349,7 +351,7 @@ const createStyleMixin =
349
351
  this.#overrideStyleEle.innerHTML = this.#overrideStyleEle?.sheet?.cssRules[0].cssText;
350
352
  }
351
353
 
352
- #createComponentStyle() {
354
+ #createMappingStyle() {
353
355
  const themeStyle = document.createElement('style');
354
356
  themeStyle.id = 'style-mixin-mappings';
355
357
  themeStyle.innerHTML = createStyle(
@@ -370,13 +372,14 @@ const createStyleMixin =
370
372
 
371
373
  this.#addClassName(superclass.componentName);
372
374
 
373
- this.#createComponentStyle();
375
+ // TODO: we should do everything we can on the constructor
376
+ // when dragging & dropping these styles are created over & over
377
+ this.#createMappingStyle();
374
378
  this.#createComponentTheme();
375
- this.#createAttrOverrideStyle();
379
+ this.#createOverridesStyle();
376
380
 
377
381
  // this is instead attributeChangedCallback because we cannot use static methods in this case
378
- observeAttributes(this, this.#updateOverrideStyle.bind(this), {});
379
-
382
+ observeAttributes(this, this.#updateOverridesStyle.bind(this), {});
380
383
  }
381
384
  }
382
385
 
@@ -398,7 +401,7 @@ const draggableMixin = (superclass) =>
398
401
  super();
399
402
 
400
403
  this.#styleEle = document.createElement('style');
401
- this.#styleEle.innerText = `${this.baseSelector} { cursor: inherit }`;
404
+ this.#styleEle.innerText = `* { cursor: inherit!important }`;
402
405
  }
403
406
 
404
407
  #handleDraggableStyle(isDraggable) {
@@ -417,31 +420,32 @@ const draggableMixin = (superclass) =>
417
420
  }
418
421
  };
419
422
 
420
- class DescopeBaseClass extends HTMLElement {}
423
+ const createBaseClass = ({ componentName, baseSelector = '' }) => {
424
+ class DescopeBaseClass extends HTMLElement {
425
+ static get componentName() {
426
+ return componentName;
427
+ }
421
428
 
422
- const hoverableMixin =
423
- (relativeSelector = '') =>
424
- (superclass) =>
425
- class HovrerableMixinClass extends superclass {
426
- connectedCallback() {
427
- super.connectedCallback?.();
429
+ #baseElement;
430
+ get baseSelector() {
431
+ return baseSelector
432
+ }
428
433
 
429
- const onMouseOver = (e) => {
430
- this.shadowRoot.host.setAttribute('hover', 'true');
431
- e.target.addEventListener(
432
- 'mouseleave',
433
- () => this.shadowRoot.host.removeAttribute('hover'),
434
- { once: true }
435
- );
436
- };
434
+ get baseElement() {
435
+ this.#baseElement ??= this.baseSelector ?
436
+ this.rootElement.querySelector(this.baseSelector) :
437
+ this;
437
438
 
438
- const baseElement = this.shadowRoot.querySelector(
439
- this.baseSelector + relativeSelector
440
- );
439
+ return this.#baseElement
440
+ }
441
441
 
442
- baseElement.addEventListener('mouseover', onMouseOver);
443
- }
444
- };
442
+ get rootElement() {
443
+ return this.shadowRoot || this
444
+ }
445
+ }
446
+
447
+ return compose(componentNameValidationMixin, hoverableMixin)(DescopeBaseClass)
448
+ };
445
449
 
446
450
  const createProxy = ({
447
451
  componentName,
@@ -460,38 +464,39 @@ const createProxy = ({
460
464
  </${wrappedEleName}>
461
465
  `;
462
466
 
463
- class ProxyElement extends DescopeBaseClass {
464
- static get componentName() {
465
- return componentName;
466
- }
467
-
467
+ class ProxyClass extends createBaseClass({ componentName, baseSelector: wrappedEleName }) {
468
468
  constructor() {
469
469
  super().attachShadow({ mode: 'open' }).innerHTML = template;
470
470
  this.hostElement = this.shadowRoot.host;
471
- this.baseSelector = wrappedEleName;
472
471
  this.shadowRoot.getElementById('create-proxy').innerHTML =
473
- typeof style === 'function' ? style() : style;
472
+ isFunction(style) ? style() : style;
474
473
  }
475
474
 
475
+ #boundOnFocus = this.#onFocus.bind(this);
476
+
477
+ // we want to focus on the proxy element when focusing our WCP
478
+ #onFocus() {
479
+ this.proxyElement.focus();
480
+ }
481
+
482
+ focus = this.#onFocus
483
+
476
484
  connectedCallback() {
477
485
  if (this.shadowRoot.isConnected) {
478
486
  this.proxyElement = this.shadowRoot.querySelector(wrappedEleName);
479
487
 
488
+ this.addEventListener('focus', this.#boundOnFocus);
489
+
480
490
  // this is needed for components that uses props, such as combo box
481
491
  forwardProps(this.hostElement, this.proxyElement, includeForwardProps);
482
492
 
483
- this.setAttribute('tabindex', '0');
484
-
485
- // we want to focus on the proxy element when focusing our WC
486
- this.addEventListener('focus', () => {
487
- this.proxyElement.focus();
488
- });
489
-
490
493
  // `onkeydown` is set on `proxyElement` support proper tab-index navigation
491
494
  // this support is needed since both proxy host and element catch `focus`/`blur` event
492
- // which causes faulty behaviour.
495
+ // which causes faulty behavior.
496
+ // we need this to happen only when the proxy component is in the light DOM,
497
+ // otherwise it will focus the nested proxy element
493
498
  this.proxyElement.onkeydown = (e) => {
494
- if (e.shiftKey && e.keyCode === 9) {
499
+ if (e.shiftKey && e.keyCode === 9 && this.getRootNode() === document) {
495
500
  this.removeAttribute('tabindex');
496
501
  // We want to defer the action of setting the tab index back
497
502
  // so it will happen after focusing the previous element
@@ -499,10 +504,6 @@ const createProxy = ({
499
504
  }
500
505
  };
501
506
 
502
- // sync events
503
- this.addEventListener = (...args) =>
504
- this.proxyElement.addEventListener(...args);
505
-
506
507
  syncAttrs(this.proxyElement, this.hostElement, {
507
508
  excludeAttrs: excludeAttrsSync,
508
509
  includeAttrs: includeAttrsSync
@@ -510,28 +511,30 @@ const createProxy = ({
510
511
  }
511
512
  }
512
513
 
513
- disconnectedCallback() {
514
- this.proxyElement.removeEventListener('mouseover', this.mouseoverCbRef);
515
- }
516
-
517
514
  attributeChangedCallback() {
518
515
  if (!this.proxyElement) {
519
516
  return;
520
517
  }
521
518
  }
519
+
520
+ disconnectedCallback() {
521
+ super.disconnectedCallback?.();
522
+
523
+ this.removeEventListener('focus', this.#boundOnFocus);
524
+ }
522
525
  }
523
526
 
524
- return compose(hoverableMixin())(ProxyElement);
527
+ return ProxyClass;
525
528
  };
526
529
 
527
- const events = [
528
- 'change',
529
- 'input',
530
- 'blur',
531
- 'focus',
532
- 'invalid',
533
- 'valid',
534
- ];
530
+ // move create event to here
531
+
532
+ // usage example:
533
+ // #dispatchSomething = createDispatchEvent.bind(this, 'something')
534
+ function createDispatchEvent(eventName) {
535
+ this[`on${eventName}`]?.(); // in case we got an event callback as property
536
+ this.dispatchEvent(new Event(eventName));
537
+ }
535
538
 
536
539
  const observedAttributes = [
537
540
  'required',
@@ -542,7 +545,7 @@ const errorAttributes = {
542
545
  valueMissing: 'data-errormessage-value-missing',
543
546
  patternMismatch: 'data-errormessage-pattern-mismatch'
544
547
  };
545
- const inputMixin = (superclass) => class InputMixinClass extends superclass {
548
+ const inputValidationMixin = (superclass) => class InputValidationMixinClass extends superclass {
546
549
  static get observedAttributes() {
547
550
  return [
548
551
  ...superclass.observedAttributes || [],
@@ -554,18 +557,19 @@ const inputMixin = (superclass) => class InputMixinClass extends superclass {
554
557
  return true;
555
558
  }
556
559
 
560
+ #dispatchValid = createDispatchEvent.bind(this, 'valid')
561
+ #dispatchInvalid = createDispatchEvent.bind(this, 'invalid')
562
+
557
563
  #internals
558
564
 
565
+ #boundedHandleInput
566
+
559
567
  constructor() {
560
568
  super();
561
569
 
562
570
  this.#internals = this.attachInternals();
563
571
 
564
- for (const event of events) {
565
- this[`dispatch${upperFirst(event)}`] = function () {
566
- this.dispatchInputEvent(event);
567
- };
568
- }
572
+ this.#boundedHandleInput = this.#handleInput.bind(this);
569
573
  }
570
574
 
571
575
  get defaultErrorMsgValueMissing() {
@@ -591,13 +595,7 @@ const inputMixin = (superclass) => class InputMixinClass extends superclass {
591
595
  }
592
596
  }
593
597
 
594
- get isReadOnly() {
595
- return this.hasAttribute('readonly') && this.getAttribute('readonly') !== 'false'
596
- }
597
-
598
598
  setValidity() {
599
- if (this.isReadOnly) return;
600
-
601
599
  const validity = this.getValidity();
602
600
  this.#internals.setValidity(validity, this.getErrorMessage(validity));
603
601
  }
@@ -607,7 +605,7 @@ const inputMixin = (superclass) => class InputMixinClass extends superclass {
607
605
  }
608
606
 
609
607
  getValidity() {
610
- throw Error('getValidity', 'is not implemented')
608
+ console.warn('getValidity', 'is not implemented');
611
609
  }
612
610
 
613
611
  checkValidity() {
@@ -639,36 +637,11 @@ const inputMixin = (superclass) => class InputMixinClass extends superclass {
639
637
  return this.getAttribute('pattern')
640
638
  }
641
639
 
642
- get value() {
643
- throw Error('get value', 'is not implemented')
644
- }
645
-
646
- set value(value) {
647
- throw Error('set value', 'is not implemented')
648
- }
649
-
650
- handleFocus() {
651
- throw Error('handleFocus', 'is not implemented')
652
- }
653
-
654
- handleInput() {
640
+ #handleInput() {
655
641
  this.setValidity();
656
642
  this.handleDispatchValidationEvents();
657
643
  }
658
644
 
659
- handleBlur() {
660
- throw Error('handleBlur', 'is not implemented')
661
- }
662
-
663
- handleChange() {
664
- throw Error('handleChange', 'is not implemented')
665
- }
666
-
667
- dispatchInputEvent(eventName) {
668
- this[`on${eventName}`]?.(); // in case we got an event callback as property
669
- this.dispatchEvent(new InputEvent(eventName));
670
- }
671
-
672
645
  attributeChangedCallback(attrName, oldValue, newValue) {
673
646
  super.attributeChangedCallback?.(attrName, oldValue, newValue);
674
647
 
@@ -679,25 +652,23 @@ const inputMixin = (superclass) => class InputMixinClass extends superclass {
679
652
 
680
653
  handleDispatchValidationEvents() {
681
654
  if (this.checkValidity()) {
682
- this.dispatchValid();
655
+ this.#dispatchValid();
683
656
  } else {
684
- this.dispatchInvalid();
657
+ this.#dispatchInvalid();
685
658
  }
686
659
  }
687
660
 
688
661
  connectedCallback() {
689
662
  super.connectedCallback?.();
690
663
 
664
+ this.addEventListener('input', this.#boundedHandleInput);
665
+
691
666
  this.setValidity();
692
667
  this.handleDispatchValidationEvents();
668
+ }
693
669
 
694
- // create proxy replace the addEventListener fn
695
- // and we want to be able to access the focus events
696
- // of this element and not the nested element's events
697
- this.onfocus = this.handleFocus.bind(this);
698
- this.addEventListener('input', this.handleInput.bind(this));
699
- this.addEventListener('blur', this.handleBlur.bind(this));
700
- this.addEventListener('change', this.handleBlur.bind(this));
670
+ disconnectedCallback() {
671
+ this.removeEventListener('input', this.#boundedHandleInput);
701
672
  }
702
673
  };
703
674
 
@@ -730,18 +701,28 @@ const getNestedInput = (ele) => {
730
701
  };
731
702
 
732
703
  const proxyInputMixin = (superclass) =>
733
- class proxyInputMixinClass extends inputMixin(superclass) {
704
+ class proxyInputMixinClass extends inputValidationMixin(superclass) {
734
705
  static get observedAttributes() {
735
706
  return [...superclass.observedAttributes || [], ...errorAttrs];
736
707
  }
737
708
 
738
709
  #inputElement
739
710
 
711
+ #boundHandleFocus
712
+ #boundHandleInvalid
713
+ #boundHandleValid
714
+
740
715
  constructor() {
741
716
  super();
742
717
 
743
718
  this.#inputElement = super.inputElement;
744
719
 
720
+ this.#boundHandleFocus = this.#handleFocus.bind(this);
721
+ this.#boundHandleInvalid = this.#handleInvalid.bind(this);
722
+ this.#boundHandleValid = this.#handleValid.bind(this);
723
+
724
+ this.baseEle = this.shadowRoot.querySelector(this.baseSelector);
725
+
745
726
  }
746
727
 
747
728
  get inputElement() {
@@ -749,7 +730,7 @@ const proxyInputMixin = (superclass) =>
749
730
  const textAreaSlot = this.baseEle.shadowRoot.querySelector('slot[name="textarea"]');
750
731
 
751
732
  this.#inputElement ??= getNestedInput(inputSlot) || getNestedInput(textAreaSlot);
752
-
733
+
753
734
  if (!this.#inputElement) throw Error('no input was found');
754
735
 
755
736
  return this.#inputElement
@@ -765,27 +746,17 @@ const proxyInputMixin = (superclass) =>
765
746
 
766
747
  reportValidityOnInternalInput() {
767
748
  setTimeout(() => {
749
+ this.baseEle.focus(); //TODO: check if this is needed
768
750
  this.inputElement.reportValidity();
769
- }, 0);
770
- }
771
-
772
- handleBlur() { }
773
-
774
- handleFocus() {
775
- this.inputElement.focus();
776
- if (this.hasAttribute('invalid')) {
777
- this.reportValidityOnInternalInput();
778
- }
751
+ });
779
752
  }
780
753
 
781
754
  // we want reportValidity to behave like form submission
782
755
  reportValidity() {
783
- const isValid = super.reportValidity();
784
756
  if (!isValid) {
785
757
  this.setAttribute('invalid', 'true');
786
- this.inputElement.focus();
758
+ this.reportValidityOnInternalInput();
787
759
  }
788
- this.reportValidityOnInternalInput();
789
760
  }
790
761
 
791
762
  setInternalInputErrorMessage() {
@@ -794,48 +765,47 @@ const proxyInputMixin = (superclass) =>
794
765
  }
795
766
  }
796
767
 
797
- connectedCallback() {
798
- this.baseEle = this.shadowRoot.querySelector(this.baseSelector);
799
-
800
- // this is our way to identify that the form was submitted
801
- // in this case, we want the input to be in error state if it's not valid
802
- this.addEventListener('focus', (e) => {
803
- if (e.relatedTarget?.form) {
804
- if (!this.checkValidity()) {
805
- this.setAttribute('invalid', 'true');
806
- }
768
+ // when clicking on the form submit button and the input is invalid
769
+ // we want it to appear as invalid
770
+ #handleFocus(e) {
771
+ if (e.relatedTarget?.form) {
772
+ if (!this.checkValidity()) {
773
+ this.setAttribute('invalid', 'true');
774
+ }
807
775
 
808
- if (this.hasAttribute('invalid')) {
809
- this.reportValidityOnInternalInput();
810
- }
776
+ if (this.hasAttribute('invalid')) {
777
+ this.reportValidityOnInternalInput();
811
778
  }
812
- });
779
+ }
780
+ }
813
781
 
814
- this.addEventListener('invalid', () => {
815
- this.setInternalInputErrorMessage();
816
- this.setAttribute('error-message', this.validationMessage);
817
- });
782
+ #handleInvalid() {
783
+ this.setInternalInputErrorMessage();
784
+ this.setAttribute('error-message', this.validationMessage);
785
+ }
818
786
 
819
- this.addEventListener('valid', () => {
820
- this.removeAttribute('invalid');
821
- });
787
+ #handleValid() {
788
+ this.removeAttribute('invalid');
789
+ }
822
790
 
791
+ connectedCallback() {
823
792
  super.connectedCallback?.();
824
793
 
825
- this.inputElement.addEventListener('input', () => {
794
+ // this is our way to identify that the form was submitted
795
+ // in this case, we want the input to be in error state if it's not valid
796
+ this.addEventListener('focus', this.#boundHandleFocus);
797
+
798
+ this.addEventListener('invalid', this.#boundHandleInvalid);
799
+ this.addEventListener('valid', this.#boundHandleValid);
800
+
801
+ this.addEventListener('input', () => {
826
802
  this.inputElement.setCustomValidity('');
827
803
  if (!this.inputElement.checkValidity())
828
804
  this.setInternalInputErrorMessage();
829
805
  });
830
806
 
831
- this.inputElement.addEventListener('invalid', () => {
832
- this.setValidity();
833
- this.setInternalInputErrorMessage();
834
- this.setAttribute('error-message', this.validationMessage);
835
- });
836
-
837
807
  // this is needed in order to make sure the form input validation is working
838
- if (!this.hasAttribute('tabindex')) {
808
+ if (!this.hasAttribute('tabindex') && this.getRootNode() === document) {
839
809
  this.setAttribute('tabindex', 0);
840
810
  }
841
811
 
@@ -844,6 +814,12 @@ const proxyInputMixin = (superclass) =>
844
814
  this.setSelectionRange = this.inputElement.setSelectionRange?.bind(this.inputElement);
845
815
  }
846
816
 
817
+ disconnectedCallback() {
818
+ this.removeEventListener('focus', this.#boundHandleFocus);
819
+ this.removeEventListener('invalid', this.#boundHandleInvalid);
820
+ this.removeEventListener('valid', this.#boundHandleValid);
821
+ }
822
+
847
823
  attributeChangedCallback(attrName, oldValue, newValue) {
848
824
  super.attributeChangedCallback?.(attrName, oldValue, newValue);
849
825
 
@@ -854,7 +830,7 @@ const proxyInputMixin = (superclass) =>
854
830
  };
855
831
 
856
832
  const componentNameValidationMixin = (superclass) =>
857
- class DraggableMixinClass extends superclass {
833
+ class ComponentNameValidationMixinClass extends superclass {
858
834
  #checkComponentName() {
859
835
  const currentComponentName = this.shadowRoot.host.tagName.toLowerCase();
860
836
 
@@ -879,10 +855,168 @@ const componentNameValidationMixin = (superclass) =>
879
855
  }
880
856
  };
881
857
 
882
- const sanitizeSelector = (selector) => selector.replace(/[^\w\s]/gi, '');
858
+ const hoverableMixin =
859
+ (superclass) =>
860
+ class HoverableMixinClass extends superclass {
861
+ #boundOnMouseOver = this.#onMouseOver.bind(this)
862
+
863
+ #onMouseOver(e) {
864
+ this.setAttribute('hover', 'true');
865
+ e.target.addEventListener(
866
+ 'mouseleave',
867
+ () => this.shadowRoot.host.removeAttribute('hover'),
868
+ { once: true }
869
+ );
870
+ }
871
+
872
+ connectedCallback() {
873
+ super.connectedCallback?.();
874
+
875
+ const baseElement = this.shadowRoot.querySelector(
876
+ this.baseSelector
877
+ );
878
+
879
+ baseElement.addEventListener('mouseover', this.#boundOnMouseOver);
880
+ }
881
+ };
882
+
883
+ const events = [
884
+ 'blur',
885
+ 'focus',
886
+ 'focusin',
887
+ 'focusout',
888
+ ];
883
889
 
884
- const appendSuffixToKeys = (obj, suffix) => Object.keys(obj).reduce((acc, key) =>
885
- Object.assign(acc, { [suffix + upperFirst(key)]: obj[key] }), {});
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
+
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
+ }
985
+
986
+ onFocus() {
987
+ console.warn('onFocus', 'is not implemented');
988
+ }
989
+
990
+ dispatchInputEvent(eventName) {
991
+ this[`on${eventName}`]?.(); // in case we got an event callback as property
992
+ this.dispatchEvent(new InputEvent(eventName));
993
+ }
994
+
995
+ connectedCallback() {
996
+ super.connectedCallback?.();
997
+
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
+ }
1003
+
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);
1009
+ }
1010
+ };
1011
+
1012
+ // this is needed because we might generate the same css vars names
1013
+ // e.g. "overlayColor" attribute in style mixin's mapping,
1014
+ // will generate the same var as "color" attribute in portals's mapping
1015
+ // when the portal name is "overlay".
1016
+ // because of that, we are adding this separator that is also used as a differentiator
1017
+ const DISPLAY_NAME_SEPARATOR = '_';
1018
+
1019
+ const sanitizeSelector = (selector) => selector.replace(/[^\w\s]/gi, '');
886
1020
 
887
1021
  const getDomNode = (maybeDomNode) => maybeDomNode.host || maybeDomNode;
888
1022
 
@@ -897,7 +1031,7 @@ const portalMixin = ({ name, selector, mappings = {} }) => (superclass) => {
897
1031
  static get cssVarList() {
898
1032
  return {
899
1033
  ...BaseClass.cssVarList,
900
- ...createCssVarsList(superclass.componentName, appendSuffixToKeys(mappings, eleDisplayName))
1034
+ [eleDisplayName]: createCssVarsList(kebabCaseJoin(superclass.componentName, DISPLAY_NAME_SEPARATOR + eleDisplayName), mappings)
901
1035
  };
902
1036
  }
903
1037
 
@@ -907,14 +1041,14 @@ const portalMixin = ({ name, selector, mappings = {} }) => (superclass) => {
907
1041
  // we cannot use "this" before calling "super"
908
1042
  const getRootElement = (that) => {
909
1043
  const baseEle = that.shadowRoot.querySelector(that.baseSelector);
910
- const portal = baseEle.shadowRoot.querySelector(selector);
1044
+ const portal = selector ? baseEle.shadowRoot.querySelector(selector) : baseEle;
911
1045
 
912
1046
  return portal.shadowRoot || portal
913
1047
  };
914
1048
 
915
1049
  super({
916
1050
  getRootElement,
917
- componentNameSuffix: eleDisplayName,
1051
+ componentNameSuffix: DISPLAY_NAME_SEPARATOR + eleDisplayName,
918
1052
  themeSection: PORTAL_THEME_PREFIX + eleDisplayName,
919
1053
  baseSelector: ':host'
920
1054
  });
@@ -936,6 +1070,52 @@ const portalMixin = ({ name, selector, mappings = {} }) => (superclass) => {
936
1070
  }
937
1071
  };
938
1072
 
1073
+ const changeMixin = (superclass) => class ChangeMixinClass extends superclass {
1074
+
1075
+ #boundedHandleChange
1076
+ #boundedHandleBlur
1077
+
1078
+ #removeChangeListener
1079
+ #removeBlurListener
1080
+
1081
+ #dispatchChange = createDispatchEvent.bind(this, 'change')
1082
+
1083
+ constructor() {
1084
+ super();
1085
+
1086
+ this.#boundedHandleChange = this.#handleChange.bind(this);
1087
+ this.#boundedHandleBlur = this.#handleBlur.bind(this);
1088
+ }
1089
+
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
1096
+ e.stopPropagation();
1097
+ }
1098
+ }
1099
+
1100
+ #handleBlur() {
1101
+ // on blur, we want to dispatch a change event
1102
+ this.#dispatchChange();
1103
+ }
1104
+
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
+ }
1112
+
1113
+ disconnectedCallback() {
1114
+ this.removeEventListener('change', this.#boundedHandleChange);
1115
+ this.removeEventListener('blur', this.#boundedHandleBlur);
1116
+ }
1117
+ };
1118
+
939
1119
  const componentName$i = getComponentName('button');
940
1120
 
941
1121
  const editorOverrides = `vaadin-button::part(label) { pointer-events: none; }`;
@@ -1063,41 +1243,36 @@ customElements.define(componentName$h, Checkbox);
1063
1243
 
1064
1244
  const componentName$g = getComponentName('loader-linear');
1065
1245
 
1066
- class RawLoaderLinear extends DescopeBaseClass {
1246
+ class RawLoaderLinear extends createBaseClass({ componentName: componentName$g, baseSelector: ':host > div' }) {
1067
1247
  static get componentName() {
1068
1248
  return componentName$g;
1069
1249
  }
1070
1250
  constructor() {
1071
1251
  super();
1072
- const template = document.createElement('template');
1073
- template.innerHTML = `
1074
- <style>
1075
- @keyframes tilt {
1076
- 0% { transform: translateX(0); }
1077
- 50% { transform: translateX(400%); }
1078
- }
1079
- :host {
1080
- position: relative;
1081
- display: inline-block
1082
- }
1083
- div::after {
1084
- content: '';
1085
- animation-name: tilt;
1086
- position: absolute;
1087
- left: 0;
1088
- }
1089
-
1090
- :host > div {
1091
- width: 100%;
1092
- }
1093
- </style>
1094
- <div></div>
1095
- `;
1096
1252
 
1097
- this.attachShadow({ mode: 'open' });
1098
- this.shadowRoot.appendChild(template.content.cloneNode(true));
1253
+ this.attachShadow({ mode: 'open' }).innerHTML = `
1254
+ <style>
1255
+ @keyframes tilt {
1256
+ 0% { transform: translateX(0); }
1257
+ 50% { transform: translateX(400%); }
1258
+ }
1259
+ :host {
1260
+ position: relative;
1261
+ display: inline-block
1262
+ }
1263
+ div::after {
1264
+ content: '';
1265
+ animation-name: tilt;
1266
+ position: absolute;
1267
+ left: 0;
1268
+ }
1099
1269
 
1100
- this.baseSelector = ':host > div';
1270
+ :host > div {
1271
+ width: 100%;
1272
+ }
1273
+ </style>
1274
+ <div></div>
1275
+ `;
1101
1276
  }
1102
1277
  }
1103
1278
 
@@ -1132,34 +1307,26 @@ customElements.define(componentName$g, LoaderLinear);
1132
1307
 
1133
1308
  const componentName$f = getComponentName('loader-radial');
1134
1309
 
1135
- class RawLoaderRadial extends DescopeBaseClass {
1136
- static get componentName() {
1137
- return componentName$f;
1138
- }
1310
+ class RawLoaderRadial extends createBaseClass({ componentName: componentName$f, baseSelector: ':host > div' }) {
1139
1311
  constructor() {
1140
1312
  super();
1141
- const template = document.createElement('template');
1142
- template.innerHTML = `
1143
- <style>
1144
- @keyframes spin {
1145
- 0% { transform: rotate(0deg); }
1146
- 100% { transform: rotate(360deg); }
1147
- }
1148
- :host {
1149
- position: relative;
1150
- display: inline-flex;
1151
- }
1152
- :host > div {
1153
- animation-name: spin;
1154
- }
1155
- </style>
1156
- <div></div>
1157
- `;
1158
1313
 
1159
- this.attachShadow({ mode: 'open' });
1160
- this.shadowRoot.appendChild(template.content.cloneNode(true));
1161
-
1162
- this.baseSelector = ':host > div';
1314
+ this.attachShadow({ mode: 'open' }).innerHTML = `
1315
+ <style>
1316
+ @keyframes spin {
1317
+ 0% { transform: rotate(0deg); }
1318
+ 100% { transform: rotate(360deg); }
1319
+ }
1320
+ :host {
1321
+ position: relative;
1322
+ display: inline-flex;
1323
+ }
1324
+ :host > div {
1325
+ animation-name: spin;
1326
+ }
1327
+ </style>
1328
+ <div></div>
1329
+ `;
1163
1330
  }
1164
1331
  }
1165
1332
 
@@ -1190,33 +1357,25 @@ customElements.define(componentName$f, LoaderRadial);
1190
1357
 
1191
1358
  const componentName$e = getComponentName('container');
1192
1359
 
1193
- class RawContainer extends DescopeBaseClass {
1194
- static get componentName() {
1195
- return componentName$e;
1196
- }
1360
+ class RawContainer extends createBaseClass({componentName: componentName$e, baseSelector: ':host > slot'}) {
1197
1361
  constructor() {
1198
1362
  super();
1199
- const template = document.createElement('template');
1200
- template.innerHTML = `
1201
- <style>
1202
- :host > slot {
1203
- box-sizing: border-box;
1204
- width: 100%;
1205
- height: 100%;
1206
- display: flex;
1207
- overflow: auto;
1208
- }
1209
- :host {
1210
- display: inline-block;
1211
- }
1212
- </style>
1213
- <slot></slot>
1214
- `;
1215
-
1216
- this.attachShadow({ mode: 'open' });
1217
- this.shadowRoot.appendChild(template.content.cloneNode(true));
1218
1363
 
1219
- this.baseSelector = ':host > slot';
1364
+ this.attachShadow({ mode: 'open' }).innerHTML = `
1365
+ <style>
1366
+ :host > slot {
1367
+ box-sizing: border-box;
1368
+ width: 100%;
1369
+ height: 100%;
1370
+ display: flex;
1371
+ overflow: auto;
1372
+ }
1373
+ :host {
1374
+ display: inline-block;
1375
+ }
1376
+ </style>
1377
+ <slot></slot>
1378
+ `;
1220
1379
  }
1221
1380
  }
1222
1381
 
@@ -1273,51 +1432,43 @@ const DatePicker = compose(
1273
1432
  customElements.define(componentName$d, DatePicker);
1274
1433
 
1275
1434
  const componentName$c = getComponentName('divider');
1276
- class RawDivider extends DescopeBaseClass {
1277
- static get componentName() {
1278
- return componentName$c;
1279
- }
1435
+ class RawDivider extends createBaseClass({ componentName: componentName$c, baseSelector: ':host > div' }) {
1280
1436
  constructor() {
1281
1437
  super();
1282
1438
 
1283
- const template = document.createElement('template');
1284
- template.innerHTML = `
1285
- <style>
1286
- :host > div {
1287
- display: flex;
1288
- height: 100%;
1289
- width: 100%;
1290
- }
1291
- :host > div::before,
1292
- :host > div::after {
1293
- content: '';
1294
- flex-grow: 1;
1295
- }
1296
-
1297
- descope-text {
1298
- flex-grow: 0;
1299
- flex-shrink: 0;
1300
- }
1439
+ this.attachShadow({ mode: 'open' }).innerHTML = `
1440
+ <style>
1441
+ :host > div {
1442
+ display: flex;
1443
+ height: 100%;
1444
+ width: 100%;
1445
+ }
1446
+ :host > div::before,
1447
+ :host > div::after {
1448
+ content: '';
1449
+ flex-grow: 1;
1450
+ }
1301
1451
 
1302
- :host(:empty) descope-text {
1303
- display: none;
1304
- }
1452
+ descope-text {
1453
+ flex-grow: 0;
1454
+ flex-shrink: 0;
1455
+ }
1305
1456
 
1306
- :host([vertical="true"]) div {
1307
- width: fit-content;
1308
- }
1457
+ :host(:empty) descope-text {
1458
+ display: none;
1459
+ }
1309
1460
 
1310
- </style>
1311
- <div>
1312
- <descope-text>
1313
- <slot></slot>
1314
- </descope-text>
1315
- </div>
1316
- `;
1317
- this.attachShadow({ mode: 'open' });
1318
- this.shadowRoot.appendChild(template.content.cloneNode(true));
1461
+ :host([vertical="true"]) div {
1462
+ width: fit-content;
1463
+ }
1319
1464
 
1320
- this.baseSelector = ':host > div';
1465
+ </style>
1466
+ <div>
1467
+ <descope-text>
1468
+ <slot></slot>
1469
+ </descope-text>
1470
+ </div>
1471
+ `;
1321
1472
 
1322
1473
  this.textComponent = this.shadowRoot.querySelector('descope-text');
1323
1474
 
@@ -1345,7 +1496,7 @@ const Divider = compose(
1345
1496
  alignItems: root,
1346
1497
  alignSelf: root,
1347
1498
  flexDirection: root,
1348
- textPadding: {...text$2, property: 'padding'},
1499
+ textPadding: { ...text$2, property: 'padding' },
1349
1500
  width: host$1,
1350
1501
  padding: host$1,
1351
1502
  backgroundColor: [before, after],
@@ -1361,14 +1512,11 @@ const Divider = compose(
1361
1512
 
1362
1513
  const componentName$b = getComponentName('text');
1363
1514
 
1364
- class RawText extends DescopeBaseClass {
1365
- static get componentName() {
1366
- return componentName$b;
1367
- }
1515
+ class RawText extends createBaseClass({ componentName: componentName$b, baseSelector: ':host > slot' }) {
1368
1516
  constructor() {
1369
1517
  super();
1370
- const template = document.createElement('template');
1371
- template.innerHTML = `
1518
+
1519
+ this.attachShadow({ mode: 'open' }).innerHTML = `
1372
1520
  <style>
1373
1521
  :host {
1374
1522
  display: inline-block;
@@ -1380,11 +1528,6 @@ class RawText extends DescopeBaseClass {
1380
1528
  </style>
1381
1529
  <slot></slot>
1382
1530
  `;
1383
-
1384
- this.attachShadow({ mode: 'open' });
1385
- this.shadowRoot.appendChild(template.content.cloneNode(true));
1386
-
1387
- this.baseSelector = ':host > slot';
1388
1531
  }
1389
1532
  }
1390
1533
 
@@ -1516,15 +1659,12 @@ overrides$5 = `
1516
1659
  customElements.define(componentName$a, EmailField);
1517
1660
 
1518
1661
  const componentName$9 = getComponentName('link');
1519
- class RawLink extends DescopeBaseClass {
1520
- static get componentName() {
1521
- return componentName$9;
1522
- }
1662
+ class RawLink extends createBaseClass({ componentName: componentName$9, baseSelector: ':host a' }) {
1523
1663
  constructor() {
1524
1664
  super();
1525
- const template = document.createElement('template');
1665
+ document.createElement('template');
1526
1666
 
1527
- template.innerHTML = `
1667
+ this.attachShadow({ mode: 'open' }).innerHTML = `
1528
1668
  <style>
1529
1669
  :host {
1530
1670
  display: inline-block;
@@ -1542,9 +1682,6 @@ class RawLink extends DescopeBaseClass {
1542
1682
  </div>
1543
1683
  `;
1544
1684
 
1545
- this.attachShadow({ mode: 'open' });
1546
- this.shadowRoot.appendChild(template.content.cloneNode(true));
1547
-
1548
1685
  forwardAttrs(this, this.shadowRoot.querySelector('a'), {
1549
1686
  includeAttrs: ['href', 'target', 'tooltip'],
1550
1687
  mapAttrs: {
@@ -1555,24 +1692,23 @@ class RawLink extends DescopeBaseClass {
1555
1692
  forwardAttrs(this, this.shadowRoot.querySelector('descope-text'), {
1556
1693
  includeAttrs: ['mode', 'variant'],
1557
1694
  });
1558
-
1559
- this.baseSelector = ':host > div';
1560
1695
  }
1561
1696
  }
1562
1697
 
1563
1698
  const selectors$2 = {
1564
1699
  host: { selector: () => 'host' },
1565
- anchor: { selector: '> a' },
1566
- text: { selector: Text.componentName }
1700
+ anchor: {},
1701
+ wrapper: {selector: () => ':host > div'},
1702
+ text: { selector: () => Text.componentName }
1567
1703
  };
1568
1704
 
1569
- const { anchor, text: text$1, host } = selectors$2;
1705
+ const { anchor, text: text$1, host, wrapper } = selectors$2;
1570
1706
 
1571
1707
  const Link = compose(
1572
1708
  createStyleMixin({
1573
1709
  mappings: {
1574
1710
  width: host,
1575
- textAlign: {},
1711
+ textAlign: wrapper,
1576
1712
  color: [anchor, { ...text$1, property: Text.cssVarList.color }],
1577
1713
  cursor: anchor,
1578
1714
  borderBottomWidth: anchor,
@@ -1580,7 +1716,6 @@ const Link = compose(
1580
1716
  borderBottomColor: anchor
1581
1717
  },
1582
1718
  }),
1583
- hoverableMixin(anchor.selector),
1584
1719
  draggableMixin,
1585
1720
  componentNameValidationMixin
1586
1721
  )(RawLink);
@@ -1592,23 +1727,15 @@ const componentName$8 = getComponentName('logo');
1592
1727
  let style;
1593
1728
  const getStyle = () => style;
1594
1729
 
1595
- class RawLogo extends DescopeBaseClass {
1596
- static get componentName() {
1597
- return componentName$8;
1598
- }
1730
+ class RawLogo extends createBaseClass({ componentName: componentName$8, baseSelector: ':host > div' }) {
1599
1731
  constructor() {
1600
1732
  super();
1601
- const template = document.createElement('template');
1602
- template.innerHTML = `
1733
+
1734
+ this.attachShadow({ mode: 'open' }).innerHTML = `
1603
1735
  <style>
1604
1736
  ${getStyle()}
1605
1737
  </style>
1606
1738
  <div></div>`;
1607
-
1608
- this.attachShadow({ mode: 'open' });
1609
- this.shadowRoot.appendChild(template.content.cloneNode(true));
1610
-
1611
- this.baseSelector = ':host > div';
1612
1739
  }
1613
1740
  }
1614
1741
 
@@ -1702,7 +1829,7 @@ overrides$4 = `
1702
1829
 
1703
1830
  customElements.define(componentName$7, NumberField);
1704
1831
 
1705
- var BaseInputClass = inputMixin(HTMLElement); //todo: maybe we should use base class?
1832
+ var BaseInputClass = compose(focusMixin, inputValidationMixin, changeMixin)(HTMLElement); //todo: maybe we should use base class?
1706
1833
 
1707
1834
  const focusElement = (ele) => {
1708
1835
  ele?.focus();
@@ -1723,11 +1850,10 @@ const componentName$6 = getComponentName('passcode-internal');
1723
1850
  class PasscodeInternal extends BaseInputClass {
1724
1851
  static get observedAttributes() {
1725
1852
  return [
1726
- ...BaseInputClass.observedAttributes,
1853
+ ...(BaseInputClass.observedAttributes || []),
1727
1854
  'disabled',
1728
1855
  'bordered',
1729
1856
  'size',
1730
- 'readonly'
1731
1857
  ];
1732
1858
  }
1733
1859
 
@@ -1735,10 +1861,12 @@ class PasscodeInternal extends BaseInputClass {
1735
1861
  return componentName$6;
1736
1862
  }
1737
1863
 
1864
+ #boundHandleInvalid = this.#handleInvalid.bind(this)
1865
+ #boundHandleValid = this.#handleValid.bind(this)
1866
+ #boundHandleBlur = this.#handleBlur.bind(this)
1867
+
1738
1868
  constructor() {
1739
1869
  super();
1740
- const template = document.createElement('template');
1741
-
1742
1870
  const inputs = [...Array(this.digits).keys()].map((idx) => `
1743
1871
  <descope-text-field
1744
1872
  st-width="35px"
@@ -1748,14 +1876,12 @@ class PasscodeInternal extends BaseInputClass {
1748
1876
  ></descope-text-field>
1749
1877
  `);
1750
1878
 
1751
- template.innerHTML = `
1879
+ this.innerHTML = `
1752
1880
  <div>
1753
1881
  ${inputs.join('')}
1754
1882
  </div>
1755
1883
  `;
1756
1884
 
1757
- this.appendChild(template.content.cloneNode(true));
1758
-
1759
1885
  this.baseSelector = ':host > div';
1760
1886
 
1761
1887
  this.inputs = Array.from(this.querySelectorAll('descope-text-field'));
@@ -1783,15 +1909,13 @@ class PasscodeInternal extends BaseInputClass {
1783
1909
  return `^$|^\\d{${this.digits},}$`
1784
1910
  }
1785
1911
 
1786
- handleInputsInvalid() {
1787
- setTimeout(() => {
1788
- if (this.hasAttribute('invalid')) {
1789
- this.inputs.forEach(input => input.setAttribute('invalid', 'true'));
1790
- }
1791
- });
1912
+ #handleInvalid() {
1913
+ if (this.hasAttribute('invalid')) {
1914
+ this.inputs.forEach(input => input.setAttribute('invalid', 'true'));
1915
+ }
1792
1916
  }
1793
1917
 
1794
- handleInputsValid() {
1918
+ #handleValid() {
1795
1919
  this.inputs.forEach(input => input.removeAttribute('invalid'));
1796
1920
  }
1797
1921
 
@@ -1807,17 +1931,25 @@ class PasscodeInternal extends BaseInputClass {
1807
1931
  }
1808
1932
  };
1809
1933
 
1810
- handleFocus() {
1934
+ onFocus(){
1811
1935
  this.inputs[0].focus();
1812
1936
  }
1813
1937
 
1814
- async connectedCallback() {
1815
- super.connectedCallback();
1938
+ connectedCallback() {
1939
+ super.connectedCallback?.();
1816
1940
 
1817
1941
  this.initInputs();
1818
1942
 
1819
- this.addEventListener('invalid', this.handleInputsInvalid);
1820
- this.addEventListener('valid', this.handleInputsValid);
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);
1821
1953
  }
1822
1954
 
1823
1955
  getInputIdx(inputEle) {
@@ -1841,66 +1973,49 @@ class PasscodeInternal extends BaseInputClass {
1841
1973
  currentInput.value = charArr[i] ?? '';
1842
1974
 
1843
1975
  const nextInput = this.getNextInput(currentInput);
1976
+
1844
1977
  if (nextInput === currentInput) break;
1845
1978
  currentInput = nextInput;
1846
1979
  }
1847
1980
 
1848
- !currentInput.hasAttribute('focused') && focusElement(currentInput);
1981
+ focusElement(currentInput);
1849
1982
  };
1850
1983
 
1851
- handleBlur() {
1852
- this.handleInputsInvalid();
1984
+ #handleBlur() {
1985
+ this.#handleInvalid();
1853
1986
  }
1854
1987
 
1855
1988
  initInputs() {
1856
1989
  this.inputs.forEach((input) => {
1857
-
1858
- // in order to simulate blur on the input
1859
- // we are checking if focus on one of the digits happened immediately after blur on another digit
1860
- // if not, the component is no longer focused and we should simulate blur
1861
- input.addEventListener('blur', (e) => {
1862
- e.stopPropagation();
1863
- const timerId = setTimeout(() => {
1864
- this.dispatchBlur();
1865
- });
1866
-
1867
- this.inputs.forEach((ele) =>
1868
- ele.addEventListener('focus', () => clearTimeout(timerId), { once: true })
1869
- );
1870
- });
1871
-
1872
1990
  input.oninput = (e) => {
1873
- e.stopPropagation();
1874
1991
  const charArr = getSanitizedCharacters(input.value);
1875
1992
 
1876
- if (!charArr.length) input.value = ''; // if we got an invalid value we want to clear the input
1993
+ if (!charArr.length) {
1994
+ // if we got an invalid value we want to clear the input
1995
+ 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
+ }
1877
2001
  else this.fillDigits(charArr, input);
1878
-
1879
- this.dispatchInput();
1880
2002
  };
1881
2003
 
1882
2004
  input.onkeydown = ({ key }) => {
2005
+ // when user deletes a digit, we want to focus the previous digit
1883
2006
  if (key === 'Backspace') {
1884
- input.value = '';
1885
-
1886
- // if the user deleted the digit we want to focus the previous digit
1887
- const prevInput = this.getPrevInput(input);
1888
-
1889
- !prevInput.hasAttribute('focused') && setTimeout(() => {
1890
- focusElement(prevInput);
2007
+ setTimeout(() => {
2008
+ focusElement(this.getPrevInput(input));
1891
2009
  });
1892
-
1893
- this.dispatchInput();
1894
2010
  } else if (key.match(/^(\d)$/g)) { // if input is a digit
1895
2011
  input.value = ''; // we are clearing the previous value so we can override it with the new value
1896
2012
  }
1897
-
1898
2013
  };
1899
2014
  });
1900
2015
  }
1901
2016
 
1902
2017
  attributeChangedCallback(attrName, oldValue, newValue) {
1903
- super.attributeChangedCallback(attrName, oldValue, newValue);
2018
+ super.attributeChangedCallback?.(attrName, oldValue, newValue);
1904
2019
 
1905
2020
  if (oldValue !== newValue) {
1906
2021
  if (PasscodeInternal.observedAttributes.includes(attrName) && !BaseInputClass.observedAttributes.includes(attrName)) {
@@ -1999,21 +2114,10 @@ const customMixin = (superclass) =>
1999
2114
 
2000
2115
  this.proxyElement.appendChild(template.content.cloneNode(true));
2001
2116
 
2002
- // we want to control when the element is out of focus
2003
- // so the validations will be triggered blur event is dispatched from descope-passcode internal (and not every time focusing a digit)
2004
- this.proxyElement._setFocused = () => { };
2005
-
2006
2117
  this.inputElement = this.shadowRoot.querySelector(componentName$6);
2007
2118
 
2008
2119
  forwardAttrs(this.shadowRoot.host, this.inputElement, { includeAttrs: ['required', 'pattern'] });
2009
2120
 
2010
- // we want to trigger validation only when dispatching a blur event from the descope-passcode-internal
2011
- this.inputElement.addEventListener('blur', (e) => {
2012
- // we do not want native blur events, only the ones that we are sending
2013
- if (!e.isTrusted){
2014
- this.proxyElement.validate();
2015
- }
2016
- });
2017
2121
  }
2018
2122
  };
2019
2123
 
@@ -2050,6 +2154,10 @@ const Passcode = compose(
2050
2154
  display: inline-block;
2051
2155
  }
2052
2156
 
2157
+ :host([readonly]) descope-passcode-internal > div {
2158
+ pointer-events: none;
2159
+ }
2160
+
2053
2161
  descope-passcode-internal {
2054
2162
  -webkit-mask-image: none;
2055
2163
  display: flex;
@@ -3231,9 +3339,21 @@ const ComboBoxMixin = (superclass) => class ComboBoxMixinClass extends superclas
3231
3339
  };
3232
3340
  }
3233
3341
 
3342
+ // the default vaadin behavior is to attach the overlay to the body when opened
3343
+ // we do not want that because it's difficult to style the overlay in this way
3344
+ // so we override it to open inside the shadow DOM
3345
+ #overrideOverlaySettings() {
3346
+ const overlay = this.baseElement.shadowRoot.querySelector('vaadin-combo-box-overlay');
3347
+
3348
+ overlay._attachOverlay = function () { this.bringToFront(); };
3349
+ overlay._detachOverlay = function () { };
3350
+ overlay._enterModalState = function () { };
3351
+ }
3352
+
3234
3353
  connectedCallback() {
3235
3354
  super.connectedCallback?.();
3236
3355
 
3356
+ this.#overrideOverlaySettings();
3237
3357
  observeChildren(this, this.#onChildrenChange.bind(this));
3238
3358
  }
3239
3359
  };
@@ -3251,15 +3371,17 @@ const ComboBox = compose(
3251
3371
  borderWidth: input,
3252
3372
  cursor: toggle,
3253
3373
  height: input,
3374
+ // overlayBackground: { property: () => ComboBox.cssVarList.overlay.backgroundColor },
3375
+ // overlayBorder: { property: () => ComboBox.cssVarList.overlay.border }
3254
3376
  }
3255
3377
  }),
3256
3378
  draggableMixin,
3257
3379
  portalMixin({
3258
3380
  name: 'overlay',
3259
- selector: 'vaadin-combo-box-scroller',
3381
+ selector: '',
3260
3382
  mappings: {
3261
- border: { selector: () => '::slotted(*)' },
3262
- backgroundColor: {},
3383
+ // border: { selector: 'vaadin-combo-box-scroller' },
3384
+ // backgroundColor: { selector: 'vaadin-combo-box-item' },
3263
3385
  }
3264
3386
  }),
3265
3387
  proxyInputMixin,
@@ -3296,15 +3418,8 @@ const comboBox = {
3296
3418
  [vars.borderWidth]: '0',
3297
3419
  [vars.cursor]: 'pointer',
3298
3420
  [vars.padding]: '0',
3299
-
3300
- '@overlay': {
3301
- [vars.overlayBackgroundColor] : 'red',
3302
- [vars.overlayBorder]: '3px solid blue',
3303
-
3304
- _hover: {
3305
- [vars.overlayBackgroundColor] : 'blue',
3306
- }
3307
- }
3421
+ // [vars.overlayBackground]: 'blue',
3422
+ // [vars.overlayBorder]: '3px solid red',
3308
3423
  };
3309
3424
 
3310
3425
  var components = {