@faasjs/react 3.7.0-beta.3 → 3.7.0-beta.5

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
@@ -153,7 +153,7 @@ declare function createSplittingContext<T extends Record<string, any>>(defaultVa
153
153
  use: <NewT extends T = T>() => Readonly<NewT>;
154
154
  };
155
155
 
156
- type SetPrefix<S extends string | number | symbol> = S extends string ? (S extends `${infer First}${infer Rest}` ? `set${Capitalize<First>}${Rest}` : never) : never;
156
+ type SetPrefix<S extends string | number | symbol> = S extends string ? S extends `${infer First}${infer Rest}` ? `set${Capitalize<First>}${Rest}` : never : never;
157
157
  type StateSetters<T> = {
158
158
  [K in keyof T as SetPrefix<K>]: Dispatch<SetStateAction<T[K]>>;
159
159
  };
@@ -374,9 +374,10 @@ declare const OptionalWrapper: React.FC<OptionalWrapperProps> & {
374
374
  whyDidYouRender: boolean;
375
375
  };
376
376
 
377
- type FormRules = {
378
- type?: 'string' | 'number';
379
- required?: boolean;
377
+ type FormButtonElementProps = {
378
+ children?: React.ReactNode;
379
+ disabled: boolean;
380
+ submit: () => Promise<void>;
380
381
  };
381
382
 
382
383
  type FormInputElementProps = {
@@ -385,6 +386,15 @@ type FormInputElementProps = {
385
386
  onChange: (value: any) => void;
386
387
  };
387
388
 
389
+ type FormRules = {
390
+ type?: 'string' | 'number';
391
+ required?: boolean;
392
+ custom?: (value: any) => Promise<FormError | undefined>;
393
+ };
394
+ type FormError = {
395
+ message: string;
396
+ };
397
+
388
398
  type InferFormInputProps<T extends ComponentType<FormInputElementProps> | JSXElementConstructor<any>> = T extends ComponentType<FormInputElementProps> ? Omit<ComponentProps<T>, 'name' | 'value' | 'onChange'> : Omit<ComponentProps<T>, 'name' | 'value'>;
389
399
  type FormInputProps<FormElements extends FormElementTypes = FormElementTypes> = {
390
400
  Input?: ComponentType<FormInputElementProps>;
@@ -400,12 +410,6 @@ type FormLabelElementProps<FormElements extends FormElementTypes = FormElementTy
400
410
  input?: FormInputProps<FormElements>;
401
411
  };
402
412
 
403
- type FormButtonElementProps = {
404
- children?: React.ReactNode;
405
- disabled?: boolean;
406
- onClick?: () => void;
407
- };
408
-
409
413
  type FormElementTypes = {
410
414
  Label: ComponentType<FormLabelElementProps>;
411
415
  Input: ComponentType<FormInputElementProps>;
@@ -430,9 +434,11 @@ type FormContextProps<Values extends Record<string, any> = Record<string, any>>
430
434
  onSubmit: (values: Values) => Promise<void>;
431
435
  Elements: FormElementTypes;
432
436
  submitting: boolean;
433
- setSubmitting: React.Dispatch<React.SetStateAction<boolean>>;
437
+ setSubmitting: Dispatch<SetStateAction<boolean>>;
434
438
  values: Values;
435
- setValues: React.Dispatch<React.SetStateAction<Values>>;
439
+ setValues: Dispatch<SetStateAction<Values>>;
440
+ errors: Record<string, FormError>;
441
+ setErrors: Dispatch<SetStateAction<Record<string, FormError>>>;
436
442
  };
437
443
  declare const FormContextProvider: <NewT extends FormContextProps<Record<string, any>> = FormContextProps<Record<string, any>>>(props: {
438
444
  value?: NewT;
package/dist/index.d.ts CHANGED
@@ -153,7 +153,7 @@ declare function createSplittingContext<T extends Record<string, any>>(defaultVa
153
153
  use: <NewT extends T = T>() => Readonly<NewT>;
154
154
  };
155
155
 
156
- type SetPrefix<S extends string | number | symbol> = S extends string ? (S extends `${infer First}${infer Rest}` ? `set${Capitalize<First>}${Rest}` : never) : never;
156
+ type SetPrefix<S extends string | number | symbol> = S extends string ? S extends `${infer First}${infer Rest}` ? `set${Capitalize<First>}${Rest}` : never : never;
157
157
  type StateSetters<T> = {
158
158
  [K in keyof T as SetPrefix<K>]: Dispatch<SetStateAction<T[K]>>;
159
159
  };
@@ -374,9 +374,10 @@ declare const OptionalWrapper: React.FC<OptionalWrapperProps> & {
374
374
  whyDidYouRender: boolean;
375
375
  };
376
376
 
377
- type FormRules = {
378
- type?: 'string' | 'number';
379
- required?: boolean;
377
+ type FormButtonElementProps = {
378
+ children?: React.ReactNode;
379
+ disabled: boolean;
380
+ submit: () => Promise<void>;
380
381
  };
381
382
 
382
383
  type FormInputElementProps = {
@@ -385,6 +386,15 @@ type FormInputElementProps = {
385
386
  onChange: (value: any) => void;
386
387
  };
387
388
 
389
+ type FormRules = {
390
+ type?: 'string' | 'number';
391
+ required?: boolean;
392
+ custom?: (value: any) => Promise<FormError | undefined>;
393
+ };
394
+ type FormError = {
395
+ message: string;
396
+ };
397
+
388
398
  type InferFormInputProps<T extends ComponentType<FormInputElementProps> | JSXElementConstructor<any>> = T extends ComponentType<FormInputElementProps> ? Omit<ComponentProps<T>, 'name' | 'value' | 'onChange'> : Omit<ComponentProps<T>, 'name' | 'value'>;
389
399
  type FormInputProps<FormElements extends FormElementTypes = FormElementTypes> = {
390
400
  Input?: ComponentType<FormInputElementProps>;
@@ -400,12 +410,6 @@ type FormLabelElementProps<FormElements extends FormElementTypes = FormElementTy
400
410
  input?: FormInputProps<FormElements>;
401
411
  };
402
412
 
403
- type FormButtonElementProps = {
404
- children?: React.ReactNode;
405
- disabled?: boolean;
406
- onClick?: () => void;
407
- };
408
-
409
413
  type FormElementTypes = {
410
414
  Label: ComponentType<FormLabelElementProps>;
411
415
  Input: ComponentType<FormInputElementProps>;
@@ -430,9 +434,11 @@ type FormContextProps<Values extends Record<string, any> = Record<string, any>>
430
434
  onSubmit: (values: Values) => Promise<void>;
431
435
  Elements: FormElementTypes;
432
436
  submitting: boolean;
433
- setSubmitting: React.Dispatch<React.SetStateAction<boolean>>;
437
+ setSubmitting: Dispatch<SetStateAction<boolean>>;
434
438
  values: Values;
435
- setValues: React.Dispatch<React.SetStateAction<Values>>;
439
+ setValues: Dispatch<SetStateAction<Values>>;
440
+ errors: Record<string, FormError>;
441
+ setErrors: Dispatch<SetStateAction<Record<string, FormError>>>;
436
442
  };
437
443
  declare const FormContextProvider: <NewT extends FormContextProps<Record<string, any>> = FormContextProps<Record<string, any>>>(props: {
438
444
  value?: NewT;
package/dist/index.js CHANGED
@@ -122,7 +122,10 @@ function useSplittingState(initialStates) {
122
122
  const states = {};
123
123
  for (const key of Object.keys(initialStates)) {
124
124
  const state = react.useState(initialStates[key]);
125
- Object.assign(states, { [key]: state[0], [`set${String(key).charAt(0).toUpperCase()}${String(key).slice(1)}`]: state[1] });
125
+ Object.assign(states, {
126
+ [key]: state[0],
127
+ [`set${String(key).charAt(0).toUpperCase()}${String(key).slice(1)}`]: state[1]
128
+ });
126
129
  }
127
130
  return states;
128
131
  }
@@ -163,6 +166,17 @@ FaasDataWrapper.whyDidYouRender = true;
163
166
  function withFaasData(Component2, faasProps) {
164
167
  return (props) => /* @__PURE__ */ jsxRuntime.jsx(FaasDataWrapper, { ...faasProps, children: /* @__PURE__ */ jsxRuntime.jsx(Component2, { ...props }) });
165
168
  }
169
+
170
+ // src/faas.ts
171
+ async function faas(action, params, options) {
172
+ const client = getClient(options?.baseUrl);
173
+ if (client.onError)
174
+ return client.browserClient.action(action, params, options).catch(async (res) => {
175
+ await client.onError(action, params)(res);
176
+ return Promise.reject(res);
177
+ });
178
+ return client.browserClient.action(action, params, options);
179
+ }
166
180
  function useFaas(action, defaultParams, options = {}) {
167
181
  const [loading, setLoading] = react.useState(true);
168
182
  const [data, setData] = react.useState();
@@ -261,17 +275,6 @@ function useFaas(action, defaultParams, options = {}) {
261
275
  };
262
276
  }
263
277
  useFaas.whyDidYouRender = true;
264
-
265
- // src/faas.ts
266
- async function faas(action, params, options) {
267
- const client = getClient(options?.baseUrl);
268
- if (client.onError)
269
- return client.browserClient.action(action, params, options).catch(async (res) => {
270
- await client.onError(action, params)(res);
271
- return Promise.reject(res);
272
- });
273
- return client.browserClient.action(action, params, options);
274
- }
275
278
  var clients = {};
276
279
  function FaasReactClient({ baseUrl, options, onError } = {
277
280
  baseUrl: "/"
@@ -340,7 +343,17 @@ OptionalWrapper.displayName = "OptionalWrapper";
340
343
  OptionalWrapper.whyDidYouRender = true;
341
344
 
342
345
  // src/Form/context.tsx
343
- var FormContext = createSplittingContext(["items", "onSubmit", "Elements", "submitting", "setSubmitting", "values", "setValues"]);
346
+ var FormContext = createSplittingContext([
347
+ "items",
348
+ "onSubmit",
349
+ "Elements",
350
+ "submitting",
351
+ "setSubmitting",
352
+ "values",
353
+ "setValues",
354
+ "errors",
355
+ "setErrors"
356
+ ]);
344
357
  var FormContextProvider = FormContext.Provider;
345
358
  var useFormContext = FormContext.use;
346
359
  function FormLabel(props) {
@@ -356,6 +369,70 @@ function FormBody() {
356
369
  }
357
370
  FormBody.displayName = "FormBody";
358
371
  FormBody.whyDidYouRender = true;
372
+
373
+ // 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) {
382
+ const errors = {};
383
+ for (const item of items) {
384
+ 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;
389
+ }
390
+ }
391
+ return errors;
392
+ }
393
+ function FormFooter() {
394
+ const {
395
+ submitting,
396
+ setSubmitting,
397
+ onSubmit,
398
+ values,
399
+ Elements,
400
+ items,
401
+ setErrors
402
+ } = useFormContext();
403
+ return /* @__PURE__ */ jsxRuntime.jsx(
404
+ Elements.Button,
405
+ {
406
+ disabled: submitting,
407
+ submit: async () => {
408
+ setSubmitting(true);
409
+ const errors = await validValues(items, values);
410
+ if (Object.keys(errors).length) {
411
+ setErrors(errors);
412
+ setSubmitting(false);
413
+ return;
414
+ }
415
+ onSubmit(values).finally(() => setSubmitting(false));
416
+ },
417
+ children: "Submit"
418
+ }
419
+ );
420
+ }
421
+ FormFooter.displayName = "FormFooter";
422
+ FormFooter.whyDidYouRender = true;
423
+ var FormButtonElement = react.forwardRef(({ disabled, children, submit, ...props }, ref) => /* @__PURE__ */ jsxRuntime.jsx(
424
+ "button",
425
+ {
426
+ type: "button",
427
+ disabled,
428
+ onClick: submit,
429
+ ...props,
430
+ ref,
431
+ children
432
+ }
433
+ ));
434
+ FormButtonElement.displayName = "FormButtonElement";
435
+ FormButtonElement.whyDidYouRender = true;
359
436
  var FormInputElement = react.forwardRef(({ onChange, ...props }, ref) => /* @__PURE__ */ jsxRuntime.jsx("input", { ...props, onChange: (e) => onChange(e.target.value), ref }));
360
437
  FormInputElement.displayName = "FormInputElement";
361
438
  FormInputElement.whyDidYouRender = true;
@@ -366,7 +443,7 @@ var FormLabelElement = ({
366
443
  Label,
367
444
  input
368
445
  }) => {
369
- const { values, setValues } = useFormContext();
446
+ const { values, setValues, errors } = useFormContext();
370
447
  if (Label)
371
448
  return /* @__PURE__ */ jsxRuntime.jsx(
372
449
  Label,
@@ -400,24 +477,12 @@ var FormLabelElement = ({
400
477
  }))
401
478
  }
402
479
  ),
403
- description
480
+ description,
481
+ errors[name]?.message
404
482
  ] });
405
483
  };
406
484
  FormLabelElement.displayName = "FormLabelElement";
407
485
  FormLabelElement.whyDidYouRender = true;
408
- var FormButtonElement = react.forwardRef(({ disabled, children, onClick, ...props }, ref) => /* @__PURE__ */ jsxRuntime.jsx(
409
- "button",
410
- {
411
- type: "button",
412
- disabled,
413
- onClick,
414
- ...props,
415
- ref,
416
- children
417
- }
418
- ));
419
- FormButtonElement.displayName = "FormButtonElement";
420
- FormButtonElement.whyDidYouRender = true;
421
486
 
422
487
  // src/Form/elements/index.ts
423
488
  var FormDefaultElements = {
@@ -425,22 +490,6 @@ var FormDefaultElements = {
425
490
  Input: FormInputElement,
426
491
  Button: FormButtonElement
427
492
  };
428
- function FormFooter() {
429
- const { submitting, setSubmitting, onSubmit, values, Elements } = useFormContext();
430
- return /* @__PURE__ */ jsxRuntime.jsx(
431
- Elements.Button,
432
- {
433
- disabled: submitting,
434
- onClick: () => {
435
- setSubmitting(true);
436
- onSubmit(values).finally(() => setSubmitting(false));
437
- },
438
- children: "Submit"
439
- }
440
- );
441
- }
442
- FormFooter.displayName = "FormFooter";
443
- FormFooter.whyDidYouRender = true;
444
493
  function mergeValues(items, defaultValues = {}) {
445
494
  const values = {};
446
495
  for (const item of items)
@@ -450,6 +499,7 @@ function mergeValues(items, defaultValues = {}) {
450
499
  function FormContainer({ defaultValues, elements, ...props }) {
451
500
  const states = useSplittingState({
452
501
  values: mergeValues(props.items, defaultValues),
502
+ errors: {},
453
503
  submitting: false,
454
504
  Elements: Object.assign(FormDefaultElements, elements)
455
505
  });
package/dist/index.mjs CHANGED
@@ -120,7 +120,10 @@ function useSplittingState(initialStates) {
120
120
  const states = {};
121
121
  for (const key of Object.keys(initialStates)) {
122
122
  const state = useState(initialStates[key]);
123
- Object.assign(states, { [key]: state[0], [`set${String(key).charAt(0).toUpperCase()}${String(key).slice(1)}`]: state[1] });
123
+ Object.assign(states, {
124
+ [key]: state[0],
125
+ [`set${String(key).charAt(0).toUpperCase()}${String(key).slice(1)}`]: state[1]
126
+ });
124
127
  }
125
128
  return states;
126
129
  }
@@ -161,6 +164,17 @@ FaasDataWrapper.whyDidYouRender = true;
161
164
  function withFaasData(Component2, faasProps) {
162
165
  return (props) => /* @__PURE__ */ jsx(FaasDataWrapper, { ...faasProps, children: /* @__PURE__ */ jsx(Component2, { ...props }) });
163
166
  }
167
+
168
+ // src/faas.ts
169
+ async function faas(action, params, options) {
170
+ const client = getClient(options?.baseUrl);
171
+ if (client.onError)
172
+ return client.browserClient.action(action, params, options).catch(async (res) => {
173
+ await client.onError(action, params)(res);
174
+ return Promise.reject(res);
175
+ });
176
+ return client.browserClient.action(action, params, options);
177
+ }
164
178
  function useFaas(action, defaultParams, options = {}) {
165
179
  const [loading, setLoading] = useState(true);
166
180
  const [data, setData] = useState();
@@ -259,17 +273,6 @@ function useFaas(action, defaultParams, options = {}) {
259
273
  };
260
274
  }
261
275
  useFaas.whyDidYouRender = true;
262
-
263
- // src/faas.ts
264
- async function faas(action, params, options) {
265
- const client = getClient(options?.baseUrl);
266
- if (client.onError)
267
- return client.browserClient.action(action, params, options).catch(async (res) => {
268
- await client.onError(action, params)(res);
269
- return Promise.reject(res);
270
- });
271
- return client.browserClient.action(action, params, options);
272
- }
273
276
  var clients = {};
274
277
  function FaasReactClient({ baseUrl, options, onError } = {
275
278
  baseUrl: "/"
@@ -338,7 +341,17 @@ OptionalWrapper.displayName = "OptionalWrapper";
338
341
  OptionalWrapper.whyDidYouRender = true;
339
342
 
340
343
  // src/Form/context.tsx
341
- var FormContext = createSplittingContext(["items", "onSubmit", "Elements", "submitting", "setSubmitting", "values", "setValues"]);
344
+ var FormContext = createSplittingContext([
345
+ "items",
346
+ "onSubmit",
347
+ "Elements",
348
+ "submitting",
349
+ "setSubmitting",
350
+ "values",
351
+ "setValues",
352
+ "errors",
353
+ "setErrors"
354
+ ]);
342
355
  var FormContextProvider = FormContext.Provider;
343
356
  var useFormContext = FormContext.use;
344
357
  function FormLabel(props) {
@@ -354,6 +367,70 @@ function FormBody() {
354
367
  }
355
368
  FormBody.displayName = "FormBody";
356
369
  FormBody.whyDidYouRender = true;
370
+
371
+ // 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) {
380
+ const errors = {};
381
+ for (const item of items) {
382
+ 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;
387
+ }
388
+ }
389
+ return errors;
390
+ }
391
+ function FormFooter() {
392
+ const {
393
+ submitting,
394
+ setSubmitting,
395
+ onSubmit,
396
+ values,
397
+ Elements,
398
+ items,
399
+ setErrors
400
+ } = useFormContext();
401
+ return /* @__PURE__ */ jsx(
402
+ Elements.Button,
403
+ {
404
+ disabled: submitting,
405
+ submit: async () => {
406
+ setSubmitting(true);
407
+ const errors = await validValues(items, values);
408
+ if (Object.keys(errors).length) {
409
+ setErrors(errors);
410
+ setSubmitting(false);
411
+ return;
412
+ }
413
+ onSubmit(values).finally(() => setSubmitting(false));
414
+ },
415
+ children: "Submit"
416
+ }
417
+ );
418
+ }
419
+ FormFooter.displayName = "FormFooter";
420
+ FormFooter.whyDidYouRender = true;
421
+ var FormButtonElement = forwardRef(({ disabled, children, submit, ...props }, ref) => /* @__PURE__ */ jsx(
422
+ "button",
423
+ {
424
+ type: "button",
425
+ disabled,
426
+ onClick: submit,
427
+ ...props,
428
+ ref,
429
+ children
430
+ }
431
+ ));
432
+ FormButtonElement.displayName = "FormButtonElement";
433
+ FormButtonElement.whyDidYouRender = true;
357
434
  var FormInputElement = forwardRef(({ onChange, ...props }, ref) => /* @__PURE__ */ jsx("input", { ...props, onChange: (e) => onChange(e.target.value), ref }));
358
435
  FormInputElement.displayName = "FormInputElement";
359
436
  FormInputElement.whyDidYouRender = true;
@@ -364,7 +441,7 @@ var FormLabelElement = ({
364
441
  Label,
365
442
  input
366
443
  }) => {
367
- const { values, setValues } = useFormContext();
444
+ const { values, setValues, errors } = useFormContext();
368
445
  if (Label)
369
446
  return /* @__PURE__ */ jsx(
370
447
  Label,
@@ -398,24 +475,12 @@ var FormLabelElement = ({
398
475
  }))
399
476
  }
400
477
  ),
401
- description
478
+ description,
479
+ errors[name]?.message
402
480
  ] });
403
481
  };
404
482
  FormLabelElement.displayName = "FormLabelElement";
405
483
  FormLabelElement.whyDidYouRender = true;
406
- var FormButtonElement = forwardRef(({ disabled, children, onClick, ...props }, ref) => /* @__PURE__ */ jsx(
407
- "button",
408
- {
409
- type: "button",
410
- disabled,
411
- onClick,
412
- ...props,
413
- ref,
414
- children
415
- }
416
- ));
417
- FormButtonElement.displayName = "FormButtonElement";
418
- FormButtonElement.whyDidYouRender = true;
419
484
 
420
485
  // src/Form/elements/index.ts
421
486
  var FormDefaultElements = {
@@ -423,22 +488,6 @@ var FormDefaultElements = {
423
488
  Input: FormInputElement,
424
489
  Button: FormButtonElement
425
490
  };
426
- function FormFooter() {
427
- const { submitting, setSubmitting, onSubmit, values, Elements } = useFormContext();
428
- return /* @__PURE__ */ jsx(
429
- Elements.Button,
430
- {
431
- disabled: submitting,
432
- onClick: () => {
433
- setSubmitting(true);
434
- onSubmit(values).finally(() => setSubmitting(false));
435
- },
436
- children: "Submit"
437
- }
438
- );
439
- }
440
- FormFooter.displayName = "FormFooter";
441
- FormFooter.whyDidYouRender = true;
442
491
  function mergeValues(items, defaultValues = {}) {
443
492
  const values = {};
444
493
  for (const item of items)
@@ -448,6 +497,7 @@ function mergeValues(items, defaultValues = {}) {
448
497
  function FormContainer({ defaultValues, elements, ...props }) {
449
498
  const states = useSplittingState({
450
499
  values: mergeValues(props.items, defaultValues),
500
+ errors: {},
451
501
  submitting: false,
452
502
  Elements: Object.assign(FormDefaultElements, elements)
453
503
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@faasjs/react",
3
- "version": "3.7.0-beta.3",
3
+ "version": "3.7.0-beta.5",
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.3"
37
+ "@faasjs/browser": "3.7.0-beta.5"
38
38
  },
39
39
  "devDependencies": {
40
- "@faasjs/browser": "3.7.0-beta.3",
40
+ "@faasjs/browser": "3.7.0-beta.5",
41
41
  "@types/react": "*",
42
42
  "react": "*"
43
43
  },