@faasjs/react 3.6.1 → 3.7.0-beta.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -44,6 +44,7 @@ npm install @faasjs/react
44
44
  - [faas](functions/faas.md)
45
45
  - [FaasDataWrapper](functions/FaasDataWrapper.md)
46
46
  - [FaasReactClient](functions/FaasReactClient.md)
47
+ - [Form](functions/Form.md)
47
48
  - [getClient](functions/getClient.md)
48
49
  - [OptionalWrapper](functions/OptionalWrapper.md)
49
50
  - [useConstant](functions/useConstant.md)
package/dist/index.d.mts CHANGED
@@ -2,7 +2,7 @@ import { FaasAction, FaasData, FaasParams } from '@faasjs/types';
2
2
  export { FaasAction, FaasData, FaasParams } from '@faasjs/types';
3
3
  import { Response, BaseUrl, ResponseError, Options, FaasBrowserClient } from '@faasjs/browser';
4
4
  export { Options, Response, ResponseError, ResponseHeaders } from '@faasjs/browser';
5
- import { ReactNode, Dispatch, SetStateAction, ReactElement, Component, ComponentType, ComponentProps } from 'react';
5
+ import { ReactNode, Dispatch, SetStateAction, ReactElement, Component, ComponentType, ComponentProps, JSXElementConstructor } from 'react';
6
6
  import * as react_jsx_runtime from 'react/jsx-runtime';
7
7
 
8
8
  /**
@@ -371,4 +371,64 @@ declare const OptionalWrapper: React.FC<OptionalWrapperProps> & {
371
371
  whyDidYouRender: boolean;
372
372
  };
373
373
 
374
- export { ErrorBoundary, type ErrorBoundaryProps, type ErrorChildrenProps, type FaasDataInjection, FaasDataWrapper, type FaasDataWrapperProps, FaasReactClient, type FaasReactClientInstance, type FaasReactClientOptions, type OnError, OptionalWrapper, type OptionalWrapperProps, createSplittingContext, equal, faas, getClient, useConstant, useEqualCallback, useEqualEffect, useEqualMemo, useEqualMemoize, useFaas, type useFaasOptions, useSplittingState, withFaasData };
374
+ type FormRules = {
375
+ type?: 'string' | 'number';
376
+ required?: boolean;
377
+ };
378
+
379
+ type FormInputComponentProps = {
380
+ name: string;
381
+ value: any;
382
+ onChange: (value: any) => void;
383
+ };
384
+ type FormInputComponent<Extra extends Record<string, any> = Record<string, any>> = ComponentType<FormInputComponentProps & Extra>;
385
+ type InferFormInputProps<T extends FormInputComponent | JSXElementConstructor<any>> = T extends FormInputComponent ? Omit<ComponentProps<T>, 'name' | 'value' | 'onChange'> : Omit<ComponentProps<T>, 'name' | 'value'>;
386
+ type FormInputProps<FormElements extends FormElementTypes = FormElementTypes> = {
387
+ Input: FormInputComponent;
388
+ } | {
389
+ type?: 'input';
390
+ props?: InferFormInputProps<FormElements['input']>;
391
+ } | {
392
+ type: 'select';
393
+ props?: InferFormInputProps<FormElements['select']>;
394
+ };
395
+
396
+ type FormLabelProps<FormElements extends FormElementTypes = FormElementTypes> = {
397
+ name: string;
398
+ rules?: FormRules;
399
+ label?: {
400
+ title?: ReactNode;
401
+ description?: ReactNode;
402
+ Label?: React.ComponentType<FormLabelProps>;
403
+ };
404
+ input?: FormInputProps<FormElements>;
405
+ };
406
+
407
+ type FormButtonProps = {
408
+ children?: React.ReactNode;
409
+ disabled?: boolean;
410
+ onClick?: () => void;
411
+ };
412
+
413
+ type FormSelectOption = {
414
+ value: string | number;
415
+ label: string;
416
+ };
417
+ type FormElementTypes = {
418
+ label: ComponentType<FormLabelProps>;
419
+ input: FormInputComponent;
420
+ select: FormInputComponent<{
421
+ options?: FormSelectOption[];
422
+ }>;
423
+ button: ComponentType<FormButtonProps>;
424
+ };
425
+
426
+ type FormProps<Values extends Record<string, any> = Record<string, any>, FormElements extends FormElementTypes = FormElementTypes> = {
427
+ items: FormLabelProps<FormElements>[];
428
+ onSubmit?: (values: Values) => Promise<void>;
429
+ elements?: FormElements;
430
+ defaultValues?: Values;
431
+ };
432
+ 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;
433
+
434
+ export { ErrorBoundary, type ErrorBoundaryProps, type ErrorChildrenProps, type FaasDataInjection, FaasDataWrapper, type FaasDataWrapperProps, FaasReactClient, type FaasReactClientInstance, type FaasReactClientOptions, FormContainer as Form, type OnError, OptionalWrapper, type OptionalWrapperProps, createSplittingContext, equal, faas, getClient, useConstant, useEqualCallback, useEqualEffect, useEqualMemo, useEqualMemoize, useFaas, type useFaasOptions, useSplittingState, withFaasData };
package/dist/index.d.ts CHANGED
@@ -2,7 +2,7 @@ import { FaasAction, FaasData, FaasParams } from '@faasjs/types';
2
2
  export { FaasAction, FaasData, FaasParams } from '@faasjs/types';
3
3
  import { Response, BaseUrl, ResponseError, Options, FaasBrowserClient } from '@faasjs/browser';
4
4
  export { Options, Response, ResponseError, ResponseHeaders } from '@faasjs/browser';
5
- import { ReactNode, Dispatch, SetStateAction, ReactElement, Component, ComponentType, ComponentProps } from 'react';
5
+ import { ReactNode, Dispatch, SetStateAction, ReactElement, Component, ComponentType, ComponentProps, JSXElementConstructor } from 'react';
6
6
  import * as react_jsx_runtime from 'react/jsx-runtime';
7
7
 
8
8
  /**
@@ -371,4 +371,64 @@ declare const OptionalWrapper: React.FC<OptionalWrapperProps> & {
371
371
  whyDidYouRender: boolean;
372
372
  };
373
373
 
374
- export { ErrorBoundary, type ErrorBoundaryProps, type ErrorChildrenProps, type FaasDataInjection, FaasDataWrapper, type FaasDataWrapperProps, FaasReactClient, type FaasReactClientInstance, type FaasReactClientOptions, type OnError, OptionalWrapper, type OptionalWrapperProps, createSplittingContext, equal, faas, getClient, useConstant, useEqualCallback, useEqualEffect, useEqualMemo, useEqualMemoize, useFaas, type useFaasOptions, useSplittingState, withFaasData };
374
+ type FormRules = {
375
+ type?: 'string' | 'number';
376
+ required?: boolean;
377
+ };
378
+
379
+ type FormInputComponentProps = {
380
+ name: string;
381
+ value: any;
382
+ onChange: (value: any) => void;
383
+ };
384
+ type FormInputComponent<Extra extends Record<string, any> = Record<string, any>> = ComponentType<FormInputComponentProps & Extra>;
385
+ type InferFormInputProps<T extends FormInputComponent | JSXElementConstructor<any>> = T extends FormInputComponent ? Omit<ComponentProps<T>, 'name' | 'value' | 'onChange'> : Omit<ComponentProps<T>, 'name' | 'value'>;
386
+ type FormInputProps<FormElements extends FormElementTypes = FormElementTypes> = {
387
+ Input: FormInputComponent;
388
+ } | {
389
+ type?: 'input';
390
+ props?: InferFormInputProps<FormElements['input']>;
391
+ } | {
392
+ type: 'select';
393
+ props?: InferFormInputProps<FormElements['select']>;
394
+ };
395
+
396
+ type FormLabelProps<FormElements extends FormElementTypes = FormElementTypes> = {
397
+ name: string;
398
+ rules?: FormRules;
399
+ label?: {
400
+ title?: ReactNode;
401
+ description?: ReactNode;
402
+ Label?: React.ComponentType<FormLabelProps>;
403
+ };
404
+ input?: FormInputProps<FormElements>;
405
+ };
406
+
407
+ type FormButtonProps = {
408
+ children?: React.ReactNode;
409
+ disabled?: boolean;
410
+ onClick?: () => void;
411
+ };
412
+
413
+ type FormSelectOption = {
414
+ value: string | number;
415
+ label: string;
416
+ };
417
+ type FormElementTypes = {
418
+ label: ComponentType<FormLabelProps>;
419
+ input: FormInputComponent;
420
+ select: FormInputComponent<{
421
+ options?: FormSelectOption[];
422
+ }>;
423
+ button: ComponentType<FormButtonProps>;
424
+ };
425
+
426
+ type FormProps<Values extends Record<string, any> = Record<string, any>, FormElements extends FormElementTypes = FormElementTypes> = {
427
+ items: FormLabelProps<FormElements>[];
428
+ onSubmit?: (values: Values) => Promise<void>;
429
+ elements?: FormElements;
430
+ defaultValues?: Values;
431
+ };
432
+ 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;
433
+
434
+ export { ErrorBoundary, type ErrorBoundaryProps, type ErrorChildrenProps, type FaasDataInjection, FaasDataWrapper, type FaasDataWrapperProps, FaasReactClient, type FaasReactClientInstance, type FaasReactClientOptions, FormContainer as Form, type OnError, OptionalWrapper, type OptionalWrapperProps, createSplittingContext, equal, faas, getClient, useConstant, useEqualCallback, useEqualEffect, useEqualMemo, useEqualMemoize, useFaas, type useFaasOptions, useSplittingState, withFaasData };
package/dist/index.js CHANGED
@@ -334,9 +334,152 @@ var OptionalWrapper = ({ condition, Wrapper, wrapperProps, children }) => {
334
334
  };
335
335
  OptionalWrapper.whyDidYouRender = true;
336
336
 
337
+ // src/Form/context.tsx
338
+ var FormContext = createSplittingContext(["items", "onSubmit", "elements", "submitting", "setSubmitting", "values", "setValues"]);
339
+ var FormContextProvider = FormContext.Provider;
340
+ var useFormContext = FormContext.use;
341
+ function FormLabel(props) {
342
+ const { elements } = useFormContext();
343
+ if (props.label?.Label) return /* @__PURE__ */ jsxRuntime.jsx(props.label.Label, { ...props });
344
+ return /* @__PURE__ */ jsxRuntime.jsx(elements.label, { ...props });
345
+ }
346
+ function FormBody() {
347
+ const { items } = useFormContext();
348
+ return items.map((item) => /* @__PURE__ */ jsxRuntime.jsx(FormLabel, { ...item }, item.name));
349
+ }
350
+ function processValue(input, rules) {
351
+ let value = input;
352
+ if (typeof input === "object" && "target" in input) {
353
+ value = input.target.value;
354
+ }
355
+ switch (rules?.type) {
356
+ case "number":
357
+ return Number(value);
358
+ case "string":
359
+ return String(value);
360
+ default:
361
+ return value;
362
+ }
363
+ }
364
+ function FormInput({
365
+ name,
366
+ rules,
367
+ ...rest
368
+ }) {
369
+ const { elements, values, setValues } = useFormContext();
370
+ const value = values?.[name];
371
+ if ("Input" in rest && rest.Input) {
372
+ return /* @__PURE__ */ jsxRuntime.jsx(
373
+ rest.Input,
374
+ {
375
+ name,
376
+ value,
377
+ onChange: (v) => setValues((prev) => ({
378
+ ...prev,
379
+ [name]: v
380
+ }))
381
+ }
382
+ );
383
+ }
384
+ if ("type" in rest || "props" in rest) {
385
+ switch (rest.type) {
386
+ case "select":
387
+ return /* @__PURE__ */ jsxRuntime.jsx(
388
+ elements.select,
389
+ {
390
+ name,
391
+ value,
392
+ onChange: (v) => setValues((prev) => ({
393
+ ...prev,
394
+ [name]: processValue(v, rules)
395
+ })),
396
+ ...rest.props
397
+ }
398
+ );
399
+ default:
400
+ return /* @__PURE__ */ jsxRuntime.jsx(
401
+ elements.input,
402
+ {
403
+ name,
404
+ value,
405
+ onChange: (v) => setValues((prev) => ({
406
+ ...prev,
407
+ [name]: processValue(v, rules)
408
+ })),
409
+ ...rest.props
410
+ }
411
+ );
412
+ }
413
+ }
414
+ return /* @__PURE__ */ jsxRuntime.jsx(
415
+ elements.input,
416
+ {
417
+ name,
418
+ value,
419
+ onChange: (v) => setValues((prev) => ({
420
+ ...prev,
421
+ [name]: processValue(v, rules)
422
+ }))
423
+ }
424
+ );
425
+ }
426
+ var FormElements = {
427
+ label: react.forwardRef(({ name, label, input, rules }, ref) => /* @__PURE__ */ jsxRuntime.jsxs("label", { ref, children: [
428
+ label?.title ?? name,
429
+ /* @__PURE__ */ jsxRuntime.jsx(FormInput, { name, rules, ...input }),
430
+ label?.description
431
+ ] })),
432
+ input: react.forwardRef(
433
+ (props, ref) => /* @__PURE__ */ jsxRuntime.jsx("input", { ...props, ref })
434
+ ),
435
+ select: react.forwardRef(({ options, ...props }, ref) => /* @__PURE__ */ jsxRuntime.jsx("select", { ...props, ref, children: options?.map((option) => /* @__PURE__ */ jsxRuntime.jsx("option", { value: option.value, children: option.label }, option.value)) })),
436
+ button: react.forwardRef(({ disabled, children, onClick, ...props }, ref) => /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", disabled, onClick, ...props, ref, children }))
437
+ };
438
+ function FormFooter() {
439
+ const { submitting, setSubmitting, onSubmit, values, elements } = useFormContext();
440
+ return /* @__PURE__ */ jsxRuntime.jsx(
441
+ elements.button,
442
+ {
443
+ disabled: submitting,
444
+ onClick: () => {
445
+ setSubmitting(true);
446
+ onSubmit(values).finally(() => setSubmitting(false));
447
+ },
448
+ children: "Submit"
449
+ }
450
+ );
451
+ }
452
+ function mergeValues(items, defaultValues = {}) {
453
+ const values = {};
454
+ for (const item of items)
455
+ values[item.name] = defaultValues[item.name] ?? "";
456
+ return values;
457
+ }
458
+ function FormContainer({ defaultValues, elements, ...props }) {
459
+ const states = useSplittingState({
460
+ values: mergeValues(props.items, defaultValues),
461
+ submitting: false,
462
+ elements: Object.assign(FormElements, elements)
463
+ });
464
+ return /* @__PURE__ */ jsxRuntime.jsxs(
465
+ FormContextProvider,
466
+ {
467
+ value: {
468
+ ...states,
469
+ ...props
470
+ },
471
+ children: [
472
+ /* @__PURE__ */ jsxRuntime.jsx(FormBody, {}),
473
+ /* @__PURE__ */ jsxRuntime.jsx(FormFooter, {})
474
+ ]
475
+ }
476
+ );
477
+ }
478
+
337
479
  exports.ErrorBoundary = ErrorBoundary;
338
480
  exports.FaasDataWrapper = FaasDataWrapper;
339
481
  exports.FaasReactClient = FaasReactClient;
482
+ exports.Form = FormContainer;
340
483
  exports.OptionalWrapper = OptionalWrapper;
341
484
  exports.createSplittingContext = createSplittingContext;
342
485
  exports.equal = equal;
package/dist/index.mjs CHANGED
@@ -1,5 +1,5 @@
1
- import { useRef, useMemo, useEffect, useCallback, createContext, useState, cloneElement, Component, useContext } from 'react';
2
- import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
1
+ import { forwardRef, useRef, useMemo, useEffect, useCallback, createContext, useState, cloneElement, Component, useContext } from 'react';
2
+ import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
3
3
  import { FaasBrowserClient } from '@faasjs/browser';
4
4
 
5
5
  // src/constant.ts
@@ -332,4 +332,146 @@ var OptionalWrapper = ({ condition, Wrapper, wrapperProps, children }) => {
332
332
  };
333
333
  OptionalWrapper.whyDidYouRender = true;
334
334
 
335
- export { ErrorBoundary, FaasDataWrapper, FaasReactClient, OptionalWrapper, createSplittingContext, equal, faas, getClient, useConstant, useEqualCallback, useEqualEffect, useEqualMemo, useEqualMemoize, useFaas, useSplittingState, withFaasData };
335
+ // src/Form/context.tsx
336
+ var FormContext = createSplittingContext(["items", "onSubmit", "elements", "submitting", "setSubmitting", "values", "setValues"]);
337
+ var FormContextProvider = FormContext.Provider;
338
+ var useFormContext = FormContext.use;
339
+ function FormLabel(props) {
340
+ const { elements } = useFormContext();
341
+ if (props.label?.Label) return /* @__PURE__ */ jsx(props.label.Label, { ...props });
342
+ return /* @__PURE__ */ jsx(elements.label, { ...props });
343
+ }
344
+ function FormBody() {
345
+ const { items } = useFormContext();
346
+ return items.map((item) => /* @__PURE__ */ jsx(FormLabel, { ...item }, item.name));
347
+ }
348
+ function processValue(input, rules) {
349
+ let value = input;
350
+ if (typeof input === "object" && "target" in input) {
351
+ value = input.target.value;
352
+ }
353
+ switch (rules?.type) {
354
+ case "number":
355
+ return Number(value);
356
+ case "string":
357
+ return String(value);
358
+ default:
359
+ return value;
360
+ }
361
+ }
362
+ function FormInput({
363
+ name,
364
+ rules,
365
+ ...rest
366
+ }) {
367
+ const { elements, values, setValues } = useFormContext();
368
+ const value = values?.[name];
369
+ if ("Input" in rest && rest.Input) {
370
+ return /* @__PURE__ */ jsx(
371
+ rest.Input,
372
+ {
373
+ name,
374
+ value,
375
+ onChange: (v) => setValues((prev) => ({
376
+ ...prev,
377
+ [name]: v
378
+ }))
379
+ }
380
+ );
381
+ }
382
+ if ("type" in rest || "props" in rest) {
383
+ switch (rest.type) {
384
+ case "select":
385
+ return /* @__PURE__ */ jsx(
386
+ elements.select,
387
+ {
388
+ name,
389
+ value,
390
+ onChange: (v) => setValues((prev) => ({
391
+ ...prev,
392
+ [name]: processValue(v, rules)
393
+ })),
394
+ ...rest.props
395
+ }
396
+ );
397
+ default:
398
+ return /* @__PURE__ */ jsx(
399
+ elements.input,
400
+ {
401
+ name,
402
+ value,
403
+ onChange: (v) => setValues((prev) => ({
404
+ ...prev,
405
+ [name]: processValue(v, rules)
406
+ })),
407
+ ...rest.props
408
+ }
409
+ );
410
+ }
411
+ }
412
+ return /* @__PURE__ */ jsx(
413
+ elements.input,
414
+ {
415
+ name,
416
+ value,
417
+ onChange: (v) => setValues((prev) => ({
418
+ ...prev,
419
+ [name]: processValue(v, rules)
420
+ }))
421
+ }
422
+ );
423
+ }
424
+ var FormElements = {
425
+ label: forwardRef(({ name, label, input, rules }, ref) => /* @__PURE__ */ jsxs("label", { ref, children: [
426
+ label?.title ?? name,
427
+ /* @__PURE__ */ jsx(FormInput, { name, rules, ...input }),
428
+ label?.description
429
+ ] })),
430
+ input: forwardRef(
431
+ (props, ref) => /* @__PURE__ */ jsx("input", { ...props, ref })
432
+ ),
433
+ select: forwardRef(({ options, ...props }, ref) => /* @__PURE__ */ jsx("select", { ...props, ref, children: options?.map((option) => /* @__PURE__ */ jsx("option", { value: option.value, children: option.label }, option.value)) })),
434
+ button: forwardRef(({ disabled, children, onClick, ...props }, ref) => /* @__PURE__ */ jsx("button", { type: "button", disabled, onClick, ...props, ref, children }))
435
+ };
436
+ function FormFooter() {
437
+ const { submitting, setSubmitting, onSubmit, values, elements } = useFormContext();
438
+ return /* @__PURE__ */ jsx(
439
+ elements.button,
440
+ {
441
+ disabled: submitting,
442
+ onClick: () => {
443
+ setSubmitting(true);
444
+ onSubmit(values).finally(() => setSubmitting(false));
445
+ },
446
+ children: "Submit"
447
+ }
448
+ );
449
+ }
450
+ function mergeValues(items, defaultValues = {}) {
451
+ const values = {};
452
+ for (const item of items)
453
+ values[item.name] = defaultValues[item.name] ?? "";
454
+ return values;
455
+ }
456
+ function FormContainer({ defaultValues, elements, ...props }) {
457
+ const states = useSplittingState({
458
+ values: mergeValues(props.items, defaultValues),
459
+ submitting: false,
460
+ elements: Object.assign(FormElements, elements)
461
+ });
462
+ return /* @__PURE__ */ jsxs(
463
+ FormContextProvider,
464
+ {
465
+ value: {
466
+ ...states,
467
+ ...props
468
+ },
469
+ children: [
470
+ /* @__PURE__ */ jsx(FormBody, {}),
471
+ /* @__PURE__ */ jsx(FormFooter, {})
472
+ ]
473
+ }
474
+ );
475
+ }
476
+
477
+ export { ErrorBoundary, FaasDataWrapper, FaasReactClient, FormContainer as Form, OptionalWrapper, createSplittingContext, equal, faas, getClient, useConstant, useEqualCallback, useEqualEffect, useEqualMemo, useEqualMemoize, useFaas, useSplittingState, withFaasData };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@faasjs/react",
3
- "version": "3.6.1",
3
+ "version": "3.7.0-beta.0",
4
4
  "license": "MIT",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",
@@ -28,19 +28,18 @@
28
28
  },
29
29
  "funding": "https://github.com/sponsors/faasjs",
30
30
  "scripts": {
31
- "build": "tsup src/index.tsx --config ../../tsup.config.json"
31
+ "build": "tsup src/index.tsx --config ../../tsup.config.json --external react"
32
32
  },
33
33
  "files": [
34
34
  "dist"
35
35
  ],
36
36
  "peerDependencies": {
37
- "react": "*",
38
- "@faasjs/browser": "3.6.1"
37
+ "@faasjs/browser": "3.7.0-beta.0"
39
38
  },
40
39
  "devDependencies": {
40
+ "@faasjs/browser": "3.7.0-beta.0",
41
41
  "@types/react": "*",
42
- "react": "*",
43
- "@faasjs/browser": "3.6.1"
42
+ "react": "*"
44
43
  },
45
44
  "engines": {
46
45
  "node": ">=22.0.0",