form-father 0.0.22 → 0.1.1
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 +32 -5
- package/index.js +129 -147
- package/index.js.map +1 -1
- package/package.json +1 -1
- package/types/index.d.ts +21 -5
- package/types/validators.d.ts +11 -0
package/FormFather.min.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
var FormFather=function(){"use strict";function t(t,e,r,o){return new(r||(r=Promise))((function(n,s){function i(t){try{a(o.next(t))}catch(t){s(t)}}function l(t){try{a(o.throw(t))}catch(t){s(t)}}function a(t){var e;t.done?n(t.value):(e=t.value,e instanceof r?e:new r((function(t){t(e)}))).then(i,l)}a((o=o.apply(t,e||[])).next())}))}function e(t){const e="FORM"===t.tagName?Array.from(t.elements):Array.from(t.querySelectorAll("input, select, textarea, button")),r=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++)r.append(`${e}[${t}]`,o[t])}else{const o=t.files;o&&o[0]&&r.append(e,o[0])}else{let n;if("select-one"===o||"select-multiple"===o){const s=t;if("select-multiple"===o){Array.from(s.selectedOptions).map((t=>t.value)).forEach(((t,o)=>{r.append(`${e}[${o}]`,t)}))}else n=s.value,n&&r.append(e,n)}else if("tel"===o)n=t.value.split(/[\s()-]/).join("").replace(/^8/,"+7");else if("checkbox"===o||"radio"===o){if(!t.checked)return;n=t.value}else if("date"===o){const e=t.getAttribute("data-date-format");n=e?function(t,e){const r=new Date(t),o=t=>t.toString().padStart(2,"0"),n=o(r.getDate()),s=o(r.getMonth()+1),i=r.getFullYear().toString();return e.replace("dd",n).replace("mm",s).replace("yyyy",i)}(t.value,e):t.value}else n=t.value;n&&"select-one"!==o&&"select-multiple"!==o&&r.append(e,n)}})),r}"function"==typeof SuppressedError&&SuppressedError;class r{constructor(t,e){this.$submit=null,this.waitResponse=!1,this.inputs=null,this.$licensesCheckbox=null,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"},r.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),this.$licensesCheckbox=this.$el.querySelector('input[data-input-name="user-consent"]'),this.$licensesCheckbox&&(this.$licensesCheckbox.checked?this.enableSubmit():this.disableSubmit(),this.$licensesCheckbox.addEventListener("change",(()=>{this.$licensesCheckbox.checked?this.enableSubmit():this.disableSubmit()})));const r=()=>t(this,void 0,void 0,(function*(){var t;const r=this.$el.getAttribute("action")||"",o=(null===(t=this.$el.getAttribute("method"))||void 0===t?void 0:t.toUpperCase())||"POST",n=this.$el.getAttribute("enctype")||"application/x-www-form-urlencoded";let s=null,i={};const l=e(this.$el);let a={};if(l.forEach(((t,e)=>{a[e]=t})),this.config.logging)for(const t of l.keys())console.log(l.get(t));switch("function"==typeof this.config.wrapData&&(a=this.config.wrapData(a)),n){case"application/x-www-form-urlencoded":s=new URLSearchParams(e(this.$el)).toString(),i["Content-Type"]="application/x-www-form-urlencoded";break;case"multipart/form-data":s=e(this.$el);break;case"text/plain":s=Array.from(e(this.$el)).map((([t,e])=>`${encodeURIComponent(t)}=${encodeURIComponent(e)}`)).join("\n"),i["Content-Type"]="text/plain";break;case"application/json":s=JSON.stringify(a),i["Content-Type"]="application/json";break;default:console.warn(`Неизвестный enctype: ${n}. Используется application/x-www-form-urlencoded.`),s=new URLSearchParams(e(this.$el)).toString(),i["Content-Type"]="application/x-www-form-urlencoded"}return fetch(r,{method:o,body:s,headers:i})})),o=()=>t(this,void 0,void 0,(function*(){var t,e,o,n,s,i,l,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 c=yield r(),u=yield c.json();if(this.waitResponse=!1,!0===this.config.showLoaderButton&&this.hideLoader(),null===(n=(o=this.config).onResponse)||void 0===n||n.call(o,u,this),200===c.status)if(function(t){if(Object.prototype.hasOwnProperty.call(t,"redirect-url")&&(window.location.href=t["redirect-url"]),Object.prototype.hasOwnProperty.call(t,"reload")&&!0===t.reload&&window.location.reload(),Object.prototype.hasOwnProperty.call(t,"error-toast")){const e=document.querySelector('[data-toast-name="formErrorToast"]');if(e){const r=e.querySelector(".toast__text");r&&(r.textContent=t["error-toast"],window.Toast.showToast("formErrorToast",{removeAfterClose:!1}))}}}(u),!0===u.success)null===(i=(s=this.config).onResponseSuccess)||void 0===i||i.call(s,u,this);else if(null===(a=(l=this.config).onResponseUnsuccess)||void 0===a||a.call(l,u,this),!0===u.error&&(u["error-msg"]&&this.showErrorForm(u["error-msg"]),u.errors)){const t=[];u.errors.forEach((e=>{const r=this.$el.querySelector(`input[name="${e.name}"]`);r?(t.push(r),this.showError(r,e["error-msg"])):console.warn(`Не найдено поле с именем: ${e.name}, для вывода ошибки: ${e["error-msg"]}`)})),!0===this.config.scrollToFirstErroredInput&&this.scrollToFirstErroredInput(t)}}}));this.$el.addEventListener("submit",(t=>{t.preventDefault(),this.checkInputs()&&(!this.$licensesCheckbox||this.$licensesCheckbox&&this.$licensesCheckbox.checked)&&o()}))}showError(t,e){(function(t,e){if(null==t)return null;let r=t;for(;r;){if(r.matches(e))return r;r=r.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`,r=document.createElement("span");r.innerHTML=e,r.className="button__loader",null===(t=this.$submit)||void 0===t||t.insertAdjacentElement("afterbegin",r),requestAnimationFrame((()=>{r.setAttribute("data-showed","")}))}hideLoader(){var t,e;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.classList.remove("active"),r.removeAttribute("data-showed"),setTimeout((()=>{r.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 r=null,o=1/0;if(t.forEach((t=>{const n=e.findIndex((e=>e===t));n<o&&(o=n,r=t)})),r){const t=r.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)}}}checkInputs(){var t;let e=!0;const r={},o=[];if(null===(t=this.inputs)||void 0===t||t.forEach((t=>{var n;const s=t,i=s.getAttribute("data-check-type")||s.getAttribute("type")||"";if("SELECT"===s.tagName){const t=s.selectedOptions[0];t&&""===t.value&&s.hasAttribute("required")&&(this.showError(s,"Пустое значение"),o.push(s),e=!1)}else if("checkbox"===i)s.hasAttribute("required")&&!s.checked&&(this.showError(s,"Необходимо согласие"),o.push(s),e=!1);else if((null===(n=this.config.customTypeError)||void 0===n?void 0:n.name)!==i||"TEXTAREA"===s.tagName&&!this.config.customTypeError)if(s.value.length>0){if("email"===i&&(l=s.value,/^(([^<>()[\].,;:\s@"]+(\.[^<>()[\].,;:\s@"]+)*)|(".+"))@(([^<>()[\].,;:\s@"]+\.)+[^<>()[\].,;:\s@"]{2,})$/iu.test(l)||(this.showError(s,"Неверный формат"),o.push(s),e=!1)),"tel"===i){const t=this.config.validatePhone||(t=>function(t){const e=[];return Array.from(t).forEach((t=>{(Number(t)||0===Number(t))&&e.push(t)})),!(e.length<11||e.length<12&&"+"===t[1])}(t.value));t(s)||(this.showError(s,"Неверный формат"),o.push(s),e=!1)}if("url"===i&&(function(t){const e=/^https?:\/\//i.test(t)?t:`http://${t}`;try{return new URL(e),!0}catch(t){return!1}}(s.value)||(this.showError(s,"Неверный формат"),o.push(s),e=!1)),"text"===i&&"not-numbers"===s.getAttribute("data-check-type")&&/[0-9]/.test(s.value)&&(this.showError(s,"Неверный формат"),o.push(s),e=!1),"radio"===i){const t=s.getAttribute("name")||"";r[t]||(r[t]={}),r[t][Object.keys(r[t]).length]=s}}else s.hasAttribute("required")&&(this.showError(s,"Пустое значение"),o.push(s),e=!1);var l})),Object.keys(r).length>0)for(const t in r){const e=r[t];let o=!1,n=null;for(const t in e){const r=e[t];if(r.hasAttribute("required")&&(o=!0),r.checked){if(null!==n)return!1;n=r}}if(null===n&&o)return this.showErrorForm("Необходимо указать оценку"),!1}return!0===this.config.scrollToFirstErroredInput&&this.scrollToFirstErroredInput(o),e}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 r=t;"radio"===r.type||"checkbox"===r.type?r.checked=!1:(r.value="",null===(e=r.parentElement)||void 0===e||e.classList.remove("filled"),r.dispatchEvent(new Event("input")))}))}showErrorForm(t){var e;const r=this.$el.querySelectorAll(".input__wrapper"),o=r[r.length-1];let n=this.$el.querySelector(".error-block-under-input__wrapper");n&&o.nextElementSibling===n?(n.classList.add("error-block-under-input--warning"),n.querySelector(".error-block-under-input__icon").innerHTML="\x3c!-- SVG content --\x3e",n.querySelector(".error-block-under-input__main-text").textContent=t):(n=document.createElement("div"),n.className="error-block-under-input__wrapper",n.innerHTML=`\n <div class="error-block-under-input error-block-under-input--warning">\n <div class="error-block-under-input__icon-wrapper">\n <span class="error-block-under-input__icon">\n \x3c!-- SVG content --\x3e\n </span>\n <span class="error-block-under-input__icon error-block-under-input__icon--animated" style="display:none;"></span>\n </div>\n <p class="error-block-under-input__text">\n <span class="error-block-under-input__main-text">${t}</span>\n <span class="error-block-under-input__secondary-text"></span>\n </p>\n </div>`,o.insertAdjacentElement("afterend",n)),this.$el.addEventListener("input",(()=>{this.hideErrorForm()}),{passive:!0,once:!0}),null===(e=n.querySelector(".error-block-under-input"))||void 0===e||e.classList.remove("error-block-under-input--success");const s=n.querySelector(".error-block-under-input__secondary-text");s&&(s.textContent="");const i=parseFloat(n.style.height)||0,l=parseFloat(window.getComputedStyle(n).paddingTop)||0;i!==n.querySelector(".error-block-under-input").scrollHeight+16&&(n.style.height=`${i}px`,n.style.opacity="0",n.style.paddingTop=`${l}px`)}hideErrorForm(){const t=this.$el.querySelectorAll(".input__wrapper"),e=t[t.length-1].nextElementSibling;e&&e.classList.contains("error-block-under-input__wrapper")}isCorrectArguments(){return"FORM"!==this.$el.tagName?(console.warn("$el не является тегом form"),!1):!!this.$submit||(console.warn("В форме нет кнопки с типом submit"),!1)}}return r.defaultParams={},r}();
|
|
1
|
+
var FormFather=function(e){"use strict";function t(e,t,r,o){return new(r||(r=Promise))((function(n,s){function i(e){try{a(o.next(e))}catch(e){s(e)}}function l(e){try{a(o.throw(e))}catch(e){s(e)}}function a(e){var t;e.done?n(e.value):(t=e.value,t instanceof r?t:new r((function(e){e(t)}))).then(i,l)}a((o=o.apply(e,t||[])).next())}))}function r(e){const t="FORM"===e.tagName?Array.from(e.elements):Array.from(e.querySelectorAll("input, select, textarea, button")),r=new FormData;return t.forEach((e=>{if(!(e instanceof HTMLInputElement||e instanceof HTMLSelectElement||e instanceof HTMLTextAreaElement||e instanceof HTMLButtonElement))return;if(!e.name||e.hasAttribute("data-no-serialize"))return;const{name:t,type:o}=e;if("file"===o)if("true"===e.getAttribute("multiple")){const o=e.files;if(o)for(let e=0;e<o.length;e++)r.append(`${t}[${e}]`,o[e])}else{const o=e.files;o&&o[0]&&r.append(t,o[0])}else{let n;if("select-one"===o||"select-multiple"===o){const s=e;if("select-multiple"===o){Array.from(s.selectedOptions).map((e=>e.value)).forEach(((e,o)=>{r.append(`${t}[${o}]`,e)}))}else n=s.value,n&&r.append(t,n)}else if("tel"===o)n=e.value.split(/[\s()-]/).join("").replace(/^8/,"+7");else if("checkbox"===o||"radio"===o){if(!e.checked)return;n=e.value}else if("date"===o){const t=e.getAttribute("data-date-format");n=t?function(e,t){const r=new Date(e),o=e=>e.toString().padStart(2,"0"),n=o(r.getDate()),s=o(r.getMonth()+1),i=r.getFullYear().toString();return t.replace("dd",n).replace("mm",s).replace("yyyy",i)}(e.value,t):e.value}else n=e.value;n&&"select-one"!==o&&"select-multiple"!==o&&r.append(t,n)}})),r}var o;"function"==typeof SuppressedError&&SuppressedError;const n=Symbol.for("FormFather.validators"),s=null!==(o=globalThis[n])&&void 0!==o?o:globalThis[n]=new Map;function i(e,t,r,{override:o=!1}={}){!s.has(e)||o?s.set(e,{fn:t,defaultMessage:r}):console.warn(`[FormFather] Validator "${e}" already exists; pass { override: true } to replace`)}const l=e=>s.get(e);i("required",(e=>e.trim().length>0),"Пустое значение"),i("email",(e=>/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(e)),"Неверный формат"),i("tel",(e=>/^\+7\d{10}$/.test(e)),"Неверный формат"),i("url",(e=>/^(https?:\/\/).+\..+/.test(e)),"Неверный формат"),i("not-numbers",(e=>!/[0-9]/.test(e)),"Неверный формат");class a{constructor(e,t){this.$submit=null,this.waitResponse=!1,this.inputs=null,this.$licensesCheckbox=null,this.$el=e,this.options=t;this.config=Object.assign({},{onSubmit:()=>{},onResponse:()=>{},onResponseSuccess:()=>{},onResponseUnsuccess:()=>{},customTypeError:null,loaderColor:"#fff",showLoaderButton:!0,scrollToFirstErroredInput:!0,logging:!1,inputSelector:".input",inputWrapperSelector:".input-primary"},a.defaultParams,t),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(e){this.defaultParams=Object.assign(Object.assign({},this.defaultParams),e)}initialization(){this.inputs=this.$el.querySelectorAll(this.config.inputSelector),this.$licensesCheckbox=this.$el.querySelector('input[data-input-name="user-consent"]'),this.$licensesCheckbox&&(this.$licensesCheckbox.checked?this.enableSubmit():this.disableSubmit(),this.$licensesCheckbox.addEventListener("change",(()=>{this.$licensesCheckbox.checked?this.enableSubmit():this.disableSubmit()})));const e=()=>t(this,void 0,void 0,(function*(){var e;const t=this.$el.getAttribute("action")||"",o=(null===(e=this.$el.getAttribute("method"))||void 0===e?void 0:e.toUpperCase())||"POST",n=this.$el.getAttribute("enctype")||"application/x-www-form-urlencoded";let s=null,i={};const l=r(this.$el);let a={};if(l.forEach(((e,t)=>{a[t]=e})),this.config.logging)for(const e of l.keys())console.log(l.get(e));switch("function"==typeof this.config.wrapData&&(a=this.config.wrapData(a)),n){case"application/x-www-form-urlencoded":s=new URLSearchParams(r(this.$el)).toString(),i["Content-Type"]="application/x-www-form-urlencoded";break;case"multipart/form-data":s=r(this.$el);break;case"text/plain":s=Array.from(r(this.$el)).map((([e,t])=>`${encodeURIComponent(e)}=${encodeURIComponent(t)}`)).join("\n"),i["Content-Type"]="text/plain";break;case"application/json":s=JSON.stringify(a),i["Content-Type"]="application/json";break;default:console.warn(`Неизвестный enctype: ${n}. Используется application/x-www-form-urlencoded.`),s=new URLSearchParams(r(this.$el)).toString(),i["Content-Type"]="application/x-www-form-urlencoded"}return fetch(t,{method:o,body:s,headers:i})})),o=()=>t(this,void 0,void 0,(function*(){var t,r,o,n,s,i,l,a;if(!this.waitResponse){!0===this.config.showLoaderButton&&this.showLoader(),null===(r=(t=this.config).onSubmit)||void 0===r||r.call(t,this),this.waitResponse=!0;const c=yield e(),u=yield c.json();if(this.waitResponse=!1,!0===this.config.showLoaderButton&&this.hideLoader(),null===(n=(o=this.config).onResponse)||void 0===n||n.call(o,u,this),200===c.status)if(function(e){if(Object.prototype.hasOwnProperty.call(e,"redirect-url")&&(window.location.href=e["redirect-url"]),Object.prototype.hasOwnProperty.call(e,"reload")&&!0===e.reload&&window.location.reload(),Object.prototype.hasOwnProperty.call(e,"error-toast")){const t=document.querySelector('[data-toast-name="formErrorToast"]');if(t){const r=t.querySelector(".toast__text");r&&(r.textContent=e["error-toast"],window.Toast.showToast("formErrorToast",{removeAfterClose:!1}))}}}(u),!0===u.success)null===(i=(s=this.config).onResponseSuccess)||void 0===i||i.call(s,u,this);else if(null===(a=(l=this.config).onResponseUnsuccess)||void 0===a||a.call(l,u,this),!0===u.error&&(u["error-msg"]&&this.showErrorForm(u["error-msg"]),u.errors)){const e=[];u.errors.forEach((t=>{const r=this.$el.querySelector(`input[name="${t.name}"]`);r?(e.push(r),this.showError(r,t["error-msg"])):console.warn(`Не найдено поле с именем: ${t.name}, для вывода ошибки: ${t["error-msg"]}`)})),!0===this.config.scrollToFirstErroredInput&&this.scrollToFirstErroredInput(e)}}}));this.$el.addEventListener("submit",(e=>t(this,void 0,void 0,(function*(){e.preventDefault();(yield this.validate())&&(!this.$licensesCheckbox||this.$licensesCheckbox&&this.$licensesCheckbox.checked)&&o()}))))}showError(e,t){(function(e,t){if(null==e)return null;let r=e;for(;r;){if(r.matches(t))return r;r=r.parentElement}return null})(e,this.config.inputWrapperSelector).showError(t)}showLoader(){var e;const t=`\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`,r=document.createElement("span");r.innerHTML=t,r.className="button__loader",null===(e=this.$submit)||void 0===e||e.insertAdjacentElement("afterbegin",r),requestAnimationFrame((()=>{r.setAttribute("data-showed","")}))}hideLoader(){var e,t;null===(e=this.$submit)||void 0===e||e.classList.remove("button--loading");const r=null===(t=this.$submit)||void 0===t?void 0:t.querySelector(".button__loader");r&&(r.classList.remove("active"),r.removeAttribute("data-showed"),setTimeout((()=>{r.remove()}),250))}hideError(e){const t=e.parentElement;t&&t.classList.contains("input__wrapper")&&(t.getAttribute("data-type-error"),t.classList.remove("input__wrapper--error"))}scrollToFirstErroredInput(e){if(e.length>0){const t=Array.from(this.$el.querySelectorAll("input"));let r=null,o=1/0;if(e.forEach((e=>{const n=t.findIndex((t=>t===e));n<o&&(o=n,r=e)})),r){const e=r.getBoundingClientRect(),t=window.pageYOffset||document.documentElement.scrollTop,o=e.top+t-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 t(this,void 0,void 0,(function*(){const e=Object.assign(Object.assign({},a.defaultValidationSchema||{}),this.config.validationSchema||{}),r=e=>console.warn(`[FormFather] Unknown validation rule "${e}"`),o=[],n=new WeakSet;let s=!0;const i=(e,t=[])=>{var o;const n=[];e.hasAttribute("required")&&n.push("required"),n.push(...t);const s=(null!==(o=e.getAttribute("data-custom-validate"))&&void 0!==o?o:"").split(",").map((e=>e.trim())).filter(Boolean);n.push(...s);const i=[];for(const e of n){const t="string"==typeof e?e:e.rule;l(t)?i.push(e):r(t)}return i},c=(e,r,...n)=>t(this,[e,r,...n],void 0,(function*(e,t,r={}){var n;if(!("checkbox"===e.type||"radio"===e.type?!e.checked:0===e.value.trim().length)||t.includes("required")){for(const i of t){const{rule:t,params:a}="string"==typeof i?{rule:i,params:void 0}:i,c=l(t);if(!(yield c.fn(e.value,e,this.$el,a))){s=!1;const i=null!==(n=r[t])&&void 0!==n?n:c.defaultMessage;return this.showError(e,i),void o.push(e)}}this.hideError(e)}else this.hideError(e)}));for(const[t,r]of Object.entries(e)){const{selector:e,rules:o=[],messages:s={}}=r,l=e?this.$el.querySelectorAll(e):this.$el.querySelectorAll(`[data-validate="${t}"]`);for(const e of Array.from(l))n.has(e)||(n.add(e),yield c(e,i(e,o),s))}const u=Array.from(this.$el.querySelectorAll("[data-custom-validate]")).filter((e=>!n.has(e)));for(const e of u)n.add(e),yield c(e,i(e,[]));return!s&&this.config.scrollToFirstErroredInput&&this.scrollToFirstErroredInput(o),s}))}disableSubmit(){var e;null===(e=this.$submit)||void 0===e||e.setAttribute("disabled","")}enableSubmit(){var e;null===(e=this.$submit)||void 0===e||e.removeAttribute("disabled")}clearInputs(){var e;null===(e=this.inputs)||void 0===e||e.forEach((e=>{var t;const r=e;"radio"===r.type||"checkbox"===r.type?r.checked=!1:(r.value="",null===(t=r.parentElement)||void 0===t||t.classList.remove("filled"),r.dispatchEvent(new Event("input")))}))}showErrorForm(e){var t;const r=this.$el.querySelectorAll(".input__wrapper"),o=r[r.length-1];let n=this.$el.querySelector(".error-block-under-input__wrapper");n&&o.nextElementSibling===n?(n.classList.add("error-block-under-input--warning"),n.querySelector(".error-block-under-input__icon").innerHTML="\x3c!-- SVG content --\x3e",n.querySelector(".error-block-under-input__main-text").textContent=e):(n=document.createElement("div"),n.className="error-block-under-input__wrapper",n.innerHTML=`\n <div class="error-block-under-input error-block-under-input--warning">\n <div class="error-block-under-input__icon-wrapper">\n <span class="error-block-under-input__icon">\n \x3c!-- SVG content --\x3e\n </span>\n <span class="error-block-under-input__icon error-block-under-input__icon--animated" style="display:none;"></span>\n </div>\n <p class="error-block-under-input__text">\n <span class="error-block-under-input__main-text">${e}</span>\n <span class="error-block-under-input__secondary-text"></span>\n </p>\n </div>`,o.insertAdjacentElement("afterend",n)),this.$el.addEventListener("input",(()=>{this.hideErrorForm()}),{passive:!0,once:!0}),null===(t=n.querySelector(".error-block-under-input"))||void 0===t||t.classList.remove("error-block-under-input--success");const s=n.querySelector(".error-block-under-input__secondary-text");s&&(s.textContent="");const i=parseFloat(n.style.height)||0,l=parseFloat(window.getComputedStyle(n).paddingTop)||0;i!==n.querySelector(".error-block-under-input").scrollHeight+16&&(n.style.height=`${i}px`,n.style.opacity="0",n.style.paddingTop=`${l}px`)}hideErrorForm(){const e=this.$el.querySelectorAll(".input__wrapper"),t=e[e.length-1].nextElementSibling;t&&t.classList.contains("error-block-under-input__wrapper")}isCorrectArguments(){return"FORM"!==this.$el.tagName?(console.warn("$el не является тегом form"),!1):!!this.$submit||(console.warn("В форме нет кнопки с типом submit"),!1)}}return a.defaultParams={},a.defaultValidationSchema={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"]}},e.default=a,e.getAllValidators=function(){return new Map(s)},e.getValidator=l,e.registerValidator=i,Object.defineProperty(e,"__esModule",{value:!0}),e}({});
|
|
2
2
|
//# sourceMappingURL=FormFather.min.js.map
|
package/index.d.ts
CHANGED
|
@@ -1,3 +1,22 @@
|
|
|
1
|
+
type ValidatorFn = (value: string, $input: HTMLInputElement, $form: HTMLElement, params?: any) => boolean | Promise<boolean>;
|
|
2
|
+
interface Validator {
|
|
3
|
+
fn: ValidatorFn;
|
|
4
|
+
defaultMessage: string;
|
|
5
|
+
}
|
|
6
|
+
declare function registerValidator(name: string, fn: ValidatorFn, defaultMessage: string, { override }?: {
|
|
7
|
+
override?: boolean;
|
|
8
|
+
}): void;
|
|
9
|
+
declare const getValidator: (name: string) => Validator | undefined;
|
|
10
|
+
declare function getAllValidators(): Map<string, Validator>;
|
|
11
|
+
|
|
12
|
+
type ValidationSchema = Record<string, {
|
|
13
|
+
rules: (string | {
|
|
14
|
+
rule: string;
|
|
15
|
+
params?: any;
|
|
16
|
+
})[];
|
|
17
|
+
selector: string;
|
|
18
|
+
messages?: Record<string, string>;
|
|
19
|
+
}>;
|
|
1
20
|
/** Параметры формы. */
|
|
2
21
|
interface FormOptions {
|
|
3
22
|
/**
|
|
@@ -51,6 +70,8 @@ interface FormOptions {
|
|
|
51
70
|
validatePhone?: (input: HTMLInputElement) => boolean;
|
|
52
71
|
/** Функция для обёртки отправляемых данных. */
|
|
53
72
|
wrapData?: (data: Record<string, any>) => Record<string, any>;
|
|
73
|
+
/** Схема валидации: поле → массив правил + override-сообщения */
|
|
74
|
+
validationSchema?: ValidationSchema;
|
|
54
75
|
}
|
|
55
76
|
/**
|
|
56
77
|
* Реализует форму отправки данных.
|
|
@@ -66,6 +87,7 @@ declare class Form {
|
|
|
66
87
|
private inputs;
|
|
67
88
|
private $licensesCheckbox;
|
|
68
89
|
private static defaultParams;
|
|
90
|
+
static defaultValidationSchema: ValidationSchema;
|
|
69
91
|
/**
|
|
70
92
|
* Создать форму.
|
|
71
93
|
*
|
|
@@ -107,13 +129,18 @@ declare class Form {
|
|
|
107
129
|
*/
|
|
108
130
|
private scrollToFirstErroredInput;
|
|
109
131
|
/**
|
|
110
|
-
* Проверяет все поля
|
|
132
|
+
* Проверяет все поля ввода.
|
|
133
|
+
*
|
|
134
|
+
* Порядок для КАЖДОГО `<input>`:
|
|
111
135
|
*
|
|
112
|
-
*
|
|
136
|
+
* 1. `required` — если атрибут `required`
|
|
137
|
+
* 2. правила из `validationSchema`
|
|
138
|
+
* 3. правила из `data-custom-validate`
|
|
113
139
|
*
|
|
114
|
-
*
|
|
140
|
+
* Для одного поля показывается только первая ошибка. Неизвестные правила фиксируются `console.warn` и исключаются ДО
|
|
141
|
+
* валидации, поэтому валидационный цикл больше не проверяет их наличие.
|
|
115
142
|
*/
|
|
116
|
-
|
|
143
|
+
validate(): Promise<boolean>;
|
|
117
144
|
/** Блокирует кнопку отправки данных. */
|
|
118
145
|
private disableSubmit;
|
|
119
146
|
/** Снимает блокировку с кнопки отправки данных. */
|
|
@@ -136,4 +163,4 @@ declare class Form {
|
|
|
136
163
|
private isCorrectArguments;
|
|
137
164
|
}
|
|
138
165
|
|
|
139
|
-
export { Form as default };
|
|
166
|
+
export { Form as default, getAllValidators, getValidator, registerValidator };
|
package/index.js
CHANGED
|
@@ -73,50 +73,6 @@ function closest($el, selector) {
|
|
|
73
73
|
}
|
|
74
74
|
return null;
|
|
75
75
|
}
|
|
76
|
-
/**
|
|
77
|
-
* Проверяет является ли строка валидным адресом электронной почты.
|
|
78
|
-
*
|
|
79
|
-
* @param {string} value - Строка, которую нужно проверить.
|
|
80
|
-
*
|
|
81
|
-
* @returns {boolean} Является ли строка валидным адресом электронной почты true/false.
|
|
82
|
-
*/
|
|
83
|
-
function isEmailValid(value) {
|
|
84
|
-
return /^(([^<>()[\].,;:\s@"]+(\.[^<>()[\].,;:\s@"]+)*)|(".+"))@(([^<>()[\].,;:\s@"]+\.)+[^<>()[\].,;:\s@"]{2,})$/iu.test(value);
|
|
85
|
-
}
|
|
86
|
-
/**
|
|
87
|
-
* Проверяет является ли строка валидным url.
|
|
88
|
-
*
|
|
89
|
-
* @param {string} originalUrlString - Строка, которую нужно проверить.
|
|
90
|
-
*
|
|
91
|
-
* @returns {boolean} Является ли строка валидным url адресом true/false.
|
|
92
|
-
*/
|
|
93
|
-
function isUrlValid(originalUrlString) {
|
|
94
|
-
const urlString = /^https?:\/\//i.test(originalUrlString) ? originalUrlString : `http://${originalUrlString}`;
|
|
95
|
-
try {
|
|
96
|
-
new URL(urlString);
|
|
97
|
-
return true;
|
|
98
|
-
}
|
|
99
|
-
catch (e) {
|
|
100
|
-
return false;
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
/**
|
|
104
|
-
* Проверяет является ли строка валидным номером телефона.
|
|
105
|
-
*
|
|
106
|
-
* @param {string} value - Строка, которую нужно проверить.
|
|
107
|
-
*
|
|
108
|
-
* @returns {boolean} Является ли строка валидным номером телефона true/false.
|
|
109
|
-
*/
|
|
110
|
-
function isPhoneValid(value) {
|
|
111
|
-
const numbersArray = [];
|
|
112
|
-
Array.from(value).forEach(char => {
|
|
113
|
-
if (!!Number(char) || Number(char) === 0)
|
|
114
|
-
numbersArray.push(char);
|
|
115
|
-
});
|
|
116
|
-
if (numbersArray.length < 11 || (numbersArray.length < 12 && value[1] === '+'))
|
|
117
|
-
return false;
|
|
118
|
-
return true;
|
|
119
|
-
}
|
|
120
76
|
/**
|
|
121
77
|
* Кастомная сериализация формы в объект FormData.
|
|
122
78
|
*
|
|
@@ -225,6 +181,29 @@ function parseCommonResponseProperties(responseBody) {
|
|
|
225
181
|
}
|
|
226
182
|
}
|
|
227
183
|
|
|
184
|
+
var _a;
|
|
185
|
+
/* ---------- singleton-storage на globalThis ---------- */
|
|
186
|
+
const GLOBAL_KEY = Symbol.for('FormFather.validators');
|
|
187
|
+
const validators = (_a = globalThis[GLOBAL_KEY]) !== null && _a !== void 0 ? _a : (globalThis[GLOBAL_KEY] = new Map());
|
|
188
|
+
/* ---------- API ---------- */
|
|
189
|
+
function registerValidator(name, fn, defaultMessage, { override = false } = {}) {
|
|
190
|
+
if (validators.has(name) && !override) {
|
|
191
|
+
console.warn(`[FormFather] Validator "${name}" already exists; pass { override: true } to replace`);
|
|
192
|
+
return;
|
|
193
|
+
}
|
|
194
|
+
validators.set(name, { fn, defaultMessage });
|
|
195
|
+
}
|
|
196
|
+
const getValidator = (name) => validators.get(name);
|
|
197
|
+
function getAllValidators() {
|
|
198
|
+
return new Map(validators); // копию, чтобы никто не мутировал оригинал
|
|
199
|
+
}
|
|
200
|
+
/* ---------- built-in rules ---------- */
|
|
201
|
+
registerValidator('required', v => v.trim().length > 0, 'Пустое значение');
|
|
202
|
+
registerValidator('email', v => /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(v), 'Неверный формат');
|
|
203
|
+
registerValidator('tel', v => /^\+7\d{10}$/.test(v), 'Неверный формат');
|
|
204
|
+
registerValidator('url', v => /^(https?:\/\/).+\..+/.test(v), 'Неверный формат');
|
|
205
|
+
registerValidator('not-numbers', v => !/[0-9]/.test(v), 'Неверный формат');
|
|
206
|
+
|
|
228
207
|
/**
|
|
229
208
|
* Реализует форму отправки данных.
|
|
230
209
|
*
|
|
@@ -391,11 +370,12 @@ class Form {
|
|
|
391
370
|
}
|
|
392
371
|
}
|
|
393
372
|
});
|
|
394
|
-
this.$el.addEventListener('submit', e => {
|
|
373
|
+
this.$el.addEventListener('submit', (e) => __awaiter(this, void 0, void 0, function* () {
|
|
395
374
|
e.preventDefault();
|
|
396
|
-
|
|
375
|
+
const isValid = yield this.validate();
|
|
376
|
+
if (isValid && (!this.$licensesCheckbox || (this.$licensesCheckbox && this.$licensesCheckbox.checked)))
|
|
397
377
|
submit();
|
|
398
|
-
});
|
|
378
|
+
}));
|
|
399
379
|
}
|
|
400
380
|
/**
|
|
401
381
|
* Показывает ошибку для поля ввода.
|
|
@@ -494,115 +474,95 @@ class Form {
|
|
|
494
474
|
}
|
|
495
475
|
}
|
|
496
476
|
/**
|
|
497
|
-
* Проверяет все поля
|
|
477
|
+
* Проверяет все поля ввода.
|
|
498
478
|
*
|
|
499
|
-
*
|
|
479
|
+
* Порядок для КАЖДОГО `<input>`:
|
|
500
480
|
*
|
|
501
|
-
*
|
|
481
|
+
* 1. `required` — если атрибут `required`
|
|
482
|
+
* 2. правила из `validationSchema`
|
|
483
|
+
* 3. правила из `data-custom-validate`
|
|
484
|
+
*
|
|
485
|
+
* Для одного поля показывается только первая ошибка. Неизвестные правила фиксируются `console.warn` и исключаются ДО
|
|
486
|
+
* валидации, поэтому валидационный цикл больше не проверяет их наличие.
|
|
502
487
|
*/
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
488
|
+
validate() {
|
|
489
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
490
|
+
/* ---------- финальная схема (defaults + config) ---------- */
|
|
491
|
+
const schema = Object.assign(Object.assign({}, (Form.defaultValidationSchema || {})), (this.config.validationSchema || {}));
|
|
492
|
+
const warnUnknown = (r) => console.warn(`[FormFather] Unknown validation rule "${r}"`);
|
|
493
|
+
const erroredInputs = [];
|
|
494
|
+
const processed = new WeakSet();
|
|
495
|
+
let ok = true;
|
|
496
|
+
/* ---------- helpers ---------- */
|
|
497
|
+
/** Собирает и тут же фильтрует unknown-rules */
|
|
498
|
+
const collectRules = ($input, fromSchema = []) => {
|
|
499
|
+
var _a;
|
|
500
|
+
const list = [];
|
|
501
|
+
if ($input.hasAttribute('required'))
|
|
502
|
+
list.push('required'); // 1
|
|
503
|
+
list.push(...fromSchema); // 2
|
|
504
|
+
const custom = ((_a = $input.getAttribute('data-custom-validate')) !== null && _a !== void 0 ? _a : '')
|
|
505
|
+
.split(',')
|
|
506
|
+
.map(s => s.trim())
|
|
507
|
+
.filter(Boolean);
|
|
508
|
+
list.push(...custom); // 3
|
|
509
|
+
/* —–– предварительная фильтрация unknown ––– */
|
|
510
|
+
const filtered = [];
|
|
511
|
+
for (const r of list) {
|
|
512
|
+
const ruleName = typeof r === 'string' ? r : r.rule;
|
|
513
|
+
if (getValidator(ruleName))
|
|
514
|
+
filtered.push(r);
|
|
515
|
+
else
|
|
516
|
+
warnUnknown(ruleName);
|
|
518
517
|
}
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
518
|
+
return filtered;
|
|
519
|
+
};
|
|
520
|
+
const validateInput = ($input_1, rules_1, ...args_1) => __awaiter(this, [$input_1, rules_1, ...args_1], void 0, function* ($input, rules, msgs = {}) {
|
|
521
|
+
var _a;
|
|
522
|
+
const empty = $input.type === 'checkbox' || $input.type === 'radio' ? !$input.checked : $input.value.trim().length === 0;
|
|
523
|
+
if (empty && !rules.includes('required')) {
|
|
524
|
+
this.hideError($input);
|
|
525
|
+
return;
|
|
525
526
|
}
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
if (
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
}
|
|
537
|
-
if (inputType === 'tel') {
|
|
538
|
-
const isPhoneValidChecker = this.config.validatePhone ||
|
|
539
|
-
(($input) => {
|
|
540
|
-
return isPhoneValid($input.value);
|
|
541
|
-
});
|
|
542
|
-
if (!isPhoneValidChecker($input)) {
|
|
543
|
-
this.showError($input, 'Неверный формат');
|
|
544
|
-
inputsList.push($input);
|
|
545
|
-
isCorrect = false;
|
|
546
|
-
}
|
|
547
|
-
}
|
|
548
|
-
if (inputType === 'url') {
|
|
549
|
-
if (!isUrlValid($input.value)) {
|
|
550
|
-
this.showError($input, 'Неверный формат');
|
|
551
|
-
inputsList.push($input);
|
|
552
|
-
isCorrect = false;
|
|
553
|
-
}
|
|
554
|
-
}
|
|
555
|
-
if (inputType === 'text') {
|
|
556
|
-
if ($input.getAttribute('data-check-type') === 'not-numbers') {
|
|
557
|
-
if (/[0-9]/.test($input.value)) {
|
|
558
|
-
this.showError($input, 'Неверный формат');
|
|
559
|
-
inputsList.push($input);
|
|
560
|
-
isCorrect = false;
|
|
561
|
-
}
|
|
562
|
-
}
|
|
563
|
-
}
|
|
564
|
-
if (inputType === 'radio') {
|
|
565
|
-
const name = $input.getAttribute('name') || '';
|
|
566
|
-
if (!radioGroups[name]) {
|
|
567
|
-
radioGroups[name] = {};
|
|
568
|
-
}
|
|
569
|
-
radioGroups[name][Object.keys(radioGroups[name]).length] = $input;
|
|
527
|
+
for (const r of rules) {
|
|
528
|
+
const { rule, params } = typeof r === 'string' ? { rule: r, params: undefined } : r;
|
|
529
|
+
const vd = getValidator(rule); // гарантированно существует
|
|
530
|
+
const passed = yield vd.fn($input.value, $input, this.$el, params);
|
|
531
|
+
if (!passed) {
|
|
532
|
+
ok = false;
|
|
533
|
+
const msg = (_a = msgs[rule]) !== null && _a !== void 0 ? _a : vd.defaultMessage;
|
|
534
|
+
this.showError($input, msg);
|
|
535
|
+
erroredInputs.push($input);
|
|
536
|
+
return; // первую ошибку отобразили
|
|
570
537
|
}
|
|
571
538
|
}
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
539
|
+
this.hideError($input);
|
|
540
|
+
});
|
|
541
|
+
/* ---------- 1. inputs из schema (config + defaults) ---------- */
|
|
542
|
+
for (const [key, def] of Object.entries(schema)) {
|
|
543
|
+
const { selector, rules = [], messages = {} } = def;
|
|
544
|
+
const nodeList = selector
|
|
545
|
+
? this.$el.querySelectorAll(selector) // явный CSS-селектор
|
|
546
|
+
: this.$el.querySelectorAll(`[data-validate="${key}"]`); // fallback по name
|
|
547
|
+
for (const $input of Array.from(nodeList)) {
|
|
548
|
+
if (processed.has($input))
|
|
549
|
+
continue;
|
|
550
|
+
processed.add($input);
|
|
551
|
+
yield validateInput($input, collectRules($input, rules), messages);
|
|
576
552
|
}
|
|
577
553
|
}
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
for (const
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
let $checkedInput = null;
|
|
584
|
-
for (const keyGroup in group) {
|
|
585
|
-
const $radio = group[keyGroup];
|
|
586
|
-
if ($radio.hasAttribute('required'))
|
|
587
|
-
isRequired = true;
|
|
588
|
-
if ($radio.checked) {
|
|
589
|
-
if ($checkedInput === null) {
|
|
590
|
-
$checkedInput = $radio;
|
|
591
|
-
}
|
|
592
|
-
else {
|
|
593
|
-
return false;
|
|
594
|
-
}
|
|
595
|
-
}
|
|
596
|
-
}
|
|
597
|
-
if ($checkedInput === null && isRequired) {
|
|
598
|
-
this.showErrorForm('Необходимо указать оценку');
|
|
599
|
-
return false;
|
|
600
|
-
}
|
|
554
|
+
/* ---------- 2. inputs только с data-custom ---------- */
|
|
555
|
+
const rest = Array.from(this.$el.querySelectorAll('[data-custom-validate]')).filter($i => !processed.has($i));
|
|
556
|
+
for (const $input of rest) {
|
|
557
|
+
processed.add($input);
|
|
558
|
+
yield validateInput($input, collectRules($input, []));
|
|
601
559
|
}
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
560
|
+
/* ---------- scroll ---------- */
|
|
561
|
+
if (!ok && this.config.scrollToFirstErroredInput) {
|
|
562
|
+
this.scrollToFirstErroredInput(erroredInputs);
|
|
563
|
+
}
|
|
564
|
+
return ok;
|
|
565
|
+
});
|
|
606
566
|
}
|
|
607
567
|
/** Блокирует кнопку отправки данных. */
|
|
608
568
|
disableSubmit() {
|
|
@@ -704,6 +664,28 @@ class Form {
|
|
|
704
664
|
}
|
|
705
665
|
}
|
|
706
666
|
Form.defaultParams = {};
|
|
667
|
+
Form.defaultValidationSchema = {
|
|
668
|
+
tel: {
|
|
669
|
+
selector: 'input[type="tel"]',
|
|
670
|
+
rules: ['tel'],
|
|
671
|
+
},
|
|
672
|
+
email: {
|
|
673
|
+
selector: 'input[type="email"]',
|
|
674
|
+
rules: ['email'],
|
|
675
|
+
},
|
|
676
|
+
required: {
|
|
677
|
+
selector: '[required]',
|
|
678
|
+
rules: ['required'],
|
|
679
|
+
},
|
|
680
|
+
url: {
|
|
681
|
+
selector: 'input[type="url"]',
|
|
682
|
+
rules: ['url'],
|
|
683
|
+
},
|
|
684
|
+
'not-numbers': {
|
|
685
|
+
selector: 'input[data-validate="not-numbers"]',
|
|
686
|
+
rules: ['not-numbers'],
|
|
687
|
+
},
|
|
688
|
+
};
|
|
707
689
|
|
|
708
|
-
export { Form as default };
|
|
690
|
+
export { Form as default, getAllValidators, getValidator, registerValidator };
|
|
709
691
|
//# sourceMappingURL=index.js.map
|
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
|
@@ -1,3 +1,12 @@
|
|
|
1
|
+
export * from './validators';
|
|
2
|
+
type ValidationSchema = Record<string, {
|
|
3
|
+
rules: (string | {
|
|
4
|
+
rule: string;
|
|
5
|
+
params?: any;
|
|
6
|
+
})[];
|
|
7
|
+
selector: string;
|
|
8
|
+
messages?: Record<string, string>;
|
|
9
|
+
}>;
|
|
1
10
|
/** Параметры формы. */
|
|
2
11
|
interface FormOptions {
|
|
3
12
|
/**
|
|
@@ -51,6 +60,8 @@ interface FormOptions {
|
|
|
51
60
|
validatePhone?: (input: HTMLInputElement) => boolean;
|
|
52
61
|
/** Функция для обёртки отправляемых данных. */
|
|
53
62
|
wrapData?: (data: Record<string, any>) => Record<string, any>;
|
|
63
|
+
/** Схема валидации: поле → массив правил + override-сообщения */
|
|
64
|
+
validationSchema?: ValidationSchema;
|
|
54
65
|
}
|
|
55
66
|
/**
|
|
56
67
|
* Реализует форму отправки данных.
|
|
@@ -66,6 +77,7 @@ export default class Form {
|
|
|
66
77
|
private inputs;
|
|
67
78
|
private $licensesCheckbox;
|
|
68
79
|
private static defaultParams;
|
|
80
|
+
static defaultValidationSchema: ValidationSchema;
|
|
69
81
|
/**
|
|
70
82
|
* Создать форму.
|
|
71
83
|
*
|
|
@@ -107,13 +119,18 @@ export default class Form {
|
|
|
107
119
|
*/
|
|
108
120
|
private scrollToFirstErroredInput;
|
|
109
121
|
/**
|
|
110
|
-
* Проверяет все поля
|
|
122
|
+
* Проверяет все поля ввода.
|
|
111
123
|
*
|
|
112
|
-
*
|
|
124
|
+
* Порядок для КАЖДОГО `<input>`:
|
|
113
125
|
*
|
|
114
|
-
*
|
|
126
|
+
* 1. `required` — если атрибут `required`
|
|
127
|
+
* 2. правила из `validationSchema`
|
|
128
|
+
* 3. правила из `data-custom-validate`
|
|
129
|
+
*
|
|
130
|
+
* Для одного поля показывается только первая ошибка. Неизвестные правила фиксируются `console.warn` и исключаются ДО
|
|
131
|
+
* валидации, поэтому валидационный цикл больше не проверяет их наличие.
|
|
115
132
|
*/
|
|
116
|
-
|
|
133
|
+
validate(): Promise<boolean>;
|
|
117
134
|
/** Блокирует кнопку отправки данных. */
|
|
118
135
|
private disableSubmit;
|
|
119
136
|
/** Снимает блокировку с кнопки отправки данных. */
|
|
@@ -135,4 +152,3 @@ export default class Form {
|
|
|
135
152
|
*/
|
|
136
153
|
private isCorrectArguments;
|
|
137
154
|
}
|
|
138
|
-
export {};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
type ValidatorFn = (value: string, $input: HTMLInputElement, $form: HTMLElement, params?: any) => boolean | Promise<boolean>;
|
|
2
|
+
interface Validator {
|
|
3
|
+
fn: ValidatorFn;
|
|
4
|
+
defaultMessage: string;
|
|
5
|
+
}
|
|
6
|
+
export declare function registerValidator(name: string, fn: ValidatorFn, defaultMessage: string, { override }?: {
|
|
7
|
+
override?: boolean;
|
|
8
|
+
}): void;
|
|
9
|
+
export declare const getValidator: (name: string) => Validator | undefined;
|
|
10
|
+
export declare function getAllValidators(): Map<string, Validator>;
|
|
11
|
+
export {};
|