@rilaykit/forms 5.2.1 → 6.0.0
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 +154 -6
- package/dist/index.d.ts +154 -6
- package/dist/index.js +1 -1
- package/dist/index.mjs +1 -1
- package/package.json +2 -2
package/dist/index.d.mts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
|
-
import { ril, FieldValidationConfig, FormFieldConfig, FormFieldRow, FormValidationConfig, FormValidator, FormConfiguration, ComponentRendererBaseProps, FormBodyRendererProps, ValidationError, ValidationResult, FormRowRendererProps, FormSubmitButtonRendererProps } from '@rilaykit/core';
|
|
2
|
+
import { ril, FieldValidationConfig, ConditionalBehavior, FormFieldConfig, FormFieldRow, FormValidationConfig, FormValidator, FormConfiguration, ComponentRendererBaseProps, FormBodyRendererProps, ValidationError, ValidationResult, FormRowRendererProps, FormSubmitButtonRendererProps } from '@rilaykit/core';
|
|
3
3
|
export * from '@rilaykit/core';
|
|
4
4
|
import React$1 from 'react';
|
|
5
5
|
|
|
@@ -31,6 +31,8 @@ type FieldConfig<C extends Record<string, any>, T extends keyof C> = {
|
|
|
31
31
|
props?: Partial<C[T]>;
|
|
32
32
|
/** Validation configuration for this field */
|
|
33
33
|
validation?: FieldValidationConfig;
|
|
34
|
+
/** Conditional behavior configuration for this field */
|
|
35
|
+
conditions?: ConditionalBehavior;
|
|
34
36
|
};
|
|
35
37
|
/**
|
|
36
38
|
* Form builder class for creating type-safe form configurations
|
|
@@ -122,7 +124,6 @@ declare class form<C extends Record<string, any> = Record<string, never>> {
|
|
|
122
124
|
*
|
|
123
125
|
* @template T - The component type
|
|
124
126
|
* @param fieldConfigs - Array of field configurations for the row
|
|
125
|
-
* @param rowOptions - Optional row layout configuration
|
|
126
127
|
* @returns A complete FormFieldRow configuration
|
|
127
128
|
* @throws Error if no fields provided or more than 3 fields specified
|
|
128
129
|
*
|
|
@@ -174,7 +175,6 @@ declare class form<C extends Record<string, any> = Record<string, never>> {
|
|
|
174
175
|
*
|
|
175
176
|
* @template T - The component type
|
|
176
177
|
* @param fieldConfigs - Array of field configurations
|
|
177
|
-
* @param rowOptions - Optional row layout configuration applied to all rows
|
|
178
178
|
* @returns The form builder instance for method chaining
|
|
179
179
|
*
|
|
180
180
|
* @example
|
|
@@ -369,6 +369,26 @@ declare class form<C extends Record<string, any> = Record<string, never>> {
|
|
|
369
369
|
* ```
|
|
370
370
|
*/
|
|
371
371
|
addFieldValidation(fieldId: string, validationConfig: FieldValidationConfig): this;
|
|
372
|
+
/**
|
|
373
|
+
* Adds conditions to a specific field by ID
|
|
374
|
+
*
|
|
375
|
+
* This method allows adding conditional behavior to a field after it has been created,
|
|
376
|
+
* useful for dynamic conditional requirements.
|
|
377
|
+
*
|
|
378
|
+
* @param fieldId - The ID of the field to add conditions to
|
|
379
|
+
* @param conditions - Conditional behavior configuration
|
|
380
|
+
* @returns The form builder instance for method chaining
|
|
381
|
+
* @throws Error if the field with the specified ID is not found
|
|
382
|
+
*
|
|
383
|
+
* @example
|
|
384
|
+
* ```typescript
|
|
385
|
+
* builder.addFieldConditions('phone', {
|
|
386
|
+
* visible: when('contactMethod').equals('phone').build(),
|
|
387
|
+
* required: when('contactMethod').equals('phone').build()
|
|
388
|
+
* });
|
|
389
|
+
* ```
|
|
390
|
+
*/
|
|
391
|
+
addFieldConditions(fieldId: string, conditions: ConditionalBehavior): this;
|
|
372
392
|
/**
|
|
373
393
|
* Creates a deep copy of the current form builder
|
|
374
394
|
*
|
|
@@ -551,8 +571,75 @@ interface FormFieldProps {
|
|
|
551
571
|
disabled?: boolean;
|
|
552
572
|
customProps?: Record<string, any>;
|
|
553
573
|
className?: string;
|
|
574
|
+
/** Force field to be visible even if conditions say otherwise (for debugging) */
|
|
575
|
+
forceVisible?: boolean;
|
|
576
|
+
}
|
|
577
|
+
declare function FormField({ fieldId, disabled, customProps, className, forceVisible, }: FormFieldProps): react_jsx_runtime.JSX.Element | null;
|
|
578
|
+
|
|
579
|
+
interface ConditionEvaluationResult {
|
|
580
|
+
visible: boolean;
|
|
581
|
+
disabled: boolean;
|
|
582
|
+
required: boolean;
|
|
583
|
+
readonly: boolean;
|
|
554
584
|
}
|
|
555
|
-
|
|
585
|
+
/**
|
|
586
|
+
* Hook to evaluate conditional behaviors based on form data
|
|
587
|
+
*
|
|
588
|
+
* @param conditions - The conditional behavior configuration
|
|
589
|
+
* @param formData - Current form data to evaluate against
|
|
590
|
+
* @param defaultState - Default state when no conditions are provided
|
|
591
|
+
* @returns Evaluated condition results
|
|
592
|
+
*/
|
|
593
|
+
declare function useConditionEvaluation(conditions?: ConditionalBehavior, formData?: Record<string, any>, defaultState?: Partial<ConditionEvaluationResult>): ConditionEvaluationResult;
|
|
594
|
+
/**
|
|
595
|
+
* Hook to evaluate conditions for multiple fields at once
|
|
596
|
+
*
|
|
597
|
+
* @param fieldsWithConditions - Map of field IDs to their conditional behaviors
|
|
598
|
+
* @param formData - Current form data
|
|
599
|
+
* @returns Map of field IDs to their evaluated conditions
|
|
600
|
+
*/
|
|
601
|
+
declare function useMultipleConditionEvaluation(fieldsWithConditions: Record<string, ConditionalBehavior | undefined>, formData?: Record<string, any>): Record<string, ConditionEvaluationResult>;
|
|
602
|
+
|
|
603
|
+
interface UseFormConditionsProps {
|
|
604
|
+
formConfig: FormConfiguration;
|
|
605
|
+
formValues: Record<string, any>;
|
|
606
|
+
}
|
|
607
|
+
interface UseFormConditionsReturn {
|
|
608
|
+
fieldConditions: Record<string, ConditionEvaluationResult>;
|
|
609
|
+
hasConditionalFields: boolean;
|
|
610
|
+
getFieldCondition: (fieldId: string) => ConditionEvaluationResult | undefined;
|
|
611
|
+
isFieldVisible: (fieldId: string) => boolean;
|
|
612
|
+
isFieldDisabled: (fieldId: string) => boolean;
|
|
613
|
+
isFieldRequired: (fieldId: string) => boolean;
|
|
614
|
+
isFieldReadonly: (fieldId: string) => boolean;
|
|
615
|
+
}
|
|
616
|
+
/**
|
|
617
|
+
* Hook to manage conditional behaviors for form fields
|
|
618
|
+
*
|
|
619
|
+
* This hook evaluates conditions for all form fields and provides
|
|
620
|
+
* convenient methods to check field states.
|
|
621
|
+
*
|
|
622
|
+
* @param props - Configuration for form conditions
|
|
623
|
+
* @returns Object containing field conditions and helper methods
|
|
624
|
+
*
|
|
625
|
+
* @example
|
|
626
|
+
* ```tsx
|
|
627
|
+
* const {
|
|
628
|
+
* fieldConditions,
|
|
629
|
+
* isFieldVisible,
|
|
630
|
+
* isFieldDisabled
|
|
631
|
+
* } = useFormConditions({
|
|
632
|
+
* formConfig,
|
|
633
|
+
* formValues
|
|
634
|
+
* });
|
|
635
|
+
*
|
|
636
|
+
* // Check if a field should be visible
|
|
637
|
+
* if (!isFieldVisible('phoneField')) {
|
|
638
|
+
* return null;
|
|
639
|
+
* }
|
|
640
|
+
* ```
|
|
641
|
+
*/
|
|
642
|
+
declare function useFormConditions({ formConfig, formValues, }: UseFormConditionsProps): UseFormConditionsReturn;
|
|
556
643
|
|
|
557
644
|
interface FormState {
|
|
558
645
|
values: Record<string, any>;
|
|
@@ -561,11 +648,72 @@ interface FormState {
|
|
|
561
648
|
touched: Record<string, boolean>;
|
|
562
649
|
isDirty: boolean;
|
|
563
650
|
isSubmitting: boolean;
|
|
564
|
-
isValid: boolean;
|
|
565
651
|
}
|
|
652
|
+
type FormAction = {
|
|
653
|
+
type: 'SET_VALUE';
|
|
654
|
+
fieldId: string;
|
|
655
|
+
value: any;
|
|
656
|
+
} | {
|
|
657
|
+
type: 'SET_FIELD_ERRORS';
|
|
658
|
+
fieldId: string;
|
|
659
|
+
errors: ValidationError[];
|
|
660
|
+
} | {
|
|
661
|
+
type: 'SET_FIELD_VALIDATION_STATE';
|
|
662
|
+
fieldId: string;
|
|
663
|
+
state: 'idle' | 'validating' | 'valid' | 'invalid';
|
|
664
|
+
} | {
|
|
665
|
+
type: 'SET_FIELD_TOUCHED';
|
|
666
|
+
fieldId: string;
|
|
667
|
+
} | {
|
|
668
|
+
type: 'SET_SUBMITTING';
|
|
669
|
+
isSubmitting: boolean;
|
|
670
|
+
} | {
|
|
671
|
+
type: 'RESET';
|
|
672
|
+
values?: Record<string, any>;
|
|
673
|
+
};
|
|
674
|
+
interface UseFormStateProps {
|
|
675
|
+
defaultValues?: Record<string, any>;
|
|
676
|
+
onFieldChange?: (fieldId: string, value: any, formData: Record<string, any>) => void;
|
|
677
|
+
}
|
|
678
|
+
declare function useFormState({ defaultValues, onFieldChange }: UseFormStateProps): {
|
|
679
|
+
formState: FormState;
|
|
680
|
+
setValue: (fieldId: string, value: any) => void;
|
|
681
|
+
setFieldTouched: (fieldId: string) => void;
|
|
682
|
+
setError: (fieldId: string, errors: ValidationError[]) => void;
|
|
683
|
+
clearError: (fieldId: string) => void;
|
|
684
|
+
setFieldValidationState: (fieldId: string, state: "idle" | "validating" | "valid" | "invalid") => void;
|
|
685
|
+
setSubmitting: (isSubmitting: boolean) => void;
|
|
686
|
+
reset: (values?: Record<string, any>) => void;
|
|
687
|
+
isFormValid: () => boolean;
|
|
688
|
+
};
|
|
689
|
+
|
|
690
|
+
interface UseFormSubmissionProps {
|
|
691
|
+
formState: FormState;
|
|
692
|
+
onSubmit?: (data: Record<string, any>) => void | Promise<void>;
|
|
693
|
+
validateForm: () => Promise<ValidationResult>;
|
|
694
|
+
setSubmitting: (isSubmitting: boolean) => void;
|
|
695
|
+
}
|
|
696
|
+
declare function useFormSubmission({ formState, onSubmit, validateForm, setSubmitting, }: UseFormSubmissionProps): {
|
|
697
|
+
submit: (event?: React$1.FormEvent) => Promise<boolean>;
|
|
698
|
+
};
|
|
699
|
+
|
|
700
|
+
interface UseFormValidationProps {
|
|
701
|
+
formConfig: FormConfiguration;
|
|
702
|
+
formState: FormState;
|
|
703
|
+
conditionsHelpers: Omit<UseFormConditionsReturn, 'fieldConditions'>;
|
|
704
|
+
setFieldValidationState: (fieldId: string, state: 'idle' | 'validating' | 'valid' | 'invalid') => void;
|
|
705
|
+
setError: (fieldId: string, errors: any[]) => void;
|
|
706
|
+
}
|
|
707
|
+
declare function useFormValidation({ formConfig, formState, conditionsHelpers, setFieldValidationState, setError, }: UseFormValidationProps): {
|
|
708
|
+
validateField: (fieldId: string, value?: any) => Promise<ValidationResult>;
|
|
709
|
+
validateForm: () => Promise<ValidationResult>;
|
|
710
|
+
};
|
|
711
|
+
|
|
566
712
|
interface FormContextValue {
|
|
567
713
|
formState: FormState;
|
|
568
714
|
formConfig: FormConfiguration;
|
|
715
|
+
fieldConditions: Record<string, ConditionEvaluationResult>;
|
|
716
|
+
conditionsHelpers: Omit<UseFormConditionsReturn, 'fieldConditions'>;
|
|
569
717
|
setValue: (fieldId: string, value: any) => void;
|
|
570
718
|
setFieldTouched: (fieldId: string, touched?: boolean) => void;
|
|
571
719
|
validateField: (fieldId: string, value?: any) => Promise<ValidationResult>;
|
|
@@ -594,4 +742,4 @@ declare function FormRow({ row, className, ...props }: FormRowProps): react_jsx_
|
|
|
594
742
|
|
|
595
743
|
declare function FormSubmitButton({ className, ...props }: ComponentRendererBaseProps<FormSubmitButtonRendererProps>): react_jsx_runtime.JSX.Element;
|
|
596
744
|
|
|
597
|
-
export { type FieldConfig, Form, FormBody, form as FormBuilder, FormField, FormProvider, FormRow, FormSubmitButton, createForm, form, useFormContext };
|
|
745
|
+
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
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
|
-
import { ril, FieldValidationConfig, FormFieldConfig, FormFieldRow, FormValidationConfig, FormValidator, FormConfiguration, ComponentRendererBaseProps, FormBodyRendererProps, ValidationError, ValidationResult, FormRowRendererProps, FormSubmitButtonRendererProps } from '@rilaykit/core';
|
|
2
|
+
import { ril, FieldValidationConfig, ConditionalBehavior, FormFieldConfig, FormFieldRow, FormValidationConfig, FormValidator, FormConfiguration, ComponentRendererBaseProps, FormBodyRendererProps, ValidationError, ValidationResult, FormRowRendererProps, FormSubmitButtonRendererProps } from '@rilaykit/core';
|
|
3
3
|
export * from '@rilaykit/core';
|
|
4
4
|
import React$1 from 'react';
|
|
5
5
|
|
|
@@ -31,6 +31,8 @@ type FieldConfig<C extends Record<string, any>, T extends keyof C> = {
|
|
|
31
31
|
props?: Partial<C[T]>;
|
|
32
32
|
/** Validation configuration for this field */
|
|
33
33
|
validation?: FieldValidationConfig;
|
|
34
|
+
/** Conditional behavior configuration for this field */
|
|
35
|
+
conditions?: ConditionalBehavior;
|
|
34
36
|
};
|
|
35
37
|
/**
|
|
36
38
|
* Form builder class for creating type-safe form configurations
|
|
@@ -122,7 +124,6 @@ declare class form<C extends Record<string, any> = Record<string, never>> {
|
|
|
122
124
|
*
|
|
123
125
|
* @template T - The component type
|
|
124
126
|
* @param fieldConfigs - Array of field configurations for the row
|
|
125
|
-
* @param rowOptions - Optional row layout configuration
|
|
126
127
|
* @returns A complete FormFieldRow configuration
|
|
127
128
|
* @throws Error if no fields provided or more than 3 fields specified
|
|
128
129
|
*
|
|
@@ -174,7 +175,6 @@ declare class form<C extends Record<string, any> = Record<string, never>> {
|
|
|
174
175
|
*
|
|
175
176
|
* @template T - The component type
|
|
176
177
|
* @param fieldConfigs - Array of field configurations
|
|
177
|
-
* @param rowOptions - Optional row layout configuration applied to all rows
|
|
178
178
|
* @returns The form builder instance for method chaining
|
|
179
179
|
*
|
|
180
180
|
* @example
|
|
@@ -369,6 +369,26 @@ declare class form<C extends Record<string, any> = Record<string, never>> {
|
|
|
369
369
|
* ```
|
|
370
370
|
*/
|
|
371
371
|
addFieldValidation(fieldId: string, validationConfig: FieldValidationConfig): this;
|
|
372
|
+
/**
|
|
373
|
+
* Adds conditions to a specific field by ID
|
|
374
|
+
*
|
|
375
|
+
* This method allows adding conditional behavior to a field after it has been created,
|
|
376
|
+
* useful for dynamic conditional requirements.
|
|
377
|
+
*
|
|
378
|
+
* @param fieldId - The ID of the field to add conditions to
|
|
379
|
+
* @param conditions - Conditional behavior configuration
|
|
380
|
+
* @returns The form builder instance for method chaining
|
|
381
|
+
* @throws Error if the field with the specified ID is not found
|
|
382
|
+
*
|
|
383
|
+
* @example
|
|
384
|
+
* ```typescript
|
|
385
|
+
* builder.addFieldConditions('phone', {
|
|
386
|
+
* visible: when('contactMethod').equals('phone').build(),
|
|
387
|
+
* required: when('contactMethod').equals('phone').build()
|
|
388
|
+
* });
|
|
389
|
+
* ```
|
|
390
|
+
*/
|
|
391
|
+
addFieldConditions(fieldId: string, conditions: ConditionalBehavior): this;
|
|
372
392
|
/**
|
|
373
393
|
* Creates a deep copy of the current form builder
|
|
374
394
|
*
|
|
@@ -551,8 +571,75 @@ interface FormFieldProps {
|
|
|
551
571
|
disabled?: boolean;
|
|
552
572
|
customProps?: Record<string, any>;
|
|
553
573
|
className?: string;
|
|
574
|
+
/** Force field to be visible even if conditions say otherwise (for debugging) */
|
|
575
|
+
forceVisible?: boolean;
|
|
576
|
+
}
|
|
577
|
+
declare function FormField({ fieldId, disabled, customProps, className, forceVisible, }: FormFieldProps): react_jsx_runtime.JSX.Element | null;
|
|
578
|
+
|
|
579
|
+
interface ConditionEvaluationResult {
|
|
580
|
+
visible: boolean;
|
|
581
|
+
disabled: boolean;
|
|
582
|
+
required: boolean;
|
|
583
|
+
readonly: boolean;
|
|
554
584
|
}
|
|
555
|
-
|
|
585
|
+
/**
|
|
586
|
+
* Hook to evaluate conditional behaviors based on form data
|
|
587
|
+
*
|
|
588
|
+
* @param conditions - The conditional behavior configuration
|
|
589
|
+
* @param formData - Current form data to evaluate against
|
|
590
|
+
* @param defaultState - Default state when no conditions are provided
|
|
591
|
+
* @returns Evaluated condition results
|
|
592
|
+
*/
|
|
593
|
+
declare function useConditionEvaluation(conditions?: ConditionalBehavior, formData?: Record<string, any>, defaultState?: Partial<ConditionEvaluationResult>): ConditionEvaluationResult;
|
|
594
|
+
/**
|
|
595
|
+
* Hook to evaluate conditions for multiple fields at once
|
|
596
|
+
*
|
|
597
|
+
* @param fieldsWithConditions - Map of field IDs to their conditional behaviors
|
|
598
|
+
* @param formData - Current form data
|
|
599
|
+
* @returns Map of field IDs to their evaluated conditions
|
|
600
|
+
*/
|
|
601
|
+
declare function useMultipleConditionEvaluation(fieldsWithConditions: Record<string, ConditionalBehavior | undefined>, formData?: Record<string, any>): Record<string, ConditionEvaluationResult>;
|
|
602
|
+
|
|
603
|
+
interface UseFormConditionsProps {
|
|
604
|
+
formConfig: FormConfiguration;
|
|
605
|
+
formValues: Record<string, any>;
|
|
606
|
+
}
|
|
607
|
+
interface UseFormConditionsReturn {
|
|
608
|
+
fieldConditions: Record<string, ConditionEvaluationResult>;
|
|
609
|
+
hasConditionalFields: boolean;
|
|
610
|
+
getFieldCondition: (fieldId: string) => ConditionEvaluationResult | undefined;
|
|
611
|
+
isFieldVisible: (fieldId: string) => boolean;
|
|
612
|
+
isFieldDisabled: (fieldId: string) => boolean;
|
|
613
|
+
isFieldRequired: (fieldId: string) => boolean;
|
|
614
|
+
isFieldReadonly: (fieldId: string) => boolean;
|
|
615
|
+
}
|
|
616
|
+
/**
|
|
617
|
+
* Hook to manage conditional behaviors for form fields
|
|
618
|
+
*
|
|
619
|
+
* This hook evaluates conditions for all form fields and provides
|
|
620
|
+
* convenient methods to check field states.
|
|
621
|
+
*
|
|
622
|
+
* @param props - Configuration for form conditions
|
|
623
|
+
* @returns Object containing field conditions and helper methods
|
|
624
|
+
*
|
|
625
|
+
* @example
|
|
626
|
+
* ```tsx
|
|
627
|
+
* const {
|
|
628
|
+
* fieldConditions,
|
|
629
|
+
* isFieldVisible,
|
|
630
|
+
* isFieldDisabled
|
|
631
|
+
* } = useFormConditions({
|
|
632
|
+
* formConfig,
|
|
633
|
+
* formValues
|
|
634
|
+
* });
|
|
635
|
+
*
|
|
636
|
+
* // Check if a field should be visible
|
|
637
|
+
* if (!isFieldVisible('phoneField')) {
|
|
638
|
+
* return null;
|
|
639
|
+
* }
|
|
640
|
+
* ```
|
|
641
|
+
*/
|
|
642
|
+
declare function useFormConditions({ formConfig, formValues, }: UseFormConditionsProps): UseFormConditionsReturn;
|
|
556
643
|
|
|
557
644
|
interface FormState {
|
|
558
645
|
values: Record<string, any>;
|
|
@@ -561,11 +648,72 @@ interface FormState {
|
|
|
561
648
|
touched: Record<string, boolean>;
|
|
562
649
|
isDirty: boolean;
|
|
563
650
|
isSubmitting: boolean;
|
|
564
|
-
isValid: boolean;
|
|
565
651
|
}
|
|
652
|
+
type FormAction = {
|
|
653
|
+
type: 'SET_VALUE';
|
|
654
|
+
fieldId: string;
|
|
655
|
+
value: any;
|
|
656
|
+
} | {
|
|
657
|
+
type: 'SET_FIELD_ERRORS';
|
|
658
|
+
fieldId: string;
|
|
659
|
+
errors: ValidationError[];
|
|
660
|
+
} | {
|
|
661
|
+
type: 'SET_FIELD_VALIDATION_STATE';
|
|
662
|
+
fieldId: string;
|
|
663
|
+
state: 'idle' | 'validating' | 'valid' | 'invalid';
|
|
664
|
+
} | {
|
|
665
|
+
type: 'SET_FIELD_TOUCHED';
|
|
666
|
+
fieldId: string;
|
|
667
|
+
} | {
|
|
668
|
+
type: 'SET_SUBMITTING';
|
|
669
|
+
isSubmitting: boolean;
|
|
670
|
+
} | {
|
|
671
|
+
type: 'RESET';
|
|
672
|
+
values?: Record<string, any>;
|
|
673
|
+
};
|
|
674
|
+
interface UseFormStateProps {
|
|
675
|
+
defaultValues?: Record<string, any>;
|
|
676
|
+
onFieldChange?: (fieldId: string, value: any, formData: Record<string, any>) => void;
|
|
677
|
+
}
|
|
678
|
+
declare function useFormState({ defaultValues, onFieldChange }: UseFormStateProps): {
|
|
679
|
+
formState: FormState;
|
|
680
|
+
setValue: (fieldId: string, value: any) => void;
|
|
681
|
+
setFieldTouched: (fieldId: string) => void;
|
|
682
|
+
setError: (fieldId: string, errors: ValidationError[]) => void;
|
|
683
|
+
clearError: (fieldId: string) => void;
|
|
684
|
+
setFieldValidationState: (fieldId: string, state: "idle" | "validating" | "valid" | "invalid") => void;
|
|
685
|
+
setSubmitting: (isSubmitting: boolean) => void;
|
|
686
|
+
reset: (values?: Record<string, any>) => void;
|
|
687
|
+
isFormValid: () => boolean;
|
|
688
|
+
};
|
|
689
|
+
|
|
690
|
+
interface UseFormSubmissionProps {
|
|
691
|
+
formState: FormState;
|
|
692
|
+
onSubmit?: (data: Record<string, any>) => void | Promise<void>;
|
|
693
|
+
validateForm: () => Promise<ValidationResult>;
|
|
694
|
+
setSubmitting: (isSubmitting: boolean) => void;
|
|
695
|
+
}
|
|
696
|
+
declare function useFormSubmission({ formState, onSubmit, validateForm, setSubmitting, }: UseFormSubmissionProps): {
|
|
697
|
+
submit: (event?: React$1.FormEvent) => Promise<boolean>;
|
|
698
|
+
};
|
|
699
|
+
|
|
700
|
+
interface UseFormValidationProps {
|
|
701
|
+
formConfig: FormConfiguration;
|
|
702
|
+
formState: FormState;
|
|
703
|
+
conditionsHelpers: Omit<UseFormConditionsReturn, 'fieldConditions'>;
|
|
704
|
+
setFieldValidationState: (fieldId: string, state: 'idle' | 'validating' | 'valid' | 'invalid') => void;
|
|
705
|
+
setError: (fieldId: string, errors: any[]) => void;
|
|
706
|
+
}
|
|
707
|
+
declare function useFormValidation({ formConfig, formState, conditionsHelpers, setFieldValidationState, setError, }: UseFormValidationProps): {
|
|
708
|
+
validateField: (fieldId: string, value?: any) => Promise<ValidationResult>;
|
|
709
|
+
validateForm: () => Promise<ValidationResult>;
|
|
710
|
+
};
|
|
711
|
+
|
|
566
712
|
interface FormContextValue {
|
|
567
713
|
formState: FormState;
|
|
568
714
|
formConfig: FormConfiguration;
|
|
715
|
+
fieldConditions: Record<string, ConditionEvaluationResult>;
|
|
716
|
+
conditionsHelpers: Omit<UseFormConditionsReturn, 'fieldConditions'>;
|
|
569
717
|
setValue: (fieldId: string, value: any) => void;
|
|
570
718
|
setFieldTouched: (fieldId: string, touched?: boolean) => void;
|
|
571
719
|
validateField: (fieldId: string, value?: any) => Promise<ValidationResult>;
|
|
@@ -594,4 +742,4 @@ declare function FormRow({ row, className, ...props }: FormRowProps): react_jsx_
|
|
|
594
742
|
|
|
595
743
|
declare function FormSubmitButton({ className, ...props }: ComponentRendererBaseProps<FormSubmitButtonRendererProps>): react_jsx_runtime.JSX.Element;
|
|
596
744
|
|
|
597
|
-
export { type FieldConfig, Form, FormBody, form as FormBuilder, FormField, FormProvider, FormRow, FormSubmitButton, createForm, form, useFormContext };
|
|
745
|
+
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 se=require('react'),core=require('@rilaykit/core'),jsxRuntime=require('react/jsx-runtime');function _interopDefault(e){return e&&e.__esModule?e:{default:e}}var se__default=/*#__PURE__*/_interopDefault(se);var v=class t{constructor(e,r){this.rows=[];this.idGenerator=new core.IdGenerator;this.config=e,this.formId=r||`form-${Date.now()}`;}static create(e,r){return new t(e,r)}createFormField(e){let r=this.config.getComponent(e.type);if(!r)throw new Error(`No component found with type "${e.type}"`);let i;return (r.validation||e.validation)&&(i={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:i}}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(i=>this.createFormField(i));return {id:this.idGenerator.next("row"),fields:r,maxColumns:e.length}}add(...e){let r,i=false;if(e.length===1&&Array.isArray(e[0])?(r=e[0],i=true):r=e,r.length===0)throw new Error("At least one field is required");if(i&&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 m=this.createRow([o]);this.rows.push(m);}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 i=this.findField(e);if(!i)throw new Error(`Field with ID "${e}" not found`);return Object.assign(i,{...r,props:{...i.props,...r.props}}),this}findField(e){for(let r of this.rows){let i=r.fields.find(o=>o.id===e);if(i)return i}return null}removeField(e){return this.rows=this.rows.map(r=>({...r,fields:r.fields.filter(i=>i.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 i=this.findField(e);if(!i)throw new Error(`Field with ID "${e}" not found`);let o={...i.validation,...r,validators:[...i.validation?.validators||[],...r.validators||[]]};return this.updateField(e,{validation:o})}clone(e){let r=new t(this.config,e||`${this.formId}-clone`);return r.rows=core.deepClone(this.rows),r}validate(){let e=[],r=this.getFields(),i=r.map(o=>o.id);try{core.ensureUnique(i,"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(i=>i.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 Z(t,e){return v.create(t,e)}core.ril.prototype.form=function(t){return v.create(this,t)};function k(){return {isValid:true,errors:[]}}function te(t,e){switch(e.type){case "SET_VALUE":{let r={...t.values,[e.fieldId]:e.value};return {...t,values:r,isDirty:true}}case "SET_FIELD_ERRORS":return {...t,errors:{...t.errors,[e.fieldId]:e.errors},touched:{...t.touched,[e.fieldId]:true}};case "SET_FIELD_VALIDATION_STATE":return {...t,validationState:{...t.validationState,[e.fieldId]:e.state}};case "SET_FIELD_TOUCHED":return {...t,touched:{...t.touched,[e.fieldId]:e.touched}};case "SET_SUBMITTING":return {...t,isSubmitting:e.isSubmitting};case "SET_FORM_VALIDITY":return {...t,isValid:e.isValid};case "RESET":return {values:e.values||{},errors:{},validationState:{},touched:{},isDirty:false,isSubmitting:false,isValid:true};default:return t}}var U=se.createContext(null);function B({children:t,formConfig:e,defaultValues:r={},onSubmit:i,onFieldChange:o,className:m}){let R={values:r,errors:{},validationState:{},touched:{},isDirty:false,isSubmitting:false,isValid:true},[s,d]=se.useReducer(te,R),a=se.useRef(i),c=se.useRef(o),u=se.useRef(r);a.current=i,c.current=o,se.useEffect(()=>{JSON.stringify(u.current)!==JSON.stringify(r)&&(u.current=r,d({type:"RESET",values:r}));},[r]);let E=se.useCallback((n,l)=>{d({type:"SET_VALUE",fieldId:n,value:l}),c.current?.(n,l,{...s.values,[n]:l});},[s.values]),S=se.useCallback((n,l=true)=>{d({type:"SET_FIELD_TOUCHED",fieldId:n,touched:l});},[]),g=se.useCallback(async(n,l)=>{let p=e.allFields.find(f=>f.id===n);if(!p||!p.validation?.validators)return k();let y=l!==void 0?l:s.values[n],_=core.createValidationContext({fieldId:n,formId:e.id,allFormData:{...s.values,[n]:y}});d({type:"SET_FIELD_VALIDATION_STATE",fieldId:n,state:"validating"});try{let f=await core.runValidatorsAsync(p.validation.validators,y,_);return d({type:"SET_FIELD_ERRORS",fieldId:n,errors:f.errors}),d({type:"SET_FIELD_VALIDATION_STATE",fieldId:n,state:f.isValid?"valid":"invalid"}),f}catch(f){let x={isValid:false,errors:[{message:f instanceof Error?f.message:"Validation failed",code:"VALIDATION_ERROR"}]};return d({type:"SET_FIELD_ERRORS",fieldId:n,errors:x.errors}),d({type:"SET_FIELD_VALIDATION_STATE",fieldId:n,state:"invalid"}),x}},[e,s.values]),w=se.useCallback(async()=>{let n=e.allFields.filter(f=>f.validation?.validators).map(f=>g(f.id)),l=await Promise.all(n),p=l.some(f=>!f.isValid),y=k();if(e.validation?.validators){let f=core.createValidationContext({formId:e.id,allFormData:s.values});try{y=await core.runValidatorsAsync(e.validation.validators,s.values,f);}catch(x){y={isValid:false,errors:[{message:x instanceof Error?x.message:"Form validation failed",code:"FORM_VALIDATION_ERROR"}]};}}let _={isValid:!p&&y.isValid,errors:[...l.flatMap(f=>f.errors),...y.errors]};return d({type:"SET_FORM_VALIDITY",isValid:_.isValid}),_},[e,s.values,g]),T=se.useCallback(()=>{let n=Object.values(s.errors).some(p=>p.length>0),l=Object.values(s.validationState).some(p=>p==="invalid");return s.isValid&&!n&&!l},[s.isValid,s.errors,s.validationState]),V=se.useMemo(()=>e,[e]),b=se.useCallback(n=>{d({type:"RESET",values:n});},[]),P=se.useCallback((n,l)=>{d({type:"SET_FIELD_ERRORS",fieldId:n,errors:l}),l.length>0&&d({type:"SET_FORM_VALIDITY",isValid:false});},[]),C=se.useCallback(n=>{d({type:"SET_FIELD_ERRORS",fieldId:n,errors:[]});},[]),D=se.useCallback(async n=>{n?.preventDefault();try{return d({type:"SET_SUBMITTING",isSubmitting:!0}),(await w()).isValid?(a.current&&await a.current(s.values),!0):!1}catch(l){return console.error("Error during form submission:",l),false}finally{d({type:"SET_SUBMITTING",isSubmitting:false});}},[s.values,w]),H=se.useMemo(()=>({formState:s,formConfig:V,setValue:E,setFieldTouched:S,validateField:g,validateForm:w,isFormValid:T,reset:b,submit:D,setError:P,clearError:C}),[s,V,E,S,g,w,T,b,D,P,C]);return jsxRuntime.jsx(U.Provider,{value:H,children:jsxRuntime.jsx("form",{onSubmit:D,className:m,noValidate:true,children:t})})}function h(){let t=se.useContext(U);if(!t)throw new Error("useFormContext must be used within a FormProvider");return t}function ne({formConfig:t,defaultValues:e,onSubmit:r,onFieldChange:i,children:o}){let m=se.useMemo(()=>t instanceof v?t.build():t,[t]);return jsxRuntime.jsx(B,{formConfig:m,defaultValues:e,onSubmit:r,onFieldChange:i,children:o})}function O({fieldId:t,disabled:e=false,customProps:r={},className:i}){let{formState:o,formConfig:m,setValue:R,setFieldTouched:s,validateField:d}=h(),a=se.useMemo(()=>m.allFields.find(C=>C.id===t),[m.allFields,t]);if(!a)throw new Error(`Field with ID "${t}" not found`);let c=se.useMemo(()=>m.config.getComponent(a.componentId),[m.config,a.componentId]);if(!c)throw new Error(`Component with ID "${a.componentId}" not found`);let u=se.useMemo(()=>({value:o.values[t],errors:o.errors[t]||[],validationState:o.validationState[t]||"idle",isValidating:o.validationState[t]==="validating",isTouched:o.touched[t]||false}),[o.values,o.errors,o.validationState,o.touched,t]),E=se.useCallback(async C=>{R(a.id,C),(a.validation?.validateOnChange||u.isTouched)&&await d(a.id,C);},[a.id,a.validation?.validateOnChange,u.isTouched,R,d]),S=se.useCallback(async()=>{u.isTouched||s(a.id,true),(a.validation?.validateOnBlur||!a.validation?.validateOnChange)&&await d(a.id);},[a.id,a.validation?.validateOnBlur,a.validation?.validateOnChange,u.isTouched,s,d]),g=se.useMemo(()=>({...c.defaultProps??{},...a.props,...r}),[c.defaultProps,a.props,r]),w=se.useMemo(()=>({id:a.id,props:g,value:u.value,onChange:E,onBlur:S,disabled:e,error:u.errors,isValidating:u.isValidating,touched:u.isTouched}),[a.id,g,u.value,u.errors,u.isValidating,u.isTouched,E,S,e]),T=m.renderConfig?.fieldRenderer,V=c.renderer(w),b=c.useFieldRenderer!==false,P=T&&b?T({children:V,id:a.id,disabled:e,...g,error:u.errors,isValidating:u.isValidating,touched:u.isTouched}):V;return jsxRuntime.jsx("div",{className:i,"data-field-id":a.id,"data-field-type":c.type,children:P})}se__default.default.memo(O);function J({row:t,className:e,...r}){let{formConfig:i}=h(),o=t.fields.map(R=>jsxRuntime.jsx(O,{fieldId:R.id},R.id)),m={row:t,children:o,className:e};return jsxRuntime.jsx(core.ComponentRendererWrapper,{name:"FormRow",renderer:i.renderConfig?.rowRenderer,props:m,...r,children:o})}var Y=J;function fe({className:t,...e}){let{formConfig:r}=h(),i=se.useMemo(()=>r.rows.map(m=>jsxRuntime.jsx(Y,{row:m},m.id)),[r.rows]),o={formConfig:r,children:i,className:t};return jsxRuntime.jsx(core.ComponentRendererWrapper,{name:"FormBody",renderer:r.renderConfig?.bodyRenderer,props:o,...e,children:i})}function pe({className:t,...e}){let{formState:r,submit:i,formConfig:o}=h(),m={isSubmitting:r.isSubmitting,onSubmit:i,className:t};return jsxRuntime.jsx(core.ComponentRendererWrapper,{name:"FormSubmitButton",renderer:o.renderConfig?.submitButtonRenderer,props:m,...e})}exports.Form=ne;exports.FormBody=fe;exports.FormBuilder=v;exports.FormField=O;exports.FormProvider=B;exports.FormRow=J;exports.FormSubmitButton=pe;exports.createForm=Z;exports.form=v;exports.useFormContext=h;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,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]}})});
|
package/dist/index.mjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import se,{createContext,useMemo,useCallback,useContext,useReducer,useRef,useEffect}from'react';import {ril,IdGenerator,deepClone,ensureUnique,createValidationContext,runValidatorsAsync,ComponentRendererWrapper}from'@rilaykit/core';export*from'@rilaykit/core';import {jsx}from'react/jsx-runtime';var v=class t{constructor(e,r){this.rows=[];this.idGenerator=new IdGenerator;this.config=e,this.formId=r||`form-${Date.now()}`;}static create(e,r){return new t(e,r)}createFormField(e){let r=this.config.getComponent(e.type);if(!r)throw new Error(`No component found with type "${e.type}"`);let i;return (r.validation||e.validation)&&(i={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:i}}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(i=>this.createFormField(i));return {id:this.idGenerator.next("row"),fields:r,maxColumns:e.length}}add(...e){let r,i=false;if(e.length===1&&Array.isArray(e[0])?(r=e[0],i=true):r=e,r.length===0)throw new Error("At least one field is required");if(i&&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 m=this.createRow([o]);this.rows.push(m);}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 i=this.findField(e);if(!i)throw new Error(`Field with ID "${e}" not found`);return Object.assign(i,{...r,props:{...i.props,...r.props}}),this}findField(e){for(let r of this.rows){let i=r.fields.find(o=>o.id===e);if(i)return i}return null}removeField(e){return this.rows=this.rows.map(r=>({...r,fields:r.fields.filter(i=>i.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 i=this.findField(e);if(!i)throw new Error(`Field with ID "${e}" not found`);let o={...i.validation,...r,validators:[...i.validation?.validators||[],...r.validators||[]]};return this.updateField(e,{validation:o})}clone(e){let r=new t(this.config,e||`${this.formId}-clone`);return r.rows=deepClone(this.rows),r}validate(){let e=[],r=this.getFields(),i=r.map(o=>o.id);try{ensureUnique(i,"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(i=>i.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 Z(t,e){return v.create(t,e)}ril.prototype.form=function(t){return v.create(this,t)};function k(){return {isValid:true,errors:[]}}function te(t,e){switch(e.type){case "SET_VALUE":{let r={...t.values,[e.fieldId]:e.value};return {...t,values:r,isDirty:true}}case "SET_FIELD_ERRORS":return {...t,errors:{...t.errors,[e.fieldId]:e.errors},touched:{...t.touched,[e.fieldId]:true}};case "SET_FIELD_VALIDATION_STATE":return {...t,validationState:{...t.validationState,[e.fieldId]:e.state}};case "SET_FIELD_TOUCHED":return {...t,touched:{...t.touched,[e.fieldId]:e.touched}};case "SET_SUBMITTING":return {...t,isSubmitting:e.isSubmitting};case "SET_FORM_VALIDITY":return {...t,isValid:e.isValid};case "RESET":return {values:e.values||{},errors:{},validationState:{},touched:{},isDirty:false,isSubmitting:false,isValid:true};default:return t}}var U=createContext(null);function B({children:t,formConfig:e,defaultValues:r={},onSubmit:i,onFieldChange:o,className:m}){let R={values:r,errors:{},validationState:{},touched:{},isDirty:false,isSubmitting:false,isValid:true},[s,d]=useReducer(te,R),a=useRef(i),c=useRef(o),u=useRef(r);a.current=i,c.current=o,useEffect(()=>{JSON.stringify(u.current)!==JSON.stringify(r)&&(u.current=r,d({type:"RESET",values:r}));},[r]);let E=useCallback((n,l)=>{d({type:"SET_VALUE",fieldId:n,value:l}),c.current?.(n,l,{...s.values,[n]:l});},[s.values]),S=useCallback((n,l=true)=>{d({type:"SET_FIELD_TOUCHED",fieldId:n,touched:l});},[]),g=useCallback(async(n,l)=>{let p=e.allFields.find(f=>f.id===n);if(!p||!p.validation?.validators)return k();let y=l!==void 0?l:s.values[n],_=createValidationContext({fieldId:n,formId:e.id,allFormData:{...s.values,[n]:y}});d({type:"SET_FIELD_VALIDATION_STATE",fieldId:n,state:"validating"});try{let f=await runValidatorsAsync(p.validation.validators,y,_);return d({type:"SET_FIELD_ERRORS",fieldId:n,errors:f.errors}),d({type:"SET_FIELD_VALIDATION_STATE",fieldId:n,state:f.isValid?"valid":"invalid"}),f}catch(f){let x={isValid:false,errors:[{message:f instanceof Error?f.message:"Validation failed",code:"VALIDATION_ERROR"}]};return d({type:"SET_FIELD_ERRORS",fieldId:n,errors:x.errors}),d({type:"SET_FIELD_VALIDATION_STATE",fieldId:n,state:"invalid"}),x}},[e,s.values]),w=useCallback(async()=>{let n=e.allFields.filter(f=>f.validation?.validators).map(f=>g(f.id)),l=await Promise.all(n),p=l.some(f=>!f.isValid),y=k();if(e.validation?.validators){let f=createValidationContext({formId:e.id,allFormData:s.values});try{y=await runValidatorsAsync(e.validation.validators,s.values,f);}catch(x){y={isValid:false,errors:[{message:x instanceof Error?x.message:"Form validation failed",code:"FORM_VALIDATION_ERROR"}]};}}let _={isValid:!p&&y.isValid,errors:[...l.flatMap(f=>f.errors),...y.errors]};return d({type:"SET_FORM_VALIDITY",isValid:_.isValid}),_},[e,s.values,g]),T=useCallback(()=>{let n=Object.values(s.errors).some(p=>p.length>0),l=Object.values(s.validationState).some(p=>p==="invalid");return s.isValid&&!n&&!l},[s.isValid,s.errors,s.validationState]),V=useMemo(()=>e,[e]),b=useCallback(n=>{d({type:"RESET",values:n});},[]),P=useCallback((n,l)=>{d({type:"SET_FIELD_ERRORS",fieldId:n,errors:l}),l.length>0&&d({type:"SET_FORM_VALIDITY",isValid:false});},[]),C=useCallback(n=>{d({type:"SET_FIELD_ERRORS",fieldId:n,errors:[]});},[]),D=useCallback(async n=>{n?.preventDefault();try{return d({type:"SET_SUBMITTING",isSubmitting:!0}),(await w()).isValid?(a.current&&await a.current(s.values),!0):!1}catch(l){return console.error("Error during form submission:",l),false}finally{d({type:"SET_SUBMITTING",isSubmitting:false});}},[s.values,w]),H=useMemo(()=>({formState:s,formConfig:V,setValue:E,setFieldTouched:S,validateField:g,validateForm:w,isFormValid:T,reset:b,submit:D,setError:P,clearError:C}),[s,V,E,S,g,w,T,b,D,P,C]);return jsx(U.Provider,{value:H,children:jsx("form",{onSubmit:D,className:m,noValidate:true,children:t})})}function h(){let t=useContext(U);if(!t)throw new Error("useFormContext must be used within a FormProvider");return t}function ne({formConfig:t,defaultValues:e,onSubmit:r,onFieldChange:i,children:o}){let m=useMemo(()=>t instanceof v?t.build():t,[t]);return jsx(B,{formConfig:m,defaultValues:e,onSubmit:r,onFieldChange:i,children:o})}function O({fieldId:t,disabled:e=false,customProps:r={},className:i}){let{formState:o,formConfig:m,setValue:R,setFieldTouched:s,validateField:d}=h(),a=useMemo(()=>m.allFields.find(C=>C.id===t),[m.allFields,t]);if(!a)throw new Error(`Field with ID "${t}" not found`);let c=useMemo(()=>m.config.getComponent(a.componentId),[m.config,a.componentId]);if(!c)throw new Error(`Component with ID "${a.componentId}" not found`);let u=useMemo(()=>({value:o.values[t],errors:o.errors[t]||[],validationState:o.validationState[t]||"idle",isValidating:o.validationState[t]==="validating",isTouched:o.touched[t]||false}),[o.values,o.errors,o.validationState,o.touched,t]),E=useCallback(async C=>{R(a.id,C),(a.validation?.validateOnChange||u.isTouched)&&await d(a.id,C);},[a.id,a.validation?.validateOnChange,u.isTouched,R,d]),S=useCallback(async()=>{u.isTouched||s(a.id,true),(a.validation?.validateOnBlur||!a.validation?.validateOnChange)&&await d(a.id);},[a.id,a.validation?.validateOnBlur,a.validation?.validateOnChange,u.isTouched,s,d]),g=useMemo(()=>({...c.defaultProps??{},...a.props,...r}),[c.defaultProps,a.props,r]),w=useMemo(()=>({id:a.id,props:g,value:u.value,onChange:E,onBlur:S,disabled:e,error:u.errors,isValidating:u.isValidating,touched:u.isTouched}),[a.id,g,u.value,u.errors,u.isValidating,u.isTouched,E,S,e]),T=m.renderConfig?.fieldRenderer,V=c.renderer(w),b=c.useFieldRenderer!==false,P=T&&b?T({children:V,id:a.id,disabled:e,...g,error:u.errors,isValidating:u.isValidating,touched:u.isTouched}):V;return jsx("div",{className:i,"data-field-id":a.id,"data-field-type":c.type,children:P})}se.memo(O);function J({row:t,className:e,...r}){let{formConfig:i}=h(),o=t.fields.map(R=>jsx(O,{fieldId:R.id},R.id)),m={row:t,children:o,className:e};return jsx(ComponentRendererWrapper,{name:"FormRow",renderer:i.renderConfig?.rowRenderer,props:m,...r,children:o})}var Y=J;function fe({className:t,...e}){let{formConfig:r}=h(),i=useMemo(()=>r.rows.map(m=>jsx(Y,{row:m},m.id)),[r.rows]),o={formConfig:r,children:i,className:t};return jsx(ComponentRendererWrapper,{name:"FormBody",renderer:r.renderConfig?.bodyRenderer,props:o,...e,children:i})}function pe({className:t,...e}){let{formState:r,submit:i,formConfig:o}=h(),m={isSubmitting:r.isSubmitting,onSubmit:i,className:t};return jsx(ComponentRendererWrapper,{name:"FormSubmitButton",renderer:o.renderConfig?.submitButtonRenderer,props:m,...e})}export{ne as Form,fe as FormBody,v as FormBuilder,O as FormField,B as FormProvider,J as FormRow,pe as FormSubmitButton,Z as createForm,v as form,h as useFormContext};
|
|
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};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rilaykit/forms",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "6.0.0",
|
|
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": "
|
|
27
|
+
"@rilaykit/core": "6.0.0"
|
|
28
28
|
},
|
|
29
29
|
"peerDependencies": {
|
|
30
30
|
"react": ">=18.0.0",
|