@faasjs/react 3.7.0-beta.6 → 3.7.0-beta.8

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/README.md CHANGED
@@ -34,7 +34,7 @@ React plugin for FaasJS.
34
34
  ## Install
35
35
 
36
36
  ```sh
37
- npm install @faasjs/react
37
+ npm install @faasjs/react react
38
38
  ```
39
39
 
40
40
  ## Functions
@@ -46,6 +46,7 @@ npm install @faasjs/react
46
46
  - [FaasReactClient](functions/FaasReactClient.md)
47
47
  - [Form](functions/Form.md)
48
48
  - [FormContextProvider](functions/FormContextProvider.md)
49
+ - [FormItem](functions/FormItem.md)
49
50
  - [getClient](functions/getClient.md)
50
51
  - [OptionalWrapper](functions/OptionalWrapper.md)
51
52
  - [useConstant](functions/useConstant.md)
@@ -56,6 +57,7 @@ npm install @faasjs/react
56
57
  - [useFaas](functions/useFaas.md)
57
58
  - [useFormContext](functions/useFormContext.md)
58
59
  - [useSplittingState](functions/useSplittingState.md)
60
+ - [validValues](functions/validValues.md)
59
61
  - [withFaasData](functions/withFaasData.md)
60
62
 
61
63
  ## Classes
@@ -80,10 +82,16 @@ npm install @faasjs/react
80
82
  - [FaasReactClientOptions](type-aliases/FaasReactClientOptions.md)
81
83
  - [FormButtonElementProps](type-aliases/FormButtonElementProps.md)
82
84
  - [FormContextProps](type-aliases/FormContextProps.md)
85
+ - [FormDefaultRulesOptions](type-aliases/FormDefaultRulesOptions.md)
83
86
  - [FormElementTypes](type-aliases/FormElementTypes.md)
84
87
  - [FormInputElementProps](type-aliases/FormInputElementProps.md)
88
+ - [FormItemName](type-aliases/FormItemName.md)
89
+ - [FormItemProps](type-aliases/FormItemProps.md)
85
90
  - [FormLabelElementProps](type-aliases/FormLabelElementProps.md)
86
91
  - [FormProps](type-aliases/FormProps.md)
92
+ - [FormRule](type-aliases/FormRule.md)
93
+ - [FormRules](type-aliases/FormRules.md)
94
+ - [InferFormRulesOptions](type-aliases/InferFormRulesOptions.md)
87
95
  - [OnError](type-aliases/OnError.md)
88
96
  - [OptionalWrapperProps](type-aliases/OptionalWrapperProps.md)
89
97
  - [Options](type-aliases/Options.md)
@@ -93,3 +101,4 @@ npm install @faasjs/react
93
101
  ## Variables
94
102
 
95
103
  - [FormDefaultElements](variables/FormDefaultElements.md)
104
+ - [FormDefaultRules](variables/FormDefaultRules.md)
package/dist/index.d.mts CHANGED
@@ -124,7 +124,7 @@ declare function createSplittingContext<T extends Record<string, any>>(defaultVa
124
124
  * ```
125
125
  */
126
126
  Provider<NewT extends T = T>(props: {
127
- value?: NewT;
127
+ value?: Partial<NewT>;
128
128
  children: ReactNode;
129
129
  /**
130
130
  * Whether to use memoization for the children.
@@ -135,6 +135,20 @@ declare function createSplittingContext<T extends Record<string, any>>(defaultVa
135
135
  * `any[]`: memoize the children with specific dependencies.
136
136
  */
137
137
  memo?: true | any[];
138
+ /**
139
+ * An object containing initial values that will be automatically converted into state variables using {@link useSplittingState} hook. Each property will create both a state value and its setter following the pattern: value/setValue.
140
+ *
141
+ * @example
142
+ * ```tsx
143
+ * <Provider
144
+ * initializeStates={{
145
+ * value: 0,
146
+ * }}
147
+ * >
148
+ * // Children will have access to: value, setValue
149
+ * </Provider>
150
+ */
151
+ initializeStates?: Partial<NewT>;
138
152
  }): ReactNode;
139
153
  /**
140
154
  * The hook to use the splitting context.
@@ -394,21 +408,39 @@ type FormInputElementProps = {
394
408
  onChange: (value: any) => void;
395
409
  };
396
410
 
411
+ type FormLabelElementProps = {
412
+ name: string;
413
+ title?: ReactNode;
414
+ description?: ReactNode;
415
+ error?: Error;
416
+ /** as Input element */
417
+ children: ReactNode;
418
+ };
419
+
420
+ type FormElementTypes = {
421
+ Label: ComponentType<FormLabelElementProps>;
422
+ Input: ComponentType<FormInputElementProps>;
423
+ Button: ComponentType<FormButtonElementProps>;
424
+ };
425
+ declare const FormDefaultElements: FormElementTypes;
426
+
397
427
  declare const FormDefaultLang: {
398
428
  submit: string;
399
429
  required: string;
430
+ string: string;
400
431
  number: string;
401
432
  };
402
433
  type FormLang = typeof FormDefaultLang;
403
434
 
404
- type FormRules = {
405
- type?: 'string' | 'number';
406
- required?: boolean;
407
- custom?: (value: any) => Promise<FormError | undefined>;
408
- };
409
- type FormError = {
410
- message: string;
435
+ type FormRule<Options = any> = (value: any, options?: Options, lang?: FormLang) => Promise<void>;
436
+ type InferRuleOption<T> = T extends (value: any, options: infer O, lang?: FormLang) => Promise<void> ? O : never;
437
+ type FormRules = Record<string, FormRule>;
438
+ type InferFormRulesOptions<T> = {
439
+ [K in keyof T]: InferRuleOption<T[K]>;
411
440
  };
441
+ declare const FormDefaultRules: FormRules;
442
+ type FormDefaultRulesOptions = InferFormRulesOptions<typeof FormDefaultRules>;
443
+ declare function validValues(rules: FormRules, items: FormItemProps[], values: Record<string, any>, lang: FormLang): Promise<Record<string, Error>>;
412
444
 
413
445
  type InferFormInputProps<T extends ComponentType<FormInputElementProps> | JSXElementConstructor<any>> = T extends ComponentType<FormInputElementProps> ? Omit<ComponentProps<T>, 'name' | 'value' | 'onChange'> : Omit<ComponentProps<T>, 'name' | 'value'>;
414
446
  type FormInputProps<FormElements extends FormElementTypes = FormElementTypes> = {
@@ -416,52 +448,71 @@ type FormInputProps<FormElements extends FormElementTypes = FormElementTypes> =
416
448
  props?: InferFormInputProps<FormElements['Input']>;
417
449
  };
418
450
 
419
- type FormLabelElementProps<FormElements extends FormElementTypes = FormElementTypes> = {
420
- name: string;
421
- rules?: FormRules;
422
- title?: ReactNode;
423
- description?: ReactNode;
424
- Label?: FormElements['Label'];
451
+ type FormItemName = string;
452
+ type FormItemProps<FormElements extends FormElementTypes = FormElementTypes, FormRulesOptions extends Record<string, any> = FormDefaultRulesOptions> = {
453
+ name: FormItemName;
454
+ label?: Omit<FormLabelElementProps, 'name'> & {
455
+ Label?: ComponentType<FormLabelElementProps>;
456
+ };
425
457
  input?: FormInputProps<FormElements>;
458
+ rules?: FormRulesOptions;
426
459
  };
460
+ declare function FormItem(props: FormItemProps): react_jsx_runtime.JSX.Element;
461
+ declare namespace FormItem {
462
+ var displayName: string;
463
+ var whyDidYouRender: boolean;
464
+ }
427
465
 
428
- type FormElementTypes = {
429
- Label: ComponentType<FormLabelElementProps>;
430
- Input: ComponentType<FormInputElementProps>;
431
- Button: ComponentType<FormButtonElementProps>;
432
- };
433
- declare const FormDefaultElements: FormElementTypes;
434
-
435
- type FormProps<Values extends Record<string, any> = Record<string, any>, FormElements extends FormElementTypes = FormElementTypes> = {
436
- items: FormLabelElementProps<FormElements>[];
466
+ type FormProps<Values extends Record<string, any> = Record<string, any>, FormElements extends FormElementTypes = FormElementTypes, Rules extends FormRules = typeof FormDefaultRules> = {
467
+ items: FormItemProps<FormElements, InferFormRulesOptions<Rules>>[];
437
468
  onSubmit?: (values: Values) => Promise<void>;
438
469
  Elements?: Partial<FormElements>;
439
470
  lang?: Partial<FormLang>;
440
471
  defaultValues?: Values;
472
+ rules?: typeof FormDefaultRules & Rules;
441
473
  };
442
- declare function FormContainer<Values extends Record<string, any> = Record<string, any>, FormElements extends FormElementTypes = FormElementTypes>({ defaultValues, Elements, lang, ...props }: FormProps<Values, FormElements>): react_jsx_runtime.JSX.Element;
474
+ /**
475
+ * FormContainer component is a wrapper that provides context and state management for form elements.
476
+ * It initializes form states such as values, errors, submitting status, elements, language, and rules.
477
+ *
478
+ * @template Values - The type of form values, defaults to Record<string, any>.
479
+ * @template FormElements - The type of form elements, defaults to FormElementTypes.
480
+ * @template Rules - The type of form rules, defaults to FormDefaultRules.
481
+ *
482
+ * @param {FormProps<Values, FormElements, Rules>} props - The properties for the FormContainer component.
483
+ * @param {Values} props.defaultValues - The default values for the form fields.
484
+ * @param {FormElements} props.Elements - The form elements to be used in the form.
485
+ * @param {Rules} props.rules - The validation rules for the form fields.
486
+ * @param {FormLang} props.lang - The language settings for the form.
487
+ * @param {Partial<FormContextProps>} props - Additional properties for the form context.
488
+ *
489
+ * @returns {JSX.Element} The FormContainer component.
490
+ */
491
+ declare function FormContainer<Values extends Record<string, any> = Record<string, any>, FormElements extends FormElementTypes = FormElementTypes, Rules extends FormRules = typeof FormDefaultRules>({ defaultValues, Elements, rules, lang, ...props }: FormProps<Values, FormElements, Rules>): react_jsx_runtime.JSX.Element;
443
492
  declare namespace FormContainer {
444
493
  var displayName: string;
445
494
  var whyDidYouRender: boolean;
446
495
  }
447
496
 
448
- type FormContextProps<Values extends Record<string, any> = Record<string, any>> = {
497
+ type FormContextProps<Values extends Record<string, any> = Record<string, any>, Rules extends FormRules = typeof FormDefaultRules> = {
449
498
  items: FormLabelElementProps[];
450
499
  onSubmit: (values: Values) => Promise<void>;
451
500
  Elements: FormElementTypes;
452
501
  lang: FormLang;
502
+ rules: typeof FormDefaultRules & Rules;
453
503
  submitting: boolean;
454
504
  setSubmitting: Dispatch<SetStateAction<boolean>>;
455
505
  values: Values;
456
506
  setValues: Dispatch<SetStateAction<Values>>;
457
- errors: Record<string, FormError>;
458
- setErrors: Dispatch<SetStateAction<Record<string, FormError>>>;
507
+ errors: Record<string, Error>;
508
+ setErrors: Dispatch<SetStateAction<Record<string, Error>>>;
459
509
  };
460
- declare const FormContextProvider: <NewT extends FormContextProps<Record<string, any>> = FormContextProps<Record<string, any>>>(props: {
461
- value?: NewT;
510
+ declare const FormContextProvider: <NewT extends FormContextProps<Record<string, any>, FormRules> = FormContextProps<Record<string, any>, FormRules>>(props: {
511
+ value?: Partial<NewT>;
462
512
  children: react.ReactNode;
463
513
  memo?: true | any[];
514
+ initializeStates?: Partial<NewT>;
464
515
  }) => react.ReactNode;
465
- declare const useFormContext: <NewT extends FormContextProps<Record<string, any>> = FormContextProps<Record<string, any>>>() => Readonly<NewT>;
516
+ declare const useFormContext: <NewT extends FormContextProps<Record<string, any>, FormRules> = FormContextProps<Record<string, any>, FormRules>>() => Readonly<NewT>;
466
517
 
467
- export { ErrorBoundary, type ErrorBoundaryProps, type ErrorChildrenProps, type FaasDataInjection, FaasDataWrapper, type FaasDataWrapperProps, FaasReactClient, type FaasReactClientInstance, type FaasReactClientOptions, FormContainer as Form, type FormButtonElementProps, type FormContextProps, FormContextProvider, FormDefaultElements, type FormElementTypes, type FormInputElementProps, type FormLabelElementProps, type FormProps, type OnError, OptionalWrapper, type OptionalWrapperProps, createSplittingContext, equal, faas, getClient, useConstant, useEqualCallback, useEqualEffect, useEqualMemo, useEqualMemoize, useFaas, type useFaasOptions, useFormContext, useSplittingState, withFaasData };
518
+ export { ErrorBoundary, type ErrorBoundaryProps, type ErrorChildrenProps, type FaasDataInjection, FaasDataWrapper, type FaasDataWrapperProps, FaasReactClient, type FaasReactClientInstance, type FaasReactClientOptions, FormContainer as Form, type FormButtonElementProps, type FormContextProps, FormContextProvider, FormDefaultElements, FormDefaultRules, type FormDefaultRulesOptions, type FormElementTypes, type FormInputElementProps, FormItem, type FormItemName, type FormItemProps, type FormLabelElementProps, type FormProps, type FormRule, type FormRules, type InferFormRulesOptions, type OnError, OptionalWrapper, type OptionalWrapperProps, createSplittingContext, equal, faas, getClient, useConstant, useEqualCallback, useEqualEffect, useEqualMemo, useEqualMemoize, useFaas, type useFaasOptions, useFormContext, useSplittingState, validValues, withFaasData };
package/dist/index.d.ts CHANGED
@@ -124,7 +124,7 @@ declare function createSplittingContext<T extends Record<string, any>>(defaultVa
124
124
  * ```
125
125
  */
126
126
  Provider<NewT extends T = T>(props: {
127
- value?: NewT;
127
+ value?: Partial<NewT>;
128
128
  children: ReactNode;
129
129
  /**
130
130
  * Whether to use memoization for the children.
@@ -135,6 +135,20 @@ declare function createSplittingContext<T extends Record<string, any>>(defaultVa
135
135
  * `any[]`: memoize the children with specific dependencies.
136
136
  */
137
137
  memo?: true | any[];
138
+ /**
139
+ * An object containing initial values that will be automatically converted into state variables using {@link useSplittingState} hook. Each property will create both a state value and its setter following the pattern: value/setValue.
140
+ *
141
+ * @example
142
+ * ```tsx
143
+ * <Provider
144
+ * initializeStates={{
145
+ * value: 0,
146
+ * }}
147
+ * >
148
+ * // Children will have access to: value, setValue
149
+ * </Provider>
150
+ */
151
+ initializeStates?: Partial<NewT>;
138
152
  }): ReactNode;
139
153
  /**
140
154
  * The hook to use the splitting context.
@@ -394,21 +408,39 @@ type FormInputElementProps = {
394
408
  onChange: (value: any) => void;
395
409
  };
396
410
 
411
+ type FormLabelElementProps = {
412
+ name: string;
413
+ title?: ReactNode;
414
+ description?: ReactNode;
415
+ error?: Error;
416
+ /** as Input element */
417
+ children: ReactNode;
418
+ };
419
+
420
+ type FormElementTypes = {
421
+ Label: ComponentType<FormLabelElementProps>;
422
+ Input: ComponentType<FormInputElementProps>;
423
+ Button: ComponentType<FormButtonElementProps>;
424
+ };
425
+ declare const FormDefaultElements: FormElementTypes;
426
+
397
427
  declare const FormDefaultLang: {
398
428
  submit: string;
399
429
  required: string;
430
+ string: string;
400
431
  number: string;
401
432
  };
402
433
  type FormLang = typeof FormDefaultLang;
403
434
 
404
- type FormRules = {
405
- type?: 'string' | 'number';
406
- required?: boolean;
407
- custom?: (value: any) => Promise<FormError | undefined>;
408
- };
409
- type FormError = {
410
- message: string;
435
+ type FormRule<Options = any> = (value: any, options?: Options, lang?: FormLang) => Promise<void>;
436
+ type InferRuleOption<T> = T extends (value: any, options: infer O, lang?: FormLang) => Promise<void> ? O : never;
437
+ type FormRules = Record<string, FormRule>;
438
+ type InferFormRulesOptions<T> = {
439
+ [K in keyof T]: InferRuleOption<T[K]>;
411
440
  };
441
+ declare const FormDefaultRules: FormRules;
442
+ type FormDefaultRulesOptions = InferFormRulesOptions<typeof FormDefaultRules>;
443
+ declare function validValues(rules: FormRules, items: FormItemProps[], values: Record<string, any>, lang: FormLang): Promise<Record<string, Error>>;
412
444
 
413
445
  type InferFormInputProps<T extends ComponentType<FormInputElementProps> | JSXElementConstructor<any>> = T extends ComponentType<FormInputElementProps> ? Omit<ComponentProps<T>, 'name' | 'value' | 'onChange'> : Omit<ComponentProps<T>, 'name' | 'value'>;
414
446
  type FormInputProps<FormElements extends FormElementTypes = FormElementTypes> = {
@@ -416,52 +448,71 @@ type FormInputProps<FormElements extends FormElementTypes = FormElementTypes> =
416
448
  props?: InferFormInputProps<FormElements['Input']>;
417
449
  };
418
450
 
419
- type FormLabelElementProps<FormElements extends FormElementTypes = FormElementTypes> = {
420
- name: string;
421
- rules?: FormRules;
422
- title?: ReactNode;
423
- description?: ReactNode;
424
- Label?: FormElements['Label'];
451
+ type FormItemName = string;
452
+ type FormItemProps<FormElements extends FormElementTypes = FormElementTypes, FormRulesOptions extends Record<string, any> = FormDefaultRulesOptions> = {
453
+ name: FormItemName;
454
+ label?: Omit<FormLabelElementProps, 'name'> & {
455
+ Label?: ComponentType<FormLabelElementProps>;
456
+ };
425
457
  input?: FormInputProps<FormElements>;
458
+ rules?: FormRulesOptions;
426
459
  };
460
+ declare function FormItem(props: FormItemProps): react_jsx_runtime.JSX.Element;
461
+ declare namespace FormItem {
462
+ var displayName: string;
463
+ var whyDidYouRender: boolean;
464
+ }
427
465
 
428
- type FormElementTypes = {
429
- Label: ComponentType<FormLabelElementProps>;
430
- Input: ComponentType<FormInputElementProps>;
431
- Button: ComponentType<FormButtonElementProps>;
432
- };
433
- declare const FormDefaultElements: FormElementTypes;
434
-
435
- type FormProps<Values extends Record<string, any> = Record<string, any>, FormElements extends FormElementTypes = FormElementTypes> = {
436
- items: FormLabelElementProps<FormElements>[];
466
+ type FormProps<Values extends Record<string, any> = Record<string, any>, FormElements extends FormElementTypes = FormElementTypes, Rules extends FormRules = typeof FormDefaultRules> = {
467
+ items: FormItemProps<FormElements, InferFormRulesOptions<Rules>>[];
437
468
  onSubmit?: (values: Values) => Promise<void>;
438
469
  Elements?: Partial<FormElements>;
439
470
  lang?: Partial<FormLang>;
440
471
  defaultValues?: Values;
472
+ rules?: typeof FormDefaultRules & Rules;
441
473
  };
442
- declare function FormContainer<Values extends Record<string, any> = Record<string, any>, FormElements extends FormElementTypes = FormElementTypes>({ defaultValues, Elements, lang, ...props }: FormProps<Values, FormElements>): react_jsx_runtime.JSX.Element;
474
+ /**
475
+ * FormContainer component is a wrapper that provides context and state management for form elements.
476
+ * It initializes form states such as values, errors, submitting status, elements, language, and rules.
477
+ *
478
+ * @template Values - The type of form values, defaults to Record<string, any>.
479
+ * @template FormElements - The type of form elements, defaults to FormElementTypes.
480
+ * @template Rules - The type of form rules, defaults to FormDefaultRules.
481
+ *
482
+ * @param {FormProps<Values, FormElements, Rules>} props - The properties for the FormContainer component.
483
+ * @param {Values} props.defaultValues - The default values for the form fields.
484
+ * @param {FormElements} props.Elements - The form elements to be used in the form.
485
+ * @param {Rules} props.rules - The validation rules for the form fields.
486
+ * @param {FormLang} props.lang - The language settings for the form.
487
+ * @param {Partial<FormContextProps>} props - Additional properties for the form context.
488
+ *
489
+ * @returns {JSX.Element} The FormContainer component.
490
+ */
491
+ declare function FormContainer<Values extends Record<string, any> = Record<string, any>, FormElements extends FormElementTypes = FormElementTypes, Rules extends FormRules = typeof FormDefaultRules>({ defaultValues, Elements, rules, lang, ...props }: FormProps<Values, FormElements, Rules>): react_jsx_runtime.JSX.Element;
443
492
  declare namespace FormContainer {
444
493
  var displayName: string;
445
494
  var whyDidYouRender: boolean;
446
495
  }
447
496
 
448
- type FormContextProps<Values extends Record<string, any> = Record<string, any>> = {
497
+ type FormContextProps<Values extends Record<string, any> = Record<string, any>, Rules extends FormRules = typeof FormDefaultRules> = {
449
498
  items: FormLabelElementProps[];
450
499
  onSubmit: (values: Values) => Promise<void>;
451
500
  Elements: FormElementTypes;
452
501
  lang: FormLang;
502
+ rules: typeof FormDefaultRules & Rules;
453
503
  submitting: boolean;
454
504
  setSubmitting: Dispatch<SetStateAction<boolean>>;
455
505
  values: Values;
456
506
  setValues: Dispatch<SetStateAction<Values>>;
457
- errors: Record<string, FormError>;
458
- setErrors: Dispatch<SetStateAction<Record<string, FormError>>>;
507
+ errors: Record<string, Error>;
508
+ setErrors: Dispatch<SetStateAction<Record<string, Error>>>;
459
509
  };
460
- declare const FormContextProvider: <NewT extends FormContextProps<Record<string, any>> = FormContextProps<Record<string, any>>>(props: {
461
- value?: NewT;
510
+ declare const FormContextProvider: <NewT extends FormContextProps<Record<string, any>, FormRules> = FormContextProps<Record<string, any>, FormRules>>(props: {
511
+ value?: Partial<NewT>;
462
512
  children: react.ReactNode;
463
513
  memo?: true | any[];
514
+ initializeStates?: Partial<NewT>;
464
515
  }) => react.ReactNode;
465
- declare const useFormContext: <NewT extends FormContextProps<Record<string, any>> = FormContextProps<Record<string, any>>>() => Readonly<NewT>;
516
+ declare const useFormContext: <NewT extends FormContextProps<Record<string, any>, FormRules> = FormContextProps<Record<string, any>, FormRules>>() => Readonly<NewT>;
466
517
 
467
- export { ErrorBoundary, type ErrorBoundaryProps, type ErrorChildrenProps, type FaasDataInjection, FaasDataWrapper, type FaasDataWrapperProps, FaasReactClient, type FaasReactClientInstance, type FaasReactClientOptions, FormContainer as Form, type FormButtonElementProps, type FormContextProps, FormContextProvider, FormDefaultElements, type FormElementTypes, type FormInputElementProps, type FormLabelElementProps, type FormProps, type OnError, OptionalWrapper, type OptionalWrapperProps, createSplittingContext, equal, faas, getClient, useConstant, useEqualCallback, useEqualEffect, useEqualMemo, useEqualMemoize, useFaas, type useFaasOptions, useFormContext, useSplittingState, withFaasData };
518
+ export { ErrorBoundary, type ErrorBoundaryProps, type ErrorChildrenProps, type FaasDataInjection, FaasDataWrapper, type FaasDataWrapperProps, FaasReactClient, type FaasReactClientInstance, type FaasReactClientOptions, FormContainer as Form, type FormButtonElementProps, type FormContextProps, FormContextProvider, FormDefaultElements, FormDefaultRules, type FormDefaultRulesOptions, type FormElementTypes, type FormInputElementProps, FormItem, type FormItemName, type FormItemProps, type FormLabelElementProps, type FormProps, type FormRule, type FormRules, type InferFormRulesOptions, type OnError, OptionalWrapper, type OptionalWrapperProps, createSplittingContext, equal, faas, getClient, useConstant, useEqualCallback, useEqualEffect, useEqualMemo, useEqualMemoize, useFaas, type useFaasOptions, useFormContext, useSplittingState, validValues, withFaasData };
package/dist/index.js CHANGED
@@ -49,7 +49,6 @@ function equal(a, b) {
49
49
  return a === b;
50
50
  case Object: {
51
51
  const keys = Object.keys(a);
52
- if (keys.length !== Object.keys(b).length) return false;
53
52
  for (const key of keys) {
54
53
  if (!equal(a[key], b[key])) return false;
55
54
  }
@@ -80,6 +79,17 @@ function useEqualCallback(callback, dependencies) {
80
79
  useEqualMemoize(dependencies)
81
80
  );
82
81
  }
82
+ function useSplittingState(initialStates) {
83
+ const states = {};
84
+ for (const key of Object.keys(initialStates)) {
85
+ const state = react.useState(initialStates[key]);
86
+ Object.assign(states, {
87
+ [key]: state[0],
88
+ [`set${String(key).charAt(0).toUpperCase()}${String(key).slice(1)}`]: state[1]
89
+ });
90
+ }
91
+ return states;
92
+ }
83
93
  function createSplittingContext(defaultValue) {
84
94
  const keys = Array.isArray(defaultValue) ? defaultValue : Object.keys(defaultValue);
85
95
  const defaultValues = Array.isArray(defaultValue) ? keys.reduce((prev, cur) => {
@@ -89,13 +99,14 @@ function createSplittingContext(defaultValue) {
89
99
  const contexts = {};
90
100
  for (const key of keys) contexts[key] = react.createContext(defaultValues[key]);
91
101
  function Provider(props) {
102
+ const states = props.initializeStates ? useSplittingState(props.initializeStates) : {};
92
103
  let children = props.memo ? useEqualMemo(
93
104
  () => props.children,
94
105
  props.memo === true ? [] : props.memo
95
106
  ) : props.children;
96
107
  for (const key of keys) {
97
108
  const Context = contexts[key];
98
- const value = props.value?.[key] ?? defaultValues[key];
109
+ const value = props.value?.[key] ?? states[key] ?? defaultValues[key];
99
110
  children = /* @__PURE__ */ jsxRuntime.jsx(Context.Provider, { value, children });
100
111
  }
101
112
  return children;
@@ -119,17 +130,6 @@ function createSplittingContext(defaultValue) {
119
130
  use
120
131
  };
121
132
  }
122
- function useSplittingState(initialStates) {
123
- const states = {};
124
- for (const key of Object.keys(initialStates)) {
125
- const state = react.useState(initialStates[key]);
126
- Object.assign(states, {
127
- [key]: state[0],
128
- [`set${String(key).charAt(0).toUpperCase()}${String(key).slice(1)}`]: state[1]
129
- });
130
- }
131
- return states;
132
- }
133
133
  function FaasDataWrapper(props) {
134
134
  const request = getClient(props.baseUrl).useFaas(
135
135
  props.action,
@@ -354,40 +354,71 @@ var FormContext = createSplittingContext([
354
354
  "values",
355
355
  "setValues",
356
356
  "errors",
357
- "setErrors"
357
+ "setErrors",
358
+ "rules"
358
359
  ]);
359
360
  var FormContextProvider = FormContext.Provider;
360
361
  var useFormContext = FormContext.use;
361
- function FormLabel(props) {
362
- const { Elements } = useFormContext();
363
- if (props.Label) return /* @__PURE__ */ jsxRuntime.jsx(props.Label, { ...props });
364
- return /* @__PURE__ */ jsxRuntime.jsx(Elements.Label, { ...props });
362
+ function FormItem(props) {
363
+ const { Elements, values, setValues } = useFormContext();
364
+ const Label = props.label?.Label ?? Elements.Label;
365
+ const Input = props.input?.Input ?? Elements.Input;
366
+ return /* @__PURE__ */ jsxRuntime.jsx(Label, { name: props.name, ...props.label, children: /* @__PURE__ */ jsxRuntime.jsx(
367
+ Input,
368
+ {
369
+ name: props.name,
370
+ value: values[props.name],
371
+ onChange: (v) => setValues((prev) => ({
372
+ ...prev,
373
+ [props.name]: v
374
+ }))
375
+ }
376
+ ) });
365
377
  }
366
- FormLabel.displayName = "FormLabel";
367
- FormLabel.whyDidYouRender = true;
378
+ FormItem.displayName = "FormItem";
379
+ FormItem.whyDidYouRender = true;
368
380
  function FormBody() {
369
381
  const { items } = useFormContext();
370
- return items.map((item) => /* @__PURE__ */ jsxRuntime.jsx(FormLabel, { ...item }, item.name));
382
+ return items.map((item) => /* @__PURE__ */ jsxRuntime.jsx(FormItem, { ...item }, item.name));
371
383
  }
372
384
  FormBody.displayName = "FormBody";
373
385
  FormBody.whyDidYouRender = true;
374
386
 
375
387
  // src/Form/rules.ts
376
- async function validValue(rules, value, lang) {
377
- if (rules.required && (value === null || value === void 0 || value === "" || Number.isNaN(value)))
378
- return { message: lang.required };
379
- if (rules.type === "number" && Number.isNaN(Number(value)))
380
- return { message: lang.number };
381
- return rules.custom?.(value);
382
- }
383
- async function validValues(items, values, lang) {
388
+ var FormDefaultRules = {
389
+ required: async (value, _, lang) => {
390
+ if (value === null || value === void 0 || value === "" || Number.isNaN(value)) {
391
+ throw Error(lang?.required);
392
+ }
393
+ },
394
+ type: async (value, options, lang) => {
395
+ switch (options) {
396
+ case "string":
397
+ if (typeof value !== "string") throw Error(lang?.string);
398
+ break;
399
+ case "number":
400
+ if (Number.isNaN(Number(value))) throw Error(lang?.number);
401
+ break;
402
+ }
403
+ },
404
+ custom: async (value, options) => {
405
+ return options(value);
406
+ }
407
+ };
408
+ async function validValues(rules, items, values, lang) {
384
409
  const errors = {};
385
410
  for (const item of items) {
386
411
  const value = values[item.name];
387
- const rules = item.rules;
388
- if (rules) {
389
- const error = await validValue(rules, value, lang);
390
- if (error) errors[item.name] = error;
412
+ const rulesOptions = item.rules;
413
+ if (rulesOptions) {
414
+ for (const [name, options] of Object.entries(rulesOptions)) {
415
+ try {
416
+ await rules[name](value, options, lang);
417
+ } catch (error) {
418
+ errors[item.name] = error;
419
+ break;
420
+ }
421
+ }
391
422
  }
392
423
  }
393
424
  return errors;
@@ -401,7 +432,8 @@ function FormFooter() {
401
432
  Elements,
402
433
  items,
403
434
  setErrors,
404
- lang
435
+ lang,
436
+ rules
405
437
  } = useFormContext();
406
438
  return /* @__PURE__ */ jsxRuntime.jsx(
407
439
  Elements.Button,
@@ -409,7 +441,7 @@ function FormFooter() {
409
441
  disabled: submitting,
410
442
  submit: async () => {
411
443
  setSubmitting(true);
412
- const errors = await validValues(items, values, lang);
444
+ const errors = await validValues(rules, items, values, lang);
413
445
  if (Object.keys(errors).length) {
414
446
  setErrors(errors);
415
447
  setSubmitting(false);
@@ -443,45 +475,14 @@ var FormLabelElement = ({
443
475
  name,
444
476
  title,
445
477
  description,
446
- Label,
447
- input
478
+ error,
479
+ children
448
480
  }) => {
449
- const { values, setValues, errors } = useFormContext();
450
- if (Label)
451
- return /* @__PURE__ */ jsxRuntime.jsx(
452
- Label,
453
- {
454
- name,
455
- title,
456
- description,
457
- input
458
- }
459
- );
460
481
  return /* @__PURE__ */ jsxRuntime.jsxs("label", { children: [
461
482
  title ?? name,
462
- input?.Input ? /* @__PURE__ */ jsxRuntime.jsx(
463
- input.Input,
464
- {
465
- name,
466
- value: values[name],
467
- onChange: (v) => setValues((prev) => ({
468
- ...prev,
469
- [name]: v
470
- }))
471
- }
472
- ) : /* @__PURE__ */ jsxRuntime.jsx(
473
- FormInputElement,
474
- {
475
- name,
476
- value: values[name],
477
- onChange: (v) => setValues((prev) => ({
478
- ...prev,
479
- [name]: v
480
- }))
481
- }
482
- ),
483
+ children,
483
484
  description,
484
- errors[name]?.message
485
+ error && /* @__PURE__ */ jsxRuntime.jsx("div", { style: { color: "red" }, children: error.message })
485
486
  ] });
486
487
  };
487
488
  FormLabelElement.displayName = "FormLabelElement";
@@ -498,6 +499,7 @@ var FormDefaultElements = {
498
499
  var FormDefaultLang = {
499
500
  submit: "Submit",
500
501
  required: "This field is required",
502
+ string: "This field must be a string",
501
503
  number: "This field must be a number"
502
504
  };
503
505
  function mergeValues(items, defaultValues = {}) {
@@ -509,23 +511,25 @@ function mergeValues(items, defaultValues = {}) {
509
511
  function FormContainer({
510
512
  defaultValues,
511
513
  Elements,
514
+ rules,
512
515
  lang,
513
516
  ...props
514
517
  }) {
515
- const states = useSplittingState({
516
- values: mergeValues(props.items, defaultValues),
517
- errors: {},
518
- submitting: false,
519
- Elements: Object.assign(FormDefaultElements, Elements),
520
- lang: Object.assign(FormDefaultLang, lang)
521
- });
522
518
  return /* @__PURE__ */ jsxRuntime.jsxs(
523
519
  FormContextProvider,
524
520
  {
525
- value: {
526
- ...states,
527
- ...props
521
+ initializeStates: {
522
+ values: mergeValues(props.items, defaultValues),
523
+ errors: {},
524
+ submitting: false,
525
+ Elements: Object.assign(
526
+ FormDefaultElements,
527
+ Elements
528
+ ),
529
+ lang: Object.assign(FormDefaultLang, lang),
530
+ rules: Object.assign(FormDefaultRules, rules)
528
531
  },
532
+ value: props,
529
533
  memo: true,
530
534
  children: [
531
535
  /* @__PURE__ */ jsxRuntime.jsx(FormBody, {}),
@@ -543,6 +547,8 @@ exports.FaasReactClient = FaasReactClient;
543
547
  exports.Form = FormContainer;
544
548
  exports.FormContextProvider = FormContextProvider;
545
549
  exports.FormDefaultElements = FormDefaultElements;
550
+ exports.FormDefaultRules = FormDefaultRules;
551
+ exports.FormItem = FormItem;
546
552
  exports.OptionalWrapper = OptionalWrapper;
547
553
  exports.createSplittingContext = createSplittingContext;
548
554
  exports.equal = equal;
@@ -556,4 +562,5 @@ exports.useEqualMemoize = useEqualMemoize;
556
562
  exports.useFaas = useFaas;
557
563
  exports.useFormContext = useFormContext;
558
564
  exports.useSplittingState = useSplittingState;
565
+ exports.validValues = validValues;
559
566
  exports.withFaasData = withFaasData;
package/dist/index.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { forwardRef, useRef, useMemo, useEffect, useCallback, createContext, useState, cloneElement, Component, useContext } from 'react';
1
+ import { forwardRef, useRef, useMemo, useEffect, useCallback, useState, createContext, cloneElement, Component, useContext } from 'react';
2
2
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
3
3
  import { FaasBrowserClient } from '@faasjs/browser';
4
4
 
@@ -47,7 +47,6 @@ function equal(a, b) {
47
47
  return a === b;
48
48
  case Object: {
49
49
  const keys = Object.keys(a);
50
- if (keys.length !== Object.keys(b).length) return false;
51
50
  for (const key of keys) {
52
51
  if (!equal(a[key], b[key])) return false;
53
52
  }
@@ -78,6 +77,17 @@ function useEqualCallback(callback, dependencies) {
78
77
  useEqualMemoize(dependencies)
79
78
  );
80
79
  }
80
+ function useSplittingState(initialStates) {
81
+ const states = {};
82
+ for (const key of Object.keys(initialStates)) {
83
+ const state = useState(initialStates[key]);
84
+ Object.assign(states, {
85
+ [key]: state[0],
86
+ [`set${String(key).charAt(0).toUpperCase()}${String(key).slice(1)}`]: state[1]
87
+ });
88
+ }
89
+ return states;
90
+ }
81
91
  function createSplittingContext(defaultValue) {
82
92
  const keys = Array.isArray(defaultValue) ? defaultValue : Object.keys(defaultValue);
83
93
  const defaultValues = Array.isArray(defaultValue) ? keys.reduce((prev, cur) => {
@@ -87,13 +97,14 @@ function createSplittingContext(defaultValue) {
87
97
  const contexts = {};
88
98
  for (const key of keys) contexts[key] = createContext(defaultValues[key]);
89
99
  function Provider(props) {
100
+ const states = props.initializeStates ? useSplittingState(props.initializeStates) : {};
90
101
  let children = props.memo ? useEqualMemo(
91
102
  () => props.children,
92
103
  props.memo === true ? [] : props.memo
93
104
  ) : props.children;
94
105
  for (const key of keys) {
95
106
  const Context = contexts[key];
96
- const value = props.value?.[key] ?? defaultValues[key];
107
+ const value = props.value?.[key] ?? states[key] ?? defaultValues[key];
97
108
  children = /* @__PURE__ */ jsx(Context.Provider, { value, children });
98
109
  }
99
110
  return children;
@@ -117,17 +128,6 @@ function createSplittingContext(defaultValue) {
117
128
  use
118
129
  };
119
130
  }
120
- function useSplittingState(initialStates) {
121
- const states = {};
122
- for (const key of Object.keys(initialStates)) {
123
- const state = useState(initialStates[key]);
124
- Object.assign(states, {
125
- [key]: state[0],
126
- [`set${String(key).charAt(0).toUpperCase()}${String(key).slice(1)}`]: state[1]
127
- });
128
- }
129
- return states;
130
- }
131
131
  function FaasDataWrapper(props) {
132
132
  const request = getClient(props.baseUrl).useFaas(
133
133
  props.action,
@@ -352,40 +352,71 @@ var FormContext = createSplittingContext([
352
352
  "values",
353
353
  "setValues",
354
354
  "errors",
355
- "setErrors"
355
+ "setErrors",
356
+ "rules"
356
357
  ]);
357
358
  var FormContextProvider = FormContext.Provider;
358
359
  var useFormContext = FormContext.use;
359
- function FormLabel(props) {
360
- const { Elements } = useFormContext();
361
- if (props.Label) return /* @__PURE__ */ jsx(props.Label, { ...props });
362
- return /* @__PURE__ */ jsx(Elements.Label, { ...props });
360
+ function FormItem(props) {
361
+ const { Elements, values, setValues } = useFormContext();
362
+ const Label = props.label?.Label ?? Elements.Label;
363
+ const Input = props.input?.Input ?? Elements.Input;
364
+ return /* @__PURE__ */ jsx(Label, { name: props.name, ...props.label, children: /* @__PURE__ */ jsx(
365
+ Input,
366
+ {
367
+ name: props.name,
368
+ value: values[props.name],
369
+ onChange: (v) => setValues((prev) => ({
370
+ ...prev,
371
+ [props.name]: v
372
+ }))
373
+ }
374
+ ) });
363
375
  }
364
- FormLabel.displayName = "FormLabel";
365
- FormLabel.whyDidYouRender = true;
376
+ FormItem.displayName = "FormItem";
377
+ FormItem.whyDidYouRender = true;
366
378
  function FormBody() {
367
379
  const { items } = useFormContext();
368
- return items.map((item) => /* @__PURE__ */ jsx(FormLabel, { ...item }, item.name));
380
+ return items.map((item) => /* @__PURE__ */ jsx(FormItem, { ...item }, item.name));
369
381
  }
370
382
  FormBody.displayName = "FormBody";
371
383
  FormBody.whyDidYouRender = true;
372
384
 
373
385
  // src/Form/rules.ts
374
- async function validValue(rules, value, lang) {
375
- if (rules.required && (value === null || value === void 0 || value === "" || Number.isNaN(value)))
376
- return { message: lang.required };
377
- if (rules.type === "number" && Number.isNaN(Number(value)))
378
- return { message: lang.number };
379
- return rules.custom?.(value);
380
- }
381
- async function validValues(items, values, lang) {
386
+ var FormDefaultRules = {
387
+ required: async (value, _, lang) => {
388
+ if (value === null || value === void 0 || value === "" || Number.isNaN(value)) {
389
+ throw Error(lang?.required);
390
+ }
391
+ },
392
+ type: async (value, options, lang) => {
393
+ switch (options) {
394
+ case "string":
395
+ if (typeof value !== "string") throw Error(lang?.string);
396
+ break;
397
+ case "number":
398
+ if (Number.isNaN(Number(value))) throw Error(lang?.number);
399
+ break;
400
+ }
401
+ },
402
+ custom: async (value, options) => {
403
+ return options(value);
404
+ }
405
+ };
406
+ async function validValues(rules, items, values, lang) {
382
407
  const errors = {};
383
408
  for (const item of items) {
384
409
  const value = values[item.name];
385
- const rules = item.rules;
386
- if (rules) {
387
- const error = await validValue(rules, value, lang);
388
- if (error) errors[item.name] = error;
410
+ const rulesOptions = item.rules;
411
+ if (rulesOptions) {
412
+ for (const [name, options] of Object.entries(rulesOptions)) {
413
+ try {
414
+ await rules[name](value, options, lang);
415
+ } catch (error) {
416
+ errors[item.name] = error;
417
+ break;
418
+ }
419
+ }
389
420
  }
390
421
  }
391
422
  return errors;
@@ -399,7 +430,8 @@ function FormFooter() {
399
430
  Elements,
400
431
  items,
401
432
  setErrors,
402
- lang
433
+ lang,
434
+ rules
403
435
  } = useFormContext();
404
436
  return /* @__PURE__ */ jsx(
405
437
  Elements.Button,
@@ -407,7 +439,7 @@ function FormFooter() {
407
439
  disabled: submitting,
408
440
  submit: async () => {
409
441
  setSubmitting(true);
410
- const errors = await validValues(items, values, lang);
442
+ const errors = await validValues(rules, items, values, lang);
411
443
  if (Object.keys(errors).length) {
412
444
  setErrors(errors);
413
445
  setSubmitting(false);
@@ -441,45 +473,14 @@ var FormLabelElement = ({
441
473
  name,
442
474
  title,
443
475
  description,
444
- Label,
445
- input
476
+ error,
477
+ children
446
478
  }) => {
447
- const { values, setValues, errors } = useFormContext();
448
- if (Label)
449
- return /* @__PURE__ */ jsx(
450
- Label,
451
- {
452
- name,
453
- title,
454
- description,
455
- input
456
- }
457
- );
458
479
  return /* @__PURE__ */ jsxs("label", { children: [
459
480
  title ?? name,
460
- input?.Input ? /* @__PURE__ */ jsx(
461
- input.Input,
462
- {
463
- name,
464
- value: values[name],
465
- onChange: (v) => setValues((prev) => ({
466
- ...prev,
467
- [name]: v
468
- }))
469
- }
470
- ) : /* @__PURE__ */ jsx(
471
- FormInputElement,
472
- {
473
- name,
474
- value: values[name],
475
- onChange: (v) => setValues((prev) => ({
476
- ...prev,
477
- [name]: v
478
- }))
479
- }
480
- ),
481
+ children,
481
482
  description,
482
- errors[name]?.message
483
+ error && /* @__PURE__ */ jsx("div", { style: { color: "red" }, children: error.message })
483
484
  ] });
484
485
  };
485
486
  FormLabelElement.displayName = "FormLabelElement";
@@ -496,6 +497,7 @@ var FormDefaultElements = {
496
497
  var FormDefaultLang = {
497
498
  submit: "Submit",
498
499
  required: "This field is required",
500
+ string: "This field must be a string",
499
501
  number: "This field must be a number"
500
502
  };
501
503
  function mergeValues(items, defaultValues = {}) {
@@ -507,23 +509,25 @@ function mergeValues(items, defaultValues = {}) {
507
509
  function FormContainer({
508
510
  defaultValues,
509
511
  Elements,
512
+ rules,
510
513
  lang,
511
514
  ...props
512
515
  }) {
513
- const states = useSplittingState({
514
- values: mergeValues(props.items, defaultValues),
515
- errors: {},
516
- submitting: false,
517
- Elements: Object.assign(FormDefaultElements, Elements),
518
- lang: Object.assign(FormDefaultLang, lang)
519
- });
520
516
  return /* @__PURE__ */ jsxs(
521
517
  FormContextProvider,
522
518
  {
523
- value: {
524
- ...states,
525
- ...props
519
+ initializeStates: {
520
+ values: mergeValues(props.items, defaultValues),
521
+ errors: {},
522
+ submitting: false,
523
+ Elements: Object.assign(
524
+ FormDefaultElements,
525
+ Elements
526
+ ),
527
+ lang: Object.assign(FormDefaultLang, lang),
528
+ rules: Object.assign(FormDefaultRules, rules)
526
529
  },
530
+ value: props,
527
531
  memo: true,
528
532
  children: [
529
533
  /* @__PURE__ */ jsx(FormBody, {}),
@@ -535,4 +539,4 @@ function FormContainer({
535
539
  FormContainer.displayName = "FormContainer";
536
540
  FormContainer.whyDidYouRender = true;
537
541
 
538
- export { ErrorBoundary, FaasDataWrapper, FaasReactClient, FormContainer as Form, FormContextProvider, FormDefaultElements, OptionalWrapper, createSplittingContext, equal, faas, getClient, useConstant, useEqualCallback, useEqualEffect, useEqualMemo, useEqualMemoize, useFaas, useFormContext, useSplittingState, withFaasData };
542
+ export { ErrorBoundary, FaasDataWrapper, FaasReactClient, FormContainer as Form, FormContextProvider, FormDefaultElements, FormDefaultRules, FormItem, OptionalWrapper, createSplittingContext, equal, faas, getClient, useConstant, useEqualCallback, useEqualEffect, useEqualMemo, useEqualMemoize, useFaas, useFormContext, useSplittingState, validValues, withFaasData };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@faasjs/react",
3
- "version": "3.7.0-beta.6",
3
+ "version": "3.7.0-beta.8",
4
4
  "license": "MIT",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",
@@ -34,10 +34,10 @@
34
34
  "dist"
35
35
  ],
36
36
  "peerDependencies": {
37
- "@faasjs/browser": "3.7.0-beta.6"
37
+ "@faasjs/browser": "3.7.0-beta.8"
38
38
  },
39
39
  "devDependencies": {
40
- "@faasjs/browser": "3.7.0-beta.6",
40
+ "@faasjs/browser": "3.7.0-beta.8",
41
41
  "@types/react": "*",
42
42
  "react": "*"
43
43
  },