@egodesign/form 1.10.0 → 1.10.2
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
CHANGED
|
@@ -76,10 +76,11 @@ const myForm = new EgoForm({
|
|
|
76
76
|
"invalid": "message",
|
|
77
77
|
}
|
|
78
78
|
},
|
|
79
|
-
onStepChange: (previous, next) => console.log(
|
|
79
|
+
onStepChange: (previous, next) => console.log(previous, next),
|
|
80
80
|
onBeforeValidation: instance => instance.resumeValidation(),
|
|
81
|
-
onValidationError: fields => console.log(fields),
|
|
82
|
-
onValidityChange: (isValid,
|
|
81
|
+
onValidationError: (fields, instance) => console.log(fields),
|
|
82
|
+
onValidityChange: (isValid, instance) => console.log('Full form validity:', isValid),
|
|
83
|
+
onCurrentStepValidityChange: (isValid, currentStep, instance) => console.log(`Step ${currentStep} validity:`, isValid),
|
|
83
84
|
onSubmitStart: () => console.log('Submit start'),
|
|
84
85
|
onBeforeSubmission: instance => instance.resumeSubmission(),
|
|
85
86
|
onSubmitEnd: () => console.log('Submit end'),
|
|
@@ -138,8 +139,9 @@ const myForm: EgoForm = new EgoForm({
|
|
|
138
139
|
},
|
|
139
140
|
onStepChange: (prev: string, next: string) => console.log(prev, next),
|
|
140
141
|
onBeforeValidation: (instance: EgoForm) => instance.resumeValidation(),
|
|
141
|
-
onValidationError: (fields: string[], instance: EgoForm) => console.log(
|
|
142
|
-
onValidityChange: (isValid: boolean, instance: EgoForm)
|
|
142
|
+
onValidationError: (fields: string[], instance: EgoForm) => console.log(fields),
|
|
143
|
+
onValidityChange: (isValid: boolean, instance: EgoForm) => console.log('Full form validity:', isValid),
|
|
144
|
+
onCurrentStepValidityChange: (isValid: boolean, currentStep: number, instance: EgoForm) => console.log(`Step ${currentStep} validity:`, isValid),
|
|
143
145
|
onSubmitStart: () => console.log('Submit start'),
|
|
144
146
|
onBeforeSubmission: (instance: EgoForm) => instance.resumeSubmission(),
|
|
145
147
|
onSubmitEnd: () => console.log('Submit end'),
|
|
@@ -251,7 +253,8 @@ const myForm: EgoForm = new EgoForm({
|
|
|
251
253
|
| --- | ----------- | ----------- |
|
|
252
254
|
| *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`.
|
|
253
255
|
| *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`.
|
|
254
|
-
| *onValidityChange* | Event triggered when the validity of the form changes. | An anonymous function or `null`.
|
|
256
|
+
| *onValidityChange* | Event triggered when the validity of the **entire form** changes (all fields). Receives `isValid` (boolean) and the form instance. Useful for tracking overall form validity and enabling/disabling final submit buttons. | An anonymous function or `null`.
|
|
257
|
+
| *onCurrentStepValidityChange* | Event triggered when the validity of the **current step** changes. Only relevant for multi-step forms. Receives `isValid` (boolean), `currentStep` (number), and the form instance. Useful for enabling/disabling "Next" buttons in multi-step forms. | An anonymous function or `null`.
|
|
255
258
|
| *onBeforeValidation* | Event triggered before the validation starts. The callback function receives a reference to the instance itself | An anonymous function or `null`.
|
|
256
259
|
| *onSubmitStart* | Event triggered when the submit starts. | An anonymous function or `null`.
|
|
257
260
|
| *onBeforeSubmission* | Event triggered before the submission starts, right after successful validation. The callback function receives a reference to the instance itself | An anonymous function or `null`.
|
|
@@ -260,6 +263,62 @@ const myForm: EgoForm = new EgoForm({
|
|
|
260
263
|
| *onError* | Event triggered when the response returns an error. | An anonymous function or `null`.
|
|
261
264
|
<br>
|
|
262
265
|
|
|
266
|
+
### Validity Tracking
|
|
267
|
+
|
|
268
|
+
EgoForm provides two separate validity tracking systems to give you fine-grained control over form state:
|
|
269
|
+
|
|
270
|
+
#### Properties
|
|
271
|
+
- **`isValid`**: Tracks the validity of **all fields** in the entire form, regardless of the current step in multi-step forms.
|
|
272
|
+
- **`isCurrentStepValid`**: Tracks the validity of the current step (or all visited steps up to the current one in multi-step forms).
|
|
273
|
+
|
|
274
|
+
#### Events
|
|
275
|
+
- **`onValidityChange`**: Fires when `isValid` changes. Useful for:
|
|
276
|
+
- Enabling/disabling the final submit button
|
|
277
|
+
- Showing overall form progress indicators
|
|
278
|
+
- Tracking complete form validity in analytics
|
|
279
|
+
|
|
280
|
+
- **`onCurrentStepValidityChange`**: Fires when `isCurrentStepValid` changes. Useful for:
|
|
281
|
+
- Enabling/disabling "Next" buttons in multi-step forms
|
|
282
|
+
- Showing step-specific validation feedback
|
|
283
|
+
- Tracking step completion progress
|
|
284
|
+
|
|
285
|
+
#### Multi-Step Forms
|
|
286
|
+
In multi-step forms, both validity states are tracked independently:
|
|
287
|
+
- `isCurrentStepValid` represents validation of fields up to the highest visited step
|
|
288
|
+
- `isValid` represents validation of ALL fields in the entire form
|
|
289
|
+
- When changing steps, `onCurrentStepValidityChange` fires with the step validity
|
|
290
|
+
- Both properties are updated on blur validation and form submission
|
|
291
|
+
|
|
292
|
+
#### Regular Forms
|
|
293
|
+
In regular (single-step) forms, both properties behave identically since there's only one "step".
|
|
294
|
+
|
|
295
|
+
#### Example Usage
|
|
296
|
+
|
|
297
|
+
```javascript
|
|
298
|
+
const form = new EgoForm({
|
|
299
|
+
element: document.querySelector('#myMultiStepForm'),
|
|
300
|
+
|
|
301
|
+
// Track overall form validity (all fields)
|
|
302
|
+
onValidityChange: (isValid, formInstance) => {
|
|
303
|
+
const submitBtn = document.querySelector('#finalSubmit');
|
|
304
|
+
submitBtn.disabled = !isValid;
|
|
305
|
+
console.log('Form is', isValid ? 'valid' : 'invalid');
|
|
306
|
+
},
|
|
307
|
+
|
|
308
|
+
// Track current step validity
|
|
309
|
+
onCurrentStepValidityChange: (isValid, currentStep, formInstance) => {
|
|
310
|
+
const nextBtn = document.querySelector('#nextStepBtn');
|
|
311
|
+
nextBtn.disabled = !isValid;
|
|
312
|
+
console.log(`Step ${currentStep} is`, isValid ? 'valid' : 'invalid');
|
|
313
|
+
}
|
|
314
|
+
});
|
|
315
|
+
|
|
316
|
+
// You can also access the properties directly
|
|
317
|
+
console.log(form.isValid); // true/false - entire form
|
|
318
|
+
console.log(form.isCurrentStepValid); // true/false - current step
|
|
319
|
+
```
|
|
320
|
+
<br>
|
|
321
|
+
|
|
263
322
|
### Validation:
|
|
264
323
|
In order to use validations, you must set the correct data type for each field. You can do so by adding a `type` data attribute to the field element, e.g; `<div class="form__field" data-type="text">`. This attribute will be used by the validator to run certain tests. Here's a list of the different available data types:
|
|
265
324
|
| Name | Description |
|
|
@@ -1 +1 @@
|
|
|
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}async 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"),n=e.querySelector(".form__control"),l=s?e.querySelector(".form__control:checked"):null,u=n?n.getAttribute("name"):null,d=Object.keys(this.customValidations),c=e.dataset.minLength?Number(e.dataset.minLength):null,h=e.dataset.maxLength?Number(e.dataset.maxLength):null;if(n||this.throwError("control not found."),u||this.throwError("control name not found."),this.debug&&i(`validating field "${u}"`),a&&!r&&!n?.value)return!0;if(r&&!n?.value)return o&&this.validationMessages&&u&&(o.textContent=this.validationMessages[u]?this.validationMessages[u].empty:this.validationMessages.default.empty),this.displayFieldError({control:n,field:e,errorElement:o}),!1;if(n?.value){if(c&&n.value.length<c)return o&&this.validationMessages&&(o.textContent=this.validationMessages.default.minLength.replace("[[var]]",c.toString())),this.displayFieldError({control:n,field:e,errorElement:o}),!1;if(h&&n.value.length>h)return o&&this.validationMessages&&(o.textContent=this.validationMessages.default.maxLength.replace("[[var]]",h.toString())),this.displayFieldError({control:n,field:e,errorElement:o}),!1}if(s&&!l){if(!a)return this.displayFieldError({control:n,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(n&&n.value&&!this.isValidEmail({email:n.value}))return o&&this.validationMessages&&(o.textContent=this.validationMessages.email.invalid),this.displayFieldError({control:n,field:e,errorElement:o}),!1;break;case"password_repeat":{const t=document.getElementById("password");if(t&&n?.value!=t?.value)return o&&this.validationMessages&&(o.textContent=this.validationMessages.password_repeat.unequal),this.displayFieldError({control:n,field:e,errorElement:o}),!1;break}case"cuil":case"cuit":if(n&&n.value&&!this.isValidCuitCuil({num:n.value}))return o&&this.validationMessages&&(o.textContent=this.validationMessages.cuil.invalid),this.displayFieldError({control:n,field:e,errorElement:o}),!1;break;case"url":if(n&&n.value&&!this.isValidUrl({urlString:n.value}))return o&&this.validationMessages&&(o.textContent=this.validationMessages.url.invalid),this.displayFieldError({control:n,field:e,errorElement:o}),!1;break;case"money":if(n){const t=n.dataset.currency?n.dataset.currency:"$";if(""==n.value||n.value==t)return o&&this.validationMessages&&(o.textContent=this.validationMessages.default.empty),this.displayFieldError({control:n,field:e,errorElement:o}),!1}break;case"single-checkbox":if(n&&n.hasOwnProperty("checked")){if(!n.checked)return this.displayFieldError({control:n,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(n){if(!await i.condition(n.value,u))return o&&(o.textContent=i.message||""),this.displayFieldError({control:n,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,n=i&&i.files?i.files[0].size:0,l=i&&i.files?parseFloat((n/1048576).toFixed(1)):0;l>r||!n?(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&&(n?l<s?t=this.validationMessages.file.min_size.replace("[[var]]",s+" MB"):l>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:n,fieldGroups:l,extraFields:u,serializerIgnoreList:d,customValidations:c,customValidationMessages:h,onStepChange:m,onValidationError:f,onSubmitStart:p,onSubmitEnd:g,onSuccess:F,onError:v,onBeforeValidation:b,onBeforeSubmit:S,onBeforeSubmission:y,onValidityChange:E,resetOnSuccess:_,resetLoaderOnSuccess:L,scrollOnError:q,scrollOnErrorOffset:x,preventValidation:C,preventSubmit:V,preventSubmission:D,disbleStepsTransition:A,debug:w}){this.form=e,this.submitType=s||"fetch",this.submitDataFormat=a||"formData",this.requestHeaders=n||{},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:h||null,debug:w??!1}),this.onValidationError=f??null,this.onStepChange=m??null,this.onSubmitStart=p??null,this.onSubmitEnd=g??null,this.onSuccess=F??null,this.onError=v??null,this.onBeforeValidation=b??null,this.onBeforeSubmit=S??null,this.onBeforeSubmission=y??null,this.onValidityChange=E??null,this.fieldGroups=l??null,this.extraFields=u??[],this.hasFile=!1,this.serializerIgnoreList=d||[],this.resetOnSuccess=_??!0,this.resetLoaderOnSuccess=L??!0,this.scrollOnError=q??!0,this.scrollOnErrorOffset=x||0;const M=this.form.querySelector(".form__step.--active");if(this.currentStep=M?Number(M.dataset.step):0,this.highestVisitedStep=this.currentStep,this.currentStepOptional=!1,this.disbleStepsTransition=A??!1,this.stepChanging=!1,this.preventValidation=C??!1,this.preventSubmit=V??!1,this.preventSubmission=D??!1,this.debug=w??!1,this.declareHandlers(),!this.actionUrl||""===this.actionUrl)throw new Error("The form doesn't have an action attribute or submitUrl wasn't provided.");this.checkForRequiredFields()&&(this.isValid=!1),this.debug&&i("initialized!")}checkForRequiredFields(){return this.form.querySelectorAll(`.${this.classes.requiredField}`).length>0}setValidity(e){this.isValid!==e&&(this.isValid=e,"function"==typeof this.onValidityChange&&this.onValidityChange(e,this))}submit(){"function"==typeof this.onBeforeValidation&&this.onBeforeValidation(this),this.onBeforeValidation||"function"!=typeof this.onBeforeSubmit||(this.debug&&i("WARNING: onBeforeSubmit will be deprecated in future versions, use onBeforeValidation or onBeforeSubmission instead."),this.onBeforeSubmit(this)),this.preventValidation||this.preventSubmit||(this.debug&&i("WARNING: preventSubmit will be deprecated in future versions, use preventValidation or preventSubmission instead."),this.resumeValidation())}resumeSubmit(){this.resumeValidation()}async resumeValidation(){this.debug&&i(`submitting using ${this.submitType}!`),this.submittingForm({submitting:!0});let e=!0;const t=[];let s;if(this.currentStep>0&&this.highestVisitedStep>0){s=[];for(let e=1;e<=this.highestVisitedStep;e++){const t=this.form.querySelector(`.form__step[data-step="${e}"]`);if(t){const e=Array.from(t.querySelectorAll(".form__field"));s.push(...e)}const i=this.form.querySelector(`.form__step[data-step="${e}b"]`);if(i&&i.classList.contains("--active")){const e=Array.from(i.querySelectorAll(".form__field"));s.push(...e)}}}else s=Array.from(this.form.querySelectorAll(".form__field"));for(const i of s){if(!await this.validator.validateField({field:i})){const s=i.querySelector(".form__control");s&&t.push(s.name),e=!1}}if(this.setValidity(e),this.isValid)"function"==typeof this.onBeforeSubmission&&this.onBeforeSubmission(this),this.preventSubmission||this.resumeSubmission();else{if("function"==typeof this.onValidationError&&this.onValidationError(t,this),this.debug&&i(`this fields have failed validation: ${t.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})&&(r=e,a=this.scrollOnErrorOffset||0,r&&window.scrollTo({behavior:"smooth",top:r.getBoundingClientRect().top-document.body.getBoundingClientRect().top-a}))}this.submittingForm({submitting:!1,force:!0}),this.stepChanging=!1}var r,a}resumeSubmission(){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:"formData"===this.submitDataFormat});fetch(this.actionUrl,{method:this.submitMethod,headers:{...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()}}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.highestVisitedStep=1,this.changeStep({step:1}))}async 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=s?.querySelectorAll(`.${this.classes.requiredIfFilledField}`),o="next"===t?this.currentStep+1:"prev"!==t||this.currentStepOptional?"optional"===t?this.currentStep+"b":"number"==typeof t?t:this.currentStep:this.currentStep-1,n=this.form.querySelector('[data-step="'+o+'"]');if(this.currentStep!==o||this.currentStepOptional){this.stepChanging=!0;let l=!0;const u=[];if((r||a)&&("next"===t||"optional"===t)){if(r)for(const e of Array.from(r))if(!await this.validator.validateField({field:e})){l=!1;const t=e.querySelector(".form__control");u.push(t?.getAttribute("name"))}if(a)for(const e of Array.from(a)){const t=e.querySelector(".form__control");t?.value&&(await this.validator.validateField({field:e})||(l=!1,u.push(t?.getAttribute("name"))))}}if(this.setValidity(l),this.isValid){if(s&&n){const r=()=>{n.classList.add("--active"),this.stepChanging=!1,this.currentStepOptional="optional"===t,this.currentStep=parseInt(o),("next"===t||"optional"===t)&&this.currentStep>this.highestVisitedStep&&(this.highestVisitedStep=this.currentStep),"function"==typeof this.onStepChange&&this.onStepChange(i.toString(),o.toString())};this.disbleStepsTransition?(s.classList.remove("--active"),r()):e({element:s,enter:!1,time:200,displayType:"flex",callback:()=>{s.classList.remove("--active"),e({element:n,enter:!0,time:200,displayType:"flex",callback:r})}})}}else this.stepChanging=!1,"function"==typeof this.onValidationError&&this.onValidationError(u,this)}}}nextStep(){return this.changeStep({step:"next"})}optionalStep(){return this.changeStep({step:"optional"})}prevStep(){return 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().trim().split(i):[e.toString()],a=new RegExp(/\B(?=(\d{3})+(?!\d))/g),o=r[0].replace(a,t);let n="";return n=s&&s>0&&r[1]&&r[1].length?o+i+r[1].slice(0,s):o,n}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,"").trim()}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(e=!1){const i=this;if(!this.submitBtn)throw new Error(`There's no submit button in this form "${this.form.id}".`);e||this.submitBtn.addEventListener("click",(function(e){e.preventDefault(),i.submit()})),this.validator.realTimeValidations({form:this.form}),this.form.querySelectorAll(`.form__field.${this.classes.validateOnBlur}`).forEach((e=>{e.querySelector(".form__control")?.addEventListener("blur",(async()=>{await this.validator.validateField({field:e})||this.setValidity(!1)}))})),this.form.querySelectorAll(`.form__field.${this.classes.validateOnInput}`).forEach((e=>{const t=e.querySelector(".form__control");t?.addEventListener("input",(async()=>{await this.validator.validateField({field:e})&&this.validator.clearControlError({control:t})}))})),e||(this.form.querySelectorAll(".form__next-step").forEach((e=>{e.addEventListener("click",i.nextStep.bind(i))})),this.form.querySelectorAll(".form__optional-step").forEach((e=>{e.addEventListener("click",i.optionalStep.bind(i))})),this.form.querySelectorAll(".form__prev-step").forEach((e=>{e.addEventListener("click",i.prevStep.bind(i))}))),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((e=>{e.addEventListener("click",(()=>{const s=t({element:e,className:"form__field"});s&&i.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 n(){e.value=i.filterNumber({value:e.value,ignoreList:[a]})}e.addEventListener("focus",n),e.addEventListener("input",n),e.addEventListener("paste",n),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:"",n=s&&s.dataset.decimals?s.dataset.decimals:"";function l(){e.value=i.filterNumber({value:e.value,ignoreList:[o]})}e.addEventListener("focus",l),e.addEventListener("input",l),e.addEventListener("paste",l),e.addEventListener("blur",(()=>{e.value=this.filterMoneyAmount({num:e.value,currency:r,thousands:a,decimals:o,decimalSteps:parseInt(n)})}))})),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})))}))}refresh(){this.declareHandlers(!0)}};
|
|
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}async validateField({field:e,silent:t=!1}){const s=e.dataset.type||"",r=["radio","checkbox"].includes(s),a=e.classList.contains(this.classes.requiredField),o=e.classList.contains(this.classes.requiredIfFilledField),n=e.querySelector(".form__error"),l=e.querySelector(".form__control"),u=r?e.querySelector(".form__control:checked"):null,d=l?l.getAttribute("name"):null,c=Object.keys(this.customValidations),h=e.dataset.minLength?Number(e.dataset.minLength):null,m=e.dataset.maxLength?Number(e.dataset.maxLength):null;if(l||this.throwError("control not found."),d||this.throwError("control name not found."),this.debug&&i(`validating field "${d}"`),o&&!a&&!l?.value)return!0;if(a&&!l?.value)return t||(n&&this.validationMessages&&d&&(n.textContent=this.validationMessages[d]?this.validationMessages[d].empty:this.validationMessages.default.empty),this.displayFieldError({control:l,field:e,errorElement:n})),!1;if(l?.value){if(h&&l.value.length<h)return t||(n&&this.validationMessages&&(n.textContent=this.validationMessages.default.minLength.replace("[[var]]",h.toString())),this.displayFieldError({control:l,field:e,errorElement:n})),!1;if(m&&l.value.length>m)return t||(n&&this.validationMessages&&(n.textContent=this.validationMessages.default.maxLength.replace("[[var]]",m.toString())),this.displayFieldError({control:l,field:e,errorElement:n})),!1}if(r&&!u){if(!o)return t||(this.displayFieldError({control:l,field:e,errorElement:n}),n&&this.validationMessages&&d&&(n.textContent=this.validationMessages[d]?this.validationMessages[d].empty:this.validationMessages.default.empty)),!1}else{switch(s){case"email":if(l&&l.value&&!this.isValidEmail({email:l.value}))return t||(n&&this.validationMessages&&(n.textContent=this.validationMessages.email.invalid),this.displayFieldError({control:l,field:e,errorElement:n})),!1;break;case"password_repeat":{const i=document.getElementById("password");if(i&&l?.value!=i?.value)return t||(n&&this.validationMessages&&(n.textContent=this.validationMessages.password_repeat.unequal),this.displayFieldError({control:l,field:e,errorElement:n})),!1;break}case"cuil":case"cuit":if(l&&l.value&&!this.isValidCuitCuil({num:l.value}))return t||(n&&this.validationMessages&&(n.textContent=this.validationMessages.cuil.invalid),this.displayFieldError({control:l,field:e,errorElement:n})),!1;break;case"url":if(l&&l.value&&!this.isValidUrl({urlString:l.value}))return t||(n&&this.validationMessages&&(n.textContent=this.validationMessages.url.invalid),this.displayFieldError({control:l,field:e,errorElement:n})),!1;break;case"money":if(l){const i=l.dataset.currency?l.dataset.currency:"$";if(""==l.value||l.value==i)return t||(n&&this.validationMessages&&(n.textContent=this.validationMessages.default.empty),this.displayFieldError({control:l,field:e,errorElement:n})),!1}break;case"single-checkbox":if(l&&l.hasOwnProperty("checked")){if(!l.checked)return t||(this.displayFieldError({control:l,field:e,errorElement:n}),n&&this.validationMessages&&d&&(n.textContent=this.validationMessages[d]?this.validationMessages[d].empty:this.validationMessages.default.empty)),!1}}for(const i of c)if(s===i)for(const i of this.customValidations[s])if(l){if(!await i.condition(l.value,d))return t||(n&&(n.textContent=i.message||""),this.displayFieldError({control:l,field:e,errorElement:n})),!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,n=i&&i.files?i.files[0].size:0,l=i&&i.files?parseFloat((n/1048576).toFixed(1)):0;l>r||!n?(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&&(n?l<s?t=this.validationMessages.file.min_size.replace("[[var]]",s+" MB"):l>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:n,fieldGroups:l,extraFields:u,serializerIgnoreList:d,customValidations:c,customValidationMessages:h,onStepChange:m,onValidationError:f,onSubmitStart:p,onSubmitEnd:g,onSuccess:F,onError:v,onBeforeValidation:S,onBeforeSubmit:y,onBeforeSubmission:b,onValidityChange:E,onCurrentStepValidityChange:_,resetOnSuccess:C,resetLoaderOnSuccess:L,scrollOnError:V,scrollOnErrorOffset:q,preventValidation:x,preventSubmit:A,preventSubmission:w,disbleStepsTransition:D,debug:M}){this.form=e,this.submitType=s||"fetch",this.submitDataFormat=a||"formData",this.requestHeaders=n||{},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.isCurrentStepValid=!0,this.validator=new r({customValidations:c||{},classes:this.classes,customValidationMessages:h||null,debug:M??!1}),this.onValidationError=f??null,this.onStepChange=m??null,this.onSubmitStart=p??null,this.onSubmitEnd=g??null,this.onSuccess=F??null,this.onError=v??null,this.onBeforeValidation=S??null,this.onBeforeSubmit=y??null,this.onBeforeSubmission=b??null,this.onValidityChange=E??null,this.onCurrentStepValidityChange=_??null,this.fieldGroups=l??null,this.extraFields=u??[],this.hasFile=!1,this.serializerIgnoreList=d||[],this.resetOnSuccess=C??!0,this.resetLoaderOnSuccess=L??!0,this.scrollOnError=V??!0,this.scrollOnErrorOffset=q||0;const z=this.form.querySelector(".form__step.--active");if(this.currentStep=z?Number(z.dataset.step):0,this.highestVisitedStep=this.currentStep,this.currentStepOptional=!1,this.disbleStepsTransition=D??!1,this.stepChanging=!1,this.preventValidation=x??!1,this.preventSubmit=A??!1,this.preventSubmission=w??!1,this.debug=M??!1,this.declareHandlers(),!this.actionUrl||""===this.actionUrl)throw new Error("The form doesn't have an action attribute or submitUrl wasn't provided.");this.checkForRequiredFields()&&(this.setValidity(!1),this.setCurrentStepValidity(!1)),this.debug&&i("initialized!")}checkForRequiredFields(){return this.form.querySelectorAll(`.${this.classes.requiredField}`).length>0}setValidity(e){this.isValid!==e&&(this.isValid=e,"function"==typeof this.onValidityChange&&this.onValidityChange(e,this))}setCurrentStepValidity(e){this.isCurrentStepValid!==e&&(this.isCurrentStepValid=e,"function"==typeof this.onCurrentStepValidityChange&&this.onCurrentStepValidityChange(e,this.currentStep,this))}async validateAllFields(){let e=!0;const t=Array.from(this.form.querySelectorAll(".form__field"));for(const i of t){await this.validator.validateField({field:i,silent:!0})||(e=!1)}return this.setValidity(e),e}submit(){"function"==typeof this.onBeforeValidation&&this.onBeforeValidation(this),this.onBeforeValidation||"function"!=typeof this.onBeforeSubmit||(this.debug&&i("WARNING: onBeforeSubmit will be deprecated in future versions, use onBeforeValidation or onBeforeSubmission instead."),this.onBeforeSubmit(this)),this.preventValidation||this.preventSubmit||(this.debug&&i("WARNING: preventSubmit will be deprecated in future versions, use preventValidation or preventSubmission instead."),this.resumeValidation())}resumeSubmit(){this.resumeValidation()}async resumeValidation(){this.debug&&i(`submitting using ${this.submitType}!`),this.submittingForm({submitting:!0});let e=!0;const t=[];let s;if(this.currentStep>0&&this.highestVisitedStep>0){s=[];for(let e=1;e<=this.highestVisitedStep;e++){const t=this.form.querySelector(`.form__step[data-step="${e}"]`);if(t){const e=Array.from(t.querySelectorAll(".form__field"));s.push(...e)}const i=this.form.querySelector(`.form__step[data-step="${e}b"]`);if(i&&i.classList.contains("--active")){const e=Array.from(i.querySelectorAll(".form__field"));s.push(...e)}}}else s=Array.from(this.form.querySelectorAll(".form__field"));for(const i of s){if(!await this.validator.validateField({field:i})){const s=i.querySelector(".form__control");s&&t.push(s.name),e=!1}}if(this.setCurrentStepValidity(e),await this.validateAllFields(),this.isCurrentStepValid)"function"==typeof this.onBeforeSubmission&&this.onBeforeSubmission(this),this.preventSubmission||this.resumeSubmission();else{if("function"==typeof this.onValidationError&&this.onValidationError(t,this),this.debug&&i(`this fields have failed validation: ${t.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})&&(r=e,a=this.scrollOnErrorOffset||0,r&&window.scrollTo({behavior:"smooth",top:r.getBoundingClientRect().top-document.body.getBoundingClientRect().top-a}))}this.submittingForm({submitting:!1,force:!0}),this.stepChanging=!1}var r,a}resumeSubmission(){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:"formData"===this.submitDataFormat});fetch(this.actionUrl,{method:this.submitMethod,headers:{...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()}}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.highestVisitedStep=1,this.changeStep({step:1}))}async 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=s?.querySelectorAll(`.${this.classes.requiredIfFilledField}`),o="next"===t?this.currentStep+1:"prev"!==t||this.currentStepOptional?"optional"===t?this.currentStep+"b":"number"==typeof t?t:this.currentStep:this.currentStep-1,n=this.form.querySelector('[data-step="'+o+'"]');if(this.currentStep!==o||this.currentStepOptional){this.stepChanging=!0;let l=!0;const u=[];if((r||a)&&("next"===t||"optional"===t)){if(r)for(const e of Array.from(r))if(!await this.validator.validateField({field:e})){l=!1;const t=e.querySelector(".form__control");u.push(t?.getAttribute("name"))}if(a)for(const e of Array.from(a)){const t=e.querySelector(".form__control");t?.value&&(await this.validator.validateField({field:e})||(l=!1,u.push(t?.getAttribute("name"))))}}if(this.setCurrentStepValidity(l),await this.validateAllFields(),l){if(s&&n){const r=()=>{n.classList.add("--active"),this.stepChanging=!1,this.currentStepOptional="optional"===t,this.currentStep=parseInt(o),("next"===t||"optional"===t)&&this.currentStep>this.highestVisitedStep&&(this.highestVisitedStep=this.currentStep),"function"==typeof this.onStepChange&&this.onStepChange(i.toString(),o.toString())};this.disbleStepsTransition?(s.classList.remove("--active"),r()):e({element:s,enter:!1,time:200,displayType:"flex",callback:()=>{s.classList.remove("--active"),e({element:n,enter:!0,time:200,displayType:"flex",callback:r})}})}}else this.stepChanging=!1,"function"==typeof this.onValidationError&&this.onValidationError(u,this)}}}nextStep(){return this.changeStep({step:"next"})}optionalStep(){return this.changeStep({step:"optional"})}prevStep(){return 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().trim().split(i):[e.toString()],a=new RegExp(/\B(?=(\d{3})+(?!\d))/g),o=r[0].replace(a,t);let n="";return n=s&&s>0&&r[1]&&r[1].length?o+i+r[1].slice(0,s):o,n}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,"").trim()}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(e=!1){const i=this;if(!this.submitBtn)throw new Error(`There's no submit button in this form "${this.form.id}".`);e||this.submitBtn.addEventListener("click",(function(e){e.preventDefault(),i.submit()})),this.validator.realTimeValidations({form:this.form}),this.form.querySelectorAll(`.form__field.${this.classes.validateOnBlur}`).forEach((e=>{e.querySelector(".form__control")?.addEventListener("blur",(async()=>{await this.validator.validateField({field:e}),await this.validateAllFields()}))})),this.form.querySelectorAll(`.form__field.${this.classes.validateOnInput}`).forEach((e=>{const t=e.querySelector(".form__control");t?.addEventListener("input",(async()=>{await this.validator.validateField({field:e})&&this.validator.clearControlError({control:t})}))})),e||(this.form.querySelectorAll(".form__next-step").forEach((e=>{e.addEventListener("click",i.nextStep.bind(i))})),this.form.querySelectorAll(".form__optional-step").forEach((e=>{e.addEventListener("click",i.optionalStep.bind(i))})),this.form.querySelectorAll(".form__prev-step").forEach((e=>{e.addEventListener("click",i.prevStep.bind(i))}))),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((e=>{e.addEventListener("click",(()=>{const s=t({element:e,className:"form__field"});s&&i.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 n(){e.value=i.filterNumber({value:e.value,ignoreList:[a]})}e.addEventListener("focus",n),e.addEventListener("input",n),e.addEventListener("paste",n),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:"",n=s&&s.dataset.decimals?s.dataset.decimals:"";function l(){e.value=i.filterNumber({value:e.value,ignoreList:[o]})}e.addEventListener("focus",l),e.addEventListener("input",l),e.addEventListener("paste",l),e.addEventListener("blur",(()=>{e.value=this.filterMoneyAmount({num:e.value,currency:r,thousands:a,decimals:o,decimalSteps:parseInt(n)})}))})),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})))}))}refresh(){this.declareHandlers(!0)}};
|
|
@@ -66,12 +66,14 @@ export default class EgoForm implements EgoFormInterface {
|
|
|
66
66
|
onBeforeSubmit: Function | null;
|
|
67
67
|
onBeforeSubmission: Function | null;
|
|
68
68
|
onValidityChange: Function | null;
|
|
69
|
+
onCurrentStepValidityChange: Function | null;
|
|
69
70
|
currentStep: number;
|
|
70
71
|
currentStepOptional: boolean;
|
|
71
72
|
highestVisitedStep: number;
|
|
72
73
|
stepChanging: boolean;
|
|
73
74
|
disbleStepsTransition: boolean;
|
|
74
75
|
isValid: boolean;
|
|
76
|
+
isCurrentStepValid: boolean;
|
|
75
77
|
hasFile: boolean;
|
|
76
78
|
resetOnSuccess: boolean;
|
|
77
79
|
resetLoaderOnSuccess: boolean;
|
|
@@ -81,9 +83,11 @@ export default class EgoForm implements EgoFormInterface {
|
|
|
81
83
|
preventSubmit: boolean;
|
|
82
84
|
preventSubmission: boolean;
|
|
83
85
|
debug: boolean;
|
|
84
|
-
constructor({ element, classes, submitType, submitDataFormat, submitUrl, requestHeaders, fieldGroups, extraFields, serializerIgnoreList, customValidations, customValidationMessages, onStepChange, onValidationError, onSubmitStart, onSubmitEnd, onSuccess, onError, onBeforeValidation, onBeforeSubmit, onBeforeSubmission, onValidityChange, resetOnSuccess, resetLoaderOnSuccess, scrollOnError, scrollOnErrorOffset, preventValidation, preventSubmit, preventSubmission, disbleStepsTransition, debug }: EgoFormOptions);
|
|
86
|
+
constructor({ element, classes, submitType, submitDataFormat, submitUrl, requestHeaders, fieldGroups, extraFields, serializerIgnoreList, customValidations, customValidationMessages, onStepChange, onValidationError, onSubmitStart, onSubmitEnd, onSuccess, onError, onBeforeValidation, onBeforeSubmit, onBeforeSubmission, onValidityChange, onCurrentStepValidityChange, resetOnSuccess, resetLoaderOnSuccess, scrollOnError, scrollOnErrorOffset, preventValidation, preventSubmit, preventSubmission, disbleStepsTransition, debug }: EgoFormOptions);
|
|
85
87
|
checkForRequiredFields(): boolean;
|
|
86
88
|
setValidity(isValid: boolean): void;
|
|
89
|
+
setCurrentStepValidity(isValid: boolean): void;
|
|
90
|
+
validateAllFields(): Promise<boolean>;
|
|
87
91
|
submit(): void;
|
|
88
92
|
resumeSubmit(): void;
|
|
89
93
|
resumeValidation(): Promise<void>;
|
|
@@ -1 +1 @@
|
|
|
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}async 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"),n=e.querySelector(".form__control"),l=s?e.querySelector(".form__control:checked"):null,u=n?n.getAttribute("name"):null,d=Object.keys(this.customValidations),c=e.dataset.minLength?Number(e.dataset.minLength):null,h=e.dataset.maxLength?Number(e.dataset.maxLength):null;if(n||this.throwError("control not found."),u||this.throwError("control name not found."),this.debug&&i(`validating field "${u}"`),a&&!r&&!n?.value)return!0;if(r&&!n?.value)return o&&this.validationMessages&&u&&(o.textContent=this.validationMessages[u]?this.validationMessages[u].empty:this.validationMessages.default.empty),this.displayFieldError({control:n,field:e,errorElement:o}),!1;if(n?.value){if(c&&n.value.length<c)return o&&this.validationMessages&&(o.textContent=this.validationMessages.default.minLength.replace("[[var]]",c.toString())),this.displayFieldError({control:n,field:e,errorElement:o}),!1;if(h&&n.value.length>h)return o&&this.validationMessages&&(o.textContent=this.validationMessages.default.maxLength.replace("[[var]]",h.toString())),this.displayFieldError({control:n,field:e,errorElement:o}),!1}if(s&&!l){if(!a)return this.displayFieldError({control:n,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(n&&n.value&&!this.isValidEmail({email:n.value}))return o&&this.validationMessages&&(o.textContent=this.validationMessages.email.invalid),this.displayFieldError({control:n,field:e,errorElement:o}),!1;break;case"password_repeat":{const t=document.getElementById("password");if(t&&n?.value!=t?.value)return o&&this.validationMessages&&(o.textContent=this.validationMessages.password_repeat.unequal),this.displayFieldError({control:n,field:e,errorElement:o}),!1;break}case"cuil":case"cuit":if(n&&n.value&&!this.isValidCuitCuil({num:n.value}))return o&&this.validationMessages&&(o.textContent=this.validationMessages.cuil.invalid),this.displayFieldError({control:n,field:e,errorElement:o}),!1;break;case"url":if(n&&n.value&&!this.isValidUrl({urlString:n.value}))return o&&this.validationMessages&&(o.textContent=this.validationMessages.url.invalid),this.displayFieldError({control:n,field:e,errorElement:o}),!1;break;case"money":if(n){const t=n.dataset.currency?n.dataset.currency:"$";if(""==n.value||n.value==t)return o&&this.validationMessages&&(o.textContent=this.validationMessages.default.empty),this.displayFieldError({control:n,field:e,errorElement:o}),!1}break;case"single-checkbox":if(n&&n.hasOwnProperty("checked")){if(!n.checked)return this.displayFieldError({control:n,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(n){if(!await i.condition(n.value,u))return o&&(o.textContent=i.message||""),this.displayFieldError({control:n,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,n=i&&i.files?i.files[0].size:0,l=i&&i.files?parseFloat((n/1048576).toFixed(1)):0;l>r||!n?(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&&(n?l<s?t=this.validationMessages.file.min_size.replace("[[var]]",s+" MB"):l>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:n,fieldGroups:l,extraFields:u,serializerIgnoreList:d,customValidations:c,customValidationMessages:h,onStepChange:m,onValidationError:f,onSubmitStart:p,onSubmitEnd:g,onSuccess:F,onError:v,onBeforeValidation:b,onBeforeSubmit:S,onBeforeSubmission:y,onValidityChange:E,resetOnSuccess:_,resetLoaderOnSuccess:L,scrollOnError:q,scrollOnErrorOffset:x,preventValidation:C,preventSubmit:V,preventSubmission:D,disbleStepsTransition:A,debug:w}){this.form=e,this.submitType=s||"fetch",this.submitDataFormat=a||"formData",this.requestHeaders=n||{},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:h||null,debug:w??!1}),this.onValidationError=f??null,this.onStepChange=m??null,this.onSubmitStart=p??null,this.onSubmitEnd=g??null,this.onSuccess=F??null,this.onError=v??null,this.onBeforeValidation=b??null,this.onBeforeSubmit=S??null,this.onBeforeSubmission=y??null,this.onValidityChange=E??null,this.fieldGroups=l??null,this.extraFields=u??[],this.hasFile=!1,this.serializerIgnoreList=d||[],this.resetOnSuccess=_??!0,this.resetLoaderOnSuccess=L??!0,this.scrollOnError=q??!0,this.scrollOnErrorOffset=x||0;const M=this.form.querySelector(".form__step.--active");if(this.currentStep=M?Number(M.dataset.step):0,this.highestVisitedStep=this.currentStep,this.currentStepOptional=!1,this.disbleStepsTransition=A??!1,this.stepChanging=!1,this.preventValidation=C??!1,this.preventSubmit=V??!1,this.preventSubmission=D??!1,this.debug=w??!1,this.declareHandlers(),!this.actionUrl||""===this.actionUrl)throw new Error("The form doesn't have an action attribute or submitUrl wasn't provided.");this.checkForRequiredFields()&&(this.isValid=!1),this.debug&&i("initialized!")}checkForRequiredFields(){return this.form.querySelectorAll(`.${this.classes.requiredField}`).length>0}setValidity(e){this.isValid!==e&&(this.isValid=e,"function"==typeof this.onValidityChange&&this.onValidityChange(e,this))}submit(){"function"==typeof this.onBeforeValidation&&this.onBeforeValidation(this),this.onBeforeValidation||"function"!=typeof this.onBeforeSubmit||(this.debug&&i("WARNING: onBeforeSubmit will be deprecated in future versions, use onBeforeValidation or onBeforeSubmission instead."),this.onBeforeSubmit(this)),this.preventValidation||this.preventSubmit||(this.debug&&i("WARNING: preventSubmit will be deprecated in future versions, use preventValidation or preventSubmission instead."),this.resumeValidation())}resumeSubmit(){this.resumeValidation()}async resumeValidation(){this.debug&&i(`submitting using ${this.submitType}!`),this.submittingForm({submitting:!0});let e=!0;const t=[];let s;if(this.currentStep>0&&this.highestVisitedStep>0){s=[];for(let e=1;e<=this.highestVisitedStep;e++){const t=this.form.querySelector(`.form__step[data-step="${e}"]`);if(t){const e=Array.from(t.querySelectorAll(".form__field"));s.push(...e)}const i=this.form.querySelector(`.form__step[data-step="${e}b"]`);if(i&&i.classList.contains("--active")){const e=Array.from(i.querySelectorAll(".form__field"));s.push(...e)}}}else s=Array.from(this.form.querySelectorAll(".form__field"));for(const i of s){if(!await this.validator.validateField({field:i})){const s=i.querySelector(".form__control");s&&t.push(s.name),e=!1}}if(this.setValidity(e),this.isValid)"function"==typeof this.onBeforeSubmission&&this.onBeforeSubmission(this),this.preventSubmission||this.resumeSubmission();else{if("function"==typeof this.onValidationError&&this.onValidationError(t,this),this.debug&&i(`this fields have failed validation: ${t.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})&&(r=e,a=this.scrollOnErrorOffset||0,r&&window.scrollTo({behavior:"smooth",top:r.getBoundingClientRect().top-document.body.getBoundingClientRect().top-a}))}this.submittingForm({submitting:!1,force:!0}),this.stepChanging=!1}var r,a}resumeSubmission(){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:"formData"===this.submitDataFormat});fetch(this.actionUrl,{method:this.submitMethod,headers:{...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()}}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.highestVisitedStep=1,this.changeStep({step:1}))}async 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=s?.querySelectorAll(`.${this.classes.requiredIfFilledField}`),o="next"===t?this.currentStep+1:"prev"!==t||this.currentStepOptional?"optional"===t?this.currentStep+"b":"number"==typeof t?t:this.currentStep:this.currentStep-1,n=this.form.querySelector('[data-step="'+o+'"]');if(this.currentStep!==o||this.currentStepOptional){this.stepChanging=!0;let l=!0;const u=[];if((r||a)&&("next"===t||"optional"===t)){if(r)for(const e of Array.from(r))if(!await this.validator.validateField({field:e})){l=!1;const t=e.querySelector(".form__control");u.push(t?.getAttribute("name"))}if(a)for(const e of Array.from(a)){const t=e.querySelector(".form__control");t?.value&&(await this.validator.validateField({field:e})||(l=!1,u.push(t?.getAttribute("name"))))}}if(this.setValidity(l),this.isValid){if(s&&n){const r=()=>{n.classList.add("--active"),this.stepChanging=!1,this.currentStepOptional="optional"===t,this.currentStep=parseInt(o),("next"===t||"optional"===t)&&this.currentStep>this.highestVisitedStep&&(this.highestVisitedStep=this.currentStep),"function"==typeof this.onStepChange&&this.onStepChange(i.toString(),o.toString())};this.disbleStepsTransition?(s.classList.remove("--active"),r()):e({element:s,enter:!1,time:200,displayType:"flex",callback:()=>{s.classList.remove("--active"),e({element:n,enter:!0,time:200,displayType:"flex",callback:r})}})}}else this.stepChanging=!1,"function"==typeof this.onValidationError&&this.onValidationError(u,this)}}}nextStep(){return this.changeStep({step:"next"})}optionalStep(){return this.changeStep({step:"optional"})}prevStep(){return 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().trim().split(i):[e.toString()],a=new RegExp(/\B(?=(\d{3})+(?!\d))/g),o=r[0].replace(a,t);let n="";return n=s&&s>0&&r[1]&&r[1].length?o+i+r[1].slice(0,s):o,n}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,"").trim()}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(e=!1){const i=this;if(!this.submitBtn)throw new Error(`There's no submit button in this form "${this.form.id}".`);e||this.submitBtn.addEventListener("click",(function(e){e.preventDefault(),i.submit()})),this.validator.realTimeValidations({form:this.form}),this.form.querySelectorAll(`.form__field.${this.classes.validateOnBlur}`).forEach((e=>{e.querySelector(".form__control")?.addEventListener("blur",(async()=>{await this.validator.validateField({field:e})||this.setValidity(!1)}))})),this.form.querySelectorAll(`.form__field.${this.classes.validateOnInput}`).forEach((e=>{const t=e.querySelector(".form__control");t?.addEventListener("input",(async()=>{await this.validator.validateField({field:e})&&this.validator.clearControlError({control:t})}))})),e||(this.form.querySelectorAll(".form__next-step").forEach((e=>{e.addEventListener("click",i.nextStep.bind(i))})),this.form.querySelectorAll(".form__optional-step").forEach((e=>{e.addEventListener("click",i.optionalStep.bind(i))})),this.form.querySelectorAll(".form__prev-step").forEach((e=>{e.addEventListener("click",i.prevStep.bind(i))}))),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((e=>{e.addEventListener("click",(()=>{const s=t({element:e,className:"form__field"});s&&i.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 n(){e.value=i.filterNumber({value:e.value,ignoreList:[a]})}e.addEventListener("focus",n),e.addEventListener("input",n),e.addEventListener("paste",n),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:"",n=s&&s.dataset.decimals?s.dataset.decimals:"";function l(){e.value=i.filterNumber({value:e.value,ignoreList:[o]})}e.addEventListener("focus",l),e.addEventListener("input",l),e.addEventListener("paste",l),e.addEventListener("blur",(()=>{e.value=this.filterMoneyAmount({num:e.value,currency:r,thousands:a,decimals:o,decimalSteps:parseInt(n)})}))})),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})))}))}refresh(){this.declareHandlers(!0)}}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}async validateField({field:e,silent:t=!1}){const s=e.dataset.type||"",r=["radio","checkbox"].includes(s),a=e.classList.contains(this.classes.requiredField),o=e.classList.contains(this.classes.requiredIfFilledField),n=e.querySelector(".form__error"),l=e.querySelector(".form__control"),u=r?e.querySelector(".form__control:checked"):null,d=l?l.getAttribute("name"):null,c=Object.keys(this.customValidations),h=e.dataset.minLength?Number(e.dataset.minLength):null,m=e.dataset.maxLength?Number(e.dataset.maxLength):null;if(l||this.throwError("control not found."),d||this.throwError("control name not found."),this.debug&&i(`validating field "${d}"`),o&&!a&&!l?.value)return!0;if(a&&!l?.value)return t||(n&&this.validationMessages&&d&&(n.textContent=this.validationMessages[d]?this.validationMessages[d].empty:this.validationMessages.default.empty),this.displayFieldError({control:l,field:e,errorElement:n})),!1;if(l?.value){if(h&&l.value.length<h)return t||(n&&this.validationMessages&&(n.textContent=this.validationMessages.default.minLength.replace("[[var]]",h.toString())),this.displayFieldError({control:l,field:e,errorElement:n})),!1;if(m&&l.value.length>m)return t||(n&&this.validationMessages&&(n.textContent=this.validationMessages.default.maxLength.replace("[[var]]",m.toString())),this.displayFieldError({control:l,field:e,errorElement:n})),!1}if(r&&!u){if(!o)return t||(this.displayFieldError({control:l,field:e,errorElement:n}),n&&this.validationMessages&&d&&(n.textContent=this.validationMessages[d]?this.validationMessages[d].empty:this.validationMessages.default.empty)),!1}else{switch(s){case"email":if(l&&l.value&&!this.isValidEmail({email:l.value}))return t||(n&&this.validationMessages&&(n.textContent=this.validationMessages.email.invalid),this.displayFieldError({control:l,field:e,errorElement:n})),!1;break;case"password_repeat":{const i=document.getElementById("password");if(i&&l?.value!=i?.value)return t||(n&&this.validationMessages&&(n.textContent=this.validationMessages.password_repeat.unequal),this.displayFieldError({control:l,field:e,errorElement:n})),!1;break}case"cuil":case"cuit":if(l&&l.value&&!this.isValidCuitCuil({num:l.value}))return t||(n&&this.validationMessages&&(n.textContent=this.validationMessages.cuil.invalid),this.displayFieldError({control:l,field:e,errorElement:n})),!1;break;case"url":if(l&&l.value&&!this.isValidUrl({urlString:l.value}))return t||(n&&this.validationMessages&&(n.textContent=this.validationMessages.url.invalid),this.displayFieldError({control:l,field:e,errorElement:n})),!1;break;case"money":if(l){const i=l.dataset.currency?l.dataset.currency:"$";if(""==l.value||l.value==i)return t||(n&&this.validationMessages&&(n.textContent=this.validationMessages.default.empty),this.displayFieldError({control:l,field:e,errorElement:n})),!1}break;case"single-checkbox":if(l&&l.hasOwnProperty("checked")){if(!l.checked)return t||(this.displayFieldError({control:l,field:e,errorElement:n}),n&&this.validationMessages&&d&&(n.textContent=this.validationMessages[d]?this.validationMessages[d].empty:this.validationMessages.default.empty)),!1}}for(const i of c)if(s===i)for(const i of this.customValidations[s])if(l){if(!await i.condition(l.value,d))return t||(n&&(n.textContent=i.message||""),this.displayFieldError({control:l,field:e,errorElement:n})),!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,n=i&&i.files?i.files[0].size:0,l=i&&i.files?parseFloat((n/1048576).toFixed(1)):0;l>r||!n?(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&&(n?l<s?t=this.validationMessages.file.min_size.replace("[[var]]",s+" MB"):l>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:n,fieldGroups:l,extraFields:u,serializerIgnoreList:d,customValidations:c,customValidationMessages:h,onStepChange:m,onValidationError:f,onSubmitStart:p,onSubmitEnd:g,onSuccess:F,onError:v,onBeforeValidation:S,onBeforeSubmit:y,onBeforeSubmission:b,onValidityChange:E,onCurrentStepValidityChange:_,resetOnSuccess:C,resetLoaderOnSuccess:L,scrollOnError:V,scrollOnErrorOffset:q,preventValidation:x,preventSubmit:A,preventSubmission:w,disbleStepsTransition:D,debug:M}){this.form=e,this.submitType=s||"fetch",this.submitDataFormat=a||"formData",this.requestHeaders=n||{},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.isCurrentStepValid=!0,this.validator=new r({customValidations:c||{},classes:this.classes,customValidationMessages:h||null,debug:M??!1}),this.onValidationError=f??null,this.onStepChange=m??null,this.onSubmitStart=p??null,this.onSubmitEnd=g??null,this.onSuccess=F??null,this.onError=v??null,this.onBeforeValidation=S??null,this.onBeforeSubmit=y??null,this.onBeforeSubmission=b??null,this.onValidityChange=E??null,this.onCurrentStepValidityChange=_??null,this.fieldGroups=l??null,this.extraFields=u??[],this.hasFile=!1,this.serializerIgnoreList=d||[],this.resetOnSuccess=C??!0,this.resetLoaderOnSuccess=L??!0,this.scrollOnError=V??!0,this.scrollOnErrorOffset=q||0;const z=this.form.querySelector(".form__step.--active");if(this.currentStep=z?Number(z.dataset.step):0,this.highestVisitedStep=this.currentStep,this.currentStepOptional=!1,this.disbleStepsTransition=D??!1,this.stepChanging=!1,this.preventValidation=x??!1,this.preventSubmit=A??!1,this.preventSubmission=w??!1,this.debug=M??!1,this.declareHandlers(),!this.actionUrl||""===this.actionUrl)throw new Error("The form doesn't have an action attribute or submitUrl wasn't provided.");this.checkForRequiredFields()&&(this.setValidity(!1),this.setCurrentStepValidity(!1)),this.debug&&i("initialized!")}checkForRequiredFields(){return this.form.querySelectorAll(`.${this.classes.requiredField}`).length>0}setValidity(e){this.isValid!==e&&(this.isValid=e,"function"==typeof this.onValidityChange&&this.onValidityChange(e,this))}setCurrentStepValidity(e){this.isCurrentStepValid!==e&&(this.isCurrentStepValid=e,"function"==typeof this.onCurrentStepValidityChange&&this.onCurrentStepValidityChange(e,this.currentStep,this))}async validateAllFields(){let e=!0;const t=Array.from(this.form.querySelectorAll(".form__field"));for(const i of t){await this.validator.validateField({field:i,silent:!0})||(e=!1)}return this.setValidity(e),e}submit(){"function"==typeof this.onBeforeValidation&&this.onBeforeValidation(this),this.onBeforeValidation||"function"!=typeof this.onBeforeSubmit||(this.debug&&i("WARNING: onBeforeSubmit will be deprecated in future versions, use onBeforeValidation or onBeforeSubmission instead."),this.onBeforeSubmit(this)),this.preventValidation||this.preventSubmit||(this.debug&&i("WARNING: preventSubmit will be deprecated in future versions, use preventValidation or preventSubmission instead."),this.resumeValidation())}resumeSubmit(){this.resumeValidation()}async resumeValidation(){this.debug&&i(`submitting using ${this.submitType}!`),this.submittingForm({submitting:!0});let e=!0;const t=[];let s;if(this.currentStep>0&&this.highestVisitedStep>0){s=[];for(let e=1;e<=this.highestVisitedStep;e++){const t=this.form.querySelector(`.form__step[data-step="${e}"]`);if(t){const e=Array.from(t.querySelectorAll(".form__field"));s.push(...e)}const i=this.form.querySelector(`.form__step[data-step="${e}b"]`);if(i&&i.classList.contains("--active")){const e=Array.from(i.querySelectorAll(".form__field"));s.push(...e)}}}else s=Array.from(this.form.querySelectorAll(".form__field"));for(const i of s){if(!await this.validator.validateField({field:i})){const s=i.querySelector(".form__control");s&&t.push(s.name),e=!1}}if(this.setCurrentStepValidity(e),await this.validateAllFields(),this.isCurrentStepValid)"function"==typeof this.onBeforeSubmission&&this.onBeforeSubmission(this),this.preventSubmission||this.resumeSubmission();else{if("function"==typeof this.onValidationError&&this.onValidationError(t,this),this.debug&&i(`this fields have failed validation: ${t.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})&&(r=e,a=this.scrollOnErrorOffset||0,r&&window.scrollTo({behavior:"smooth",top:r.getBoundingClientRect().top-document.body.getBoundingClientRect().top-a}))}this.submittingForm({submitting:!1,force:!0}),this.stepChanging=!1}var r,a}resumeSubmission(){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:"formData"===this.submitDataFormat});fetch(this.actionUrl,{method:this.submitMethod,headers:{...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()}}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.highestVisitedStep=1,this.changeStep({step:1}))}async 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=s?.querySelectorAll(`.${this.classes.requiredIfFilledField}`),o="next"===t?this.currentStep+1:"prev"!==t||this.currentStepOptional?"optional"===t?this.currentStep+"b":"number"==typeof t?t:this.currentStep:this.currentStep-1,n=this.form.querySelector('[data-step="'+o+'"]');if(this.currentStep!==o||this.currentStepOptional){this.stepChanging=!0;let l=!0;const u=[];if((r||a)&&("next"===t||"optional"===t)){if(r)for(const e of Array.from(r))if(!await this.validator.validateField({field:e})){l=!1;const t=e.querySelector(".form__control");u.push(t?.getAttribute("name"))}if(a)for(const e of Array.from(a)){const t=e.querySelector(".form__control");t?.value&&(await this.validator.validateField({field:e})||(l=!1,u.push(t?.getAttribute("name"))))}}if(this.setCurrentStepValidity(l),await this.validateAllFields(),l){if(s&&n){const r=()=>{n.classList.add("--active"),this.stepChanging=!1,this.currentStepOptional="optional"===t,this.currentStep=parseInt(o),("next"===t||"optional"===t)&&this.currentStep>this.highestVisitedStep&&(this.highestVisitedStep=this.currentStep),"function"==typeof this.onStepChange&&this.onStepChange(i.toString(),o.toString())};this.disbleStepsTransition?(s.classList.remove("--active"),r()):e({element:s,enter:!1,time:200,displayType:"flex",callback:()=>{s.classList.remove("--active"),e({element:n,enter:!0,time:200,displayType:"flex",callback:r})}})}}else this.stepChanging=!1,"function"==typeof this.onValidationError&&this.onValidationError(u,this)}}}nextStep(){return this.changeStep({step:"next"})}optionalStep(){return this.changeStep({step:"optional"})}prevStep(){return 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().trim().split(i):[e.toString()],a=new RegExp(/\B(?=(\d{3})+(?!\d))/g),o=r[0].replace(a,t);let n="";return n=s&&s>0&&r[1]&&r[1].length?o+i+r[1].slice(0,s):o,n}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,"").trim()}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(e=!1){const i=this;if(!this.submitBtn)throw new Error(`There's no submit button in this form "${this.form.id}".`);e||this.submitBtn.addEventListener("click",(function(e){e.preventDefault(),i.submit()})),this.validator.realTimeValidations({form:this.form}),this.form.querySelectorAll(`.form__field.${this.classes.validateOnBlur}`).forEach((e=>{e.querySelector(".form__control")?.addEventListener("blur",(async()=>{await this.validator.validateField({field:e}),await this.validateAllFields()}))})),this.form.querySelectorAll(`.form__field.${this.classes.validateOnInput}`).forEach((e=>{const t=e.querySelector(".form__control");t?.addEventListener("input",(async()=>{await this.validator.validateField({field:e})&&this.validator.clearControlError({control:t})}))})),e||(this.form.querySelectorAll(".form__next-step").forEach((e=>{e.addEventListener("click",i.nextStep.bind(i))})),this.form.querySelectorAll(".form__optional-step").forEach((e=>{e.addEventListener("click",i.optionalStep.bind(i))})),this.form.querySelectorAll(".form__prev-step").forEach((e=>{e.addEventListener("click",i.prevStep.bind(i))}))),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((e=>{e.addEventListener("click",(()=>{const s=t({element:e,className:"form__field"});s&&i.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 n(){e.value=i.filterNumber({value:e.value,ignoreList:[a]})}e.addEventListener("focus",n),e.addEventListener("input",n),e.addEventListener("paste",n),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:"",n=s&&s.dataset.decimals?s.dataset.decimals:"";function l(){e.value=i.filterNumber({value:e.value,ignoreList:[o]})}e.addEventListener("focus",l),e.addEventListener("input",l),e.addEventListener("paste",l),e.addEventListener("blur",(()=>{e.value=this.filterMoneyAmount({num:e.value,currency:r,thousands:a,decimals:o,decimalSteps:parseInt(n)})}))})),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})))}))}refresh(){this.declareHandlers(!0)}}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);let o=(new Date).getTime();!function s(){e.style.opacity=t?(+e.style.opacity+((new Date).getTime()-o)/i).toString():(+e.style.opacity-((new Date).getTime()-o)/i).toString(),o=(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}async validateField({field:e}){const t=e.dataset.type||"",s=["radio","checkbox"].includes(t),r=e.classList.contains(this.classes.requiredField),o=e.classList.contains(this.classes.requiredIfFilledField),a=e.querySelector(".form__error"),n=e.querySelector(".form__control"),l=s?e.querySelector(".form__control:checked"):null,u=n?n.getAttribute("name"):null,d=Object.keys(this.customValidations),c=e.dataset.minLength?Number(e.dataset.minLength):null,h=e.dataset.maxLength?Number(e.dataset.maxLength):null;if(n||this.throwError("control not found."),u||this.throwError("control name not found."),this.debug&&i(`validating field "${u}"`),o&&!r&&!n?.value)return!0;if(r&&!n?.value)return a&&this.validationMessages&&u&&(a.textContent=this.validationMessages[u]?this.validationMessages[u].empty:this.validationMessages.default.empty),this.displayFieldError({control:n,field:e,errorElement:a}),!1;if(n?.value){if(c&&n.value.length<c)return a&&this.validationMessages&&(a.textContent=this.validationMessages.default.minLength.replace("[[var]]",c.toString())),this.displayFieldError({control:n,field:e,errorElement:a}),!1;if(h&&n.value.length>h)return a&&this.validationMessages&&(a.textContent=this.validationMessages.default.maxLength.replace("[[var]]",h.toString())),this.displayFieldError({control:n,field:e,errorElement:a}),!1}if(s&&!l){if(!o)return this.displayFieldError({control:n,field:e,errorElement:a}),a&&this.validationMessages&&u&&(a.textContent=this.validationMessages[u]?this.validationMessages[u].empty:this.validationMessages.default.empty),!1}else{switch(t){case"email":if(n&&n.value&&!this.isValidEmail({email:n.value}))return a&&this.validationMessages&&(a.textContent=this.validationMessages.email.invalid),this.displayFieldError({control:n,field:e,errorElement:a}),!1;break;case"password_repeat":{const t=document.getElementById("password");if(t&&n?.value!=t?.value)return a&&this.validationMessages&&(a.textContent=this.validationMessages.password_repeat.unequal),this.displayFieldError({control:n,field:e,errorElement:a}),!1;break}case"cuil":case"cuit":if(n&&n.value&&!this.isValidCuitCuil({num:n.value}))return a&&this.validationMessages&&(a.textContent=this.validationMessages.cuil.invalid),this.displayFieldError({control:n,field:e,errorElement:a}),!1;break;case"url":if(n&&n.value&&!this.isValidUrl({urlString:n.value}))return a&&this.validationMessages&&(a.textContent=this.validationMessages.url.invalid),this.displayFieldError({control:n,field:e,errorElement:a}),!1;break;case"money":if(n){const t=n.dataset.currency?n.dataset.currency:"$";if(""==n.value||n.value==t)return a&&this.validationMessages&&(a.textContent=this.validationMessages.default.empty),this.displayFieldError({control:n,field:e,errorElement:a}),!1}break;case"single-checkbox":if(n&&n.hasOwnProperty("checked")){if(!n.checked)return this.displayFieldError({control:n,field:e,errorElement:a}),a&&this.validationMessages&&u&&(a.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(n){if(!await i.condition(n.value,u))return a&&(a.textContent=i.message||""),this.displayFieldError({control:n,field:e,errorElement:a}),!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),o=e.querySelector(".form__error"),a=i&&i.files?i.files[0].name:null,n=i&&i.files?i.files[0].size:0,l=i&&i.files?parseFloat((n/1048576).toFixed(1)):0;l>r||!n?(i&&(i.value="",i.setAttribute("aria-invalid","true")),e.classList.remove("--has-file"),e.classList.add(this.classes.fieldHasError)):a?e.classList.add("--has-file"):e.classList.remove("--has-file"),this.validationMessages&&(n?l<s?t=this.validationMessages.file.min_size.replace("[[var]]",s+" MB"):l>r&&(t=this.validationMessages.file.max_size.replace("[[var]]",r+" MB")):t=this.validationMessages.file.empty),t&&o&&(o.textContent=t,this.displayFieldError({control:i,field:e,errorElement:o}))}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:o,submitUrl:a,requestHeaders:n,fieldGroups:l,extraFields:u,serializerIgnoreList:d,customValidations:c,customValidationMessages:h,onStepChange:m,onValidationError:f,onSubmitStart:p,onSubmitEnd:g,onSuccess:F,onError:v,onBeforeValidation:b,onBeforeSubmit:S,onBeforeSubmission:y,onValidityChange:E,resetOnSuccess:_,resetLoaderOnSuccess:L,scrollOnError:q,scrollOnErrorOffset:x,preventValidation:C,preventSubmit:V,preventSubmission:D,disbleStepsTransition:A,debug:w}){this.form=e,this.submitType=s||"fetch",this.submitDataFormat=o||"formData",this.requestHeaders=n||{},this.actionUrl=this.form.getAttribute("action")||a||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:h||null,debug:w??!1}),this.onValidationError=f??null,this.onStepChange=m??null,this.onSubmitStart=p??null,this.onSubmitEnd=g??null,this.onSuccess=F??null,this.onError=v??null,this.onBeforeValidation=b??null,this.onBeforeSubmit=S??null,this.onBeforeSubmission=y??null,this.onValidityChange=E??null,this.fieldGroups=l??null,this.extraFields=u??[],this.hasFile=!1,this.serializerIgnoreList=d||[],this.resetOnSuccess=_??!0,this.resetLoaderOnSuccess=L??!0,this.scrollOnError=q??!0,this.scrollOnErrorOffset=x||0;const M=this.form.querySelector(".form__step.--active");if(this.currentStep=M?Number(M.dataset.step):0,this.highestVisitedStep=this.currentStep,this.currentStepOptional=!1,this.disbleStepsTransition=A??!1,this.stepChanging=!1,this.preventValidation=C??!1,this.preventSubmit=V??!1,this.preventSubmission=D??!1,this.debug=w??!1,this.declareHandlers(),!this.actionUrl||""===this.actionUrl)throw new Error("The form doesn't have an action attribute or submitUrl wasn't provided.");this.checkForRequiredFields()&&(this.isValid=!1),this.debug&&i("initialized!")}checkForRequiredFields(){return this.form.querySelectorAll(`.${this.classes.requiredField}`).length>0}setValidity(e){this.isValid!==e&&(this.isValid=e,"function"==typeof this.onValidityChange&&this.onValidityChange(e,this))}submit(){"function"==typeof this.onBeforeValidation&&this.onBeforeValidation(this),this.onBeforeValidation||"function"!=typeof this.onBeforeSubmit||(this.debug&&i("WARNING: onBeforeSubmit will be deprecated in future versions, use onBeforeValidation or onBeforeSubmission instead."),this.onBeforeSubmit(this)),this.preventValidation||this.preventSubmit||(this.debug&&i("WARNING: preventSubmit will be deprecated in future versions, use preventValidation or preventSubmission instead."),this.resumeValidation())}resumeSubmit(){this.resumeValidation()}async resumeValidation(){this.debug&&i(`submitting using ${this.submitType}!`),this.submittingForm({submitting:!0});let e=!0;const t=[];let s;if(this.currentStep>0&&this.highestVisitedStep>0){s=[];for(let e=1;e<=this.highestVisitedStep;e++){const t=this.form.querySelector(`.form__step[data-step="${e}"]`);if(t){const e=Array.from(t.querySelectorAll(".form__field"));s.push(...e)}const i=this.form.querySelector(`.form__step[data-step="${e}b"]`);if(i&&i.classList.contains("--active")){const e=Array.from(i.querySelectorAll(".form__field"));s.push(...e)}}}else s=Array.from(this.form.querySelectorAll(".form__field"));for(const i of s){if(!await this.validator.validateField({field:i})){const s=i.querySelector(".form__control");s&&t.push(s.name),e=!1}}if(this.setValidity(e),this.isValid)"function"==typeof this.onBeforeSubmission&&this.onBeforeSubmission(this),this.preventSubmission||this.resumeSubmission();else{if("function"==typeof this.onValidationError&&this.onValidationError(t,this),this.debug&&i(`this fields have failed validation: ${t.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})&&(r=e,o=this.scrollOnErrorOffset||0,r&&window.scrollTo({behavior:"smooth",top:r.getBoundingClientRect().top-document.body.getBoundingClientRect().top-o}))}this.submittingForm({submitting:!1,force:!0}),this.stepChanging=!1}var r,o}resumeSubmission(){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:"formData"===this.submitDataFormat});fetch(this.actionUrl,{method:this.submitMethod,headers:{...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()}}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.highestVisitedStep=1,this.changeStep({step:1}))}async 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}`),o=s?.querySelectorAll(`.${this.classes.requiredIfFilledField}`),a="next"===t?this.currentStep+1:"prev"!==t||this.currentStepOptional?"optional"===t?this.currentStep+"b":"number"==typeof t?t:this.currentStep:this.currentStep-1,n=this.form.querySelector('[data-step="'+a+'"]');if(this.currentStep!==a||this.currentStepOptional){this.stepChanging=!0;let l=!0;const u=[];if((r||o)&&("next"===t||"optional"===t)){if(r)for(const e of Array.from(r))if(!await this.validator.validateField({field:e})){l=!1;const t=e.querySelector(".form__control");u.push(t?.getAttribute("name"))}if(o)for(const e of Array.from(o)){const t=e.querySelector(".form__control");t?.value&&(await this.validator.validateField({field:e})||(l=!1,u.push(t?.getAttribute("name"))))}}if(this.setValidity(l),this.isValid){if(s&&n){const r=()=>{n.classList.add("--active"),this.stepChanging=!1,this.currentStepOptional="optional"===t,this.currentStep=parseInt(a),("next"===t||"optional"===t)&&this.currentStep>this.highestVisitedStep&&(this.highestVisitedStep=this.currentStep),"function"==typeof this.onStepChange&&this.onStepChange(i.toString(),a.toString())};this.disbleStepsTransition?(s.classList.remove("--active"),r()):e({element:s,enter:!1,time:200,displayType:"flex",callback:()=>{s.classList.remove("--active"),e({element:n,enter:!0,time:200,displayType:"flex",callback:r})}})}}else this.stepChanging=!1,"function"==typeof this.onValidationError&&this.onValidationError(u,this)}}}nextStep(){return this.changeStep({step:"next"})}optionalStep(){return this.changeStep({step:"optional"})}prevStep(){return 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().trim().split(i):[e.toString()],o=new RegExp(/\B(?=(\d{3})+(?!\d))/g),a=r[0].replace(o,t);let n="";return n=s&&s>0&&r[1]&&r[1].length?a+i+r[1].slice(0,s):a,n}filterMoneyAmount({num:e,currency:t="$",thousands:i=".",decimals:s=",",decimalSteps:r}){if(!e||e==t)return"";let o=this.filterNumber({value:e,ignoreList:[s||""]});return o=this.filterFormattedQuantity({num:o,thousands:i,decimals:s,decimalSteps:r}),`${t} ${o}`}filterPhoneNumber({number:e}){let t=new RegExp(/[^\d+\-() ]*/g);return e.replace(t,"").trim()}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(e=!1){const i=this;if(!this.submitBtn)throw new Error(`There's no submit button in this form "${this.form.id}".`);e||this.submitBtn.addEventListener("click",(function(e){e.preventDefault(),i.submit()})),this.validator.realTimeValidations({form:this.form}),this.form.querySelectorAll(`.form__field.${this.classes.validateOnBlur}`).forEach((e=>{e.querySelector(".form__control")?.addEventListener("blur",(async()=>{await this.validator.validateField({field:e})||this.setValidity(!1)}))})),this.form.querySelectorAll(`.form__field.${this.classes.validateOnInput}`).forEach((e=>{const t=e.querySelector(".form__control");t?.addEventListener("input",(async()=>{await this.validator.validateField({field:e})&&this.validator.clearControlError({control:t})}))})),e||(this.form.querySelectorAll(".form__next-step").forEach((e=>{e.addEventListener("click",i.nextStep.bind(i))})),this.form.querySelectorAll(".form__optional-step").forEach((e=>{e.addEventListener("click",i.optionalStep.bind(i))})),this.form.querySelectorAll(".form__prev-step").forEach((e=>{e.addEventListener("click",i.prevStep.bind(i))}))),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((e=>{e.addEventListener("click",(()=>{const s=t({element:e,className:"form__field"});s&&i.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:"",o=s&&s.dataset.decimalSeparator?s.dataset.decimalSeparator:"",a=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=r?i.filterFormattedQuantity({num:e.value,thousands:r,decimals:o,decimalSteps:parseInt(a)}):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:"$",o=s&&s.dataset.thousandsSeparator?s.dataset.thousandsSeparator:".",a=s&&s.dataset.decimalSeparator?s.dataset.decimalSeparator:"",n=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=this.filterMoneyAmount({num:e.value,currency:r,thousands:o,decimals:a,decimalSteps:parseInt(n)})}))})),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})))}))}refresh(){this.declareHandlers(!0)}}}));
|
|
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}async validateField({field:e,silent:t=!1}){const s=e.dataset.type||"",r=["radio","checkbox"].includes(s),a=e.classList.contains(this.classes.requiredField),o=e.classList.contains(this.classes.requiredIfFilledField),n=e.querySelector(".form__error"),l=e.querySelector(".form__control"),u=r?e.querySelector(".form__control:checked"):null,d=l?l.getAttribute("name"):null,c=Object.keys(this.customValidations),h=e.dataset.minLength?Number(e.dataset.minLength):null,m=e.dataset.maxLength?Number(e.dataset.maxLength):null;if(l||this.throwError("control not found."),d||this.throwError("control name not found."),this.debug&&i(`validating field "${d}"`),o&&!a&&!l?.value)return!0;if(a&&!l?.value)return t||(n&&this.validationMessages&&d&&(n.textContent=this.validationMessages[d]?this.validationMessages[d].empty:this.validationMessages.default.empty),this.displayFieldError({control:l,field:e,errorElement:n})),!1;if(l?.value){if(h&&l.value.length<h)return t||(n&&this.validationMessages&&(n.textContent=this.validationMessages.default.minLength.replace("[[var]]",h.toString())),this.displayFieldError({control:l,field:e,errorElement:n})),!1;if(m&&l.value.length>m)return t||(n&&this.validationMessages&&(n.textContent=this.validationMessages.default.maxLength.replace("[[var]]",m.toString())),this.displayFieldError({control:l,field:e,errorElement:n})),!1}if(r&&!u){if(!o)return t||(this.displayFieldError({control:l,field:e,errorElement:n}),n&&this.validationMessages&&d&&(n.textContent=this.validationMessages[d]?this.validationMessages[d].empty:this.validationMessages.default.empty)),!1}else{switch(s){case"email":if(l&&l.value&&!this.isValidEmail({email:l.value}))return t||(n&&this.validationMessages&&(n.textContent=this.validationMessages.email.invalid),this.displayFieldError({control:l,field:e,errorElement:n})),!1;break;case"password_repeat":{const i=document.getElementById("password");if(i&&l?.value!=i?.value)return t||(n&&this.validationMessages&&(n.textContent=this.validationMessages.password_repeat.unequal),this.displayFieldError({control:l,field:e,errorElement:n})),!1;break}case"cuil":case"cuit":if(l&&l.value&&!this.isValidCuitCuil({num:l.value}))return t||(n&&this.validationMessages&&(n.textContent=this.validationMessages.cuil.invalid),this.displayFieldError({control:l,field:e,errorElement:n})),!1;break;case"url":if(l&&l.value&&!this.isValidUrl({urlString:l.value}))return t||(n&&this.validationMessages&&(n.textContent=this.validationMessages.url.invalid),this.displayFieldError({control:l,field:e,errorElement:n})),!1;break;case"money":if(l){const i=l.dataset.currency?l.dataset.currency:"$";if(""==l.value||l.value==i)return t||(n&&this.validationMessages&&(n.textContent=this.validationMessages.default.empty),this.displayFieldError({control:l,field:e,errorElement:n})),!1}break;case"single-checkbox":if(l&&l.hasOwnProperty("checked")){if(!l.checked)return t||(this.displayFieldError({control:l,field:e,errorElement:n}),n&&this.validationMessages&&d&&(n.textContent=this.validationMessages[d]?this.validationMessages[d].empty:this.validationMessages.default.empty)),!1}}for(const i of c)if(s===i)for(const i of this.customValidations[s])if(l){if(!await i.condition(l.value,d))return t||(n&&(n.textContent=i.message||""),this.displayFieldError({control:l,field:e,errorElement:n})),!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,n=i&&i.files?i.files[0].size:0,l=i&&i.files?parseFloat((n/1048576).toFixed(1)):0;l>r||!n?(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&&(n?l<s?t=this.validationMessages.file.min_size.replace("[[var]]",s+" MB"):l>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:n,fieldGroups:l,extraFields:u,serializerIgnoreList:d,customValidations:c,customValidationMessages:h,onStepChange:m,onValidationError:f,onSubmitStart:p,onSubmitEnd:g,onSuccess:F,onError:v,onBeforeValidation:S,onBeforeSubmit:y,onBeforeSubmission:b,onValidityChange:E,onCurrentStepValidityChange:_,resetOnSuccess:C,resetLoaderOnSuccess:L,scrollOnError:V,scrollOnErrorOffset:q,preventValidation:x,preventSubmit:A,preventSubmission:w,disbleStepsTransition:D,debug:M}){this.form=e,this.submitType=s||"fetch",this.submitDataFormat=a||"formData",this.requestHeaders=n||{},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.isCurrentStepValid=!0,this.validator=new r({customValidations:c||{},classes:this.classes,customValidationMessages:h||null,debug:M??!1}),this.onValidationError=f??null,this.onStepChange=m??null,this.onSubmitStart=p??null,this.onSubmitEnd=g??null,this.onSuccess=F??null,this.onError=v??null,this.onBeforeValidation=S??null,this.onBeforeSubmit=y??null,this.onBeforeSubmission=b??null,this.onValidityChange=E??null,this.onCurrentStepValidityChange=_??null,this.fieldGroups=l??null,this.extraFields=u??[],this.hasFile=!1,this.serializerIgnoreList=d||[],this.resetOnSuccess=C??!0,this.resetLoaderOnSuccess=L??!0,this.scrollOnError=V??!0,this.scrollOnErrorOffset=q||0;const z=this.form.querySelector(".form__step.--active");if(this.currentStep=z?Number(z.dataset.step):0,this.highestVisitedStep=this.currentStep,this.currentStepOptional=!1,this.disbleStepsTransition=D??!1,this.stepChanging=!1,this.preventValidation=x??!1,this.preventSubmit=A??!1,this.preventSubmission=w??!1,this.debug=M??!1,this.declareHandlers(),!this.actionUrl||""===this.actionUrl)throw new Error("The form doesn't have an action attribute or submitUrl wasn't provided.");this.checkForRequiredFields()&&(this.setValidity(!1),this.setCurrentStepValidity(!1)),this.debug&&i("initialized!")}checkForRequiredFields(){return this.form.querySelectorAll(`.${this.classes.requiredField}`).length>0}setValidity(e){this.isValid!==e&&(this.isValid=e,"function"==typeof this.onValidityChange&&this.onValidityChange(e,this))}setCurrentStepValidity(e){this.isCurrentStepValid!==e&&(this.isCurrentStepValid=e,"function"==typeof this.onCurrentStepValidityChange&&this.onCurrentStepValidityChange(e,this.currentStep,this))}async validateAllFields(){let e=!0;const t=Array.from(this.form.querySelectorAll(".form__field"));for(const i of t){await this.validator.validateField({field:i,silent:!0})||(e=!1)}return this.setValidity(e),e}submit(){"function"==typeof this.onBeforeValidation&&this.onBeforeValidation(this),this.onBeforeValidation||"function"!=typeof this.onBeforeSubmit||(this.debug&&i("WARNING: onBeforeSubmit will be deprecated in future versions, use onBeforeValidation or onBeforeSubmission instead."),this.onBeforeSubmit(this)),this.preventValidation||this.preventSubmit||(this.debug&&i("WARNING: preventSubmit will be deprecated in future versions, use preventValidation or preventSubmission instead."),this.resumeValidation())}resumeSubmit(){this.resumeValidation()}async resumeValidation(){this.debug&&i(`submitting using ${this.submitType}!`),this.submittingForm({submitting:!0});let e=!0;const t=[];let s;if(this.currentStep>0&&this.highestVisitedStep>0){s=[];for(let e=1;e<=this.highestVisitedStep;e++){const t=this.form.querySelector(`.form__step[data-step="${e}"]`);if(t){const e=Array.from(t.querySelectorAll(".form__field"));s.push(...e)}const i=this.form.querySelector(`.form__step[data-step="${e}b"]`);if(i&&i.classList.contains("--active")){const e=Array.from(i.querySelectorAll(".form__field"));s.push(...e)}}}else s=Array.from(this.form.querySelectorAll(".form__field"));for(const i of s){if(!await this.validator.validateField({field:i})){const s=i.querySelector(".form__control");s&&t.push(s.name),e=!1}}if(this.setCurrentStepValidity(e),await this.validateAllFields(),this.isCurrentStepValid)"function"==typeof this.onBeforeSubmission&&this.onBeforeSubmission(this),this.preventSubmission||this.resumeSubmission();else{if("function"==typeof this.onValidationError&&this.onValidationError(t,this),this.debug&&i(`this fields have failed validation: ${t.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})&&(r=e,a=this.scrollOnErrorOffset||0,r&&window.scrollTo({behavior:"smooth",top:r.getBoundingClientRect().top-document.body.getBoundingClientRect().top-a}))}this.submittingForm({submitting:!1,force:!0}),this.stepChanging=!1}var r,a}resumeSubmission(){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:"formData"===this.submitDataFormat});fetch(this.actionUrl,{method:this.submitMethod,headers:{...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()}}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.highestVisitedStep=1,this.changeStep({step:1}))}async 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=s?.querySelectorAll(`.${this.classes.requiredIfFilledField}`),o="next"===t?this.currentStep+1:"prev"!==t||this.currentStepOptional?"optional"===t?this.currentStep+"b":"number"==typeof t?t:this.currentStep:this.currentStep-1,n=this.form.querySelector('[data-step="'+o+'"]');if(this.currentStep!==o||this.currentStepOptional){this.stepChanging=!0;let l=!0;const u=[];if((r||a)&&("next"===t||"optional"===t)){if(r)for(const e of Array.from(r))if(!await this.validator.validateField({field:e})){l=!1;const t=e.querySelector(".form__control");u.push(t?.getAttribute("name"))}if(a)for(const e of Array.from(a)){const t=e.querySelector(".form__control");t?.value&&(await this.validator.validateField({field:e})||(l=!1,u.push(t?.getAttribute("name"))))}}if(this.setCurrentStepValidity(l),await this.validateAllFields(),l){if(s&&n){const r=()=>{n.classList.add("--active"),this.stepChanging=!1,this.currentStepOptional="optional"===t,this.currentStep=parseInt(o),("next"===t||"optional"===t)&&this.currentStep>this.highestVisitedStep&&(this.highestVisitedStep=this.currentStep),"function"==typeof this.onStepChange&&this.onStepChange(i.toString(),o.toString())};this.disbleStepsTransition?(s.classList.remove("--active"),r()):e({element:s,enter:!1,time:200,displayType:"flex",callback:()=>{s.classList.remove("--active"),e({element:n,enter:!0,time:200,displayType:"flex",callback:r})}})}}else this.stepChanging=!1,"function"==typeof this.onValidationError&&this.onValidationError(u,this)}}}nextStep(){return this.changeStep({step:"next"})}optionalStep(){return this.changeStep({step:"optional"})}prevStep(){return 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().trim().split(i):[e.toString()],a=new RegExp(/\B(?=(\d{3})+(?!\d))/g),o=r[0].replace(a,t);let n="";return n=s&&s>0&&r[1]&&r[1].length?o+i+r[1].slice(0,s):o,n}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,"").trim()}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(e=!1){const i=this;if(!this.submitBtn)throw new Error(`There's no submit button in this form "${this.form.id}".`);e||this.submitBtn.addEventListener("click",(function(e){e.preventDefault(),i.submit()})),this.validator.realTimeValidations({form:this.form}),this.form.querySelectorAll(`.form__field.${this.classes.validateOnBlur}`).forEach((e=>{e.querySelector(".form__control")?.addEventListener("blur",(async()=>{await this.validator.validateField({field:e}),await this.validateAllFields()}))})),this.form.querySelectorAll(`.form__field.${this.classes.validateOnInput}`).forEach((e=>{const t=e.querySelector(".form__control");t?.addEventListener("input",(async()=>{await this.validator.validateField({field:e})&&this.validator.clearControlError({control:t})}))})),e||(this.form.querySelectorAll(".form__next-step").forEach((e=>{e.addEventListener("click",i.nextStep.bind(i))})),this.form.querySelectorAll(".form__optional-step").forEach((e=>{e.addEventListener("click",i.optionalStep.bind(i))})),this.form.querySelectorAll(".form__prev-step").forEach((e=>{e.addEventListener("click",i.prevStep.bind(i))}))),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((e=>{e.addEventListener("click",(()=>{const s=t({element:e,className:"form__field"});s&&i.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 n(){e.value=i.filterNumber({value:e.value,ignoreList:[a]})}e.addEventListener("focus",n),e.addEventListener("input",n),e.addEventListener("paste",n),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:"",n=s&&s.dataset.decimals?s.dataset.decimals:"";function l(){e.value=i.filterNumber({value:e.value,ignoreList:[o]})}e.addEventListener("focus",l),e.addEventListener("input",l),e.addEventListener("paste",l),e.addEventListener("blur",(()=>{e.value=this.filterMoneyAmount({num:e.value,currency:r,thousands:a,decimals:o,decimalSteps:parseInt(n)})}))})),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})))}))}refresh(){this.declareHandlers(!0)}}}));
|
|
@@ -4,8 +4,9 @@ export default class EgoFormValidator implements EgoFormValidatorInterface {
|
|
|
4
4
|
validationMessages: EgoFormValidationMessages | null;
|
|
5
5
|
debug: boolean;
|
|
6
6
|
constructor({ customValidations, classes, customValidationMessages, debug }: EgoFormValidatorOptions);
|
|
7
|
-
validateField({ field }: {
|
|
7
|
+
validateField({ field, silent }: {
|
|
8
8
|
field: EgoFormControl;
|
|
9
|
+
silent?: boolean;
|
|
9
10
|
}): Promise<boolean>;
|
|
10
11
|
isValidEmail({ email }: {
|
|
11
12
|
email: string;
|