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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.mts CHANGED
@@ -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.
@@ -397,18 +411,22 @@ type FormInputElementProps = {
397
411
  declare const FormDefaultLang: {
398
412
  submit: string;
399
413
  required: string;
414
+ string: string;
400
415
  number: string;
401
416
  };
402
417
  type FormLang = typeof FormDefaultLang;
403
418
 
404
- type FormRules = {
405
- type?: 'string' | 'number';
406
- required?: boolean;
407
- custom?: (value: any) => Promise<FormError | undefined>;
408
- };
409
419
  type FormError = {
410
420
  message: string;
411
421
  };
422
+ type FormRule<Options = any> = (value: any, options?: Options, lang?: FormLang) => Promise<FormError | undefined>;
423
+ type InferRuleOption<T> = T extends (value: any, options: infer O, lang?: FormLang) => Promise<FormError | undefined> ? O : never;
424
+ type FormRules = Record<string, FormRule>;
425
+ type InferFormRulesOptions<T> = {
426
+ [K in keyof T]: InferRuleOption<T[K]>;
427
+ };
428
+ declare const FormDefaultRules: FormRules;
429
+ type FormDefaultRulesOptions = InferFormRulesOptions<typeof FormDefaultRules>;
412
430
 
413
431
  type InferFormInputProps<T extends ComponentType<FormInputElementProps> | JSXElementConstructor<any>> = T extends ComponentType<FormInputElementProps> ? Omit<ComponentProps<T>, 'name' | 'value' | 'onChange'> : Omit<ComponentProps<T>, 'name' | 'value'>;
414
432
  type FormInputProps<FormElements extends FormElementTypes = FormElementTypes> = {
@@ -416,9 +434,9 @@ type FormInputProps<FormElements extends FormElementTypes = FormElementTypes> =
416
434
  props?: InferFormInputProps<FormElements['Input']>;
417
435
  };
418
436
 
419
- type FormLabelElementProps<FormElements extends FormElementTypes = FormElementTypes> = {
437
+ type FormLabelElementProps<FormElements extends FormElementTypes = FormElementTypes, FormRulesOptions extends Record<string, any> = FormDefaultRulesOptions> = {
420
438
  name: string;
421
- rules?: FormRules;
439
+ rules?: FormRulesOptions;
422
440
  title?: ReactNode;
423
441
  description?: ReactNode;
424
442
  Label?: FormElements['Label'];
@@ -432,24 +450,26 @@ type FormElementTypes = {
432
450
  };
433
451
  declare const FormDefaultElements: FormElementTypes;
434
452
 
435
- type FormProps<Values extends Record<string, any> = Record<string, any>, FormElements extends FormElementTypes = FormElementTypes> = {
436
- items: FormLabelElementProps<FormElements>[];
453
+ type FormProps<Values extends Record<string, any> = Record<string, any>, FormElements extends FormElementTypes = FormElementTypes, Rules extends FormRules = typeof FormDefaultRules> = {
454
+ items: FormLabelElementProps<FormElements, InferFormRulesOptions<Rules>>[];
437
455
  onSubmit?: (values: Values) => Promise<void>;
438
456
  Elements?: Partial<FormElements>;
439
457
  lang?: Partial<FormLang>;
440
458
  defaultValues?: Values;
459
+ rules?: typeof FormDefaultRules & Rules;
441
460
  };
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;
461
+ 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
462
  declare namespace FormContainer {
444
463
  var displayName: string;
445
464
  var whyDidYouRender: boolean;
446
465
  }
447
466
 
448
- type FormContextProps<Values extends Record<string, any> = Record<string, any>> = {
467
+ type FormContextProps<Values extends Record<string, any> = Record<string, any>, Rules extends FormRules = typeof FormDefaultRules> = {
449
468
  items: FormLabelElementProps[];
450
469
  onSubmit: (values: Values) => Promise<void>;
451
470
  Elements: FormElementTypes;
452
471
  lang: FormLang;
472
+ rules: typeof FormDefaultRules & Rules;
453
473
  submitting: boolean;
454
474
  setSubmitting: Dispatch<SetStateAction<boolean>>;
455
475
  values: Values;
@@ -457,11 +477,12 @@ type FormContextProps<Values extends Record<string, any> = Record<string, any>>
457
477
  errors: Record<string, FormError>;
458
478
  setErrors: Dispatch<SetStateAction<Record<string, FormError>>>;
459
479
  };
460
- declare const FormContextProvider: <NewT extends FormContextProps<Record<string, any>> = FormContextProps<Record<string, any>>>(props: {
461
- value?: NewT;
480
+ declare const FormContextProvider: <NewT extends FormContextProps<Record<string, any>, FormRules> = FormContextProps<Record<string, any>, FormRules>>(props: {
481
+ value?: Partial<NewT>;
462
482
  children: react.ReactNode;
463
483
  memo?: true | any[];
484
+ initializeStates?: Partial<NewT>;
464
485
  }) => react.ReactNode;
465
- declare const useFormContext: <NewT extends FormContextProps<Record<string, any>> = FormContextProps<Record<string, any>>>() => Readonly<NewT>;
486
+ declare const useFormContext: <NewT extends FormContextProps<Record<string, any>, FormRules> = FormContextProps<Record<string, any>, FormRules>>() => Readonly<NewT>;
466
487
 
467
488
  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 };
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.
@@ -397,18 +411,22 @@ type FormInputElementProps = {
397
411
  declare const FormDefaultLang: {
398
412
  submit: string;
399
413
  required: string;
414
+ string: string;
400
415
  number: string;
401
416
  };
402
417
  type FormLang = typeof FormDefaultLang;
403
418
 
404
- type FormRules = {
405
- type?: 'string' | 'number';
406
- required?: boolean;
407
- custom?: (value: any) => Promise<FormError | undefined>;
408
- };
409
419
  type FormError = {
410
420
  message: string;
411
421
  };
422
+ type FormRule<Options = any> = (value: any, options?: Options, lang?: FormLang) => Promise<FormError | undefined>;
423
+ type InferRuleOption<T> = T extends (value: any, options: infer O, lang?: FormLang) => Promise<FormError | undefined> ? O : never;
424
+ type FormRules = Record<string, FormRule>;
425
+ type InferFormRulesOptions<T> = {
426
+ [K in keyof T]: InferRuleOption<T[K]>;
427
+ };
428
+ declare const FormDefaultRules: FormRules;
429
+ type FormDefaultRulesOptions = InferFormRulesOptions<typeof FormDefaultRules>;
412
430
 
413
431
  type InferFormInputProps<T extends ComponentType<FormInputElementProps> | JSXElementConstructor<any>> = T extends ComponentType<FormInputElementProps> ? Omit<ComponentProps<T>, 'name' | 'value' | 'onChange'> : Omit<ComponentProps<T>, 'name' | 'value'>;
414
432
  type FormInputProps<FormElements extends FormElementTypes = FormElementTypes> = {
@@ -416,9 +434,9 @@ type FormInputProps<FormElements extends FormElementTypes = FormElementTypes> =
416
434
  props?: InferFormInputProps<FormElements['Input']>;
417
435
  };
418
436
 
419
- type FormLabelElementProps<FormElements extends FormElementTypes = FormElementTypes> = {
437
+ type FormLabelElementProps<FormElements extends FormElementTypes = FormElementTypes, FormRulesOptions extends Record<string, any> = FormDefaultRulesOptions> = {
420
438
  name: string;
421
- rules?: FormRules;
439
+ rules?: FormRulesOptions;
422
440
  title?: ReactNode;
423
441
  description?: ReactNode;
424
442
  Label?: FormElements['Label'];
@@ -432,24 +450,26 @@ type FormElementTypes = {
432
450
  };
433
451
  declare const FormDefaultElements: FormElementTypes;
434
452
 
435
- type FormProps<Values extends Record<string, any> = Record<string, any>, FormElements extends FormElementTypes = FormElementTypes> = {
436
- items: FormLabelElementProps<FormElements>[];
453
+ type FormProps<Values extends Record<string, any> = Record<string, any>, FormElements extends FormElementTypes = FormElementTypes, Rules extends FormRules = typeof FormDefaultRules> = {
454
+ items: FormLabelElementProps<FormElements, InferFormRulesOptions<Rules>>[];
437
455
  onSubmit?: (values: Values) => Promise<void>;
438
456
  Elements?: Partial<FormElements>;
439
457
  lang?: Partial<FormLang>;
440
458
  defaultValues?: Values;
459
+ rules?: typeof FormDefaultRules & Rules;
441
460
  };
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;
461
+ 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
462
  declare namespace FormContainer {
444
463
  var displayName: string;
445
464
  var whyDidYouRender: boolean;
446
465
  }
447
466
 
448
- type FormContextProps<Values extends Record<string, any> = Record<string, any>> = {
467
+ type FormContextProps<Values extends Record<string, any> = Record<string, any>, Rules extends FormRules = typeof FormDefaultRules> = {
449
468
  items: FormLabelElementProps[];
450
469
  onSubmit: (values: Values) => Promise<void>;
451
470
  Elements: FormElementTypes;
452
471
  lang: FormLang;
472
+ rules: typeof FormDefaultRules & Rules;
453
473
  submitting: boolean;
454
474
  setSubmitting: Dispatch<SetStateAction<boolean>>;
455
475
  values: Values;
@@ -457,11 +477,12 @@ type FormContextProps<Values extends Record<string, any> = Record<string, any>>
457
477
  errors: Record<string, FormError>;
458
478
  setErrors: Dispatch<SetStateAction<Record<string, FormError>>>;
459
479
  };
460
- declare const FormContextProvider: <NewT extends FormContextProps<Record<string, any>> = FormContextProps<Record<string, any>>>(props: {
461
- value?: NewT;
480
+ declare const FormContextProvider: <NewT extends FormContextProps<Record<string, any>, FormRules> = FormContextProps<Record<string, any>, FormRules>>(props: {
481
+ value?: Partial<NewT>;
462
482
  children: react.ReactNode;
463
483
  memo?: true | any[];
484
+ initializeStates?: Partial<NewT>;
464
485
  }) => react.ReactNode;
465
- declare const useFormContext: <NewT extends FormContextProps<Record<string, any>> = FormContextProps<Record<string, any>>>() => Readonly<NewT>;
486
+ declare const useFormContext: <NewT extends FormContextProps<Record<string, any>, FormRules> = FormContextProps<Record<string, any>, FormRules>>() => Readonly<NewT>;
466
487
 
467
488
  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 };
package/dist/index.js CHANGED
@@ -80,6 +80,17 @@ function useEqualCallback(callback, dependencies) {
80
80
  useEqualMemoize(dependencies)
81
81
  );
82
82
  }
83
+ function useSplittingState(initialStates) {
84
+ const states = {};
85
+ for (const key of Object.keys(initialStates)) {
86
+ const state = react.useState(initialStates[key]);
87
+ Object.assign(states, {
88
+ [key]: state[0],
89
+ [`set${String(key).charAt(0).toUpperCase()}${String(key).slice(1)}`]: state[1]
90
+ });
91
+ }
92
+ return states;
93
+ }
83
94
  function createSplittingContext(defaultValue) {
84
95
  const keys = Array.isArray(defaultValue) ? defaultValue : Object.keys(defaultValue);
85
96
  const defaultValues = Array.isArray(defaultValue) ? keys.reduce((prev, cur) => {
@@ -89,13 +100,14 @@ function createSplittingContext(defaultValue) {
89
100
  const contexts = {};
90
101
  for (const key of keys) contexts[key] = react.createContext(defaultValues[key]);
91
102
  function Provider(props) {
103
+ const states = props.initializeStates ? useSplittingState(props.initializeStates) : {};
92
104
  let children = props.memo ? useEqualMemo(
93
105
  () => props.children,
94
106
  props.memo === true ? [] : props.memo
95
107
  ) : props.children;
96
108
  for (const key of keys) {
97
109
  const Context = contexts[key];
98
- const value = props.value?.[key] ?? defaultValues[key];
110
+ const value = props.value?.[key] ?? states[key] ?? defaultValues[key];
99
111
  children = /* @__PURE__ */ jsxRuntime.jsx(Context.Provider, { value, children });
100
112
  }
101
113
  return children;
@@ -119,17 +131,6 @@ function createSplittingContext(defaultValue) {
119
131
  use
120
132
  };
121
133
  }
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
134
  function FaasDataWrapper(props) {
134
135
  const request = getClient(props.baseUrl).useFaas(
135
136
  props.action,
@@ -354,7 +355,8 @@ var FormContext = createSplittingContext([
354
355
  "values",
355
356
  "setValues",
356
357
  "errors",
357
- "setErrors"
358
+ "setErrors",
359
+ "rules"
358
360
  ]);
359
361
  var FormContextProvider = FormContext.Provider;
360
362
  var useFormContext = FormContext.use;
@@ -373,21 +375,48 @@ FormBody.displayName = "FormBody";
373
375
  FormBody.whyDidYouRender = true;
374
376
 
375
377
  // 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) {
378
+ var FormDefaultRules = {
379
+ required: async (value, _, lang) => {
380
+ if (value === null || value === void 0 || value === "" || Number.isNaN(value)) {
381
+ return { message: lang?.required };
382
+ }
383
+ },
384
+ type: async (value, options, lang) => {
385
+ switch (options) {
386
+ case "string":
387
+ if (typeof value !== "string") {
388
+ return { message: lang?.string };
389
+ }
390
+ break;
391
+ case "number":
392
+ if (Number.isNaN(Number(value))) {
393
+ return { message: lang?.number };
394
+ }
395
+ break;
396
+ }
397
+ },
398
+ custom: async (value, options) => {
399
+ return options(value);
400
+ }
401
+ };
402
+ async function validValues(rules, items, values, lang) {
384
403
  const errors = {};
385
404
  for (const item of items) {
386
405
  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;
406
+ const rulesOptions = item.rules;
407
+ if (rulesOptions) {
408
+ for (const [name, options] of Object.entries(rulesOptions)) {
409
+ const handler = rules[name];
410
+ if (!handler) {
411
+ console.warn(`Rule "${name}" is not defined`);
412
+ continue;
413
+ }
414
+ const error = await handler(value, options, lang);
415
+ if (error) {
416
+ errors[item.name] = error;
417
+ break;
418
+ }
419
+ }
391
420
  }
392
421
  }
393
422
  return errors;
@@ -401,7 +430,8 @@ function FormFooter() {
401
430
  Elements,
402
431
  items,
403
432
  setErrors,
404
- lang
433
+ lang,
434
+ rules
405
435
  } = useFormContext();
406
436
  return /* @__PURE__ */ jsxRuntime.jsx(
407
437
  Elements.Button,
@@ -409,7 +439,7 @@ function FormFooter() {
409
439
  disabled: submitting,
410
440
  submit: async () => {
411
441
  setSubmitting(true);
412
- const errors = await validValues(items, values, lang);
442
+ const errors = await validValues(rules, items, values, lang);
413
443
  if (Object.keys(errors).length) {
414
444
  setErrors(errors);
415
445
  setSubmitting(false);
@@ -498,6 +528,7 @@ var FormDefaultElements = {
498
528
  var FormDefaultLang = {
499
529
  submit: "Submit",
500
530
  required: "This field is required",
531
+ string: "This field must be a string",
501
532
  number: "This field must be a number"
502
533
  };
503
534
  function mergeValues(items, defaultValues = {}) {
@@ -509,23 +540,25 @@ function mergeValues(items, defaultValues = {}) {
509
540
  function FormContainer({
510
541
  defaultValues,
511
542
  Elements,
543
+ rules,
512
544
  lang,
513
545
  ...props
514
546
  }) {
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
547
  return /* @__PURE__ */ jsxRuntime.jsxs(
523
548
  FormContextProvider,
524
549
  {
525
- value: {
526
- ...states,
527
- ...props
550
+ initializeStates: {
551
+ values: mergeValues(props.items, defaultValues),
552
+ errors: {},
553
+ submitting: false,
554
+ Elements: Object.assign(
555
+ FormDefaultElements,
556
+ Elements
557
+ ),
558
+ lang: Object.assign(FormDefaultLang, lang),
559
+ rules: Object.assign(FormDefaultRules, rules)
528
560
  },
561
+ value: props,
529
562
  memo: true,
530
563
  children: [
531
564
  /* @__PURE__ */ jsxRuntime.jsx(FormBody, {}),
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
 
@@ -78,6 +78,17 @@ function useEqualCallback(callback, dependencies) {
78
78
  useEqualMemoize(dependencies)
79
79
  );
80
80
  }
81
+ function useSplittingState(initialStates) {
82
+ const states = {};
83
+ for (const key of Object.keys(initialStates)) {
84
+ const state = useState(initialStates[key]);
85
+ Object.assign(states, {
86
+ [key]: state[0],
87
+ [`set${String(key).charAt(0).toUpperCase()}${String(key).slice(1)}`]: state[1]
88
+ });
89
+ }
90
+ return states;
91
+ }
81
92
  function createSplittingContext(defaultValue) {
82
93
  const keys = Array.isArray(defaultValue) ? defaultValue : Object.keys(defaultValue);
83
94
  const defaultValues = Array.isArray(defaultValue) ? keys.reduce((prev, cur) => {
@@ -87,13 +98,14 @@ function createSplittingContext(defaultValue) {
87
98
  const contexts = {};
88
99
  for (const key of keys) contexts[key] = createContext(defaultValues[key]);
89
100
  function Provider(props) {
101
+ const states = props.initializeStates ? useSplittingState(props.initializeStates) : {};
90
102
  let children = props.memo ? useEqualMemo(
91
103
  () => props.children,
92
104
  props.memo === true ? [] : props.memo
93
105
  ) : props.children;
94
106
  for (const key of keys) {
95
107
  const Context = contexts[key];
96
- const value = props.value?.[key] ?? defaultValues[key];
108
+ const value = props.value?.[key] ?? states[key] ?? defaultValues[key];
97
109
  children = /* @__PURE__ */ jsx(Context.Provider, { value, children });
98
110
  }
99
111
  return children;
@@ -117,17 +129,6 @@ function createSplittingContext(defaultValue) {
117
129
  use
118
130
  };
119
131
  }
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
132
  function FaasDataWrapper(props) {
132
133
  const request = getClient(props.baseUrl).useFaas(
133
134
  props.action,
@@ -352,7 +353,8 @@ var FormContext = createSplittingContext([
352
353
  "values",
353
354
  "setValues",
354
355
  "errors",
355
- "setErrors"
356
+ "setErrors",
357
+ "rules"
356
358
  ]);
357
359
  var FormContextProvider = FormContext.Provider;
358
360
  var useFormContext = FormContext.use;
@@ -371,21 +373,48 @@ FormBody.displayName = "FormBody";
371
373
  FormBody.whyDidYouRender = true;
372
374
 
373
375
  // 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) {
376
+ var FormDefaultRules = {
377
+ required: async (value, _, lang) => {
378
+ if (value === null || value === void 0 || value === "" || Number.isNaN(value)) {
379
+ return { message: lang?.required };
380
+ }
381
+ },
382
+ type: async (value, options, lang) => {
383
+ switch (options) {
384
+ case "string":
385
+ if (typeof value !== "string") {
386
+ return { message: lang?.string };
387
+ }
388
+ break;
389
+ case "number":
390
+ if (Number.isNaN(Number(value))) {
391
+ return { message: lang?.number };
392
+ }
393
+ break;
394
+ }
395
+ },
396
+ custom: async (value, options) => {
397
+ return options(value);
398
+ }
399
+ };
400
+ async function validValues(rules, items, values, lang) {
382
401
  const errors = {};
383
402
  for (const item of items) {
384
403
  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;
404
+ const rulesOptions = item.rules;
405
+ if (rulesOptions) {
406
+ for (const [name, options] of Object.entries(rulesOptions)) {
407
+ const handler = rules[name];
408
+ if (!handler) {
409
+ console.warn(`Rule "${name}" is not defined`);
410
+ continue;
411
+ }
412
+ const error = await handler(value, options, lang);
413
+ if (error) {
414
+ errors[item.name] = error;
415
+ break;
416
+ }
417
+ }
389
418
  }
390
419
  }
391
420
  return errors;
@@ -399,7 +428,8 @@ function FormFooter() {
399
428
  Elements,
400
429
  items,
401
430
  setErrors,
402
- lang
431
+ lang,
432
+ rules
403
433
  } = useFormContext();
404
434
  return /* @__PURE__ */ jsx(
405
435
  Elements.Button,
@@ -407,7 +437,7 @@ function FormFooter() {
407
437
  disabled: submitting,
408
438
  submit: async () => {
409
439
  setSubmitting(true);
410
- const errors = await validValues(items, values, lang);
440
+ const errors = await validValues(rules, items, values, lang);
411
441
  if (Object.keys(errors).length) {
412
442
  setErrors(errors);
413
443
  setSubmitting(false);
@@ -496,6 +526,7 @@ var FormDefaultElements = {
496
526
  var FormDefaultLang = {
497
527
  submit: "Submit",
498
528
  required: "This field is required",
529
+ string: "This field must be a string",
499
530
  number: "This field must be a number"
500
531
  };
501
532
  function mergeValues(items, defaultValues = {}) {
@@ -507,23 +538,25 @@ function mergeValues(items, defaultValues = {}) {
507
538
  function FormContainer({
508
539
  defaultValues,
509
540
  Elements,
541
+ rules,
510
542
  lang,
511
543
  ...props
512
544
  }) {
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
545
  return /* @__PURE__ */ jsxs(
521
546
  FormContextProvider,
522
547
  {
523
- value: {
524
- ...states,
525
- ...props
548
+ initializeStates: {
549
+ values: mergeValues(props.items, defaultValues),
550
+ errors: {},
551
+ submitting: false,
552
+ Elements: Object.assign(
553
+ FormDefaultElements,
554
+ Elements
555
+ ),
556
+ lang: Object.assign(FormDefaultLang, lang),
557
+ rules: Object.assign(FormDefaultRules, rules)
526
558
  },
559
+ value: props,
527
560
  memo: true,
528
561
  children: [
529
562
  /* @__PURE__ */ jsx(FormBody, {}),
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.7",
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.7"
38
38
  },
39
39
  "devDependencies": {
40
- "@faasjs/browser": "3.7.0-beta.6",
40
+ "@faasjs/browser": "3.7.0-beta.7",
41
41
  "@types/react": "*",
42
42
  "react": "*"
43
43
  },