UNPKG

red-form

Version:

A powerful, type-safe React form library that lets you create dynamic dialog-based forms using schema definitions — inspired by Formik but designed for real-time UI rendering and reusability.

2 lines (1 loc) 16.7 kB
import{createContext as _,Fragment as Y,useCallback as F,useContext as X,useEffect as k,useMemo as T,useRef as O,useState as v}from"react";import{Fragment as N,jsx as o,jsxs as x}from"react/jsx-runtime";var V=_({open:()=>0,close:()=>{}});function J(e,t,r){let n=T(()=>{let d={};return Object.entries(e).forEach(([b,f])=>{d[b]=f.value}),d},[e]),a=O(null),[m,s]=v(n),[i,l]=v({}),[g,u]=v({}),[c,p]=v(),[h,K]=v(!1),S=(d,b)=>{i[d]&&l(f=>({...f,[d]:void 0})),s(f=>({...f,[d]:b}))},L=(d,b)=>{l(f=>({...f,[d]:[...f[d]||[],b]}))},A=(d,b)=>{u(f=>({...f,[d]:b}))},j=d=>{d!==c&&p(d)},C=d=>{d.target.name&&(u(b=>({...b,[d.target.name]:!0})),p(void 0),a.current=null)},I=d=>{d.target.name&&p(d.target.name)},E=d=>{d.target.name&&S(d.target.name,d.target.value)},R=d=>({name:d,id:d,value:m[d],required:!!e[d].required,disabled:!!e[d].disabled,placeholder:e[d].placeholder,autoComplete:e[d].component==="text"&&e[d].autoFill?e[d].autoFill:void 0,onChange:E,onBlur:C,onFocus:I,onMouseDown:b=>{b.stopPropagation()},ref:d===c?a:void 0}),z=F(()=>{let d={};return Object.entries(e).forEach(([b,f])=>{let w=m[b],y=[];if(f.disabled)return y;f.validate?f.validate({field:b,props:f,form:$}).forEach(q=>y.push(q)):(f.required&&(w===void 0||w===""||Array.isArray(w)&&w.length===0)&&y.push("filed is required."),(f.component==="text"||f.component==="textarea")&&(f.max!==void 0&&w.length>f.max&&y.push(`Field Length must be less than ${f.max}.`),f.min!==void 0&&w.length<f.min&&y.push(`Field Lenght must be more than ${f.min}.`)),f.component==="range"&&(w>f.max&&y.push(`Field value must be less than ${f.max}.`),w<f.min&&y.push(`Field value must be more than ${f.min}`))),y.length>0&&(d[b]=y)}),l(d),Object.keys(d).length===0},[m,e]),P=F(async()=>{K(!0),await t(m),K(!1)},[z,t,m]),H=F(d=>{d.preventDefault(),d.stopPropagation(),P()},[P]),B=F(()=>{s(n),u({}),l({})},[n]),$={submitting:h,setSubmitting:K,values:m,setFieldValue:S,setValues:s,errors:i,setFieldError:L,setErrors:l,touched:g,setFieldTouched:A,setTouched:u,handleBlur:C,setFieldActive:j,activeField:c,handleFocus:I,handleChange:E,handleSubmit:H,resetForm:B,validate:z,getFieldProps:R,submit:P};return k(()=>{a&&a.current&&c===a.current.name&&a.current.focus()},[a,c]),$}var M=({schema:e,title:t,description:r,disabled:n,onSubmit:a,onChange:m,onError:s,onBlur:i,sx:l={},options:g})=>{let u=J(e,async p=>{a&&(g&&g.validateOn&&g.validateOn.includes("submit")&&u.validate()?await a(p):await a(p))});k(()=>{s&&s(u.errors)},[u.errors]),k(()=>{m&&m(u.values),!n&&g&&g.validateOn&&g.validateOn.includes("change")&&u.validate()},[u.values]),k(()=>{i&&i(u.touched)},[u.touched]);let c=T(()=>Object.values(u.errors).filter(p=>p).length>0||u.submitting,[u.errors,u.submitting]);return x("div",{className:"red-form-container",onMouseDown:()=>{u.setFieldActive(void 0)},style:{...l.conteiner},children:[t&&o("div",{className:"red-form-title",style:{...l.title},children:t}),r&&o("p",{className:"red-form-description",style:{...l.description},children:r}),x("form",{className:"red-form",onSubmit:u.handleSubmit,style:{...l.form},children:[Object.entries(e).map(([p,h])=>(h.disabled=!!n||!!h.disabled,o(U,{field:p,props:h,form:u,sx:l},p))),n&&o("div",{style:{width:"100%",height:"100%",position:"absolute",cursor:"default",pointerEvents:"all",zIndex:999,inset:0}}),a&&!n&&x("div",{className:"red-form-action-area",style:{...l.actionArea},children:[o("button",{className:"red-form-button red-form-reset-button",onClick:p=>{p.preventDefault(),u.resetForm()},style:{...l.resetButton},children:"Reset"}),o("button",{disabled:c,className:`red-form-button ${c?"red-form-submit-button-disabled red-form-error-shadow":"red-form-submit-button"}`,type:"submit",style:{...l.submitButton},children:"Submit"})]})]})]})},U=({field:e,props:t,form:r,sx:n})=>{let a=r.errors[e],m=T(()=>({gridColumn:t.span?`span ${t.span}`:void 0,cursor:t.disabled?"not-allowed":void 0,...n.inputContainer}),[t.span,t.disabled]);return t.component==="custom"&&!t.inputBase?o("div",{style:{...m},className:`${a?"red-form-error":""}`,children:o(D,{field:e,props:t,form:r,error:a,sx:n})}):x("div",{className:`red-form-input-container ${a?"red-form-error":""}`,style:m,children:[x("div",{className:"red-form-input-label-container",style:{...n.inputLabelContainer},children:[x("label",{className:`red-form-input-label ${a?"red-form-error":""}`,htmlFor:e,style:{...n.inputLabel},children:[t.label," ",o("span",{className:"red-form-error",children:t.required&&"*"})]}),t.information&&x("div",{className:"red-form-tooltip-container",style:{...n.tooltipContainer},children:[o("div",{className:"red-form-info-icon",style:{...n.tooltipInfoIcon},children:"i"}),o("div",{className:"red-form-tooltip",style:{...n.tooltip},children:t.information})]})]}),o(W,{field:e,props:t,form:r,error:a,sx:n}),a&&!t.disabled?o("ul",{className:`red-form-error-list ${a?"red-form-error":""}`,style:{...n.errorList},children:a.map(s=>o("li",{style:{...n.errorItem},children:s},s))}):o(N,{children:t.helperText!==void 0&&o("p",{className:`red-form-helper-text ${a?"red-form-error":""}`,style:{...n.helperText},children:t.helperText})})]})},W=e=>{switch(e.props.component){case"radio":return o(xe,{...e});case"checkbox":return o(pe,{...e});case"switch":return o(ge,{...e});case"image":return o(ve,{...e});default:return o(G,{...e})}},G=e=>x("div",{className:`red-form-input-base ${e.props.disabled?"":`${e.error?"red-form-error red-form-error-shadow":"red-form-input-base-shadow"}`}`,style:{...e.sx.inputBase},children:[e.props.adorment&&e.props.adorment.start&&o(N,{children:e.props.adorment.start}),o(Q,{...e}),e.props.adorment&&e.props.adorment.end&&o(N,{children:e.props.adorment.end})]}),Q=e=>{switch(e.props.component){case"text":return o(Z,{...e});case"textarea":return o(me,{...e});case"search":return o(ee,{...e});case"email":return o(re,{...e});case"telephone":return o(te,{...e});case"password":return o(oe,{...e});case"number":return o(ne,{...e});case"date":return o(ae,{...e});case"datetime":return o(ce,{...e});case"time":return o(ie,{...e});case"month":return o(de,{...e});case"week":return o(se,{...e});case"range":return o(le,{...e});case"color":return o(ue,{...e});case"select":return o(fe,{...e});case"multi-select":return o(be,{...e});case"tags":return o(he,{...e});case"custom":return o(D,{...e});default:return o("div",{children:"Nothing To Render"})}},D=({field:e,props:t,form:r,error:n,sx:a})=>t.component!=="custom"?null:o(Y,{children:t.render({field:e,props:t,form:r,error:n,sx:a})}),Z=({field:e,props:t,form:r,error:n})=>t.component!=="text"?null:o("input",{type:"text",className:`red-form-input ${n?"red-form-error":""}`,...r.getFieldProps(e)}),ee=({field:e,props:t,form:r,error:n})=>t.component!=="search"?null:o("input",{type:"search",className:`red-form-input ${n?"red-form-error":""}`,...r.getFieldProps(e)}),re=({field:e,props:t,form:r,error:n})=>t.component!=="email"?null:o("input",{type:"email",className:`red-form-input ${n?"red-form-error":""}`,...r.getFieldProps(e)}),te=({field:e,props:t,form:r,error:n})=>t.component!=="telephone"?null:o("input",{type:"tel",...r.getFieldProps(e),className:`red-form-input ${n?"red-form-error":""}`}),oe=({field:e,props:t,form:r,error:n})=>t.component!=="password"?null:o("input",{type:"password",className:`red-form-input ${n?"red-form-error":""}`,...r.getFieldProps(e)}),ne=({field:e,props:t,form:r,error:n})=>t.component!=="number"?null:o("input",{type:"number",min:t.min,max:t.max,className:`red-form-input ${n?"red-form-error":""}`,...r.getFieldProps(e)}),ae=({field:e,props:t,form:r,error:n})=>t.component!=="date"?null:o("input",{type:"date",className:`red-form-input ${n?"red-form-error":""}`,...r.getFieldProps(e)}),ie=({field:e,props:t,form:r,error:n})=>t.component!=="time"?null:o("input",{type:"time",className:`red-form-input ${n?"red-form-error":""}`,...r.getFieldProps(e)}),se=({field:e,props:t,form:r,error:n})=>t.component!=="week"?null:o("input",{type:"week",className:`red-form-input ${n?"red-form-error":""}`,...r.getFieldProps(e)}),de=({field:e,props:t,form:r,error:n})=>t.component!=="month"?null:o("input",{type:"month",className:`red-form-input ${n?"red-form-error":""}`,...r.getFieldProps(e)}),le=({field:e,props:t,form:r,error:n})=>t.component!=="range"?null:x("div",{className:"red-form-range-field-container",children:[o("input",{type:"range",min:t.min,max:t.max,step:t.step,className:`red-form-input red-form-range-field ${n?"red-form-error":""}`,...r.getFieldProps(e)}),o("div",{className:"red-form-range-field-value",children:r.values[e]})]}),ce=({field:e,props:t,form:r,error:n})=>t.component!=="datetime"?null:o("input",{type:"datetime-local",className:`red-form-input ${n?"red-form-error":""}`,...r.getFieldProps(e)}),me=({field:e,props:t,form:r,error:n})=>t.component!=="textarea"?null:o("textarea",{className:`red-form-input red-form-textarea ${n?"red-form-error":""}`,...r.getFieldProps(e)}),ue=({field:e,props:t,form:r,error:n})=>t.component!=="color"?null:x("label",{htmlFor:e,className:"red-form-color-field-container",children:[o("input",{type:"color",className:"red-form-color-field",...r.getFieldProps(e)}),o("div",{style:{background:r.values[e]},className:"red-form-color-field-dot"}),o("div",{className:`red-form-color-field-value ${n?"red-form-error":""}`,children:r.values[e]})]}),fe=({field:e,props:t,form:r,error:n})=>t.component!=="select"?null:x("select",{id:e,value:r.values[e],onChange:a=>r.setFieldValue(e,a.target.value),className:`red-form-select-field ${t.disabled?"red-form-select-field-disabled":""} ${n?"red-form-error":""}`,children:[x("option",{value:"",className:"red-form-select-option",children:["Select ",t.label]}),t.options.map(a=>{let m=typeof a=="string"?a:a.label,s=typeof a=="string"?a:a.value;return o("option",{value:s,className:"red-form-select-option",children:m},s)})]}),pe=({field:e,props:t,form:r,error:n})=>t.component!=="checkbox"?null:o("div",{className:`${t.direction==="column"?"red-form-checkbox-base-column":"red-form-checkbox-base-row"} ${n?"red-form-error":""}`,children:t.options.map((a,m)=>{let s=typeof a=="string"?a:a.label,i=typeof a=="string"?a:a.value;return x("div",{className:"red-form-checkbox-field-item",children:[o("input",{type:"checkbox",className:"red-form-checkbox-field-input",readOnly:!0,name:e,checked:Array.isArray(r.values[e])?r.values[e].includes(i):r.values[e]===i,onClick:()=>{Array.isArray(r.values[e])?r.values[e].includes(i)?r.setFieldValue(e,r.values[e].filter(l=>i!==l)):r.setFieldValue(e,[...r.values[e],i]):r.values[e]===i?r.setFieldValue(e,void 0):r.setFieldValue(e,i)}}),o("div",{className:"red-form-checkbox-field-label",children:s})]},i)})}),xe=({field:e,props:t,form:r,error:n})=>t.component!=="radio"?null:o("div",{className:`${t.direction==="column"?"red-form-radio-base-column":"red-form-radio-base-row"} ${n?"red-form-error":""}`,children:t.options.map((a,m)=>{let s=typeof a=="string"?a:a.label,i=typeof a=="string"?a:a.value,l=()=>{r.values[e]===i?r.setFieldValue(e,""):r.setFieldValue(e,i)};return x("div",{className:"red-form-radio-field-item",children:[o("input",{type:"radio",readOnly:!0,className:"red-form-radio-field-input",name:e,checked:i===r.values[e],value:i,onClick:l,disabled:t.disabled,onKeyUp:g=>{g.preventDefault(),g.key===" "&&l()}}),o("div",{className:"red-form-radio-field-label",children:s})]},i)})}),ge=({field:e,props:t,form:r,error:n})=>t.component!=="switch"?null:x("label",{className:"red-form-switch-base",children:[o("input",{type:"checkbox",...r.getFieldProps(e),checked:r.values[e],disabled:t.disabled,onChange:a=>{r.setFieldValue(e,a.target.checked)}}),o("span",{className:"red-form-switch",children:o("span",{className:"red-form-slider"})})]}),be=({field:e,props:t,form:r,error:n})=>{if(t.component!=="multi-select")return null;let[a,m]=v(""),[s,i]=v(0),l=T(()=>t.options.reduce((c,p)=>(typeof p=="string"?c[p]=p:c[p.value]=p.label,c),{}),[t.options]),g=T(()=>Object.keys(l),[l]),u=T(()=>g.filter(c=>r.values[e].includes(c)?!1:c.toLowerCase().includes(a.toLowerCase())).slice(0,3),[a,g,r.values[e]]);return k(()=>{i(0)},[a,u]),x("div",{className:"red-form-multi-select-wrapper",children:[x("div",{className:"red-form-multi-select-container",children:[r.values[e].map(c=>x("div",{className:"red-form-multi-select-item",children:[o("span",{children:l[c]}),o("span",{className:"red-form-multi-select-item-cross",onMouseDown:p=>{p.stopPropagation(),p.preventDefault()},onClick:p=>{p.stopPropagation(),p.preventDefault(),r.setFieldValue(e,r.values[e].filter(h=>h!==c))},children:"\xD7"})]},c)),o("input",{...r.getFieldProps(e),value:a,onChange:c=>{m(c.target.value)},onBlur:()=>{},required:!1,autoComplete:"off",onKeyDown:c=>{c.key==="Enter"?(c.preventDefault(),u[s]&&(r.setFieldValue(e,[...r.values[e],u[s]]),m(""))):c.key==="ArrowDown"?(c.preventDefault(),u.length-1===s?i(0):i(s+1)):c.key==="ArrowUp"?(c.preventDefault(),i(s===0?u.length-1:s-1)):c.key==="Backspace"&&a===""&&r.setFieldValue(e,[...r.values[e]].slice(0,-1))},type:"text",className:"red-form-input red-form-multi-select-input"})]}),r.activeField===e&&u.length>0&&o("div",{className:"red-form-multi-select-suggestion-container",children:u.map((c,p)=>o("div",{className:`suggestion-item ${s===p?"active":""}`,onMouseDown:h=>{h.stopPropagation(),h.preventDefault(),r.setFieldValue(e,[...r.values[e],c]),m("")},children:l[c]},c))})]})},he=({field:e,props:t,form:r,error:n})=>{if(t.component!=="tags")return null;let[a,m]=v("");return x("div",{className:"red-form-tags-container",children:[r.values[e].map(s=>x("div",{className:"red-form-tags-item",children:[o("span",{children:s}),o("span",{className:"red-form-tags-item-cross",onClick:()=>{r.setFieldValue(e,r.values[e].filter(i=>i!==s))},children:"\xD7"})]},s)),o("input",{value:a,onChange:s=>{s.target.value.endsWith(",")?(r.setFieldValue(e,[...r.values[e],a.trim()]),m("")):m(s.target.value)},placeholder:t.placeholder,disabled:t.disabled,onKeyDown:s=>{s.key==="Enter"?(s.preventDefault(),r.setFieldValue(e,[...r.values[e],a.trim()]),m("")):s.key==="Backspace"&&a===""&&r.setFieldValue(e,[...r.values[e]].slice(0,-1))},name:e,id:e,type:"text",className:"red-form-input red-form-tags-input"})]})},ve=({field:e,props:t,form:r,error:n})=>t.component!=="image"?null:o("div",{className:`red-form-image-base ${n?"red-form-error red-form-error-shadow":""}`,children:r.values[e]?x(N,{children:[o("div",{className:"red-form-image-remove-base"}),o("div",{className:"red-form-image-remove-button",onClick:()=>{r.setFieldValue(e,"")},children:"\xD7"}),o("img",{className:"red-form-image-view",src:r.values[e]})]}):x("label",{htmlFor:e,className:"red-form-image-input",children:["+",o("input",{type:"file",...r.getFieldProps(e),disabled:t.disabled,value:void 0,onChange:a=>{if(a.target.files&&a.target.files.length===1){let m=a.target.files.item(0);m&&t.onSelect&&t.onSelect(m).then(s=>{r.setFieldValue(e,s)}),a.target.files=null}},style:{display:"none"}})]})}),Pe=({children:e})=>{let[t,r]=v({}),n=T(()=>Object.entries(t),[t]),a=s=>{let i=Date.now();return r(l=>({...l,[i]:s})),i},m=s=>{r(i=>{let l={...i},g=l[s].onClose;return setTimeout(()=>{g()},50),delete l[s],l})};return x(V.Provider,{value:{open:a,close:m},children:[n.map(([s,i])=>{let l=Number(s);return o(ye,{isOpen:!0,height:i.height,width:i.width,minHeight:i.minHeight,onClose:i.close!==!1?()=>{m(l)}:void 0,children:o(M,{schema:i.schema,onSubmit:i.onSubmit,onBlur:i.onBlur,onChange:i.onChange,onError:i.onError,title:i.title,description:i.description})},l)}),e]})},Se=(e,t)=>{let[r,n]=v({}),{open:a,close:m}=X(V),s={open:()=>{if(!r.id){let i={...t,schema:e,onClose:()=>{n({})}},l=a(i);n({id:l,props:i})}},close:()=>{r.id&&t.close!==!1&&(m(r.id),n({}))}};return k(()=>{t.open&&JSON.stringify(r.props)!==JSON.stringify(t)&&s.open()},[]),s},ye=e=>{let t=O(null);k(()=>(e.isOpen?document.body.style.overflow="hidden":document.body.style.overflow="",()=>{document.body.style.overflow=""}),[e.isOpen]),k(()=>{let n=a=>{a.key==="Escape"&&e.onClose&&e.onClose()};return e.isOpen&&window.addEventListener("keydown",n),()=>window.removeEventListener("keydown",n)},[e.isOpen,e.onClose]);let r=n=>{t.current&&!t.current.contains(n.target)&&e.onClose&&e.onClose()};return e.isOpen?o("div",{onClick:r,className:"red-form-modal-background",children:x("div",{className:"red-form-modal",ref:t,style:{width:e.width,minHeight:e.minHeight,height:e.height},children:[x("div",{className:"red-form-modal-header",children:[o("div",{className:"red-form-modal-title",children:e.title}),e.onClose&&o("button",{onClick:e.onClose,className:"red-form-modal-close-button","aria-label":"Close",children:"\xD7"})]}),o("div",{style:{flex:1},className:"red-form-modal-container",children:e.children})]})}):null},Ce=e=>e,Ie=M;export{Pe as FormProvider,ye as Modal,Ce as create,Ie as default,J as useForm,Se as useModalForm};