@faasjs/react 3.7.0-beta.5 → 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.
@@ -264,6 +278,14 @@ type FaasReactClientOptions = {
264
278
  /** @default `/` */
265
279
  baseUrl?: BaseUrl;
266
280
  options?: Options;
281
+ /**
282
+ * @example
283
+ * ```ts
284
+ * onError: (action, params) => async (res) => {
285
+ * console.error(action, params, res)
286
+ * }
287
+ * ```
288
+ */
267
289
  onError?: OnError;
268
290
  };
269
291
  type FaasReactClientInstance = {
@@ -386,14 +408,25 @@ type FormInputElementProps = {
386
408
  onChange: (value: any) => void;
387
409
  };
388
410
 
389
- type FormRules = {
390
- type?: 'string' | 'number';
391
- required?: boolean;
392
- custom?: (value: any) => Promise<FormError | undefined>;
411
+ declare const FormDefaultLang: {
412
+ submit: string;
413
+ required: string;
414
+ string: string;
415
+ number: string;
393
416
  };
417
+ type FormLang = typeof FormDefaultLang;
418
+
394
419
  type FormError = {
395
420
  message: string;
396
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>;
397
430
 
398
431
  type InferFormInputProps<T extends ComponentType<FormInputElementProps> | JSXElementConstructor<any>> = T extends ComponentType<FormInputElementProps> ? Omit<ComponentProps<T>, 'name' | 'value' | 'onChange'> : Omit<ComponentProps<T>, 'name' | 'value'>;
399
432
  type FormInputProps<FormElements extends FormElementTypes = FormElementTypes> = {
@@ -401,9 +434,9 @@ type FormInputProps<FormElements extends FormElementTypes = FormElementTypes> =
401
434
  props?: InferFormInputProps<FormElements['Input']>;
402
435
  };
403
436
 
404
- type FormLabelElementProps<FormElements extends FormElementTypes = FormElementTypes> = {
437
+ type FormLabelElementProps<FormElements extends FormElementTypes = FormElementTypes, FormRulesOptions extends Record<string, any> = FormDefaultRulesOptions> = {
405
438
  name: string;
406
- rules?: FormRules;
439
+ rules?: FormRulesOptions;
407
440
  title?: ReactNode;
408
441
  description?: ReactNode;
409
442
  Label?: FormElements['Label'];
@@ -417,22 +450,26 @@ type FormElementTypes = {
417
450
  };
418
451
  declare const FormDefaultElements: FormElementTypes;
419
452
 
420
- type FormProps<Values extends Record<string, any> = Record<string, any>, FormElements extends FormElementTypes = FormElementTypes> = {
421
- 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>>[];
422
455
  onSubmit?: (values: Values) => Promise<void>;
423
- elements?: Partial<FormElements>;
456
+ Elements?: Partial<FormElements>;
457
+ lang?: Partial<FormLang>;
424
458
  defaultValues?: Values;
459
+ rules?: typeof FormDefaultRules & Rules;
425
460
  };
426
- declare function FormContainer<Values extends Record<string, any> = Record<string, any>, FormElements extends FormElementTypes = FormElementTypes>({ defaultValues, elements, ...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;
427
462
  declare namespace FormContainer {
428
463
  var displayName: string;
429
464
  var whyDidYouRender: boolean;
430
465
  }
431
466
 
432
- 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> = {
433
468
  items: FormLabelElementProps[];
434
469
  onSubmit: (values: Values) => Promise<void>;
435
470
  Elements: FormElementTypes;
471
+ lang: FormLang;
472
+ rules: typeof FormDefaultRules & Rules;
436
473
  submitting: boolean;
437
474
  setSubmitting: Dispatch<SetStateAction<boolean>>;
438
475
  values: Values;
@@ -440,11 +477,12 @@ type FormContextProps<Values extends Record<string, any> = Record<string, any>>
440
477
  errors: Record<string, FormError>;
441
478
  setErrors: Dispatch<SetStateAction<Record<string, FormError>>>;
442
479
  };
443
- declare const FormContextProvider: <NewT extends FormContextProps<Record<string, any>> = FormContextProps<Record<string, any>>>(props: {
444
- value?: NewT;
480
+ declare const FormContextProvider: <NewT extends FormContextProps<Record<string, any>, FormRules> = FormContextProps<Record<string, any>, FormRules>>(props: {
481
+ value?: Partial<NewT>;
445
482
  children: react.ReactNode;
446
483
  memo?: true | any[];
484
+ initializeStates?: Partial<NewT>;
447
485
  }) => react.ReactNode;
448
- 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>;
449
487
 
450
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.
@@ -264,6 +278,14 @@ type FaasReactClientOptions = {
264
278
  /** @default `/` */
265
279
  baseUrl?: BaseUrl;
266
280
  options?: Options;
281
+ /**
282
+ * @example
283
+ * ```ts
284
+ * onError: (action, params) => async (res) => {
285
+ * console.error(action, params, res)
286
+ * }
287
+ * ```
288
+ */
267
289
  onError?: OnError;
268
290
  };
269
291
  type FaasReactClientInstance = {
@@ -386,14 +408,25 @@ type FormInputElementProps = {
386
408
  onChange: (value: any) => void;
387
409
  };
388
410
 
389
- type FormRules = {
390
- type?: 'string' | 'number';
391
- required?: boolean;
392
- custom?: (value: any) => Promise<FormError | undefined>;
411
+ declare const FormDefaultLang: {
412
+ submit: string;
413
+ required: string;
414
+ string: string;
415
+ number: string;
393
416
  };
417
+ type FormLang = typeof FormDefaultLang;
418
+
394
419
  type FormError = {
395
420
  message: string;
396
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>;
397
430
 
398
431
  type InferFormInputProps<T extends ComponentType<FormInputElementProps> | JSXElementConstructor<any>> = T extends ComponentType<FormInputElementProps> ? Omit<ComponentProps<T>, 'name' | 'value' | 'onChange'> : Omit<ComponentProps<T>, 'name' | 'value'>;
399
432
  type FormInputProps<FormElements extends FormElementTypes = FormElementTypes> = {
@@ -401,9 +434,9 @@ type FormInputProps<FormElements extends FormElementTypes = FormElementTypes> =
401
434
  props?: InferFormInputProps<FormElements['Input']>;
402
435
  };
403
436
 
404
- type FormLabelElementProps<FormElements extends FormElementTypes = FormElementTypes> = {
437
+ type FormLabelElementProps<FormElements extends FormElementTypes = FormElementTypes, FormRulesOptions extends Record<string, any> = FormDefaultRulesOptions> = {
405
438
  name: string;
406
- rules?: FormRules;
439
+ rules?: FormRulesOptions;
407
440
  title?: ReactNode;
408
441
  description?: ReactNode;
409
442
  Label?: FormElements['Label'];
@@ -417,22 +450,26 @@ type FormElementTypes = {
417
450
  };
418
451
  declare const FormDefaultElements: FormElementTypes;
419
452
 
420
- type FormProps<Values extends Record<string, any> = Record<string, any>, FormElements extends FormElementTypes = FormElementTypes> = {
421
- 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>>[];
422
455
  onSubmit?: (values: Values) => Promise<void>;
423
- elements?: Partial<FormElements>;
456
+ Elements?: Partial<FormElements>;
457
+ lang?: Partial<FormLang>;
424
458
  defaultValues?: Values;
459
+ rules?: typeof FormDefaultRules & Rules;
425
460
  };
426
- declare function FormContainer<Values extends Record<string, any> = Record<string, any>, FormElements extends FormElementTypes = FormElementTypes>({ defaultValues, elements, ...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;
427
462
  declare namespace FormContainer {
428
463
  var displayName: string;
429
464
  var whyDidYouRender: boolean;
430
465
  }
431
466
 
432
- 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> = {
433
468
  items: FormLabelElementProps[];
434
469
  onSubmit: (values: Values) => Promise<void>;
435
470
  Elements: FormElementTypes;
471
+ lang: FormLang;
472
+ rules: typeof FormDefaultRules & Rules;
436
473
  submitting: boolean;
437
474
  setSubmitting: Dispatch<SetStateAction<boolean>>;
438
475
  values: Values;
@@ -440,11 +477,12 @@ type FormContextProps<Values extends Record<string, any> = Record<string, any>>
440
477
  errors: Record<string, FormError>;
441
478
  setErrors: Dispatch<SetStateAction<Record<string, FormError>>>;
442
479
  };
443
- declare const FormContextProvider: <NewT extends FormContextProps<Record<string, any>> = FormContextProps<Record<string, any>>>(props: {
444
- value?: NewT;
480
+ declare const FormContextProvider: <NewT extends FormContextProps<Record<string, any>, FormRules> = FormContextProps<Record<string, any>, FormRules>>(props: {
481
+ value?: Partial<NewT>;
445
482
  children: react.ReactNode;
446
483
  memo?: true | any[];
484
+ initializeStates?: Partial<NewT>;
447
485
  }) => react.ReactNode;
448
- 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>;
449
487
 
450
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
@@ -20,6 +20,7 @@ function equal(a, b) {
20
20
  if ((a === null || a === void 0) && (b === null || b === void 0))
21
21
  return true;
22
22
  if (typeof a !== typeof b) return false;
23
+ if (b === null || b === void 0) return false;
23
24
  const ctor = a.constructor;
24
25
  if (ctor !== b.constructor) return false;
25
26
  switch (ctor) {
@@ -79,6 +80,17 @@ function useEqualCallback(callback, dependencies) {
79
80
  useEqualMemoize(dependencies)
80
81
  );
81
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
+ }
82
94
  function createSplittingContext(defaultValue) {
83
95
  const keys = Array.isArray(defaultValue) ? defaultValue : Object.keys(defaultValue);
84
96
  const defaultValues = Array.isArray(defaultValue) ? keys.reduce((prev, cur) => {
@@ -88,13 +100,14 @@ function createSplittingContext(defaultValue) {
88
100
  const contexts = {};
89
101
  for (const key of keys) contexts[key] = react.createContext(defaultValues[key]);
90
102
  function Provider(props) {
103
+ const states = props.initializeStates ? useSplittingState(props.initializeStates) : {};
91
104
  let children = props.memo ? useEqualMemo(
92
105
  () => props.children,
93
106
  props.memo === true ? [] : props.memo
94
107
  ) : props.children;
95
108
  for (const key of keys) {
96
109
  const Context = contexts[key];
97
- const value = props.value?.[key] ?? defaultValues[key];
110
+ const value = props.value?.[key] ?? states[key] ?? defaultValues[key];
98
111
  children = /* @__PURE__ */ jsxRuntime.jsx(Context.Provider, { value, children });
99
112
  }
100
113
  return children;
@@ -118,17 +131,6 @@ function createSplittingContext(defaultValue) {
118
131
  use
119
132
  };
120
133
  }
121
- function useSplittingState(initialStates) {
122
- const states = {};
123
- for (const key of Object.keys(initialStates)) {
124
- const state = react.useState(initialStates[key]);
125
- Object.assign(states, {
126
- [key]: state[0],
127
- [`set${String(key).charAt(0).toUpperCase()}${String(key).slice(1)}`]: state[1]
128
- });
129
- }
130
- return states;
131
- }
132
134
  function FaasDataWrapper(props) {
133
135
  const request = getClient(props.baseUrl).useFaas(
134
136
  props.action,
@@ -347,12 +349,14 @@ var FormContext = createSplittingContext([
347
349
  "items",
348
350
  "onSubmit",
349
351
  "Elements",
352
+ "lang",
350
353
  "submitting",
351
354
  "setSubmitting",
352
355
  "values",
353
356
  "setValues",
354
357
  "errors",
355
- "setErrors"
358
+ "setErrors",
359
+ "rules"
356
360
  ]);
357
361
  var FormContextProvider = FormContext.Provider;
358
362
  var useFormContext = FormContext.use;
@@ -371,21 +375,48 @@ FormBody.displayName = "FormBody";
371
375
  FormBody.whyDidYouRender = true;
372
376
 
373
377
  // src/Form/rules.ts
374
- async function validValue(rules, value) {
375
- if (rules.required && (value === null || value === void 0 || value === "" || Number.isNaN(value)))
376
- return { message: "This field is required" };
377
- if (rules.type === "number" && Number.isNaN(Number(value)))
378
- return { message: "This field must be a number" };
379
- return rules.custom?.(value);
380
- }
381
- async function validValues(items, values) {
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) {
382
403
  const errors = {};
383
404
  for (const item of items) {
384
405
  const value = values[item.name];
385
- const rules = item.rules;
386
- if (rules) {
387
- const error = await validValue(rules, value);
388
- 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
+ }
389
420
  }
390
421
  }
391
422
  return errors;
@@ -398,7 +429,9 @@ function FormFooter() {
398
429
  values,
399
430
  Elements,
400
431
  items,
401
- setErrors
432
+ setErrors,
433
+ lang,
434
+ rules
402
435
  } = useFormContext();
403
436
  return /* @__PURE__ */ jsxRuntime.jsx(
404
437
  Elements.Button,
@@ -406,7 +439,7 @@ function FormFooter() {
406
439
  disabled: submitting,
407
440
  submit: async () => {
408
441
  setSubmitting(true);
409
- const errors = await validValues(items, values);
442
+ const errors = await validValues(rules, items, values, lang);
410
443
  if (Object.keys(errors).length) {
411
444
  setErrors(errors);
412
445
  setSubmitting(false);
@@ -414,7 +447,7 @@ function FormFooter() {
414
447
  }
415
448
  onSubmit(values).finally(() => setSubmitting(false));
416
449
  },
417
- children: "Submit"
450
+ children: lang.submit
418
451
  }
419
452
  );
420
453
  }
@@ -490,26 +523,42 @@ var FormDefaultElements = {
490
523
  Input: FormInputElement,
491
524
  Button: FormButtonElement
492
525
  };
526
+
527
+ // src/Form/lang.ts
528
+ var FormDefaultLang = {
529
+ submit: "Submit",
530
+ required: "This field is required",
531
+ string: "This field must be a string",
532
+ number: "This field must be a number"
533
+ };
493
534
  function mergeValues(items, defaultValues = {}) {
494
535
  const values = {};
495
536
  for (const item of items)
496
537
  values[item.name] = defaultValues[item.name] ?? "";
497
538
  return values;
498
539
  }
499
- function FormContainer({ defaultValues, elements, ...props }) {
500
- const states = useSplittingState({
501
- values: mergeValues(props.items, defaultValues),
502
- errors: {},
503
- submitting: false,
504
- Elements: Object.assign(FormDefaultElements, elements)
505
- });
540
+ function FormContainer({
541
+ defaultValues,
542
+ Elements,
543
+ rules,
544
+ lang,
545
+ ...props
546
+ }) {
506
547
  return /* @__PURE__ */ jsxRuntime.jsxs(
507
548
  FormContextProvider,
508
549
  {
509
- value: {
510
- ...states,
511
- ...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)
512
560
  },
561
+ value: props,
513
562
  memo: true,
514
563
  children: [
515
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
 
@@ -18,6 +18,7 @@ function equal(a, b) {
18
18
  if ((a === null || a === void 0) && (b === null || b === void 0))
19
19
  return true;
20
20
  if (typeof a !== typeof b) return false;
21
+ if (b === null || b === void 0) return false;
21
22
  const ctor = a.constructor;
22
23
  if (ctor !== b.constructor) return false;
23
24
  switch (ctor) {
@@ -77,6 +78,17 @@ function useEqualCallback(callback, dependencies) {
77
78
  useEqualMemoize(dependencies)
78
79
  );
79
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
+ }
80
92
  function createSplittingContext(defaultValue) {
81
93
  const keys = Array.isArray(defaultValue) ? defaultValue : Object.keys(defaultValue);
82
94
  const defaultValues = Array.isArray(defaultValue) ? keys.reduce((prev, cur) => {
@@ -86,13 +98,14 @@ function createSplittingContext(defaultValue) {
86
98
  const contexts = {};
87
99
  for (const key of keys) contexts[key] = createContext(defaultValues[key]);
88
100
  function Provider(props) {
101
+ const states = props.initializeStates ? useSplittingState(props.initializeStates) : {};
89
102
  let children = props.memo ? useEqualMemo(
90
103
  () => props.children,
91
104
  props.memo === true ? [] : props.memo
92
105
  ) : props.children;
93
106
  for (const key of keys) {
94
107
  const Context = contexts[key];
95
- const value = props.value?.[key] ?? defaultValues[key];
108
+ const value = props.value?.[key] ?? states[key] ?? defaultValues[key];
96
109
  children = /* @__PURE__ */ jsx(Context.Provider, { value, children });
97
110
  }
98
111
  return children;
@@ -116,17 +129,6 @@ function createSplittingContext(defaultValue) {
116
129
  use
117
130
  };
118
131
  }
119
- function useSplittingState(initialStates) {
120
- const states = {};
121
- for (const key of Object.keys(initialStates)) {
122
- const state = useState(initialStates[key]);
123
- Object.assign(states, {
124
- [key]: state[0],
125
- [`set${String(key).charAt(0).toUpperCase()}${String(key).slice(1)}`]: state[1]
126
- });
127
- }
128
- return states;
129
- }
130
132
  function FaasDataWrapper(props) {
131
133
  const request = getClient(props.baseUrl).useFaas(
132
134
  props.action,
@@ -345,12 +347,14 @@ var FormContext = createSplittingContext([
345
347
  "items",
346
348
  "onSubmit",
347
349
  "Elements",
350
+ "lang",
348
351
  "submitting",
349
352
  "setSubmitting",
350
353
  "values",
351
354
  "setValues",
352
355
  "errors",
353
- "setErrors"
356
+ "setErrors",
357
+ "rules"
354
358
  ]);
355
359
  var FormContextProvider = FormContext.Provider;
356
360
  var useFormContext = FormContext.use;
@@ -369,21 +373,48 @@ FormBody.displayName = "FormBody";
369
373
  FormBody.whyDidYouRender = true;
370
374
 
371
375
  // src/Form/rules.ts
372
- async function validValue(rules, value) {
373
- if (rules.required && (value === null || value === void 0 || value === "" || Number.isNaN(value)))
374
- return { message: "This field is required" };
375
- if (rules.type === "number" && Number.isNaN(Number(value)))
376
- return { message: "This field must be a number" };
377
- return rules.custom?.(value);
378
- }
379
- async function validValues(items, values) {
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) {
380
401
  const errors = {};
381
402
  for (const item of items) {
382
403
  const value = values[item.name];
383
- const rules = item.rules;
384
- if (rules) {
385
- const error = await validValue(rules, value);
386
- 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
+ }
387
418
  }
388
419
  }
389
420
  return errors;
@@ -396,7 +427,9 @@ function FormFooter() {
396
427
  values,
397
428
  Elements,
398
429
  items,
399
- setErrors
430
+ setErrors,
431
+ lang,
432
+ rules
400
433
  } = useFormContext();
401
434
  return /* @__PURE__ */ jsx(
402
435
  Elements.Button,
@@ -404,7 +437,7 @@ function FormFooter() {
404
437
  disabled: submitting,
405
438
  submit: async () => {
406
439
  setSubmitting(true);
407
- const errors = await validValues(items, values);
440
+ const errors = await validValues(rules, items, values, lang);
408
441
  if (Object.keys(errors).length) {
409
442
  setErrors(errors);
410
443
  setSubmitting(false);
@@ -412,7 +445,7 @@ function FormFooter() {
412
445
  }
413
446
  onSubmit(values).finally(() => setSubmitting(false));
414
447
  },
415
- children: "Submit"
448
+ children: lang.submit
416
449
  }
417
450
  );
418
451
  }
@@ -488,26 +521,42 @@ var FormDefaultElements = {
488
521
  Input: FormInputElement,
489
522
  Button: FormButtonElement
490
523
  };
524
+
525
+ // src/Form/lang.ts
526
+ var FormDefaultLang = {
527
+ submit: "Submit",
528
+ required: "This field is required",
529
+ string: "This field must be a string",
530
+ number: "This field must be a number"
531
+ };
491
532
  function mergeValues(items, defaultValues = {}) {
492
533
  const values = {};
493
534
  for (const item of items)
494
535
  values[item.name] = defaultValues[item.name] ?? "";
495
536
  return values;
496
537
  }
497
- function FormContainer({ defaultValues, elements, ...props }) {
498
- const states = useSplittingState({
499
- values: mergeValues(props.items, defaultValues),
500
- errors: {},
501
- submitting: false,
502
- Elements: Object.assign(FormDefaultElements, elements)
503
- });
538
+ function FormContainer({
539
+ defaultValues,
540
+ Elements,
541
+ rules,
542
+ lang,
543
+ ...props
544
+ }) {
504
545
  return /* @__PURE__ */ jsxs(
505
546
  FormContextProvider,
506
547
  {
507
- value: {
508
- ...states,
509
- ...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)
510
558
  },
559
+ value: props,
511
560
  memo: true,
512
561
  children: [
513
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.5",
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.5"
37
+ "@faasjs/browser": "3.7.0-beta.7"
38
38
  },
39
39
  "devDependencies": {
40
- "@faasjs/browser": "3.7.0-beta.5",
40
+ "@faasjs/browser": "3.7.0-beta.7",
41
41
  "@types/react": "*",
42
42
  "react": "*"
43
43
  },