@rilaykit/forms 7.0.0 → 8.0.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/dist/index.d.mts CHANGED
@@ -740,6 +740,13 @@ interface FormRowProps extends ComponentRendererBaseProps<FormRowRendererProps>
740
740
  }
741
741
  declare function FormRow({ row, className, ...props }: FormRowProps): react_jsx_runtime.JSX.Element;
742
742
 
743
- declare function FormSubmitButton({ className, ...props }: ComponentRendererBaseProps<FormSubmitButtonRendererProps>): react_jsx_runtime.JSX.Element;
743
+ interface FormSubmitButtonProps extends ComponentRendererBaseProps<FormSubmitButtonRendererProps> {
744
+ /**
745
+ * Override the isSubmitting state from form context
746
+ * If provided, this value will be used instead of the form's isSubmitting state
747
+ */
748
+ isSubmitting?: boolean;
749
+ }
750
+ declare function FormSubmitButton({ className, isSubmitting: overrideIsSubmitting, ...props }: FormSubmitButtonProps): react_jsx_runtime.JSX.Element;
744
751
 
745
752
  export { type ConditionEvaluationResult, type FieldConfig, Form, type FormAction, FormBody, form as FormBuilder, FormField, FormProvider, FormRow, type FormState, FormSubmitButton, type UseFormConditionsProps, type UseFormConditionsReturn, type UseFormStateProps, type UseFormSubmissionProps, type UseFormValidationProps, createForm, form, useConditionEvaluation, useFormConditions, useFormContext, useFormState, useFormSubmission, useFormValidation, useMultipleConditionEvaluation };
package/dist/index.d.ts CHANGED
@@ -740,6 +740,13 @@ interface FormRowProps extends ComponentRendererBaseProps<FormRowRendererProps>
740
740
  }
741
741
  declare function FormRow({ row, className, ...props }: FormRowProps): react_jsx_runtime.JSX.Element;
742
742
 
743
- declare function FormSubmitButton({ className, ...props }: ComponentRendererBaseProps<FormSubmitButtonRendererProps>): react_jsx_runtime.JSX.Element;
743
+ interface FormSubmitButtonProps extends ComponentRendererBaseProps<FormSubmitButtonRendererProps> {
744
+ /**
745
+ * Override the isSubmitting state from form context
746
+ * If provided, this value will be used instead of the form's isSubmitting state
747
+ */
748
+ isSubmitting?: boolean;
749
+ }
750
+ declare function FormSubmitButton({ className, isSubmitting: overrideIsSubmitting, ...props }: FormSubmitButtonProps): react_jsx_runtime.JSX.Element;
744
751
 
745
752
  export { type ConditionEvaluationResult, type FieldConfig, Form, type FormAction, FormBody, form as FormBuilder, FormField, FormProvider, FormRow, type FormState, FormSubmitButton, type UseFormConditionsProps, type UseFormConditionsReturn, type UseFormStateProps, type UseFormSubmissionProps, type UseFormValidationProps, createForm, form, useConditionEvaluation, useFormConditions, useFormContext, useFormState, useFormSubmission, useFormValidation, useMultipleConditionEvaluation };
package/dist/index.js CHANGED
@@ -1 +1 @@
1
- 'use strict';var Re=require('react'),core=require('@rilaykit/core'),jsxRuntime=require('react/jsx-runtime');function _interopDefault(e){return e&&e.__esModule?e:{default:e}}var Re__default=/*#__PURE__*/_interopDefault(Re);var h=class i{constructor(e,r){this.rows=[];this.idGenerator=new core.IdGenerator;this.config=e,this.formId=r||`form-${Math.random().toString(36).substring(2,15)}`;}static create(e,r){return new i(e,r)}createFormField(e){let r=this.config.getComponent(e.type);if(!r)throw new Error(`No component found with type "${e.type}"`);let t;return (r.validation||e.validation)&&(t={validateOnChange:e.validation?.validateOnChange??r.validation?.validateOnChange,validateOnBlur:e.validation?.validateOnBlur??r.validation?.validateOnBlur,debounceMs:e.validation?.debounceMs??r.validation?.debounceMs,validators:[...r.validation?.validators||[],...e.validation?.validators||[]]}),{id:e.id||this.idGenerator.next("field"),componentId:r.id,props:{...r.defaultProps,...e.props},validation:t,conditions:e.conditions}}createRow(e){if(e.length===0)throw new Error("At least one field is required");if(e.length>3)throw new Error("Maximum 3 fields per row");let r=e.map(t=>this.createFormField(t));return {id:this.idGenerator.next("row"),fields:r,maxColumns:e.length}}add(...e){let r,t=false;if(e.length===1&&Array.isArray(e[0])?(r=e[0],t=true):r=e,r.length===0)throw new Error("At least one field is required");if(t&&r.length>3)throw new Error("Maximum 3 fields per row");if(r.length===1){let o=this.createRow(r);return this.rows.push(o),this}if(r.length<=3){let o=this.createRow(r);return this.rows.push(o),this}for(let o of r){let d=this.createRow([o]);this.rows.push(d);}return this}addSeparateRows(e){for(let r of e)this.add(r);return this}setId(e){return this.formId=e,this}updateField(e,r){let t=this.findField(e);if(!t)throw new Error(`Field with ID "${e}" not found`);return Object.assign(t,{...r,props:{...t.props,...r.props}}),this}findField(e){for(let r of this.rows){let t=r.fields.find(o=>o.id===e);if(t)return t}return null}removeField(e){return this.rows=this.rows.map(r=>({...r,fields:r.fields.filter(t=>t.id!==e)})).filter(r=>r.fields.length>0),this}getField(e){return this.findField(e)||void 0}getFields(){return this.rows.flatMap(e=>e.fields)}getRows(){return [...this.rows]}clear(){return this.rows=[],this.idGenerator.reset(),this}setValidation(e){return this.formValidation=e,this}addValidators(e){return this.formValidation||(this.formValidation={validators:[]}),this.formValidation={...this.formValidation,validators:[...this.formValidation.validators||[],...e]},this}addFieldValidation(e,r){let t=this.findField(e);if(!t)throw new Error(`Field with ID "${e}" not found`);let o={...t.validation,...r,validators:[...t.validation?.validators||[],...r.validators||[]]};return this.updateField(e,{validation:o})}addFieldConditions(e,r){let t=this.findField(e);if(!t)throw new Error(`Field with ID "${e}" not found`);let o={...t.conditions,...r};return this.updateField(e,{conditions:o})}clone(e){let r=new i(this.config,e||`${this.formId}-clone`);return r.rows=core.deepClone(this.rows),r}validate(){let e=[],r=this.getFields(),t=r.map(o=>o.id);try{core.ensureUnique(t,"field");}catch(o){e.push(o instanceof Error?o.message:String(o));}for(let o of r)this.config.hasComponent(o.componentId)||e.push(`Component "${o.componentId}" not found for field "${o.id}"`);for(let o of this.rows)o.fields.length>3&&e.push(`Row "${o.id}" has ${o.fields.length} fields, maximum is 3`),o.fields.length===0&&e.push(`Row "${o.id}" is empty`);return e}build(){let e=this.validate();if(e.length>0)throw new Error(`Form validation failed: ${e.join(", ")}`);return {id:this.formId,rows:[...this.rows],allFields:this.getFields(),config:this.config,renderConfig:this.config.getFormRenderConfig(),validation:this.formValidation}}toJSON(){return {id:this.formId,rows:this.rows}}fromJSON(e){return e.id&&(this.formId=e.id),e.rows&&(this.rows=e.rows),this}getStats(){let e=this.getFields(),r=this.rows.map(t=>t.fields.length);return {totalFields:e.length,totalRows:this.rows.length,averageFieldsPerRow:this.rows.length>0?e.length/this.rows.length:0,maxFieldsInRow:r.length>0?Math.max(...r):0,minFieldsInRow:r.length>0?Math.min(...r):0}}};function ae(i,e){return h.create(i,e)}core.ril.prototype.form=function(i){return h.create(this,i)};function De(i,e={},r={}){return Re.useMemo(()=>{if(!i)return {visible:r.visible??true,disabled:r.disabled??false,required:r.required??false,readonly:r.readonly??false};let t=o=>{try{return o&&typeof o=="object"&&"build"in o?core.evaluateCondition(o.build(),e):core.evaluateCondition(o,e)}catch(d){return console.warn("Error evaluating condition:",d),false}};return {visible:i.visible?t(i.visible):r.visible??true,disabled:i.disabled?t(i.disabled):r.disabled??false,required:i.required?t(i.required):r.required??false,readonly:i.readonly?t(i.readonly):r.readonly??false}},[i,e,r])}function L(i,e={}){return Re.useMemo(()=>{let r={};for(let[t,o]of Object.entries(i))if(r[t]={visible:true,disabled:false,required:false,readonly:false},o){let d=m=>{try{return m&&typeof m=="object"&&"build"in m?core.evaluateCondition(m.build(),e):core.evaluateCondition(m,e)}catch(s){return console.warn(`Error evaluating condition for field ${t}:`,s),false}};r[t]={visible:o.visible?d(o.visible):true,disabled:o.disabled?d(o.disabled):false,required:o.required?d(o.required):false,readonly:o.readonly?d(o.readonly):false};}return r},[i,e])}function $({formConfig:i,formValues:e}){let r=Re.useMemo(()=>{let l={};for(let a of i.allFields)a.conditions&&(l[a.id]=a.conditions);return l},[i.allFields]),t=Re.useMemo(()=>Object.keys(r).length>0,[r]),o=L(t?r:{},t?e:{}),d=Re.useCallback(l=>o[l],[o]),m=Re.useCallback(l=>{let a=o[l];return a?a.visible:true},[o]),s=Re.useCallback(l=>{let a=o[l];return a?a.disabled:false},[o]),F=Re.useCallback(l=>{let a=o[l];return a?a.required:false},[o]),f=Re.useCallback(l=>{let a=o[l];return a?a.readonly:false},[o]);return Re.useMemo(()=>({fieldConditions:o,hasConditionalFields:t,getFieldCondition:d,isFieldVisible:m,isFieldDisabled:s,isFieldRequired:F,isFieldReadonly:f}),[o,t,d,m,s,F,f])}function ue(i,e){switch(e.type){case "SET_VALUE":return {...i,values:{...i.values,[e.fieldId]:e.value},isDirty:true};case "SET_FIELD_ERRORS":return {...i,errors:{...i.errors,[e.fieldId]:e.errors}};case "SET_FIELD_VALIDATION_STATE":return {...i,validationState:{...i.validationState,[e.fieldId]:e.state}};case "SET_FIELD_TOUCHED":return {...i,touched:{...i.touched,[e.fieldId]:true}};case "SET_SUBMITTING":return {...i,isSubmitting:e.isSubmitting};case "RESET":return {values:e.values||{},errors:{},validationState:{},touched:{},isDirty:false,isSubmitting:false};default:return i}}function G({defaultValues:i={},onFieldChange:e}){let r={values:i,errors:{},validationState:{},touched:{},isDirty:false,isSubmitting:false},[t,o]=Re.useReducer(ue,r),d=Re.useRef(e);d.current=e;let m=Re.useCallback((u,c)=>{o({type:"SET_VALUE",fieldId:u,value:c}),d.current?.(u,c,{...t.values,[u]:c});},[t.values]),s=Re.useCallback(u=>{o({type:"SET_FIELD_TOUCHED",fieldId:u});},[]),F=Re.useCallback((u,c)=>{o({type:"SET_FIELD_ERRORS",fieldId:u,errors:c});},[]),f=Re.useCallback(u=>{o({type:"SET_FIELD_ERRORS",fieldId:u,errors:[]});},[]),l=Re.useCallback((u,c)=>{o({type:"SET_FIELD_VALIDATION_STATE",fieldId:u,state:c});},[]),a=Re.useCallback(u=>{o({type:"SET_SUBMITTING",isSubmitting:u});},[]),n=Re.useCallback(u=>{o({type:"RESET",values:u});},[]),v=Re.useCallback(()=>{let u=Object.values(t.errors).some(p=>p.length>0),c=Object.values(t.validationState).some(p=>p==="invalid");return !u&&!c},[t.errors,t.validationState]);return {formState:t,setValue:m,setFieldTouched:s,setError:F,clearError:f,setFieldValidationState:l,setSubmitting:a,reset:n,isFormValid:v}}function W({formState:i,onSubmit:e,validateForm:r,setSubmitting:t}){let o=Re.useRef(e);return o.current=e,{submit:Re.useCallback(async m=>{m?.preventDefault();try{return t(!0),(await r()).isValid?(o.current&&await o.current(i.values),!0):!1}catch(s){return console.error("Error during form submission:",s),false}finally{t(false);}},[i.values,r,t])}}function k(){return {isValid:true,errors:[]}}function H({formConfig:i,formState:e,conditionsHelpers:r,setFieldValidationState:t,setError:o}){let d=Re.useCallback(async(s,F)=>{let f=i.allFields.find(n=>n.id===s);if(!f)return k();if(!r.isFieldVisible(s))return o(s,[]),t(s,"valid"),k();if(!f.validation?.validators?.length)return o(s,[]),t(s,"valid"),k();let l=F!==void 0?F:e.values[s],a=core.createValidationContext({fieldId:s,formId:i.id,allFormData:{...e.values,[s]:l}});t(s,"validating");try{let n=await core.runValidatorsAsync(f.validation.validators,l,a),v=r.isFieldRequired(s),u=l==null||l==="";if(v&&u&&!n.errors.some(p=>p.code==="REQUIRED"||p.message.toLowerCase().includes("required"))){let p={isValid:!1,errors:[{message:"This field is required",code:"CONDITIONAL_REQUIRED"},...n.errors]};return o(s,p.errors),t(s,"invalid"),p}return o(s,n.errors),t(s,n.isValid?"valid":"invalid"),n}catch(n){let v={isValid:false,errors:[{message:n instanceof Error?n.message:"Validation failed",code:"VALIDATION_ERROR"}]};return o(s,v.errors),t(s,"invalid"),v}},[i,e.values,r,t,o]),m=Re.useCallback(async()=>{let s=i.allFields.filter(n=>{let v=r.isFieldVisible(n.id),u=n.validation?.validators&&n.validation.validators.length>0;return v&&u}),F=i.allFields.filter(n=>!r.isFieldVisible(n.id));for(let n of F)o(n.id,[]),t(n.id,"valid");let f=await Promise.all(s.map(n=>d(n.id))),l=f.some(n=>!n.isValid),a=k();if(i.validation?.validators?.length){let n=Object.keys(e.values).reduce((u,c)=>(r.isFieldVisible(c)&&(u[c]=e.values[c]),u),{}),v=core.createValidationContext({formId:i.id,allFormData:n});try{a=await core.runValidatorsAsync(i.validation.validators,n,v);}catch(u){a={isValid:false,errors:[{message:u instanceof Error?u.message:"Form validation failed",code:"FORM_VALIDATION_ERROR"}]};}}return {isValid:!l&&a.isValid,errors:[...f.flatMap(n=>n.errors),...a.errors]}},[i,e.values,r,d,o,t]);return {validateField:d,validateForm:m}}var K=Re.createContext(null);function M({children:i,formConfig:e,defaultValues:r={},onSubmit:t,onFieldChange:o,className:d}){let m=Re.useRef(e.id),{formState:s,setValue:F,setFieldTouched:f,reset:l,setError:a,clearError:n,setFieldValidationState:v,setSubmitting:u,isFormValid:c}=G({defaultValues:r,onFieldChange:o}),{fieldConditions:p,hasConditionalFields:b,getFieldCondition:w,isFieldVisible:R,isFieldDisabled:E,isFieldRequired:V,isFieldReadonly:I}=$({formConfig:e,formValues:s.values}),x=Re.useMemo(()=>({hasConditionalFields:b,getFieldCondition:w,isFieldVisible:R,isFieldDisabled:E,isFieldRequired:V,isFieldReadonly:I}),[b,w,R,E,V,I]),{validateField:S,validateForm:P}=H({formConfig:e,formState:s,conditionsHelpers:x,setFieldValidationState:v,setError:a}),{submit:y}=W({formState:s,onSubmit:t,validateForm:P,setSubmitting:u});Re.useEffect(()=>{(m.current===null||e.id!==m.current)&&(m.current=e.id,l(r));},[e.id,l]);let T=Re.useMemo(()=>e,[e]),q=Re.useMemo(()=>({formState:s,formConfig:T,fieldConditions:p,conditionsHelpers:x,setValue:F,setFieldTouched:f,validateField:S,validateForm:P,isFormValid:c,reset:l,submit:y,setError:a,clearError:n}),[s,T,p,x,F,f,S,P,c,l,y,a,n]);return jsxRuntime.jsx(K.Provider,{value:q,children:jsxRuntime.jsx("form",{onSubmit:y,className:d,noValidate:true,children:i})})}function g(){let i=Re.useContext(K);if(!i)throw new Error("useFormContext must be used within a FormProvider");return i}function he({formConfig:i,defaultValues:e,onSubmit:r,onFieldChange:t,children:o}){let d=Re.useMemo(()=>i instanceof h?i.build():i,[i]);return jsxRuntime.jsx(M,{formConfig:d,defaultValues:e,onSubmit:r,onFieldChange:t,children:o})}function _({fieldId:i,disabled:e=false,customProps:r={},className:t,forceVisible:o=false}){let{formState:d,formConfig:m,setValue:s,setFieldTouched:F,validateField:f,conditionsHelpers:l}=g(),a=m.allFields.find(O=>O.id===i);if(!a)throw new Error(`Field with ID "${i}" not found`);let n=m.config.getComponent(a.componentId);if(!n)throw new Error(`Component with ID "${a.componentId}" not found`);let v=d.values[i],u=d.errors[i]||[],c=d.validationState[i]||"idle",p=d.touched[i]||false,b=c==="validating",w=o||l.isFieldVisible(i),R=e||l.isFieldDisabled(i),E=l.isFieldRequired(i),V=l.isFieldReadonly(i);if(!w)return null;let I=Re.useCallback(async O=>{s(i,O),(a.validation?.validateOnChange||p)&&await f(i,O);},[i,s,f,a.validation?.validateOnChange,p]),x=Re.useCallback(async()=>{p||F(i),a.validation?.validateOnBlur!==false&&await f(i);},[i,p,F,f,a.validation?.validateOnBlur]),S={...n.defaultProps??{},...a.props,...r,disabled:R,required:E,readOnly:V},P={id:i,props:S,value:v,onChange:I,onBlur:x,disabled:R,error:u,isValidating:b,touched:p},y=m.renderConfig?.fieldRenderer,T=n.renderer(P),q=n.useFieldRenderer!==false,oe=y&&q?y({children:T,id:i,...S,error:u,isValidating:b,touched:p}):T;return jsxRuntime.jsx("div",{className:t,"data-field-id":i,"data-field-type":n.type,"data-field-visible":w,"data-field-disabled":R,"data-field-required":E,"data-field-readonly":V,children:oe})}Re__default.default.memo(_);function Z({row:i,className:e,...r}){let{formConfig:t}=g(),o=i.fields.map(m=>jsxRuntime.jsx(_,{fieldId:m.id},m.id)),d={row:i,children:o,className:e};return jsxRuntime.jsx(core.ComponentRendererWrapper,{name:"FormRow",renderer:t.renderConfig?.rowRenderer,props:d,...r,children:o})}var ee=Z;function Ve({className:i,...e}){let{formConfig:r}=g(),t=Re.useMemo(()=>r.rows.map(d=>jsxRuntime.jsx(ee,{row:d},d.id)),[r.rows]),o={formConfig:r,children:t,className:i};return jsxRuntime.jsx(core.ComponentRendererWrapper,{name:"FormBody",renderer:r.renderConfig?.bodyRenderer,props:o,...e,children:t})}function Se({className:i,...e}){let{formState:r,submit:t,formConfig:o}=g(),d={isSubmitting:r.isSubmitting,onSubmit:t,className:i};return jsxRuntime.jsx(core.ComponentRendererWrapper,{name:"FormSubmitButton",renderer:o.renderConfig?.submitButtonRenderer,props:d,...e})}exports.Form=he;exports.FormBody=Ve;exports.FormBuilder=h;exports.FormField=_;exports.FormProvider=M;exports.FormRow=Z;exports.FormSubmitButton=Se;exports.createForm=ae;exports.form=h;exports.useConditionEvaluation=De;exports.useFormConditions=$;exports.useFormContext=g;exports.useFormState=G;exports.useFormSubmission=W;exports.useFormValidation=H;exports.useMultipleConditionEvaluation=L;Object.keys(core).forEach(function(k){if(k!=='default'&&!Object.prototype.hasOwnProperty.call(exports,k))Object.defineProperty(exports,k,{enumerable:true,get:function(){return core[k]}})});
1
+ 'use strict';var Re=require('react'),core=require('@rilaykit/core'),jsxRuntime=require('react/jsx-runtime');function _interopDefault(e){return e&&e.__esModule?e:{default:e}}var Re__default=/*#__PURE__*/_interopDefault(Re);var h=class i{constructor(e,o){this.rows=[];this.idGenerator=new core.IdGenerator;this.config=e,this.formId=o||`form-${Math.random().toString(36).substring(2,15)}`;}static create(e,o){return new i(e,o)}createFormField(e){let o=this.config.getComponent(e.type);if(!o)throw new Error(`No component found with type "${e.type}"`);let t;return (o.validation||e.validation)&&(t={validateOnChange:e.validation?.validateOnChange??o.validation?.validateOnChange,validateOnBlur:e.validation?.validateOnBlur??o.validation?.validateOnBlur,debounceMs:e.validation?.debounceMs??o.validation?.debounceMs,validators:[...o.validation?.validators||[],...e.validation?.validators||[]]}),{id:e.id||this.idGenerator.next("field"),componentId:o.id,props:{...o.defaultProps,...e.props},validation:t,conditions:e.conditions}}createRow(e){if(e.length===0)throw new Error("At least one field is required");if(e.length>3)throw new Error("Maximum 3 fields per row");let o=e.map(t=>this.createFormField(t));return {id:this.idGenerator.next("row"),fields:o,maxColumns:e.length}}add(...e){let o,t=false;if(e.length===1&&Array.isArray(e[0])?(o=e[0],t=true):o=e,o.length===0)throw new Error("At least one field is required");if(t&&o.length>3)throw new Error("Maximum 3 fields per row");if(o.length===1){let r=this.createRow(o);return this.rows.push(r),this}if(o.length<=3){let r=this.createRow(o);return this.rows.push(r),this}for(let r of o){let d=this.createRow([r]);this.rows.push(d);}return this}addSeparateRows(e){for(let o of e)this.add(o);return this}setId(e){return this.formId=e,this}updateField(e,o){let t=this.findField(e);if(!t)throw new Error(`Field with ID "${e}" not found`);return Object.assign(t,{...o,props:{...t.props,...o.props}}),this}findField(e){for(let o of this.rows){let t=o.fields.find(r=>r.id===e);if(t)return t}return null}removeField(e){return this.rows=this.rows.map(o=>({...o,fields:o.fields.filter(t=>t.id!==e)})).filter(o=>o.fields.length>0),this}getField(e){return this.findField(e)||void 0}getFields(){return this.rows.flatMap(e=>e.fields)}getRows(){return [...this.rows]}clear(){return this.rows=[],this.idGenerator.reset(),this}setValidation(e){return this.formValidation=e,this}addValidators(e){return this.formValidation||(this.formValidation={validators:[]}),this.formValidation={...this.formValidation,validators:[...this.formValidation.validators||[],...e]},this}addFieldValidation(e,o){let t=this.findField(e);if(!t)throw new Error(`Field with ID "${e}" not found`);let r={...t.validation,...o,validators:[...t.validation?.validators||[],...o.validators||[]]};return this.updateField(e,{validation:r})}addFieldConditions(e,o){let t=this.findField(e);if(!t)throw new Error(`Field with ID "${e}" not found`);let r={...t.conditions,...o};return this.updateField(e,{conditions:r})}clone(e){let o=new i(this.config,e||`${this.formId}-clone`);return o.rows=core.deepClone(this.rows),o}validate(){let e=[],o=this.getFields(),t=o.map(r=>r.id);try{core.ensureUnique(t,"field");}catch(r){e.push(r instanceof Error?r.message:String(r));}for(let r of o)this.config.hasComponent(r.componentId)||e.push(`Component "${r.componentId}" not found for field "${r.id}"`);for(let r of this.rows)r.fields.length>3&&e.push(`Row "${r.id}" has ${r.fields.length} fields, maximum is 3`),r.fields.length===0&&e.push(`Row "${r.id}" is empty`);return e}build(){let e=this.validate();if(e.length>0)throw new Error(`Form validation failed: ${e.join(", ")}`);return {id:this.formId,rows:[...this.rows],allFields:this.getFields(),config:this.config,renderConfig:this.config.getFormRenderConfig(),validation:this.formValidation}}toJSON(){return {id:this.formId,rows:this.rows}}fromJSON(e){return e.id&&(this.formId=e.id),e.rows&&(this.rows=e.rows),this}getStats(){let e=this.getFields(),o=this.rows.map(t=>t.fields.length);return {totalFields:e.length,totalRows:this.rows.length,averageFieldsPerRow:this.rows.length>0?e.length/this.rows.length:0,maxFieldsInRow:o.length>0?Math.max(...o):0,minFieldsInRow:o.length>0?Math.min(...o):0}}};function ae(i,e){return h.create(i,e)}core.ril.prototype.form=function(i){return h.create(this,i)};function De(i,e={},o={}){return Re.useMemo(()=>{if(!i)return {visible:o.visible??true,disabled:o.disabled??false,required:o.required??false,readonly:o.readonly??false};let t=r=>{try{return r&&typeof r=="object"&&"build"in r?core.evaluateCondition(r.build(),e):core.evaluateCondition(r,e)}catch(d){return console.warn("Error evaluating condition:",d),false}};return {visible:i.visible?t(i.visible):o.visible??true,disabled:i.disabled?t(i.disabled):o.disabled??false,required:i.required?t(i.required):o.required??false,readonly:i.readonly?t(i.readonly):o.readonly??false}},[i,e,o])}function L(i,e={}){return Re.useMemo(()=>{let o={};for(let[t,r]of Object.entries(i))if(o[t]={visible:true,disabled:false,required:false,readonly:false},r){let d=m=>{try{return m&&typeof m=="object"&&"build"in m?core.evaluateCondition(m.build(),e):core.evaluateCondition(m,e)}catch(s){return console.warn(`Error evaluating condition for field ${t}:`,s),false}};o[t]={visible:r.visible?d(r.visible):true,disabled:r.disabled?d(r.disabled):false,required:r.required?d(r.required):false,readonly:r.readonly?d(r.readonly):false};}return o},[i,e])}function $({formConfig:i,formValues:e}){let o=Re.useMemo(()=>{let l={};for(let a of i.allFields)a.conditions&&(l[a.id]=a.conditions);return l},[i.allFields]),t=Re.useMemo(()=>Object.keys(o).length>0,[o]),r=L(t?o:{},t?e:{}),d=Re.useCallback(l=>r[l],[r]),m=Re.useCallback(l=>{let a=r[l];return a?a.visible:true},[r]),s=Re.useCallback(l=>{let a=r[l];return a?a.disabled:false},[r]),F=Re.useCallback(l=>{let a=r[l];return a?a.required:false},[r]),f=Re.useCallback(l=>{let a=r[l];return a?a.readonly:false},[r]);return Re.useMemo(()=>({fieldConditions:r,hasConditionalFields:t,getFieldCondition:d,isFieldVisible:m,isFieldDisabled:s,isFieldRequired:F,isFieldReadonly:f}),[r,t,d,m,s,F,f])}function ue(i,e){switch(e.type){case "SET_VALUE":return {...i,values:{...i.values,[e.fieldId]:e.value},isDirty:true};case "SET_FIELD_ERRORS":return {...i,errors:{...i.errors,[e.fieldId]:e.errors}};case "SET_FIELD_VALIDATION_STATE":return {...i,validationState:{...i.validationState,[e.fieldId]:e.state}};case "SET_FIELD_TOUCHED":return {...i,touched:{...i.touched,[e.fieldId]:true}};case "SET_SUBMITTING":return {...i,isSubmitting:e.isSubmitting};case "RESET":return {values:e.values||{},errors:{},validationState:{},touched:{},isDirty:false,isSubmitting:false};default:return i}}function G({defaultValues:i={},onFieldChange:e}){let o={values:i,errors:{},validationState:{},touched:{},isDirty:false,isSubmitting:false},[t,r]=Re.useReducer(ue,o),d=Re.useRef(e);d.current=e;let m=Re.useCallback((u,c)=>{r({type:"SET_VALUE",fieldId:u,value:c}),d.current?.(u,c,{...t.values,[u]:c});},[t.values]),s=Re.useCallback(u=>{r({type:"SET_FIELD_TOUCHED",fieldId:u});},[]),F=Re.useCallback((u,c)=>{r({type:"SET_FIELD_ERRORS",fieldId:u,errors:c});},[]),f=Re.useCallback(u=>{r({type:"SET_FIELD_ERRORS",fieldId:u,errors:[]});},[]),l=Re.useCallback((u,c)=>{r({type:"SET_FIELD_VALIDATION_STATE",fieldId:u,state:c});},[]),a=Re.useCallback(u=>{r({type:"SET_SUBMITTING",isSubmitting:u});},[]),n=Re.useCallback(u=>{r({type:"RESET",values:u});},[]),g=Re.useCallback(()=>{let u=Object.values(t.errors).some(p=>p.length>0),c=Object.values(t.validationState).some(p=>p==="invalid");return !u&&!c},[t.errors,t.validationState]);return {formState:t,setValue:m,setFieldTouched:s,setError:F,clearError:f,setFieldValidationState:l,setSubmitting:a,reset:n,isFormValid:g}}function W({formState:i,onSubmit:e,validateForm:o,setSubmitting:t}){let r=Re.useRef(e);return r.current=e,{submit:Re.useCallback(async m=>{m?.preventDefault();try{return t(!0),(await o()).isValid?(r.current&&await r.current(i.values),!0):!1}catch(s){return console.error("Error during form submission:",s),false}finally{t(false);}},[i.values,o,t])}}function k(){return {isValid:true,errors:[]}}function H({formConfig:i,formState:e,conditionsHelpers:o,setFieldValidationState:t,setError:r}){let d=Re.useCallback(async(s,F)=>{let f=i.allFields.find(n=>n.id===s);if(!f)return k();if(!o.isFieldVisible(s))return r(s,[]),t(s,"valid"),k();if(!f.validation?.validators?.length)return r(s,[]),t(s,"valid"),k();let l=F!==void 0?F:e.values[s],a=core.createValidationContext({fieldId:s,formId:i.id,allFormData:{...e.values,[s]:l}});t(s,"validating");try{let n=await core.runValidatorsAsync(f.validation.validators,l,a),g=o.isFieldRequired(s),u=l==null||l==="";if(g&&u&&!n.errors.some(p=>p.code==="REQUIRED"||p.message.toLowerCase().includes("required"))){let p={isValid:!1,errors:[{message:"This field is required",code:"CONDITIONAL_REQUIRED"},...n.errors]};return r(s,p.errors),t(s,"invalid"),p}return r(s,n.errors),t(s,n.isValid?"valid":"invalid"),n}catch(n){let g={isValid:false,errors:[{message:n instanceof Error?n.message:"Validation failed",code:"VALIDATION_ERROR"}]};return r(s,g.errors),t(s,"invalid"),g}},[i,e.values,o,t,r]),m=Re.useCallback(async()=>{let s=i.allFields.filter(n=>{let g=o.isFieldVisible(n.id),u=n.validation?.validators&&n.validation.validators.length>0;return g&&u}),F=i.allFields.filter(n=>!o.isFieldVisible(n.id));for(let n of F)r(n.id,[]),t(n.id,"valid");let f=await Promise.all(s.map(n=>d(n.id))),l=f.some(n=>!n.isValid),a=k();if(i.validation?.validators?.length){let n=Object.keys(e.values).reduce((u,c)=>(o.isFieldVisible(c)&&(u[c]=e.values[c]),u),{}),g=core.createValidationContext({formId:i.id,allFormData:n});try{a=await core.runValidatorsAsync(i.validation.validators,n,g);}catch(u){a={isValid:false,errors:[{message:u instanceof Error?u.message:"Form validation failed",code:"FORM_VALIDATION_ERROR"}]};}}return {isValid:!l&&a.isValid,errors:[...f.flatMap(n=>n.errors),...a.errors]}},[i,e.values,o,d,r,t]);return {validateField:d,validateForm:m}}var K=Re.createContext(null);function M({children:i,formConfig:e,defaultValues:o={},onSubmit:t,onFieldChange:r,className:d}){let m=Re.useRef(e.id),{formState:s,setValue:F,setFieldTouched:f,reset:l,setError:a,clearError:n,setFieldValidationState:g,setSubmitting:u,isFormValid:c}=G({defaultValues:o,onFieldChange:r}),{fieldConditions:p,hasConditionalFields:b,getFieldCondition:w,isFieldVisible:R,isFieldDisabled:E,isFieldRequired:x,isFieldReadonly:I}=$({formConfig:e,formValues:s.values}),V=Re.useMemo(()=>({hasConditionalFields:b,getFieldCondition:w,isFieldVisible:R,isFieldDisabled:E,isFieldRequired:x,isFieldReadonly:I}),[b,w,R,E,x,I]),{validateField:S,validateForm:P}=H({formConfig:e,formState:s,conditionsHelpers:V,setFieldValidationState:g,setError:a}),{submit:y}=W({formState:s,onSubmit:t,validateForm:P,setSubmitting:u});Re.useEffect(()=>{(m.current===null||e.id!==m.current)&&(m.current=e.id,l(o));},[e.id,l]);let T=Re.useMemo(()=>e,[e]),q=Re.useMemo(()=>({formState:s,formConfig:T,fieldConditions:p,conditionsHelpers:V,setValue:F,setFieldTouched:f,validateField:S,validateForm:P,isFormValid:c,reset:l,submit:y,setError:a,clearError:n}),[s,T,p,V,F,f,S,P,c,l,y,a,n]);return jsxRuntime.jsx(K.Provider,{value:q,children:jsxRuntime.jsx("form",{onSubmit:y,className:d,noValidate:true,children:i})})}function v(){let i=Re.useContext(K);if(!i)throw new Error("useFormContext must be used within a FormProvider");return i}function he({formConfig:i,defaultValues:e,onSubmit:o,onFieldChange:t,children:r}){let d=Re.useMemo(()=>i instanceof h?i.build():i,[i]);return jsxRuntime.jsx(M,{formConfig:d,defaultValues:e,onSubmit:o,onFieldChange:t,children:r})}function _({fieldId:i,disabled:e=false,customProps:o={},className:t,forceVisible:r=false}){let{formState:d,formConfig:m,setValue:s,setFieldTouched:F,validateField:f,conditionsHelpers:l}=v(),a=m.allFields.find(O=>O.id===i);if(!a)throw new Error(`Field with ID "${i}" not found`);let n=m.config.getComponent(a.componentId);if(!n)throw new Error(`Component with ID "${a.componentId}" not found`);let g=d.values[i],u=d.errors[i]||[],c=d.validationState[i]||"idle",p=d.touched[i]||false,b=c==="validating",w=r||l.isFieldVisible(i),R=e||l.isFieldDisabled(i),E=l.isFieldRequired(i),x=l.isFieldReadonly(i);if(!w)return null;let I=Re.useCallback(async O=>{s(i,O),(a.validation?.validateOnChange||p)&&await f(i,O);},[i,s,f,a.validation?.validateOnChange,p]),V=Re.useCallback(async()=>{p||F(i),a.validation?.validateOnBlur!==false&&await f(i);},[i,p,F,f,a.validation?.validateOnBlur]),S={...n.defaultProps??{},...a.props,...o,disabled:R,required:E,readOnly:x},P={id:i,props:S,value:g,onChange:I,onBlur:V,disabled:R,error:u,isValidating:b,touched:p},y=m.renderConfig?.fieldRenderer,T=n.renderer(P),q=n.useFieldRenderer!==false,re=y&&q?y({children:T,id:i,...S,error:u,isValidating:b,touched:p}):T;return jsxRuntime.jsx("div",{className:t,"data-field-id":i,"data-field-type":n.type,"data-field-visible":w,"data-field-disabled":R,"data-field-required":E,"data-field-readonly":x,children:re})}Re__default.default.memo(_);function Z({row:i,className:e,...o}){let{formConfig:t}=v(),r=i.fields.map(m=>jsxRuntime.jsx(_,{fieldId:m.id},m.id)),d={row:i,children:r,className:e};return jsxRuntime.jsx(core.ComponentRendererWrapper,{name:"FormRow",renderer:t.renderConfig?.rowRenderer,props:d,...o,children:r})}var ee=Z;function xe({className:i,...e}){let{formConfig:o}=v(),t=Re.useMemo(()=>o.rows.map(d=>jsxRuntime.jsx(ee,{row:d},d.id)),[o.rows]),r={formConfig:o,children:t,className:i};return jsxRuntime.jsx(core.ComponentRendererWrapper,{name:"FormBody",renderer:o.renderConfig?.bodyRenderer,props:r,...e,children:t})}function Se({className:i,isSubmitting:e,...o}){let{formState:t,submit:r,formConfig:d}=v(),m={isSubmitting:e??t.isSubmitting,onSubmit:r,className:i};return jsxRuntime.jsx(core.ComponentRendererWrapper,{name:"FormSubmitButton",renderer:d.renderConfig?.submitButtonRenderer,props:m,...o})}exports.Form=he;exports.FormBody=xe;exports.FormBuilder=h;exports.FormField=_;exports.FormProvider=M;exports.FormRow=Z;exports.FormSubmitButton=Se;exports.createForm=ae;exports.form=h;exports.useConditionEvaluation=De;exports.useFormConditions=$;exports.useFormContext=v;exports.useFormState=G;exports.useFormSubmission=W;exports.useFormValidation=H;exports.useMultipleConditionEvaluation=L;Object.keys(core).forEach(function(k){if(k!=='default'&&!Object.prototype.hasOwnProperty.call(exports,k))Object.defineProperty(exports,k,{enumerable:true,get:function(){return core[k]}})});
package/dist/index.mjs CHANGED
@@ -1 +1 @@
1
- import Re,{createContext,useCallback,useContext,useMemo,useReducer,useRef,useEffect}from'react';import {ril,IdGenerator,deepClone,ensureUnique,createValidationContext,runValidatorsAsync,ComponentRendererWrapper,evaluateCondition}from'@rilaykit/core';export*from'@rilaykit/core';import {jsx}from'react/jsx-runtime';var h=class i{constructor(e,r){this.rows=[];this.idGenerator=new IdGenerator;this.config=e,this.formId=r||`form-${Math.random().toString(36).substring(2,15)}`;}static create(e,r){return new i(e,r)}createFormField(e){let r=this.config.getComponent(e.type);if(!r)throw new Error(`No component found with type "${e.type}"`);let t;return (r.validation||e.validation)&&(t={validateOnChange:e.validation?.validateOnChange??r.validation?.validateOnChange,validateOnBlur:e.validation?.validateOnBlur??r.validation?.validateOnBlur,debounceMs:e.validation?.debounceMs??r.validation?.debounceMs,validators:[...r.validation?.validators||[],...e.validation?.validators||[]]}),{id:e.id||this.idGenerator.next("field"),componentId:r.id,props:{...r.defaultProps,...e.props},validation:t,conditions:e.conditions}}createRow(e){if(e.length===0)throw new Error("At least one field is required");if(e.length>3)throw new Error("Maximum 3 fields per row");let r=e.map(t=>this.createFormField(t));return {id:this.idGenerator.next("row"),fields:r,maxColumns:e.length}}add(...e){let r,t=false;if(e.length===1&&Array.isArray(e[0])?(r=e[0],t=true):r=e,r.length===0)throw new Error("At least one field is required");if(t&&r.length>3)throw new Error("Maximum 3 fields per row");if(r.length===1){let o=this.createRow(r);return this.rows.push(o),this}if(r.length<=3){let o=this.createRow(r);return this.rows.push(o),this}for(let o of r){let d=this.createRow([o]);this.rows.push(d);}return this}addSeparateRows(e){for(let r of e)this.add(r);return this}setId(e){return this.formId=e,this}updateField(e,r){let t=this.findField(e);if(!t)throw new Error(`Field with ID "${e}" not found`);return Object.assign(t,{...r,props:{...t.props,...r.props}}),this}findField(e){for(let r of this.rows){let t=r.fields.find(o=>o.id===e);if(t)return t}return null}removeField(e){return this.rows=this.rows.map(r=>({...r,fields:r.fields.filter(t=>t.id!==e)})).filter(r=>r.fields.length>0),this}getField(e){return this.findField(e)||void 0}getFields(){return this.rows.flatMap(e=>e.fields)}getRows(){return [...this.rows]}clear(){return this.rows=[],this.idGenerator.reset(),this}setValidation(e){return this.formValidation=e,this}addValidators(e){return this.formValidation||(this.formValidation={validators:[]}),this.formValidation={...this.formValidation,validators:[...this.formValidation.validators||[],...e]},this}addFieldValidation(e,r){let t=this.findField(e);if(!t)throw new Error(`Field with ID "${e}" not found`);let o={...t.validation,...r,validators:[...t.validation?.validators||[],...r.validators||[]]};return this.updateField(e,{validation:o})}addFieldConditions(e,r){let t=this.findField(e);if(!t)throw new Error(`Field with ID "${e}" not found`);let o={...t.conditions,...r};return this.updateField(e,{conditions:o})}clone(e){let r=new i(this.config,e||`${this.formId}-clone`);return r.rows=deepClone(this.rows),r}validate(){let e=[],r=this.getFields(),t=r.map(o=>o.id);try{ensureUnique(t,"field");}catch(o){e.push(o instanceof Error?o.message:String(o));}for(let o of r)this.config.hasComponent(o.componentId)||e.push(`Component "${o.componentId}" not found for field "${o.id}"`);for(let o of this.rows)o.fields.length>3&&e.push(`Row "${o.id}" has ${o.fields.length} fields, maximum is 3`),o.fields.length===0&&e.push(`Row "${o.id}" is empty`);return e}build(){let e=this.validate();if(e.length>0)throw new Error(`Form validation failed: ${e.join(", ")}`);return {id:this.formId,rows:[...this.rows],allFields:this.getFields(),config:this.config,renderConfig:this.config.getFormRenderConfig(),validation:this.formValidation}}toJSON(){return {id:this.formId,rows:this.rows}}fromJSON(e){return e.id&&(this.formId=e.id),e.rows&&(this.rows=e.rows),this}getStats(){let e=this.getFields(),r=this.rows.map(t=>t.fields.length);return {totalFields:e.length,totalRows:this.rows.length,averageFieldsPerRow:this.rows.length>0?e.length/this.rows.length:0,maxFieldsInRow:r.length>0?Math.max(...r):0,minFieldsInRow:r.length>0?Math.min(...r):0}}};function ae(i,e){return h.create(i,e)}ril.prototype.form=function(i){return h.create(this,i)};function De(i,e={},r={}){return useMemo(()=>{if(!i)return {visible:r.visible??true,disabled:r.disabled??false,required:r.required??false,readonly:r.readonly??false};let t=o=>{try{return o&&typeof o=="object"&&"build"in o?evaluateCondition(o.build(),e):evaluateCondition(o,e)}catch(d){return console.warn("Error evaluating condition:",d),false}};return {visible:i.visible?t(i.visible):r.visible??true,disabled:i.disabled?t(i.disabled):r.disabled??false,required:i.required?t(i.required):r.required??false,readonly:i.readonly?t(i.readonly):r.readonly??false}},[i,e,r])}function L(i,e={}){return useMemo(()=>{let r={};for(let[t,o]of Object.entries(i))if(r[t]={visible:true,disabled:false,required:false,readonly:false},o){let d=m=>{try{return m&&typeof m=="object"&&"build"in m?evaluateCondition(m.build(),e):evaluateCondition(m,e)}catch(s){return console.warn(`Error evaluating condition for field ${t}:`,s),false}};r[t]={visible:o.visible?d(o.visible):true,disabled:o.disabled?d(o.disabled):false,required:o.required?d(o.required):false,readonly:o.readonly?d(o.readonly):false};}return r},[i,e])}function $({formConfig:i,formValues:e}){let r=useMemo(()=>{let l={};for(let a of i.allFields)a.conditions&&(l[a.id]=a.conditions);return l},[i.allFields]),t=useMemo(()=>Object.keys(r).length>0,[r]),o=L(t?r:{},t?e:{}),d=useCallback(l=>o[l],[o]),m=useCallback(l=>{let a=o[l];return a?a.visible:true},[o]),s=useCallback(l=>{let a=o[l];return a?a.disabled:false},[o]),F=useCallback(l=>{let a=o[l];return a?a.required:false},[o]),f=useCallback(l=>{let a=o[l];return a?a.readonly:false},[o]);return useMemo(()=>({fieldConditions:o,hasConditionalFields:t,getFieldCondition:d,isFieldVisible:m,isFieldDisabled:s,isFieldRequired:F,isFieldReadonly:f}),[o,t,d,m,s,F,f])}function ue(i,e){switch(e.type){case "SET_VALUE":return {...i,values:{...i.values,[e.fieldId]:e.value},isDirty:true};case "SET_FIELD_ERRORS":return {...i,errors:{...i.errors,[e.fieldId]:e.errors}};case "SET_FIELD_VALIDATION_STATE":return {...i,validationState:{...i.validationState,[e.fieldId]:e.state}};case "SET_FIELD_TOUCHED":return {...i,touched:{...i.touched,[e.fieldId]:true}};case "SET_SUBMITTING":return {...i,isSubmitting:e.isSubmitting};case "RESET":return {values:e.values||{},errors:{},validationState:{},touched:{},isDirty:false,isSubmitting:false};default:return i}}function G({defaultValues:i={},onFieldChange:e}){let r={values:i,errors:{},validationState:{},touched:{},isDirty:false,isSubmitting:false},[t,o]=useReducer(ue,r),d=useRef(e);d.current=e;let m=useCallback((u,c)=>{o({type:"SET_VALUE",fieldId:u,value:c}),d.current?.(u,c,{...t.values,[u]:c});},[t.values]),s=useCallback(u=>{o({type:"SET_FIELD_TOUCHED",fieldId:u});},[]),F=useCallback((u,c)=>{o({type:"SET_FIELD_ERRORS",fieldId:u,errors:c});},[]),f=useCallback(u=>{o({type:"SET_FIELD_ERRORS",fieldId:u,errors:[]});},[]),l=useCallback((u,c)=>{o({type:"SET_FIELD_VALIDATION_STATE",fieldId:u,state:c});},[]),a=useCallback(u=>{o({type:"SET_SUBMITTING",isSubmitting:u});},[]),n=useCallback(u=>{o({type:"RESET",values:u});},[]),v=useCallback(()=>{let u=Object.values(t.errors).some(p=>p.length>0),c=Object.values(t.validationState).some(p=>p==="invalid");return !u&&!c},[t.errors,t.validationState]);return {formState:t,setValue:m,setFieldTouched:s,setError:F,clearError:f,setFieldValidationState:l,setSubmitting:a,reset:n,isFormValid:v}}function W({formState:i,onSubmit:e,validateForm:r,setSubmitting:t}){let o=useRef(e);return o.current=e,{submit:useCallback(async m=>{m?.preventDefault();try{return t(!0),(await r()).isValid?(o.current&&await o.current(i.values),!0):!1}catch(s){return console.error("Error during form submission:",s),false}finally{t(false);}},[i.values,r,t])}}function k(){return {isValid:true,errors:[]}}function H({formConfig:i,formState:e,conditionsHelpers:r,setFieldValidationState:t,setError:o}){let d=useCallback(async(s,F)=>{let f=i.allFields.find(n=>n.id===s);if(!f)return k();if(!r.isFieldVisible(s))return o(s,[]),t(s,"valid"),k();if(!f.validation?.validators?.length)return o(s,[]),t(s,"valid"),k();let l=F!==void 0?F:e.values[s],a=createValidationContext({fieldId:s,formId:i.id,allFormData:{...e.values,[s]:l}});t(s,"validating");try{let n=await runValidatorsAsync(f.validation.validators,l,a),v=r.isFieldRequired(s),u=l==null||l==="";if(v&&u&&!n.errors.some(p=>p.code==="REQUIRED"||p.message.toLowerCase().includes("required"))){let p={isValid:!1,errors:[{message:"This field is required",code:"CONDITIONAL_REQUIRED"},...n.errors]};return o(s,p.errors),t(s,"invalid"),p}return o(s,n.errors),t(s,n.isValid?"valid":"invalid"),n}catch(n){let v={isValid:false,errors:[{message:n instanceof Error?n.message:"Validation failed",code:"VALIDATION_ERROR"}]};return o(s,v.errors),t(s,"invalid"),v}},[i,e.values,r,t,o]),m=useCallback(async()=>{let s=i.allFields.filter(n=>{let v=r.isFieldVisible(n.id),u=n.validation?.validators&&n.validation.validators.length>0;return v&&u}),F=i.allFields.filter(n=>!r.isFieldVisible(n.id));for(let n of F)o(n.id,[]),t(n.id,"valid");let f=await Promise.all(s.map(n=>d(n.id))),l=f.some(n=>!n.isValid),a=k();if(i.validation?.validators?.length){let n=Object.keys(e.values).reduce((u,c)=>(r.isFieldVisible(c)&&(u[c]=e.values[c]),u),{}),v=createValidationContext({formId:i.id,allFormData:n});try{a=await runValidatorsAsync(i.validation.validators,n,v);}catch(u){a={isValid:false,errors:[{message:u instanceof Error?u.message:"Form validation failed",code:"FORM_VALIDATION_ERROR"}]};}}return {isValid:!l&&a.isValid,errors:[...f.flatMap(n=>n.errors),...a.errors]}},[i,e.values,r,d,o,t]);return {validateField:d,validateForm:m}}var K=createContext(null);function M({children:i,formConfig:e,defaultValues:r={},onSubmit:t,onFieldChange:o,className:d}){let m=useRef(e.id),{formState:s,setValue:F,setFieldTouched:f,reset:l,setError:a,clearError:n,setFieldValidationState:v,setSubmitting:u,isFormValid:c}=G({defaultValues:r,onFieldChange:o}),{fieldConditions:p,hasConditionalFields:b,getFieldCondition:w,isFieldVisible:R,isFieldDisabled:E,isFieldRequired:V,isFieldReadonly:I}=$({formConfig:e,formValues:s.values}),x=useMemo(()=>({hasConditionalFields:b,getFieldCondition:w,isFieldVisible:R,isFieldDisabled:E,isFieldRequired:V,isFieldReadonly:I}),[b,w,R,E,V,I]),{validateField:S,validateForm:P}=H({formConfig:e,formState:s,conditionsHelpers:x,setFieldValidationState:v,setError:a}),{submit:y}=W({formState:s,onSubmit:t,validateForm:P,setSubmitting:u});useEffect(()=>{(m.current===null||e.id!==m.current)&&(m.current=e.id,l(r));},[e.id,l]);let T=useMemo(()=>e,[e]),q=useMemo(()=>({formState:s,formConfig:T,fieldConditions:p,conditionsHelpers:x,setValue:F,setFieldTouched:f,validateField:S,validateForm:P,isFormValid:c,reset:l,submit:y,setError:a,clearError:n}),[s,T,p,x,F,f,S,P,c,l,y,a,n]);return jsx(K.Provider,{value:q,children:jsx("form",{onSubmit:y,className:d,noValidate:true,children:i})})}function g(){let i=useContext(K);if(!i)throw new Error("useFormContext must be used within a FormProvider");return i}function he({formConfig:i,defaultValues:e,onSubmit:r,onFieldChange:t,children:o}){let d=useMemo(()=>i instanceof h?i.build():i,[i]);return jsx(M,{formConfig:d,defaultValues:e,onSubmit:r,onFieldChange:t,children:o})}function _({fieldId:i,disabled:e=false,customProps:r={},className:t,forceVisible:o=false}){let{formState:d,formConfig:m,setValue:s,setFieldTouched:F,validateField:f,conditionsHelpers:l}=g(),a=m.allFields.find(O=>O.id===i);if(!a)throw new Error(`Field with ID "${i}" not found`);let n=m.config.getComponent(a.componentId);if(!n)throw new Error(`Component with ID "${a.componentId}" not found`);let v=d.values[i],u=d.errors[i]||[],c=d.validationState[i]||"idle",p=d.touched[i]||false,b=c==="validating",w=o||l.isFieldVisible(i),R=e||l.isFieldDisabled(i),E=l.isFieldRequired(i),V=l.isFieldReadonly(i);if(!w)return null;let I=useCallback(async O=>{s(i,O),(a.validation?.validateOnChange||p)&&await f(i,O);},[i,s,f,a.validation?.validateOnChange,p]),x=useCallback(async()=>{p||F(i),a.validation?.validateOnBlur!==false&&await f(i);},[i,p,F,f,a.validation?.validateOnBlur]),S={...n.defaultProps??{},...a.props,...r,disabled:R,required:E,readOnly:V},P={id:i,props:S,value:v,onChange:I,onBlur:x,disabled:R,error:u,isValidating:b,touched:p},y=m.renderConfig?.fieldRenderer,T=n.renderer(P),q=n.useFieldRenderer!==false,oe=y&&q?y({children:T,id:i,...S,error:u,isValidating:b,touched:p}):T;return jsx("div",{className:t,"data-field-id":i,"data-field-type":n.type,"data-field-visible":w,"data-field-disabled":R,"data-field-required":E,"data-field-readonly":V,children:oe})}Re.memo(_);function Z({row:i,className:e,...r}){let{formConfig:t}=g(),o=i.fields.map(m=>jsx(_,{fieldId:m.id},m.id)),d={row:i,children:o,className:e};return jsx(ComponentRendererWrapper,{name:"FormRow",renderer:t.renderConfig?.rowRenderer,props:d,...r,children:o})}var ee=Z;function Ve({className:i,...e}){let{formConfig:r}=g(),t=useMemo(()=>r.rows.map(d=>jsx(ee,{row:d},d.id)),[r.rows]),o={formConfig:r,children:t,className:i};return jsx(ComponentRendererWrapper,{name:"FormBody",renderer:r.renderConfig?.bodyRenderer,props:o,...e,children:t})}function Se({className:i,...e}){let{formState:r,submit:t,formConfig:o}=g(),d={isSubmitting:r.isSubmitting,onSubmit:t,className:i};return jsx(ComponentRendererWrapper,{name:"FormSubmitButton",renderer:o.renderConfig?.submitButtonRenderer,props:d,...e})}export{he as Form,Ve as FormBody,h as FormBuilder,_ as FormField,M as FormProvider,Z as FormRow,Se as FormSubmitButton,ae as createForm,h as form,De as useConditionEvaluation,$ as useFormConditions,g as useFormContext,G as useFormState,W as useFormSubmission,H as useFormValidation,L as useMultipleConditionEvaluation};
1
+ import Re,{createContext,useCallback,useContext,useMemo,useReducer,useRef,useEffect}from'react';import {ril,IdGenerator,deepClone,ensureUnique,createValidationContext,runValidatorsAsync,ComponentRendererWrapper,evaluateCondition}from'@rilaykit/core';export*from'@rilaykit/core';import {jsx}from'react/jsx-runtime';var h=class i{constructor(e,o){this.rows=[];this.idGenerator=new IdGenerator;this.config=e,this.formId=o||`form-${Math.random().toString(36).substring(2,15)}`;}static create(e,o){return new i(e,o)}createFormField(e){let o=this.config.getComponent(e.type);if(!o)throw new Error(`No component found with type "${e.type}"`);let t;return (o.validation||e.validation)&&(t={validateOnChange:e.validation?.validateOnChange??o.validation?.validateOnChange,validateOnBlur:e.validation?.validateOnBlur??o.validation?.validateOnBlur,debounceMs:e.validation?.debounceMs??o.validation?.debounceMs,validators:[...o.validation?.validators||[],...e.validation?.validators||[]]}),{id:e.id||this.idGenerator.next("field"),componentId:o.id,props:{...o.defaultProps,...e.props},validation:t,conditions:e.conditions}}createRow(e){if(e.length===0)throw new Error("At least one field is required");if(e.length>3)throw new Error("Maximum 3 fields per row");let o=e.map(t=>this.createFormField(t));return {id:this.idGenerator.next("row"),fields:o,maxColumns:e.length}}add(...e){let o,t=false;if(e.length===1&&Array.isArray(e[0])?(o=e[0],t=true):o=e,o.length===0)throw new Error("At least one field is required");if(t&&o.length>3)throw new Error("Maximum 3 fields per row");if(o.length===1){let r=this.createRow(o);return this.rows.push(r),this}if(o.length<=3){let r=this.createRow(o);return this.rows.push(r),this}for(let r of o){let d=this.createRow([r]);this.rows.push(d);}return this}addSeparateRows(e){for(let o of e)this.add(o);return this}setId(e){return this.formId=e,this}updateField(e,o){let t=this.findField(e);if(!t)throw new Error(`Field with ID "${e}" not found`);return Object.assign(t,{...o,props:{...t.props,...o.props}}),this}findField(e){for(let o of this.rows){let t=o.fields.find(r=>r.id===e);if(t)return t}return null}removeField(e){return this.rows=this.rows.map(o=>({...o,fields:o.fields.filter(t=>t.id!==e)})).filter(o=>o.fields.length>0),this}getField(e){return this.findField(e)||void 0}getFields(){return this.rows.flatMap(e=>e.fields)}getRows(){return [...this.rows]}clear(){return this.rows=[],this.idGenerator.reset(),this}setValidation(e){return this.formValidation=e,this}addValidators(e){return this.formValidation||(this.formValidation={validators:[]}),this.formValidation={...this.formValidation,validators:[...this.formValidation.validators||[],...e]},this}addFieldValidation(e,o){let t=this.findField(e);if(!t)throw new Error(`Field with ID "${e}" not found`);let r={...t.validation,...o,validators:[...t.validation?.validators||[],...o.validators||[]]};return this.updateField(e,{validation:r})}addFieldConditions(e,o){let t=this.findField(e);if(!t)throw new Error(`Field with ID "${e}" not found`);let r={...t.conditions,...o};return this.updateField(e,{conditions:r})}clone(e){let o=new i(this.config,e||`${this.formId}-clone`);return o.rows=deepClone(this.rows),o}validate(){let e=[],o=this.getFields(),t=o.map(r=>r.id);try{ensureUnique(t,"field");}catch(r){e.push(r instanceof Error?r.message:String(r));}for(let r of o)this.config.hasComponent(r.componentId)||e.push(`Component "${r.componentId}" not found for field "${r.id}"`);for(let r of this.rows)r.fields.length>3&&e.push(`Row "${r.id}" has ${r.fields.length} fields, maximum is 3`),r.fields.length===0&&e.push(`Row "${r.id}" is empty`);return e}build(){let e=this.validate();if(e.length>0)throw new Error(`Form validation failed: ${e.join(", ")}`);return {id:this.formId,rows:[...this.rows],allFields:this.getFields(),config:this.config,renderConfig:this.config.getFormRenderConfig(),validation:this.formValidation}}toJSON(){return {id:this.formId,rows:this.rows}}fromJSON(e){return e.id&&(this.formId=e.id),e.rows&&(this.rows=e.rows),this}getStats(){let e=this.getFields(),o=this.rows.map(t=>t.fields.length);return {totalFields:e.length,totalRows:this.rows.length,averageFieldsPerRow:this.rows.length>0?e.length/this.rows.length:0,maxFieldsInRow:o.length>0?Math.max(...o):0,minFieldsInRow:o.length>0?Math.min(...o):0}}};function ae(i,e){return h.create(i,e)}ril.prototype.form=function(i){return h.create(this,i)};function De(i,e={},o={}){return useMemo(()=>{if(!i)return {visible:o.visible??true,disabled:o.disabled??false,required:o.required??false,readonly:o.readonly??false};let t=r=>{try{return r&&typeof r=="object"&&"build"in r?evaluateCondition(r.build(),e):evaluateCondition(r,e)}catch(d){return console.warn("Error evaluating condition:",d),false}};return {visible:i.visible?t(i.visible):o.visible??true,disabled:i.disabled?t(i.disabled):o.disabled??false,required:i.required?t(i.required):o.required??false,readonly:i.readonly?t(i.readonly):o.readonly??false}},[i,e,o])}function L(i,e={}){return useMemo(()=>{let o={};for(let[t,r]of Object.entries(i))if(o[t]={visible:true,disabled:false,required:false,readonly:false},r){let d=m=>{try{return m&&typeof m=="object"&&"build"in m?evaluateCondition(m.build(),e):evaluateCondition(m,e)}catch(s){return console.warn(`Error evaluating condition for field ${t}:`,s),false}};o[t]={visible:r.visible?d(r.visible):true,disabled:r.disabled?d(r.disabled):false,required:r.required?d(r.required):false,readonly:r.readonly?d(r.readonly):false};}return o},[i,e])}function $({formConfig:i,formValues:e}){let o=useMemo(()=>{let l={};for(let a of i.allFields)a.conditions&&(l[a.id]=a.conditions);return l},[i.allFields]),t=useMemo(()=>Object.keys(o).length>0,[o]),r=L(t?o:{},t?e:{}),d=useCallback(l=>r[l],[r]),m=useCallback(l=>{let a=r[l];return a?a.visible:true},[r]),s=useCallback(l=>{let a=r[l];return a?a.disabled:false},[r]),F=useCallback(l=>{let a=r[l];return a?a.required:false},[r]),f=useCallback(l=>{let a=r[l];return a?a.readonly:false},[r]);return useMemo(()=>({fieldConditions:r,hasConditionalFields:t,getFieldCondition:d,isFieldVisible:m,isFieldDisabled:s,isFieldRequired:F,isFieldReadonly:f}),[r,t,d,m,s,F,f])}function ue(i,e){switch(e.type){case "SET_VALUE":return {...i,values:{...i.values,[e.fieldId]:e.value},isDirty:true};case "SET_FIELD_ERRORS":return {...i,errors:{...i.errors,[e.fieldId]:e.errors}};case "SET_FIELD_VALIDATION_STATE":return {...i,validationState:{...i.validationState,[e.fieldId]:e.state}};case "SET_FIELD_TOUCHED":return {...i,touched:{...i.touched,[e.fieldId]:true}};case "SET_SUBMITTING":return {...i,isSubmitting:e.isSubmitting};case "RESET":return {values:e.values||{},errors:{},validationState:{},touched:{},isDirty:false,isSubmitting:false};default:return i}}function G({defaultValues:i={},onFieldChange:e}){let o={values:i,errors:{},validationState:{},touched:{},isDirty:false,isSubmitting:false},[t,r]=useReducer(ue,o),d=useRef(e);d.current=e;let m=useCallback((u,c)=>{r({type:"SET_VALUE",fieldId:u,value:c}),d.current?.(u,c,{...t.values,[u]:c});},[t.values]),s=useCallback(u=>{r({type:"SET_FIELD_TOUCHED",fieldId:u});},[]),F=useCallback((u,c)=>{r({type:"SET_FIELD_ERRORS",fieldId:u,errors:c});},[]),f=useCallback(u=>{r({type:"SET_FIELD_ERRORS",fieldId:u,errors:[]});},[]),l=useCallback((u,c)=>{r({type:"SET_FIELD_VALIDATION_STATE",fieldId:u,state:c});},[]),a=useCallback(u=>{r({type:"SET_SUBMITTING",isSubmitting:u});},[]),n=useCallback(u=>{r({type:"RESET",values:u});},[]),g=useCallback(()=>{let u=Object.values(t.errors).some(p=>p.length>0),c=Object.values(t.validationState).some(p=>p==="invalid");return !u&&!c},[t.errors,t.validationState]);return {formState:t,setValue:m,setFieldTouched:s,setError:F,clearError:f,setFieldValidationState:l,setSubmitting:a,reset:n,isFormValid:g}}function W({formState:i,onSubmit:e,validateForm:o,setSubmitting:t}){let r=useRef(e);return r.current=e,{submit:useCallback(async m=>{m?.preventDefault();try{return t(!0),(await o()).isValid?(r.current&&await r.current(i.values),!0):!1}catch(s){return console.error("Error during form submission:",s),false}finally{t(false);}},[i.values,o,t])}}function k(){return {isValid:true,errors:[]}}function H({formConfig:i,formState:e,conditionsHelpers:o,setFieldValidationState:t,setError:r}){let d=useCallback(async(s,F)=>{let f=i.allFields.find(n=>n.id===s);if(!f)return k();if(!o.isFieldVisible(s))return r(s,[]),t(s,"valid"),k();if(!f.validation?.validators?.length)return r(s,[]),t(s,"valid"),k();let l=F!==void 0?F:e.values[s],a=createValidationContext({fieldId:s,formId:i.id,allFormData:{...e.values,[s]:l}});t(s,"validating");try{let n=await runValidatorsAsync(f.validation.validators,l,a),g=o.isFieldRequired(s),u=l==null||l==="";if(g&&u&&!n.errors.some(p=>p.code==="REQUIRED"||p.message.toLowerCase().includes("required"))){let p={isValid:!1,errors:[{message:"This field is required",code:"CONDITIONAL_REQUIRED"},...n.errors]};return r(s,p.errors),t(s,"invalid"),p}return r(s,n.errors),t(s,n.isValid?"valid":"invalid"),n}catch(n){let g={isValid:false,errors:[{message:n instanceof Error?n.message:"Validation failed",code:"VALIDATION_ERROR"}]};return r(s,g.errors),t(s,"invalid"),g}},[i,e.values,o,t,r]),m=useCallback(async()=>{let s=i.allFields.filter(n=>{let g=o.isFieldVisible(n.id),u=n.validation?.validators&&n.validation.validators.length>0;return g&&u}),F=i.allFields.filter(n=>!o.isFieldVisible(n.id));for(let n of F)r(n.id,[]),t(n.id,"valid");let f=await Promise.all(s.map(n=>d(n.id))),l=f.some(n=>!n.isValid),a=k();if(i.validation?.validators?.length){let n=Object.keys(e.values).reduce((u,c)=>(o.isFieldVisible(c)&&(u[c]=e.values[c]),u),{}),g=createValidationContext({formId:i.id,allFormData:n});try{a=await runValidatorsAsync(i.validation.validators,n,g);}catch(u){a={isValid:false,errors:[{message:u instanceof Error?u.message:"Form validation failed",code:"FORM_VALIDATION_ERROR"}]};}}return {isValid:!l&&a.isValid,errors:[...f.flatMap(n=>n.errors),...a.errors]}},[i,e.values,o,d,r,t]);return {validateField:d,validateForm:m}}var K=createContext(null);function M({children:i,formConfig:e,defaultValues:o={},onSubmit:t,onFieldChange:r,className:d}){let m=useRef(e.id),{formState:s,setValue:F,setFieldTouched:f,reset:l,setError:a,clearError:n,setFieldValidationState:g,setSubmitting:u,isFormValid:c}=G({defaultValues:o,onFieldChange:r}),{fieldConditions:p,hasConditionalFields:b,getFieldCondition:w,isFieldVisible:R,isFieldDisabled:E,isFieldRequired:x,isFieldReadonly:I}=$({formConfig:e,formValues:s.values}),V=useMemo(()=>({hasConditionalFields:b,getFieldCondition:w,isFieldVisible:R,isFieldDisabled:E,isFieldRequired:x,isFieldReadonly:I}),[b,w,R,E,x,I]),{validateField:S,validateForm:P}=H({formConfig:e,formState:s,conditionsHelpers:V,setFieldValidationState:g,setError:a}),{submit:y}=W({formState:s,onSubmit:t,validateForm:P,setSubmitting:u});useEffect(()=>{(m.current===null||e.id!==m.current)&&(m.current=e.id,l(o));},[e.id,l]);let T=useMemo(()=>e,[e]),q=useMemo(()=>({formState:s,formConfig:T,fieldConditions:p,conditionsHelpers:V,setValue:F,setFieldTouched:f,validateField:S,validateForm:P,isFormValid:c,reset:l,submit:y,setError:a,clearError:n}),[s,T,p,V,F,f,S,P,c,l,y,a,n]);return jsx(K.Provider,{value:q,children:jsx("form",{onSubmit:y,className:d,noValidate:true,children:i})})}function v(){let i=useContext(K);if(!i)throw new Error("useFormContext must be used within a FormProvider");return i}function he({formConfig:i,defaultValues:e,onSubmit:o,onFieldChange:t,children:r}){let d=useMemo(()=>i instanceof h?i.build():i,[i]);return jsx(M,{formConfig:d,defaultValues:e,onSubmit:o,onFieldChange:t,children:r})}function _({fieldId:i,disabled:e=false,customProps:o={},className:t,forceVisible:r=false}){let{formState:d,formConfig:m,setValue:s,setFieldTouched:F,validateField:f,conditionsHelpers:l}=v(),a=m.allFields.find(O=>O.id===i);if(!a)throw new Error(`Field with ID "${i}" not found`);let n=m.config.getComponent(a.componentId);if(!n)throw new Error(`Component with ID "${a.componentId}" not found`);let g=d.values[i],u=d.errors[i]||[],c=d.validationState[i]||"idle",p=d.touched[i]||false,b=c==="validating",w=r||l.isFieldVisible(i),R=e||l.isFieldDisabled(i),E=l.isFieldRequired(i),x=l.isFieldReadonly(i);if(!w)return null;let I=useCallback(async O=>{s(i,O),(a.validation?.validateOnChange||p)&&await f(i,O);},[i,s,f,a.validation?.validateOnChange,p]),V=useCallback(async()=>{p||F(i),a.validation?.validateOnBlur!==false&&await f(i);},[i,p,F,f,a.validation?.validateOnBlur]),S={...n.defaultProps??{},...a.props,...o,disabled:R,required:E,readOnly:x},P={id:i,props:S,value:g,onChange:I,onBlur:V,disabled:R,error:u,isValidating:b,touched:p},y=m.renderConfig?.fieldRenderer,T=n.renderer(P),q=n.useFieldRenderer!==false,re=y&&q?y({children:T,id:i,...S,error:u,isValidating:b,touched:p}):T;return jsx("div",{className:t,"data-field-id":i,"data-field-type":n.type,"data-field-visible":w,"data-field-disabled":R,"data-field-required":E,"data-field-readonly":x,children:re})}Re.memo(_);function Z({row:i,className:e,...o}){let{formConfig:t}=v(),r=i.fields.map(m=>jsx(_,{fieldId:m.id},m.id)),d={row:i,children:r,className:e};return jsx(ComponentRendererWrapper,{name:"FormRow",renderer:t.renderConfig?.rowRenderer,props:d,...o,children:r})}var ee=Z;function xe({className:i,...e}){let{formConfig:o}=v(),t=useMemo(()=>o.rows.map(d=>jsx(ee,{row:d},d.id)),[o.rows]),r={formConfig:o,children:t,className:i};return jsx(ComponentRendererWrapper,{name:"FormBody",renderer:o.renderConfig?.bodyRenderer,props:r,...e,children:t})}function Se({className:i,isSubmitting:e,...o}){let{formState:t,submit:r,formConfig:d}=v(),m={isSubmitting:e??t.isSubmitting,onSubmit:r,className:i};return jsx(ComponentRendererWrapper,{name:"FormSubmitButton",renderer:d.renderConfig?.submitButtonRenderer,props:m,...o})}export{he as Form,xe as FormBody,h as FormBuilder,_ as FormField,M as FormProvider,Z as FormRow,Se as FormSubmitButton,ae as createForm,h as form,De as useConditionEvaluation,$ as useFormConditions,v as useFormContext,G as useFormState,W as useFormSubmission,H as useFormValidation,L as useMultipleConditionEvaluation};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rilaykit/forms",
3
- "version": "7.0.0",
3
+ "version": "8.0.1",
4
4
  "description": "Form building utilities and components for RilayKit",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -24,7 +24,7 @@
24
24
  "url": "https://github.com/andyoucreate/rilay/issues"
25
25
  },
26
26
  "dependencies": {
27
- "@rilaykit/core": "7.0.0"
27
+ "@rilaykit/core": "8.1.0"
28
28
  },
29
29
  "peerDependencies": {
30
30
  "react": ">=18.0.0",