@egodesign/form 1.6.0 → 1.7.0
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/README.md +68 -3
- package/dist/js/egodesign.form.cjs.min.js +1 -1
- package/dist/js/egodesign.form.d.ts +78 -0
- package/dist/js/egodesign.form.esm.min.js +1 -1
- package/dist/js/egodesign.form.umd.min.js +1 -1
- package/dist/js/modules/ValidationClass.d.ts +34 -0
- package/dist/js/modules/tools.d.ts +15 -0
- package/dist/js/modules/validationMessages.d.ts +2 -0
- package/package.json +12 -9
package/README.md
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
# EgoForm
|
|
2
|
-
Lightweight, dependency-free JavaScript library for comprehensive form validation and submission. Customize validation rules with ease and seamlessly integrate into any vanilla project.
|
|
2
|
+
Lightweight, dependency-free JavaScript/Typescript library for comprehensive form validation and submission. Customize validation rules with ease and seamlessly integrate into any vanilla project.
|
|
3
3
|
</br></br>
|
|
4
4
|
|
|
5
5
|

|
|
@@ -11,6 +11,7 @@ Lightweight, dependency-free JavaScript library for comprehensive form validatio
|
|
|
11
11
|
- Customization: Control class names and error messages to match your design.
|
|
12
12
|
- Flexible Submission: Choose between fetch, get, or post methods for form submission.
|
|
13
13
|
- Event Handling: Respond to validation errors, form submission events, and successful/failed responses.
|
|
14
|
+
- TypeScript Support: Fully typed and well-documented.
|
|
14
15
|
|
|
15
16
|
|
|
16
17
|
## Usage:
|
|
@@ -27,6 +28,10 @@ yarn add @egodesign/form
|
|
|
27
28
|
<br>
|
|
28
29
|
|
|
29
30
|
2. Import the **`EgoForm`** class into your file and create as many instances as needed.
|
|
31
|
+
|
|
32
|
+
<details>
|
|
33
|
+
<summary>JavaScript</summary>
|
|
34
|
+
|
|
30
35
|
```js
|
|
31
36
|
import EgoForm from '@egodesign/form';
|
|
32
37
|
|
|
@@ -80,6 +85,66 @@ const myForm = new EgoForm({
|
|
|
80
85
|
onError: err => console.log('Error', err)
|
|
81
86
|
});
|
|
82
87
|
```
|
|
88
|
+
</details>
|
|
89
|
+
|
|
90
|
+
<details>
|
|
91
|
+
<summary>TypeScript</summary>
|
|
92
|
+
|
|
93
|
+
```ts
|
|
94
|
+
import EgoForm from '@egodesign/form';
|
|
95
|
+
|
|
96
|
+
const myForm: EgoForm = new EgoForm({
|
|
97
|
+
element: document.getElementById('myForm'),
|
|
98
|
+
submitType: 'fetch',
|
|
99
|
+
debug: true,
|
|
100
|
+
submitDataFormat: 'formData',
|
|
101
|
+
requestHeaders: {},
|
|
102
|
+
fieldGroups: {
|
|
103
|
+
phone_numbers: [
|
|
104
|
+
'phone',
|
|
105
|
+
'mobile'
|
|
106
|
+
]
|
|
107
|
+
},
|
|
108
|
+
extraFields: [
|
|
109
|
+
{
|
|
110
|
+
name: 'extra',
|
|
111
|
+
value: 'value'
|
|
112
|
+
}
|
|
113
|
+
],
|
|
114
|
+
serializerIgnoreList: ['ignore'],
|
|
115
|
+
classes: {
|
|
116
|
+
requiredField: '--required',
|
|
117
|
+
requiredIfFilledField: '--required-if-filled',
|
|
118
|
+
fieldHasError: '--has-error',
|
|
119
|
+
controlHasError: 'is-danger',
|
|
120
|
+
hiddenErrorMessage: 'is-hidden',
|
|
121
|
+
formSubmittingState: '--submitting',
|
|
122
|
+
buttonSubmittingState: 'is-loading'
|
|
123
|
+
},
|
|
124
|
+
customValidations: {
|
|
125
|
+
test: [{
|
|
126
|
+
name: 'isValid',
|
|
127
|
+
condition: (value: string | number) => value === 'testing',
|
|
128
|
+
message: 'This field value should be "testing".'
|
|
129
|
+
}]
|
|
130
|
+
},
|
|
131
|
+
customValidationsMessages: {
|
|
132
|
+
"fieldName": {
|
|
133
|
+
"empty": "message",
|
|
134
|
+
"invalid": "message",
|
|
135
|
+
}
|
|
136
|
+
},
|
|
137
|
+
onValidationError: (fields: string[], instance: EgoForm) => {
|
|
138
|
+
console.log(instance, fields);
|
|
139
|
+
},
|
|
140
|
+
onStepChange: (prev: string, next: string) => console.log(prev, next),
|
|
141
|
+
onSubmitStart: () => console.log('Submit start'),
|
|
142
|
+
onSubmitEnd: () => console.log('Submit end'),
|
|
143
|
+
onSuccess: (resp: Response) => console.log('Success', resp),
|
|
144
|
+
onError: (err: Error) => console.log('Error', err)
|
|
145
|
+
});
|
|
146
|
+
```
|
|
147
|
+
</details>
|
|
83
148
|
|
|
84
149
|
## HTML structure sample:
|
|
85
150
|
```html
|
|
@@ -179,8 +244,8 @@ const myForm = new EgoForm({
|
|
|
179
244
|
| Name | Description | Accepted values |
|
|
180
245
|
| --- | ----------- | ----------- |
|
|
181
246
|
| *onStepChange* | Event triggered every time there's a step change. Only available for stepped forms. It returns the previous and the next steps. | An anonymous function or `null`.
|
|
182
|
-
| *onValidationError* | Event triggered when there's any validation error.
|
|
183
|
-
| *onBeforeSubmit* | Event triggered before the submit starts. | An anonymous function or `null`.
|
|
247
|
+
| *onValidationError* | Event triggered when there's any validation error. The callback function receives an array containing the names of invalid fields and a reference to the instance itself. | An anonymous function or `null`.
|
|
248
|
+
| *onBeforeSubmit* | Event triggered before the submit starts. The callback function receives a reference to the instance itself | An anonymous function or `null`.
|
|
184
249
|
| *onSubmitStart* | Event triggered when the submit starts. | An anonymous function or `null`.
|
|
185
250
|
| *onSubmitEnd* | Event triggered when the submit ends, regardless of the outcome. | An anonymous function or `null`.
|
|
186
251
|
| *onSuccess* | Event triggered when the request results successful. | An anonymous function or `null`.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";const t=({element:t,enter:e,time:s,displayType:i,callback:r})=>{t.style.opacity=e?0:1,e&&(t.style.display=i||"block");let a=+new Date;!function i(){t.style.opacity=e?+t.style.opacity+(new Date-a)/(s||200):+t.style.opacity-(new Date-a)/(s||200),a=+new Date,e&&+t.style.opacity<1||!e&&+t.style.opacity>0?window.requestAnimationFrame&&requestAnimationFrame(i)||setTimeout(i,16):(e||(t.style.display="none"),r&&"function"==typeof r&&r())}()},e=({element:t,className:e})=>{if(!t)return!1;for(;t&&!t.classList.contains(e);)t=!!t.parentElement&&t.parentElement;return t};function s(t,e="log"){"log"==e?console.log("::EgoForm:: "+t):"data"==e&&(console.log("::EgoForm:: DATA"),console.table(t))}const i={default:{empty:"Campo requerido.",invalid:"Campo nó válido.",minLength:"Debe tener al menos [[var]] caracteres.",maxLength:"Debe tener como máximo [[var]] caracteres."},email:{empty:"El email es requerido.",invalid:"El email no es válido."},message:{empty:"El mensaje es requerido."},password:{empty:"Ingrese una contraseña."},password_repeat:{empty:"Debe repetir la contraseña.",unequal:"Las contraseñas no coindicen."},cuil:{empty:"Campo requerido.",min:"El número debe tener exactamente 11 dígitos.",invalid:"El número ingresado no es válido."},url:{empty:"Campo requerido.",invalid:"URL no válida."},file:{empty:"Campo requerido",min_size:"El tamaño mínimo es [[var]]",max_size:"El tamaño máximo es [[var]]"}};class r{constructor({customValidations:t,classes:e,customValidationMessages:s,debug:r}){this.customValidations=t,this.validationMessages={...i,...s}||i,this.classes=e,this.debug=r}validateField(t){const e=t.dataset.type,i=["radio","checkbox"].includes(e),r=t.classList.contains(this.classes.requiredField),a=t.classList.contains(this.classes.requiredIfFilledField),o=t.querySelector(".form__error"),l=t.querySelector(".form__control"),n=i?t.querySelector(".form__control:checked"):null,u=l?l.getAttribute("name"):"",d=Object.keys(this.customValidations),c=t.dataset.minLength?parseInt(t.dataset.minLength):null,h=t.dataset.maxLength?parseInt(t.dataset.maxLength):null;if(l||this.throwError("control not found."),u||this.throwError("control name not found."),this.debug&&s(`validating field "${u}"`),r&&!l.value)return o&&(o.textContent=this.validationMessages[u]?this.validationMessages[u].empty:this.validationMessages.default.empty),this.displayFieldError(l,t,o),!1;if(l.value){if(c&&l.value.length<c)return o&&(o.textContent=this.validationMessages.default.minLength.replace("[[var]]",c)),this.displayFieldError(l,t,o),!1;if(h&&l.value.length>h)return o&&(o.textContent=this.validationMessages.default.maxLength.replace("[[var]]",h)),this.displayFieldError(l,t,o),!1}if(i&&!n){if(!a)return this.displayFieldError(l,t,o),o&&(o.textContent=this.validationMessages[u]?this.validationMessages[u].empty:this.validationMessages.default.empty),!1}else{switch(e){case"email":if(l.value&&!this.isValidEmail(l.value))return o&&(o.textContent=this.validationMessages.email.invalid),this.displayFieldError(l,t,o),!1;break;case"password_repeat":{const e=document.getElementById("password");if(e&&l.value!=e.value)return o&&(o.textContent=this.validationMessages.password_repeat.unequal),this.displayFieldError(l,t,o),!1;break}case"cuil":case"cuit":if(l.value&&!this.isValidCuitCuil(l.value))return o&&(o.textContent=this.validationMessages.cuil.invalid),this.displayFieldError(l,t,o),!1;break;case"url":if(l.value&&!this.isValidUrl(l.value))return o&&(o.textContent=this.validationMessages.url.invalid),this.displayFieldError(l,t,o),!1;break;case"money":const e=l.dataset.currency?l.dataset.currency:"$";if(""==l.value||l.value==e)return o&&(o.textContent=this.validationMessages.default.empty),this.displayFieldError(l,t,o),!1;break;case"single-checkbox":if(!l.checked)return this.displayFieldError(l,t,o),o&&(o.textContent=this.validationMessages[u]?this.validationMessages[u].empty:this.validationMessages.default.empty),!1}for(const s of d)if(e===s)for(const s of this.customValidations[e])if(!s.condition(l.value))return o&&(o.textContent=s.message||""),this.displayFieldError(l,t,o),!1}return!0}isValidEmail(t){return/(?!.*\.{2})^([a-z\d!#$%&'*+\-/=?^_`{|}~\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]+(\.[a-z\d!#$%&'*+\-/=?^_`{|}~\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]+)*|"((([\t]*\r\n)?[\t]+)?([\x01-\x08\x0b\x0c\x0e-\x1f\x7f\x21\x23-\x5b\x5d-\x7e\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]|\\[\x01-\x09\x0b\x0c\x0d-\x7f\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]))*(([\t]*\r\n)?[\t]+)?")@(([a-z\d\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]|[a-z\d\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF][a-z\d\-._~\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]*[a-z\d\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])\.)+([a-z\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]|[a-z\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF][a-z\d\-._~\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]*[a-z\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])\.?$/i.test(t.toLowerCase())}isValidCuitCuil(t){const e=[5,4,3,2,7,6,5,4,3,2];let s=t.toString().replace(/[^0-9]/g,"").split("");if(11!=s.length)return!1;s=s.map((t=>parseInt(t)));const i=parseInt(s.pop());let r=11-s.reduce(((t,s,i)=>t+s*e[i]),0)%11;return 10==r&&(r=9),11==r&&(r=0),r==i}isValidUrl(t){return!!new RegExp("^(https?:\\/\\/)?((([a-z\\d]([a-z\\d-]*[a-z\\d])*)\\.)+[a-z]{2,}|((\\d{1,3}\\.){3}\\d{1,3}))(\\:\\d+)?(\\/[-a-z\\d%_.~+]*)*(\\?[;&a-z\\d%_.~+=-]*)?(\\#[-a-z\\d_]*)?$","i").test(t)}isValidFileSize(t){let e=null;const s=t.querySelector(".form__control"),i=parseFloat(t.dataset.minSize),r=parseFloat(t.dataset.maxSize),a=t.querySelector(".form__error"),o=s.files[0].name,l=s.files[0].size,n=parseFloat(s.files.length?(l/1048576).toFixed(1):0);n>r||!l?(s.value="",t.classList.remove("--has-file"),s.setAttribute("aria-invalid","true"),t.classList.add(this.classes.fieldHasError)):o?(this.hasFile=!0,t.classList.add("--has-file")):(this.hasFile=!1,t.classList.remove("--has-file")),l?n<i?e=this.validationMessages.file.min_size.replace("[[var]]",i+" MB"):n>r&&(e=this.validationMessages.file.max_size.replace("[[var]]",r+" MB")):e=this.validationMessages.file.empty,e&&(a.textContent=e,this.displayFieldError(s,t,a))}displayFieldError(t,e,s){t.setAttribute("aria-invalid","true"),e.classList.add(this.classes.fieldHasError),this.classes.controlHasError&&t.classList.add(this.classes.controlHasError),s&&s.classList.remove(this.classes.hiddenErrorMessage)}clearControlError(t){t.setAttribute("aria-invalid","false"),this.classes.controlHasError&&t.classList.remove(this.classes.controlHasError);const s=e({element:t,className:"form__field"}),i=s.querySelector(".form__error");s.classList.remove(this.classes.fieldHasError),i&&(i.textContent="",i.classList.add(this.classes.hiddenErrorMessage))}realTimeValidations(t){t.querySelectorAll('.form__field[data-type="file"]').forEach((t=>{const e=t.querySelector(".form__control");e?.addEventListener("change",(e=>{e.stopPropagation(),this.isValidFileSize(t)}))}))}throwError(t){throw new Error(`EgoForm Error: ${t}`)}}module.exports=class{constructor({element:t,classes:e,submitType:i,submitDataFormat:a,submitUrl:o,requestHeaders:l,fieldGroups:n,extraFields:u,serializerIgnoreList:d,customValidations:c,customValidationMessages:h,onStepChange:m,onValidationError:f,onSubmitStart:p,onSubmitEnd:F,onSuccess:v,onError:g,onBeforeSubmit:y,resetOnSuccess:E,resetLoaderOnSuccess:b,scrollOnError:S,preventSubmit:_,debug:L}){if(this.form=t,this.submitType=i||"fetch",this.submitDataFormat=a||"formData",this.requestHeaders=l||{},this.actionUrl=this.form.getAttribute("action")||o||null,this.submitMethod=this.form.getAttribute("method")||"POST",this.submitBtn=this.form.querySelector('button[type="submit"]')||null,this.classes={requiredField:"--required",requiredIfFilledField:"--required-if-filled",fieldHasError:"--has-error",controlHasError:!1,hiddenErrorMessage:"--hidden",formSubmittingState:"--submitting",buttonSubmittingState:"--loading",clearFieldError:"--clear-error",validateOnBlur:"--validate-onblur",validateOnInput:"--validate-oninput",...e},this.isValid=!0,this.validator=new r({customValidations:c||{},classes:this.classes,customValidationMessages:h||null,debug:L}),this.onValidationError=f??!1,this.onStepChange=m??!1,this.onSubmitStart=p??!1,this.onSubmitEnd=F??!1,this.onSuccess=v??!1,this.onError=g??!1,this.onBeforeSubmit=y??!1,this.fieldGroups=n??!1,this.extraFields=u??[],this.hasFile=!1,this.serializerIgnoreList=d||[],this.resetOnSuccess=E??!0,this.resetLoaderOnSuccess=b??!0,this.scrollOnError=S??!0,this.currentStep=this.form.querySelector(".form__step")?parseInt(this.form.querySelector(".form__step.--active").dataset.step):0,this.currentStepOptional=!1,this.stepChanging=!1,this.preventSubmit=_??!1,this.debug=L??!1,this.declareHandlers(),!this.actionUrl||""===this.actionUrl)throw new Error("The form doesn't have an action attribute or submitUrl wasn't provided.");this.debug&&s("initialized!")}submit(){this.preventSubmit||this.resumeSubmit()}resumeSubmit(){this.debug&&s(`submitting using ${this.submitType}!`),this.submittingForm(!0),this.isValid=!0;const t=[];if(this.form.querySelectorAll(".form__field").forEach((e=>{this.validator.validateField(e)||(t.push(e.querySelector(".form__control")?.name),this.isValid=!1)})),this.isValid)this.debug?(s("the form was submitted!"),s(this.serializeData(),"data"),setTimeout((()=>{this.submittingForm(!1,!0)}),1e3)):"fetch"==this.submitType?fetch(this.actionUrl,{method:this.submitMethod,headers:"json"===this.submitDataFormat?{"Content-Type":"application/json",...this.requestHeaders}:{...this.requestHeaders},body:"json"===this.submitDataFormat?JSON.stringify(this.serializeData()):this.serializeData(!0)}).then((t=>{200===t.status||201===t.status?(this.resetOnSuccess&&this.reset(),"function"==typeof this.onSuccess&&this.onSuccess(t)):"function"==typeof this.onError&&this.onError(t)})).catch((t=>{"function"==typeof this.onError&&this.onError(t)})).finally((()=>{this.submittingForm(!1)})):this.form.submit();else if(this.submittingForm(!1,!0),"function"==typeof this.onValidationError&&this.onValidationError(t),this.debug&&s(`this fields have failed validation: ${t.toString().replace(/,/g,", ")}.`),this.scrollOnError){const t=this.form.querySelector(`.form__field.${this.classes.fieldHasError}`);(t=>{const e=t.getBoundingClientRect();return e.top>=0&&e.left>=0&&e.bottom<=(window.innerHeight||document.documentElement.clientHeight)&&e.right<=(window.innerWidth||document.documentElement.clientWidth)})(t)||t.scrollIntoView({behavior:"smooth"})}}submittingForm(t,e=!1){let s=document.getElementsByTagName("body").item(0);t?(this.form.classList.add(this.classes.formSubmittingState),this.submitBtn.classList.add(this.classes.buttonSubmittingState),s.classList.add("--block"),"function"==typeof this.onSubmitStart&&this.onSubmitStart()):((this.resetLoaderOnSuccess||e)&&(this.form.classList.remove(this.classes.formSubmittingState),this.submitBtn.classList.remove(this.classes.buttonSubmittingState),s.classList.remove("--block")),"function"==typeof this.onSubmitEnd&&this.onSubmitEnd())}serializeData(t=!1){const e=new FormData(this.form),s={};for(const t of e){const[e,i]=t;if(!this.serializerIgnoreList.includes(e)){if(i instanceof File&&!i.size&&!i.name)continue;s.hasOwnProperty(e)?s[e]=[...s[e],i]:s[e]=i}}if(this.fieldGroups)for(const t in this.fieldGroups)if(Object.hasOwnProperty.call(this.fieldGroups,t)){let i=[{}];for(const r of this.fieldGroups[t])i[0][r]=e.get(r),delete s[r];s[t]=i}if(this.extraFields.length)for(const t of this.extraFields)t.hasOwnProperty("name")&&t.hasOwnProperty("value")&&(s[t.name]=t.value);return t?e:s}reset(){this.form.reset(),this.form.querySelectorAll(".form__field").forEach((t=>t.classList.remove("--filled",`${this.classes.fieldHasError}`))),this.form.querySelectorAll(".form__control").forEach((t=>t.setAttribute("aria-invalid","false"))),this.currentStep&&this.changeStep(1)}changeStep(e){if(!this.stepChanging){const s=this.currentStepOptional?this.currentStep+"b":this.currentStep,i=this.form.querySelector('[data-step="'+s+'"]'),r=i.querySelectorAll(`.${this.classes.requiredField}`),a="next"===e?this.currentStep+1:"prev"!==e||this.currentStepOptional?"optional"===e?this.currentStep+"b":this.currentStep:this.currentStep-1,o=this.form.querySelector('[data-step="'+a+'"]');(this.currentStep!==a||this.currentStepOptional)&&(this.stepChanging=!0,this.isValid=!0,setTimeout((()=>{!r||"next"!==e&&"optional"!==e||r.forEach((t=>{this.validator.validateField(t)||(this.isValid=!1)})),i&&o&&this.isValid?t({element:i,enter:!1,time:200,displayType:"flex",callback:()=>{i.classList.remove("--active"),t({element:o,enter:!0,time:200,displayType:"flex",callback:()=>{o.classList.add("--active"),this.stepChanging=!1,this.currentStepOptional="optional"===e,this.currentStep=parseInt(a),"function"==typeof this.onStepChange&&this.onStepChange(s,a)}})}}):this.stepChanging=!1}),50))}}nextStep(){this.changeStep("next")}optionalStep(){this.changeStep("optional")}prevStep(){this.changeStep("prev")}isControlFilled(t){let e=t.parentElement;""!==t.value?e.classList.add("--filled"):e.classList.remove("--filled")}filterNumber(t,e=[]){const s=e.join(""),i=new RegExp("[^"+s+"0-9]","g");return t.toString().replace(i,"")}filterFormattedQuantity(t,e=".",s=",",i){const r=s?t.toString().split(s):[t],a=r[0].replace(/\B(?=(\d{3})+(?!\d))/g,e);let o="";return o=i>0&&r[1]&&r[1].length?a+s+r[1].slice(0,i):a,o}filterMoneyAmount(t,e="$",s=".",i=",",r){if(!t||t==e)return"";let a=this.filterNumber(t,[i||""]);return a=this.filterFormattedQuantity(a,s,i,r),`${e} ${a}`}filterPhoneNumber(t){return t.replace(/[^\d+\-() ]*/g,"")}togglePasswordVisibility(t){this.debug&&s("Password visibility toggled!");const e=t.parentElement.querySelector(".form__control");if(e){const s="password"===e.getAttribute("type")?"text":"password";e.setAttribute("type",s),t.classList.toggle("--hide")}}declareHandlers(){const t=this;if(!this.submitBtn)throw new Error(`There's no submit button in this form "${this.form.id}".`);this.submitBtn.addEventListener("click",(function(e){e.preventDefault(),"function"==typeof t.onBeforeSubmit&&t.onBeforeSubmit(),t.submit()})),this.validator.realTimeValidations(this.form),this.form.querySelectorAll(`.form__field.${this.classes.validateOnBlur}`).forEach((t=>{t.querySelector(".form__control").addEventListener("blur",(()=>{this.validator.validateField(t)||(this.isValid=!1)}))})),this.form.querySelectorAll(`.form__field.${this.classes.validateOnInput}`).forEach((t=>{const e=t.querySelector(".form__control");e.addEventListener("input",(()=>{this.validator.validateField(t)&&this.validator.clearControlError(e)}))})),this.form.querySelectorAll(".form__next-step").forEach((e=>{e.addEventListener("click",t.nextStep.bind(t))})),this.form.querySelectorAll(".form__optional-step").forEach((e=>{e.addEventListener("click",t.optionalStep.bind(t))})),this.form.querySelectorAll(".form__prev-step").forEach((e=>{e.addEventListener("click",t.prevStep.bind(t))})),this.form.querySelectorAll(".form__control").forEach((t=>{this.isControlFilled(t),t.addEventListener("keyup",(()=>{this.isControlFilled(t)})),t.addEventListener("change",(()=>{this.isControlFilled(t)}))})),this.form.querySelectorAll(".form__control").forEach((t=>{t.addEventListener("focus",(()=>{this.validator.clearControlError(t)}))})),this.form.querySelectorAll("."+this.classes.clearFieldError).forEach((s=>{s.addEventListener("click",(()=>{const i=e({element:s,className:"form__field"});t.validator.clearControlError(i.querySelector(".form__control"))}))})),this.form.querySelectorAll(".form__field.--number input").forEach((t=>{const s=this,i=e({element:t,className:"form__field"}),r=i&&i.dataset.thousandsSeparator?i.dataset.thousandsSeparator:null,a=i&&i.dataset.decimalSeparator?i.dataset.decimalSeparator:"",o=i&&i.dataset.decimals?i.dataset.decimals:"";function l(){t.value=s.filterNumber(t.value,[a])}t.addEventListener("focus",l),t.addEventListener("input",l),t.addEventListener("paste",l),t.addEventListener("blur",(()=>{t.value=r?s.filterFormattedQuantity(t.value,r,a,parseInt(o)):s.filterNumber(t.value)}))})),this.form.querySelectorAll(".form__field.--money-amount input").forEach((t=>{const s=this,i=e({element:t,className:"form__field"}),r=i&&i.dataset.currency?i.dataset.currency:"$",a=i&&i.dataset.thousandsSeparator?i.dataset.thousandsSeparator:".",o=i&&i.dataset.decimalSeparator?i.dataset.decimalSeparator:"",l=i&&i.dataset.decimals?i.dataset.decimals:"";function n(){t.value=s.filterNumber(t.value,[o])}t.addEventListener("focus",n),t.addEventListener("input",n),t.addEventListener("paste",n),t.addEventListener("blur",(()=>{t.value=this.filterMoneyAmount(t.value,r,a,o,parseInt(l))}))})),this.form.querySelectorAll(".form__field.--phone input").forEach((t=>{t.addEventListener("input",(()=>{t.value=this.filterPhoneNumber(t.value)})),t.addEventListener("paste",(()=>{t.value=this.filterPhoneNumber(t.value)}))})),this.form.querySelectorAll(".form__toggle-password-visibility").forEach((t=>{t.addEventListener("click",this.togglePasswordVisibility(t))}))}};
|
|
1
|
+
"use strict";const e=({element:e,enter:t,time:i,displayType:s,callback:r})=>{e.style.opacity=t?"0":"1",t&&(e.style.display=s);let a=(new Date).getTime();!function s(){e.style.opacity=t?(+e.style.opacity+((new Date).getTime()-a)/i).toString():(+e.style.opacity-((new Date).getTime()-a)/i).toString(),a=(new Date).getTime(),t&&Number(e.style.opacity)<1||!t&&Number(e.style.opacity)>0?requestAnimationFrame(s)||setTimeout(s,16):(t||(e.style.display="none"),r&&"function"==typeof r&&r())}()},t=({element:e,className:t})=>{if(!e)return null;for(;e&&!e.classList.contains(t);)e=e.parentElement?e.parentElement:null;return e};function i(e,t="log"){"log"==t?console.log("::EgoForm:: "+e):"data"==t&&(console.log("::EgoForm:: DATA"),console.table(e))}const s={default:{empty:"Campo requerido.",invalid:"Campo nó válido.",minLength:"Debe tener al menos [[var]] caracteres.",maxLength:"Debe tener como máximo [[var]] caracteres."},email:{empty:"El email es requerido.",invalid:"El email no es válido."},message:{empty:"El mensaje es requerido."},password:{empty:"Ingrese una contraseña."},password_repeat:{empty:"Debe repetir la contraseña.",unequal:"Las contraseñas no coindicen."},cuil:{empty:"Campo requerido.",min:"El número debe tener exactamente 11 dígitos.",invalid:"El número ingresado no es válido."},url:{empty:"Campo requerido.",invalid:"URL no válida."},file:{empty:"Campo requerido",min_size:"El tamaño mínimo es [[var]]",max_size:"El tamaño máximo es [[var]]"}};class r{constructor({customValidations:e,classes:t,customValidationMessages:i,debug:r}){this.customValidations=e,this.validationMessages={...s,...i},this.classes=t,this.debug=r}validateField({field:e}){const t=e.dataset.type||"",s=["radio","checkbox"].includes(t),r=e.classList.contains(this.classes.requiredField),a=e.classList.contains(this.classes.requiredIfFilledField),o=e.querySelector(".form__error"),l=e.querySelector(".form__control"),n=s?e.querySelector(".form__control:checked"):null,u=l?l.getAttribute("name"):null,d=Object.keys(this.customValidations),c=e.dataset.minLength?Number(e.dataset.minLength):null,m=e.dataset.maxLength?Number(e.dataset.maxLength):null;if(l||this.throwError("control not found."),u||this.throwError("control name not found."),this.debug&&i(`validating field "${u}"`),r&&!l?.value)return o&&this.validationMessages&&u&&(o.textContent=this.validationMessages[u]?this.validationMessages[u].empty:this.validationMessages.default.empty),this.displayFieldError({control:l,field:e,errorElement:o}),!1;if(l?.value){if(c&&l.value.length<c)return o&&this.validationMessages&&(o.textContent=this.validationMessages.default.minLength.replace("[[var]]",c.toString())),this.displayFieldError({control:l,field:e,errorElement:o}),!1;if(m&&l.value.length>m)return o&&this.validationMessages&&(o.textContent=this.validationMessages.default.maxLength.replace("[[var]]",m.toString())),this.displayFieldError({control:l,field:e,errorElement:o}),!1}if(s&&!n){if(!a)return this.displayFieldError({control:l,field:e,errorElement:o}),o&&this.validationMessages&&u&&(o.textContent=this.validationMessages[u]?this.validationMessages[u].empty:this.validationMessages.default.empty),!1}else{switch(t){case"email":if(l&&l.value&&!this.isValidEmail({email:l.value}))return o&&this.validationMessages&&(o.textContent=this.validationMessages.email.invalid),this.displayFieldError({control:l,field:e,errorElement:o}),!1;break;case"password_repeat":{const t=document.getElementById("password");if(t&&l?.value!=t?.value)return o&&this.validationMessages&&(o.textContent=this.validationMessages.password_repeat.unequal),this.displayFieldError({control:l,field:e,errorElement:o}),!1;break}case"cuil":case"cuit":if(l&&l.value&&!this.isValidCuitCuil({num:l.value}))return o&&this.validationMessages&&(o.textContent=this.validationMessages.cuil.invalid),this.displayFieldError({control:l,field:e,errorElement:o}),!1;break;case"url":if(l&&l.value&&!this.isValidUrl({urlString:l.value}))return o&&this.validationMessages&&(o.textContent=this.validationMessages.url.invalid),this.displayFieldError({control:l,field:e,errorElement:o}),!1;break;case"money":if(l){const t=l.dataset.currency?l.dataset.currency:"$";if(""==l.value||l.value==t)return o&&this.validationMessages&&(o.textContent=this.validationMessages.default.empty),this.displayFieldError({control:l,field:e,errorElement:o}),!1}break;case"single-checkbox":if(l&&l.hasOwnProperty("checked")){if(!l.checked)return this.displayFieldError({control:l,field:e,errorElement:o}),o&&this.validationMessages&&u&&(o.textContent=this.validationMessages[u]?this.validationMessages[u].empty:this.validationMessages.default.empty),!1}}for(const i of d)if(t===i)for(const i of this.customValidations[t])if(l&&!i.condition(l.value))return o&&(o.textContent=i.message||""),this.displayFieldError({control:l,field:e,errorElement:o}),!1}return!0}isValidEmail({email:e}){return/(?!.*\.{2})^([a-z\d!#$%&'*+\-/=?^_`{|}~\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]+(\.[a-z\d!#$%&'*+\-/=?^_`{|}~\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]+)*|"((([\t]*\r\n)?[\t]+)?([\x01-\x08\x0b\x0c\x0e-\x1f\x7f\x21\x23-\x5b\x5d-\x7e\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]|\\[\x01-\x09\x0b\x0c\x0d-\x7f\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]))*(([\t]*\r\n)?[\t]+)?")@(([a-z\d\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]|[a-z\d\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF][a-z\d\-._~\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]*[a-z\d\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])\.)+([a-z\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]|[a-z\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF][a-z\d\-._~\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]*[a-z\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])\.?$/i.test(e.toLowerCase())}isValidCuitCuil({num:e}){const t=[5,4,3,2,7,6,5,4,3,2];let i=e.toString().replace(/[^0-9]/g,"").split("");if(11!=i.length)return!1;i=i.map((e=>Number(e)));const s=Number(i.pop());let r=11-i.reduce(((e,i,s)=>e+Number(i)*t[s]),0)%11;return 10==r&&(r=9),11==r&&(r=0),r==s}isValidUrl({urlString:e}){return!!new RegExp("^(https?:\\/\\/)?((([a-z\\d]([a-z\\d-]*[a-z\\d])*)\\.)+[a-z]{2,}|((\\d{1,3}\\.){3}\\d{1,3}))(\\:\\d+)?(\\/[-a-z\\d%_.~+]*)*(\\?[;&a-z\\d%_.~+=-]*)?(\\#[-a-z\\d_]*)?$","i").test(e)}isValidFileSize({field:e}){let t=null;const i=e.querySelector(".form__control"),s=Number(e.dataset.minSize),r=Number(e.dataset.maxSize),a=e.querySelector(".form__error"),o=i&&i.files?i.files[0].name:null,l=i&&i.files?i.files[0].size:0,n=i&&i.files?parseFloat((l/1048576).toFixed(1)):0;n>r||!l?(i&&(i.value="",i.setAttribute("aria-invalid","true")),e.classList.remove("--has-file"),e.classList.add(this.classes.fieldHasError)):o?e.classList.add("--has-file"):e.classList.remove("--has-file"),this.validationMessages&&(l?n<s?t=this.validationMessages.file.min_size.replace("[[var]]",s+" MB"):n>r&&(t=this.validationMessages.file.max_size.replace("[[var]]",r+" MB")):t=this.validationMessages.file.empty),t&&a&&(a.textContent=t,this.displayFieldError({control:i,field:e,errorElement:a}))}displayFieldError({control:e,field:t,errorElement:i}){e&&(e.setAttribute("aria-invalid","true"),t.classList.add(this.classes.fieldHasError),this.classes.controlHasError&&e.classList.add(this.classes.controlHasError),i&&i.classList.remove(this.classes.hiddenErrorMessage))}clearControlError({control:e}){if(!e)return;e.setAttribute("aria-invalid","false"),this.classes.controlHasError&&e.classList.remove(this.classes.controlHasError);const i=t({element:e,className:"form__field"}),s=i?.querySelector(".form__error");i?.classList.remove(this.classes.fieldHasError),s&&(s.textContent="",s.classList.add(this.classes.hiddenErrorMessage))}realTimeValidations({form:e}){e.querySelectorAll('.form__field[data-type="file"]').forEach((e=>{const t=e.querySelector(".form__control");t?.addEventListener("change",(t=>{t.stopPropagation(),this.isValidFileSize({field:e})}))}))}throwError(e){throw new Error(`EgoForm Error: ${e}`)}}module.exports=class{constructor({element:e,classes:t,submitType:s,submitDataFormat:a,submitUrl:o,requestHeaders:l,fieldGroups:n,extraFields:u,serializerIgnoreList:d,customValidations:c,customValidationMessages:m,onStepChange:h,onValidationError:f,onSubmitStart:p,onSubmitEnd:F,onSuccess:g,onError:v,onBeforeSubmit:b,resetOnSuccess:E,resetLoaderOnSuccess:S,scrollOnError:y,preventSubmit:_,debug:L}){this.form=e,this.submitType=s||"fetch",this.submitDataFormat=a||"formData",this.requestHeaders=l||{},this.actionUrl=this.form.getAttribute("action")||o||null,this.submitMethod=this.form.getAttribute("method")||"POST",this.submitBtn=this.form.querySelector('button[type="submit"]')||null,this.classes={requiredField:"--required",requiredIfFilledField:"--required-if-filled",fieldHasError:"--has-error",hiddenErrorMessage:"--hidden",formSubmittingState:"--submitting",buttonSubmittingState:"--loading",clearFieldError:"--clear-error",validateOnBlur:"--validate-onblur",validateOnInput:"--validate-oninput",...t},this.isValid=!0,this.validator=new r({customValidations:c||{},classes:this.classes,customValidationMessages:m||null,debug:L??!1}),this.onValidationError=f??null,this.onStepChange=h??null,this.onSubmitStart=p??null,this.onSubmitEnd=F??null,this.onSuccess=g??null,this.onError=v??null,this.onBeforeSubmit=b??null,this.fieldGroups=n??null,this.extraFields=u??[],this.hasFile=!1,this.serializerIgnoreList=d||[],this.resetOnSuccess=E??!0,this.resetLoaderOnSuccess=S??!0,this.scrollOnError=y??!0;const x=this.form.querySelector(".form__step.--active");if(this.currentStep=x?Number(x.dataset.step):0,this.currentStepOptional=!1,this.stepChanging=!1,this.preventSubmit=_??!1,this.debug=L??!1,this.declareHandlers(),!this.actionUrl||""===this.actionUrl)throw new Error("The form doesn't have an action attribute or submitUrl wasn't provided.");this.debug&&i("initialized!")}submit(){this.preventSubmit||this.resumeSubmit()}resumeSubmit(){this.debug&&i(`submitting using ${this.submitType}!`),this.submittingForm({submitting:!0}),this.isValid=!0;const e=[];if(this.form.querySelectorAll(".form__field").forEach((t=>{if(!this.validator.validateField({field:t})){const i=t.querySelector(".form__control");i&&e.push(i.name),this.isValid=!1}})),this.isValid)if(this.debug)i("the form was submitted!"),i(JSON.parse(this.serializeData({returnFormData:!1})),"data"),setTimeout((()=>{this.submittingForm({submitting:!1,force:!0})}),1e3);else if("fetch"==this.submitType&&this.actionUrl){const e=this.serializeData({returnFormData:"json"===this.submitDataFormat});fetch(this.actionUrl,{method:this.submitMethod,headers:"json"===this.submitDataFormat?{"Content-Type":"application/json",...this.requestHeaders}:{...this.requestHeaders},body:e}).then((e=>{200===e.status||201===e.status?(this.resetOnSuccess&&this.reset(),"function"==typeof this.onSuccess&&this.onSuccess(e)):"function"==typeof this.onError&&this.onError(e)})).catch((e=>{"function"==typeof this.onError&&this.onError(e)})).finally((()=>{this.submittingForm({submitting:!1})}))}else{if("fetch"==this.submitType&&!this.actionUrl)throw new Error("Missing submit URL: When using 'fetch' submitType, specify either the form's 'action' or the 'submitUrl' option.");this.form.submit()}else if(this.submittingForm({submitting:!1,force:!0}),"function"==typeof this.onValidationError&&this.onValidationError(e,this),this.debug&&i(`this fields have failed validation: ${e.toString().replace(/,/g,", ")}.`),this.scrollOnError){const e=this.form.querySelector(`.form__field.${this.classes.fieldHasError}`);e&&!(({element:e})=>{const t=e.getBoundingClientRect();return t.top>=0&&t.left>=0&&t.bottom<=(window.innerHeight||document.documentElement.clientHeight)&&t.right<=(window.innerWidth||document.documentElement.clientWidth)})({element:e})&&e.scrollIntoView({behavior:"smooth"})}}submittingForm({submitting:e,force:t=!1}){let i=document.getElementsByTagName("body").item(0);e?(this.form.classList.add(this.classes.formSubmittingState),this.submitBtn&&this.submitBtn.classList.add(this.classes.buttonSubmittingState),i&&i.classList.add("--block"),"function"==typeof this.onSubmitStart&&this.onSubmitStart()):((this.resetLoaderOnSuccess||t)&&(this.form.classList.remove(this.classes.formSubmittingState),this.submitBtn&&this.submitBtn.classList.remove(this.classes.buttonSubmittingState),i&&i.classList.remove("--block")),"function"==typeof this.onSubmitEnd&&this.onSubmitEnd())}serializeData({returnFormData:e=!1}){const t=new FormData(this.form),i={};for(const e of t){const[t,s]=e;if(!this.serializerIgnoreList.includes(t)){if(s instanceof File&&!s.size&&!s.name)continue;i.hasOwnProperty(t)?i[t]=[...i[t],s]:i[t]=s}}if(this.fieldGroups)for(const e in this.fieldGroups)if(Object.hasOwnProperty.call(this.fieldGroups,e)){let s=[{}];for(const r of this.fieldGroups[e])s[0][r]=t.get(r),delete i[r];i[e]=s}if(this.extraFields.length)for(const e of this.extraFields)e.hasOwnProperty("name")&&e.hasOwnProperty("value")&&(t.append(e.name,e.value),i[e.name]=e.value);return e?t:JSON.stringify(i)}reset(){this.form.reset(),this.form.querySelectorAll(".form__field").forEach((e=>e.classList.remove("--filled",`${this.classes.fieldHasError}`))),this.form.querySelectorAll(".form__control").forEach((e=>e.setAttribute("aria-invalid","false"))),this.currentStep&&this.changeStep({step:1})}changeStep({step:t}){if(!this.stepChanging){const i=this.currentStepOptional?this.currentStep.toString()+"b":this.currentStep.toString(),s=this.form.querySelector('[data-step="'+i+'"]'),r=s?.querySelectorAll(`.${this.classes.requiredField}`),a="next"===t?this.currentStep+1:"prev"!==t||this.currentStepOptional?"optional"===t?this.currentStep+"b":this.currentStep:this.currentStep-1,o=this.form.querySelector('[data-step="'+a+'"]');(this.currentStep!==a||this.currentStepOptional)&&(this.stepChanging=!0,this.isValid=!0,setTimeout((()=>{!r||"next"!==t&&"optional"!==t||r.forEach((e=>{this.validator.validateField({field:e})||(this.isValid=!1)})),s&&o&&this.isValid?e({element:s,enter:!1,time:200,displayType:"flex",callback:()=>{s.classList.remove("--active"),e({element:o,enter:!0,time:200,displayType:"flex",callback:()=>{o.classList.add("--active"),this.stepChanging=!1,this.currentStepOptional="optional"===t,this.currentStep=parseInt(a),"function"==typeof this.onStepChange&&this.onStepChange(i.toString(),a.toString())}})}}):this.stepChanging=!1}),50))}}nextStep(){this.changeStep({step:"next"})}optionalStep(){this.changeStep({step:"optional"})}prevStep(){this.changeStep({step:"prev"})}isControlFilled({control:e}){let t=e.parentElement;t&&(""!==e.value?t.classList.add("--filled"):t.classList.remove("--filled"))}filterNumber({value:e,ignoreList:t=[]}){const i=t.join(""),s=new RegExp("[^"+i+"0-9]","g");return e.toString().replace(s,"")}filterFormattedQuantity({num:e,thousands:t=".",decimals:i=",",decimalSteps:s}){const r=i?e.toString().split(i):[e.toString()],a=new RegExp(/\B(?=(\d{3})+(?!\d))/g),o=r[0].replace(a,t);let l="";return l=s&&s>0&&r[1]&&r[1].length?o+i+r[1].slice(0,s):o,l}filterMoneyAmount({num:e,currency:t="$",thousands:i=".",decimals:s=",",decimalSteps:r}){if(!e||e==t)return"";let a=this.filterNumber({value:e,ignoreList:[s||""]});return a=this.filterFormattedQuantity({num:a,thousands:i,decimals:s,decimalSteps:r}),`${t} ${a}`}filterPhoneNumber({number:e}){let t=new RegExp(/[^\d+\-() ]*/g);return e.replace(t,"")}togglePasswordVisibility({btn:e}){this.debug&&i("Password visibility toggled!");const t=e?.parentElement?.querySelector(".form__control");if(t){const i="password"===t.getAttribute("type")?"text":"password";t.setAttribute("type",i),e.classList.toggle("--hide")}}declareHandlers(){const e=this;if(!this.submitBtn)throw new Error(`There's no submit button in this form "${this.form.id}".`);this.submitBtn.addEventListener("click",(function(t){t.preventDefault(),"function"==typeof e.onBeforeSubmit&&e.onBeforeSubmit(e),e.submit()})),this.validator.realTimeValidations({form:this.form}),this.form.querySelectorAll(`.form__field.${this.classes.validateOnBlur}`).forEach((e=>{e.querySelector(".form__control")?.addEventListener("blur",(()=>{this.validator.validateField({field:e})||(this.isValid=!1)}))})),this.form.querySelectorAll(`.form__field.${this.classes.validateOnInput}`).forEach((e=>{const t=e.querySelector(".form__control");t?.addEventListener("input",(()=>{this.validator.validateField({field:e})&&this.validator.clearControlError({control:t})}))})),this.form.querySelectorAll(".form__next-step").forEach((t=>{t.addEventListener("click",e.nextStep.bind(e))})),this.form.querySelectorAll(".form__optional-step").forEach((t=>{t.addEventListener("click",e.optionalStep.bind(e))})),this.form.querySelectorAll(".form__prev-step").forEach((t=>{t.addEventListener("click",e.prevStep.bind(e))})),this.form.querySelectorAll(".form__control").forEach((e=>{this.isControlFilled({control:e}),e.addEventListener("keyup",(()=>{this.isControlFilled({control:e})})),e.addEventListener("change",(()=>{this.isControlFilled({control:e})}))})),this.form.querySelectorAll(".form__control").forEach((e=>{e.addEventListener("focus",(()=>{this.validator.clearControlError({control:e})}))})),this.form.querySelectorAll("."+this.classes.clearFieldError).forEach((i=>{i.addEventListener("click",(()=>{const s=t({element:i,className:"form__field"});s&&e.validator.clearControlError({control:s.querySelector(".form__control")})}))})),this.form.querySelectorAll(".form__field.--number input").forEach((e=>{const i=this,s=t({element:e,className:"form__field"}),r=s&&s.dataset.thousandsSeparator?s.dataset.thousandsSeparator:"",a=s&&s.dataset.decimalSeparator?s.dataset.decimalSeparator:"",o=s&&s.dataset.decimals?s.dataset.decimals:"";function l(){e.value=i.filterNumber({value:e.value,ignoreList:[a]})}e.addEventListener("focus",l),e.addEventListener("input",l),e.addEventListener("paste",l),e.addEventListener("blur",(()=>{e.value=r?i.filterFormattedQuantity({num:e.value,thousands:r,decimals:a,decimalSteps:parseInt(o)}):i.filterNumber({value:e.value})}))})),this.form.querySelectorAll(".form__field.--money-amount input").forEach((e=>{const i=this,s=t({element:e,className:"form__field"}),r=s&&s.dataset.currency?s.dataset.currency:"$",a=s&&s.dataset.thousandsSeparator?s.dataset.thousandsSeparator:".",o=s&&s.dataset.decimalSeparator?s.dataset.decimalSeparator:"",l=s&&s.dataset.decimals?s.dataset.decimals:"";function n(){e.value=i.filterNumber({value:e.value,ignoreList:[o]})}e.addEventListener("focus",n),e.addEventListener("input",n),e.addEventListener("paste",n),e.addEventListener("blur",(()=>{e.value=this.filterMoneyAmount({num:e.value,currency:r,thousands:a,decimals:o,decimalSteps:parseInt(l)})}))})),this.form.querySelectorAll(".form__field.--phone input").forEach((e=>{e.addEventListener("input",(()=>{e.value=this.filterPhoneNumber({number:e.value})})),e.addEventListener("paste",(()=>{e.value=this.filterPhoneNumber({number:e.value})}))})),this.form.querySelectorAll(".form__toggle-password-visibility").forEach((e=>{e.addEventListener("click",(()=>this.togglePasswordVisibility({btn:e})))}))}};
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
export default class EgoForm implements EgoFormInterface {
|
|
2
|
+
form: HTMLFormElement;
|
|
3
|
+
submitBtn: HTMLButtonElement | null;
|
|
4
|
+
classes: EgoFormCSSClassess;
|
|
5
|
+
submitType: EgoFormSubmitType;
|
|
6
|
+
submitMethod: 'GET' | 'POST';
|
|
7
|
+
submitDataFormat: EgoFormDataFormat;
|
|
8
|
+
actionUrl: RequestInfo | URL | null;
|
|
9
|
+
requestHeaders: Record<string, string>;
|
|
10
|
+
fieldGroups: Record<string, string[]> | null;
|
|
11
|
+
extraFields: {
|
|
12
|
+
name: string;
|
|
13
|
+
value: string;
|
|
14
|
+
}[];
|
|
15
|
+
serializerIgnoreList: string[];
|
|
16
|
+
validator: any;
|
|
17
|
+
onStepChange: Function | null;
|
|
18
|
+
onValidationError: Function | null;
|
|
19
|
+
onSubmitStart: Function | null;
|
|
20
|
+
onSubmitEnd: Function | null;
|
|
21
|
+
onSuccess: Function | null;
|
|
22
|
+
onError: Function | null;
|
|
23
|
+
onBeforeSubmit: Function | null;
|
|
24
|
+
currentStep: number;
|
|
25
|
+
currentStepOptional: boolean;
|
|
26
|
+
stepChanging: boolean;
|
|
27
|
+
isValid: boolean;
|
|
28
|
+
hasFile: boolean;
|
|
29
|
+
resetOnSuccess: boolean;
|
|
30
|
+
resetLoaderOnSuccess: boolean;
|
|
31
|
+
scrollOnError: boolean;
|
|
32
|
+
preventSubmit: boolean;
|
|
33
|
+
debug: boolean;
|
|
34
|
+
constructor({ element, classes, submitType, submitDataFormat, submitUrl, requestHeaders, fieldGroups, extraFields, serializerIgnoreList, customValidations, customValidationMessages, onStepChange, onValidationError, onSubmitStart, onSubmitEnd, onSuccess, onError, onBeforeSubmit, resetOnSuccess, resetLoaderOnSuccess, scrollOnError, preventSubmit, debug }: EgoFormOptions);
|
|
35
|
+
submit(): void;
|
|
36
|
+
resumeSubmit(): void;
|
|
37
|
+
submittingForm({ submitting, force }: {
|
|
38
|
+
submitting: boolean;
|
|
39
|
+
force?: boolean;
|
|
40
|
+
}): void;
|
|
41
|
+
serializeData({ returnFormData }: {
|
|
42
|
+
returnFormData?: boolean;
|
|
43
|
+
}): BodyInit | FormData;
|
|
44
|
+
reset(): void;
|
|
45
|
+
changeStep({ step }: {
|
|
46
|
+
step: 'next' | 'prev' | 'optional' | number;
|
|
47
|
+
}): void;
|
|
48
|
+
nextStep(): void;
|
|
49
|
+
optionalStep(): void;
|
|
50
|
+
prevStep(): void;
|
|
51
|
+
isControlFilled({ control }: {
|
|
52
|
+
control: EgoFormControl;
|
|
53
|
+
}): void;
|
|
54
|
+
filterNumber({ value, ignoreList }: {
|
|
55
|
+
value: string | number;
|
|
56
|
+
ignoreList?: string[];
|
|
57
|
+
}): string;
|
|
58
|
+
filterFormattedQuantity({ num, thousands, decimals, decimalSteps }: {
|
|
59
|
+
num: string | number;
|
|
60
|
+
thousands?: string;
|
|
61
|
+
decimals?: string;
|
|
62
|
+
decimalSteps?: number;
|
|
63
|
+
}): string;
|
|
64
|
+
filterMoneyAmount({ num, currency, thousands, decimals, decimalSteps }: {
|
|
65
|
+
num: string | number;
|
|
66
|
+
currency?: string;
|
|
67
|
+
thousands?: string;
|
|
68
|
+
decimals?: string;
|
|
69
|
+
decimalSteps?: number;
|
|
70
|
+
}): string;
|
|
71
|
+
filterPhoneNumber({ number }: {
|
|
72
|
+
number: string;
|
|
73
|
+
}): string;
|
|
74
|
+
togglePasswordVisibility({ btn }: {
|
|
75
|
+
btn: HTMLElement;
|
|
76
|
+
}): void;
|
|
77
|
+
declareHandlers(): void;
|
|
78
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
const t=({element:t,enter:e,time:s,displayType:i,callback:r})=>{t.style.opacity=e?0:1,e&&(t.style.display=i||"block");let a=+new Date;!function i(){t.style.opacity=e?+t.style.opacity+(new Date-a)/(s||200):+t.style.opacity-(new Date-a)/(s||200),a=+new Date,e&&+t.style.opacity<1||!e&&+t.style.opacity>0?window.requestAnimationFrame&&requestAnimationFrame(i)||setTimeout(i,16):(e||(t.style.display="none"),r&&"function"==typeof r&&r())}()},e=({element:t,className:e})=>{if(!t)return!1;for(;t&&!t.classList.contains(e);)t=!!t.parentElement&&t.parentElement;return t};function s(t,e="log"){"log"==e?console.log("::EgoForm:: "+t):"data"==e&&(console.log("::EgoForm:: DATA"),console.table(t))}const i={default:{empty:"Campo requerido.",invalid:"Campo nó válido.",minLength:"Debe tener al menos [[var]] caracteres.",maxLength:"Debe tener como máximo [[var]] caracteres."},email:{empty:"El email es requerido.",invalid:"El email no es válido."},message:{empty:"El mensaje es requerido."},password:{empty:"Ingrese una contraseña."},password_repeat:{empty:"Debe repetir la contraseña.",unequal:"Las contraseñas no coindicen."},cuil:{empty:"Campo requerido.",min:"El número debe tener exactamente 11 dígitos.",invalid:"El número ingresado no es válido."},url:{empty:"Campo requerido.",invalid:"URL no válida."},file:{empty:"Campo requerido",min_size:"El tamaño mínimo es [[var]]",max_size:"El tamaño máximo es [[var]]"}};class r{constructor({customValidations:t,classes:e,customValidationMessages:s,debug:r}){this.customValidations=t,this.validationMessages={...i,...s}||i,this.classes=e,this.debug=r}validateField(t){const e=t.dataset.type,i=["radio","checkbox"].includes(e),r=t.classList.contains(this.classes.requiredField),a=t.classList.contains(this.classes.requiredIfFilledField),o=t.querySelector(".form__error"),l=t.querySelector(".form__control"),n=i?t.querySelector(".form__control:checked"):null,u=l?l.getAttribute("name"):"",d=Object.keys(this.customValidations),c=t.dataset.minLength?parseInt(t.dataset.minLength):null,h=t.dataset.maxLength?parseInt(t.dataset.maxLength):null;if(l||this.throwError("control not found."),u||this.throwError("control name not found."),this.debug&&s(`validating field "${u}"`),r&&!l.value)return o&&(o.textContent=this.validationMessages[u]?this.validationMessages[u].empty:this.validationMessages.default.empty),this.displayFieldError(l,t,o),!1;if(l.value){if(c&&l.value.length<c)return o&&(o.textContent=this.validationMessages.default.minLength.replace("[[var]]",c)),this.displayFieldError(l,t,o),!1;if(h&&l.value.length>h)return o&&(o.textContent=this.validationMessages.default.maxLength.replace("[[var]]",h)),this.displayFieldError(l,t,o),!1}if(i&&!n){if(!a)return this.displayFieldError(l,t,o),o&&(o.textContent=this.validationMessages[u]?this.validationMessages[u].empty:this.validationMessages.default.empty),!1}else{switch(e){case"email":if(l.value&&!this.isValidEmail(l.value))return o&&(o.textContent=this.validationMessages.email.invalid),this.displayFieldError(l,t,o),!1;break;case"password_repeat":{const e=document.getElementById("password");if(e&&l.value!=e.value)return o&&(o.textContent=this.validationMessages.password_repeat.unequal),this.displayFieldError(l,t,o),!1;break}case"cuil":case"cuit":if(l.value&&!this.isValidCuitCuil(l.value))return o&&(o.textContent=this.validationMessages.cuil.invalid),this.displayFieldError(l,t,o),!1;break;case"url":if(l.value&&!this.isValidUrl(l.value))return o&&(o.textContent=this.validationMessages.url.invalid),this.displayFieldError(l,t,o),!1;break;case"money":const e=l.dataset.currency?l.dataset.currency:"$";if(""==l.value||l.value==e)return o&&(o.textContent=this.validationMessages.default.empty),this.displayFieldError(l,t,o),!1;break;case"single-checkbox":if(!l.checked)return this.displayFieldError(l,t,o),o&&(o.textContent=this.validationMessages[u]?this.validationMessages[u].empty:this.validationMessages.default.empty),!1}for(const s of d)if(e===s)for(const s of this.customValidations[e])if(!s.condition(l.value))return o&&(o.textContent=s.message||""),this.displayFieldError(l,t,o),!1}return!0}isValidEmail(t){return/(?!.*\.{2})^([a-z\d!#$%&'*+\-/=?^_`{|}~\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]+(\.[a-z\d!#$%&'*+\-/=?^_`{|}~\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]+)*|"((([\t]*\r\n)?[\t]+)?([\x01-\x08\x0b\x0c\x0e-\x1f\x7f\x21\x23-\x5b\x5d-\x7e\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]|\\[\x01-\x09\x0b\x0c\x0d-\x7f\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]))*(([\t]*\r\n)?[\t]+)?")@(([a-z\d\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]|[a-z\d\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF][a-z\d\-._~\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]*[a-z\d\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])\.)+([a-z\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]|[a-z\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF][a-z\d\-._~\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]*[a-z\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])\.?$/i.test(t.toLowerCase())}isValidCuitCuil(t){const e=[5,4,3,2,7,6,5,4,3,2];let s=t.toString().replace(/[^0-9]/g,"").split("");if(11!=s.length)return!1;s=s.map((t=>parseInt(t)));const i=parseInt(s.pop());let r=11-s.reduce(((t,s,i)=>t+s*e[i]),0)%11;return 10==r&&(r=9),11==r&&(r=0),r==i}isValidUrl(t){return!!new RegExp("^(https?:\\/\\/)?((([a-z\\d]([a-z\\d-]*[a-z\\d])*)\\.)+[a-z]{2,}|((\\d{1,3}\\.){3}\\d{1,3}))(\\:\\d+)?(\\/[-a-z\\d%_.~+]*)*(\\?[;&a-z\\d%_.~+=-]*)?(\\#[-a-z\\d_]*)?$","i").test(t)}isValidFileSize(t){let e=null;const s=t.querySelector(".form__control"),i=parseFloat(t.dataset.minSize),r=parseFloat(t.dataset.maxSize),a=t.querySelector(".form__error"),o=s.files[0].name,l=s.files[0].size,n=parseFloat(s.files.length?(l/1048576).toFixed(1):0);n>r||!l?(s.value="",t.classList.remove("--has-file"),s.setAttribute("aria-invalid","true"),t.classList.add(this.classes.fieldHasError)):o?(this.hasFile=!0,t.classList.add("--has-file")):(this.hasFile=!1,t.classList.remove("--has-file")),l?n<i?e=this.validationMessages.file.min_size.replace("[[var]]",i+" MB"):n>r&&(e=this.validationMessages.file.max_size.replace("[[var]]",r+" MB")):e=this.validationMessages.file.empty,e&&(a.textContent=e,this.displayFieldError(s,t,a))}displayFieldError(t,e,s){t.setAttribute("aria-invalid","true"),e.classList.add(this.classes.fieldHasError),this.classes.controlHasError&&t.classList.add(this.classes.controlHasError),s&&s.classList.remove(this.classes.hiddenErrorMessage)}clearControlError(t){t.setAttribute("aria-invalid","false"),this.classes.controlHasError&&t.classList.remove(this.classes.controlHasError);const s=e({element:t,className:"form__field"}),i=s.querySelector(".form__error");s.classList.remove(this.classes.fieldHasError),i&&(i.textContent="",i.classList.add(this.classes.hiddenErrorMessage))}realTimeValidations(t){t.querySelectorAll('.form__field[data-type="file"]').forEach((t=>{const e=t.querySelector(".form__control");e?.addEventListener("change",(e=>{e.stopPropagation(),this.isValidFileSize(t)}))}))}throwError(t){throw new Error(`EgoForm Error: ${t}`)}}class a{constructor({element:t,classes:e,submitType:i,submitDataFormat:a,submitUrl:o,requestHeaders:l,fieldGroups:n,extraFields:u,serializerIgnoreList:d,customValidations:c,customValidationMessages:h,onStepChange:m,onValidationError:f,onSubmitStart:p,onSubmitEnd:F,onSuccess:v,onError:g,onBeforeSubmit:y,resetOnSuccess:E,resetLoaderOnSuccess:b,scrollOnError:S,preventSubmit:_,debug:L}){if(this.form=t,this.submitType=i||"fetch",this.submitDataFormat=a||"formData",this.requestHeaders=l||{},this.actionUrl=this.form.getAttribute("action")||o||null,this.submitMethod=this.form.getAttribute("method")||"POST",this.submitBtn=this.form.querySelector('button[type="submit"]')||null,this.classes={requiredField:"--required",requiredIfFilledField:"--required-if-filled",fieldHasError:"--has-error",controlHasError:!1,hiddenErrorMessage:"--hidden",formSubmittingState:"--submitting",buttonSubmittingState:"--loading",clearFieldError:"--clear-error",validateOnBlur:"--validate-onblur",validateOnInput:"--validate-oninput",...e},this.isValid=!0,this.validator=new r({customValidations:c||{},classes:this.classes,customValidationMessages:h||null,debug:L}),this.onValidationError=f??!1,this.onStepChange=m??!1,this.onSubmitStart=p??!1,this.onSubmitEnd=F??!1,this.onSuccess=v??!1,this.onError=g??!1,this.onBeforeSubmit=y??!1,this.fieldGroups=n??!1,this.extraFields=u??[],this.hasFile=!1,this.serializerIgnoreList=d||[],this.resetOnSuccess=E??!0,this.resetLoaderOnSuccess=b??!0,this.scrollOnError=S??!0,this.currentStep=this.form.querySelector(".form__step")?parseInt(this.form.querySelector(".form__step.--active").dataset.step):0,this.currentStepOptional=!1,this.stepChanging=!1,this.preventSubmit=_??!1,this.debug=L??!1,this.declareHandlers(),!this.actionUrl||""===this.actionUrl)throw new Error("The form doesn't have an action attribute or submitUrl wasn't provided.");this.debug&&s("initialized!")}submit(){this.preventSubmit||this.resumeSubmit()}resumeSubmit(){this.debug&&s(`submitting using ${this.submitType}!`),this.submittingForm(!0),this.isValid=!0;const t=[];if(this.form.querySelectorAll(".form__field").forEach((e=>{this.validator.validateField(e)||(t.push(e.querySelector(".form__control")?.name),this.isValid=!1)})),this.isValid)this.debug?(s("the form was submitted!"),s(this.serializeData(),"data"),setTimeout((()=>{this.submittingForm(!1,!0)}),1e3)):"fetch"==this.submitType?fetch(this.actionUrl,{method:this.submitMethod,headers:"json"===this.submitDataFormat?{"Content-Type":"application/json",...this.requestHeaders}:{...this.requestHeaders},body:"json"===this.submitDataFormat?JSON.stringify(this.serializeData()):this.serializeData(!0)}).then((t=>{200===t.status||201===t.status?(this.resetOnSuccess&&this.reset(),"function"==typeof this.onSuccess&&this.onSuccess(t)):"function"==typeof this.onError&&this.onError(t)})).catch((t=>{"function"==typeof this.onError&&this.onError(t)})).finally((()=>{this.submittingForm(!1)})):this.form.submit();else if(this.submittingForm(!1,!0),"function"==typeof this.onValidationError&&this.onValidationError(t),this.debug&&s(`this fields have failed validation: ${t.toString().replace(/,/g,", ")}.`),this.scrollOnError){const t=this.form.querySelector(`.form__field.${this.classes.fieldHasError}`);(t=>{const e=t.getBoundingClientRect();return e.top>=0&&e.left>=0&&e.bottom<=(window.innerHeight||document.documentElement.clientHeight)&&e.right<=(window.innerWidth||document.documentElement.clientWidth)})(t)||t.scrollIntoView({behavior:"smooth"})}}submittingForm(t,e=!1){let s=document.getElementsByTagName("body").item(0);t?(this.form.classList.add(this.classes.formSubmittingState),this.submitBtn.classList.add(this.classes.buttonSubmittingState),s.classList.add("--block"),"function"==typeof this.onSubmitStart&&this.onSubmitStart()):((this.resetLoaderOnSuccess||e)&&(this.form.classList.remove(this.classes.formSubmittingState),this.submitBtn.classList.remove(this.classes.buttonSubmittingState),s.classList.remove("--block")),"function"==typeof this.onSubmitEnd&&this.onSubmitEnd())}serializeData(t=!1){const e=new FormData(this.form),s={};for(const t of e){const[e,i]=t;if(!this.serializerIgnoreList.includes(e)){if(i instanceof File&&!i.size&&!i.name)continue;s.hasOwnProperty(e)?s[e]=[...s[e],i]:s[e]=i}}if(this.fieldGroups)for(const t in this.fieldGroups)if(Object.hasOwnProperty.call(this.fieldGroups,t)){let i=[{}];for(const r of this.fieldGroups[t])i[0][r]=e.get(r),delete s[r];s[t]=i}if(this.extraFields.length)for(const t of this.extraFields)t.hasOwnProperty("name")&&t.hasOwnProperty("value")&&(s[t.name]=t.value);return t?e:s}reset(){this.form.reset(),this.form.querySelectorAll(".form__field").forEach((t=>t.classList.remove("--filled",`${this.classes.fieldHasError}`))),this.form.querySelectorAll(".form__control").forEach((t=>t.setAttribute("aria-invalid","false"))),this.currentStep&&this.changeStep(1)}changeStep(e){if(!this.stepChanging){const s=this.currentStepOptional?this.currentStep+"b":this.currentStep,i=this.form.querySelector('[data-step="'+s+'"]'),r=i.querySelectorAll(`.${this.classes.requiredField}`),a="next"===e?this.currentStep+1:"prev"!==e||this.currentStepOptional?"optional"===e?this.currentStep+"b":this.currentStep:this.currentStep-1,o=this.form.querySelector('[data-step="'+a+'"]');(this.currentStep!==a||this.currentStepOptional)&&(this.stepChanging=!0,this.isValid=!0,setTimeout((()=>{!r||"next"!==e&&"optional"!==e||r.forEach((t=>{this.validator.validateField(t)||(this.isValid=!1)})),i&&o&&this.isValid?t({element:i,enter:!1,time:200,displayType:"flex",callback:()=>{i.classList.remove("--active"),t({element:o,enter:!0,time:200,displayType:"flex",callback:()=>{o.classList.add("--active"),this.stepChanging=!1,this.currentStepOptional="optional"===e,this.currentStep=parseInt(a),"function"==typeof this.onStepChange&&this.onStepChange(s,a)}})}}):this.stepChanging=!1}),50))}}nextStep(){this.changeStep("next")}optionalStep(){this.changeStep("optional")}prevStep(){this.changeStep("prev")}isControlFilled(t){let e=t.parentElement;""!==t.value?e.classList.add("--filled"):e.classList.remove("--filled")}filterNumber(t,e=[]){const s=e.join(""),i=new RegExp("[^"+s+"0-9]","g");return t.toString().replace(i,"")}filterFormattedQuantity(t,e=".",s=",",i){const r=s?t.toString().split(s):[t],a=r[0].replace(/\B(?=(\d{3})+(?!\d))/g,e);let o="";return o=i>0&&r[1]&&r[1].length?a+s+r[1].slice(0,i):a,o}filterMoneyAmount(t,e="$",s=".",i=",",r){if(!t||t==e)return"";let a=this.filterNumber(t,[i||""]);return a=this.filterFormattedQuantity(a,s,i,r),`${e} ${a}`}filterPhoneNumber(t){return t.replace(/[^\d+\-() ]*/g,"")}togglePasswordVisibility(t){this.debug&&s("Password visibility toggled!");const e=t.parentElement.querySelector(".form__control");if(e){const s="password"===e.getAttribute("type")?"text":"password";e.setAttribute("type",s),t.classList.toggle("--hide")}}declareHandlers(){const t=this;if(!this.submitBtn)throw new Error(`There's no submit button in this form "${this.form.id}".`);this.submitBtn.addEventListener("click",(function(e){e.preventDefault(),"function"==typeof t.onBeforeSubmit&&t.onBeforeSubmit(),t.submit()})),this.validator.realTimeValidations(this.form),this.form.querySelectorAll(`.form__field.${this.classes.validateOnBlur}`).forEach((t=>{t.querySelector(".form__control").addEventListener("blur",(()=>{this.validator.validateField(t)||(this.isValid=!1)}))})),this.form.querySelectorAll(`.form__field.${this.classes.validateOnInput}`).forEach((t=>{const e=t.querySelector(".form__control");e.addEventListener("input",(()=>{this.validator.validateField(t)&&this.validator.clearControlError(e)}))})),this.form.querySelectorAll(".form__next-step").forEach((e=>{e.addEventListener("click",t.nextStep.bind(t))})),this.form.querySelectorAll(".form__optional-step").forEach((e=>{e.addEventListener("click",t.optionalStep.bind(t))})),this.form.querySelectorAll(".form__prev-step").forEach((e=>{e.addEventListener("click",t.prevStep.bind(t))})),this.form.querySelectorAll(".form__control").forEach((t=>{this.isControlFilled(t),t.addEventListener("keyup",(()=>{this.isControlFilled(t)})),t.addEventListener("change",(()=>{this.isControlFilled(t)}))})),this.form.querySelectorAll(".form__control").forEach((t=>{t.addEventListener("focus",(()=>{this.validator.clearControlError(t)}))})),this.form.querySelectorAll("."+this.classes.clearFieldError).forEach((s=>{s.addEventListener("click",(()=>{const i=e({element:s,className:"form__field"});t.validator.clearControlError(i.querySelector(".form__control"))}))})),this.form.querySelectorAll(".form__field.--number input").forEach((t=>{const s=this,i=e({element:t,className:"form__field"}),r=i&&i.dataset.thousandsSeparator?i.dataset.thousandsSeparator:null,a=i&&i.dataset.decimalSeparator?i.dataset.decimalSeparator:"",o=i&&i.dataset.decimals?i.dataset.decimals:"";function l(){t.value=s.filterNumber(t.value,[a])}t.addEventListener("focus",l),t.addEventListener("input",l),t.addEventListener("paste",l),t.addEventListener("blur",(()=>{t.value=r?s.filterFormattedQuantity(t.value,r,a,parseInt(o)):s.filterNumber(t.value)}))})),this.form.querySelectorAll(".form__field.--money-amount input").forEach((t=>{const s=this,i=e({element:t,className:"form__field"}),r=i&&i.dataset.currency?i.dataset.currency:"$",a=i&&i.dataset.thousandsSeparator?i.dataset.thousandsSeparator:".",o=i&&i.dataset.decimalSeparator?i.dataset.decimalSeparator:"",l=i&&i.dataset.decimals?i.dataset.decimals:"";function n(){t.value=s.filterNumber(t.value,[o])}t.addEventListener("focus",n),t.addEventListener("input",n),t.addEventListener("paste",n),t.addEventListener("blur",(()=>{t.value=this.filterMoneyAmount(t.value,r,a,o,parseInt(l))}))})),this.form.querySelectorAll(".form__field.--phone input").forEach((t=>{t.addEventListener("input",(()=>{t.value=this.filterPhoneNumber(t.value)})),t.addEventListener("paste",(()=>{t.value=this.filterPhoneNumber(t.value)}))})),this.form.querySelectorAll(".form__toggle-password-visibility").forEach((t=>{t.addEventListener("click",this.togglePasswordVisibility(t))}))}}export{a as default};
|
|
1
|
+
const e=({element:e,enter:t,time:i,displayType:s,callback:r})=>{e.style.opacity=t?"0":"1",t&&(e.style.display=s);let a=(new Date).getTime();!function s(){e.style.opacity=t?(+e.style.opacity+((new Date).getTime()-a)/i).toString():(+e.style.opacity-((new Date).getTime()-a)/i).toString(),a=(new Date).getTime(),t&&Number(e.style.opacity)<1||!t&&Number(e.style.opacity)>0?requestAnimationFrame(s)||setTimeout(s,16):(t||(e.style.display="none"),r&&"function"==typeof r&&r())}()},t=({element:e,className:t})=>{if(!e)return null;for(;e&&!e.classList.contains(t);)e=e.parentElement?e.parentElement:null;return e};function i(e,t="log"){"log"==t?console.log("::EgoForm:: "+e):"data"==t&&(console.log("::EgoForm:: DATA"),console.table(e))}const s={default:{empty:"Campo requerido.",invalid:"Campo nó válido.",minLength:"Debe tener al menos [[var]] caracteres.",maxLength:"Debe tener como máximo [[var]] caracteres."},email:{empty:"El email es requerido.",invalid:"El email no es válido."},message:{empty:"El mensaje es requerido."},password:{empty:"Ingrese una contraseña."},password_repeat:{empty:"Debe repetir la contraseña.",unequal:"Las contraseñas no coindicen."},cuil:{empty:"Campo requerido.",min:"El número debe tener exactamente 11 dígitos.",invalid:"El número ingresado no es válido."},url:{empty:"Campo requerido.",invalid:"URL no válida."},file:{empty:"Campo requerido",min_size:"El tamaño mínimo es [[var]]",max_size:"El tamaño máximo es [[var]]"}};class r{constructor({customValidations:e,classes:t,customValidationMessages:i,debug:r}){this.customValidations=e,this.validationMessages={...s,...i},this.classes=t,this.debug=r}validateField({field:e}){const t=e.dataset.type||"",s=["radio","checkbox"].includes(t),r=e.classList.contains(this.classes.requiredField),a=e.classList.contains(this.classes.requiredIfFilledField),o=e.querySelector(".form__error"),l=e.querySelector(".form__control"),n=s?e.querySelector(".form__control:checked"):null,u=l?l.getAttribute("name"):null,d=Object.keys(this.customValidations),c=e.dataset.minLength?Number(e.dataset.minLength):null,m=e.dataset.maxLength?Number(e.dataset.maxLength):null;if(l||this.throwError("control not found."),u||this.throwError("control name not found."),this.debug&&i(`validating field "${u}"`),r&&!l?.value)return o&&this.validationMessages&&u&&(o.textContent=this.validationMessages[u]?this.validationMessages[u].empty:this.validationMessages.default.empty),this.displayFieldError({control:l,field:e,errorElement:o}),!1;if(l?.value){if(c&&l.value.length<c)return o&&this.validationMessages&&(o.textContent=this.validationMessages.default.minLength.replace("[[var]]",c.toString())),this.displayFieldError({control:l,field:e,errorElement:o}),!1;if(m&&l.value.length>m)return o&&this.validationMessages&&(o.textContent=this.validationMessages.default.maxLength.replace("[[var]]",m.toString())),this.displayFieldError({control:l,field:e,errorElement:o}),!1}if(s&&!n){if(!a)return this.displayFieldError({control:l,field:e,errorElement:o}),o&&this.validationMessages&&u&&(o.textContent=this.validationMessages[u]?this.validationMessages[u].empty:this.validationMessages.default.empty),!1}else{switch(t){case"email":if(l&&l.value&&!this.isValidEmail({email:l.value}))return o&&this.validationMessages&&(o.textContent=this.validationMessages.email.invalid),this.displayFieldError({control:l,field:e,errorElement:o}),!1;break;case"password_repeat":{const t=document.getElementById("password");if(t&&l?.value!=t?.value)return o&&this.validationMessages&&(o.textContent=this.validationMessages.password_repeat.unequal),this.displayFieldError({control:l,field:e,errorElement:o}),!1;break}case"cuil":case"cuit":if(l&&l.value&&!this.isValidCuitCuil({num:l.value}))return o&&this.validationMessages&&(o.textContent=this.validationMessages.cuil.invalid),this.displayFieldError({control:l,field:e,errorElement:o}),!1;break;case"url":if(l&&l.value&&!this.isValidUrl({urlString:l.value}))return o&&this.validationMessages&&(o.textContent=this.validationMessages.url.invalid),this.displayFieldError({control:l,field:e,errorElement:o}),!1;break;case"money":if(l){const t=l.dataset.currency?l.dataset.currency:"$";if(""==l.value||l.value==t)return o&&this.validationMessages&&(o.textContent=this.validationMessages.default.empty),this.displayFieldError({control:l,field:e,errorElement:o}),!1}break;case"single-checkbox":if(l&&l.hasOwnProperty("checked")){if(!l.checked)return this.displayFieldError({control:l,field:e,errorElement:o}),o&&this.validationMessages&&u&&(o.textContent=this.validationMessages[u]?this.validationMessages[u].empty:this.validationMessages.default.empty),!1}}for(const i of d)if(t===i)for(const i of this.customValidations[t])if(l&&!i.condition(l.value))return o&&(o.textContent=i.message||""),this.displayFieldError({control:l,field:e,errorElement:o}),!1}return!0}isValidEmail({email:e}){return/(?!.*\.{2})^([a-z\d!#$%&'*+\-/=?^_`{|}~\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]+(\.[a-z\d!#$%&'*+\-/=?^_`{|}~\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]+)*|"((([\t]*\r\n)?[\t]+)?([\x01-\x08\x0b\x0c\x0e-\x1f\x7f\x21\x23-\x5b\x5d-\x7e\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]|\\[\x01-\x09\x0b\x0c\x0d-\x7f\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]))*(([\t]*\r\n)?[\t]+)?")@(([a-z\d\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]|[a-z\d\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF][a-z\d\-._~\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]*[a-z\d\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])\.)+([a-z\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]|[a-z\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF][a-z\d\-._~\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]*[a-z\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])\.?$/i.test(e.toLowerCase())}isValidCuitCuil({num:e}){const t=[5,4,3,2,7,6,5,4,3,2];let i=e.toString().replace(/[^0-9]/g,"").split("");if(11!=i.length)return!1;i=i.map((e=>Number(e)));const s=Number(i.pop());let r=11-i.reduce(((e,i,s)=>e+Number(i)*t[s]),0)%11;return 10==r&&(r=9),11==r&&(r=0),r==s}isValidUrl({urlString:e}){return!!new RegExp("^(https?:\\/\\/)?((([a-z\\d]([a-z\\d-]*[a-z\\d])*)\\.)+[a-z]{2,}|((\\d{1,3}\\.){3}\\d{1,3}))(\\:\\d+)?(\\/[-a-z\\d%_.~+]*)*(\\?[;&a-z\\d%_.~+=-]*)?(\\#[-a-z\\d_]*)?$","i").test(e)}isValidFileSize({field:e}){let t=null;const i=e.querySelector(".form__control"),s=Number(e.dataset.minSize),r=Number(e.dataset.maxSize),a=e.querySelector(".form__error"),o=i&&i.files?i.files[0].name:null,l=i&&i.files?i.files[0].size:0,n=i&&i.files?parseFloat((l/1048576).toFixed(1)):0;n>r||!l?(i&&(i.value="",i.setAttribute("aria-invalid","true")),e.classList.remove("--has-file"),e.classList.add(this.classes.fieldHasError)):o?e.classList.add("--has-file"):e.classList.remove("--has-file"),this.validationMessages&&(l?n<s?t=this.validationMessages.file.min_size.replace("[[var]]",s+" MB"):n>r&&(t=this.validationMessages.file.max_size.replace("[[var]]",r+" MB")):t=this.validationMessages.file.empty),t&&a&&(a.textContent=t,this.displayFieldError({control:i,field:e,errorElement:a}))}displayFieldError({control:e,field:t,errorElement:i}){e&&(e.setAttribute("aria-invalid","true"),t.classList.add(this.classes.fieldHasError),this.classes.controlHasError&&e.classList.add(this.classes.controlHasError),i&&i.classList.remove(this.classes.hiddenErrorMessage))}clearControlError({control:e}){if(!e)return;e.setAttribute("aria-invalid","false"),this.classes.controlHasError&&e.classList.remove(this.classes.controlHasError);const i=t({element:e,className:"form__field"}),s=i?.querySelector(".form__error");i?.classList.remove(this.classes.fieldHasError),s&&(s.textContent="",s.classList.add(this.classes.hiddenErrorMessage))}realTimeValidations({form:e}){e.querySelectorAll('.form__field[data-type="file"]').forEach((e=>{const t=e.querySelector(".form__control");t?.addEventListener("change",(t=>{t.stopPropagation(),this.isValidFileSize({field:e})}))}))}throwError(e){throw new Error(`EgoForm Error: ${e}`)}}class a{constructor({element:e,classes:t,submitType:s,submitDataFormat:a,submitUrl:o,requestHeaders:l,fieldGroups:n,extraFields:u,serializerIgnoreList:d,customValidations:c,customValidationMessages:m,onStepChange:h,onValidationError:f,onSubmitStart:p,onSubmitEnd:F,onSuccess:g,onError:v,onBeforeSubmit:b,resetOnSuccess:E,resetLoaderOnSuccess:S,scrollOnError:y,preventSubmit:_,debug:L}){this.form=e,this.submitType=s||"fetch",this.submitDataFormat=a||"formData",this.requestHeaders=l||{},this.actionUrl=this.form.getAttribute("action")||o||null,this.submitMethod=this.form.getAttribute("method")||"POST",this.submitBtn=this.form.querySelector('button[type="submit"]')||null,this.classes={requiredField:"--required",requiredIfFilledField:"--required-if-filled",fieldHasError:"--has-error",hiddenErrorMessage:"--hidden",formSubmittingState:"--submitting",buttonSubmittingState:"--loading",clearFieldError:"--clear-error",validateOnBlur:"--validate-onblur",validateOnInput:"--validate-oninput",...t},this.isValid=!0,this.validator=new r({customValidations:c||{},classes:this.classes,customValidationMessages:m||null,debug:L??!1}),this.onValidationError=f??null,this.onStepChange=h??null,this.onSubmitStart=p??null,this.onSubmitEnd=F??null,this.onSuccess=g??null,this.onError=v??null,this.onBeforeSubmit=b??null,this.fieldGroups=n??null,this.extraFields=u??[],this.hasFile=!1,this.serializerIgnoreList=d||[],this.resetOnSuccess=E??!0,this.resetLoaderOnSuccess=S??!0,this.scrollOnError=y??!0;const x=this.form.querySelector(".form__step.--active");if(this.currentStep=x?Number(x.dataset.step):0,this.currentStepOptional=!1,this.stepChanging=!1,this.preventSubmit=_??!1,this.debug=L??!1,this.declareHandlers(),!this.actionUrl||""===this.actionUrl)throw new Error("The form doesn't have an action attribute or submitUrl wasn't provided.");this.debug&&i("initialized!")}submit(){this.preventSubmit||this.resumeSubmit()}resumeSubmit(){this.debug&&i(`submitting using ${this.submitType}!`),this.submittingForm({submitting:!0}),this.isValid=!0;const e=[];if(this.form.querySelectorAll(".form__field").forEach((t=>{if(!this.validator.validateField({field:t})){const i=t.querySelector(".form__control");i&&e.push(i.name),this.isValid=!1}})),this.isValid)if(this.debug)i("the form was submitted!"),i(JSON.parse(this.serializeData({returnFormData:!1})),"data"),setTimeout((()=>{this.submittingForm({submitting:!1,force:!0})}),1e3);else if("fetch"==this.submitType&&this.actionUrl){const e=this.serializeData({returnFormData:"json"===this.submitDataFormat});fetch(this.actionUrl,{method:this.submitMethod,headers:"json"===this.submitDataFormat?{"Content-Type":"application/json",...this.requestHeaders}:{...this.requestHeaders},body:e}).then((e=>{200===e.status||201===e.status?(this.resetOnSuccess&&this.reset(),"function"==typeof this.onSuccess&&this.onSuccess(e)):"function"==typeof this.onError&&this.onError(e)})).catch((e=>{"function"==typeof this.onError&&this.onError(e)})).finally((()=>{this.submittingForm({submitting:!1})}))}else{if("fetch"==this.submitType&&!this.actionUrl)throw new Error("Missing submit URL: When using 'fetch' submitType, specify either the form's 'action' or the 'submitUrl' option.");this.form.submit()}else if(this.submittingForm({submitting:!1,force:!0}),"function"==typeof this.onValidationError&&this.onValidationError(e,this),this.debug&&i(`this fields have failed validation: ${e.toString().replace(/,/g,", ")}.`),this.scrollOnError){const e=this.form.querySelector(`.form__field.${this.classes.fieldHasError}`);e&&!(({element:e})=>{const t=e.getBoundingClientRect();return t.top>=0&&t.left>=0&&t.bottom<=(window.innerHeight||document.documentElement.clientHeight)&&t.right<=(window.innerWidth||document.documentElement.clientWidth)})({element:e})&&e.scrollIntoView({behavior:"smooth"})}}submittingForm({submitting:e,force:t=!1}){let i=document.getElementsByTagName("body").item(0);e?(this.form.classList.add(this.classes.formSubmittingState),this.submitBtn&&this.submitBtn.classList.add(this.classes.buttonSubmittingState),i&&i.classList.add("--block"),"function"==typeof this.onSubmitStart&&this.onSubmitStart()):((this.resetLoaderOnSuccess||t)&&(this.form.classList.remove(this.classes.formSubmittingState),this.submitBtn&&this.submitBtn.classList.remove(this.classes.buttonSubmittingState),i&&i.classList.remove("--block")),"function"==typeof this.onSubmitEnd&&this.onSubmitEnd())}serializeData({returnFormData:e=!1}){const t=new FormData(this.form),i={};for(const e of t){const[t,s]=e;if(!this.serializerIgnoreList.includes(t)){if(s instanceof File&&!s.size&&!s.name)continue;i.hasOwnProperty(t)?i[t]=[...i[t],s]:i[t]=s}}if(this.fieldGroups)for(const e in this.fieldGroups)if(Object.hasOwnProperty.call(this.fieldGroups,e)){let s=[{}];for(const r of this.fieldGroups[e])s[0][r]=t.get(r),delete i[r];i[e]=s}if(this.extraFields.length)for(const e of this.extraFields)e.hasOwnProperty("name")&&e.hasOwnProperty("value")&&(t.append(e.name,e.value),i[e.name]=e.value);return e?t:JSON.stringify(i)}reset(){this.form.reset(),this.form.querySelectorAll(".form__field").forEach((e=>e.classList.remove("--filled",`${this.classes.fieldHasError}`))),this.form.querySelectorAll(".form__control").forEach((e=>e.setAttribute("aria-invalid","false"))),this.currentStep&&this.changeStep({step:1})}changeStep({step:t}){if(!this.stepChanging){const i=this.currentStepOptional?this.currentStep.toString()+"b":this.currentStep.toString(),s=this.form.querySelector('[data-step="'+i+'"]'),r=s?.querySelectorAll(`.${this.classes.requiredField}`),a="next"===t?this.currentStep+1:"prev"!==t||this.currentStepOptional?"optional"===t?this.currentStep+"b":this.currentStep:this.currentStep-1,o=this.form.querySelector('[data-step="'+a+'"]');(this.currentStep!==a||this.currentStepOptional)&&(this.stepChanging=!0,this.isValid=!0,setTimeout((()=>{!r||"next"!==t&&"optional"!==t||r.forEach((e=>{this.validator.validateField({field:e})||(this.isValid=!1)})),s&&o&&this.isValid?e({element:s,enter:!1,time:200,displayType:"flex",callback:()=>{s.classList.remove("--active"),e({element:o,enter:!0,time:200,displayType:"flex",callback:()=>{o.classList.add("--active"),this.stepChanging=!1,this.currentStepOptional="optional"===t,this.currentStep=parseInt(a),"function"==typeof this.onStepChange&&this.onStepChange(i.toString(),a.toString())}})}}):this.stepChanging=!1}),50))}}nextStep(){this.changeStep({step:"next"})}optionalStep(){this.changeStep({step:"optional"})}prevStep(){this.changeStep({step:"prev"})}isControlFilled({control:e}){let t=e.parentElement;t&&(""!==e.value?t.classList.add("--filled"):t.classList.remove("--filled"))}filterNumber({value:e,ignoreList:t=[]}){const i=t.join(""),s=new RegExp("[^"+i+"0-9]","g");return e.toString().replace(s,"")}filterFormattedQuantity({num:e,thousands:t=".",decimals:i=",",decimalSteps:s}){const r=i?e.toString().split(i):[e.toString()],a=new RegExp(/\B(?=(\d{3})+(?!\d))/g),o=r[0].replace(a,t);let l="";return l=s&&s>0&&r[1]&&r[1].length?o+i+r[1].slice(0,s):o,l}filterMoneyAmount({num:e,currency:t="$",thousands:i=".",decimals:s=",",decimalSteps:r}){if(!e||e==t)return"";let a=this.filterNumber({value:e,ignoreList:[s||""]});return a=this.filterFormattedQuantity({num:a,thousands:i,decimals:s,decimalSteps:r}),`${t} ${a}`}filterPhoneNumber({number:e}){let t=new RegExp(/[^\d+\-() ]*/g);return e.replace(t,"")}togglePasswordVisibility({btn:e}){this.debug&&i("Password visibility toggled!");const t=e?.parentElement?.querySelector(".form__control");if(t){const i="password"===t.getAttribute("type")?"text":"password";t.setAttribute("type",i),e.classList.toggle("--hide")}}declareHandlers(){const e=this;if(!this.submitBtn)throw new Error(`There's no submit button in this form "${this.form.id}".`);this.submitBtn.addEventListener("click",(function(t){t.preventDefault(),"function"==typeof e.onBeforeSubmit&&e.onBeforeSubmit(e),e.submit()})),this.validator.realTimeValidations({form:this.form}),this.form.querySelectorAll(`.form__field.${this.classes.validateOnBlur}`).forEach((e=>{e.querySelector(".form__control")?.addEventListener("blur",(()=>{this.validator.validateField({field:e})||(this.isValid=!1)}))})),this.form.querySelectorAll(`.form__field.${this.classes.validateOnInput}`).forEach((e=>{const t=e.querySelector(".form__control");t?.addEventListener("input",(()=>{this.validator.validateField({field:e})&&this.validator.clearControlError({control:t})}))})),this.form.querySelectorAll(".form__next-step").forEach((t=>{t.addEventListener("click",e.nextStep.bind(e))})),this.form.querySelectorAll(".form__optional-step").forEach((t=>{t.addEventListener("click",e.optionalStep.bind(e))})),this.form.querySelectorAll(".form__prev-step").forEach((t=>{t.addEventListener("click",e.prevStep.bind(e))})),this.form.querySelectorAll(".form__control").forEach((e=>{this.isControlFilled({control:e}),e.addEventListener("keyup",(()=>{this.isControlFilled({control:e})})),e.addEventListener("change",(()=>{this.isControlFilled({control:e})}))})),this.form.querySelectorAll(".form__control").forEach((e=>{e.addEventListener("focus",(()=>{this.validator.clearControlError({control:e})}))})),this.form.querySelectorAll("."+this.classes.clearFieldError).forEach((i=>{i.addEventListener("click",(()=>{const s=t({element:i,className:"form__field"});s&&e.validator.clearControlError({control:s.querySelector(".form__control")})}))})),this.form.querySelectorAll(".form__field.--number input").forEach((e=>{const i=this,s=t({element:e,className:"form__field"}),r=s&&s.dataset.thousandsSeparator?s.dataset.thousandsSeparator:"",a=s&&s.dataset.decimalSeparator?s.dataset.decimalSeparator:"",o=s&&s.dataset.decimals?s.dataset.decimals:"";function l(){e.value=i.filterNumber({value:e.value,ignoreList:[a]})}e.addEventListener("focus",l),e.addEventListener("input",l),e.addEventListener("paste",l),e.addEventListener("blur",(()=>{e.value=r?i.filterFormattedQuantity({num:e.value,thousands:r,decimals:a,decimalSteps:parseInt(o)}):i.filterNumber({value:e.value})}))})),this.form.querySelectorAll(".form__field.--money-amount input").forEach((e=>{const i=this,s=t({element:e,className:"form__field"}),r=s&&s.dataset.currency?s.dataset.currency:"$",a=s&&s.dataset.thousandsSeparator?s.dataset.thousandsSeparator:".",o=s&&s.dataset.decimalSeparator?s.dataset.decimalSeparator:"",l=s&&s.dataset.decimals?s.dataset.decimals:"";function n(){e.value=i.filterNumber({value:e.value,ignoreList:[o]})}e.addEventListener("focus",n),e.addEventListener("input",n),e.addEventListener("paste",n),e.addEventListener("blur",(()=>{e.value=this.filterMoneyAmount({num:e.value,currency:r,thousands:a,decimals:o,decimalSteps:parseInt(l)})}))})),this.form.querySelectorAll(".form__field.--phone input").forEach((e=>{e.addEventListener("input",(()=>{e.value=this.filterPhoneNumber({number:e.value})})),e.addEventListener("paste",(()=>{e.value=this.filterPhoneNumber({number:e.value})}))})),this.form.querySelectorAll(".form__toggle-password-visibility").forEach((e=>{e.addEventListener("click",(()=>this.togglePasswordVisibility({btn:e})))}))}}export{a as default};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t():"function"==typeof define&&define.amd?define(t):((e="undefined"!=typeof globalThis?globalThis:e||self).egodesign=e.egodesign||{},e.egodesign.form=t())}(this,(function(){"use strict";const e=({element:e,enter:t,time:i,displayType:s,callback:r})=>{e.style.opacity=t?0:1,t&&(e.style.display=s||"block");let a=+new Date;!function s(){e.style.opacity=t?+e.style.opacity+(new Date-a)/(i||200):+e.style.opacity-(new Date-a)/(i||200),a=+new Date,t&&+e.style.opacity<1||!t&&+e.style.opacity>0?window.requestAnimationFrame&&requestAnimationFrame(s)||setTimeout(s,16):(t||(e.style.display="none"),r&&"function"==typeof r&&r())}()},t=({element:e,className:t})=>{if(!e)return!1;for(;e&&!e.classList.contains(t);)e=!!e.parentElement&&e.parentElement;return e};function i(e,t="log"){"log"==t?console.log("::EgoForm:: "+e):"data"==t&&(console.log("::EgoForm:: DATA"),console.table(e))}const s={default:{empty:"Campo requerido.",invalid:"Campo nó válido.",minLength:"Debe tener al menos [[var]] caracteres.",maxLength:"Debe tener como máximo [[var]] caracteres."},email:{empty:"El email es requerido.",invalid:"El email no es válido."},message:{empty:"El mensaje es requerido."},password:{empty:"Ingrese una contraseña."},password_repeat:{empty:"Debe repetir la contraseña.",unequal:"Las contraseñas no coindicen."},cuil:{empty:"Campo requerido.",min:"El número debe tener exactamente 11 dígitos.",invalid:"El número ingresado no es válido."},url:{empty:"Campo requerido.",invalid:"URL no válida."},file:{empty:"Campo requerido",min_size:"El tamaño mínimo es [[var]]",max_size:"El tamaño máximo es [[var]]"}};class r{constructor({customValidations:e,classes:t,customValidationMessages:i,debug:r}){this.customValidations=e,this.validationMessages={...s,...i}||s,this.classes=t,this.debug=r}validateField(e){const t=e.dataset.type,s=["radio","checkbox"].includes(t),r=e.classList.contains(this.classes.requiredField),a=e.classList.contains(this.classes.requiredIfFilledField),o=e.querySelector(".form__error"),l=e.querySelector(".form__control"),n=s?e.querySelector(".form__control:checked"):null,d=l?l.getAttribute("name"):"",u=Object.keys(this.customValidations),c=e.dataset.minLength?parseInt(e.dataset.minLength):null,h=e.dataset.maxLength?parseInt(e.dataset.maxLength):null;if(l||this.throwError("control not found."),d||this.throwError("control name not found."),this.debug&&i(`validating field "${d}"`),r&&!l.value)return o&&(o.textContent=this.validationMessages[d]?this.validationMessages[d].empty:this.validationMessages.default.empty),this.displayFieldError(l,e,o),!1;if(l.value){if(c&&l.value.length<c)return o&&(o.textContent=this.validationMessages.default.minLength.replace("[[var]]",c)),this.displayFieldError(l,e,o),!1;if(h&&l.value.length>h)return o&&(o.textContent=this.validationMessages.default.maxLength.replace("[[var]]",h)),this.displayFieldError(l,e,o),!1}if(s&&!n){if(!a)return this.displayFieldError(l,e,o),o&&(o.textContent=this.validationMessages[d]?this.validationMessages[d].empty:this.validationMessages.default.empty),!1}else{switch(t){case"email":if(l.value&&!this.isValidEmail(l.value))return o&&(o.textContent=this.validationMessages.email.invalid),this.displayFieldError(l,e,o),!1;break;case"password_repeat":{const t=document.getElementById("password");if(t&&l.value!=t.value)return o&&(o.textContent=this.validationMessages.password_repeat.unequal),this.displayFieldError(l,e,o),!1;break}case"cuil":case"cuit":if(l.value&&!this.isValidCuitCuil(l.value))return o&&(o.textContent=this.validationMessages.cuil.invalid),this.displayFieldError(l,e,o),!1;break;case"url":if(l.value&&!this.isValidUrl(l.value))return o&&(o.textContent=this.validationMessages.url.invalid),this.displayFieldError(l,e,o),!1;break;case"money":const t=l.dataset.currency?l.dataset.currency:"$";if(""==l.value||l.value==t)return o&&(o.textContent=this.validationMessages.default.empty),this.displayFieldError(l,e,o),!1;break;case"single-checkbox":if(!l.checked)return this.displayFieldError(l,e,o),o&&(o.textContent=this.validationMessages[d]?this.validationMessages[d].empty:this.validationMessages.default.empty),!1}for(const i of u)if(t===i)for(const i of this.customValidations[t])if(!i.condition(l.value))return o&&(o.textContent=i.message||""),this.displayFieldError(l,e,o),!1}return!0}isValidEmail(e){return/(?!.*\.{2})^([a-z\d!#$%&'*+\-/=?^_`{|}~\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]+(\.[a-z\d!#$%&'*+\-/=?^_`{|}~\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]+)*|"((([\t]*\r\n)?[\t]+)?([\x01-\x08\x0b\x0c\x0e-\x1f\x7f\x21\x23-\x5b\x5d-\x7e\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]|\\[\x01-\x09\x0b\x0c\x0d-\x7f\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]))*(([\t]*\r\n)?[\t]+)?")@(([a-z\d\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]|[a-z\d\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF][a-z\d\-._~\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]*[a-z\d\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])\.)+([a-z\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]|[a-z\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF][a-z\d\-._~\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]*[a-z\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])\.?$/i.test(e.toLowerCase())}isValidCuitCuil(e){const t=[5,4,3,2,7,6,5,4,3,2];let i=e.toString().replace(/[^0-9]/g,"").split("");if(11!=i.length)return!1;i=i.map((e=>parseInt(e)));const s=parseInt(i.pop());let r=11-i.reduce(((e,i,s)=>e+i*t[s]),0)%11;return 10==r&&(r=9),11==r&&(r=0),r==s}isValidUrl(e){return!!new RegExp("^(https?:\\/\\/)?((([a-z\\d]([a-z\\d-]*[a-z\\d])*)\\.)+[a-z]{2,}|((\\d{1,3}\\.){3}\\d{1,3}))(\\:\\d+)?(\\/[-a-z\\d%_.~+]*)*(\\?[;&a-z\\d%_.~+=-]*)?(\\#[-a-z\\d_]*)?$","i").test(e)}isValidFileSize(e){let t=null;const i=e.querySelector(".form__control"),s=parseFloat(e.dataset.minSize),r=parseFloat(e.dataset.maxSize),a=e.querySelector(".form__error"),o=i.files[0].name,l=i.files[0].size,n=parseFloat(i.files.length?(l/1048576).toFixed(1):0);n>r||!l?(i.value="",e.classList.remove("--has-file"),i.setAttribute("aria-invalid","true"),e.classList.add(this.classes.fieldHasError)):o?(this.hasFile=!0,e.classList.add("--has-file")):(this.hasFile=!1,e.classList.remove("--has-file")),l?n<s?t=this.validationMessages.file.min_size.replace("[[var]]",s+" MB"):n>r&&(t=this.validationMessages.file.max_size.replace("[[var]]",r+" MB")):t=this.validationMessages.file.empty,t&&(a.textContent=t,this.displayFieldError(i,e,a))}displayFieldError(e,t,i){e.setAttribute("aria-invalid","true"),t.classList.add(this.classes.fieldHasError),this.classes.controlHasError&&e.classList.add(this.classes.controlHasError),i&&i.classList.remove(this.classes.hiddenErrorMessage)}clearControlError(e){e.setAttribute("aria-invalid","false"),this.classes.controlHasError&&e.classList.remove(this.classes.controlHasError);const i=t({element:e,className:"form__field"}),s=i.querySelector(".form__error");i.classList.remove(this.classes.fieldHasError),s&&(s.textContent="",s.classList.add(this.classes.hiddenErrorMessage))}realTimeValidations(e){e.querySelectorAll('.form__field[data-type="file"]').forEach((e=>{const t=e.querySelector(".form__control");t?.addEventListener("change",(t=>{t.stopPropagation(),this.isValidFileSize(e)}))}))}throwError(e){throw new Error(`EgoForm Error: ${e}`)}}return class{constructor({element:e,classes:t,submitType:s,submitDataFormat:a,submitUrl:o,requestHeaders:l,fieldGroups:n,extraFields:d,serializerIgnoreList:u,customValidations:c,customValidationMessages:h,onStepChange:m,onValidationError:f,onSubmitStart:p,onSubmitEnd:F,onSuccess:g,onError:v,onBeforeSubmit:y,resetOnSuccess:b,resetLoaderOnSuccess:E,scrollOnError:S,preventSubmit:_,debug:L}){if(this.form=e,this.submitType=s||"fetch",this.submitDataFormat=a||"formData",this.requestHeaders=l||{},this.actionUrl=this.form.getAttribute("action")||o||null,this.submitMethod=this.form.getAttribute("method")||"POST",this.submitBtn=this.form.querySelector('button[type="submit"]')||null,this.classes={requiredField:"--required",requiredIfFilledField:"--required-if-filled",fieldHasError:"--has-error",controlHasError:!1,hiddenErrorMessage:"--hidden",formSubmittingState:"--submitting",buttonSubmittingState:"--loading",clearFieldError:"--clear-error",validateOnBlur:"--validate-onblur",validateOnInput:"--validate-oninput",...t},this.isValid=!0,this.validator=new r({customValidations:c||{},classes:this.classes,customValidationMessages:h||null,debug:L}),this.onValidationError=f??!1,this.onStepChange=m??!1,this.onSubmitStart=p??!1,this.onSubmitEnd=F??!1,this.onSuccess=g??!1,this.onError=v??!1,this.onBeforeSubmit=y??!1,this.fieldGroups=n??!1,this.extraFields=d??[],this.hasFile=!1,this.serializerIgnoreList=u||[],this.resetOnSuccess=b??!0,this.resetLoaderOnSuccess=E??!0,this.scrollOnError=S??!0,this.currentStep=this.form.querySelector(".form__step")?parseInt(this.form.querySelector(".form__step.--active").dataset.step):0,this.currentStepOptional=!1,this.stepChanging=!1,this.preventSubmit=_??!1,this.debug=L??!1,this.declareHandlers(),!this.actionUrl||""===this.actionUrl)throw new Error("The form doesn't have an action attribute or submitUrl wasn't provided.");this.debug&&i("initialized!")}submit(){this.preventSubmit||this.resumeSubmit()}resumeSubmit(){this.debug&&i(`submitting using ${this.submitType}!`),this.submittingForm(!0),this.isValid=!0;const e=[];if(this.form.querySelectorAll(".form__field").forEach((t=>{this.validator.validateField(t)||(e.push(t.querySelector(".form__control")?.name),this.isValid=!1)})),this.isValid)this.debug?(i("the form was submitted!"),i(this.serializeData(),"data"),setTimeout((()=>{this.submittingForm(!1,!0)}),1e3)):"fetch"==this.submitType?fetch(this.actionUrl,{method:this.submitMethod,headers:"json"===this.submitDataFormat?{"Content-Type":"application/json",...this.requestHeaders}:{...this.requestHeaders},body:"json"===this.submitDataFormat?JSON.stringify(this.serializeData()):this.serializeData(!0)}).then((e=>{200===e.status||201===e.status?(this.resetOnSuccess&&this.reset(),"function"==typeof this.onSuccess&&this.onSuccess(e)):"function"==typeof this.onError&&this.onError(e)})).catch((e=>{"function"==typeof this.onError&&this.onError(e)})).finally((()=>{this.submittingForm(!1)})):this.form.submit();else if(this.submittingForm(!1,!0),"function"==typeof this.onValidationError&&this.onValidationError(e),this.debug&&i(`this fields have failed validation: ${e.toString().replace(/,/g,", ")}.`),this.scrollOnError){const e=this.form.querySelector(`.form__field.${this.classes.fieldHasError}`);(e=>{const t=e.getBoundingClientRect();return t.top>=0&&t.left>=0&&t.bottom<=(window.innerHeight||document.documentElement.clientHeight)&&t.right<=(window.innerWidth||document.documentElement.clientWidth)})(e)||e.scrollIntoView({behavior:"smooth"})}}submittingForm(e,t=!1){let i=document.getElementsByTagName("body").item(0);e?(this.form.classList.add(this.classes.formSubmittingState),this.submitBtn.classList.add(this.classes.buttonSubmittingState),i.classList.add("--block"),"function"==typeof this.onSubmitStart&&this.onSubmitStart()):((this.resetLoaderOnSuccess||t)&&(this.form.classList.remove(this.classes.formSubmittingState),this.submitBtn.classList.remove(this.classes.buttonSubmittingState),i.classList.remove("--block")),"function"==typeof this.onSubmitEnd&&this.onSubmitEnd())}serializeData(e=!1){const t=new FormData(this.form),i={};for(const e of t){const[t,s]=e;if(!this.serializerIgnoreList.includes(t)){if(s instanceof File&&!s.size&&!s.name)continue;i.hasOwnProperty(t)?i[t]=[...i[t],s]:i[t]=s}}if(this.fieldGroups)for(const e in this.fieldGroups)if(Object.hasOwnProperty.call(this.fieldGroups,e)){let s=[{}];for(const r of this.fieldGroups[e])s[0][r]=t.get(r),delete i[r];i[e]=s}if(this.extraFields.length)for(const e of this.extraFields)e.hasOwnProperty("name")&&e.hasOwnProperty("value")&&(i[e.name]=e.value);return e?t:i}reset(){this.form.reset(),this.form.querySelectorAll(".form__field").forEach((e=>e.classList.remove("--filled",`${this.classes.fieldHasError}`))),this.form.querySelectorAll(".form__control").forEach((e=>e.setAttribute("aria-invalid","false"))),this.currentStep&&this.changeStep(1)}changeStep(t){if(!this.stepChanging){const i=this.currentStepOptional?this.currentStep+"b":this.currentStep,s=this.form.querySelector('[data-step="'+i+'"]'),r=s.querySelectorAll(`.${this.classes.requiredField}`),a="next"===t?this.currentStep+1:"prev"!==t||this.currentStepOptional?"optional"===t?this.currentStep+"b":this.currentStep:this.currentStep-1,o=this.form.querySelector('[data-step="'+a+'"]');(this.currentStep!==a||this.currentStepOptional)&&(this.stepChanging=!0,this.isValid=!0,setTimeout((()=>{!r||"next"!==t&&"optional"!==t||r.forEach((e=>{this.validator.validateField(e)||(this.isValid=!1)})),s&&o&&this.isValid?e({element:s,enter:!1,time:200,displayType:"flex",callback:()=>{s.classList.remove("--active"),e({element:o,enter:!0,time:200,displayType:"flex",callback:()=>{o.classList.add("--active"),this.stepChanging=!1,this.currentStepOptional="optional"===t,this.currentStep=parseInt(a),"function"==typeof this.onStepChange&&this.onStepChange(i,a)}})}}):this.stepChanging=!1}),50))}}nextStep(){this.changeStep("next")}optionalStep(){this.changeStep("optional")}prevStep(){this.changeStep("prev")}isControlFilled(e){let t=e.parentElement;""!==e.value?t.classList.add("--filled"):t.classList.remove("--filled")}filterNumber(e,t=[]){const i=t.join(""),s=new RegExp("[^"+i+"0-9]","g");return e.toString().replace(s,"")}filterFormattedQuantity(e,t=".",i=",",s){const r=i?e.toString().split(i):[e],a=r[0].replace(/\B(?=(\d{3})+(?!\d))/g,t);let o="";return o=s>0&&r[1]&&r[1].length?a+i+r[1].slice(0,s):a,o}filterMoneyAmount(e,t="$",i=".",s=",",r){if(!e||e==t)return"";let a=this.filterNumber(e,[s||""]);return a=this.filterFormattedQuantity(a,i,s,r),`${t} ${a}`}filterPhoneNumber(e){return e.replace(/[^\d+\-() ]*/g,"")}togglePasswordVisibility(e){this.debug&&i("Password visibility toggled!");const t=e.parentElement.querySelector(".form__control");if(t){const i="password"===t.getAttribute("type")?"text":"password";t.setAttribute("type",i),e.classList.toggle("--hide")}}declareHandlers(){const e=this;if(!this.submitBtn)throw new Error(`There's no submit button in this form "${this.form.id}".`);this.submitBtn.addEventListener("click",(function(t){t.preventDefault(),"function"==typeof e.onBeforeSubmit&&e.onBeforeSubmit(),e.submit()})),this.validator.realTimeValidations(this.form),this.form.querySelectorAll(`.form__field.${this.classes.validateOnBlur}`).forEach((e=>{e.querySelector(".form__control").addEventListener("blur",(()=>{this.validator.validateField(e)||(this.isValid=!1)}))})),this.form.querySelectorAll(`.form__field.${this.classes.validateOnInput}`).forEach((e=>{const t=e.querySelector(".form__control");t.addEventListener("input",(()=>{this.validator.validateField(e)&&this.validator.clearControlError(t)}))})),this.form.querySelectorAll(".form__next-step").forEach((t=>{t.addEventListener("click",e.nextStep.bind(e))})),this.form.querySelectorAll(".form__optional-step").forEach((t=>{t.addEventListener("click",e.optionalStep.bind(e))})),this.form.querySelectorAll(".form__prev-step").forEach((t=>{t.addEventListener("click",e.prevStep.bind(e))})),this.form.querySelectorAll(".form__control").forEach((e=>{this.isControlFilled(e),e.addEventListener("keyup",(()=>{this.isControlFilled(e)})),e.addEventListener("change",(()=>{this.isControlFilled(e)}))})),this.form.querySelectorAll(".form__control").forEach((e=>{e.addEventListener("focus",(()=>{this.validator.clearControlError(e)}))})),this.form.querySelectorAll("."+this.classes.clearFieldError).forEach((i=>{i.addEventListener("click",(()=>{const s=t({element:i,className:"form__field"});e.validator.clearControlError(s.querySelector(".form__control"))}))})),this.form.querySelectorAll(".form__field.--number input").forEach((e=>{const i=this,s=t({element:e,className:"form__field"}),r=s&&s.dataset.thousandsSeparator?s.dataset.thousandsSeparator:null,a=s&&s.dataset.decimalSeparator?s.dataset.decimalSeparator:"",o=s&&s.dataset.decimals?s.dataset.decimals:"";function l(){e.value=i.filterNumber(e.value,[a])}e.addEventListener("focus",l),e.addEventListener("input",l),e.addEventListener("paste",l),e.addEventListener("blur",(()=>{e.value=r?i.filterFormattedQuantity(e.value,r,a,parseInt(o)):i.filterNumber(e.value)}))})),this.form.querySelectorAll(".form__field.--money-amount input").forEach((e=>{const i=this,s=t({element:e,className:"form__field"}),r=s&&s.dataset.currency?s.dataset.currency:"$",a=s&&s.dataset.thousandsSeparator?s.dataset.thousandsSeparator:".",o=s&&s.dataset.decimalSeparator?s.dataset.decimalSeparator:"",l=s&&s.dataset.decimals?s.dataset.decimals:"";function n(){e.value=i.filterNumber(e.value,[o])}e.addEventListener("focus",n),e.addEventListener("input",n),e.addEventListener("paste",n),e.addEventListener("blur",(()=>{e.value=this.filterMoneyAmount(e.value,r,a,o,parseInt(l))}))})),this.form.querySelectorAll(".form__field.--phone input").forEach((e=>{e.addEventListener("input",(()=>{e.value=this.filterPhoneNumber(e.value)})),e.addEventListener("paste",(()=>{e.value=this.filterPhoneNumber(e.value)}))})),this.form.querySelectorAll(".form__toggle-password-visibility").forEach((e=>{e.addEventListener("click",this.togglePasswordVisibility(e))}))}}}));
|
|
1
|
+
!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t():"function"==typeof define&&define.amd?define(t):((e="undefined"!=typeof globalThis?globalThis:e||self).egodesign=e.egodesign||{},e.egodesign.form=t())}(this,(function(){"use strict";const e=({element:e,enter:t,time:i,displayType:s,callback:r})=>{e.style.opacity=t?"0":"1",t&&(e.style.display=s);let a=(new Date).getTime();!function s(){e.style.opacity=t?(+e.style.opacity+((new Date).getTime()-a)/i).toString():(+e.style.opacity-((new Date).getTime()-a)/i).toString(),a=(new Date).getTime(),t&&Number(e.style.opacity)<1||!t&&Number(e.style.opacity)>0?requestAnimationFrame(s)||setTimeout(s,16):(t||(e.style.display="none"),r&&"function"==typeof r&&r())}()},t=({element:e,className:t})=>{if(!e)return null;for(;e&&!e.classList.contains(t);)e=e.parentElement?e.parentElement:null;return e};function i(e,t="log"){"log"==t?console.log("::EgoForm:: "+e):"data"==t&&(console.log("::EgoForm:: DATA"),console.table(e))}const s={default:{empty:"Campo requerido.",invalid:"Campo nó válido.",minLength:"Debe tener al menos [[var]] caracteres.",maxLength:"Debe tener como máximo [[var]] caracteres."},email:{empty:"El email es requerido.",invalid:"El email no es válido."},message:{empty:"El mensaje es requerido."},password:{empty:"Ingrese una contraseña."},password_repeat:{empty:"Debe repetir la contraseña.",unequal:"Las contraseñas no coindicen."},cuil:{empty:"Campo requerido.",min:"El número debe tener exactamente 11 dígitos.",invalid:"El número ingresado no es válido."},url:{empty:"Campo requerido.",invalid:"URL no válida."},file:{empty:"Campo requerido",min_size:"El tamaño mínimo es [[var]]",max_size:"El tamaño máximo es [[var]]"}};class r{constructor({customValidations:e,classes:t,customValidationMessages:i,debug:r}){this.customValidations=e,this.validationMessages={...s,...i},this.classes=t,this.debug=r}validateField({field:e}){const t=e.dataset.type||"",s=["radio","checkbox"].includes(t),r=e.classList.contains(this.classes.requiredField),a=e.classList.contains(this.classes.requiredIfFilledField),o=e.querySelector(".form__error"),l=e.querySelector(".form__control"),n=s?e.querySelector(".form__control:checked"):null,u=l?l.getAttribute("name"):null,d=Object.keys(this.customValidations),c=e.dataset.minLength?Number(e.dataset.minLength):null,m=e.dataset.maxLength?Number(e.dataset.maxLength):null;if(l||this.throwError("control not found."),u||this.throwError("control name not found."),this.debug&&i(`validating field "${u}"`),r&&!l?.value)return o&&this.validationMessages&&u&&(o.textContent=this.validationMessages[u]?this.validationMessages[u].empty:this.validationMessages.default.empty),this.displayFieldError({control:l,field:e,errorElement:o}),!1;if(l?.value){if(c&&l.value.length<c)return o&&this.validationMessages&&(o.textContent=this.validationMessages.default.minLength.replace("[[var]]",c.toString())),this.displayFieldError({control:l,field:e,errorElement:o}),!1;if(m&&l.value.length>m)return o&&this.validationMessages&&(o.textContent=this.validationMessages.default.maxLength.replace("[[var]]",m.toString())),this.displayFieldError({control:l,field:e,errorElement:o}),!1}if(s&&!n){if(!a)return this.displayFieldError({control:l,field:e,errorElement:o}),o&&this.validationMessages&&u&&(o.textContent=this.validationMessages[u]?this.validationMessages[u].empty:this.validationMessages.default.empty),!1}else{switch(t){case"email":if(l&&l.value&&!this.isValidEmail({email:l.value}))return o&&this.validationMessages&&(o.textContent=this.validationMessages.email.invalid),this.displayFieldError({control:l,field:e,errorElement:o}),!1;break;case"password_repeat":{const t=document.getElementById("password");if(t&&l?.value!=t?.value)return o&&this.validationMessages&&(o.textContent=this.validationMessages.password_repeat.unequal),this.displayFieldError({control:l,field:e,errorElement:o}),!1;break}case"cuil":case"cuit":if(l&&l.value&&!this.isValidCuitCuil({num:l.value}))return o&&this.validationMessages&&(o.textContent=this.validationMessages.cuil.invalid),this.displayFieldError({control:l,field:e,errorElement:o}),!1;break;case"url":if(l&&l.value&&!this.isValidUrl({urlString:l.value}))return o&&this.validationMessages&&(o.textContent=this.validationMessages.url.invalid),this.displayFieldError({control:l,field:e,errorElement:o}),!1;break;case"money":if(l){const t=l.dataset.currency?l.dataset.currency:"$";if(""==l.value||l.value==t)return o&&this.validationMessages&&(o.textContent=this.validationMessages.default.empty),this.displayFieldError({control:l,field:e,errorElement:o}),!1}break;case"single-checkbox":if(l&&l.hasOwnProperty("checked")){if(!l.checked)return this.displayFieldError({control:l,field:e,errorElement:o}),o&&this.validationMessages&&u&&(o.textContent=this.validationMessages[u]?this.validationMessages[u].empty:this.validationMessages.default.empty),!1}}for(const i of d)if(t===i)for(const i of this.customValidations[t])if(l&&!i.condition(l.value))return o&&(o.textContent=i.message||""),this.displayFieldError({control:l,field:e,errorElement:o}),!1}return!0}isValidEmail({email:e}){return/(?!.*\.{2})^([a-z\d!#$%&'*+\-/=?^_`{|}~\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]+(\.[a-z\d!#$%&'*+\-/=?^_`{|}~\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]+)*|"((([\t]*\r\n)?[\t]+)?([\x01-\x08\x0b\x0c\x0e-\x1f\x7f\x21\x23-\x5b\x5d-\x7e\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]|\\[\x01-\x09\x0b\x0c\x0d-\x7f\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]))*(([\t]*\r\n)?[\t]+)?")@(([a-z\d\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]|[a-z\d\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF][a-z\d\-._~\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]*[a-z\d\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])\.)+([a-z\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]|[a-z\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF][a-z\d\-._~\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]*[a-z\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])\.?$/i.test(e.toLowerCase())}isValidCuitCuil({num:e}){const t=[5,4,3,2,7,6,5,4,3,2];let i=e.toString().replace(/[^0-9]/g,"").split("");if(11!=i.length)return!1;i=i.map((e=>Number(e)));const s=Number(i.pop());let r=11-i.reduce(((e,i,s)=>e+Number(i)*t[s]),0)%11;return 10==r&&(r=9),11==r&&(r=0),r==s}isValidUrl({urlString:e}){return!!new RegExp("^(https?:\\/\\/)?((([a-z\\d]([a-z\\d-]*[a-z\\d])*)\\.)+[a-z]{2,}|((\\d{1,3}\\.){3}\\d{1,3}))(\\:\\d+)?(\\/[-a-z\\d%_.~+]*)*(\\?[;&a-z\\d%_.~+=-]*)?(\\#[-a-z\\d_]*)?$","i").test(e)}isValidFileSize({field:e}){let t=null;const i=e.querySelector(".form__control"),s=Number(e.dataset.minSize),r=Number(e.dataset.maxSize),a=e.querySelector(".form__error"),o=i&&i.files?i.files[0].name:null,l=i&&i.files?i.files[0].size:0,n=i&&i.files?parseFloat((l/1048576).toFixed(1)):0;n>r||!l?(i&&(i.value="",i.setAttribute("aria-invalid","true")),e.classList.remove("--has-file"),e.classList.add(this.classes.fieldHasError)):o?e.classList.add("--has-file"):e.classList.remove("--has-file"),this.validationMessages&&(l?n<s?t=this.validationMessages.file.min_size.replace("[[var]]",s+" MB"):n>r&&(t=this.validationMessages.file.max_size.replace("[[var]]",r+" MB")):t=this.validationMessages.file.empty),t&&a&&(a.textContent=t,this.displayFieldError({control:i,field:e,errorElement:a}))}displayFieldError({control:e,field:t,errorElement:i}){e&&(e.setAttribute("aria-invalid","true"),t.classList.add(this.classes.fieldHasError),this.classes.controlHasError&&e.classList.add(this.classes.controlHasError),i&&i.classList.remove(this.classes.hiddenErrorMessage))}clearControlError({control:e}){if(!e)return;e.setAttribute("aria-invalid","false"),this.classes.controlHasError&&e.classList.remove(this.classes.controlHasError);const i=t({element:e,className:"form__field"}),s=i?.querySelector(".form__error");i?.classList.remove(this.classes.fieldHasError),s&&(s.textContent="",s.classList.add(this.classes.hiddenErrorMessage))}realTimeValidations({form:e}){e.querySelectorAll('.form__field[data-type="file"]').forEach((e=>{const t=e.querySelector(".form__control");t?.addEventListener("change",(t=>{t.stopPropagation(),this.isValidFileSize({field:e})}))}))}throwError(e){throw new Error(`EgoForm Error: ${e}`)}}return class{constructor({element:e,classes:t,submitType:s,submitDataFormat:a,submitUrl:o,requestHeaders:l,fieldGroups:n,extraFields:u,serializerIgnoreList:d,customValidations:c,customValidationMessages:m,onStepChange:h,onValidationError:f,onSubmitStart:p,onSubmitEnd:g,onSuccess:F,onError:v,onBeforeSubmit:b,resetOnSuccess:E,resetLoaderOnSuccess:S,scrollOnError:y,preventSubmit:_,debug:L}){this.form=e,this.submitType=s||"fetch",this.submitDataFormat=a||"formData",this.requestHeaders=l||{},this.actionUrl=this.form.getAttribute("action")||o||null,this.submitMethod=this.form.getAttribute("method")||"POST",this.submitBtn=this.form.querySelector('button[type="submit"]')||null,this.classes={requiredField:"--required",requiredIfFilledField:"--required-if-filled",fieldHasError:"--has-error",hiddenErrorMessage:"--hidden",formSubmittingState:"--submitting",buttonSubmittingState:"--loading",clearFieldError:"--clear-error",validateOnBlur:"--validate-onblur",validateOnInput:"--validate-oninput",...t},this.isValid=!0,this.validator=new r({customValidations:c||{},classes:this.classes,customValidationMessages:m||null,debug:L??!1}),this.onValidationError=f??null,this.onStepChange=h??null,this.onSubmitStart=p??null,this.onSubmitEnd=g??null,this.onSuccess=F??null,this.onError=v??null,this.onBeforeSubmit=b??null,this.fieldGroups=n??null,this.extraFields=u??[],this.hasFile=!1,this.serializerIgnoreList=d||[],this.resetOnSuccess=E??!0,this.resetLoaderOnSuccess=S??!0,this.scrollOnError=y??!0;const x=this.form.querySelector(".form__step.--active");if(this.currentStep=x?Number(x.dataset.step):0,this.currentStepOptional=!1,this.stepChanging=!1,this.preventSubmit=_??!1,this.debug=L??!1,this.declareHandlers(),!this.actionUrl||""===this.actionUrl)throw new Error("The form doesn't have an action attribute or submitUrl wasn't provided.");this.debug&&i("initialized!")}submit(){this.preventSubmit||this.resumeSubmit()}resumeSubmit(){this.debug&&i(`submitting using ${this.submitType}!`),this.submittingForm({submitting:!0}),this.isValid=!0;const e=[];if(this.form.querySelectorAll(".form__field").forEach((t=>{if(!this.validator.validateField({field:t})){const i=t.querySelector(".form__control");i&&e.push(i.name),this.isValid=!1}})),this.isValid)if(this.debug)i("the form was submitted!"),i(JSON.parse(this.serializeData({returnFormData:!1})),"data"),setTimeout((()=>{this.submittingForm({submitting:!1,force:!0})}),1e3);else if("fetch"==this.submitType&&this.actionUrl){const e=this.serializeData({returnFormData:"json"===this.submitDataFormat});fetch(this.actionUrl,{method:this.submitMethod,headers:"json"===this.submitDataFormat?{"Content-Type":"application/json",...this.requestHeaders}:{...this.requestHeaders},body:e}).then((e=>{200===e.status||201===e.status?(this.resetOnSuccess&&this.reset(),"function"==typeof this.onSuccess&&this.onSuccess(e)):"function"==typeof this.onError&&this.onError(e)})).catch((e=>{"function"==typeof this.onError&&this.onError(e)})).finally((()=>{this.submittingForm({submitting:!1})}))}else{if("fetch"==this.submitType&&!this.actionUrl)throw new Error("Missing submit URL: When using 'fetch' submitType, specify either the form's 'action' or the 'submitUrl' option.");this.form.submit()}else if(this.submittingForm({submitting:!1,force:!0}),"function"==typeof this.onValidationError&&this.onValidationError(e,this),this.debug&&i(`this fields have failed validation: ${e.toString().replace(/,/g,", ")}.`),this.scrollOnError){const e=this.form.querySelector(`.form__field.${this.classes.fieldHasError}`);e&&!(({element:e})=>{const t=e.getBoundingClientRect();return t.top>=0&&t.left>=0&&t.bottom<=(window.innerHeight||document.documentElement.clientHeight)&&t.right<=(window.innerWidth||document.documentElement.clientWidth)})({element:e})&&e.scrollIntoView({behavior:"smooth"})}}submittingForm({submitting:e,force:t=!1}){let i=document.getElementsByTagName("body").item(0);e?(this.form.classList.add(this.classes.formSubmittingState),this.submitBtn&&this.submitBtn.classList.add(this.classes.buttonSubmittingState),i&&i.classList.add("--block"),"function"==typeof this.onSubmitStart&&this.onSubmitStart()):((this.resetLoaderOnSuccess||t)&&(this.form.classList.remove(this.classes.formSubmittingState),this.submitBtn&&this.submitBtn.classList.remove(this.classes.buttonSubmittingState),i&&i.classList.remove("--block")),"function"==typeof this.onSubmitEnd&&this.onSubmitEnd())}serializeData({returnFormData:e=!1}){const t=new FormData(this.form),i={};for(const e of t){const[t,s]=e;if(!this.serializerIgnoreList.includes(t)){if(s instanceof File&&!s.size&&!s.name)continue;i.hasOwnProperty(t)?i[t]=[...i[t],s]:i[t]=s}}if(this.fieldGroups)for(const e in this.fieldGroups)if(Object.hasOwnProperty.call(this.fieldGroups,e)){let s=[{}];for(const r of this.fieldGroups[e])s[0][r]=t.get(r),delete i[r];i[e]=s}if(this.extraFields.length)for(const e of this.extraFields)e.hasOwnProperty("name")&&e.hasOwnProperty("value")&&(t.append(e.name,e.value),i[e.name]=e.value);return e?t:JSON.stringify(i)}reset(){this.form.reset(),this.form.querySelectorAll(".form__field").forEach((e=>e.classList.remove("--filled",`${this.classes.fieldHasError}`))),this.form.querySelectorAll(".form__control").forEach((e=>e.setAttribute("aria-invalid","false"))),this.currentStep&&this.changeStep({step:1})}changeStep({step:t}){if(!this.stepChanging){const i=this.currentStepOptional?this.currentStep.toString()+"b":this.currentStep.toString(),s=this.form.querySelector('[data-step="'+i+'"]'),r=s?.querySelectorAll(`.${this.classes.requiredField}`),a="next"===t?this.currentStep+1:"prev"!==t||this.currentStepOptional?"optional"===t?this.currentStep+"b":this.currentStep:this.currentStep-1,o=this.form.querySelector('[data-step="'+a+'"]');(this.currentStep!==a||this.currentStepOptional)&&(this.stepChanging=!0,this.isValid=!0,setTimeout((()=>{!r||"next"!==t&&"optional"!==t||r.forEach((e=>{this.validator.validateField({field:e})||(this.isValid=!1)})),s&&o&&this.isValid?e({element:s,enter:!1,time:200,displayType:"flex",callback:()=>{s.classList.remove("--active"),e({element:o,enter:!0,time:200,displayType:"flex",callback:()=>{o.classList.add("--active"),this.stepChanging=!1,this.currentStepOptional="optional"===t,this.currentStep=parseInt(a),"function"==typeof this.onStepChange&&this.onStepChange(i.toString(),a.toString())}})}}):this.stepChanging=!1}),50))}}nextStep(){this.changeStep({step:"next"})}optionalStep(){this.changeStep({step:"optional"})}prevStep(){this.changeStep({step:"prev"})}isControlFilled({control:e}){let t=e.parentElement;t&&(""!==e.value?t.classList.add("--filled"):t.classList.remove("--filled"))}filterNumber({value:e,ignoreList:t=[]}){const i=t.join(""),s=new RegExp("[^"+i+"0-9]","g");return e.toString().replace(s,"")}filterFormattedQuantity({num:e,thousands:t=".",decimals:i=",",decimalSteps:s}){const r=i?e.toString().split(i):[e.toString()],a=new RegExp(/\B(?=(\d{3})+(?!\d))/g),o=r[0].replace(a,t);let l="";return l=s&&s>0&&r[1]&&r[1].length?o+i+r[1].slice(0,s):o,l}filterMoneyAmount({num:e,currency:t="$",thousands:i=".",decimals:s=",",decimalSteps:r}){if(!e||e==t)return"";let a=this.filterNumber({value:e,ignoreList:[s||""]});return a=this.filterFormattedQuantity({num:a,thousands:i,decimals:s,decimalSteps:r}),`${t} ${a}`}filterPhoneNumber({number:e}){let t=new RegExp(/[^\d+\-() ]*/g);return e.replace(t,"")}togglePasswordVisibility({btn:e}){this.debug&&i("Password visibility toggled!");const t=e?.parentElement?.querySelector(".form__control");if(t){const i="password"===t.getAttribute("type")?"text":"password";t.setAttribute("type",i),e.classList.toggle("--hide")}}declareHandlers(){const e=this;if(!this.submitBtn)throw new Error(`There's no submit button in this form "${this.form.id}".`);this.submitBtn.addEventListener("click",(function(t){t.preventDefault(),"function"==typeof e.onBeforeSubmit&&e.onBeforeSubmit(e),e.submit()})),this.validator.realTimeValidations({form:this.form}),this.form.querySelectorAll(`.form__field.${this.classes.validateOnBlur}`).forEach((e=>{e.querySelector(".form__control")?.addEventListener("blur",(()=>{this.validator.validateField({field:e})||(this.isValid=!1)}))})),this.form.querySelectorAll(`.form__field.${this.classes.validateOnInput}`).forEach((e=>{const t=e.querySelector(".form__control");t?.addEventListener("input",(()=>{this.validator.validateField({field:e})&&this.validator.clearControlError({control:t})}))})),this.form.querySelectorAll(".form__next-step").forEach((t=>{t.addEventListener("click",e.nextStep.bind(e))})),this.form.querySelectorAll(".form__optional-step").forEach((t=>{t.addEventListener("click",e.optionalStep.bind(e))})),this.form.querySelectorAll(".form__prev-step").forEach((t=>{t.addEventListener("click",e.prevStep.bind(e))})),this.form.querySelectorAll(".form__control").forEach((e=>{this.isControlFilled({control:e}),e.addEventListener("keyup",(()=>{this.isControlFilled({control:e})})),e.addEventListener("change",(()=>{this.isControlFilled({control:e})}))})),this.form.querySelectorAll(".form__control").forEach((e=>{e.addEventListener("focus",(()=>{this.validator.clearControlError({control:e})}))})),this.form.querySelectorAll("."+this.classes.clearFieldError).forEach((i=>{i.addEventListener("click",(()=>{const s=t({element:i,className:"form__field"});s&&e.validator.clearControlError({control:s.querySelector(".form__control")})}))})),this.form.querySelectorAll(".form__field.--number input").forEach((e=>{const i=this,s=t({element:e,className:"form__field"}),r=s&&s.dataset.thousandsSeparator?s.dataset.thousandsSeparator:"",a=s&&s.dataset.decimalSeparator?s.dataset.decimalSeparator:"",o=s&&s.dataset.decimals?s.dataset.decimals:"";function l(){e.value=i.filterNumber({value:e.value,ignoreList:[a]})}e.addEventListener("focus",l),e.addEventListener("input",l),e.addEventListener("paste",l),e.addEventListener("blur",(()=>{e.value=r?i.filterFormattedQuantity({num:e.value,thousands:r,decimals:a,decimalSteps:parseInt(o)}):i.filterNumber({value:e.value})}))})),this.form.querySelectorAll(".form__field.--money-amount input").forEach((e=>{const i=this,s=t({element:e,className:"form__field"}),r=s&&s.dataset.currency?s.dataset.currency:"$",a=s&&s.dataset.thousandsSeparator?s.dataset.thousandsSeparator:".",o=s&&s.dataset.decimalSeparator?s.dataset.decimalSeparator:"",l=s&&s.dataset.decimals?s.dataset.decimals:"";function n(){e.value=i.filterNumber({value:e.value,ignoreList:[o]})}e.addEventListener("focus",n),e.addEventListener("input",n),e.addEventListener("paste",n),e.addEventListener("blur",(()=>{e.value=this.filterMoneyAmount({num:e.value,currency:r,thousands:a,decimals:o,decimalSteps:parseInt(l)})}))})),this.form.querySelectorAll(".form__field.--phone input").forEach((e=>{e.addEventListener("input",(()=>{e.value=this.filterPhoneNumber({number:e.value})})),e.addEventListener("paste",(()=>{e.value=this.filterPhoneNumber({number:e.value})}))})),this.form.querySelectorAll(".form__toggle-password-visibility").forEach((e=>{e.addEventListener("click",(()=>this.togglePasswordVisibility({btn:e})))}))}}}));
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
export default class EgoFormValidator implements EgoFormValidatorInterface {
|
|
2
|
+
customValidations: Record<string, EgoFormCustomValidation[]>;
|
|
3
|
+
classes: EgoFormCSSClassess;
|
|
4
|
+
validationMessages: EgoFormValidationMessages | null;
|
|
5
|
+
debug: boolean;
|
|
6
|
+
constructor({ customValidations, classes, customValidationMessages, debug }: EgoFormValidatorOptions);
|
|
7
|
+
validateField({ field }: {
|
|
8
|
+
field: EgoFormControl;
|
|
9
|
+
}): boolean;
|
|
10
|
+
isValidEmail({ email }: {
|
|
11
|
+
email: string;
|
|
12
|
+
}): boolean;
|
|
13
|
+
isValidCuitCuil({ num }: {
|
|
14
|
+
num: string | number;
|
|
15
|
+
}): boolean;
|
|
16
|
+
isValidUrl({ urlString }: {
|
|
17
|
+
urlString: string;
|
|
18
|
+
}): boolean;
|
|
19
|
+
isValidFileSize({ field }: {
|
|
20
|
+
field: EgoFormControl;
|
|
21
|
+
}): void;
|
|
22
|
+
displayFieldError({ control, field, errorElement }: {
|
|
23
|
+
control: EgoFormControl | null;
|
|
24
|
+
field: HTMLElement;
|
|
25
|
+
errorElement: HTMLElement | null;
|
|
26
|
+
}): void;
|
|
27
|
+
clearControlError({ control }: {
|
|
28
|
+
control: EgoFormControl | null;
|
|
29
|
+
}): void;
|
|
30
|
+
realTimeValidations({ form }: {
|
|
31
|
+
form: HTMLFormElement;
|
|
32
|
+
}): void;
|
|
33
|
+
throwError(msg: string): void;
|
|
34
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export declare const vanillaFade: ({ element, enter, time, displayType, callback }: {
|
|
2
|
+
element: HTMLElement;
|
|
3
|
+
enter?: boolean;
|
|
4
|
+
time?: number;
|
|
5
|
+
displayType?: string;
|
|
6
|
+
callback?: Function;
|
|
7
|
+
}) => void;
|
|
8
|
+
export declare const getParentByClassName: ({ element, className }: {
|
|
9
|
+
element: HTMLElement | null;
|
|
10
|
+
className: string;
|
|
11
|
+
}) => HTMLElement | null;
|
|
12
|
+
export declare const isInViewport: ({ element }: {
|
|
13
|
+
element: HTMLElement;
|
|
14
|
+
}) => boolean;
|
|
15
|
+
export declare function showLog(msg: string, type?: 'log' | 'data'): void;
|
package/package.json
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@egodesign/form",
|
|
3
|
-
"version": "1.
|
|
4
|
-
"description": "JS Form component",
|
|
3
|
+
"version": "1.7.0",
|
|
4
|
+
"description": "JS/TS Form component",
|
|
5
5
|
"module": "dist/js/egodesign.form.esm.min.js",
|
|
6
6
|
"browser": "dist/js/egodesign.form.umd.min.js",
|
|
7
7
|
"main": "dist/js/egodesign.form.cjs.min.js",
|
|
8
|
+
"types": "dist/js/egodesign.form.d.ts",
|
|
8
9
|
"scripts": {
|
|
9
10
|
"build": "rollup -c",
|
|
10
11
|
"watch": "rollup -c -w",
|
|
@@ -25,15 +26,17 @@
|
|
|
25
26
|
},
|
|
26
27
|
"homepage": "https://github.com/AgenciaEgo/egodesign-form#readme",
|
|
27
28
|
"devDependencies": {
|
|
28
|
-
"@rollup/plugin-commonjs": "^
|
|
29
|
-
"@rollup/plugin-node-resolve": "^
|
|
30
|
-
"@rollup/plugin-terser": "^0.4.
|
|
29
|
+
"@rollup/plugin-commonjs": "^28.0.2",
|
|
30
|
+
"@rollup/plugin-node-resolve": "^16.0.0",
|
|
31
|
+
"@rollup/plugin-terser": "^0.4.4",
|
|
32
|
+
"@rollup/plugin-typescript": "^12.1.2",
|
|
31
33
|
"esm": "^3.2.25",
|
|
32
|
-
"jest": "^29.
|
|
34
|
+
"jest": "^29.7.0",
|
|
33
35
|
"jest-environment-jsdom": "^29.5.0",
|
|
34
|
-
"rollup": "^
|
|
35
|
-
"
|
|
36
|
-
"
|
|
36
|
+
"rollup": "^4.34.8",
|
|
37
|
+
"sass": "^1.60.0",
|
|
38
|
+
"tslib": "^2.8.1",
|
|
39
|
+
"typescript": "^5.8.2"
|
|
37
40
|
},
|
|
38
41
|
"files": [
|
|
39
42
|
"dist"
|