@descope/web-components-ui 1.0.50 → 1.0.52
Sign up to get free protection for your applications and to get access to all the features.
- package/dist/index.esm.js +571 -267
- package/dist/index.esm.js.map +1 -1
- package/dist/umd/433.js +1 -1
- package/dist/umd/descope-combo-index-js.js +1 -1
- package/dist/umd/descope-passcode-descope-passcode-internal-index-js.js +1 -0
- package/dist/umd/descope-passcode-index-js.js +1 -0
- package/dist/umd/descope-text-field-index-js.js +1 -1
- package/dist/umd/descope-text-index-js.js +1 -0
- package/dist/umd/index.js +1 -1
- package/package.json +1 -1
- package/src/components/descope-container/Container.js +1 -1
- package/src/components/descope-passcode/Passcode.js +141 -0
- package/src/components/descope-passcode/descope-passcode-internal/PasscodeInternal.js +213 -0
- package/src/components/descope-passcode/descope-passcode-internal/helpers.js +14 -0
- package/src/components/descope-passcode/descope-passcode-internal/index.js +3 -0
- package/src/components/descope-passcode/index.js +5 -0
- package/src/components/descope-text/Text.js +46 -0
- package/src/components/descope-text/index.js +5 -0
- package/src/componentsHelpers/createProxy/index.js +3 -4
- package/src/componentsHelpers/createStyleMixin/index.js +103 -72
- package/src/componentsHelpers/inputMixin.js +13 -13
- package/src/index.js +1 -0
- package/src/theme/components/index.js +5 -1
- package/src/theme/components/passcode.js +8 -0
- package/src/theme/components/text.js +79 -0
- package/src/theme/globals.js +49 -21
@@ -1 +1 @@
|
|
1
|
-
"use strict";(self.webpackChunkDescopeUI=self.webpackChunkDescopeUI||[]).push([[247],{2798:(e,t,o)=>{o.r(t),o.d(t,{Combo:()=>d});var n=o(9433);o(3029),o(
|
1
|
+
"use strict";(self.webpackChunkDescopeUI=self.webpackChunkDescopeUI||[]).push([[247],{2798:(e,t,o)=>{o.r(t),o.d(t,{Combo:()=>d});var n=o(9433);o(3029),o(9357);const c=document.createElement("template"),s=(0,n.iY)("combo");c.innerHTML="\n <descope-button></descope-button>\n <descope-text-field></descope-text-field>\n";class d extends HTMLElement{constructor(){super(),this.attachShadow({mode:"open"}).appendChild(c.content.cloneNode(!0))}}customElements.define(s,d)}}]);
|
@@ -0,0 +1 @@
|
|
1
|
+
"use strict";(self.webpackChunkDescopeUI=self.webpackChunkDescopeUI||[]).push([[841],{7361:(t,e,i)=>{i.d(e,{f:()=>r,Z:()=>u});var s=i(9433);const n=t=>{t?.focus(),t?.setSelectionRange(1,1)},r=(0,s.iY)("passcode-internal");class a extends HTMLElement{static get observedAttributes(){return["disabled","bordered","size"]}static get componentName(){return r}static get formAssociated(){return!0}#t;constructor(){super();const t=document.createElement("template"),e=[...Array(this.digits).keys()].map((t=>`\n\t\t<descope-text-field\n\t\t\tst-width="35px"\n\t\t\tdata-id=${t}\n\t\t\ttype="tel"\n\t\t\tautocomplete="none"\n\t\t></descope-text-field>\n\t`));t.innerHTML=`\n\t\t<div>\n\t\t\t${e.join("")}\n\t\t</div>\n\t\t`,this.appendChild(t.content.cloneNode(!0)),this.baseSelector=":host > div",this.#t=this.attachInternals(),this.inputs=Array.from(this.querySelectorAll("descope-text-field"))}checkValidity(){return this.#t.validity.valid?this.inputs.forEach((t=>t.removeAttribute("has-error-message"))):(this.inputs.forEach((t=>t.setAttribute("has-error-message","true"))),this.oninvalid?.()),this.#t.validity.valid}get digits(){return Number.parseInt(this.getAttribute("digits"))||6}get value(){return this.inputs.map((({value:t})=>t)).join("")}set value(t){}get isRequired(){return this.hasAttribute("required")&&"false"!==this.getAttribute("required")}get pattern(){return`^$|^\\d{${this.digits},}$`}get valueMissingErrMsg(){return"Please fill out this field."}get patternMismatchErrMsg(){return`Must be a ${this.digits} digits number.`}get validity(){return this.#t.validity}get validationMessage(){return this.#t.validationMessage}reportValidity(){this.#t.reportValidity()}formAssociatedCallback(){this.setValidity?.()}setValidity=()=>{this.isRequired&&!this.value?this.#t.setValidity({valueMissing:!0},this.valueMissingErrMsg):this.pattern&&!new RegExp(this.pattern).test(this.value)?this.#t.setValidity({patternMismatch:!0},this.patternMismatchErrMsg):this.#t.setValidity({})};async connectedCallback(){this.setValidity(),this.initInputs(),this.onfocus=()=>{this.inputs[0].focus()}}getInputIdx(t){return Number.parseInt(t.getAttribute("data-id"),10)}getNextInput(t){const e=this.getInputIdx(t),i=Math.min(e+1,this.inputs.length-1);return this.inputs[i]}getPrevInput(t){const e=this.getInputIdx(t),i=Math.max(e-1,0);return this.inputs[i]}fillDigits(t,e){for(let i=0;i<t.length;i+=1){e.value=t[i]??"";const s=this.getNextInput(e);if(s===e)break;e=s}!e.hasAttribute("focused")&&n(e)}initInputs(){this.inputs.forEach((t=>{t.addEventListener("blur",(()=>{const t=setTimeout((()=>{this.dispatchEvent(new Event("blur"))}));this.inputs.forEach((e=>e.addEventListener("focus",(()=>clearTimeout(t)),{once:!0})))})),t.oninput=e=>{const i=(t=>{const e=t.replace(/\s/g,"");return e.match(/^\d+$/)?[...e]:[]})(t.value);i.length?this.fillDigits(i,t):t.value="",this.setValidity()},t.onkeydown=({key:e})=>{if("Backspace"===e){t.value="";const e=this.getPrevInput(t);!e.hasAttribute("focused")&&setTimeout((()=>{n(e)}))}else e.match(/^(\d)$/g)&&(t.value="");this.setValidity()}}))}attributeChangedCallback(t,e,i){e!==i&&a.observedAttributes.includes(t)&&this.inputs.forEach((e=>e.setAttribute(t,i)))}}const u=a},4775:(t,e,i)=>{i.r(e);var s=i(7361);customElements.define(s.f,s.Z)}}]);
|
@@ -0,0 +1 @@
|
|
1
|
+
"use strict";(self.webpackChunkDescopeUI=self.webpackChunkDescopeUI||[]).push([[939],{7102:(t,e,n)=>{n.r(e);var i=n(9433),a=n(4447),r=n(9063),o=n(7361);const d=(0,i.iY)("passcode"),{borderStyle:s,borderWidth:l,...p}=a.Z,c=(0,i.qC)((0,i.yk)({mappings:{...p},nestedMappings:{borderColor:{selector:` ${r.Z.componentName}`,property:r.Z.cssVarList.borderColor}}}),i.e4,i.y7,i.Ae,(t=>class extends t{constructor(){super()}get digits(){return Number.parseInt(this.getAttribute("digits"))||6}connectedCallback(){super.connectedCallback?.();const t=document.createElement("template");t.innerHTML=`\n <${o.f} \n bordered="true" \n name="code" \n tabindex="0"\n slot="input"\n required="${this.shadowRoot.host.getAttribute("required")}"\n pattern="${this.shadowRoot.host.getAttribute("pattern")}"\n ></${o.f}>\n `;const e=Object.assign(document.createElement("slot"),{name:"input",slot:"input",part:"input"});this.proxyElement.appendChild(e),this.proxyElement._setFocused=()=>{},this.shadowRoot.host.appendChild(t.content.cloneNode(!0)),this.inputElement=this.querySelector(o.f),this.inputElement.addEventListener("blur",(()=>{this.proxyElement.validate()}))}}))((0,i.DM)({slots:[],wrappedEleName:"vaadin-text-field",style:()=>`\n :host {\n --vaadin-field-default-width: auto;\n }\n\n ::slotted([slot='input']) {\n -webkit-mask-image: none;\n display: flex;\n gap: 2px;\n align-items: center;\n padding: 0;\n }\n\n vaadin-text-field::part(input-field) {\n background-color: transparent;\n padding: 0;\n }\n\n ${u}\n `,excludeAttrsSync:["tabindex"],componentName:d})),u=`\n\t:host {\n\t\tdisplay: inline-block;\n\t}\n\n\tvaadin-text-field {\n\t\tmargin: 0;\n\t\tpadding: 0;\n\t}\n\tvaadin-text-field::part(input-field) {\n\t\toverflow: hidden;\n\t}\n\tvaadin-text-field[readonly] > input:placeholder-shown {\n\t\topacity: 1;\n\t}\n\n\tvaadin-text-field > label,\n\tvaadin-text-field::part(input-field) {\n\t\tcursor: pointer;\n\t\tcolor: var(${c.cssVarList.color});\n\t}\n\tvaadin-text-field::part(input-field):focus {\n\t\tcursor: text;\n\t}\n\tvaadin-text-field[required]::part(required-indicator)::after {\n\t\tfont-size: "12px";\n\t\tcontent: "*";\n\t\tcolor: var(${c.cssVarList.color});\n\t}\n\tvaadin-text-field[readonly]::part(input-field)::after {\n\t\tborder: 0 solid;\n\t}\n`,f=c;n(9357),n(4775),customElements.define(d,f)}}]);
|
@@ -1 +1 @@
|
|
1
|
-
"use strict";(self.webpackChunkDescopeUI=self.webpackChunkDescopeUI||[]).push([[934],{
|
1
|
+
"use strict";(self.webpackChunkDescopeUI=self.webpackChunkDescopeUI||[]).push([[934],{9063:(t,e,n)=>{n.d(e,{Z:()=>r,f:()=>l});var i=n(9433),a=n(4447);const l=(0,i.iY)("text-field");let d="";const o=(0,i.qC)((0,i.yk)({mappings:a.Z}),i.e4,i.y7,i.Ae)((0,i.DM)({slots:["prefix","suffix"],wrappedEleName:"vaadin-text-field",style:()=>d,excludeAttrsSync:["tabindex"],componentName:l}));d=`\n\t:host {\n\t\tdisplay: inline-block;\n\t}\n\n\tvaadin-text-field {\n\t\tmargin: 0;\n\t\tpadding: 0;\n\t}\n\tvaadin-text-field::part(input-field) {\n\t\toverflow: hidden;\n\t}\n\tvaadin-text-field[readonly] > input:placeholder-shown {\n\t\topacity: 1;\n\t}\n\tvaadin-text-field input:-webkit-autofill,\n\tvaadin-text-field input:-webkit-autofill::first-line,\n\tvaadin-text-field input:-webkit-autofill:hover,\n\tvaadin-text-field input:-webkit-autofill:active,\n\tvaadin-text-field input:-webkit-autofill:focus {\n\t\t-webkit-text-fill-color: var(${o.cssVarList.color});\n\t\tbox-shadow: 0 0 0 var(${o.cssVarList.height}) var(${o.cssVarList.backgroundColor}) inset;\n\t}\n\tvaadin-text-field > label,\n\tvaadin-text-field::part(input-field) {\n\t\tcursor: pointer;\n\t\tcolor: var(${o.cssVarList.color});\n\t}\n\tvaadin-text-field::part(input-field):focus {\n\t\tcursor: text;\n\t}\n\tvaadin-text-field[required]::part(required-indicator)::after {\n\t\tfont-size: "12px";\n\t\tcontent: "*";\n\t\tcolor: var(${o.cssVarList.color});\n\t}\n\tvaadin-text-field[readonly]::part(input-field)::after {\n\t\tborder: 0 solid;\n\t}\n`;const r=o},9357:(t,e,n)=>{n.r(e),n.d(e,{TextField:()=>i.Z}),n(9789);var i=n(9063);customElements.define(i.f,i.Z)}}]);
|
@@ -0,0 +1 @@
|
|
1
|
+
"use strict";(self.webpackChunkDescopeUI=self.webpackChunkDescopeUI||[]).push([[528],{2092:(e,t,o)=>{o.r(t),o.d(t,{Text:()=>i});var n=o(9433),s=o(3535);const c=(0,n.iY)("text");class l extends HTMLElement{static get componentName(){return c}constructor(){super();const e=document.createElement("template");e.innerHTML="<slot></slot>",this.attachShadow({mode:"open"}),this.shadowRoot.appendChild(e.content.cloneNode(!0)),this.baseSelector=":host > slot"}}const i=(0,n.qC)((0,n.yk)({mappings:{fontFamily:{},lineHeight:{},fontStyle:{},fontSize:{},fontWeight:{},width:{},color:{},textAlign:(0,s.zy)(),display:(0,s.zy)()}}),n.e4,n.Ae)(l);customElements.define(c,i)}}]);
|
package/dist/umd/index.js
CHANGED
@@ -1 +1 @@
|
|
1
|
-
!function(e,
|
1
|
+
!function(e,o){"object"==typeof exports&&"object"==typeof module?module.exports=o():"function"==typeof define&&define.amd?define([],o):"object"==typeof exports?exports.DescopeUI=o():e.DescopeUI=o()}(self,(()=>(()=>{var e,o,t,r={534:(e,o,t)=>{var r={"./descope-button/index.js":[3029,840,767,211,513,729,433,662],"./descope-checkbox/index.js":[7904,840,786,208,767,515,433,761],"./descope-combo/index.js":[2798,840,786,208,233,422,767,725,211,789,513,729,433,447,934,662,247],"./descope-container/index.js":[147,433,317],"./descope-date-picker/index.js":[2552,840,786,208,233,767,211,513,54,433,17],"./descope-email-field/index.js":[689,840,786,208,233,422,725,437,433,447,760],"./descope-logo/index.js":[4641,433,984],"./descope-number-field/index.js":[4951,840,786,208,233,422,806,433,447,142],"./descope-passcode/descope-passcode-internal/index.js":[4775,433,841],"./descope-passcode/index.js":[7102,840,786,208,233,422,725,789,433,447,934,841,939],"./descope-password-field/index.js":[1721,840,786,208,233,422,767,725,211,56,433,447,320],"./descope-switch-toggle/index.js":[3774,840,786,208,767,515,433,739],"./descope-text-area/index.js":[178,840,786,233,422,101,433,322],"./descope-text-field/index.js":[9357,840,786,208,233,422,725,789,433,447,934],"./descope-text/index.js":[2092,433,528]};function n(e){if(!t.o(r,e))return Promise.resolve().then((()=>{var o=new Error("Cannot find module '"+e+"'");throw o.code="MODULE_NOT_FOUND",o}));var o=r[e],n=o[0];return Promise.all(o.slice(1).map(t.e)).then((()=>t(n)))}n.keys=()=>Object.keys(r),n.id=534,e.exports=n},7507:(e,o,t)=>{const r=t(534);e.exports=r.keys().reduce(((e,o)=>(e[o.replace(/.*?([^\/]+)\/index\.js$/,"$1")]=()=>r(o),e)),{})}},n={};function s(e){var o=n[e];if(void 0!==o)return o.exports;var t=n[e]={exports:{}};return r[e](t,t.exports,s),t.exports}s.m=r,e=[],s.O=(o,t,r,n)=>{if(!t){var i=1/0;for(a=0;a<e.length;a++){for(var[t,r,n]=e[a],d=!0,c=0;c<t.length;c++)(!1&n||i>=n)&&Object.keys(s.O).every((e=>s.O[e](t[c])))?t.splice(c--,1):(d=!1,n<i&&(i=n));if(d){e.splice(a--,1);var p=r();void 0!==p&&(o=p)}}return o}n=n||0;for(var a=e.length;a>0&&e[a-1][2]>n;a--)e[a]=e[a-1];e[a]=[t,r,n]},s.F={},s.E=e=>{Object.keys(s.F).map((o=>{s.F[o](e)}))},s.d=(e,o)=>{for(var t in o)s.o(o,t)&&!s.o(e,t)&&Object.defineProperty(e,t,{enumerable:!0,get:o[t]})},s.f={},s.e=e=>Promise.all(Object.keys(s.f).reduce(((o,t)=>(s.f[t](e,o),o)),[])),s.u=e=>(({17:"descope-date-picker-index-js",142:"descope-number-field-index-js",247:"descope-combo-index-js",317:"descope-container-index-js",320:"descope-password-field-index-js",322:"descope-text-area-index-js",528:"descope-text-index-js",662:"descope-button-index-js",739:"descope-switch-toggle-index-js",760:"descope-email-field-index-js",761:"descope-checkbox-index-js",841:"descope-passcode-descope-passcode-internal-index-js",934:"descope-text-field-index-js",939:"descope-passcode-index-js",984:"descope-logo-index-js"}[e]||e)+".js"),s.g=function(){if("object"==typeof globalThis)return globalThis;try{return this||new Function("return this")()}catch(e){if("object"==typeof window)return window}}(),s.o=(e,o)=>Object.prototype.hasOwnProperty.call(e,o),o={},t="DescopeUI:",s.l=(e,r,n,i)=>{if(o[e])o[e].push(r);else{var d,c;if(void 0!==n)for(var p=document.getElementsByTagName("script"),a=0;a<p.length;a++){var l=p[a];if(l.getAttribute("src")==e||l.getAttribute("data-webpack")==t+n){d=l;break}}d||(c=!0,(d=document.createElement("script")).charset="utf-8",d.timeout=120,s.nc&&d.setAttribute("nonce",s.nc),d.setAttribute("data-webpack",t+n),d.src=e),o[e]=[r];var u=(t,r)=>{d.onerror=d.onload=null,clearTimeout(f);var n=o[e];if(delete o[e],d.parentNode&&d.parentNode.removeChild(d),n&&n.forEach((e=>e(r))),t)return t(r)},f=setTimeout(u.bind(null,void 0,{type:"timeout",target:d}),12e4);d.onerror=u.bind(null,d.onerror),d.onload=u.bind(null,d.onload),c&&document.head.appendChild(d)}},s.r=e=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},(()=>{var e;s.g.importScripts&&(e=s.g.location+"");var o=s.g.document;if(!e&&o&&(o.currentScript&&(e=o.currentScript.src),!e)){var t=o.getElementsByTagName("script");t.length&&(e=t[t.length-1].src)}if(!e)throw new Error("Automatic publicPath is not supported in this browser");e=e.replace(/#.*$/,"").replace(/\?.*$/,"").replace(/\/[^\/]+$/,"/"),s.p=e})(),(()=>{var e={826:0};s.f.j=(o,t)=>{var r=s.o(e,o)?e[o]:void 0;if(0!==r)if(r)t.push(r[2]);else{var n=new Promise(((t,n)=>r=e[o]=[t,n]));t.push(r[2]=n);var i=s.p+s.u(o),d=new Error;s.l(i,(t=>{if(s.o(e,o)&&(0!==(r=e[o])&&(e[o]=void 0),r)){var n=t&&("load"===t.type?"missing":t.type),i=t&&t.target&&t.target.src;d.message="Loading chunk "+o+" failed.\n("+n+": "+i+")",d.name="ChunkLoadError",d.type=n,d.request=i,r[1](d)}}),"chunk-"+o,o)}},s.F.j=o=>{if(!s.o(e,o)||void 0===e[o]){e[o]=null;var t=document.createElement("link");s.nc&&t.setAttribute("nonce",s.nc),t.rel="prefetch",t.as="script",t.href=s.p+s.u(o),document.head.appendChild(t)}},s.O.j=o=>0===e[o];var o=(o,t)=>{var r,n,[i,d,c]=t,p=0;if(i.some((o=>0!==e[o]))){for(r in d)s.o(d,r)&&(s.m[r]=d[r]);if(c)var a=c(s)}for(o&&o(t);p<i.length;p++)n=i[p],s.o(e,n)&&e[n]&&e[n][0](),e[n]=0;return s.O(a)},t=self.webpackChunkDescopeUI=self.webpackChunkDescopeUI||[];t.forEach(o.bind(null,0)),t.push=o.bind(null,t.push.bind(t))})(),s.O(0,[826],(()=>{[840,767,211,513,729,433,662,786,208,515,761,233,422,725,789,447,934,247,317,54,17,437,760,984,806,142,841,939,56,320,739,101,322,528].map(s.E)}),5);var i=s(7507);return s.O(i)})()));
|
package/package.json
CHANGED
@@ -0,0 +1,141 @@
|
|
1
|
+
import {
|
2
|
+
getComponentName,
|
3
|
+
createStyleMixin,
|
4
|
+
draggableMixin,
|
5
|
+
createProxy,
|
6
|
+
inputMixin,
|
7
|
+
compose,
|
8
|
+
componentNameValidationMixin
|
9
|
+
} from '../../componentsHelpers';
|
10
|
+
import { matchHostStyle } from '../../componentsHelpers/createStyleMixin/helpers';
|
11
|
+
import textFieldMappings from '../descope-text-field/textFieldMappings';
|
12
|
+
export const componentName = getComponentName('passcode');
|
13
|
+
import TextField from '../descope-text-field/TextField'
|
14
|
+
import { componentName as descopeInternalComponentName } from './descope-passcode-internal/PasscodeInternal'
|
15
|
+
|
16
|
+
const customMixin = (superclass) =>
|
17
|
+
class DraggableMixinClass extends superclass {
|
18
|
+
constructor() {
|
19
|
+
super();
|
20
|
+
}
|
21
|
+
|
22
|
+
get digits() {
|
23
|
+
return Number.parseInt(this.getAttribute('digits')) || 6
|
24
|
+
}
|
25
|
+
|
26
|
+
connectedCallback() {
|
27
|
+
super.connectedCallback?.();
|
28
|
+
const template = document.createElement('template');
|
29
|
+
|
30
|
+
//forward required & pattern TODO use forwardAttrs
|
31
|
+
template.innerHTML = `
|
32
|
+
<${descopeInternalComponentName}
|
33
|
+
bordered="true"
|
34
|
+
name="code"
|
35
|
+
tabindex="0"
|
36
|
+
slot="input"
|
37
|
+
required="${this.shadowRoot.host.getAttribute('required')}"
|
38
|
+
pattern="${this.shadowRoot.host.getAttribute('pattern')}"
|
39
|
+
></${descopeInternalComponentName}>
|
40
|
+
`
|
41
|
+
|
42
|
+
// we are adding a slot under vaadin-text-field, it should reflect all descope-passcode children to be under vaadin-text-field, this is why it has a name & slot
|
43
|
+
const slotEle = Object.assign(document.createElement('slot'), { name: 'input', slot: 'input', part: 'input' })
|
44
|
+
this.proxyElement.appendChild(slotEle)
|
45
|
+
|
46
|
+
// we want to control when the element is out of focus
|
47
|
+
// so the validations will be triggered blur event is dispatched from descope-passcode internal (and not every time focusing a digit)
|
48
|
+
this.proxyElement._setFocused = () => { }
|
49
|
+
|
50
|
+
this.shadowRoot.host.appendChild(template.content.cloneNode(true))
|
51
|
+
this.inputElement = this.querySelector(descopeInternalComponentName)
|
52
|
+
|
53
|
+
// we want to trigger validation only when dispatching a blur event from the descope-passcode-internal
|
54
|
+
this.inputElement.addEventListener('blur', () => {
|
55
|
+
this.proxyElement.validate()
|
56
|
+
})
|
57
|
+
}
|
58
|
+
}
|
59
|
+
|
60
|
+
const { borderStyle, borderWidth, ...restTextFieldMappings } = textFieldMappings;
|
61
|
+
|
62
|
+
const Passcode = compose(
|
63
|
+
createStyleMixin({
|
64
|
+
mappings: {
|
65
|
+
...restTextFieldMappings,
|
66
|
+
},
|
67
|
+
nestedMappings: {
|
68
|
+
borderColor: {
|
69
|
+
selector: ` ${TextField.componentName}`,
|
70
|
+
property: TextField.cssVarList.borderColor
|
71
|
+
}
|
72
|
+
}
|
73
|
+
}),
|
74
|
+
draggableMixin,
|
75
|
+
inputMixin,
|
76
|
+
componentNameValidationMixin,
|
77
|
+
customMixin
|
78
|
+
)(
|
79
|
+
createProxy({
|
80
|
+
slots: [],
|
81
|
+
wrappedEleName: 'vaadin-text-field',
|
82
|
+
style: () => `
|
83
|
+
:host {
|
84
|
+
--vaadin-field-default-width: auto;
|
85
|
+
}
|
86
|
+
|
87
|
+
::slotted([slot='input']) {
|
88
|
+
-webkit-mask-image: none;
|
89
|
+
display: flex;
|
90
|
+
gap: 2px;
|
91
|
+
align-items: center;
|
92
|
+
padding: 0;
|
93
|
+
}
|
94
|
+
|
95
|
+
vaadin-text-field::part(input-field) {
|
96
|
+
background-color: transparent;
|
97
|
+
padding: 0;
|
98
|
+
}
|
99
|
+
|
100
|
+
${overrides}
|
101
|
+
`,
|
102
|
+
excludeAttrsSync: ['tabindex'],
|
103
|
+
componentName
|
104
|
+
})
|
105
|
+
);
|
106
|
+
|
107
|
+
const overrides = `
|
108
|
+
:host {
|
109
|
+
display: inline-block;
|
110
|
+
}
|
111
|
+
|
112
|
+
vaadin-text-field {
|
113
|
+
margin: 0;
|
114
|
+
padding: 0;
|
115
|
+
}
|
116
|
+
vaadin-text-field::part(input-field) {
|
117
|
+
overflow: hidden;
|
118
|
+
}
|
119
|
+
vaadin-text-field[readonly] > input:placeholder-shown {
|
120
|
+
opacity: 1;
|
121
|
+
}
|
122
|
+
|
123
|
+
vaadin-text-field > label,
|
124
|
+
vaadin-text-field::part(input-field) {
|
125
|
+
cursor: pointer;
|
126
|
+
color: var(${Passcode.cssVarList.color});
|
127
|
+
}
|
128
|
+
vaadin-text-field::part(input-field):focus {
|
129
|
+
cursor: text;
|
130
|
+
}
|
131
|
+
vaadin-text-field[required]::part(required-indicator)::after {
|
132
|
+
font-size: "12px";
|
133
|
+
content: "*";
|
134
|
+
color: var(${Passcode.cssVarList.color});
|
135
|
+
}
|
136
|
+
vaadin-text-field[readonly]::part(input-field)::after {
|
137
|
+
border: 0 solid;
|
138
|
+
}
|
139
|
+
`;
|
140
|
+
|
141
|
+
export default Passcode;
|
@@ -0,0 +1,213 @@
|
|
1
|
+
import { getComponentName } from '../../../componentsHelpers';
|
2
|
+
import { getSanitizedCharacters, focusElement } from './helpers';
|
3
|
+
|
4
|
+
export const componentName = getComponentName('passcode-internal');
|
5
|
+
|
6
|
+
class PasscodeInternal extends HTMLElement {
|
7
|
+
static get observedAttributes() {
|
8
|
+
return [
|
9
|
+
'disabled',
|
10
|
+
'bordered',
|
11
|
+
'size'
|
12
|
+
];
|
13
|
+
}
|
14
|
+
|
15
|
+
static get componentName() {
|
16
|
+
return componentName;
|
17
|
+
}
|
18
|
+
|
19
|
+
static get formAssociated() {
|
20
|
+
return true;
|
21
|
+
}
|
22
|
+
|
23
|
+
#internals
|
24
|
+
|
25
|
+
constructor() {
|
26
|
+
super();
|
27
|
+
const template = document.createElement('template');
|
28
|
+
|
29
|
+
const inputs = [...Array(this.digits).keys()].map((idx) => `
|
30
|
+
<descope-text-field
|
31
|
+
st-width="35px"
|
32
|
+
data-id=${idx}
|
33
|
+
type="tel"
|
34
|
+
autocomplete="none"
|
35
|
+
></descope-text-field>
|
36
|
+
`)
|
37
|
+
|
38
|
+
template.innerHTML = `
|
39
|
+
<div>
|
40
|
+
${inputs.join('')}
|
41
|
+
</div>
|
42
|
+
`;
|
43
|
+
|
44
|
+
this.appendChild(template.content.cloneNode(true));
|
45
|
+
|
46
|
+
this.baseSelector = ':host > div';
|
47
|
+
|
48
|
+
this.#internals = this.attachInternals();
|
49
|
+
|
50
|
+
this.inputs = Array.from(this.querySelectorAll('descope-text-field'))
|
51
|
+
}
|
52
|
+
|
53
|
+
checkValidity() {
|
54
|
+
// we need to toggle the has-error-message so the text inside the digits will become red when there is an error
|
55
|
+
if (this.#internals.validity.valid) {
|
56
|
+
this.inputs.forEach(input => input.removeAttribute('has-error-message'))
|
57
|
+
} else {
|
58
|
+
this.inputs.forEach(input => input.setAttribute('has-error-message', 'true'))
|
59
|
+
// we need to call it so the has-error-message with have the correct format (="true")
|
60
|
+
this.oninvalid?.()
|
61
|
+
}
|
62
|
+
return this.#internals.validity.valid
|
63
|
+
}
|
64
|
+
|
65
|
+
get digits() {
|
66
|
+
return Number.parseInt(this.getAttribute('digits')) || 6
|
67
|
+
}
|
68
|
+
|
69
|
+
get value() {
|
70
|
+
return this.inputs.map(({ value }) => value).join('')
|
71
|
+
}
|
72
|
+
|
73
|
+
set value(val) { }
|
74
|
+
|
75
|
+
get isRequired() {
|
76
|
+
return this.hasAttribute('required') && this.getAttribute('required') !== 'false'
|
77
|
+
}
|
78
|
+
|
79
|
+
get pattern() {
|
80
|
+
return `^$|^\\d{${this.digits},}$`
|
81
|
+
}
|
82
|
+
|
83
|
+
get valueMissingErrMsg() {
|
84
|
+
return 'Please fill out this field.'
|
85
|
+
}
|
86
|
+
|
87
|
+
get patternMismatchErrMsg() {
|
88
|
+
return `Must be a ${this.digits} digits number.`
|
89
|
+
}
|
90
|
+
|
91
|
+
get validity() {
|
92
|
+
return this.#internals.validity;
|
93
|
+
}
|
94
|
+
|
95
|
+
get validationMessage() {
|
96
|
+
return this.#internals.validationMessage;
|
97
|
+
}
|
98
|
+
|
99
|
+
reportValidity() {
|
100
|
+
this.#internals.reportValidity()
|
101
|
+
}
|
102
|
+
|
103
|
+
formAssociatedCallback() {
|
104
|
+
this.setValidity?.();
|
105
|
+
}
|
106
|
+
|
107
|
+
setValidity = () => {
|
108
|
+
if (this.isRequired && !this.value) {
|
109
|
+
this.#internals.setValidity({ valueMissing: true }, this.valueMissingErrMsg);
|
110
|
+
}
|
111
|
+
else if (this.pattern && !new RegExp(this.pattern).test(this.value)) {
|
112
|
+
this.#internals.setValidity({ patternMismatch: true }, this.patternMismatchErrMsg);
|
113
|
+
}
|
114
|
+
else {
|
115
|
+
this.#internals.setValidity({})
|
116
|
+
}
|
117
|
+
};
|
118
|
+
|
119
|
+
async connectedCallback() {
|
120
|
+
this.setValidity();
|
121
|
+
this.initInputs()
|
122
|
+
|
123
|
+
this.onfocus = () => {
|
124
|
+
this.inputs[0].focus();
|
125
|
+
}
|
126
|
+
}
|
127
|
+
|
128
|
+
getInputIdx(inputEle) {
|
129
|
+
return Number.parseInt(inputEle.getAttribute('data-id'), 10)
|
130
|
+
}
|
131
|
+
|
132
|
+
getNextInput(currInput) {
|
133
|
+
const currentIdx = this.getInputIdx(currInput)
|
134
|
+
const newIdx = Math.min(currentIdx + 1, this.inputs.length - 1)
|
135
|
+
return this.inputs[newIdx]
|
136
|
+
}
|
137
|
+
|
138
|
+
getPrevInput(currInput) {
|
139
|
+
const currentIdx = this.getInputIdx(currInput)
|
140
|
+
const newIdx = Math.max(currentIdx - 1, 0)
|
141
|
+
return this.inputs[newIdx]
|
142
|
+
}
|
143
|
+
|
144
|
+
fillDigits(charArr, currentInput) {
|
145
|
+
for (let i = 0; i < charArr.length; i += 1) {
|
146
|
+
currentInput.value = charArr[i] ?? '';
|
147
|
+
|
148
|
+
const nextInput = this.getNextInput(currentInput);
|
149
|
+
if (nextInput === currentInput) break;
|
150
|
+
currentInput = nextInput;
|
151
|
+
}
|
152
|
+
|
153
|
+
!currentInput.hasAttribute('focused') && focusElement(currentInput);
|
154
|
+
};
|
155
|
+
|
156
|
+
initInputs() {
|
157
|
+
this.inputs.forEach((input) => {
|
158
|
+
|
159
|
+
// in order to simulate blur on the input
|
160
|
+
// we are checking if focus on one of the digits happened immediately after blur on another digit
|
161
|
+
// if not, the component is no longer focused and we should simulate blur
|
162
|
+
input.addEventListener('blur', () => {
|
163
|
+
const timerId = setTimeout(() => {
|
164
|
+
this.dispatchEvent(new Event('blur'))
|
165
|
+
});
|
166
|
+
|
167
|
+
this.inputs.forEach((ele) =>
|
168
|
+
ele.addEventListener('focus', () => clearTimeout(timerId), { once: true })
|
169
|
+
);
|
170
|
+
})
|
171
|
+
|
172
|
+
input.oninput = (e) => {
|
173
|
+
const charArr = getSanitizedCharacters(input.value);
|
174
|
+
|
175
|
+
if (!charArr.length) input.value = ''; // if we got an invalid value we want to clear the input
|
176
|
+
else this.fillDigits(charArr, input);
|
177
|
+
|
178
|
+
this.setValidity();
|
179
|
+
};
|
180
|
+
|
181
|
+
input.onkeydown = ({ key }) => {
|
182
|
+
if (key === 'Backspace') {
|
183
|
+
input.value = '';
|
184
|
+
|
185
|
+
// if the user deleted the digit we want to focus the previous digit
|
186
|
+
const prevInput = this.getPrevInput(input)
|
187
|
+
|
188
|
+
!prevInput.hasAttribute('focused') && setTimeout(() => {
|
189
|
+
focusElement(prevInput);
|
190
|
+
});
|
191
|
+
} else if (key.match(/^(\d)$/g)) { // if input is a digit
|
192
|
+
input.value = ''; // we are clearing the previous value so we can override it with the new value
|
193
|
+
}
|
194
|
+
|
195
|
+
this.setValidity()
|
196
|
+
};
|
197
|
+
})
|
198
|
+
}
|
199
|
+
|
200
|
+
attributeChangedCallback(
|
201
|
+
attrName,
|
202
|
+
oldValue,
|
203
|
+
newValue
|
204
|
+
) {
|
205
|
+
if (oldValue !== newValue &&
|
206
|
+
PasscodeInternal.observedAttributes.includes(attrName)) {
|
207
|
+
this.inputs.forEach((input) => input.setAttribute(attrName, newValue))
|
208
|
+
}
|
209
|
+
}
|
210
|
+
}
|
211
|
+
|
212
|
+
export default PasscodeInternal;
|
213
|
+
|
@@ -0,0 +1,14 @@
|
|
1
|
+
|
2
|
+
export const focusElement = (ele) => {
|
3
|
+
ele?.focus();
|
4
|
+
ele?.setSelectionRange(1, 1);
|
5
|
+
};
|
6
|
+
|
7
|
+
export const getSanitizedCharacters = (str) => {
|
8
|
+
const pin = str.replace(/\s/g, ''); // sanitize string
|
9
|
+
|
10
|
+
// accept only numbers
|
11
|
+
if (!pin.match(/^\d+$/)) return [];
|
12
|
+
|
13
|
+
return [...pin]; // creating array of chars
|
14
|
+
};
|
@@ -0,0 +1,46 @@
|
|
1
|
+
import {
|
2
|
+
getComponentName,
|
3
|
+
createStyleMixin,
|
4
|
+
draggableMixin,
|
5
|
+
compose,
|
6
|
+
componentNameValidationMixin
|
7
|
+
} from '../../componentsHelpers';
|
8
|
+
import { matchHostStyle } from '../../componentsHelpers/createStyleMixin/helpers';
|
9
|
+
|
10
|
+
export const componentName = getComponentName('text');
|
11
|
+
|
12
|
+
class RawText extends HTMLElement {
|
13
|
+
static get componentName() {
|
14
|
+
return componentName;
|
15
|
+
}
|
16
|
+
constructor() {
|
17
|
+
super();
|
18
|
+
const template = document.createElement('template');
|
19
|
+
template.innerHTML = `<slot></slot>`;
|
20
|
+
|
21
|
+
this.attachShadow({ mode: 'open' });
|
22
|
+
this.shadowRoot.appendChild(template.content.cloneNode(true));
|
23
|
+
|
24
|
+
this.baseSelector = ':host > slot';
|
25
|
+
}
|
26
|
+
}
|
27
|
+
|
28
|
+
const Text = compose(
|
29
|
+
createStyleMixin({
|
30
|
+
mappings: {
|
31
|
+
fontFamily: {},
|
32
|
+
lineHeight: {},
|
33
|
+
fontStyle: {},
|
34
|
+
fontSize: {},
|
35
|
+
fontWeight: {},
|
36
|
+
width: {},
|
37
|
+
color: {},
|
38
|
+
textAlign: matchHostStyle(),
|
39
|
+
display: matchHostStyle()
|
40
|
+
}
|
41
|
+
}),
|
42
|
+
draggableMixin,
|
43
|
+
componentNameValidationMixin
|
44
|
+
)(RawText);
|
45
|
+
|
46
|
+
export default Text;
|
@@ -35,9 +35,9 @@ export const createProxy = ({
|
|
35
35
|
this.setAttribute('tabindex', '0');
|
36
36
|
|
37
37
|
// we want to focus on the proxy element when focusing our WC
|
38
|
-
this.
|
38
|
+
this.addEventListener('focus', () => {
|
39
39
|
this.proxyElement.focus();
|
40
|
-
}
|
40
|
+
})
|
41
41
|
|
42
42
|
// `onkeydown` is set on `proxyElement` support proper tab-index navigation
|
43
43
|
// this support is needed since both proxy host and element catch `focus`/`blur` event
|
@@ -63,8 +63,7 @@ export const createProxy = ({
|
|
63
63
|
this.proxyElement.addEventListener('mouseover', this.mouseoverCbRef);
|
64
64
|
|
65
65
|
// sync events
|
66
|
-
this.addEventListener = this.proxyElement.addEventListener
|
67
|
-
|
66
|
+
this.addEventListener = (...args) => this.proxyElement.addEventListener(...args)
|
68
67
|
syncAttrs(this.proxyElement, this.hostElement, excludeAttrsSync);
|
69
68
|
}
|
70
69
|
}
|