@descope/web-components-ui 1.0.69 → 1.0.71
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.
- package/dist/index.esm.js +290 -385
- package/dist/index.esm.js.map +1 -1
- package/dist/umd/809.js +1 -0
- package/dist/umd/descope-button-index-js.js +1 -1
- package/dist/umd/descope-checkbox-index-js.js +1 -1
- package/dist/umd/descope-combo-box-index-js.js +1 -1
- package/dist/umd/descope-container-index-js.js +1 -1
- package/dist/umd/descope-date-picker-index-js.js +1 -1
- package/dist/umd/descope-divider-index-js.js +1 -1
- package/dist/umd/descope-email-field-index-js.js +1 -1
- package/dist/umd/descope-link-index-js.js +1 -1
- package/dist/umd/descope-loader-linear-index-js.js +1 -1
- package/dist/umd/descope-loader-radial-index-js.js +1 -1
- package/dist/umd/descope-logo-index-js.js +1 -1
- package/dist/umd/descope-number-field-index-js.js +1 -1
- package/dist/umd/descope-passcode-descope-passcode-internal-index-js.js +1 -1
- package/dist/umd/descope-passcode-index-js.js +1 -1
- package/dist/umd/descope-password-field-index-js.js +1 -1
- package/dist/umd/descope-switch-toggle-index-js.js +1 -1
- package/dist/umd/descope-text-area-index-js.js +1 -1
- package/dist/umd/descope-text-field-index-js.js +1 -1
- package/dist/umd/descope-text-index-js.js +1 -1
- package/dist/umd/index.js +1 -1
- package/package.json +1 -1
- package/src/baseClasses/createBaseClass.js +29 -2
- package/src/baseClasses/createBaseInputClass.js +9 -0
- package/src/components/descope-passcode/Passcode.js +1 -1
- package/src/components/descope-passcode/descope-passcode-internal/PasscodeInternal.js +67 -51
- package/src/helpers/mixinsHelpers.js +26 -9
- package/src/mixins/changeMixin.js +13 -39
- package/src/mixins/componentNameValidationMixin.js +2 -4
- package/src/mixins/createProxy.js +45 -53
- package/src/mixins/createStyleMixin/index.js +2 -0
- package/src/mixins/focusMixin.js +20 -125
- package/src/mixins/hoverableMixin.js +10 -16
- package/src/mixins/index.js +1 -0
- package/src/mixins/inputValidationMixin.js +10 -30
- package/src/mixins/normalizeBooleanAttributesMixin.js +11 -0
- package/src/mixins/proxyInputMixin.js +51 -58
- package/dist/umd/135.js +0 -1
- 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,6 +420,41 @@ const draggableMixin = (superclass) =>
|
|
420
420
|
}
|
421
421
|
};
|
422
422
|
|
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);
|
429
|
+
|
430
|
+
this[`on${eventName}`]?.(event); // in case we got an event callback as property
|
431
|
+
this.dispatchEvent(event);
|
432
|
+
}
|
433
|
+
|
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);
|
439
|
+
|
440
|
+
this.addEventListener('connected', () => {
|
441
|
+
clearTimeout(timerId);
|
442
|
+
}, { once: true });
|
443
|
+
|
444
|
+
const targetEle = element || this;
|
445
|
+
const boundCallback = callback.bind(this);
|
446
|
+
|
447
|
+
const onDisconnect = () => {
|
448
|
+
targetEle.removeEventListener(event, boundCallback);
|
449
|
+
};
|
450
|
+
|
451
|
+
this.addEventListener('disconnected', onDisconnect, { once: true });
|
452
|
+
|
453
|
+
targetEle.addEventListener(event, boundCallback, options);
|
454
|
+
|
455
|
+
return onDisconnect
|
456
|
+
}
|
457
|
+
|
423
458
|
const createBaseClass = ({ componentName, baseSelector = '' }) => {
|
424
459
|
class DescopeBaseClass extends HTMLElement {
|
425
460
|
static get componentName() {
|
@@ -427,10 +462,17 @@ const createBaseClass = ({ componentName, baseSelector = '' }) => {
|
|
427
462
|
}
|
428
463
|
|
429
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
|
430
471
|
get baseSelector() {
|
431
472
|
return baseSelector
|
432
473
|
}
|
433
474
|
|
475
|
+
// this is the base element, which returned by querying the base selector
|
434
476
|
get baseElement() {
|
435
477
|
this.#baseElement ??= this.baseSelector ?
|
436
478
|
this.rootElement.querySelector(this.baseSelector) :
|
@@ -439,12 +481,31 @@ const createBaseClass = ({ componentName, baseSelector = '' }) => {
|
|
439
481
|
return this.#baseElement
|
440
482
|
}
|
441
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
|
442
486
|
get rootElement() {
|
443
487
|
return this.shadowRoot || this
|
444
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
|
+
}
|
445
502
|
}
|
446
503
|
|
447
|
-
return compose(
|
504
|
+
return compose(
|
505
|
+
componentNameValidationMixin,
|
506
|
+
hoverableMixin,
|
507
|
+
normalizeBooleanAttributesMixin
|
508
|
+
)(DescopeBaseClass)
|
448
509
|
};
|
449
510
|
|
450
511
|
const createProxy = ({
|
@@ -456,87 +517,69 @@ const createProxy = ({
|
|
456
517
|
includeAttrsSync = [],
|
457
518
|
includeForwardProps = []
|
458
519
|
}) => {
|
459
|
-
const template = `
|
460
|
-
<style id="create-proxy"></style>
|
461
|
-
<${wrappedEleName}>
|
462
|
-
<slot></slot>
|
463
|
-
${slots.map((slot) => `<slot name="${slot}" slot="${slot}"></slot>`).join('')}
|
464
|
-
</${wrappedEleName}>
|
465
|
-
`;
|
466
|
-
|
467
520
|
class ProxyClass extends createBaseClass({ componentName, baseSelector: wrappedEleName }) {
|
468
|
-
|
469
|
-
|
470
|
-
this.hostElement = this.shadowRoot.host;
|
471
|
-
this.shadowRoot.getElementById('create-proxy').innerHTML =
|
472
|
-
isFunction(style) ? style() : style;
|
473
|
-
}
|
521
|
+
#dispatchBlur = createDispatchEvent.bind(this, 'blur')
|
522
|
+
#dispatchFocus = createDispatchEvent.bind(this, 'focus')
|
474
523
|
|
475
|
-
|
476
|
-
|
477
|
-
|
478
|
-
|
479
|
-
|
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
|
+
`;
|
480
533
|
}
|
481
534
|
|
482
|
-
focus = this
|
535
|
+
focus = () => this.baseElement.focus()
|
483
536
|
|
484
537
|
connectedCallback() {
|
485
|
-
|
486
|
-
this.proxyElement = this.shadowRoot.querySelector(wrappedEleName);
|
487
|
-
|
488
|
-
this.addEventListener('focus', this.#boundOnFocus);
|
489
|
-
|
490
|
-
// this is needed for components that uses props, such as combo box
|
491
|
-
forwardProps(this.hostElement, this.proxyElement, includeForwardProps);
|
492
|
-
|
493
|
-
// `onkeydown` is set on `proxyElement` support proper tab-index navigation
|
494
|
-
// this support is needed since both proxy host and element catch `focus`/`blur` event
|
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
|
498
|
-
this.proxyElement.onkeydown = (e) => {
|
499
|
-
if (e.shiftKey && e.keyCode === 9 && this.getRootNode() === document) {
|
500
|
-
this.removeAttribute('tabindex');
|
501
|
-
// We want to defer the action of setting the tab index back
|
502
|
-
// so it will happen after focusing the previous element
|
503
|
-
setTimeout(() => this.setAttribute('tabindex', '0'), 0);
|
504
|
-
}
|
505
|
-
};
|
538
|
+
super.connectedCallback?.();
|
506
539
|
|
507
|
-
|
508
|
-
|
509
|
-
|
510
|
-
});
|
511
|
-
}
|
512
|
-
}
|
540
|
+
createEventListener.call(this, 'blur', (e) => {
|
541
|
+
this.#dispatchBlur();
|
542
|
+
}, { element: this.baseElement });
|
513
543
|
|
514
|
-
|
515
|
-
|
516
|
-
|
517
|
-
}
|
518
|
-
}
|
544
|
+
createEventListener.call(this, 'focus', (e) => {
|
545
|
+
this.#dispatchFocus();
|
546
|
+
}, { element: this.baseElement });
|
519
547
|
|
520
|
-
|
521
|
-
|
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
|
+
};
|
522
571
|
|
523
|
-
this.
|
572
|
+
syncAttrs(this.baseElement, this, {
|
573
|
+
excludeAttrs: excludeAttrsSync,
|
574
|
+
includeAttrs: includeAttrsSync
|
575
|
+
});
|
524
576
|
}
|
525
577
|
}
|
526
578
|
|
527
579
|
return ProxyClass;
|
528
580
|
};
|
529
581
|
|
530
|
-
|
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
|
-
}
|
538
|
-
|
539
|
-
const observedAttributes = [
|
582
|
+
const observedAttributes$1 = [
|
540
583
|
'required',
|
541
584
|
'pattern',
|
542
585
|
];
|
@@ -549,7 +592,7 @@ const inputValidationMixin = (superclass) => class InputValidationMixinClass ext
|
|
549
592
|
static get observedAttributes() {
|
550
593
|
return [
|
551
594
|
...superclass.observedAttributes || [],
|
552
|
-
...observedAttributes
|
595
|
+
...observedAttributes$1
|
553
596
|
];
|
554
597
|
}
|
555
598
|
|
@@ -557,19 +600,12 @@ const inputValidationMixin = (superclass) => class InputValidationMixinClass ext
|
|
557
600
|
return true;
|
558
601
|
}
|
559
602
|
|
560
|
-
#dispatchValid = createDispatchEvent.bind(this, 'valid')
|
561
|
-
#dispatchInvalid = createDispatchEvent.bind(this, 'invalid')
|
562
|
-
|
563
603
|
#internals
|
564
604
|
|
565
|
-
#boundedHandleInput
|
566
|
-
|
567
605
|
constructor() {
|
568
606
|
super();
|
569
607
|
|
570
608
|
this.#internals = this.attachInternals();
|
571
|
-
|
572
|
-
this.#boundedHandleInput = this.#handleInput.bind(this);
|
573
609
|
}
|
574
610
|
|
575
611
|
get defaultErrorMsgValueMissing() {
|
@@ -595,7 +631,7 @@ const inputValidationMixin = (superclass) => class InputValidationMixinClass ext
|
|
595
631
|
}
|
596
632
|
}
|
597
633
|
|
598
|
-
setValidity() {
|
634
|
+
#setValidity() {
|
599
635
|
const validity = this.getValidity();
|
600
636
|
this.#internals.setValidity(validity, this.getErrorMessage(validity));
|
601
637
|
}
|
@@ -625,7 +661,7 @@ const inputValidationMixin = (superclass) => class InputValidationMixinClass ext
|
|
625
661
|
this.#internals.setValidity({ customError: true }, errorMessage);
|
626
662
|
} else {
|
627
663
|
this.#internals.setValidity({});
|
628
|
-
this
|
664
|
+
this.#setValidity();
|
629
665
|
}
|
630
666
|
}
|
631
667
|
|
@@ -637,38 +673,25 @@ const inputValidationMixin = (superclass) => class InputValidationMixinClass ext
|
|
637
673
|
return this.getAttribute('pattern')
|
638
674
|
}
|
639
675
|
|
640
|
-
|
641
|
-
this.
|
642
|
-
this.handleDispatchValidationEvents();
|
676
|
+
get form() {
|
677
|
+
return this.#internals.form
|
643
678
|
}
|
644
679
|
|
645
680
|
attributeChangedCallback(attrName, oldValue, newValue) {
|
646
681
|
super.attributeChangedCallback?.(attrName, oldValue, newValue);
|
647
682
|
|
648
|
-
if (observedAttributes.includes(attrName)) {
|
649
|
-
this
|
650
|
-
}
|
651
|
-
}
|
652
|
-
|
653
|
-
handleDispatchValidationEvents() {
|
654
|
-
if (this.checkValidity()) {
|
655
|
-
this.#dispatchValid();
|
656
|
-
} else {
|
657
|
-
this.#dispatchInvalid();
|
683
|
+
if (observedAttributes$1.includes(attrName)) {
|
684
|
+
this.#setValidity();
|
658
685
|
}
|
659
686
|
}
|
660
687
|
|
661
688
|
connectedCallback() {
|
662
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);
|
663
693
|
|
664
|
-
this
|
665
|
-
|
666
|
-
this.setValidity();
|
667
|
-
this.handleDispatchValidationEvents();
|
668
|
-
}
|
669
|
-
|
670
|
-
disconnectedCallback() {
|
671
|
-
this.removeEventListener('input', this.#boundedHandleInput);
|
694
|
+
this.#setValidity();
|
672
695
|
}
|
673
696
|
};
|
674
697
|
|
@@ -708,26 +731,17 @@ const proxyInputMixin = (superclass) =>
|
|
708
731
|
|
709
732
|
#inputElement
|
710
733
|
|
711
|
-
#
|
712
|
-
#boundHandleInvalid
|
713
|
-
#boundHandleValid
|
734
|
+
#dispatchChange = createDispatchEvent.bind(this, 'change')
|
714
735
|
|
715
736
|
constructor() {
|
716
737
|
super();
|
717
738
|
|
718
739
|
this.#inputElement = super.inputElement;
|
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
|
-
|
726
740
|
}
|
727
741
|
|
728
742
|
get inputElement() {
|
729
|
-
const inputSlot = this.
|
730
|
-
const textAreaSlot = this.
|
743
|
+
const inputSlot = this.baseElement.shadowRoot.querySelector('slot[name="input"]');
|
744
|
+
const textAreaSlot = this.baseElement.shadowRoot.querySelector('slot[name="textarea"]');
|
731
745
|
|
732
746
|
this.#inputElement ??= getNestedInput(inputSlot) || getNestedInput(textAreaSlot);
|
733
747
|
|
@@ -746,7 +760,6 @@ const proxyInputMixin = (superclass) =>
|
|
746
760
|
|
747
761
|
reportValidityOnInternalInput() {
|
748
762
|
setTimeout(() => {
|
749
|
-
this.baseEle.focus(); //TODO: check if this is needed
|
750
763
|
this.inputElement.reportValidity();
|
751
764
|
});
|
752
765
|
}
|
@@ -759,80 +772,82 @@ const proxyInputMixin = (superclass) =>
|
|
759
772
|
}
|
760
773
|
}
|
761
774
|
|
762
|
-
|
763
|
-
if (!this.checkValidity()) {
|
775
|
+
handleInternalInputErrorMessage() {
|
776
|
+
if (!this.inputElement.checkValidity()) {
|
764
777
|
this.inputElement.setCustomValidity(this.validationMessage);
|
765
778
|
}
|
766
779
|
}
|
767
780
|
|
768
|
-
|
769
|
-
|
770
|
-
#handleFocus(e) {
|
771
|
-
if (e.relatedTarget?.form) {
|
772
|
-
if (!this.checkValidity()) {
|
773
|
-
this.setAttribute('invalid', 'true');
|
774
|
-
}
|
775
|
-
|
776
|
-
if (this.hasAttribute('invalid')) {
|
777
|
-
this.reportValidityOnInternalInput();
|
778
|
-
}
|
779
|
-
}
|
780
|
-
}
|
781
|
-
|
782
|
-
#handleInvalid() {
|
783
|
-
this.setInternalInputErrorMessage();
|
781
|
+
#handleErrorMessage() {
|
782
|
+
this.handleInternalInputErrorMessage();
|
784
783
|
this.setAttribute('error-message', this.validationMessage);
|
785
784
|
}
|
786
785
|
|
787
|
-
#handleValid() {
|
788
|
-
this.removeAttribute('invalid');
|
789
|
-
}
|
790
|
-
|
791
786
|
connectedCallback() {
|
792
787
|
super.connectedCallback?.();
|
793
788
|
|
794
|
-
|
795
|
-
|
796
|
-
|
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);
|
797
800
|
|
798
|
-
|
799
|
-
|
801
|
+
this.#handleErrorMessage();
|
802
|
+
}
|
803
|
+
}, { element: this.inputElement });
|
804
|
+
|
805
|
+
createEventListener.call(this, 'change', () => {
|
806
|
+
this.#dispatchChange();
|
807
|
+
}, { element: this.baseElement });
|
800
808
|
|
801
|
-
|
802
|
-
this.
|
803
|
-
|
804
|
-
this
|
809
|
+
createEventListener.call(this, 'blur', () => {
|
810
|
+
if (!this.checkValidity()) {
|
811
|
+
this.setAttribute('invalid', 'true');
|
812
|
+
this.#handleErrorMessage();
|
813
|
+
}
|
805
814
|
});
|
806
815
|
|
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
|
+
}
|
828
|
+
});
|
829
|
+
|
830
|
+
createEventListener.call(this, 'invalid', this.#handleErrorMessage);
|
831
|
+
|
832
|
+
this.handleInternalInputErrorMessage();
|
833
|
+
|
807
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
|
808
836
|
if (!this.hasAttribute('tabindex') && this.getRootNode() === document) {
|
809
837
|
this.setAttribute('tabindex', 0);
|
810
838
|
}
|
811
839
|
|
812
840
|
// sync properties
|
813
841
|
propertyObserver(this, this.inputElement, 'value');
|
842
|
+
propertyObserver(this, this.inputElement, 'selectionStart');
|
814
843
|
this.setSelectionRange = this.inputElement.setSelectionRange?.bind(this.inputElement);
|
815
844
|
}
|
816
|
-
|
817
|
-
disconnectedCallback() {
|
818
|
-
this.removeEventListener('focus', this.#boundHandleFocus);
|
819
|
-
this.removeEventListener('invalid', this.#boundHandleInvalid);
|
820
|
-
this.removeEventListener('valid', this.#boundHandleValid);
|
821
|
-
}
|
822
|
-
|
823
|
-
attributeChangedCallback(attrName, oldValue, newValue) {
|
824
|
-
super.attributeChangedCallback?.(attrName, oldValue, newValue);
|
825
|
-
|
826
|
-
if (attrName === 'invalid' && newValue === '') {
|
827
|
-
this.setAttribute('invalid', 'true');
|
828
|
-
}
|
829
|
-
}
|
830
845
|
};
|
831
846
|
|
832
847
|
const componentNameValidationMixin = (superclass) =>
|
833
848
|
class ComponentNameValidationMixinClass extends superclass {
|
834
849
|
#checkComponentName() {
|
835
|
-
const currentComponentName = this.
|
850
|
+
const currentComponentName = this.localName;
|
836
851
|
|
837
852
|
if (!superclass.componentName) {
|
838
853
|
throw Error(
|
@@ -849,163 +864,48 @@ const componentNameValidationMixin = (superclass) =>
|
|
849
864
|
|
850
865
|
connectedCallback() {
|
851
866
|
super.connectedCallback?.();
|
852
|
-
|
853
|
-
this.#checkComponentName();
|
854
|
-
}
|
867
|
+
this.#checkComponentName();
|
855
868
|
}
|
856
869
|
};
|
857
870
|
|
858
871
|
const hoverableMixin =
|
859
872
|
(superclass) =>
|
860
873
|
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
874
|
connectedCallback() {
|
873
875
|
super.connectedCallback?.();
|
874
876
|
|
875
|
-
|
876
|
-
this.
|
877
|
-
|
878
|
-
|
879
|
-
|
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 });
|
880
885
|
}
|
881
886
|
};
|
882
887
|
|
883
|
-
const events = [
|
884
|
-
'blur',
|
885
|
-
'focus',
|
886
|
-
'focusin',
|
887
|
-
'focusout',
|
888
|
-
];
|
889
|
-
|
890
888
|
const focusMixin = (superclass) => class FocusMixinClass extends superclass {
|
891
|
-
|
892
|
-
|
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
|
-
|
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
|
-
|
892
|
+
super.connectedCallback?.();
|
893
|
+
|
894
|
+
createEventListener.call(this, 'blur', (e) => {
|
895
|
+
e.isTrusted && e.stopImmediatePropagation();
|
896
|
+
});
|
997
897
|
|
998
|
-
|
999
|
-
|
1000
|
-
|
1001
|
-
this.addEventListener('focusout', this.#boundedHandleFocusOut);
|
1002
|
-
}
|
898
|
+
createEventListener.call(this, 'focus', (e) => {
|
899
|
+
e.isTrusted && e.stopImmediatePropagation();
|
900
|
+
});
|
1003
901
|
|
1004
|
-
|
1005
|
-
|
1006
|
-
|
1007
|
-
|
1008
|
-
|
902
|
+
createEventListener.call(this, 'focusout', (e) => {
|
903
|
+
e.isTrusted && e.stopImmediatePropagation();
|
904
|
+
});
|
905
|
+
|
906
|
+
createEventListener.call(this, 'focusin', (e) => {
|
907
|
+
e.isTrusted && e.stopImmediatePropagation();
|
908
|
+
});
|
1009
909
|
}
|
1010
910
|
};
|
1011
911
|
|
@@ -1071,48 +971,34 @@ 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
|
-
|
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
|
-
|
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
|
-
|
1101
|
-
|
1102
|
-
|
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
|
-
|
1106
|
-
|
1107
|
-
|
1108
|
-
|
1109
|
-
|
1110
|
-
|
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
|
-
|
1114
|
-
this.removeEventListener('change', this.#boundedHandleChange);
|
1115
|
-
this.removeEventListener('blur', this.#boundedHandleBlur);
|
1001
|
+
super.attributeChangedCallback?.(attrName, oldValue, newValue);
|
1116
1002
|
}
|
1117
1003
|
};
|
1118
1004
|
|
@@ -1829,7 +1715,11 @@ overrides$4 = `
|
|
1829
1715
|
|
1830
1716
|
customElements.define(componentName$7, NumberField);
|
1831
1717
|
|
1832
|
-
|
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();
|
@@ -1847,23 +1737,26 @@ const getSanitizedCharacters = (str) => {
|
|
1847
1737
|
|
1848
1738
|
const componentName$6 = getComponentName('passcode-internal');
|
1849
1739
|
|
1740
|
+
const observedAttributes = [
|
1741
|
+
'disabled',
|
1742
|
+
'bordered',
|
1743
|
+
'size',
|
1744
|
+
'invalid'
|
1745
|
+
];
|
1746
|
+
|
1747
|
+
const BaseInputClass = createBaseInputClass({ componentName: componentName$6, baseSelector: ':host > div' });
|
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.concat(BaseInputClass.observedAttributes || []);
|
1858
1752
|
}
|
1859
1753
|
|
1860
1754
|
static get componentName() {
|
1861
1755
|
return componentName$6;
|
1862
1756
|
}
|
1863
1757
|
|
1864
|
-
#
|
1865
|
-
#
|
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="
|
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
|
-
|
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.
|
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,9 +1917,14 @@ 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 (
|
2022
|
-
this.inputs.forEach(
|
1922
|
+
if (observedAttributes.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
|
}
|
@@ -2112,7 +2017,7 @@ const customMixin = (superclass) =>
|
|
2112
2017
|
></${componentName$6}>
|
2113
2018
|
`;
|
2114
2019
|
|
2115
|
-
this.
|
2020
|
+
this.baseElement.appendChild(template.content.cloneNode(true));
|
2116
2021
|
|
2117
2022
|
this.inputElement = this.shadowRoot.querySelector(componentName$6);
|
2118
2023
|
|