form-father 0.2.6 → 0.2.8
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/FormFather.min.js +1 -1
- package/index.d.ts +5 -1
- package/index.js +182 -146
- package/index.js.map +1 -1
- package/package.json +1 -1
- package/types/index.d.ts +5 -1
package/FormFather.min.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
var FormFather=function(t){"use strict";function e(t,e,o,r){return new(o||(o=Promise))((function(i,n){function s(t){try{l(r.next(t))}catch(t){n(t)}}function a(t){try{l(r.throw(t))}catch(t){n(t)}}function l(t){var e;t.done?i(t.value):(e=t.value,e instanceof o?e:new o((function(t){t(e)}))).then(s,a)}l((r=r.apply(t,e||[])).next())}))}function o(t){const e="FORM"===t.tagName?Array.from(t.elements):Array.from(t.querySelectorAll("input, select, textarea, button")),o=new FormData;return e.forEach((t=>{if(!(t instanceof HTMLInputElement||t instanceof HTMLSelectElement||t instanceof HTMLTextAreaElement||t instanceof HTMLButtonElement))return;if(!t.name||t.hasAttribute("data-no-serialize"))return;const{name:e,type:r}=t;if("file"===r)if("true"===t.getAttribute("multiple")){const r=t.files;if(r)for(let t=0;t<r.length;t++)o.append(`${e}[${t}]`,r[t])}else{const r=t.files;r&&r[0]&&o.append(e,r[0])}else{let i;if("select-one"===r||"select-multiple"===r){const n=t;if("select-multiple"===r){Array.from(n.selectedOptions).map((t=>t.value)).forEach(((t,r)=>{o.append(`${e}[${r}]`,t)}))}else i=n.value,i&&o.append(e,i)}else if("tel"===r)i=t.value.split(/[\s()-]/).join("").replace(/^8/,"+7");else if("checkbox"===r||"radio"===r){if(!t.checked)return;i=t.value}else if("date"===r){const e=t.getAttribute("data-date-format");i=e?function(t,e){const o=new Date(t),r=t=>t.toString().padStart(2,"0"),i=r(o.getDate()),n=r(o.getMonth()+1),s=o.getFullYear().toString();return e.replace("dd",i).replace("mm",n).replace("yyyy",s)}(t.value,e):t.value}else i=t.value;i&&"select-one"!==r&&"select-multiple"!==r&&o.append(e,i)}})),o}var r;"function"==typeof SuppressedError&&SuppressedError;const i=Symbol.for("FormFather.validators"),n=null!==(r=globalThis[i])&&void 0!==r?r:globalThis[i]=new Map;function s(t,e,o,{override:r=!1}={}){!n.has(t)||r?n.set(t,{fn:e,defaultMessage:o}):console.warn(`[FormFather] Validator "${t}" already exists; pass { override: true } to replace`)}const a=t=>n.get(t);var l;s("required",((t,e,o)=>{if(e&&e.type){if("checkbox"===e.type)return e.checked;if("radio"===e.type){const t=o.querySelectorAll(`input[type="radio"][name="${e.name}"]`);return Array.from(t).some((t=>t.checked))}}return t.trim().length>0}),"Пустое значение"),s("email",(t=>/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(t)),"Неверный формат"),s("tel",(t=>/^\+7\d{10}$/.test(t)),"Неверный формат"),s("url",(t=>/^(https?:\/\/).+\..+/.test(t)),"Неверный формат"),s("not-numbers",(t=>!/[0-9]/.test(t)),"Неверный формат");const c=Symbol.for("formfather.shared"),d=null!==(l=globalThis[c])&&void 0!==l?l:globalThis[c]={defaultParams:{},defaultValidationSchema:void 0},u={tel:{selector:'input[type="tel"]',rules:["tel"]},email:{selector:'input[type="email"]',rules:["email"]},required:{selector:"[required]",rules:["required"]},url:{selector:'input[type="url"]',rules:["url"]},"not-numbers":{selector:'input[data-validate="not-numbers"]',rules:["not-numbers"]}};class p{static get defaultParams(){return d.defaultParams}static set defaultParams(t){d.defaultParams=t}static get defaultValidationSchema(){var t;return null!==(t=d.defaultValidationSchema)&&void 0!==t?t:d.defaultValidationSchema=u}static set defaultValidationSchema(t){d.defaultValidationSchema=t}constructor(t,e){this.$submit=null,this.waitResponse=!1,this.inputs=null,this._anim={duration:220,easing:"ease",paddingTopOpened:16,iconDuration:700},this.$el=t,this.options=e;this.config=Object.assign({},{onSubmit:()=>{},onResponse:()=>{},onResponseSuccess:()=>{},onResponseUnsuccess:()=>{},customTypeError:null,loaderColor:"#fff",showLoaderButton:!0,scrollToFirstErroredInput:!0,logging:!1,inputSelector:".input",inputWrapperSelector:".input-primary"},p.defaultParams,e),this.$el?(this.$submit=this.$el.querySelector('input[type="submit"], button[type="submit"]'),this.isCorrectArguments()&&this.initialization()):console.warn("Empty $el")}getOptions(){return this.options}static setDefaultParams(t){this.defaultParams=Object.assign(Object.assign({},this.defaultParams),t)}initialization(){this.inputs=this.$el.querySelectorAll(this.config.inputSelector);const t=()=>e(this,void 0,void 0,(function*(){var t;const e=this.$el.getAttribute("action")||"",r=(null===(t=this.$el.getAttribute("method"))||void 0===t?void 0:t.toUpperCase())||"POST",i=this.$el.getAttribute("enctype")||"application/x-www-form-urlencoded";let n=null,s={};const a=o(this.$el);let l={};if(a.forEach(((t,e)=>{l[e]=t})),this.config.logging)for(const t of a.keys())console.log(a.get(t));switch("function"==typeof this.config.wrapData&&(l=this.config.wrapData(l)),i){case"application/x-www-form-urlencoded":n=new URLSearchParams(o(this.$el)).toString(),s["Content-Type"]="application/x-www-form-urlencoded";break;case"multipart/form-data":n=o(this.$el);break;case"text/plain":n=Array.from(o(this.$el)).map((([t,e])=>`${encodeURIComponent(t)}=${encodeURIComponent(e)}`)).join("\n"),s["Content-Type"]="text/plain";break;case"application/json":n=JSON.stringify(l),s["Content-Type"]="application/json";break;default:console.warn(`Неизвестный enctype: ${i}. Используется application/x-www-form-urlencoded.`),n=new URLSearchParams(o(this.$el)).toString(),s["Content-Type"]="application/x-www-form-urlencoded"}return fetch(e,{method:r,body:n,headers:s})})),r=()=>e(this,void 0,void 0,(function*(){var e,o,r,i,n,s,a,l;if(!this.waitResponse){!0===this.config.showLoaderButton&&this.showLoader(),null===(o=(e=this.config).onSubmit)||void 0===o||o.call(e,this),this.waitResponse=!0;const c=yield t(),d=yield c.json();if(this.waitResponse=!1,!0===this.config.showLoaderButton&&this.hideLoader(),null===(i=(r=this.config).onResponse)||void 0===i||i.call(r,d,this),200===c.status){if(!0===d.success)null===(s=(n=this.config).onResponseSuccess)||void 0===s||s.call(n,d,this);else if(null===(l=(a=this.config).onResponseUnsuccess)||void 0===l||l.call(a,d,this),!0===d.error&&(d["error-msg"]&&this.showErrorForm(d["error-msg"]),d.errors)){const t=[];d.errors.forEach((e=>{const o=this.$el.querySelector(`input[name="${e.name}"]`);o?(t.push(o),this.showError(o,e["error-msg"])):console.warn(`Не найдено поле с именем: ${e.name}, для вывода ошибки: ${e["error-msg"]}`)})),!0===this.config.scrollToFirstErroredInput&&this.scrollToFirstErroredInput(t)}!function(t){if(Object.prototype.hasOwnProperty.call(t,"redirect-url"))if(t["redirect-url-delay"]){const e=Number(t["redirect-url-delay"]);setTimeout((()=>{window.location.href=t["redirect-url"]}),e)}else window.location.href=t["redirect-url"];else if(Object.prototype.hasOwnProperty.call(t,"reload")){if(!0===t.reload)if(t["reload-delay"]){const e=Number(t["reload-delay"]);setTimeout((()=>{window.location.reload()}),e)}else window.location.reload()}else if(Object.prototype.hasOwnProperty.call(t,"toast")){const e=t.toast;(Array.isArray(e)?e:[e]).forEach((t=>{window.showToast(t)}))}else Object.prototype.hasOwnProperty.call(t,"error-toast")&&window.showToast(t["error-toast"])}(d)}}}));this._onSubmitHandler=t=>e(this,void 0,void 0,(function*(){var e,o,i,n;t.preventDefault(),null===(o=(e=this.config).onBeforeValidate)||void 0===o||o.call(e,this);const s=yield this.validate();null===(n=(i=this.config).onAfterValidate)||void 0===n||n.call(i,s,this),s&&r()})),this.$el.addEventListener("submit",this._onSubmitHandler)}showError(t,e){(function(t,e){if(null==t)return null;let o=t;for(;o;){if(o.matches(e))return o;o=o.parentElement}return null})(t,this.config.inputWrapperSelector).showError(e)}showLoader(){var t;const e=`\n\t\t\t<svg height="38" viewBox="0 0 38 38" width="38" xmlns="http://www.w3.org/2000/svg">\n\t\t\t\t<defs>\n\t\t\t\t<linearGradient id="form-father-loader" x1="8.042%" x2="65.682%" y1="0%" y2="23.865%">\n\t\t\t\t\t<stop offset="0%" stop-color="${this.config.loaderColor}" stop-opacity="0"/>\n\t\t\t\t\t<stop offset="63.146%" stop-color="${this.config.loaderColor}" stop-opacity=".631"/>\n\t\t\t\t\t<stop offset="100%" stop-color="${this.config.loaderColor}"/>\n\t\t\t\t</linearGradient>\n\t\t\t\t</defs>\n\t\t\t\t<g fill-rule="evenodd" fill="none">\n\t\t\t\t<g transform="translate(1 1)">\n\t\t\t\t\t<path d="M36 18c0-9.94-8.06-18-18-18" id="Oval-2" stroke-width="5" stroke="url(#form-father-loader)"></path>\n\t\t\t\t\t<circle cx="36" cy="18" fill="${this.config.loaderColor}" r="1"></circle>\n\t\t\t\t</g>\n\t\t\t\t</g>\n\t\t\t</svg>\n\t\t`,o=document.createElement("span");o.innerHTML=e,o.className="button__loader",null===(t=this.$submit)||void 0===t||t.insertAdjacentElement("afterbegin",o),requestAnimationFrame((()=>{o.setAttribute("data-showed","")}))}hideLoader(){var t,e;null===(t=this.$submit)||void 0===t||t.classList.remove("button--loading");const o=null===(e=this.$submit)||void 0===e?void 0:e.querySelector(".button__loader");o&&(o.classList.remove("active"),o.removeAttribute("data-showed"),setTimeout((()=>{o.remove()}),250))}hideError(t){const e=t.parentElement;e&&e.classList.contains("input__wrapper")&&(e.getAttribute("data-type-error"),e.classList.remove("input__wrapper--error"))}scrollToFirstErroredInput(t){if(t.length>0){const e=Array.from(this.$el.querySelectorAll("input"));let o=null,r=1/0;if(t.forEach((t=>{const i=e.findIndex((e=>e===t));i<r&&(r=i,o=t)})),o){const t=o.getBoundingClientRect(),e=window.pageYOffset||document.documentElement.scrollTop,r=t.top+e-window.innerHeight/2;document.documentElement.setAttribute("data-now-scrolling",""),window.scrollTo({top:r,behavior:"smooth"}),setTimeout((()=>{document.documentElement.removeAttribute("data-now-scrolling")}),800)}}}validate(){return e(this,arguments,void 0,(function*(t=this.$el){const o=Object.assign(Object.assign({},p.defaultValidationSchema||{}),this.config.validationSchema||{}),r=t=>console.warn(`[FormFather] Unknown validation rule "${t}"`),i=[],n=new WeakSet;let s=!0;const l=(t,e=[])=>{var o;const i=[];t.hasAttribute("required")&&i.push("required"),i.push(...e);const n=(null!==(o=t.getAttribute("data-custom-validate"))&&void 0!==o?o:"").split(",").map((t=>t.trim())).filter(Boolean);i.push(...n);const s=[];for(const t of i){const e="string"==typeof t?t:t.rule;a(e)?s.push(t):r(e)}return s},c=(o,r,...n)=>e(this,[o,r,...n],void 0,(function*(e,o,r={}){if(e.hasAttribute("data-no-validate"))return;if(!("checkbox"===e.type||"radio"===e.type?!e.checked:0===e.value.trim().length)||o.includes("required")){for(const n of o){const{rule:o,params:l}="string"==typeof n?{rule:n,params:void 0}:n,c=a(o),d=yield c.fn(e.value,e,t,l),u="object"==typeof d&&null!==d,p=u?!0===d.valid:!!d;if(u&&d.effect&&(yield d.effect({value:e.value,$input:e,$form:t,params:l})),!p){s=!1;const t=d,n=u&&"string"==typeof t.message&&t.message||r[o]||c.defaultMessage;return this.showError(e,n),void i.push(e)}if(u&&d.stopOthers)return}this.hideError(e)}else this.hideError(e)}));for(const[e,r]of Object.entries(o)){const{selector:o,rules:i=[],messages:s={}}=r,a=o?t.querySelectorAll(o):t.querySelectorAll(`[data-validate="${e}"]`);for(const t of Array.from(a))n.has(t)||(n.add(t),yield c(t,l(t,i),s))}const d=Array.from(t.querySelectorAll("[data-custom-validate]")).filter((t=>!n.has(t)));for(const t of d)n.add(t),yield c(t,l(t,[]));return!s&&this.config.scrollToFirstErroredInput&&this.scrollToFirstErroredInput(i.filter((t=>!t.hasAttribute("data-no-error-scroll")))),s}))}disableSubmit(){var t;null===(t=this.$submit)||void 0===t||t.setAttribute("disabled","")}enableSubmit(){var t;null===(t=this.$submit)||void 0===t||t.removeAttribute("disabled")}clearInputs(){var t;null===(t=this.inputs)||void 0===t||t.forEach((t=>{var e;const o=t;"radio"===o.type||"checkbox"===o.type?o.checked=!1:(o.value="",null===(e=o.parentElement)||void 0===e||e.classList.remove("filled"),o.dispatchEvent(new Event("input")))}))}_prefersReducedMotion(){var t;return"undefined"!=typeof window&&(null===(t=window.matchMedia)||void 0===t?void 0:t.call(window,"(prefers-reduced-motion: reduce)").matches)}_canAnimate(t){return!(!t||!("animate"in t)||this._prefersReducedMotion())}_cancelAnimations(t){var e;null===(e=null==t?void 0:t.getAnimations)||void 0===e||e.call(t).forEach((t=>t.cancel()))}showErrorForm(t){var e;const o=this.$el.querySelectorAll(this.config.inputWrapperSelector),r=o[o.length-1];let i=this.$el.querySelector(".error-block-under-input__wrapper");i&&r.nextElementSibling===i?(i.style.boxSizing="border-box",i.style.overflow="hidden"):(i=document.createElement("div"),i.className="error-block-under-input__wrapper",i.innerHTML='\n <div class="error-block-under-input error-block-under-input--warning" style="display:grid;grid-template-columns:auto 1fr;gap:8px;align-items:start;">\n <div class="error-block-under-input__icon-wrapper" style="position:relative;width:20px;height:20px;">\n <span class="error-block-under-input__icon" style="display:block;width:20px;height:20px;"></span>\n <span class="error-block-under-input__icon error-block-under-input__icon--animated" style="display:block;position:absolute;inset:0;"></span>\n </div>\n <p class="error-block-under-input__text" style="margin:0;">\n <span class="error-block-under-input__main-text"></span>\n <span class="error-block-under-input__secondary-text"></span>\n </p>\n </div>\n ',Object.assign(i.style,{boxSizing:"border-box",overflow:"hidden",height:"0px",paddingTop:"0px",opacity:"0"}),r.insertAdjacentElement("afterend",i)),i.querySelector(".error-block-under-input__main-text").textContent=t;const n=i.querySelector(".error-block-under-input__secondary-text");n&&(n.textContent=""),null===(e=i.querySelector(".error-block-under-input"))||void 0===e||e.classList.remove("error-block-under-input--success"),i.querySelector(".error-block-under-input");const s=i.style.height,a=i.style.paddingTop;i.style.height="auto",i.style.paddingTop=`${this._anim.paddingTopOpened}px`;const l=i.clientHeight;if(i.style.height=s||"0px",i.style.paddingTop=a||"0px",parseFloat(i.style.height)===l&&1===parseFloat(i.style.opacity||"1"))this._ringIcon(i);else{if(this._cancelAnimations(i),this._canAnimate(i)){i.animate([{height:`${parseFloat(i.style.height)||0}px`,paddingTop:`${parseFloat(i.style.paddingTop)||0}px`,opacity:parseFloat(i.style.opacity||"0")},{height:`${l}px`,paddingTop:`${this._anim.paddingTopOpened}px`,opacity:1}],{duration:this._anim.duration,easing:this._anim.easing,fill:"forwards"}).addEventListener("finish",(()=>{i.style.height="auto",i.style.paddingTop=`${this._anim.paddingTopOpened}px`,i.style.opacity="1"}))}else i.style.height="auto",i.style.paddingTop=`${this._anim.paddingTopOpened}px`,i.style.opacity="1";this._ringIcon(i)}this.$el.addEventListener("input",(()=>this.hideErrorForm()),{passive:!0,once:!0})}hideErrorForm(){const t=this.$el.querySelectorAll(this.config.inputWrapperSelector),e=t[t.length-1].nextElementSibling;if(!e||!e.classList.contains("error-block-under-input__wrapper"))return;if((""===e.style.height||"auto"===e.style.height)&&(e.style.height=`${e.clientHeight}px`),e.style.paddingTop=e.style.paddingTop||`${this._anim.paddingTopOpened}px`,e.style.opacity=e.style.opacity||"1",this._cancelAnimations(e),this._canAnimate(e)){e.animate([{height:`${parseFloat(e.style.height)||e.clientHeight}px`,paddingTop:`${parseFloat(e.style.paddingTop)||this._anim.paddingTopOpened}px`,opacity:parseFloat(e.style.opacity||"1")},{height:"0px",paddingTop:"0px",opacity:0}],{duration:this._anim.duration,easing:this._anim.easing,fill:"forwards"}).addEventListener("finish",(()=>{e.remove()}))}else e.remove()}_ringIcon(t){const e=t.querySelector(".error-block-under-input__icon--animated");e&&(this._cancelAnimations(e),this._canAnimate(e)&&e.animate([{transform:"rotate(0deg) scale(1)",opacity:0},{transform:"rotate(-8deg) scale(1.05)",opacity:1,offset:.15},{transform:"rotate(8deg) scale(1.05)",opacity:1,offset:.3},{transform:"rotate(-6deg) scale(1.03)",opacity:1,offset:.45},{transform:"rotate(6deg) scale(1.03)",opacity:1,offset:.6},{transform:"rotate(0deg) scale(1)",opacity:0}],{duration:this._anim.iconDuration,easing:"ease"}))}destroy(){var t,e;this._onSubmitHandler&&(this.$el.removeEventListener("submit",this._onSubmitHandler),this._onSubmitHandler=void 0);const o=this.$el.querySelector(".error-block-under-input__wrapper");o&&(this._cancelAnimations(o),o.remove()),null===(t=this.$submit)||void 0===t||t.classList.remove("button--loading");const r=null===(e=this.$submit)||void 0===e?void 0:e.querySelector(".button__loader");r&&r.remove();this.$el.querySelectorAll("input, textarea").forEach((t=>{t instanceof HTMLInputElement&&this.hideError(t)})),document.documentElement.removeAttribute("data-now-scrolling"),this.inputs=null,this.$submit=null,this.waitResponse=!1}isCorrectArguments(){return"FORM"!==this.$el.tagName?(console.warn("$el не является тегом form"),!1):!!this.$submit||(console.warn("В форме нет кнопки с типом submit"),!1)}}return t.default=p,t.getAllValidators=function(){return new Map(n)},t.getValidator=a,t.registerValidator=s,Object.defineProperty(t,"__esModule",{value:!0}),t}({});
|
|
1
|
+
var FormFather=function(t){"use strict";function e(t,e,i,o){return new(i||(i=Promise))((function(r,n){function s(t){try{l(o.next(t))}catch(t){n(t)}}function a(t){try{l(o.throw(t))}catch(t){n(t)}}function l(t){var e;t.done?r(t.value):(e=t.value,e instanceof i?e:new i((function(t){t(e)}))).then(s,a)}l((o=o.apply(t,e||[])).next())}))}function i(t){const e="FORM"===t.tagName?Array.from(t.elements):Array.from(t.querySelectorAll("input, select, textarea, button")),i=new FormData;return e.forEach((t=>{if(!(t instanceof HTMLInputElement||t instanceof HTMLSelectElement||t instanceof HTMLTextAreaElement||t instanceof HTMLButtonElement))return;if(!t.name||t.hasAttribute("data-no-serialize"))return;const{name:e,type:o}=t;if("file"===o)if("true"===t.getAttribute("multiple")){const o=t.files;if(o)for(let t=0;t<o.length;t++)i.append(`${e}[${t}]`,o[t])}else{const o=t.files;o&&o[0]&&i.append(e,o[0])}else{let r;if("select-one"===o||"select-multiple"===o){const n=t;if("select-multiple"===o){Array.from(n.selectedOptions).map((t=>t.value)).forEach(((t,o)=>{i.append(`${e}[${o}]`,t)}))}else r=n.value,r&&i.append(e,r)}else if("tel"===o)r=t.value.split(/[\s()-]/).join("").replace(/^8/,"+7");else if("checkbox"===o||"radio"===o){if(!t.checked)return;r=t.value}else if("date"===o){const e=t.getAttribute("data-date-format");r=e?function(t,e){const i=new Date(t),o=t=>t.toString().padStart(2,"0"),r=o(i.getDate()),n=o(i.getMonth()+1),s=i.getFullYear().toString();return e.replace("dd",r).replace("mm",n).replace("yyyy",s)}(t.value,e):t.value}else r=t.value;r&&"select-one"!==o&&"select-multiple"!==o&&i.append(e,r)}})),i}var o;"function"==typeof SuppressedError&&SuppressedError;const r=Symbol.for("FormFather.validators"),n=null!==(o=globalThis[r])&&void 0!==o?o:globalThis[r]=new Map;function s(t,e,i,{override:o=!1}={}){!n.has(t)||o?n.set(t,{fn:e,defaultMessage:i}):console.warn(`[FormFather] Validator "${t}" already exists; pass { override: true } to replace`)}const a=t=>n.get(t);var l;s("required",((t,e,i)=>{if(e&&e.type){if("checkbox"===e.type)return e.checked;if("radio"===e.type){const t=i.querySelectorAll(`input[type="radio"][name="${e.name}"]`);return Array.from(t).some((t=>t.checked))}}return t.trim().length>0}),"Пустое значение"),s("email",(t=>/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(t)),"Неверный формат"),s("tel",(t=>/^\+7\d{10}$/.test(t)),"Неверный формат"),s("url",(t=>/^(https?:\/\/).+\..+/.test(t)),"Неверный формат"),s("not-numbers",(t=>!/[0-9]/.test(t)),"Неверный формат");const c=Symbol.for("formfather.shared"),d=null!==(l=globalThis[c])&&void 0!==l?l:globalThis[c]={defaultParams:{},defaultValidationSchema:void 0};let u=0;const p={tel:{selector:'input[type="tel"]',rules:["tel"]},email:{selector:'input[type="email"]',rules:["email"]},required:{selector:"[required]",rules:["required"]},url:{selector:'input[type="url"]',rules:["url"]},"not-numbers":{selector:'input[data-validate="not-numbers"]',rules:["not-numbers"]}};class h{static get defaultParams(){return d.defaultParams}static set defaultParams(t){d.defaultParams=t}static get defaultValidationSchema(){var t;return null!==(t=d.defaultValidationSchema)&&void 0!==t?t:d.defaultValidationSchema=p}static set defaultValidationSchema(t){d.defaultValidationSchema=t}constructor(t,i){this.$submits=[],this.waitResponse=!1,this.inputs=null,this._anim={duration:220,easing:"ease",paddingTopOpened:16,iconDuration:700},this.$el=t,this.options=i;this.config=Object.assign({},{onSubmit:()=>{},onResponse:()=>{},onResponseSuccess:()=>{},onResponseUnsuccess:()=>{},customTypeError:null,loaderColor:"#fff",showLoaderButton:!0,scrollToFirstErroredInput:!0,logging:!1,inputSelector:".input",inputWrapperSelector:".input-primary"},h.defaultParams,i),this.$el?(this.$submits=this.findSubmitElements(),this._onSubmitClickHandler=t=>e(this,void 0,void 0,(function*(){var e,i,o,r;t.preventDefault(),null===(i=(e=this.config).onBeforeValidate)||void 0===i||i.call(e,this);const n=yield this.validate();null===(r=(o=this.config).onAfterValidate)||void 0===r||r.call(o,n,this),n&&(yield this.submit())})),this.isCorrectArguments()&&(this.$submits.forEach((t=>{t.addEventListener("click",this._onSubmitClickHandler)})),this.initialization())):console.warn("Empty $el")}getOptions(){return this.options}static setDefaultParams(t){this.defaultParams=Object.assign(Object.assign({},this.defaultParams),t)}findSubmitElements(){return Array.from(this.$el.querySelectorAll('input[type="submit"], button[type="submit"]'))}initialization(){this.inputs=this.$el.querySelectorAll(this.config.inputSelector),this._onSubmitHandler=t=>e(this,void 0,void 0,(function*(){var e,i,o,r;t.preventDefault(),null===(i=(e=this.config).onBeforeValidate)||void 0===i||i.call(e,this);const n=yield this.validate();null===(r=(o=this.config).onAfterValidate)||void 0===r||r.call(o,n,this),n&&this.submit()})),this.$el.addEventListener("submit",this._onSubmitHandler)}showError(t,e){(function(t,e){if(null==t)return null;let i=t;for(;i;){if(i.matches(e))return i;i=i.parentElement}return null})(t,this.config.inputWrapperSelector).showError(e)}showLoader(){this.$submits.forEach((t=>{if(t.classList.add("button--loading"),t.querySelector(".button__loader"))return;const e="form-father-loader-"+u++,i=`\n\t\t\t\t<svg height="38" viewBox="0 0 38 38" width="38" xmlns="http://www.w3.org/2000/svg">\n\t\t\t\t\t<defs>\n\t\t\t\t\t<linearGradient id="${e}" x1="8.042%" x2="65.682%" y1="0%" y2="23.865%">\n\t\t\t\t\t\t<stop offset="0%" stop-color="${this.config.loaderColor}" stop-opacity="0"/>\n\t\t\t\t\t\t<stop offset="63.146%" stop-color="${this.config.loaderColor}" stop-opacity=".631"/>\n\t\t\t\t\t\t<stop offset="100%" stop-color="${this.config.loaderColor}"/>\n\t\t\t\t\t</linearGradient>\n\t\t\t\t\t</defs>\n\t\t\t\t\t<g fill-rule="evenodd" fill="none">\n\t\t\t\t\t<g transform="translate(1 1)">\n\t\t\t\t\t\t<path d="M36 18c0-9.94-8.06-18-18-18" id="Oval-2" stroke-width="5" stroke="url(#${e})"></path>\n\t\t\t\t\t\t<circle cx="36" cy="18" fill="${this.config.loaderColor}" r="1"></circle>\n\t\t\t\t\t</g>\n\t\t\t\t\t</g>\n\t\t\t\t</svg>\n\t\t\t`,o=document.createElement("span");o.innerHTML=i,o.className="button__loader",t.insertAdjacentElement("afterbegin",o),requestAnimationFrame((()=>{o.setAttribute("data-showed","")}))}))}hideLoader(){this.$submits.forEach((t=>{t.classList.remove("button--loading");const e=t.querySelector(".button__loader");e&&(e.classList.remove("active"),e.removeAttribute("data-showed"),setTimeout((()=>{e.remove()}),250))}))}hideError(t){const e=t.parentElement;e&&e.classList.contains("input__wrapper")&&(e.getAttribute("data-type-error"),e.classList.remove("input__wrapper--error"))}scrollToFirstErroredInput(t){if(t.length>0){const e=Array.from(this.$el.querySelectorAll("input"));let i=null,o=1/0;if(t.forEach((t=>{const r=e.findIndex((e=>e===t));r<o&&(o=r,i=t)})),i){const t=i.getBoundingClientRect(),e=window.pageYOffset||document.documentElement.scrollTop,o=t.top+e-window.innerHeight/2;document.documentElement.setAttribute("data-now-scrolling",""),window.scrollTo({top:o,behavior:"smooth"}),setTimeout((()=>{document.documentElement.removeAttribute("data-now-scrolling")}),800)}}}validate(){return e(this,arguments,void 0,(function*(t=this.$el){const i=Object.assign(Object.assign({},h.defaultValidationSchema||{}),this.config.validationSchema||{}),o=t=>console.warn(`[FormFather] Unknown validation rule "${t}"`),r=[],n=new WeakSet;let s=!0;const l=(t,e=[])=>{var i;const r=[];t.hasAttribute("required")&&r.push("required"),r.push(...e);const n=(null!==(i=t.getAttribute("data-custom-validate"))&&void 0!==i?i:"").split(",").map((t=>t.trim())).filter(Boolean);r.push(...n);const s=[];for(const t of r){const e="string"==typeof t?t:t.rule;a(e)?s.push(t):o(e)}return s},c=(i,o,...n)=>e(this,[i,o,...n],void 0,(function*(e,i,o={}){if(e.hasAttribute("data-no-validate"))return;if(!("checkbox"===e.type||"radio"===e.type?!e.checked:0===e.value.trim().length)||i.includes("required")){for(const n of i){const{rule:i,params:l}="string"==typeof n?{rule:n,params:void 0}:n,c=a(i),d=yield c.fn(e.value,e,t,l),u="object"==typeof d&&null!==d,p=u?!0===d.valid:!!d;if(u&&d.effect&&(yield d.effect({value:e.value,$input:e,$form:t,params:l})),!p){s=!1;const t=d,n=u&&"string"==typeof t.message&&t.message||o[i]||c.defaultMessage;return this.showError(e,n),void r.push(e)}if(u&&d.stopOthers)return}this.hideError(e)}else this.hideError(e)}));for(const[e,o]of Object.entries(i)){const{selector:i,rules:r=[],messages:s={}}=o,a=i?t.querySelectorAll(i):t.querySelectorAll(`[data-validate="${e}"]`);for(const t of Array.from(a))n.has(t)||(n.add(t),yield c(t,l(t,r),s))}const d=Array.from(t.querySelectorAll("[data-custom-validate]")).filter((t=>!n.has(t)));for(const t of d)n.add(t),yield c(t,l(t,[]));return!s&&this.config.scrollToFirstErroredInput&&this.scrollToFirstErroredInput(r.filter((t=>!t.hasAttribute("data-no-error-scroll")))),s}))}disableSubmit(){this.$submits.forEach((t=>{t.setAttribute("disabled","")}))}enableSubmit(){this.$submits.forEach((t=>{t.removeAttribute("disabled")}))}clearInputs(){var t;null===(t=this.inputs)||void 0===t||t.forEach((t=>{var e;const i=t;"radio"===i.type||"checkbox"===i.type?i.checked=!1:(i.value="",null===(e=i.parentElement)||void 0===e||e.classList.remove("filled"),i.dispatchEvent(new Event("input")))}))}_prefersReducedMotion(){var t;return"undefined"!=typeof window&&(null===(t=window.matchMedia)||void 0===t?void 0:t.call(window,"(prefers-reduced-motion: reduce)").matches)}_canAnimate(t){return!(!t||!("animate"in t)||this._prefersReducedMotion())}_cancelAnimations(t){var e;null===(e=null==t?void 0:t.getAnimations)||void 0===e||e.call(t).forEach((t=>t.cancel()))}showErrorForm(t){var e;const i=this.$el.querySelectorAll(this.config.inputWrapperSelector),o=i[i.length-1];let r=this.$el.querySelector(".error-block-under-input__wrapper");r&&o.nextElementSibling===r?(r.style.boxSizing="border-box",r.style.overflow="hidden"):(r=document.createElement("div"),r.className="error-block-under-input__wrapper",r.innerHTML='\n <div class="error-block-under-input error-block-under-input--warning" style="display:grid;grid-template-columns:auto 1fr;gap:8px;align-items:start;">\n <div class="error-block-under-input__icon-wrapper" style="position:relative;width:20px;height:20px;">\n <span class="error-block-under-input__icon" style="display:block;width:20px;height:20px;"></span>\n <span class="error-block-under-input__icon error-block-under-input__icon--animated" style="display:block;position:absolute;inset:0;"></span>\n </div>\n <p class="error-block-under-input__text" style="margin:0;">\n <span class="error-block-under-input__main-text"></span>\n <span class="error-block-under-input__secondary-text"></span>\n </p>\n </div>\n ',Object.assign(r.style,{boxSizing:"border-box",overflow:"hidden",height:"0px",paddingTop:"0px",opacity:"0"}),o.insertAdjacentElement("afterend",r)),r.querySelector(".error-block-under-input__main-text").textContent=t;const n=r.querySelector(".error-block-under-input__secondary-text");n&&(n.textContent=""),null===(e=r.querySelector(".error-block-under-input"))||void 0===e||e.classList.remove("error-block-under-input--success"),r.querySelector(".error-block-under-input");const s=r.style.height,a=r.style.paddingTop;r.style.height="auto",r.style.paddingTop=`${this._anim.paddingTopOpened}px`;const l=r.clientHeight;if(r.style.height=s||"0px",r.style.paddingTop=a||"0px",parseFloat(r.style.height)===l&&1===parseFloat(r.style.opacity||"1"))this._ringIcon(r);else{if(this._cancelAnimations(r),this._canAnimate(r)){r.animate([{height:`${parseFloat(r.style.height)||0}px`,paddingTop:`${parseFloat(r.style.paddingTop)||0}px`,opacity:parseFloat(r.style.opacity||"0")},{height:`${l}px`,paddingTop:`${this._anim.paddingTopOpened}px`,opacity:1}],{duration:this._anim.duration,easing:this._anim.easing,fill:"forwards"}).addEventListener("finish",(()=>{r.style.height="auto",r.style.paddingTop=`${this._anim.paddingTopOpened}px`,r.style.opacity="1"}))}else r.style.height="auto",r.style.paddingTop=`${this._anim.paddingTopOpened}px`,r.style.opacity="1";this._ringIcon(r)}this.$el.addEventListener("input",(()=>this.hideErrorForm()),{passive:!0,once:!0})}hideErrorForm(){const t=this.$el.querySelectorAll(this.config.inputWrapperSelector),e=t[t.length-1].nextElementSibling;if(!e||!e.classList.contains("error-block-under-input__wrapper"))return;if((""===e.style.height||"auto"===e.style.height)&&(e.style.height=`${e.clientHeight}px`),e.style.paddingTop=e.style.paddingTop||`${this._anim.paddingTopOpened}px`,e.style.opacity=e.style.opacity||"1",this._cancelAnimations(e),this._canAnimate(e)){e.animate([{height:`${parseFloat(e.style.height)||e.clientHeight}px`,paddingTop:`${parseFloat(e.style.paddingTop)||this._anim.paddingTopOpened}px`,opacity:parseFloat(e.style.opacity||"1")},{height:"0px",paddingTop:"0px",opacity:0}],{duration:this._anim.duration,easing:this._anim.easing,fill:"forwards"}).addEventListener("finish",(()=>{e.remove()}))}else e.remove()}_ringIcon(t){const e=t.querySelector(".error-block-under-input__icon--animated");e&&(this._cancelAnimations(e),this._canAnimate(e)&&e.animate([{transform:"rotate(0deg) scale(1)",opacity:0},{transform:"rotate(-8deg) scale(1.05)",opacity:1,offset:.15},{transform:"rotate(8deg) scale(1.05)",opacity:1,offset:.3},{transform:"rotate(-6deg) scale(1.03)",opacity:1,offset:.45},{transform:"rotate(6deg) scale(1.03)",opacity:1,offset:.6},{transform:"rotate(0deg) scale(1)",opacity:0}],{duration:this._anim.iconDuration,easing:"ease"}))}destroy(){this._onSubmitHandler&&(this.$el.removeEventListener("submit",this._onSubmitHandler),this._onSubmitHandler=void 0),this._onSubmitClickHandler&&(this.$submits.forEach((t=>{t.removeEventListener("click",this._onSubmitClickHandler)})),this._onSubmitClickHandler=void 0);const t=this.$el.querySelector(".error-block-under-input__wrapper");t&&(this._cancelAnimations(t),t.remove()),this.$submits.forEach((t=>{t.classList.remove("button--loading");const e=t.querySelector(".button__loader");e&&e.remove()}));this.$el.querySelectorAll("input, textarea").forEach((t=>{t instanceof HTMLInputElement&&this.hideError(t)})),document.documentElement.removeAttribute("data-now-scrolling"),this.inputs=null,this.$submits=[],this.waitResponse=!1}isCorrectArguments(){return"FORM"!==this.$el.tagName?(console.warn("$el не является тегом form"),!1):0!==this.$submits.length||(console.warn("В форме нет кнопки с типом submit"),!1)}sendData(){return e(this,void 0,void 0,(function*(){var t;const e=this.$el.getAttribute("action")||"",o=(null===(t=this.$el.getAttribute("method"))||void 0===t?void 0:t.toUpperCase())||"POST",r=this.$el.getAttribute("enctype")||"application/x-www-form-urlencoded";let n=null,s={};const a=i(this.$el);let l={};if(a.forEach(((t,e)=>{l[e]=t})),this.config.logging)for(const t of a.keys())console.log(a.get(t));switch("function"==typeof this.config.wrapData&&(l=this.config.wrapData(l)),r){case"application/x-www-form-urlencoded":n=new URLSearchParams(i(this.$el)).toString(),s["Content-Type"]="application/x-www-form-urlencoded";break;case"multipart/form-data":n=i(this.$el);break;case"text/plain":n=Array.from(i(this.$el)).map((([t,e])=>`${encodeURIComponent(t)}=${encodeURIComponent(e)}`)).join("\n"),s["Content-Type"]="text/plain";break;case"application/json":n=JSON.stringify(l),s["Content-Type"]="application/json";break;default:console.warn(`Неизвестный enctype: ${r}. Используется application/x-www-form-urlencoded.`),n=new URLSearchParams(i(this.$el)).toString(),s["Content-Type"]="application/x-www-form-urlencoded"}return fetch(e,{method:o,body:n,headers:s})}))}submit(){return e(this,void 0,void 0,(function*(){var t,e,i,o,r,n,s,a;if(!this.waitResponse){!0===this.config.showLoaderButton&&this.showLoader(),null===(e=(t=this.config).onSubmit)||void 0===e||e.call(t,this),this.waitResponse=!0;const l=yield this.sendData(),c=yield l.json();if(this.waitResponse=!1,!0===this.config.showLoaderButton&&this.hideLoader(),null===(o=(i=this.config).onResponse)||void 0===o||o.call(i,c,this),200===l.status){if(!0===c.success)null===(n=(r=this.config).onResponseSuccess)||void 0===n||n.call(r,c,this);else if(null===(a=(s=this.config).onResponseUnsuccess)||void 0===a||a.call(s,c,this),!0===c.error&&(c["error-msg"]&&this.showErrorForm(c["error-msg"]),c.errors)){const t=[];c.errors.forEach((e=>{const i=this.$el.querySelector(`input[name="${e.name}"]`);i?(t.push(i),this.showError(i,e["error-msg"])):console.warn(`Не найдено поле с именем: ${e.name}, для вывода ошибки: ${e["error-msg"]}`)})),!0===this.config.scrollToFirstErroredInput&&this.scrollToFirstErroredInput(t)}!function(t){if(Object.prototype.hasOwnProperty.call(t,"redirect-url"))if(t["redirect-url-delay"]){const e=Number(t["redirect-url-delay"]);setTimeout((()=>{window.location.href=t["redirect-url"]}),e)}else window.location.href=t["redirect-url"];else if(Object.prototype.hasOwnProperty.call(t,"reload")){if(!0===t.reload)if(t["reload-delay"]){const e=Number(t["reload-delay"]);setTimeout((()=>{window.location.reload()}),e)}else window.location.reload()}else if(Object.prototype.hasOwnProperty.call(t,"toast")){const e=t.toast;(Array.isArray(e)?e:[e]).forEach((t=>{window.showToast(t)}))}else Object.prototype.hasOwnProperty.call(t,"error-toast")&&window.showToast(t["error-toast"])}(c)}}}))}}return t.default=h,t.getAllValidators=function(){return new Map(n)},t.getValidator=a,t.registerValidator=s,Object.defineProperty(t,"__esModule",{value:!0}),t}({});
|
|
2
2
|
//# sourceMappingURL=FormFather.min.js.map
|
package/index.d.ts
CHANGED
|
@@ -105,10 +105,11 @@ declare class Form {
|
|
|
105
105
|
$el: HTMLFormElement;
|
|
106
106
|
private options;
|
|
107
107
|
private config;
|
|
108
|
-
private $
|
|
108
|
+
private $submits;
|
|
109
109
|
private waitResponse;
|
|
110
110
|
private inputs;
|
|
111
111
|
private _onSubmitHandler?;
|
|
112
|
+
private _onSubmitClickHandler?;
|
|
112
113
|
private static get defaultParams();
|
|
113
114
|
private static set defaultParams(value);
|
|
114
115
|
static get defaultValidationSchema(): ValidationSchema;
|
|
@@ -129,6 +130,7 @@ declare class Form {
|
|
|
129
130
|
* свойства, которые необходимо обновить; остальные сохранятся без изменений.
|
|
130
131
|
*/
|
|
131
132
|
static setDefaultParams(params: Partial<FormOptions>): void;
|
|
133
|
+
private findSubmitElements;
|
|
132
134
|
private initialization;
|
|
133
135
|
/**
|
|
134
136
|
* Показывает ошибку для поля ввода.
|
|
@@ -195,6 +197,8 @@ declare class Form {
|
|
|
195
197
|
* @returns {boolean} Корректны данные или нет (true/false).
|
|
196
198
|
*/
|
|
197
199
|
private isCorrectArguments;
|
|
200
|
+
private sendData;
|
|
201
|
+
private submit;
|
|
198
202
|
}
|
|
199
203
|
|
|
200
204
|
export { type ValidatorEffectCtx, type ValidatorResult, Form as default, getAllValidators, getValidator, registerValidator };
|
package/index.js
CHANGED
|
@@ -246,6 +246,7 @@ const __shared =
|
|
|
246
246
|
defaultParams: {},
|
|
247
247
|
defaultValidationSchema: undefined,
|
|
248
248
|
});
|
|
249
|
+
let loaderIdCounter = 0;
|
|
249
250
|
/** Изначальная дефолтная схема. */
|
|
250
251
|
const INITIAL_DEFAULT_SCHEMA = {
|
|
251
252
|
tel: {
|
|
@@ -295,7 +296,7 @@ class Form {
|
|
|
295
296
|
* @param {Object} options - Параметры формы.
|
|
296
297
|
*/
|
|
297
298
|
constructor($el, options) {
|
|
298
|
-
this.$
|
|
299
|
+
this.$submits = [];
|
|
299
300
|
this.waitResponse = false;
|
|
300
301
|
this.inputs = null;
|
|
301
302
|
/** Общие настройки анимаций (можно подправить под вкус) */
|
|
@@ -323,9 +324,23 @@ class Form {
|
|
|
323
324
|
/* Слияние параметров: глобальные параметры → пользовательские параметры */
|
|
324
325
|
this.config = Object.assign({}, defaultConfig, Form.defaultParams, options);
|
|
325
326
|
if (this.$el) {
|
|
326
|
-
this.$
|
|
327
|
-
|
|
327
|
+
this.$submits = this.findSubmitElements();
|
|
328
|
+
this._onSubmitClickHandler = (e) => __awaiter(this, void 0, void 0, function* () {
|
|
329
|
+
var _a, _b, _c, _d;
|
|
330
|
+
e.preventDefault();
|
|
331
|
+
(_b = (_a = this.config).onBeforeValidate) === null || _b === void 0 ? void 0 : _b.call(_a, this);
|
|
332
|
+
const isValid = yield this.validate();
|
|
333
|
+
(_d = (_c = this.config).onAfterValidate) === null || _d === void 0 ? void 0 : _d.call(_c, isValid, this);
|
|
334
|
+
if (isValid) {
|
|
335
|
+
yield this.submit();
|
|
336
|
+
}
|
|
337
|
+
});
|
|
338
|
+
if (this.isCorrectArguments()) {
|
|
339
|
+
this.$submits.forEach($submit => {
|
|
340
|
+
$submit.addEventListener('click', this._onSubmitClickHandler);
|
|
341
|
+
});
|
|
328
342
|
this.initialization();
|
|
343
|
+
}
|
|
329
344
|
}
|
|
330
345
|
else {
|
|
331
346
|
console.warn('Empty $el');
|
|
@@ -344,6 +359,9 @@ class Form {
|
|
|
344
359
|
static setDefaultParams(params) {
|
|
345
360
|
this.defaultParams = Object.assign(Object.assign({}, this.defaultParams), params);
|
|
346
361
|
}
|
|
362
|
+
findSubmitElements() {
|
|
363
|
+
return Array.from(this.$el.querySelectorAll('input[type="submit"], button[type="submit"]'));
|
|
364
|
+
}
|
|
347
365
|
initialization() {
|
|
348
366
|
this.inputs = this.$el.querySelectorAll(this.config.inputSelector);
|
|
349
367
|
/* TODO: Добавить параметр с возможностью отключать/включать кнопку, в зависимости от валидности поля. */
|
|
@@ -356,101 +374,6 @@ class Form {
|
|
|
356
374
|
// else this.disableSubmit();
|
|
357
375
|
// });
|
|
358
376
|
// }
|
|
359
|
-
const sendData = () => __awaiter(this, void 0, void 0, function* () {
|
|
360
|
-
var _a;
|
|
361
|
-
const action = this.$el.getAttribute('action') || '';
|
|
362
|
-
const method = ((_a = this.$el.getAttribute('method')) === null || _a === void 0 ? void 0 : _a.toUpperCase()) || 'POST';
|
|
363
|
-
const enctype = this.$el.getAttribute('enctype') || 'application/x-www-form-urlencoded';
|
|
364
|
-
let body = null;
|
|
365
|
-
let headers = {};
|
|
366
|
-
// Сбор данных из формы
|
|
367
|
-
const formData = serializeToFormData(this.$el);
|
|
368
|
-
let data = {};
|
|
369
|
-
formData.forEach((value, key) => {
|
|
370
|
-
data[key] = value;
|
|
371
|
-
});
|
|
372
|
-
if (this.config.logging) {
|
|
373
|
-
for (const key of formData.keys())
|
|
374
|
-
console.log(formData.get(key));
|
|
375
|
-
}
|
|
376
|
-
// Применение обёртки, если указано в настройках
|
|
377
|
-
if (typeof this.config.wrapData === 'function') {
|
|
378
|
-
data = this.config.wrapData(data);
|
|
379
|
-
}
|
|
380
|
-
switch (enctype) {
|
|
381
|
-
case 'application/x-www-form-urlencoded':
|
|
382
|
-
body = new URLSearchParams(serializeToFormData(this.$el)).toString();
|
|
383
|
-
headers['Content-Type'] = 'application/x-www-form-urlencoded';
|
|
384
|
-
break;
|
|
385
|
-
case 'multipart/form-data':
|
|
386
|
-
body = serializeToFormData(this.$el);
|
|
387
|
-
// ВАЖНО: Не указываем Content-Type, т.к. браузер сам добавит с корректной границей!
|
|
388
|
-
break;
|
|
389
|
-
case 'text/plain':
|
|
390
|
-
body = Array.from(serializeToFormData(this.$el))
|
|
391
|
-
.map(([key, value]) => `${encodeURIComponent(key)}=${encodeURIComponent(value)}`)
|
|
392
|
-
.join('\n');
|
|
393
|
-
headers['Content-Type'] = 'text/plain';
|
|
394
|
-
break;
|
|
395
|
-
case 'application/json':
|
|
396
|
-
body = JSON.stringify(data);
|
|
397
|
-
headers['Content-Type'] = 'application/json';
|
|
398
|
-
break;
|
|
399
|
-
default:
|
|
400
|
-
console.warn(`Неизвестный enctype: ${enctype}. Используется application/x-www-form-urlencoded.`);
|
|
401
|
-
body = new URLSearchParams(serializeToFormData(this.$el)).toString();
|
|
402
|
-
headers['Content-Type'] = 'application/x-www-form-urlencoded';
|
|
403
|
-
}
|
|
404
|
-
return fetch(action, {
|
|
405
|
-
method,
|
|
406
|
-
body,
|
|
407
|
-
headers,
|
|
408
|
-
});
|
|
409
|
-
});
|
|
410
|
-
const submit = () => __awaiter(this, void 0, void 0, function* () {
|
|
411
|
-
var _a, _b, _c, _d, _e, _f, _g, _h;
|
|
412
|
-
if (!this.waitResponse) {
|
|
413
|
-
if (this.config.showLoaderButton === true)
|
|
414
|
-
this.showLoader();
|
|
415
|
-
(_b = (_a = this.config).onSubmit) === null || _b === void 0 ? void 0 : _b.call(_a, this);
|
|
416
|
-
this.waitResponse = true;
|
|
417
|
-
const response = yield sendData();
|
|
418
|
-
const responseBody = yield response.json();
|
|
419
|
-
this.waitResponse = false;
|
|
420
|
-
if (this.config.showLoaderButton === true)
|
|
421
|
-
this.hideLoader();
|
|
422
|
-
(_d = (_c = this.config).onResponse) === null || _d === void 0 ? void 0 : _d.call(_c, responseBody, this);
|
|
423
|
-
if (response.status === 200) {
|
|
424
|
-
if (responseBody.success === true) {
|
|
425
|
-
(_f = (_e = this.config).onResponseSuccess) === null || _f === void 0 ? void 0 : _f.call(_e, responseBody, this);
|
|
426
|
-
}
|
|
427
|
-
else {
|
|
428
|
-
(_h = (_g = this.config).onResponseUnsuccess) === null || _h === void 0 ? void 0 : _h.call(_g, responseBody, this);
|
|
429
|
-
if (responseBody.error === true) {
|
|
430
|
-
if (responseBody['error-msg']) {
|
|
431
|
-
this.showErrorForm(responseBody['error-msg']);
|
|
432
|
-
}
|
|
433
|
-
if (responseBody.errors) {
|
|
434
|
-
const inputsList = [];
|
|
435
|
-
responseBody.errors.forEach(error => {
|
|
436
|
-
const $input = this.$el.querySelector(`input[name="${error.name}"]`);
|
|
437
|
-
if ($input) {
|
|
438
|
-
inputsList.push($input);
|
|
439
|
-
this.showError($input, error['error-msg']);
|
|
440
|
-
}
|
|
441
|
-
else {
|
|
442
|
-
console.warn(`Не найдено поле с именем: ${error.name}, для вывода ошибки: ${error['error-msg']}`);
|
|
443
|
-
}
|
|
444
|
-
});
|
|
445
|
-
if (this.config.scrollToFirstErroredInput === true)
|
|
446
|
-
this.scrollToFirstErroredInput(inputsList);
|
|
447
|
-
}
|
|
448
|
-
}
|
|
449
|
-
}
|
|
450
|
-
parseCommonResponseProperties(responseBody);
|
|
451
|
-
}
|
|
452
|
-
}
|
|
453
|
-
});
|
|
454
377
|
this._onSubmitHandler = (e) => __awaiter(this, void 0, void 0, function* () {
|
|
455
378
|
var _a, _b, _c, _d;
|
|
456
379
|
e.preventDefault();
|
|
@@ -458,7 +381,7 @@ class Form {
|
|
|
458
381
|
const isValid = yield this.validate();
|
|
459
382
|
(_d = (_c = this.config).onAfterValidate) === null || _d === void 0 ? void 0 : _d.call(_c, isValid, this);
|
|
460
383
|
if (isValid)
|
|
461
|
-
submit();
|
|
384
|
+
this.submit();
|
|
462
385
|
});
|
|
463
386
|
this.$el.addEventListener('submit', this._onSubmitHandler);
|
|
464
387
|
}
|
|
@@ -473,46 +396,50 @@ class Form {
|
|
|
473
396
|
}
|
|
474
397
|
/** Показывает лоадер */
|
|
475
398
|
showLoader() {
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
<
|
|
484
|
-
<
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
<
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
$
|
|
399
|
+
this.$submits.forEach($submit => {
|
|
400
|
+
$submit.classList.add('button--loading');
|
|
401
|
+
if ($submit.querySelector('.button__loader'))
|
|
402
|
+
return;
|
|
403
|
+
const gradientId = `form-father-loader-${loaderIdCounter++}`;
|
|
404
|
+
const loaderSvg = `
|
|
405
|
+
<svg height="38" viewBox="0 0 38 38" width="38" xmlns="http://www.w3.org/2000/svg">
|
|
406
|
+
<defs>
|
|
407
|
+
<linearGradient id="${gradientId}" x1="8.042%" x2="65.682%" y1="0%" y2="23.865%">
|
|
408
|
+
<stop offset="0%" stop-color="${this.config.loaderColor}" stop-opacity="0"/>
|
|
409
|
+
<stop offset="63.146%" stop-color="${this.config.loaderColor}" stop-opacity=".631"/>
|
|
410
|
+
<stop offset="100%" stop-color="${this.config.loaderColor}"/>
|
|
411
|
+
</linearGradient>
|
|
412
|
+
</defs>
|
|
413
|
+
<g fill-rule="evenodd" fill="none">
|
|
414
|
+
<g transform="translate(1 1)">
|
|
415
|
+
<path d="M36 18c0-9.94-8.06-18-18-18" id="Oval-2" stroke-width="5" stroke="url(#${gradientId})"></path>
|
|
416
|
+
<circle cx="36" cy="18" fill="${this.config.loaderColor}" r="1"></circle>
|
|
417
|
+
</g>
|
|
418
|
+
</g>
|
|
419
|
+
</svg>
|
|
420
|
+
`;
|
|
421
|
+
const $loaderEl = document.createElement('span');
|
|
422
|
+
$loaderEl.innerHTML = loaderSvg;
|
|
423
|
+
$loaderEl.className = 'button__loader';
|
|
424
|
+
$submit.insertAdjacentElement('afterbegin', $loaderEl);
|
|
425
|
+
requestAnimationFrame(() => {
|
|
426
|
+
$loaderEl.setAttribute('data-showed', '');
|
|
427
|
+
});
|
|
502
428
|
});
|
|
503
429
|
}
|
|
504
430
|
/** Скрывает лоадер */
|
|
505
431
|
hideLoader() {
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
432
|
+
this.$submits.forEach($submit => {
|
|
433
|
+
$submit.classList.remove('button--loading');
|
|
434
|
+
const $loader = $submit.querySelector('.button__loader');
|
|
435
|
+
if ($loader) {
|
|
436
|
+
$loader.classList.remove('active');
|
|
437
|
+
$loader.removeAttribute('data-showed');
|
|
438
|
+
setTimeout(() => {
|
|
439
|
+
$loader.remove();
|
|
440
|
+
}, 250);
|
|
441
|
+
}
|
|
442
|
+
});
|
|
516
443
|
}
|
|
517
444
|
/**
|
|
518
445
|
* Скрывает ошибку.
|
|
@@ -668,13 +595,15 @@ class Form {
|
|
|
668
595
|
}
|
|
669
596
|
/** Блокирует кнопку отправки данных. */
|
|
670
597
|
disableSubmit() {
|
|
671
|
-
|
|
672
|
-
|
|
598
|
+
this.$submits.forEach($submit => {
|
|
599
|
+
$submit.setAttribute('disabled', '');
|
|
600
|
+
});
|
|
673
601
|
}
|
|
674
602
|
/** Снимает блокировку с кнопки отправки данных. */
|
|
675
603
|
enableSubmit() {
|
|
676
|
-
|
|
677
|
-
|
|
604
|
+
this.$submits.forEach($submit => {
|
|
605
|
+
$submit.removeAttribute('disabled');
|
|
606
|
+
});
|
|
678
607
|
}
|
|
679
608
|
/** Очищает поля ввода формы. */
|
|
680
609
|
clearInputs() {
|
|
@@ -861,12 +790,18 @@ class Form {
|
|
|
861
790
|
}
|
|
862
791
|
/** Полностью снимает слушатели и чистит артефакты формы. */
|
|
863
792
|
destroy() {
|
|
864
|
-
var _a, _b;
|
|
865
793
|
// 1) Снять submit-слушатель
|
|
866
794
|
if (this._onSubmitHandler) {
|
|
867
795
|
this.$el.removeEventListener('submit', this._onSubmitHandler);
|
|
868
796
|
this._onSubmitHandler = undefined;
|
|
869
797
|
}
|
|
798
|
+
// 1.1) Снять click-слушатель с кнопки submit
|
|
799
|
+
if (this._onSubmitClickHandler) {
|
|
800
|
+
this.$submits.forEach($submit => {
|
|
801
|
+
$submit.removeEventListener('click', this._onSubmitClickHandler);
|
|
802
|
+
});
|
|
803
|
+
this._onSubmitClickHandler = undefined;
|
|
804
|
+
}
|
|
870
805
|
// 2) Удалить формовый error-блок под инпутами (если есть)
|
|
871
806
|
const formErrorWrapper = this.$el.querySelector('.error-block-under-input__wrapper');
|
|
872
807
|
if (formErrorWrapper) {
|
|
@@ -874,10 +809,12 @@ class Form {
|
|
|
874
809
|
formErrorWrapper.remove();
|
|
875
810
|
}
|
|
876
811
|
// 3) Скрыть/удалить лоадер на кнопке submit
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
btnLoader
|
|
812
|
+
this.$submits.forEach($submit => {
|
|
813
|
+
$submit.classList.remove('button--loading');
|
|
814
|
+
const btnLoader = $submit.querySelector('.button__loader');
|
|
815
|
+
if (btnLoader)
|
|
816
|
+
btnLoader.remove();
|
|
817
|
+
});
|
|
881
818
|
// 4) Сбросить ошибки у инпутов (если разметка позволяет)
|
|
882
819
|
const inputs = this.$el.querySelectorAll('input, textarea');
|
|
883
820
|
inputs.forEach($i => {
|
|
@@ -888,7 +825,7 @@ class Form {
|
|
|
888
825
|
document.documentElement.removeAttribute('data-now-scrolling');
|
|
889
826
|
// 6) Очистить ссылки на элементы, чтобы помочь GC
|
|
890
827
|
this.inputs = null;
|
|
891
|
-
this.$
|
|
828
|
+
this.$submits = [];
|
|
892
829
|
this.waitResponse = false;
|
|
893
830
|
}
|
|
894
831
|
/**
|
|
@@ -901,12 +838,111 @@ class Form {
|
|
|
901
838
|
console.warn(`$el не является тегом form`);
|
|
902
839
|
return false;
|
|
903
840
|
}
|
|
904
|
-
if (
|
|
841
|
+
if (this.$submits.length === 0) {
|
|
905
842
|
console.warn(`В форме нет кнопки с типом submit`);
|
|
906
843
|
return false;
|
|
907
844
|
}
|
|
908
845
|
return true;
|
|
909
846
|
}
|
|
847
|
+
sendData() {
|
|
848
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
849
|
+
var _a;
|
|
850
|
+
const action = this.$el.getAttribute('action') || '';
|
|
851
|
+
const method = ((_a = this.$el.getAttribute('method')) === null || _a === void 0 ? void 0 : _a.toUpperCase()) || 'POST';
|
|
852
|
+
const enctype = this.$el.getAttribute('enctype') || 'application/x-www-form-urlencoded';
|
|
853
|
+
let body = null;
|
|
854
|
+
let headers = {};
|
|
855
|
+
// Сбор данных из формы
|
|
856
|
+
const formData = serializeToFormData(this.$el);
|
|
857
|
+
let data = {};
|
|
858
|
+
formData.forEach((value, key) => {
|
|
859
|
+
data[key] = value;
|
|
860
|
+
});
|
|
861
|
+
if (this.config.logging) {
|
|
862
|
+
for (const key of formData.keys())
|
|
863
|
+
console.log(formData.get(key));
|
|
864
|
+
}
|
|
865
|
+
// Применение обёртки, если указано в настройках
|
|
866
|
+
if (typeof this.config.wrapData === 'function') {
|
|
867
|
+
data = this.config.wrapData(data);
|
|
868
|
+
}
|
|
869
|
+
switch (enctype) {
|
|
870
|
+
case 'application/x-www-form-urlencoded':
|
|
871
|
+
body = new URLSearchParams(serializeToFormData(this.$el)).toString();
|
|
872
|
+
headers['Content-Type'] = 'application/x-www-form-urlencoded';
|
|
873
|
+
break;
|
|
874
|
+
case 'multipart/form-data':
|
|
875
|
+
body = serializeToFormData(this.$el);
|
|
876
|
+
// ВАЖНО: Не указываем Content-Type, т.к. браузер сам добавит с корректной границей!
|
|
877
|
+
break;
|
|
878
|
+
case 'text/plain':
|
|
879
|
+
body = Array.from(serializeToFormData(this.$el))
|
|
880
|
+
.map(([key, value]) => `${encodeURIComponent(key)}=${encodeURIComponent(value)}`)
|
|
881
|
+
.join('\n');
|
|
882
|
+
headers['Content-Type'] = 'text/plain';
|
|
883
|
+
break;
|
|
884
|
+
case 'application/json':
|
|
885
|
+
body = JSON.stringify(data);
|
|
886
|
+
headers['Content-Type'] = 'application/json';
|
|
887
|
+
break;
|
|
888
|
+
default:
|
|
889
|
+
console.warn(`Неизвестный enctype: ${enctype}. Используется application/x-www-form-urlencoded.`);
|
|
890
|
+
body = new URLSearchParams(serializeToFormData(this.$el)).toString();
|
|
891
|
+
headers['Content-Type'] = 'application/x-www-form-urlencoded';
|
|
892
|
+
}
|
|
893
|
+
return fetch(action, {
|
|
894
|
+
method,
|
|
895
|
+
body,
|
|
896
|
+
headers,
|
|
897
|
+
});
|
|
898
|
+
});
|
|
899
|
+
}
|
|
900
|
+
submit() {
|
|
901
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
902
|
+
var _a, _b, _c, _d, _e, _f, _g, _h;
|
|
903
|
+
if (!this.waitResponse) {
|
|
904
|
+
if (this.config.showLoaderButton === true)
|
|
905
|
+
this.showLoader();
|
|
906
|
+
(_b = (_a = this.config).onSubmit) === null || _b === void 0 ? void 0 : _b.call(_a, this);
|
|
907
|
+
this.waitResponse = true;
|
|
908
|
+
const response = yield this.sendData();
|
|
909
|
+
const responseBody = yield response.json();
|
|
910
|
+
this.waitResponse = false;
|
|
911
|
+
if (this.config.showLoaderButton === true)
|
|
912
|
+
this.hideLoader();
|
|
913
|
+
(_d = (_c = this.config).onResponse) === null || _d === void 0 ? void 0 : _d.call(_c, responseBody, this);
|
|
914
|
+
if (response.status === 200) {
|
|
915
|
+
if (responseBody.success === true) {
|
|
916
|
+
(_f = (_e = this.config).onResponseSuccess) === null || _f === void 0 ? void 0 : _f.call(_e, responseBody, this);
|
|
917
|
+
}
|
|
918
|
+
else {
|
|
919
|
+
(_h = (_g = this.config).onResponseUnsuccess) === null || _h === void 0 ? void 0 : _h.call(_g, responseBody, this);
|
|
920
|
+
if (responseBody.error === true) {
|
|
921
|
+
if (responseBody['error-msg']) {
|
|
922
|
+
this.showErrorForm(responseBody['error-msg']);
|
|
923
|
+
}
|
|
924
|
+
if (responseBody.errors) {
|
|
925
|
+
const inputsList = [];
|
|
926
|
+
responseBody.errors.forEach(error => {
|
|
927
|
+
const $input = this.$el.querySelector(`input[name="${error.name}"]`);
|
|
928
|
+
if ($input) {
|
|
929
|
+
inputsList.push($input);
|
|
930
|
+
this.showError($input, error['error-msg']);
|
|
931
|
+
}
|
|
932
|
+
else {
|
|
933
|
+
console.warn(`Не найдено поле с именем: ${error.name}, для вывода ошибки: ${error['error-msg']}`);
|
|
934
|
+
}
|
|
935
|
+
});
|
|
936
|
+
if (this.config.scrollToFirstErroredInput === true)
|
|
937
|
+
this.scrollToFirstErroredInput(inputsList);
|
|
938
|
+
}
|
|
939
|
+
}
|
|
940
|
+
}
|
|
941
|
+
parseCommonResponseProperties(responseBody);
|
|
942
|
+
}
|
|
943
|
+
}
|
|
944
|
+
});
|
|
945
|
+
}
|
|
910
946
|
}
|
|
911
947
|
|
|
912
948
|
export { Form as default, getAllValidators, getValidator, registerValidator };
|
package/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
|
package/package.json
CHANGED
package/types/index.d.ts
CHANGED
|
@@ -80,10 +80,11 @@ export default class Form {
|
|
|
80
80
|
$el: HTMLFormElement;
|
|
81
81
|
private options;
|
|
82
82
|
private config;
|
|
83
|
-
private $
|
|
83
|
+
private $submits;
|
|
84
84
|
private waitResponse;
|
|
85
85
|
private inputs;
|
|
86
86
|
private _onSubmitHandler?;
|
|
87
|
+
private _onSubmitClickHandler?;
|
|
87
88
|
private static get defaultParams();
|
|
88
89
|
private static set defaultParams(value);
|
|
89
90
|
static get defaultValidationSchema(): ValidationSchema;
|
|
@@ -104,6 +105,7 @@ export default class Form {
|
|
|
104
105
|
* свойства, которые необходимо обновить; остальные сохранятся без изменений.
|
|
105
106
|
*/
|
|
106
107
|
static setDefaultParams(params: Partial<FormOptions>): void;
|
|
108
|
+
private findSubmitElements;
|
|
107
109
|
private initialization;
|
|
108
110
|
/**
|
|
109
111
|
* Показывает ошибку для поля ввода.
|
|
@@ -170,4 +172,6 @@ export default class Form {
|
|
|
170
172
|
* @returns {boolean} Корректны данные или нет (true/false).
|
|
171
173
|
*/
|
|
172
174
|
private isCorrectArguments;
|
|
175
|
+
private sendData;
|
|
176
|
+
private submit;
|
|
173
177
|
}
|