@rilaykit/forms 0.1.1-alpha.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.
@@ -0,0 +1,210 @@
1
+ import * as react_jsx_runtime from 'react/jsx-runtime';
2
+ import { FormConfiguration, ValidationError, ValidationResult, FormFieldRow, ril, InputType, LayoutType, ValidationConfig, ConditionalConfig, FormFieldConfig } from '@rilaykit/core';
3
+ export * from '@rilaykit/core';
4
+ export { createZodValidator, ril } from '@rilaykit/core';
5
+ import * as React$1 from 'react';
6
+ import React__default from 'react';
7
+
8
+ interface FormProps {
9
+ formConfig: FormConfiguration;
10
+ defaultValues?: Record<string, any>;
11
+ onSubmit?: (data: Record<string, any>) => void | Promise<void>;
12
+ onFieldChange?: (fieldId: string, value: any, formData: Record<string, any>) => void;
13
+ className?: string;
14
+ children: React.ReactNode;
15
+ }
16
+ declare function Form({ formConfig, defaultValues, onSubmit, onFieldChange, children }: FormProps): react_jsx_runtime.JSX.Element;
17
+
18
+ interface FormBodyProps {
19
+ className?: string;
20
+ }
21
+ declare function FormBody({ className }: FormBodyProps): React$1.ReactElement<any, string | React$1.JSXElementConstructor<any>>;
22
+
23
+ interface FormFieldProps {
24
+ fieldId: string;
25
+ disabled?: boolean;
26
+ customProps?: Record<string, any>;
27
+ className?: string;
28
+ }
29
+ declare function FormField({ fieldId, disabled, customProps, className, }: FormFieldProps): react_jsx_runtime.JSX.Element | null;
30
+
31
+ interface FormState {
32
+ values: Record<string, any>;
33
+ errors: Record<string, ValidationError[]>;
34
+ warnings: Record<string, ValidationError[]>;
35
+ touched: Set<string>;
36
+ isValidating: Set<string>;
37
+ isDirty: boolean;
38
+ isValid: boolean;
39
+ isSubmitting: boolean;
40
+ }
41
+ interface FormContextValue {
42
+ formState: FormState;
43
+ formConfig: FormConfiguration;
44
+ setValue: (fieldId: string, value: any) => void;
45
+ setError: (fieldId: string, errors: ValidationError[]) => void;
46
+ setWarning: (fieldId: string, warnings: ValidationError[]) => void;
47
+ clearError: (fieldId: string) => void;
48
+ clearWarning: (fieldId: string) => void;
49
+ markFieldTouched: (fieldId: string) => void;
50
+ setFieldValidating: (fieldId: string, isValidating: boolean) => void;
51
+ validateField: (fieldId: string, value?: any) => Promise<ValidationResult>;
52
+ validateAllFields: () => Promise<boolean>;
53
+ reset: (values?: Record<string, any>) => void;
54
+ submit: (event?: React__default.FormEvent) => Promise<boolean>;
55
+ }
56
+ interface FormProviderProps {
57
+ children: React__default.ReactNode;
58
+ formConfig: FormConfiguration;
59
+ defaultValues?: Record<string, any>;
60
+ onSubmit?: (data: Record<string, any>) => void | Promise<void>;
61
+ onFieldChange?: (fieldId: string, value: any, formData: Record<string, any>) => void;
62
+ className?: string;
63
+ }
64
+ declare function FormProvider({ children, formConfig, defaultValues, onSubmit, onFieldChange, className, }: FormProviderProps): react_jsx_runtime.JSX.Element;
65
+ declare function useFormContext(): FormContextValue;
66
+
67
+ interface FormRowProps {
68
+ row: FormFieldRow;
69
+ className?: string;
70
+ }
71
+ declare function FormRow({ row, className }: FormRowProps): React$1.ReactElement<any, string | React$1.JSXElementConstructor<any>>;
72
+
73
+ interface FormSubmitButtonProps {
74
+ className?: string;
75
+ children?: React__default.ReactNode;
76
+ }
77
+ declare function FormSubmitButton({ className, children }: FormSubmitButtonProps): React__default.ReactElement<any, string | React__default.JSXElementConstructor<any>>;
78
+
79
+ /**
80
+ * Form builder class for creating form configurations
81
+ * Simplified API with matrix support
82
+ */
83
+ declare class form {
84
+ private config;
85
+ private rows;
86
+ private formId;
87
+ private rowCounter;
88
+ constructor(config: ril, formId?: string);
89
+ static create(config: ril, formId?: string): form;
90
+ /**
91
+ * Add a single field (takes full width)
92
+ * @param fieldId - Unique field identifier
93
+ * @param componentSubType - Component subtype (e.g., 'text', 'email')
94
+ * @param props - Props to pass to the component
95
+ * @param options - Additional options
96
+ * @returns form instance for chaining
97
+ */
98
+ addField(fieldId: string, componentSubType: InputType | LayoutType, props?: Record<string, any>, options?: {
99
+ validation?: ValidationConfig;
100
+ conditional?: ConditionalConfig;
101
+ }): this;
102
+ /**
103
+ * Add multiple fields on the same row (max 3 fields)
104
+ * @param fieldConfigs - Array of field configurations for the row
105
+ * @param rowOptions - Row configuration options
106
+ * @returns form instance for chaining
107
+ */
108
+ addRowFields(fieldConfigs: Array<{
109
+ fieldId: string;
110
+ componentSubType: InputType | LayoutType;
111
+ props?: Record<string, any>;
112
+ validation?: ValidationConfig;
113
+ conditional?: ConditionalConfig;
114
+ }>, rowOptions?: {
115
+ spacing?: 'tight' | 'normal' | 'loose';
116
+ alignment?: 'start' | 'center' | 'end' | 'stretch';
117
+ }): this;
118
+ /**
119
+ * Add multiple fields, each on its own row
120
+ * @param fieldConfigs - Array of field configurations
121
+ * @returns form instance for chaining
122
+ */
123
+ addFields(fieldConfigs: Array<{
124
+ fieldId: string;
125
+ componentSubType: InputType | LayoutType;
126
+ props?: Record<string, any>;
127
+ validation?: ValidationConfig;
128
+ conditional?: ConditionalConfig;
129
+ }>): this;
130
+ /**
131
+ * Set form ID
132
+ * @param id - Form identifier
133
+ * @returns form instance for chaining
134
+ */
135
+ setId(id: string): this;
136
+ /**
137
+ * Update field configuration
138
+ * @param fieldId - Field identifier
139
+ * @param updates - Updates to apply
140
+ * @returns form instance for chaining
141
+ */
142
+ updateField(fieldId: string, updates: Partial<Omit<FormFieldConfig, 'id'>>): this;
143
+ /**
144
+ * Remove a field from the form
145
+ * @param fieldId - Field identifier
146
+ * @returns form instance for chaining
147
+ */
148
+ removeField(fieldId: string): this;
149
+ /**
150
+ * Get field configuration by ID
151
+ * @param fieldId - Field identifier
152
+ * @returns Field configuration or undefined
153
+ */
154
+ getField(fieldId: string): FormFieldConfig | undefined;
155
+ /**
156
+ * Get all fields as a flat array
157
+ * @returns Array of field configurations
158
+ */
159
+ getFields(): FormFieldConfig[];
160
+ /**
161
+ * Get all rows
162
+ * @returns Array of row configurations
163
+ */
164
+ getRows(): FormFieldRow[];
165
+ /**
166
+ * Clear all fields and rows
167
+ * @returns form instance for chaining
168
+ */
169
+ clear(): this;
170
+ /**
171
+ * Clone the current form builder
172
+ * @param newFormId - ID for the cloned form
173
+ * @returns New form instance
174
+ */
175
+ clone(newFormId?: string): form;
176
+ /**
177
+ * Validate the form configuration
178
+ * @returns Array of validation errors
179
+ */
180
+ validate(): string[];
181
+ /**
182
+ * Build the final form configuration with matrix support
183
+ * @returns Complete form configuration with matrix structure
184
+ */
185
+ build(): FormConfiguration;
186
+ /**
187
+ * Export form configuration as JSON
188
+ * @returns JSON representation of the form
189
+ */
190
+ toJSON(): any;
191
+ /**
192
+ * Import form configuration from JSON
193
+ * @param json - JSON representation of the form
194
+ * @returns form instance for chaining
195
+ */
196
+ fromJSON(json: any): this;
197
+ /**
198
+ * Get form statistics
199
+ * @returns Object with form statistics
200
+ */
201
+ getStats(): {
202
+ totalFields: number;
203
+ totalRows: number;
204
+ averageFieldsPerRow: number;
205
+ maxFieldsInRow: number;
206
+ minFieldsInRow: number;
207
+ };
208
+ }
209
+
210
+ export { Form, FormBody, type FormBodyProps, form as FormBuilder, type FormContextValue, FormField, type FormFieldProps, type FormProps, FormProvider, type FormProviderProps, FormRow, type FormRowProps, type FormState, FormSubmitButton, type FormSubmitButtonProps, form, useFormContext };
@@ -0,0 +1,210 @@
1
+ import * as react_jsx_runtime from 'react/jsx-runtime';
2
+ import { FormConfiguration, ValidationError, ValidationResult, FormFieldRow, ril, InputType, LayoutType, ValidationConfig, ConditionalConfig, FormFieldConfig } from '@rilaykit/core';
3
+ export * from '@rilaykit/core';
4
+ export { createZodValidator, ril } from '@rilaykit/core';
5
+ import * as React$1 from 'react';
6
+ import React__default from 'react';
7
+
8
+ interface FormProps {
9
+ formConfig: FormConfiguration;
10
+ defaultValues?: Record<string, any>;
11
+ onSubmit?: (data: Record<string, any>) => void | Promise<void>;
12
+ onFieldChange?: (fieldId: string, value: any, formData: Record<string, any>) => void;
13
+ className?: string;
14
+ children: React.ReactNode;
15
+ }
16
+ declare function Form({ formConfig, defaultValues, onSubmit, onFieldChange, children }: FormProps): react_jsx_runtime.JSX.Element;
17
+
18
+ interface FormBodyProps {
19
+ className?: string;
20
+ }
21
+ declare function FormBody({ className }: FormBodyProps): React$1.ReactElement<any, string | React$1.JSXElementConstructor<any>>;
22
+
23
+ interface FormFieldProps {
24
+ fieldId: string;
25
+ disabled?: boolean;
26
+ customProps?: Record<string, any>;
27
+ className?: string;
28
+ }
29
+ declare function FormField({ fieldId, disabled, customProps, className, }: FormFieldProps): react_jsx_runtime.JSX.Element | null;
30
+
31
+ interface FormState {
32
+ values: Record<string, any>;
33
+ errors: Record<string, ValidationError[]>;
34
+ warnings: Record<string, ValidationError[]>;
35
+ touched: Set<string>;
36
+ isValidating: Set<string>;
37
+ isDirty: boolean;
38
+ isValid: boolean;
39
+ isSubmitting: boolean;
40
+ }
41
+ interface FormContextValue {
42
+ formState: FormState;
43
+ formConfig: FormConfiguration;
44
+ setValue: (fieldId: string, value: any) => void;
45
+ setError: (fieldId: string, errors: ValidationError[]) => void;
46
+ setWarning: (fieldId: string, warnings: ValidationError[]) => void;
47
+ clearError: (fieldId: string) => void;
48
+ clearWarning: (fieldId: string) => void;
49
+ markFieldTouched: (fieldId: string) => void;
50
+ setFieldValidating: (fieldId: string, isValidating: boolean) => void;
51
+ validateField: (fieldId: string, value?: any) => Promise<ValidationResult>;
52
+ validateAllFields: () => Promise<boolean>;
53
+ reset: (values?: Record<string, any>) => void;
54
+ submit: (event?: React__default.FormEvent) => Promise<boolean>;
55
+ }
56
+ interface FormProviderProps {
57
+ children: React__default.ReactNode;
58
+ formConfig: FormConfiguration;
59
+ defaultValues?: Record<string, any>;
60
+ onSubmit?: (data: Record<string, any>) => void | Promise<void>;
61
+ onFieldChange?: (fieldId: string, value: any, formData: Record<string, any>) => void;
62
+ className?: string;
63
+ }
64
+ declare function FormProvider({ children, formConfig, defaultValues, onSubmit, onFieldChange, className, }: FormProviderProps): react_jsx_runtime.JSX.Element;
65
+ declare function useFormContext(): FormContextValue;
66
+
67
+ interface FormRowProps {
68
+ row: FormFieldRow;
69
+ className?: string;
70
+ }
71
+ declare function FormRow({ row, className }: FormRowProps): React$1.ReactElement<any, string | React$1.JSXElementConstructor<any>>;
72
+
73
+ interface FormSubmitButtonProps {
74
+ className?: string;
75
+ children?: React__default.ReactNode;
76
+ }
77
+ declare function FormSubmitButton({ className, children }: FormSubmitButtonProps): React__default.ReactElement<any, string | React__default.JSXElementConstructor<any>>;
78
+
79
+ /**
80
+ * Form builder class for creating form configurations
81
+ * Simplified API with matrix support
82
+ */
83
+ declare class form {
84
+ private config;
85
+ private rows;
86
+ private formId;
87
+ private rowCounter;
88
+ constructor(config: ril, formId?: string);
89
+ static create(config: ril, formId?: string): form;
90
+ /**
91
+ * Add a single field (takes full width)
92
+ * @param fieldId - Unique field identifier
93
+ * @param componentSubType - Component subtype (e.g., 'text', 'email')
94
+ * @param props - Props to pass to the component
95
+ * @param options - Additional options
96
+ * @returns form instance for chaining
97
+ */
98
+ addField(fieldId: string, componentSubType: InputType | LayoutType, props?: Record<string, any>, options?: {
99
+ validation?: ValidationConfig;
100
+ conditional?: ConditionalConfig;
101
+ }): this;
102
+ /**
103
+ * Add multiple fields on the same row (max 3 fields)
104
+ * @param fieldConfigs - Array of field configurations for the row
105
+ * @param rowOptions - Row configuration options
106
+ * @returns form instance for chaining
107
+ */
108
+ addRowFields(fieldConfigs: Array<{
109
+ fieldId: string;
110
+ componentSubType: InputType | LayoutType;
111
+ props?: Record<string, any>;
112
+ validation?: ValidationConfig;
113
+ conditional?: ConditionalConfig;
114
+ }>, rowOptions?: {
115
+ spacing?: 'tight' | 'normal' | 'loose';
116
+ alignment?: 'start' | 'center' | 'end' | 'stretch';
117
+ }): this;
118
+ /**
119
+ * Add multiple fields, each on its own row
120
+ * @param fieldConfigs - Array of field configurations
121
+ * @returns form instance for chaining
122
+ */
123
+ addFields(fieldConfigs: Array<{
124
+ fieldId: string;
125
+ componentSubType: InputType | LayoutType;
126
+ props?: Record<string, any>;
127
+ validation?: ValidationConfig;
128
+ conditional?: ConditionalConfig;
129
+ }>): this;
130
+ /**
131
+ * Set form ID
132
+ * @param id - Form identifier
133
+ * @returns form instance for chaining
134
+ */
135
+ setId(id: string): this;
136
+ /**
137
+ * Update field configuration
138
+ * @param fieldId - Field identifier
139
+ * @param updates - Updates to apply
140
+ * @returns form instance for chaining
141
+ */
142
+ updateField(fieldId: string, updates: Partial<Omit<FormFieldConfig, 'id'>>): this;
143
+ /**
144
+ * Remove a field from the form
145
+ * @param fieldId - Field identifier
146
+ * @returns form instance for chaining
147
+ */
148
+ removeField(fieldId: string): this;
149
+ /**
150
+ * Get field configuration by ID
151
+ * @param fieldId - Field identifier
152
+ * @returns Field configuration or undefined
153
+ */
154
+ getField(fieldId: string): FormFieldConfig | undefined;
155
+ /**
156
+ * Get all fields as a flat array
157
+ * @returns Array of field configurations
158
+ */
159
+ getFields(): FormFieldConfig[];
160
+ /**
161
+ * Get all rows
162
+ * @returns Array of row configurations
163
+ */
164
+ getRows(): FormFieldRow[];
165
+ /**
166
+ * Clear all fields and rows
167
+ * @returns form instance for chaining
168
+ */
169
+ clear(): this;
170
+ /**
171
+ * Clone the current form builder
172
+ * @param newFormId - ID for the cloned form
173
+ * @returns New form instance
174
+ */
175
+ clone(newFormId?: string): form;
176
+ /**
177
+ * Validate the form configuration
178
+ * @returns Array of validation errors
179
+ */
180
+ validate(): string[];
181
+ /**
182
+ * Build the final form configuration with matrix support
183
+ * @returns Complete form configuration with matrix structure
184
+ */
185
+ build(): FormConfiguration;
186
+ /**
187
+ * Export form configuration as JSON
188
+ * @returns JSON representation of the form
189
+ */
190
+ toJSON(): any;
191
+ /**
192
+ * Import form configuration from JSON
193
+ * @param json - JSON representation of the form
194
+ * @returns form instance for chaining
195
+ */
196
+ fromJSON(json: any): this;
197
+ /**
198
+ * Get form statistics
199
+ * @returns Object with form statistics
200
+ */
201
+ getStats(): {
202
+ totalFields: number;
203
+ totalRows: number;
204
+ averageFieldsPerRow: number;
205
+ maxFieldsInRow: number;
206
+ minFieldsInRow: number;
207
+ };
208
+ }
209
+
210
+ export { Form, FormBody, type FormBodyProps, form as FormBuilder, type FormContextValue, FormField, type FormFieldProps, type FormProps, FormProvider, type FormProviderProps, FormRow, type FormRowProps, type FormState, FormSubmitButton, type FormSubmitButtonProps, form, useFormContext };
package/dist/index.js ADDED
@@ -0,0 +1 @@
1
+ 'use strict';var ir=require('react'),jsxRuntime=require('react/jsx-runtime'),er=require('clsx'),core=require('@rilaykit/core');function _interopDefault(e){return e&&e.__esModule?e:{default:e}}var ir__default=/*#__PURE__*/_interopDefault(ir);var er__default=/*#__PURE__*/_interopDefault(er);function j(i,r){switch(r.type){case "SET_VALUE":{let o={...i.values,[r.fieldId]:r.value};return {...i,values:o,isDirty:true}}case "SET_ERROR":return {...i,errors:{...i.errors,[r.fieldId]:r.errors},isValid:false};case "SET_WARNING":return {...i,warnings:{...i.warnings,[r.fieldId]:r.warnings}};case "CLEAR_ERROR":{let o={...i.errors};return delete o[r.fieldId],{...i,errors:o}}case "CLEAR_WARNING":{let o={...i.warnings};return delete o[r.fieldId],{...i,warnings:o}}case "MARK_TOUCHED":return {...i,touched:new Set([...i.touched,r.fieldId])};case "SET_VALIDATING":{let o=new Set(i.isValidating);return r.isValidating?o.add(r.fieldId):o.delete(r.fieldId),{...i,isValidating:o}}case "SET_SUBMITTING":return {...i,isSubmitting:r.isSubmitting};case "RESET":return {values:r.values||{},errors:{},warnings:{},touched:new Set,isValidating:new Set,isDirty:false,isValid:true,isSubmitting:false};case "UPDATE_VALIDATION_STATE":{let o=Object.keys(i.errors).some(s=>i.errors[s].length>0);return {...i,isValid:!o}}default:return i}}var U=ir.createContext(null);function _({children:i,formConfig:r,defaultValues:o={},onSubmit:s,onFieldChange:e,className:a}){let m={values:o,errors:{},warnings:{},touched:new Set,isValidating:new Set,isDirty:false,isValid:true,isSubmitting:false},[d,l]=ir.useReducer(j,m),t=ir.useRef(new Map),h=ir.useRef(s),u=ir.useRef(e);h.current=s,u.current=e;let y=ir.useCallback((n,f)=>{l({type:"SET_ERROR",fieldId:n,errors:f}),l({type:"UPDATE_VALIDATION_STATE"});},[]),E=ir.useCallback((n,f)=>{l({type:"SET_WARNING",fieldId:n,warnings:f});},[]),R=ir.useCallback(n=>{l({type:"CLEAR_ERROR",fieldId:n}),l({type:"UPDATE_VALIDATION_STATE"});},[]),w=ir.useCallback(n=>{l({type:"CLEAR_WARNING",fieldId:n});},[]),b=ir.useCallback(n=>{l({type:"MARK_TOUCHED",fieldId:n});},[]),S=ir.useCallback((n,f)=>{l({type:"SET_VALIDATING",fieldId:n,isValidating:f});},[]),F=ir.useCallback((n,f)=>{if(l({type:"SET_VALUE",fieldId:n,value:f}),d.errors[n]&&d.errors[n].length>0&&(l({type:"CLEAR_ERROR",fieldId:n}),l({type:"UPDATE_VALIDATION_STATE"})),u.current){let c={...d.values,[n]:f};u.current(n,f,c);}},[d.errors,d.values]),v=ir.useMemo(()=>r,[r]),V=ir.useCallback(async(n,f)=>{let c=v.allFields.find(P=>P.id===n);if(!c?.validation?.validator)return {isValid:true,errors:[]};let z=f!==void 0?f:d.values[n],B=t.current.get(n);B&&clearTimeout(B);let Z={fieldId:n,formData:d.values,fieldProps:c.props,touched:d.touched.has(n),dirty:d.isDirty},L=c.validation.debounceMs||0;return new Promise(P=>{let O=async()=>{S(n,true);try{let p=await c.validation.validator(z,Z,c.props);p.errors.length>0?y(n,p.errors):R(n),p.warnings&&p.warnings.length>0?E(n,p.warnings):w(n),P(p);}catch(p){let M={isValid:false,errors:[{code:"validation_error",message:p instanceof Error?p.message:"Validation error"}]};y(n,M.errors),P(M);}finally{S(n,false);}};if(L>0){let p=setTimeout(O,L);t.current.set(n,p);}else O();})},[v,d.values,d.touched,d.isDirty,y,E,R,w,S]),I=ir.useCallback(async()=>{let n=v.allFields.map(c=>V(c.id));return (await Promise.all(n)).every(c=>c.isValid)},[v,V]),D=ir.useCallback(n=>{l({type:"RESET",values:n});},[]),A=ir.useCallback(async n=>{if(n?.preventDefault(),!h.current)return true;l({type:"SET_SUBMITTING",isSubmitting:true});try{return await I()?(await h.current(d.values),!0):!1}catch(f){return console.error("Error during form submission:",f),false}finally{l({type:"SET_SUBMITTING",isSubmitting:false});}},[d.values,I]),K=ir.useMemo(()=>({formState:d,formConfig:v,setValue:F,setError:y,setWarning:E,clearError:R,clearWarning:w,markFieldTouched:b,setFieldValidating:S,validateField:V,validateAllFields:I,reset:D,submit:A}),[d,v,F,y,E,R,w,b,S,V,I,D,A]);return jsxRuntime.jsx(U.Provider,{value:K,children:jsxRuntime.jsx("form",{onSubmit:A,className:a,noValidate:true,children:i})})}function C(){let i=ir.useContext(U);if(!i)throw new Error("useFormContext must be used within a FormProvider");return i}function rr({formConfig:i,defaultValues:r,onSubmit:o,onFieldChange:s,children:e}){return jsxRuntime.jsx(_,{formConfig:i,defaultValues:r,onSubmit:o,onFieldChange:s,className:"streamline-form",children:e})}function W({fieldId:i,disabled:r=false,customProps:o={},className:s}){let{formState:e,formConfig:a,setValue:m,markFieldTouched:d,validateField:l}=C(),t=ir.useMemo(()=>a.allFields.find(F=>F.id===i),[a.allFields,i]);if(!t)throw new Error(`Field with ID "${i}" not found`);let h=ir.useMemo(()=>a.config.getComponent(t.componentId),[a.config,t.componentId]);if(!h)throw new Error(`Component with ID "${t.componentId}" not found`);let u=ir.useMemo(()=>({value:e.values[t.id],errors:e.errors[t.id]||[],warnings:e.warnings[t.id]||[],touched:e.touched.has(t.id),validating:e.isValidating.has(t.id)}),[e.values,e.errors,e.warnings,e.touched,e.isValidating,t.id]),y=ir.useCallback(F=>{let v=u.errors.length>0;m(t.id,F),(t.validation?.validateOnChange||v&&t.validation?.validator||u.touched&&t.validation?.validator)&&l(t.id,F);},[t.id,t.validation,m,l,u.errors.length,u.touched]),E=ir.useCallback(()=>{d(t.id),(t.validation?.validateOnBlur||t.validation?.validator)&&l(t.id);},[t.id,t.validation,d,l]),R=ir.useMemo(()=>{if(!t.conditional)return true;try{return t.conditional.condition(e.values)}catch(F){return console.warn(`Conditional evaluation failed for field "${t.id}":`,F),true}},[t.conditional,e.values,t.id]),w=ir.useMemo(()=>{if(!t.conditional||!R)return {};switch(t.conditional.action){case "disable":return {disabled:true};case "require":return {required:true};default:return {}}},[t.conditional,R]);if(!R&&t.conditional?.action==="hide")return null;let b=ir.useMemo(()=>({...h.defaultProps,...t.props,...o,...w}),[h.defaultProps,t.props,o,w]),S=ir.useMemo(()=>({id:t.id,props:b,value:u.value,onChange:y,onBlur:E,error:u.errors,warnings:u.warnings,touched:u.touched,disabled:r||w.disabled,isValidating:u.validating}),[t.id,b,u.value,y,E,u.errors,u.warnings,u.touched,r,w.disabled,u.validating]);return jsxRuntime.jsx("div",{className:er__default.default("streamline-form-field",s),"data-field-id":t.id,"data-field-type":h.subType,children:h.renderer(S)})}var q=ir__default.default.memo(W);function H({row:i,className:r}){let{formConfig:o}=C(),s=o.renderConfig?.rowRenderer;if(!s)throw new Error(`No rowRenderer configured for form "${o.id}". Please configure a rowRenderer using config.setRowRenderer() or config.setFormRenderConfig().`);let e=i.fields.map(m=>jsxRuntime.jsx(q,{fieldId:m.id},m.id)),a={row:i,children:e,className:r,spacing:i.spacing,alignment:i.alignment};return s(a)}var J=H;function ar({className:i}){let{formConfig:r}=C(),o=r.renderConfig?.bodyRenderer;if(!o)throw new Error(`No bodyRenderer configured for form "${r.id}". Please configure a bodyRenderer using config.setBodyRenderer() or config.setFormRenderConfig().`);let s=ir.useMemo(()=>r.rows.map(a=>jsxRuntime.jsx(J,{row:a},a.id)),[r.rows]);return o({formConfig:r,children:s,className:i})}function lr({className:i,children:r}){let{formState:o,submit:s,formConfig:e}=C(),a=e.renderConfig?.submitButtonRenderer;if(!a)throw new Error(`No submitButtonRenderer configured for form "${e.id}". Please configure a submitButtonRenderer using config.setSubmitButtonRenderer() or config.setFormRenderConfig().`);let m={isSubmitting:o.isSubmitting,isValid:o.isValid,isDirty:o.isDirty,onSubmit:s,className:i,children:r};return a(m)}var x=class i{constructor(r,o){this.rows=[];this.rowCounter=0;this.config=r,this.formId=o||`form-${Date.now()}`;}static create(r,o){return new i(r,o)}addField(r,o,s={},e){return this.addRowFields([{fieldId:r,componentSubType:o,props:s,validation:e?.validation,conditional:e?.conditional}])}addRowFields(r,o){if(r.length===0)throw new Error("At least one field is required");if(r.length>3)throw new Error("Maximum 3 fields per row");let s=`row-${++this.rowCounter}`,e=[];for(let m of r){let d=this.config.getComponentsBySubType(m.componentSubType);if(d.length===0)throw new Error(`No component found with subType "${m.componentSubType}"`);let l=d[0],t={id:m.fieldId,componentId:l.id,props:{...l.defaultProps,...m.props},validation:m.validation,conditional:m.conditional};e.push(t);}let a={id:s,fields:e,maxColumns:r.length,spacing:o?.spacing||"normal",alignment:o?.alignment||"stretch"};return this.rows.push(a),this}addFields(r){for(let o of r)this.addField(o.fieldId,o.componentSubType,o.props,{validation:o.validation,conditional:o.conditional});return this}setId(r){return this.formId=r,this}updateField(r,o){let s=false;for(let e of this.rows){let a=e.fields.findIndex(m=>m.id===r);a!==-1&&(e.fields[a]={...e.fields[a],...o,props:{...e.fields[a].props,...o.props}},s=true);}if(!s)throw new Error(`Field with ID "${r}" not found`);return this}removeField(r){for(let o of this.rows){let s=o.fields.filter(e=>e.id!==r);Object.assign(o,{fields:s});}return this.rows=this.rows.filter(o=>o.fields.length>0),this}getField(r){for(let o of this.rows){let s=o.fields.find(e=>e.id===r);if(s)return s}}getFields(){return this.rows.flatMap(r=>r.fields)}getRows(){return [...this.rows]}clear(){return this.rows=[],this.rowCounter=0,this}clone(r){let o=new i(this.config,r);return o.rows=this.rows.map(s=>({...s,fields:s.fields.map(e=>({...e}))})),o.rowCounter=this.rowCounter,o}validate(){let r=[],o=this.getFields(),s=o.map(a=>a.id),e=s.filter((a,m)=>s.indexOf(a)!==m);e.length>0&&r.push(`Duplicate field IDs: ${e.join(", ")}`);for(let a of o)this.config.hasComponent(a.componentId)||r.push(`Component "${a.componentId}" not found for field "${a.id}"`);for(let a of this.rows)a.fields.length>3&&r.push(`Row "${a.id}" has ${a.fields.length} fields, maximum is 3`),a.fields.length===0&&r.push(`Row "${a.id}" is empty`);return r}build(){let r=this.validate();if(r.length>0)throw new Error(`Form validation failed: ${r.join(", ")}`);return {id:this.formId,rows:[...this.rows],allFields:this.getFields(),config:this.config,renderConfig:this.config.getFormRenderConfig()}}toJSON(){return {id:this.formId,rows:this.rows}}fromJSON(r){return r.id&&(this.formId=r.id),r.rows&&(this.rows=r.rows,this.rowCounter=this.rows.length),this}getStats(){let r=this.getFields(),o=this.rows.map(s=>s.fields.length);return {totalFields:r.length,totalRows:this.rows.length,averageFieldsPerRow:this.rows.length>0?r.length/this.rows.length:0,maxFieldsInRow:o.length>0?Math.max(...o):0,minFieldsInRow:o.length>0?Math.min(...o):0}}};Object.defineProperty(exports,"createZodValidator",{enumerable:true,get:function(){return core.createZodValidator}});Object.defineProperty(exports,"ril",{enumerable:true,get:function(){return core.ril}});exports.Form=rr;exports.FormBody=ar;exports.FormBuilder=x;exports.FormField=W;exports.FormProvider=_;exports.FormRow=H;exports.FormSubmitButton=lr;exports.form=x;exports.useFormContext=C;
package/dist/index.mjs ADDED
@@ -0,0 +1 @@
1
+ import ir,{createContext,useMemo,useCallback,useContext,useReducer,useRef}from'react';import {jsx}from'react/jsx-runtime';import er from'clsx';export{createZodValidator,ril}from'@rilaykit/core';function j(i,r){switch(r.type){case "SET_VALUE":{let o={...i.values,[r.fieldId]:r.value};return {...i,values:o,isDirty:true}}case "SET_ERROR":return {...i,errors:{...i.errors,[r.fieldId]:r.errors},isValid:false};case "SET_WARNING":return {...i,warnings:{...i.warnings,[r.fieldId]:r.warnings}};case "CLEAR_ERROR":{let o={...i.errors};return delete o[r.fieldId],{...i,errors:o}}case "CLEAR_WARNING":{let o={...i.warnings};return delete o[r.fieldId],{...i,warnings:o}}case "MARK_TOUCHED":return {...i,touched:new Set([...i.touched,r.fieldId])};case "SET_VALIDATING":{let o=new Set(i.isValidating);return r.isValidating?o.add(r.fieldId):o.delete(r.fieldId),{...i,isValidating:o}}case "SET_SUBMITTING":return {...i,isSubmitting:r.isSubmitting};case "RESET":return {values:r.values||{},errors:{},warnings:{},touched:new Set,isValidating:new Set,isDirty:false,isValid:true,isSubmitting:false};case "UPDATE_VALIDATION_STATE":{let o=Object.keys(i.errors).some(s=>i.errors[s].length>0);return {...i,isValid:!o}}default:return i}}var U=createContext(null);function _({children:i,formConfig:r,defaultValues:o={},onSubmit:s,onFieldChange:e,className:a}){let m={values:o,errors:{},warnings:{},touched:new Set,isValidating:new Set,isDirty:false,isValid:true,isSubmitting:false},[d,l]=useReducer(j,m),t=useRef(new Map),h=useRef(s),u=useRef(e);h.current=s,u.current=e;let y=useCallback((n,f)=>{l({type:"SET_ERROR",fieldId:n,errors:f}),l({type:"UPDATE_VALIDATION_STATE"});},[]),E=useCallback((n,f)=>{l({type:"SET_WARNING",fieldId:n,warnings:f});},[]),R=useCallback(n=>{l({type:"CLEAR_ERROR",fieldId:n}),l({type:"UPDATE_VALIDATION_STATE"});},[]),w=useCallback(n=>{l({type:"CLEAR_WARNING",fieldId:n});},[]),b=useCallback(n=>{l({type:"MARK_TOUCHED",fieldId:n});},[]),S=useCallback((n,f)=>{l({type:"SET_VALIDATING",fieldId:n,isValidating:f});},[]),F=useCallback((n,f)=>{if(l({type:"SET_VALUE",fieldId:n,value:f}),d.errors[n]&&d.errors[n].length>0&&(l({type:"CLEAR_ERROR",fieldId:n}),l({type:"UPDATE_VALIDATION_STATE"})),u.current){let c={...d.values,[n]:f};u.current(n,f,c);}},[d.errors,d.values]),v=useMemo(()=>r,[r]),V=useCallback(async(n,f)=>{let c=v.allFields.find(P=>P.id===n);if(!c?.validation?.validator)return {isValid:true,errors:[]};let z=f!==void 0?f:d.values[n],B=t.current.get(n);B&&clearTimeout(B);let Z={fieldId:n,formData:d.values,fieldProps:c.props,touched:d.touched.has(n),dirty:d.isDirty},L=c.validation.debounceMs||0;return new Promise(P=>{let O=async()=>{S(n,true);try{let p=await c.validation.validator(z,Z,c.props);p.errors.length>0?y(n,p.errors):R(n),p.warnings&&p.warnings.length>0?E(n,p.warnings):w(n),P(p);}catch(p){let M={isValid:false,errors:[{code:"validation_error",message:p instanceof Error?p.message:"Validation error"}]};y(n,M.errors),P(M);}finally{S(n,false);}};if(L>0){let p=setTimeout(O,L);t.current.set(n,p);}else O();})},[v,d.values,d.touched,d.isDirty,y,E,R,w,S]),I=useCallback(async()=>{let n=v.allFields.map(c=>V(c.id));return (await Promise.all(n)).every(c=>c.isValid)},[v,V]),D=useCallback(n=>{l({type:"RESET",values:n});},[]),A=useCallback(async n=>{if(n?.preventDefault(),!h.current)return true;l({type:"SET_SUBMITTING",isSubmitting:true});try{return await I()?(await h.current(d.values),!0):!1}catch(f){return console.error("Error during form submission:",f),false}finally{l({type:"SET_SUBMITTING",isSubmitting:false});}},[d.values,I]),K=useMemo(()=>({formState:d,formConfig:v,setValue:F,setError:y,setWarning:E,clearError:R,clearWarning:w,markFieldTouched:b,setFieldValidating:S,validateField:V,validateAllFields:I,reset:D,submit:A}),[d,v,F,y,E,R,w,b,S,V,I,D,A]);return jsx(U.Provider,{value:K,children:jsx("form",{onSubmit:A,className:a,noValidate:true,children:i})})}function C(){let i=useContext(U);if(!i)throw new Error("useFormContext must be used within a FormProvider");return i}function rr({formConfig:i,defaultValues:r,onSubmit:o,onFieldChange:s,children:e}){return jsx(_,{formConfig:i,defaultValues:r,onSubmit:o,onFieldChange:s,className:"streamline-form",children:e})}function W({fieldId:i,disabled:r=false,customProps:o={},className:s}){let{formState:e,formConfig:a,setValue:m,markFieldTouched:d,validateField:l}=C(),t=useMemo(()=>a.allFields.find(F=>F.id===i),[a.allFields,i]);if(!t)throw new Error(`Field with ID "${i}" not found`);let h=useMemo(()=>a.config.getComponent(t.componentId),[a.config,t.componentId]);if(!h)throw new Error(`Component with ID "${t.componentId}" not found`);let u=useMemo(()=>({value:e.values[t.id],errors:e.errors[t.id]||[],warnings:e.warnings[t.id]||[],touched:e.touched.has(t.id),validating:e.isValidating.has(t.id)}),[e.values,e.errors,e.warnings,e.touched,e.isValidating,t.id]),y=useCallback(F=>{let v=u.errors.length>0;m(t.id,F),(t.validation?.validateOnChange||v&&t.validation?.validator||u.touched&&t.validation?.validator)&&l(t.id,F);},[t.id,t.validation,m,l,u.errors.length,u.touched]),E=useCallback(()=>{d(t.id),(t.validation?.validateOnBlur||t.validation?.validator)&&l(t.id);},[t.id,t.validation,d,l]),R=useMemo(()=>{if(!t.conditional)return true;try{return t.conditional.condition(e.values)}catch(F){return console.warn(`Conditional evaluation failed for field "${t.id}":`,F),true}},[t.conditional,e.values,t.id]),w=useMemo(()=>{if(!t.conditional||!R)return {};switch(t.conditional.action){case "disable":return {disabled:true};case "require":return {required:true};default:return {}}},[t.conditional,R]);if(!R&&t.conditional?.action==="hide")return null;let b=useMemo(()=>({...h.defaultProps,...t.props,...o,...w}),[h.defaultProps,t.props,o,w]),S=useMemo(()=>({id:t.id,props:b,value:u.value,onChange:y,onBlur:E,error:u.errors,warnings:u.warnings,touched:u.touched,disabled:r||w.disabled,isValidating:u.validating}),[t.id,b,u.value,y,E,u.errors,u.warnings,u.touched,r,w.disabled,u.validating]);return jsx("div",{className:er("streamline-form-field",s),"data-field-id":t.id,"data-field-type":h.subType,children:h.renderer(S)})}var q=ir.memo(W);function H({row:i,className:r}){let{formConfig:o}=C(),s=o.renderConfig?.rowRenderer;if(!s)throw new Error(`No rowRenderer configured for form "${o.id}". Please configure a rowRenderer using config.setRowRenderer() or config.setFormRenderConfig().`);let e=i.fields.map(m=>jsx(q,{fieldId:m.id},m.id)),a={row:i,children:e,className:r,spacing:i.spacing,alignment:i.alignment};return s(a)}var J=H;function ar({className:i}){let{formConfig:r}=C(),o=r.renderConfig?.bodyRenderer;if(!o)throw new Error(`No bodyRenderer configured for form "${r.id}". Please configure a bodyRenderer using config.setBodyRenderer() or config.setFormRenderConfig().`);let s=useMemo(()=>r.rows.map(a=>jsx(J,{row:a},a.id)),[r.rows]);return o({formConfig:r,children:s,className:i})}function lr({className:i,children:r}){let{formState:o,submit:s,formConfig:e}=C(),a=e.renderConfig?.submitButtonRenderer;if(!a)throw new Error(`No submitButtonRenderer configured for form "${e.id}". Please configure a submitButtonRenderer using config.setSubmitButtonRenderer() or config.setFormRenderConfig().`);let m={isSubmitting:o.isSubmitting,isValid:o.isValid,isDirty:o.isDirty,onSubmit:s,className:i,children:r};return a(m)}var x=class i{constructor(r,o){this.rows=[];this.rowCounter=0;this.config=r,this.formId=o||`form-${Date.now()}`;}static create(r,o){return new i(r,o)}addField(r,o,s={},e){return this.addRowFields([{fieldId:r,componentSubType:o,props:s,validation:e?.validation,conditional:e?.conditional}])}addRowFields(r,o){if(r.length===0)throw new Error("At least one field is required");if(r.length>3)throw new Error("Maximum 3 fields per row");let s=`row-${++this.rowCounter}`,e=[];for(let m of r){let d=this.config.getComponentsBySubType(m.componentSubType);if(d.length===0)throw new Error(`No component found with subType "${m.componentSubType}"`);let l=d[0],t={id:m.fieldId,componentId:l.id,props:{...l.defaultProps,...m.props},validation:m.validation,conditional:m.conditional};e.push(t);}let a={id:s,fields:e,maxColumns:r.length,spacing:o?.spacing||"normal",alignment:o?.alignment||"stretch"};return this.rows.push(a),this}addFields(r){for(let o of r)this.addField(o.fieldId,o.componentSubType,o.props,{validation:o.validation,conditional:o.conditional});return this}setId(r){return this.formId=r,this}updateField(r,o){let s=false;for(let e of this.rows){let a=e.fields.findIndex(m=>m.id===r);a!==-1&&(e.fields[a]={...e.fields[a],...o,props:{...e.fields[a].props,...o.props}},s=true);}if(!s)throw new Error(`Field with ID "${r}" not found`);return this}removeField(r){for(let o of this.rows){let s=o.fields.filter(e=>e.id!==r);Object.assign(o,{fields:s});}return this.rows=this.rows.filter(o=>o.fields.length>0),this}getField(r){for(let o of this.rows){let s=o.fields.find(e=>e.id===r);if(s)return s}}getFields(){return this.rows.flatMap(r=>r.fields)}getRows(){return [...this.rows]}clear(){return this.rows=[],this.rowCounter=0,this}clone(r){let o=new i(this.config,r);return o.rows=this.rows.map(s=>({...s,fields:s.fields.map(e=>({...e}))})),o.rowCounter=this.rowCounter,o}validate(){let r=[],o=this.getFields(),s=o.map(a=>a.id),e=s.filter((a,m)=>s.indexOf(a)!==m);e.length>0&&r.push(`Duplicate field IDs: ${e.join(", ")}`);for(let a of o)this.config.hasComponent(a.componentId)||r.push(`Component "${a.componentId}" not found for field "${a.id}"`);for(let a of this.rows)a.fields.length>3&&r.push(`Row "${a.id}" has ${a.fields.length} fields, maximum is 3`),a.fields.length===0&&r.push(`Row "${a.id}" is empty`);return r}build(){let r=this.validate();if(r.length>0)throw new Error(`Form validation failed: ${r.join(", ")}`);return {id:this.formId,rows:[...this.rows],allFields:this.getFields(),config:this.config,renderConfig:this.config.getFormRenderConfig()}}toJSON(){return {id:this.formId,rows:this.rows}}fromJSON(r){return r.id&&(this.formId=r.id),r.rows&&(this.rows=r.rows,this.rowCounter=this.rows.length),this}getStats(){let r=this.getFields(),o=this.rows.map(s=>s.fields.length);return {totalFields:r.length,totalRows:this.rows.length,averageFieldsPerRow:this.rows.length>0?r.length/this.rows.length:0,maxFieldsInRow:o.length>0?Math.max(...o):0,minFieldsInRow:o.length>0?Math.min(...o):0}}};export{rr as Form,ar as FormBody,x as FormBuilder,W as FormField,_ as FormProvider,H as FormRow,lr as FormSubmitButton,x as form,C as useFormContext};
package/package.json ADDED
@@ -0,0 +1,51 @@
1
+ {
2
+ "name": "@rilaykit/forms",
3
+ "version": "0.1.1-alpha.1",
4
+ "description": "Form building utilities and components for RilayKit",
5
+ "main": "dist/index.js",
6
+ "types": "dist/index.d.ts",
7
+ "files": [
8
+ "dist"
9
+ ],
10
+ "keywords": [
11
+ "react",
12
+ "forms",
13
+ "form-builder",
14
+ "typescript"
15
+ ],
16
+ "author": "Rilay Team",
17
+ "license": "MIT",
18
+ "dependencies": {
19
+ "clsx": "^2.1.1",
20
+ "@rilaykit/core": "0.1.1-alpha.1"
21
+ },
22
+ "peerDependencies": {
23
+ "react": ">=18.0.0",
24
+ "react-dom": ">=18.0.0"
25
+ },
26
+ "devDependencies": {
27
+ "@testing-library/jest-dom": "^6.6.3",
28
+ "@testing-library/react": "^16.3.0",
29
+ "@testing-library/user-event": "^14.6.1",
30
+ "@types/react": "^18.2.0",
31
+ "@types/react-dom": "^18.2.0",
32
+ "react": "^18.3.1",
33
+ "react-dom": "^18.3.1",
34
+ "typescript": "^5.3.0",
35
+ "vitest": "^3.2.4"
36
+ },
37
+ "publishConfig": {
38
+ "access": "public"
39
+ },
40
+ "scripts": {
41
+ "build": "tsup",
42
+ "dev": "tsup --watch",
43
+ "test": "vitest",
44
+ "test:run": "vitest run",
45
+ "test:ui": "vitest --ui",
46
+ "lint": "biome check .",
47
+ "lint:fix": "biome check --write .",
48
+ "type-check": "tsc --noEmit",
49
+ "clean": "rm -rf dist"
50
+ }
51
+ }